mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-26 23:50:31 +00:00
GPU: Partially implemented texture support
This commit is contained in:
parent
cfe361c1a6
commit
ae43cc838b
@ -121,7 +121,7 @@ bool Program::Link()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::Bind()
|
||||
void Program::Bind() const
|
||||
{
|
||||
glUseProgram(m_program_id);
|
||||
}
|
||||
@ -152,15 +152,95 @@ u32 Program::RegisterUniform(const char* name)
|
||||
return id;
|
||||
}
|
||||
|
||||
void Program::Uniform1ui(u32 index, u32 value)
|
||||
void Program::Uniform1ui(u32 index, u32 x) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1ui(location, value);
|
||||
glUniform1ui(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform4f(u32 index, float x, float y, float z, float w)
|
||||
void Program::Uniform2ui(u32 index, u32 x, u32 y) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2ui(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3ui(u32 index, u32 x, u32 y, u32 z) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3ui(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4ui(u32 index, u32 x, u32 y, u32 z, u32 w) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4ui(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1i(u32 index, s32 x) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1i(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2i(u32 index, s32 x, s32 y) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2i(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3i(u32 index, s32 x, s32 y, s32 z) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3i(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4i(u32 index, s32 x, s32 y, s32 z, s32 w) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4i(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1f(u32 index, float x) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1f(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2f(u32 index, float x, float y) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2f(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3f(u32 index, float x, float y, float z) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3f(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4f(u32 index, float x, float y, float z, float w) const
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
|
@ -23,13 +23,23 @@ public:
|
||||
|
||||
bool Link();
|
||||
|
||||
void Bind();
|
||||
void Bind() const;
|
||||
|
||||
void Destroy();
|
||||
|
||||
u32 RegisterUniform(const char* name);
|
||||
void Uniform1ui(u32 index, u32 value);
|
||||
void Uniform4f(u32 index, float x, float y, float z, float w);
|
||||
void Uniform1ui(u32 index, u32 x) const;
|
||||
void Uniform2ui(u32 index, u32 x, u32 y) const;
|
||||
void Uniform3ui(u32 index, u32 x, u32 y, u32 z) const;
|
||||
void Uniform4ui(u32 index, u32 x, u32 y, u32 z, u32 w) const;
|
||||
void Uniform1i(u32 index, s32 x) const;
|
||||
void Uniform2i(u32 index, s32 x, s32 y) const;
|
||||
void Uniform3i(u32 index, s32 x, s32 y, s32 z) const;
|
||||
void Uniform4i(u32 index, s32 x, s32 y, s32 z, s32 w) const;
|
||||
void Uniform1f(u32 index, float x) const;
|
||||
void Uniform2f(u32 index, float x, float y) const;
|
||||
void Uniform3f(u32 index, float x, float y, float z) const;
|
||||
void Uniform4f(u32 index, float x, float y, float z, float w) const;
|
||||
|
||||
private:
|
||||
GLuint m_program_id = 0;
|
||||
|
@ -26,4 +26,9 @@ void Texture::Bind()
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
}
|
||||
|
||||
void Texture::Unbind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
} // namespace GL
|
@ -14,6 +14,7 @@ public:
|
||||
u32 GetHeight() const { return m_height; }
|
||||
|
||||
void Bind();
|
||||
static void Unbind();
|
||||
|
||||
private:
|
||||
GLuint m_id;
|
||||
|
@ -77,6 +77,7 @@ bool SDLInterface::CreateGLContext()
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -132,7 +133,7 @@ void main()
|
||||
|
||||
m_display_program.RegisterUniform("samp0");
|
||||
m_display_program.Bind();
|
||||
m_display_program.Uniform1ui(0, 0);
|
||||
m_display_program.Uniform1i(0, 0);
|
||||
|
||||
glGenVertexArrays(1, &m_display_vao);
|
||||
return true;
|
||||
@ -410,8 +411,6 @@ void SDLInterface::SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 off
|
||||
m_display_texture_width = width;
|
||||
m_display_texture_height = height;
|
||||
m_display_texture_changed = true;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
void SDLInterface::RenderOSDMessages()
|
||||
@ -476,8 +475,6 @@ void SDLInterface::DoSaveState(u32 index)
|
||||
|
||||
void SDLInterface::Run()
|
||||
{
|
||||
Timer last_render_time;
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
for (;;)
|
||||
@ -489,12 +486,7 @@ void SDLInterface::Run()
|
||||
break;
|
||||
}
|
||||
|
||||
while (!m_display_texture_changed || last_render_time.GetTimeSeconds() < (1.0f / 60.0f))
|
||||
{
|
||||
m_system->RunFrame();
|
||||
}
|
||||
|
||||
// Render();
|
||||
last_render_time.Reset();
|
||||
m_system->RunFrame();
|
||||
Render();
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +116,10 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||
void DMA::SetRequest(Channel channel, bool request)
|
||||
{
|
||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||
cs.request = request;
|
||||
if (cs.request == request)
|
||||
return;
|
||||
|
||||
cs.request = request;
|
||||
if (CanRunChannel(channel))
|
||||
RunDMA(channel);
|
||||
}
|
||||
@ -199,7 +201,7 @@ void DMA::RunDMA(Channel channel)
|
||||
|
||||
const u32 word_count = header >> 24;
|
||||
const u32 next_address = header & UINT32_C(0xFFFFFF);
|
||||
Log_DebugPrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", current_address,
|
||||
Log_TracePrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", current_address,
|
||||
word_count * UINT32_C(4), word_count, next_address);
|
||||
current_address += sizeof(header);
|
||||
|
||||
|
149
src/pse/gpu.cpp
149
src/pse/gpu.cpp
@ -2,6 +2,7 @@
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "bus.h"
|
||||
#include "dma.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
GPU::GPU() = default;
|
||||
@ -24,14 +25,40 @@ void GPU::Reset()
|
||||
void GPU::SoftReset()
|
||||
{
|
||||
m_GPUSTAT.bits = 0x14802000;
|
||||
UpdateDMARequest();
|
||||
UpdateGPUSTAT();
|
||||
}
|
||||
|
||||
void GPU::UpdateDMARequest()
|
||||
void GPU::UpdateGPUSTAT()
|
||||
{
|
||||
const bool request = m_GPUSTAT.dma_direction != DMADirection::Off;
|
||||
m_GPUSTAT.dma_data_request = request;
|
||||
m_dma->SetRequest(DMA::Channel::GPU, request);
|
||||
m_GPUSTAT.ready_to_send_vram = !m_GPUREAD_buffer.empty();
|
||||
m_GPUSTAT.ready_to_recieve_cmd = m_GPUREAD_buffer.empty();
|
||||
m_GPUSTAT.ready_to_recieve_dma = m_GPUREAD_buffer.empty();
|
||||
|
||||
bool dma_request;
|
||||
switch (m_GPUSTAT.dma_direction)
|
||||
{
|
||||
case DMADirection::Off:
|
||||
dma_request = false;
|
||||
break;
|
||||
|
||||
case DMADirection::FIFO:
|
||||
dma_request = true; // FIFO not full/full
|
||||
break;
|
||||
|
||||
case DMADirection::CPUtoGP0:
|
||||
dma_request = m_GPUSTAT.ready_to_recieve_dma;
|
||||
break;
|
||||
|
||||
case DMADirection::GPUREADtoCPU:
|
||||
dma_request = m_GPUSTAT.ready_to_send_vram;
|
||||
break;
|
||||
|
||||
default:
|
||||
dma_request = false;
|
||||
break;
|
||||
}
|
||||
m_GPUSTAT.dma_data_request = dma_request;
|
||||
m_dma->SetRequest(DMA::Channel::GPU, dma_request);
|
||||
}
|
||||
|
||||
u32 GPU::ReadRegister(u32 offset)
|
||||
@ -96,8 +123,16 @@ void GPU::DMAWrite(u32 value)
|
||||
|
||||
u32 GPU::ReadGPUREAD()
|
||||
{
|
||||
Log_ErrorPrintf("GPUREAD not implemented");
|
||||
return UINT32_C(0xFFFFFFFF);
|
||||
if (m_GPUREAD_buffer.empty())
|
||||
{
|
||||
Log_ErrorPrintf("GPUREAD read while buffer is empty");
|
||||
return UINT32_C(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
const u32 value = m_GPUREAD_buffer.front();
|
||||
m_GPUREAD_buffer.pop_front();
|
||||
UpdateGPUSTAT();
|
||||
return value;
|
||||
}
|
||||
|
||||
void GPU::WriteGP0(u32 value)
|
||||
@ -107,6 +142,7 @@ void GPU::WriteGP0(u32 value)
|
||||
|
||||
const u8 command = Truncate8(m_GP0_command[0] >> 24);
|
||||
const u32 param = m_GP0_command[0] & UINT32_C(0x00FFFFFF);
|
||||
UpdateGPUSTAT();
|
||||
|
||||
if (command >= 0x20 && command <= 0x7F)
|
||||
{
|
||||
@ -128,6 +164,13 @@ void GPU::WriteGP0(u32 value)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC0: // Copy Rectnagle VRAM->CPU
|
||||
{
|
||||
if (!HandleCopyRectangleVRAMToCPUCommand())
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE1: // Set draw mode
|
||||
{
|
||||
// 0..10 bits match GPUSTAT
|
||||
@ -136,6 +179,7 @@ void GPU::WriteGP0(u32 value)
|
||||
m_GPUSTAT.texture_disable = (param & (UINT32_C(1) << 11)) != 0;
|
||||
m_texture_config.x_flip = (param & (UINT32_C(1) << 12)) != 0;
|
||||
m_texture_config.y_flip = (param & (UINT32_C(1) << 13)) != 0;
|
||||
m_texture_config.SetColorMode(m_GPUSTAT.texture_color_mode);
|
||||
Log_DebugPrintf("Set draw mode %08X", param);
|
||||
}
|
||||
break;
|
||||
@ -195,6 +239,7 @@ void GPU::WriteGP0(u32 value)
|
||||
}
|
||||
|
||||
m_GP0_command.clear();
|
||||
UpdateGPUSTAT();
|
||||
}
|
||||
|
||||
void GPU::WriteGP1(u32 value)
|
||||
@ -207,7 +252,15 @@ void GPU::WriteGP1(u32 value)
|
||||
{
|
||||
m_GPUSTAT.dma_direction = static_cast<DMADirection>(param);
|
||||
Log_DebugPrintf("DMA direction <- 0x%02X", static_cast<u32>(m_GPUSTAT.dma_direction.GetValue()));
|
||||
UpdateDMARequest();
|
||||
UpdateGPUSTAT();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x05: // Set display start address
|
||||
{
|
||||
// TODO: Remove this later..
|
||||
FlushRender();
|
||||
UpdateDisplay();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -319,12 +372,90 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand()
|
||||
return true;
|
||||
}
|
||||
|
||||
FlushRender();
|
||||
UpdateVRAM(dst_x, dst_y, copy_width, copy_height, &m_GP0_command[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU::HandleCopyRectangleVRAMToCPUCommand()
|
||||
{
|
||||
if (m_GP0_command.size() < 3)
|
||||
return false;
|
||||
|
||||
const u32 width = m_GP0_command[2] & UINT32_C(0xFFFF);
|
||||
const u32 height = m_GP0_command[2] >> 16;
|
||||
const u32 num_pixels = width * height;
|
||||
const u32 num_words = ((num_pixels + 1) / 2);
|
||||
const u32 src_x = m_GP0_command[1] & UINT32_C(0xFFFF);
|
||||
const u32 src_y = m_GP0_command[1] >> 16;
|
||||
|
||||
Log_DebugPrintf("Copy rectangle from VRAM to CPU offset=(%u,%u), size=(%u,%u)", src_x, src_y, width, height);
|
||||
|
||||
if ((src_x + width) > VRAM_WIDTH || (src_x + height) > VRAM_HEIGHT)
|
||||
{
|
||||
Panic("Out of bounds VRAM copy");
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Implement.
|
||||
for (u32 i = 0; i < num_words; i++)
|
||||
m_GPUREAD_buffer.push_back(0);
|
||||
|
||||
// Is this correct?
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU::UpdateDisplay()
|
||||
{
|
||||
m_texture_config.page_changed = true;
|
||||
m_system->IncrementFrameNumber();
|
||||
}
|
||||
|
||||
void GPU::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) {}
|
||||
|
||||
void GPU::DispatchRenderCommand(RenderCommand rc, u32 num_vertices) {}
|
||||
|
||||
void GPU::FlushRender() {}
|
||||
void GPU::FlushRender() {}
|
||||
|
||||
void GPU::TextureConfig::SetColorMode(TextureColorMode new_color_mode)
|
||||
{
|
||||
if (new_color_mode == TextureColorMode::Reserved_Direct16Bit)
|
||||
new_color_mode = TextureColorMode::Direct16Bit;
|
||||
|
||||
if (color_mode == new_color_mode)
|
||||
return;
|
||||
|
||||
color_mode = new_color_mode;
|
||||
}
|
||||
|
||||
void GPU::TextureConfig::SetFromPolygonTexcoord(u32 texcoord0, u32 texcoord1)
|
||||
{
|
||||
SetFromPaletteAttribute(Truncate16(texcoord0 >> 16));
|
||||
SetFromPageAttribute(Truncate16(texcoord1 >> 16));
|
||||
}
|
||||
|
||||
void GPU::TextureConfig::SetFromRectangleTexcoord(u32 texcoord)
|
||||
{
|
||||
SetFromPaletteAttribute(Truncate16(texcoord >> 16));
|
||||
}
|
||||
|
||||
void GPU::TextureConfig::SetFromPageAttribute(u16 value)
|
||||
{
|
||||
value &= PAGE_ATTRIBUTE_MASK;
|
||||
if (page_attribute == value)
|
||||
return;
|
||||
|
||||
base_x = static_cast<s32>(ZeroExtend32(value & UINT16_C(0x1FF)) * UINT32_C(64));
|
||||
base_y = static_cast<s32>(ZeroExtend32((value >> 11) & UINT16_C(1)) * UINT32_C(512));
|
||||
page_changed = true;
|
||||
}
|
||||
|
||||
void GPU::TextureConfig::SetFromPaletteAttribute(u16 value)
|
||||
{
|
||||
value &= PALETTE_ATTRIBUTE_MASK;
|
||||
if (palette_attribute == value)
|
||||
return;
|
||||
|
||||
palette_x = static_cast<s32>(ZeroExtend32(value & UINT16_C(0x3F)) * UINT32_C(16));
|
||||
palette_y = static_cast<s32>(ZeroExtend32((value >> 6) & UINT16_C(0x1FF)));
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "common/bitfield.h"
|
||||
#include "types.h"
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
class System;
|
||||
class Bus;
|
||||
@ -30,6 +32,8 @@ protected:
|
||||
static constexpr u32 VRAM_WIDTH = 1024;
|
||||
static constexpr u32 VRAM_HEIGHT = 512;
|
||||
static constexpr u32 VRAM_SIZE = VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16);
|
||||
static constexpr u32 TEXTURE_PAGE_WIDTH = 256;
|
||||
static constexpr u32 TEXTURE_PAGE_HEIGHT = 256;
|
||||
|
||||
static constexpr s32 S11ToS32(u32 value)
|
||||
{
|
||||
@ -63,17 +67,26 @@ protected:
|
||||
R16x16 = 3
|
||||
};
|
||||
|
||||
enum class TextureColorMode : u8
|
||||
{
|
||||
Palette4Bit = 0,
|
||||
Palette8Bit = 1,
|
||||
Direct16Bit = 2,
|
||||
Reserved_Direct16Bit = 3
|
||||
};
|
||||
|
||||
union RenderCommand
|
||||
{
|
||||
u32 bits;
|
||||
|
||||
BitField<u32, u32, 0, 23> color_for_first_vertex;
|
||||
BitField<u32, bool, 24, 1> texture_enable; // not valid for lines
|
||||
BitField<u32, bool, 24, 1> texture_blending_raw; // not valid for lines
|
||||
BitField<u32, bool, 25, 1> transparency_enable;
|
||||
BitField<u32, bool, 26, 1> texture_enable;
|
||||
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
|
||||
BitField<u32, bool, 28, 1> shading_enable; // 0 - flat, 1 = gouroud
|
||||
BitField<u32, Primitive, 29, 21> primitive;
|
||||
};
|
||||
|
||||
@ -90,7 +103,10 @@ protected:
|
||||
};
|
||||
|
||||
void SoftReset();
|
||||
void UpdateDMARequest();
|
||||
|
||||
// Updates dynamic bits in GPUSTAT (ready to send VRAM/ready to receive DMA)
|
||||
void UpdateGPUSTAT();
|
||||
|
||||
u32 ReadGPUREAD();
|
||||
void WriteGP0(u32 value);
|
||||
void WriteGP1(u32 value);
|
||||
@ -98,8 +114,10 @@ protected:
|
||||
// Rendering commands, returns false if not enough data is provided
|
||||
bool HandleRenderCommand();
|
||||
bool HandleCopyRectangleCPUToVRAMCommand();
|
||||
bool HandleCopyRectangleVRAMToCPUCommand();
|
||||
|
||||
// Rendering in the backend
|
||||
virtual void UpdateDisplay();
|
||||
virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data);
|
||||
virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices);
|
||||
virtual void FlushRender();
|
||||
@ -114,7 +132,7 @@ protected:
|
||||
BitField<u32, u8, 0, 4> texture_page_x_base;
|
||||
BitField<u32, u8, 4, 1> texture_page_y_base;
|
||||
BitField<u32, u8, 5, 2> semi_transparency;
|
||||
BitField<u32, u8, 7, 2> texture_page_colors;
|
||||
BitField<u32, TextureColorMode, 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;
|
||||
@ -140,6 +158,33 @@ protected:
|
||||
|
||||
struct TextureConfig
|
||||
{
|
||||
static constexpr u16 PAGE_ATTRIBUTE_MASK = UINT16_C(0b0000100111111111);
|
||||
static constexpr u16 PALETTE_ATTRIBUTE_MASK = UINT16_C(0b0111111111111111);
|
||||
|
||||
// decoded values
|
||||
s32 base_x;
|
||||
s32 base_y;
|
||||
s32 palette_x;
|
||||
s32 palette_y;
|
||||
|
||||
// original values
|
||||
u16 page_attribute; // from register in rectangle modes/vertex in polygon modes
|
||||
u16 palette_attribute; // from vertex
|
||||
TextureColorMode color_mode; // from register/vertex in polygon modes
|
||||
|
||||
bool page_changed = false;
|
||||
|
||||
bool IsPageChanged() const { return page_changed; }
|
||||
void ClearPageChangedFlag() { page_changed = false; }
|
||||
|
||||
void SetColorMode(TextureColorMode new_color_mode);
|
||||
|
||||
void SetFromPolygonTexcoord(u32 texcoord0, u32 texcoord1);
|
||||
void SetFromRectangleTexcoord(u32 texcoord);
|
||||
|
||||
void SetFromPageAttribute(u16 value);
|
||||
void SetFromPaletteAttribute(u16 value);
|
||||
|
||||
u8 window_mask_x; // in 8 pixel steps
|
||||
u8 window_mask_y; // in 8 pixel steps
|
||||
u8 window_offset_x; // in 8 pixel steps
|
||||
@ -160,5 +205,11 @@ protected:
|
||||
s32 y;
|
||||
} m_drawing_offset = {};
|
||||
|
||||
struct TexturePageConfig
|
||||
{
|
||||
|
||||
} m_texture_page_config = {};
|
||||
|
||||
std::vector<u32> m_GP0_command;
|
||||
std::deque<u32> m_GPUREAD_buffer;
|
||||
};
|
||||
|
@ -17,8 +17,8 @@ void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices)
|
||||
const bool textured = rc.texture_enable;
|
||||
|
||||
// if we're drawing quads, we need to create a degenerate triangle to restart the triangle strip
|
||||
if (rc.quad_polygon && !m_vertex_staging.empty())
|
||||
m_vertex_staging.push_back(m_vertex_staging.back());
|
||||
if (rc.quad_polygon && !m_batch_vertices.empty())
|
||||
m_batch_vertices.push_back(m_batch_vertices.back());
|
||||
|
||||
u32 buffer_pos = 1;
|
||||
for (u32 i = 0; i < num_vertices; i++)
|
||||
@ -30,12 +30,20 @@ void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices)
|
||||
hw_vert.x = vp.x();
|
||||
hw_vert.y = vp.y();
|
||||
|
||||
// excluding lower-right coordinates
|
||||
if ((i & UINT32_C(1)) != 0)
|
||||
hw_vert.x--;
|
||||
if ((i & UINT32_C(2)) != 0)
|
||||
hw_vert.y--;
|
||||
|
||||
if (textured)
|
||||
hw_vert.texcoord = (m_GP0_command[buffer_pos++] & UINT32_C(0x0000FFFF));
|
||||
hw_vert.texcoord = Truncate16(m_GP0_command[buffer_pos++]);
|
||||
else
|
||||
hw_vert.texcoord = 0;
|
||||
|
||||
m_vertex_staging.push_back(hw_vert);
|
||||
hw_vert.padding = 0;
|
||||
|
||||
m_batch_vertices.push_back(hw_vert);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -62,35 +70,73 @@ void GPU_HW::CalcScissorRect(int* left, int* top, int* right, int* bottom)
|
||||
*bottom = m_drawing_area.bottom_right_y;
|
||||
}
|
||||
|
||||
static void DefineMacro(std::stringstream& ss, const char* name, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
ss << "#define " << name << " 1\n";
|
||||
else
|
||||
ss << "/* #define " << name << " 0 */\n";
|
||||
}
|
||||
|
||||
void GPU_HW::GenerateShaderHeader(std::stringstream& ss)
|
||||
{
|
||||
ss << "#version 330 core\n\n";
|
||||
ss << "const vec2 vram_size = vec2(float(" << VRAM_WIDTH << "), float(" << VRAM_HEIGHT << "));\n";
|
||||
ss << "const vec2 rcp_vram_size = vec2(1.0, 1.0) / vram_size;\n";
|
||||
ss << R"(
|
||||
|
||||
float fixYCoord(float y)
|
||||
{
|
||||
return 1.0 - rcp_vram_size.y - y;
|
||||
}
|
||||
|
||||
uint RGBA8ToRGBA5551(vec4 v)
|
||||
{
|
||||
uint r = uint(v.r * 255.0) >> 3;
|
||||
uint g = uint(v.g * 255.0) >> 3;
|
||||
uint b = uint(v.b * 255.0) >> 3;
|
||||
uint a = (v.a != 0.0) ? 1u : 0u;
|
||||
return (r) | (g << 5) | (b << 10) | (a << 15);
|
||||
}
|
||||
|
||||
vec4 RGBA5551ToRGBA8(uint v)
|
||||
{
|
||||
uint r = (v & 0x1Fu);
|
||||
uint g = ((v >> 5) & 0x1Fu);
|
||||
uint b = ((v >> 10) & 0x1Fu);
|
||||
uint a = ((v >> 15) & 0x01u);
|
||||
|
||||
return vec4(float(r) * 255.0, float(g) * 255.0, float(b) * 255.0, float(a) * 255.0);
|
||||
}
|
||||
)";
|
||||
}
|
||||
|
||||
std::string GPU_HW::GenerateVertexShader(bool textured)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "#version 330 core\n";
|
||||
if (textured)
|
||||
ss << "#define TEXTURED 1\n";
|
||||
else
|
||||
ss << "/* #define TEXTURED 0 */\n";
|
||||
GenerateShaderHeader(ss);
|
||||
DefineMacro(ss, "TEXTURED", textured);
|
||||
|
||||
ss << R"(
|
||||
in ivec2 a_position;
|
||||
in vec4 a_color;
|
||||
in uint a_texcoord;
|
||||
in ivec2 a_pos;
|
||||
in vec4 a_col0;
|
||||
in vec2 a_tex0;
|
||||
|
||||
out vec4 v_color;
|
||||
out vec4 v_col0;
|
||||
#if TEXTURED
|
||||
out vec2 v_texcoord;
|
||||
out vec2 v_tex0;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
// 0..+1023 -> -1..1
|
||||
float pos_x = (float(a_position.x) / 511.5) - 1.0;
|
||||
float pos_y = (float(a_position.y) / -255.5) + 1.0;
|
||||
float pos_x = (float(a_pos.x) / 511.5) - 1.0;
|
||||
float pos_y = (float(a_pos.y) / -255.5) + 1.0;
|
||||
gl_Position = vec4(pos_x, pos_y, 0.0, 1.0);
|
||||
|
||||
v_color = a_color;
|
||||
v_col0 = a_col0;
|
||||
#if TEXTURED
|
||||
v_texcoord = vec2(float(a_texcoord & 0xFFu) / 256.0, float((a_texcoord >> 8) & 0xFFu) / 256.0);
|
||||
v_tex0 = vec2(a_tex0.x / 4, a_tex0.y);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
@ -98,29 +144,154 @@ void main()
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW::GenerateFragmentShader(bool textured)
|
||||
std::string GPU_HW::GenerateFragmentShader(bool textured, bool blending)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "#version 330 core\n";
|
||||
if (textured)
|
||||
ss << "#define TEXTURED 1\n";
|
||||
else
|
||||
ss << "/* #define TEXTURED 0 */\n";
|
||||
GenerateShaderHeader(ss);
|
||||
DefineMacro(ss, "TEXTURED", textured);
|
||||
DefineMacro(ss, "BLENDING", blending);
|
||||
|
||||
ss << R"(
|
||||
in vec4 v_color;
|
||||
in vec4 v_col0;
|
||||
#if TEXTURED
|
||||
in vec2 v_texcoord;
|
||||
in vec2 v_tex0;
|
||||
uniform sampler2D samp0;
|
||||
#endif
|
||||
|
||||
out vec4 ocol0;
|
||||
out vec4 o_col0;
|
||||
|
||||
void main()
|
||||
{
|
||||
ocol0 = v_color;
|
||||
//ocol0 = vec4(1.0, 0.5, 0.5, 1.0);
|
||||
#if TEXTURED
|
||||
vec4 texcol = texture(samp0, v_tex0);
|
||||
#if BLENDING
|
||||
o_col0 = v_col0 * texcol;
|
||||
#else
|
||||
o_col0 = /*v_col0 + */texcol;
|
||||
#endif
|
||||
#else
|
||||
o_col0 = v_col0;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW::GenerateScreenQuadVertexShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
GenerateShaderHeader(ss);
|
||||
ss << R"(
|
||||
|
||||
out vec2 v_tex0;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_tex0 = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
||||
gl_Position = vec4(v_tex0 * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||
gl_Position.y = -gl_Position.y;
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW::GenerateTexturePageProgram(TextureColorMode mode)
|
||||
{
|
||||
const bool is_palette = (mode == GPU::TextureColorMode::Palette4Bit || mode == GPU::TextureColorMode::Palette8Bit);
|
||||
|
||||
std::stringstream ss;
|
||||
GenerateShaderHeader(ss);
|
||||
DefineMacro(ss, "PALETTE", is_palette);
|
||||
DefineMacro(ss, "PALETTE_4_BIT", mode == GPU::TextureColorMode::Palette4Bit);
|
||||
DefineMacro(ss, "PALETTE_8_BIT", mode == GPU::TextureColorMode::Palette8Bit);
|
||||
|
||||
ss << R"(
|
||||
uniform sampler2D samp0;
|
||||
uniform vec2 base_offset;
|
||||
|
||||
#if PALETTE
|
||||
uniform vec2 palette_offset;
|
||||
#endif
|
||||
|
||||
in vec2 v_tex0;
|
||||
out vec4 o_col0;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if PALETTE_4_BIT
|
||||
vec2 local_coords = vec2(v_tex0.x / 4.0, v_tex0.y);
|
||||
#elif PALETTE_8_BIT
|
||||
vec2 local_coords = vec2(v_tex0.x / 2.0, v_tex0.y);
|
||||
#else
|
||||
vec2 local_coords = v_tex0;
|
||||
#endif
|
||||
|
||||
// fixup coords
|
||||
vec2 coords = vec2(local_coords.x + base_offset.x, fixYCoord(local_coords.y + base_offset.y));
|
||||
|
||||
// load colour/palette
|
||||
vec4 color = texture(samp0, coords);
|
||||
|
||||
// apply palette
|
||||
#if PALETTE
|
||||
#if PALETTE_4_BIT
|
||||
uint subpixel = uint(gl_FragCoord.x) & 3u;
|
||||
uint vram_value = RGBA8ToRGBA5551(color);
|
||||
float palette_index = float((vram_value >> (subpixel * 4u)) & 0x0Fu) * rcp_vram_size.x;
|
||||
#elif PALETTE_8_BIT
|
||||
// TODO: Still has precision issues here
|
||||
uint subpixel = uint(gl_FragCoord.x) & 1u;
|
||||
float palette_index = ((subpixel == 0u) ? color.x : color.y) * (255.0 * rcp_vram_size.x);
|
||||
#endif
|
||||
vec2 palette_coords = vec2(palette_offset.x + palette_index, fixYCoord(palette_offset.y));
|
||||
color = texture(samp0, palette_coords);
|
||||
#endif
|
||||
|
||||
o_col0 = color;
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void GPU_HW::UpdateTexturePageTexture() {}
|
||||
|
||||
void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices)
|
||||
{
|
||||
if (rc.texture_enable)
|
||||
{
|
||||
// extract texture lut/page
|
||||
switch (rc.primitive)
|
||||
{
|
||||
case Primitive::Polygon:
|
||||
{
|
||||
if (rc.shading_enable)
|
||||
m_texture_config.SetFromPolygonTexcoord(m_GP0_command[2], m_GP0_command[5]);
|
||||
else
|
||||
m_texture_config.SetFromPolygonTexcoord(m_GP0_command[2], m_GP0_command[4]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_texture_config.IsPageChanged())
|
||||
{
|
||||
if (!m_batch_vertices.empty())
|
||||
FlushRender();
|
||||
|
||||
UpdateTexturePageTexture();
|
||||
m_texture_config.ClearPageChangedFlag();
|
||||
}
|
||||
}
|
||||
|
||||
// flush when the command changes
|
||||
if (!m_batch_vertices.empty()) // && m_batch_command.bits != rc.bits)
|
||||
FlushRender();
|
||||
|
||||
m_batch_command = rc;
|
||||
LoadVertices(rc, num_vertices);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "gpu.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
class GPU_HW : public GPU
|
||||
@ -15,17 +16,30 @@ protected:
|
||||
s32 x;
|
||||
s32 y;
|
||||
u32 color;
|
||||
u32 texcoord;
|
||||
u16 texcoord;
|
||||
u16 padding;
|
||||
};
|
||||
|
||||
void LoadVertices(RenderCommand rc, u32 num_vertices);
|
||||
virtual void UpdateTexturePageTexture();
|
||||
|
||||
bool IsFlushed() const { return !m_batch_vertices.empty(); }
|
||||
|
||||
void DispatchRenderCommand(RenderCommand rc, u32 num_vertices) override;
|
||||
|
||||
void CalcViewport(int* x, int* y, int* width, int* height);
|
||||
void CalcScissorRect(int* left, int* top, int* right, int* bottom);
|
||||
|
||||
std::string GenerateVertexShader(bool textured);
|
||||
std::string GenerateFragmentShader(bool textured);
|
||||
std::string GenerateFragmentShader(bool textured, bool blending);
|
||||
std::string GenerateScreenQuadVertexShader();
|
||||
std::string GenerateTexturePageProgram(TextureColorMode mode);
|
||||
|
||||
std::vector<HWVertex> m_vertex_staging;
|
||||
std::vector<HWVertex> m_batch_vertices;
|
||||
RenderCommand m_batch_command = {};
|
||||
|
||||
private:
|
||||
void GenerateShaderHeader(std::stringstream& ss);
|
||||
|
||||
void LoadVertices(RenderCommand rc, u32 num_vertices);
|
||||
};
|
||||
|
||||
|
@ -32,6 +32,11 @@ void GPU_HW_OpenGL::Reset()
|
||||
ClearFramebuffer();
|
||||
}
|
||||
|
||||
std::tuple<s32, s32> GPU_HW_OpenGL::ConvertToFramebufferCoordinates(s32 x, s32 y)
|
||||
{
|
||||
return std::make_tuple(x, static_cast<s32>(static_cast<s32>(VRAM_HEIGHT) - y));
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::CreateFramebuffer()
|
||||
{
|
||||
m_framebuffer_texture =
|
||||
@ -41,6 +46,13 @@ void GPU_HW_OpenGL::CreateFramebuffer()
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_framebuffer_texture->GetGLId(), 0);
|
||||
Assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
|
||||
m_texture_page_texture =
|
||||
std::make_unique<GL::Texture>(TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, false);
|
||||
glGenFramebuffers(1, &m_texture_page_fbo_id);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_texture_page_fbo_id);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture_page_texture->GetGLId(), 0);
|
||||
Assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::ClearFramebuffer()
|
||||
@ -51,14 +63,17 @@ void GPU_HW_OpenGL::ClearFramebuffer()
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
//m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::DestroyFramebuffer()
|
||||
{
|
||||
glDeleteFramebuffers(1, &m_texture_page_fbo_id);
|
||||
m_texture_page_fbo_id = 0;
|
||||
m_texture_page_texture.reset();
|
||||
|
||||
glDeleteFramebuffers(1, &m_framebuffer_fbo_id);
|
||||
m_framebuffer_fbo_id = 0;
|
||||
|
||||
m_framebuffer_texture.reset();
|
||||
}
|
||||
|
||||
@ -70,52 +85,86 @@ void GPU_HW_OpenGL::CreateVertexBuffer()
|
||||
|
||||
glGenVertexArrays(1, &m_vao_id);
|
||||
glBindVertexArray(m_vao_id);
|
||||
glVertexAttribIPointer(0, 2, GL_INT, sizeof(HWVertex), reinterpret_cast<void*>(offsetof(HWVertex, x)));
|
||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(HWVertex),
|
||||
reinterpret_cast<void*>(offsetof(HWVertex, color)));
|
||||
glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(HWVertex), reinterpret_cast<void*>(offsetof(HWVertex, color)));
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribIPointer(0, 2, GL_INT, sizeof(HWVertex), reinterpret_cast<void*>(offsetof(HWVertex, x)));
|
||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(HWVertex),
|
||||
reinterpret_cast<void*>(offsetof(HWVertex, color)));
|
||||
glVertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, true, sizeof(HWVertex),
|
||||
reinterpret_cast<void*>(offsetof(HWVertex, texcoord)));
|
||||
glBindVertexArray(0);
|
||||
|
||||
glGenVertexArrays(1, &m_attributeless_vao_id);
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::CompilePrograms()
|
||||
{
|
||||
for (u32 texture_enable_i = 0; texture_enable_i < 2; texture_enable_i++)
|
||||
{
|
||||
const bool texture_enable = ConvertToBool(texture_enable_i);
|
||||
const std::string vs = GenerateVertexShader(texture_enable);
|
||||
const std::string fs = GenerateFragmentShader(texture_enable);
|
||||
bool result = true;
|
||||
result &= CompileProgram(m_color_program, false, false);
|
||||
result &= CompileProgram(m_texture_program, true, false);
|
||||
result &= CompileProgram(m_blended_texture_program, true, true);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
GL::Program& prog = texture_enable ? m_texture_program : m_color_program;
|
||||
if (!prog.Compile(vs.c_str(), fs.c_str()))
|
||||
const std::string screen_quad_vs = GenerateScreenQuadVertexShader();
|
||||
for (u32 palette_size = 0; palette_size < static_cast<u32>(m_texture_page_programs.size()); palette_size++)
|
||||
{
|
||||
const std::string fs = GenerateTexturePageProgram(static_cast<TextureColorMode>(palette_size));
|
||||
|
||||
GL::Program& prog = m_texture_page_programs[palette_size];
|
||||
if (!prog.Compile(screen_quad_vs.c_str(), fs.c_str()))
|
||||
return false;
|
||||
|
||||
prog.BindAttribute(0, "a_position");
|
||||
prog.BindAttribute(1, "a_color");
|
||||
if (texture_enable)
|
||||
prog.BindAttribute(2, "a_texcoord");
|
||||
|
||||
prog.BindFragData(0, "ocol0");
|
||||
prog.BindFragData(0, "o_col0");
|
||||
|
||||
if (!prog.Link())
|
||||
return false;
|
||||
|
||||
prog.RegisterUniform("samp0");
|
||||
prog.RegisterUniform("base_offset");
|
||||
prog.RegisterUniform("palette_offset");
|
||||
prog.Bind();
|
||||
prog.Uniform1i(0, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::SetProgram(bool texture_enable)
|
||||
bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool textured, bool blending)
|
||||
{
|
||||
GL::Program& prog = texture_enable ? m_texture_program : m_color_program;
|
||||
if (!prog.IsVaild())
|
||||
const std::string vs = GenerateVertexShader(textured);
|
||||
const std::string fs = GenerateFragmentShader(textured, blending);
|
||||
if (!prog.Compile(vs.c_str(), fs.c_str()))
|
||||
return false;
|
||||
|
||||
prog.BindAttribute(0, "a_pos");
|
||||
prog.BindAttribute(1, "a_col0");
|
||||
if (textured)
|
||||
prog.BindAttribute(2, "a_tex0");
|
||||
|
||||
prog.BindFragData(0, "o_col0");
|
||||
|
||||
if (!prog.Link())
|
||||
return false;
|
||||
|
||||
prog.Bind();
|
||||
|
||||
if (textured)
|
||||
{
|
||||
prog.RegisterUniform("samp0");
|
||||
prog.Uniform1i(0, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::SetProgram(bool textured, bool blending)
|
||||
{
|
||||
const GL::Program& prog = textured ? (blending ? m_blended_texture_program : m_texture_program) : m_color_program;
|
||||
prog.Bind();
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::SetViewport()
|
||||
{
|
||||
int x, y, width, height;
|
||||
@ -144,70 +193,100 @@ inline u32 ConvertRGBA5551ToRGBA8888(u16 color)
|
||||
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
||||
void GPU_HW_OpenGL::UpdateDisplay()
|
||||
{
|
||||
const u32 pixel_count = width * height;
|
||||
std::vector<u32> rgba_data;
|
||||
rgba_data.reserve(pixel_count);
|
||||
|
||||
const u8* source_ptr = static_cast<const u8*>(data);
|
||||
for (u32 i = 0; i < pixel_count; i++)
|
||||
{
|
||||
u16 src_col;
|
||||
std::memcpy(&src_col, source_ptr, sizeof(src_col));
|
||||
source_ptr += sizeof(src_col);
|
||||
|
||||
const u32 dst_col = ConvertRGBA5551ToRGBA8888(src_col);
|
||||
rgba_data.push_back(dst_col);
|
||||
}
|
||||
|
||||
m_framebuffer_texture->Bind();
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
rgba_data.data());
|
||||
GPU_HW::UpdateDisplay();
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::DispatchRenderCommand(RenderCommand rc, u32 num_vertices)
|
||||
void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
||||
{
|
||||
LoadVertices(rc, num_vertices);
|
||||
if (m_vertex_staging.empty())
|
||||
return;
|
||||
std::vector<u32> rgba_data;
|
||||
rgba_data.reserve(width * height);
|
||||
|
||||
if (!SetProgram(rc.texture_enable))
|
||||
// reverse copy the rows so it matches opengl's lower-left origin
|
||||
const u32 source_stride = width * sizeof(u16);
|
||||
const u8* source_ptr = static_cast<const u8*>(data) + (source_stride * (height - 1));
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to set GL program");
|
||||
m_vertex_staging.clear();
|
||||
return;
|
||||
const u8* source_row_ptr = source_ptr;
|
||||
|
||||
for (u32 col = 0; col < width; col++)
|
||||
{
|
||||
u16 src_col;
|
||||
std::memcpy(&src_col, source_row_ptr, sizeof(src_col));
|
||||
source_row_ptr += sizeof(src_col);
|
||||
|
||||
const u32 dst_col = ConvertRGBA5551ToRGBA8888(src_col);
|
||||
rgba_data.push_back(dst_col);
|
||||
}
|
||||
|
||||
source_ptr -= source_stride;
|
||||
}
|
||||
|
||||
m_framebuffer_texture->Bind();
|
||||
|
||||
// lower-left origin flip happens here
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, VRAM_HEIGHT - y - height, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
rgba_data.data());
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::UpdateTexturePageTexture()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_texture_page_fbo_id);
|
||||
m_framebuffer_texture->Bind();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glViewport(0, 0, TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT);
|
||||
glBindVertexArray(m_attributeless_vao_id);
|
||||
|
||||
const GL::Program& prog = m_texture_page_programs[static_cast<u8>(m_texture_config.color_mode)];
|
||||
prog.Bind();
|
||||
|
||||
const float base_x = static_cast<float>(m_texture_config.base_x) * (1.0f / static_cast<float>(VRAM_WIDTH));
|
||||
const float base_y = static_cast<float>(m_texture_config.base_y) * (1.0f / static_cast<float>(VRAM_HEIGHT));
|
||||
prog.Uniform2f(1, base_x, base_y);
|
||||
|
||||
if (m_texture_config.color_mode >= GPU::TextureColorMode::Palette4Bit)
|
||||
{
|
||||
const float palette_x = static_cast<float>(m_texture_config.palette_x) * (1.0f / static_cast<float>(VRAM_WIDTH));
|
||||
const float palette_y = static_cast<float>(m_texture_config.palette_y) * (1.0f / static_cast<float>(VRAM_HEIGHT));
|
||||
prog.Uniform2f(2, palette_x, palette_y);
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_framebuffer_texture->Unbind();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::FlushRender()
|
||||
{
|
||||
if (m_batch_vertices.empty())
|
||||
return;
|
||||
|
||||
SetProgram(m_batch_command.texture_enable, m_batch_command.texture_blending_raw);
|
||||
SetViewport();
|
||||
|
||||
if (m_batch_command.texture_enable)
|
||||
m_texture_page_texture->Bind();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id);
|
||||
glBindVertexArray(m_vao_id);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizei>(sizeof(HWVertex) * m_vertex_staging.size()),
|
||||
m_vertex_staging.data(), GL_STREAM_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizei>(sizeof(HWVertex) * m_batch_vertices.size()),
|
||||
m_batch_vertices.data(), GL_STREAM_DRAW);
|
||||
glVertexAttribIPointer(0, 2, GL_INT, sizeof(HWVertex), reinterpret_cast<void*>(offsetof(HWVertex, x)));
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(HWVertex),
|
||||
reinterpret_cast<void*>(offsetof(HWVertex, color)));
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(HWVertex), reinterpret_cast<void*>(offsetof(HWVertex, color)));
|
||||
glVertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, true, sizeof(HWVertex),
|
||||
reinterpret_cast<void*>(offsetof(HWVertex, texcoord)));
|
||||
|
||||
glDrawArrays(rc.quad_polygon ? GL_TRIANGLE_STRIP : GL_TRIANGLES, 0, static_cast<GLsizei>(m_vertex_staging.size()));
|
||||
glDrawArrays(m_batch_command.quad_polygon ? GL_TRIANGLE_STRIP : GL_TRIANGLES, 0,
|
||||
static_cast<GLsizei>(m_batch_vertices.size()));
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
m_vertex_staging.clear();
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::FlushRender()
|
||||
{
|
||||
if (m_vertex_staging.empty())
|
||||
return;
|
||||
|
||||
m_vertex_staging.clear();
|
||||
m_batch_vertices.clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareOpenGLRenderer()
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "gpu_hw.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
class GPU_HW_OpenGL : public GPU_HW
|
||||
{
|
||||
@ -16,11 +17,14 @@ public:
|
||||
void Reset() override;
|
||||
|
||||
protected:
|
||||
void UpdateDisplay() override;
|
||||
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override;
|
||||
void DispatchRenderCommand(RenderCommand rc, u32 num_vertices) override;
|
||||
void UpdateTexturePageTexture() override;
|
||||
void FlushRender() override;
|
||||
|
||||
private:
|
||||
std::tuple<s32, s32> ConvertToFramebufferCoordinates(s32 x, s32 y);
|
||||
|
||||
void CreateFramebuffer();
|
||||
void ClearFramebuffer();
|
||||
void DestroyFramebuffer();
|
||||
@ -28,17 +32,24 @@ private:
|
||||
void CreateVertexBuffer();
|
||||
|
||||
bool CompilePrograms();
|
||||
bool CompileProgram(GL::Program& prog, bool textured, bool blending);
|
||||
|
||||
bool SetProgram(bool texture_enable);
|
||||
void SetProgram(bool textured, bool blending);
|
||||
void SetViewport();
|
||||
void SetScissor();
|
||||
|
||||
std::unique_ptr<GL::Texture> m_framebuffer_texture;
|
||||
GLuint m_framebuffer_fbo_id = 0;
|
||||
|
||||
std::unique_ptr<GL::Texture> m_texture_page_texture;
|
||||
GLuint m_texture_page_fbo_id = 0;
|
||||
|
||||
GLuint m_vertex_buffer = 0;
|
||||
GLuint m_vao_id = 0;
|
||||
GLuint m_attributeless_vao_id = 0;
|
||||
|
||||
GL::Program m_texture_program;
|
||||
GL::Program m_color_program;
|
||||
GL::Program m_blended_texture_program;
|
||||
std::array<GL::Program, 3> m_texture_page_programs;
|
||||
};
|
||||
|
@ -38,9 +38,12 @@ void System::Reset()
|
||||
m_bus->Reset();
|
||||
m_dma->Reset();
|
||||
m_gpu->Reset();
|
||||
m_frame_number = 1;
|
||||
}
|
||||
|
||||
void System::RunFrame()
|
||||
{
|
||||
m_cpu->Execute();
|
||||
u32 current_frame_number = m_frame_number;
|
||||
while (current_frame_number == m_frame_number)
|
||||
m_cpu->Execute();
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ public:
|
||||
|
||||
HostInterface* GetHostInterface() const { return m_host_interface; }
|
||||
|
||||
u32 GetFrameNumber() const { return m_frame_number; }
|
||||
void IncrementFrameNumber() { m_frame_number++; }
|
||||
|
||||
bool Initialize();
|
||||
void Reset();
|
||||
|
||||
@ -31,4 +34,5 @@ private:
|
||||
std::unique_ptr<Bus> m_bus;
|
||||
std::unique_ptr<DMA> m_dma;
|
||||
std::unique_ptr<GPU> m_gpu;
|
||||
u32 m_frame_number = 1;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user