mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-12-04 03:25:39 +00:00
8c7a192128
Should've did this in the beginning.
171 lines
7.4 KiB
C++
171 lines
7.4 KiB
C++
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
|
|
#pragma once
|
|
#include "gpu_backend.h"
|
|
#include <array>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
class GPU_SW_Backend final : public GPUBackend
|
|
{
|
|
public:
|
|
GPU_SW_Backend();
|
|
~GPU_SW_Backend() override;
|
|
|
|
bool Initialize(bool force_thread) override;
|
|
void Reset(bool clear_vram) override;
|
|
|
|
ALWAYS_INLINE_RELEASE u16 GetPixel(const u32 x, const u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }
|
|
ALWAYS_INLINE_RELEASE const u16* GetPixelPtr(const u32 x, const u32 y) const { return &m_vram[VRAM_WIDTH * y + x]; }
|
|
ALWAYS_INLINE_RELEASE u16* GetPixelPtr(const u32 x, const u32 y) { return &m_vram[VRAM_WIDTH * y + x]; }
|
|
ALWAYS_INLINE_RELEASE void SetPixel(const u32 x, const u32 y, const u16 value) { m_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<std::array<std::array<u8, 512>, DITHER_MATRIX_SIZE>, DITHER_MATRIX_SIZE>;
|
|
static constexpr DitherLUT ComputeDitherLUT();
|
|
|
|
protected:
|
|
union VRAMPixel
|
|
{
|
|
u16 bits;
|
|
|
|
BitField<u16, u8, 0, 5> r;
|
|
BitField<u16, u8, 5, 5> g;
|
|
BitField<u16, u8, 10, 5> b;
|
|
BitField<u16, bool, 15, 1> c;
|
|
|
|
void Set(u8 r_, u8 g_, u8 b_, bool c_ = false)
|
|
{
|
|
bits = (ZeroExtend16(r_)) | (ZeroExtend16(g_) << 5) | (ZeroExtend16(b_) << 10) | (static_cast<u16>(c_) << 15);
|
|
}
|
|
|
|
void ClampAndSet(u8 r_, u8 g_, u8 b_, bool c_ = false)
|
|
{
|
|
Set(std::min<u8>(r_, 0x1F), std::min<u8>(g_, 0x1F), std::min<u8>(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<u16>(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<u16>(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<u8>(std::clamp<s32>(static_cast<s32>(ZeroExtend32(r8)) + offset, 0, 255));
|
|
g8 = static_cast<u8>(std::clamp<s32>(static_cast<s32>(ZeroExtend32(g8)) + offset, 0, 255));
|
|
b8 = static_cast<u8>(std::clamp<s32>(static_cast<s32>(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<u8, u8> UnpackTexcoord(u16 texcoord)
|
|
{
|
|
return std::make_tuple(static_cast<u8>(texcoord), static_cast<u8>(texcoord >> 8));
|
|
}
|
|
|
|
static constexpr std::tuple<u8, u8, u8> UnpackColorRGB24(u32 rgb24)
|
|
{
|
|
return std::make_tuple(static_cast<u8>(rgb24), static_cast<u8>(rgb24 >> 8), static_cast<u8>(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<bool texture_enable, bool raw_texture_enable, bool transparency_enable, bool dithering_enable>
|
|
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<bool texture_enable, bool raw_texture_enable, bool transparency_enable>
|
|
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 shading_enable, bool texture_enable>
|
|
bool CalcIDeltas(i_deltas& idl, const GPUBackendDrawPolygonCommand::Vertex* A,
|
|
const GPUBackendDrawPolygonCommand::Vertex* B, const GPUBackendDrawPolygonCommand::Vertex* C);
|
|
|
|
template<bool shading_enable, bool texture_enable>
|
|
void AddIDeltas_DX(i_group& ig, const i_deltas& idl, u32 count = 1);
|
|
|
|
template<bool shading_enable, bool texture_enable>
|
|
void AddIDeltas_DY(i_group& ig, const i_deltas& idl, u32 count = 1);
|
|
|
|
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
|
bool dithering_enable>
|
|
void DrawSpan(const GPUBackendDrawPolygonCommand* cmd, s32 y, s32 x_start, s32 x_bound, i_group ig,
|
|
const i_deltas& idl);
|
|
|
|
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
|
bool dithering_enable>
|
|
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<bool shading_enable, bool transparency_enable, bool dithering_enable>
|
|
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);
|
|
|
|
std::array<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram;
|
|
};
|