// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "gpu_backend.h" #include #include #include class GPU_SW_Backend final : public GPUBackend { public: GPU_SW_Backend(); ~GPU_SW_Backend() override; bool Initialize(bool force_thread) override; void Reset() override; ALWAYS_INLINE_RELEASE u16 GetPixel(const u32 x, const u32 y) const { return g_vram[VRAM_WIDTH * y + x]; } ALWAYS_INLINE_RELEASE const u16* GetPixelPtr(const u32 x, const u32 y) const { return &g_vram[VRAM_WIDTH * y + x]; } ALWAYS_INLINE_RELEASE u16* GetPixelPtr(const u32 x, const u32 y) { return &g_vram[VRAM_WIDTH * y + x]; } ALWAYS_INLINE_RELEASE void SetPixel(const u32 x, const u32 y, const u16 value) { g_vram[VRAM_WIDTH * y + x] = value; } // this is actually (31 * 255) >> 4) == 494, but to simplify addressing we use the next power of two (512) static constexpr u32 DITHER_LUT_SIZE = 512; using DitherLUT = std::array, DITHER_MATRIX_SIZE>, DITHER_MATRIX_SIZE>; static constexpr DitherLUT ComputeDitherLUT(); protected: union VRAMPixel { u16 bits; BitField r; BitField g; BitField b; BitField c; void Set(u8 r_, u8 g_, u8 b_, bool c_ = false) { bits = (ZeroExtend16(r_)) | (ZeroExtend16(g_) << 5) | (ZeroExtend16(b_) << 10) | (static_cast(c_) << 15); } void ClampAndSet(u8 r_, u8 g_, u8 b_, bool c_ = false) { Set(std::min(r_, 0x1F), std::min(g_, 0x1F), std::min(b_, 0x1F), c_); } void SetRGB24(u32 rgb24, bool c_ = false) { bits = Truncate16(((rgb24 >> 3) & 0x1F) | (((rgb24 >> 11) & 0x1F) << 5) | (((rgb24 >> 19) & 0x1F) << 10)) | (static_cast(c_) << 15); } void SetRGB24(u8 r8, u8 g8, u8 b8, bool c_ = false) { bits = (ZeroExtend16(r8 >> 3)) | (ZeroExtend16(g8 >> 3) << 5) | (ZeroExtend16(b8 >> 3) << 10) | (static_cast(c_) << 15); } void SetRGB24Dithered(u32 x, u32 y, u8 r8, u8 g8, u8 b8, bool c_ = false) { const s32 offset = DITHER_MATRIX[y & 3][x & 3]; r8 = static_cast(std::clamp(static_cast(ZeroExtend32(r8)) + offset, 0, 255)); g8 = static_cast(std::clamp(static_cast(ZeroExtend32(g8)) + offset, 0, 255)); b8 = static_cast(std::clamp(static_cast(ZeroExtend32(b8)) + offset, 0, 255)); SetRGB24(r8, g8, b8, c_); } u32 ToRGB24() const { const u32 r_ = ZeroExtend32(r.GetValue()); const u32 g_ = ZeroExtend32(g.GetValue()); const u32 b_ = ZeroExtend32(b.GetValue()); return ((r_ << 3) | (r_ & 7)) | (((g_ << 3) | (g_ & 7)) << 8) | (((b_ << 3) | (b_ & 7)) << 16); } }; static constexpr std::tuple UnpackTexcoord(u16 texcoord) { return std::make_tuple(static_cast(texcoord), static_cast(texcoord >> 8)); } static constexpr std::tuple UnpackColorRGB24(u32 rgb24) { return std::make_tuple(static_cast(rgb24), static_cast(rgb24 >> 8), static_cast(rgb24 >> 16)); } void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, GPUBackendCommandParameters params) override; void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, GPUBackendCommandParameters params) override; void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height, GPUBackendCommandParameters params) override; void DrawPolygon(const GPUBackendDrawPolygonCommand* cmd) override; void DrawLine(const GPUBackendDrawLineCommand* cmd) override; void DrawRectangle(const GPUBackendDrawRectangleCommand* cmd) override; void FlushRender() override; void DrawingAreaChanged() override; ////////////////////////////////////////////////////////////////////////// // Rasterization ////////////////////////////////////////////////////////////////////////// template void ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x, u8 texcoord_y); template void DrawRectangle(const GPUBackendDrawRectangleCommand* cmd); using DrawRectangleFunction = void (GPU_SW_Backend::*)(const GPUBackendDrawRectangleCommand* cmd); DrawRectangleFunction GetDrawRectangleFunction(bool texture_enable, bool raw_texture_enable, bool transparency_enable); ////////////////////////////////////////////////////////////////////////// // Polygon and line rasterization ported from Mednafen ////////////////////////////////////////////////////////////////////////// struct i_deltas { u32 du_dx, dv_dx; u32 dr_dx, dg_dx, db_dx; u32 du_dy, dv_dy; u32 dr_dy, dg_dy, db_dy; }; struct i_group { u32 u, v; u32 r, g, b; }; template bool CalcIDeltas(i_deltas& idl, const GPUBackendDrawPolygonCommand::Vertex* A, const GPUBackendDrawPolygonCommand::Vertex* B, const GPUBackendDrawPolygonCommand::Vertex* C); template void AddIDeltas_DX(i_group& ig, const i_deltas& idl, u32 count = 1); template void AddIDeltas_DY(i_group& ig, const i_deltas& idl, u32 count = 1); template void DrawSpan(const GPUBackendDrawPolygonCommand* cmd, s32 y, s32 x_start, s32 x_bound, i_group ig, const i_deltas& idl); template void DrawTriangle(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0, const GPUBackendDrawPolygonCommand::Vertex* v1, const GPUBackendDrawPolygonCommand::Vertex* v2); using DrawTriangleFunction = void (GPU_SW_Backend::*)(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0, const GPUBackendDrawPolygonCommand::Vertex* v1, const GPUBackendDrawPolygonCommand::Vertex* v2); DrawTriangleFunction GetDrawTriangleFunction(bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable, bool dithering_enable); template void DrawLine(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0, const GPUBackendDrawLineCommand::Vertex* p1); using DrawLineFunction = void (GPU_SW_Backend::*)(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0, const GPUBackendDrawLineCommand::Vertex* p1); DrawLineFunction GetDrawLineFunction(bool shading_enable, bool transparency_enable, bool dithering_enable); };