Duckstation/src/core/gpu.h

458 lines
14 KiB
C
Raw Normal View History

#pragma once
#include "common/bitfield.h"
2019-09-20 13:40:19 +00:00
#include "timers.h"
#include "types.h"
#include <array>
#include <deque>
2019-10-22 13:07:51 +00:00
#include <memory>
#include <tuple>
#include <vector>
2019-09-14 10:28:47 +00:00
class StateWrapper;
2019-09-12 14:18:13 +00:00
class System;
class DMA;
2019-09-17 14:22:41 +00:00
class InterruptController;
2019-09-20 13:40:19 +00:00
class Timers;
class GPU
{
public:
enum : u32
{
VRAM_WIDTH = 1024,
VRAM_HEIGHT = 512,
VRAM_SIZE = VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16),
TEXTURE_PAGE_WIDTH = 256,
TEXTURE_PAGE_HEIGHT = 256,
DOT_TIMER_INDEX = 0,
HBLANK_TIMER_INDEX = 1
};
GPU();
2019-09-12 02:53:04 +00:00
virtual ~GPU();
2019-09-20 13:40:19 +00:00
virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers);
2019-09-12 02:53:04 +00:00
virtual void Reset();
2019-09-14 10:28:47 +00:00
virtual bool DoState(StateWrapper& sw);
// Graphics API state reset/restore - call when drawing the UI etc.
virtual void ResetGraphicsAPIState();
virtual void RestoreGraphicsAPIState();
// Render statistics debug window.
void DrawDebugStateWindow();
virtual void DrawRendererStatsWindow();
// MMIO access
u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value);
2019-09-11 04:59:41 +00:00
// DMA access
void DMARead(u32* words, u32 word_count);
void DMAWrite(const u32* words, u32 word_count);
2019-09-11 04:59:41 +00:00
// Resolution scaling.
u32 GetResolutionScale() const { return m_resolution_scale; }
u32 GetMaxResolutionScale() const { return m_max_resolution_scale; }
virtual void UpdateResolutionScale();
2019-09-12 02:53:04 +00:00
// Ticks for hblank/vblank.
2019-09-17 04:25:25 +00:00
void Execute(TickCount ticks);
// gpu_hw_opengl.cpp
static std::unique_ptr<GPU> CreateHardwareOpenGLRenderer();
2019-10-26 02:57:35 +00:00
// gpu_sw.cpp
static std::unique_ptr<GPU> CreateSoftwareRenderer();
2019-09-12 02:53:04 +00:00
protected:
// Helper/format conversion functions.
static constexpr u8 Convert5To8(u8 x5) { return (x5 << 3) | (x5 & 7); }
static constexpr u8 Convert8To5(u8 x8) { return (x8 >> 3); }
static constexpr u32 RGBA5551ToRGBA8888(u16 color)
{
u8 r = Truncate8(color & 31);
u8 g = Truncate8((color >> 5) & 31);
u8 b = Truncate8((color >> 10) & 31);
u8 a = Truncate8((color >> 15) & 1);
// 00012345 -> 1234545
b = (b << 3) | (b & 0b111);
g = (g << 3) | (g & 0b111);
r = (r << 3) | (r & 0b111);
a = a ? 255 : 0;
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
}
static constexpr u16 RGBA8888ToRGBA5551(u32 color)
{
const u16 r = Truncate16((color >> 3) & 0x1Fu);
const u16 g = Truncate16((color >> 11) & 0x1Fu);
const u16 b = Truncate16((color >> 19) & 0x1Fu);
const u16 a = Truncate16((color >> 31) & 0x01u);
return r | (g << 5) | (b << 10) | (a << 15);
}
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));
}
static constexpr u32 PackColorRGB24(u8 r, u8 g, u8 b)
{
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16);
}
static bool DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride, const void* buffer,
bool remove_alpha);
2019-09-11 04:59:41 +00:00
enum class DMADirection : u32
{
Off = 0,
FIFO = 1,
CPUtoGP0 = 2,
GPUREADtoCPU = 3
};
2019-09-12 02:53:04 +00:00
enum class Primitive : u8
{
Reserved = 0,
Polygon = 1,
Line = 2,
Rectangle = 3
};
enum class DrawRectangleSize : u8
{
Variable = 0,
R1x1 = 1,
R8x8 = 2,
R16x16 = 3
};
2019-11-01 11:47:45 +00:00
enum class TextureMode : u8
{
Palette4Bit = 0,
Palette8Bit = 1,
Direct16Bit = 2,
2019-11-01 11:47:45 +00:00
Reserved_Direct16Bit = 3,
// Not register values.
RawTextureBit = 4,
RawPalette4Bit = RawTextureBit | Palette4Bit,
RawPalette8Bit = RawTextureBit | Palette8Bit,
RawDirect16Bit = RawTextureBit | Direct16Bit,
Reserved_RawDirect16Bit = RawTextureBit | Reserved_Direct16Bit,
Disabled = 8 // Not a register value
};
2019-11-01 11:47:45 +00:00
IMPLEMENT_STATIC_FRIEND_ENUM_CLASS_BITWISE_OPERATORS(TextureMode);
2019-09-18 14:55:06 +00:00
enum class TransparencyMode : u8
{
HalfBackgroundPlusHalfForeground = 0,
BackgroundPlusForeground = 1,
BackgroundMinusForeground = 2,
2019-11-01 11:47:45 +00:00
BackgroundPlusQuarterForeground = 3,
Disabled = 4 // Not a register value
2019-09-18 14:55:06 +00:00
};
2019-09-12 02:53:04 +00:00
union RenderCommand
{
u32 bits;
BitField<u32, u32, 0, 24> color_for_first_vertex;
2019-11-01 11:47:45 +00:00
BitField<u32, bool, 24, 1> raw_texture_enable; // not valid for lines
2019-09-12 02:53:04 +00:00
BitField<u32, bool, 25, 1> transparency_enable;
BitField<u32, bool, 26, 1> texture_enable;
2019-09-12 02:53:04 +00:00
BitField<u32, DrawRectangleSize, 27, 2> rectangle_size; // only for rectangles
BitField<u32, bool, 27, 1> quad_polygon; // only for polygons
BitField<u32, bool, 27, 1> polyline; // only for lines
BitField<u32, bool, 28, 1> shading_enable; // 0 - flat, 1 = gouroud
2019-09-12 02:53:04 +00:00
BitField<u32, Primitive, 29, 21> primitive;
// Returns true if dithering should be enabled. Depends on the primitive type.
bool IsDitheringEnabled() const
{
switch (primitive)
{
case Primitive::Polygon:
return shading_enable || !raw_texture_enable;
case Primitive::Line:
return true;
case Primitive::Rectangle:
default:
return false;
}
}
2019-09-12 02:53:04 +00:00
};
// TODO: Use BitField to do sign extending instead
union VertexPosition
{
u32 bits;
BitField<u32, s32, 0, 12> x;
BitField<u32, s32, 16, 12> y;
2019-09-12 02:53:04 +00:00
};
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;
u8 GetR8() const { return Convert5To8(r); }
u8 GetG8() const { return Convert5To8(g); }
u8 GetB8() const { return Convert5To8(b); }
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);
}
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);
}
};
// 4x4 dither matrix.
static constexpr s32 DITHER_MATRIX[4][4] = {{-4, +0, -3, +1}, // row 0
{+2, -2, +3, -1}, // row 1
{-3, +1, -4, +0}, // row 2
{+4, -1, +2, -2}}; // row 3
void SoftReset();
2019-09-17 04:25:25 +00:00
// Sets dots per scanline
void UpdateCRTCConfig();
// Update ticks for this execution slice
void UpdateSliceTicks();
// Updates dynamic bits in GPUSTAT (ready to send VRAM/ready to receive DMA)
void UpdateGPUSTAT();
2019-09-11 04:59:41 +00:00
u32 ReadGPUREAD();
void WriteGP0(u32 value);
void WriteGP1(u32 value);
void HandleGetGPUInfoCommand(u32 value);
2019-09-12 02:53:04 +00:00
// Rendering in the backend
virtual void UpdateDisplay();
virtual void UpdateDrawingArea();
2019-09-14 10:45:26 +00:00
virtual void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer);
virtual void FillVRAM(u32 x, u32 y, u32 width, u32 height, u16 color);
2019-09-12 15:10:08 +00:00
virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data);
2019-09-17 14:58:30 +00:00
virtual void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height);
2019-10-13 07:33:20 +00:00
virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32* command_ptr);
2019-09-12 02:53:04 +00:00
virtual void FlushRender();
2019-09-12 14:18:13 +00:00
System* m_system = nullptr;
DMA* m_dma = nullptr;
2019-09-17 14:22:41 +00:00
InterruptController* m_interrupt_controller = nullptr;
2019-09-20 13:40:19 +00:00
Timers* m_timers = nullptr;
// Resolution scale.
u32 m_resolution_scale = 1;
u32 m_max_resolution_scale = 1;
union GPUSTAT
{
u32 bits;
BitField<u32, u8, 0, 4> texture_page_x_base;
BitField<u32, u8, 4, 1> texture_page_y_base;
2019-09-18 14:55:06 +00:00
BitField<u32, TransparencyMode, 5, 2> semi_transparency_mode;
2019-11-01 11:47:45 +00:00
BitField<u32, TextureMode, 7, 2> texture_color_mode;
BitField<u32, bool, 9, 1> dither_enable;
BitField<u32, bool, 10, 1> draw_to_display_area;
BitField<u32, bool, 11, 1> draw_set_mask_bit;
BitField<u32, bool, 12, 1> draw_to_masked_pixels;
BitField<u32, bool, 13, 1> interlaced_field;
BitField<u32, bool, 14, 1> reverse_flag;
BitField<u32, bool, 15, 1> texture_disable;
BitField<u32, u8, 16, 1> horizontal_resolution_2;
BitField<u32, u8, 17, 2> horizontal_resolution_1;
2019-10-15 12:36:10 +00:00
BitField<u32, bool, 19, 1> vertical_resolution;
BitField<u32, bool, 20, 1> pal_mode;
BitField<u32, bool, 21, 1> display_area_color_depth_24;
BitField<u32, bool, 22, 1> vertical_interlace;
BitField<u32, bool, 23, 1> display_disable;
BitField<u32, bool, 24, 1> interrupt_request;
BitField<u32, bool, 25, 1> dma_data_request;
BitField<u32, bool, 26, 1> ready_to_recieve_cmd;
BitField<u32, bool, 27, 1> ready_to_send_vram;
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
2019-09-11 04:59:41 +00:00
BitField<u32, DMADirection, 29, 2> dma_direction;
BitField<u32, bool, 31, 1> drawing_even_line;
} m_GPUSTAT = {};
struct RenderState
{
static constexpr u16 PAGE_ATTRIBUTE_TEXTURE_PAGE_MASK = UINT16_C(0b0000000000011111);
static constexpr u16 PAGE_ATTRIBUTE_MASK = UINT16_C(0b0000000111111111);
static constexpr u16 PALETTE_ATTRIBUTE_MASK = UINT16_C(0b0111111111111111);
2019-10-05 13:25:06 +00:00
static constexpr u32 TEXTURE_WINDOW_MASK = UINT16_C(0b11111111111111111111);
// decoded values
u32 texture_page_x;
u32 texture_page_y;
u32 texture_palette_x;
u32 texture_palette_y;
2019-11-01 11:47:45 +00:00
TextureMode texture_color_mode;
TransparencyMode transparency_mode;
u8 texture_window_mask_x; // in 8 pixel steps
u8 texture_window_mask_y; // in 8 pixel steps
u8 texture_window_offset_x; // in 8 pixel steps
u8 texture_window_offset_y; // in 8 pixel steps
bool texture_x_flip;
bool texture_y_flip;
// original values
u16 texpage_attribute; // from register in rectangle modes/vertex in polygon modes
u16 texlut_attribute; // from vertex
2019-10-05 13:25:06 +00:00
u32 texture_window_value;
bool texture_page_changed = false;
2019-10-05 13:25:06 +00:00
bool texture_window_changed = false;
bool IsTexturePageChanged() const { return texture_page_changed; }
void ClearTexturePageChangedFlag() { texture_page_changed = false; }
2019-10-05 13:25:06 +00:00
bool IsTextureWindowChanged() const { return texture_window_changed; }
void ClearTextureWindowChangedFlag() { texture_window_changed = false; }
void SetFromPolygonTexcoord(u32 texcoord0, u32 texcoord1);
void SetFromRectangleTexcoord(u32 texcoord);
void SetFromPageAttribute(u16 value);
void SetFromPaletteAttribute(u16 value);
2019-10-05 13:25:06 +00:00
void SetTextureWindow(u32 value);
} m_render_state = {};
struct DrawingArea
{
u32 left, top;
u32 right, bottom;
} m_drawing_area = {};
struct DrawingOffset
{
s32 x;
s32 y;
} m_drawing_offset = {};
2019-09-17 04:25:25 +00:00
struct CRTCState
{
struct Regs
{
static constexpr u32 DISPLAY_ADDRESS_START_MASK = 0b111'11111111'11111111;
static constexpr u32 HORIZONTAL_DISPLAY_RANGE_MASK = 0b11111111'11111111'11111111;
static constexpr u32 VERTICAL_DISPLAY_RANGE_MASK = 0b1111'11111111'11111111;
union
{
u32 display_address_start;
BitField<u32, u32, 0, 10> X;
BitField<u32, u32, 10, 9> Y;
};
union
{
u32 horizontal_display_range;
BitField<u32, u32, 0, 12> X1;
BitField<u32, u32, 12, 12> X2;
};
union
{
u32 vertical_display_range;
BitField<u32, u32, 0, 10> Y1;
BitField<u32, u32, 10, 10> Y2;
};
} regs;
TickCount dot_clock_divider;
u32 display_width;
u32 display_height;
2019-09-17 04:25:25 +00:00
TickCount ticks_per_scanline;
TickCount visible_ticks_per_scanline;
u32 visible_scanlines_per_frame;
2019-09-17 04:25:25 +00:00
u32 total_scanlines_per_frame;
TickCount fractional_ticks;
TickCount current_tick_in_scanline;
u32 current_scanline;
float display_aspect_ratio;
2019-09-17 04:25:25 +00:00
bool in_hblank;
bool in_vblank;
} m_crtc_state = {};
2019-10-13 07:33:20 +00:00
std::vector<u32> m_GP0_buffer;
std::deque<u32> m_GPUREAD_buffer;
private:
using GP0CommandHandler = bool (GPU::*)(const u32*&, u32);
using GP0CommandHandlerTable = std::array<GP0CommandHandler, 256>;
2019-10-22 13:07:51 +00:00
static GP0CommandHandlerTable GenerateGP0CommandHandlerTable();
// Rendering commands, returns false if not enough data is provided
bool HandleUnknownGP0Command(const u32*& command_ptr, u32 command_size);
bool HandleNOPCommand(const u32*& command_ptr, u32 command_size);
bool HandleClearCacheCommand(const u32*& command_ptr, u32 command_size);
bool HandleInterruptRequestCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetDrawModeCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetTextureWindowCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_size);
bool HandleSetMaskBitCommand(const u32*& command_ptr, u32 command_size);
bool HandleRenderCommand(const u32*& command_ptr, u32 command_size);
bool HandleFillRectangleCommand(const u32*& command_ptr, u32 command_size);
bool HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 command_size);
bool HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 command_size);
bool HandleCopyRectangleVRAMToVRAMCommand(const u32*& command_ptr, u32 command_size);
static const GP0CommandHandlerTable s_GP0_command_handler_table;
};