From 4706a906d5486b9002c3928b5bcba939653891ce Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 12 Sep 2019 12:53:04 +1000 Subject: [PATCH] GPU: Base work for hardware renderer --- src/pse/gpu.cpp | 241 +++++++++++++++++++++--------------- src/pse/gpu.h | 74 +++++++++-- src/pse/gpu_hw.cpp | 108 ++++++++++++++++ src/pse/gpu_hw.h | 28 +++++ src/pse/gpu_hw_opengl.cpp | 187 ++++++++++++++++++++++++++++ src/pse/gpu_hw_opengl.h | 44 +++++++ src/pse/pse.vcxproj | 20 +-- src/pse/pse.vcxproj.filters | 4 + src/pse/system.cpp | 31 +++-- src/pse/system.h | 21 ++-- 10 files changed, 627 insertions(+), 131 deletions(-) create mode 100644 src/pse/gpu_hw.cpp create mode 100644 src/pse/gpu_hw.h create mode 100644 src/pse/gpu_hw_opengl.cpp create mode 100644 src/pse/gpu_hw_opengl.h diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 148fe86cb..7cb0aa03b 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -4,14 +4,6 @@ #include "dma.h" Log_SetChannel(GPU); -static constexpr s32 S11ToS32(u32 value) -{ - if (value & (UINT16_C(1) << 10)) - return static_cast(UINT32_C(0xFFFFF800) | value); - else - return value; -} - GPU::GPU() = default; GPU::~GPU() = default; @@ -109,96 +101,92 @@ u32 GPU::ReadGPUREAD() void GPU::WriteGP0(u32 value) { - Assert(m_GP0_command_length < MAX_GP0_COMMAND_LENGTH); - - m_GP0_command[m_GP0_command_length++] = value; + m_GP0_command.push_back(value); + Assert(m_GP0_command.size() <= 128); const u8 command = Truncate8(m_GP0_command[0] >> 24); const u32 param = m_GP0_command[0] & UINT32_C(0x00FFFFFF); - switch (command) + + if (command >= 0x20 && command <= 0x7F) { - case 0x00: // NOP + // Draw polygon + if (!HandleRenderCommand()) + return; + } + else + { + switch (command) + { + case 0x00: // NOP + break; + + case 0xE1: // Set draw mode + { + // 0..10 bits match GPUSTAT + const u32 MASK = ((UINT32_C(1) << 11) - 1); + m_GPUSTAT.bits = (m_GPUSTAT.bits & ~MASK) | param & MASK; + 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; + Log_DebugPrintf("Set draw mode %08X", param); + } break; - case 0xE1: // Set draw mode - { - // 0..10 bits match GPUSTAT - const u32 MASK = ((UINT32_C(1) << 11) - 1); - m_GPUSTAT.bits = (m_GPUSTAT.bits & ~MASK) | param & MASK; - 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; - Log_DebugPrintf("Set draw mode %08X", param); - } - break; - - case 0xE2: // set texture window - { - m_texture_config.window_mask_x = param & UINT32_C(0x1F); - m_texture_config.window_mask_y = (param >> 5) & UINT32_C(0x1F); - m_texture_config.window_offset_x = (param >> 10) & UINT32_C(0x1F); - m_texture_config.window_offset_y = (param >> 15) & UINT32_C(0x1F); - Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_texture_config.window_mask_x, - m_texture_config.window_mask_y, m_texture_config.window_offset_x, - m_texture_config.window_offset_y); - } - break; - - case 0xE3: // Set drawing area top left - { - m_drawing_area.top_left_x = param & UINT32_C(0x3FF); - m_drawing_area.top_left_y = (param >> 10) & UINT32_C(0x1FF); - Log_DebugPrintf("Set drawing area top-left: (%u, %u)", m_drawing_area.top_left_x, m_drawing_area.top_left_y); - } - break; - - case 0xE4: // Set drawing area bottom right - { - m_drawing_area.bottom_right_x = param & UINT32_C(0x3FF); - m_drawing_area.bottom_right_y = (param >> 10) & UINT32_C(0x1FF); - Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.bottom_right_x, - m_drawing_area.bottom_right_y); - } - break; - - case 0xE5: // Set drawing offset - { - m_drawing_offset.x = S11ToS32(param & UINT32_C(0x7FF)); - m_drawing_offset.y = S11ToS32((param >> 11) & UINT32_C(0x7FF)); - Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y); - } - break; - - case 0xE6: // Mask bit setting - { - m_GPUSTAT.draw_set_mask_bit = (param & UINT32_C(0x01)) != 0; - m_GPUSTAT.draw_to_masked_pixels = (param & UINT32_C(0x01)) != 0; - Log_DebugPrintf("Set mask bit %u %u", BoolToUInt32(m_GPUSTAT.draw_set_mask_bit), - BoolToUInt32(m_GPUSTAT.draw_to_masked_pixels)); - } - break; - - default: - { - if (command < 0x20) + case 0xE2: // set texture window { + m_texture_config.window_mask_x = param & UINT32_C(0x1F); + m_texture_config.window_mask_y = (param >> 5) & UINT32_C(0x1F); + m_texture_config.window_offset_x = (param >> 10) & UINT32_C(0x1F); + m_texture_config.window_offset_y = (param >> 15) & UINT32_C(0x1F); + Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_texture_config.window_mask_x, + m_texture_config.window_mask_y, m_texture_config.window_offset_x, + m_texture_config.window_offset_y); } - else if (command < 0x40) + break; + + case 0xE3: // Set drawing area top left { - // Draw polygon - if (!HandleRenderPolygonCommand()) - return; - - break; + m_drawing_area.top_left_x = param & UINT32_C(0x3FF); + m_drawing_area.top_left_y = (param >> 10) & UINT32_C(0x1FF); + Log_DebugPrintf("Set drawing area top-left: (%u, %u)", m_drawing_area.top_left_x, m_drawing_area.top_left_y); } + break; - Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command); + case 0xE4: // Set drawing area bottom right + { + m_drawing_area.bottom_right_x = param & UINT32_C(0x3FF); + m_drawing_area.bottom_right_y = (param >> 10) & UINT32_C(0x1FF); + Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.bottom_right_x, + m_drawing_area.bottom_right_y); + } + break; + + case 0xE5: // Set drawing offset + { + m_drawing_offset.x = S11ToS32(param & UINT32_C(0x7FF)); + m_drawing_offset.y = S11ToS32((param >> 11) & UINT32_C(0x7FF)); + Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y); + } + break; + + case 0xE6: // Mask bit setting + { + m_GPUSTAT.draw_set_mask_bit = (param & UINT32_C(0x01)) != 0; + m_GPUSTAT.draw_to_masked_pixels = (param & UINT32_C(0x01)) != 0; + Log_DebugPrintf("Set mask bit %u %u", BoolToUInt32(m_GPUSTAT.draw_set_mask_bit), + BoolToUInt32(m_GPUSTAT.draw_to_masked_pixels)); + } + break; + + default: + { + Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command); + } + break; } - break; } - m_GP0_command.fill(UINT32_C(0)); - m_GP0_command_length = 0; + m_GP0_command.clear(); } void GPU::WriteGP1(u32 value) @@ -221,25 +209,84 @@ void GPU::WriteGP1(u32 value) } } -bool GPU::HandleRenderPolygonCommand() +bool GPU::HandleRenderCommand() { const u8 command = Truncate8(m_GP0_command[0] >> 24); - const bool semi_transparent = !!(command & 0x02); - const bool textured = !!(command & 0x04); - const bool four_points = !!(command & 0x08); - const bool shaded = !!(command & 0x10); - // shaded vertices use the colour from the first word for the first vertex - const u8 words_per_vertex = 1 + BoolToUInt8(textured) + BoolToUInt8(shaded); - const u8 num_vertices = four_points ? 4 : 3; - const u8 total_words = words_per_vertex * num_vertices + BoolToUInt8(!shaded); - if (m_GP0_command_length < total_words) + const RenderCommand rc{m_GP0_command[0]}; + u8 words_per_vertex; + u32 num_vertices; + u32 total_words; + switch (rc.primitive) + { + case Primitive::Polygon: + { + // shaded vertices use the colour from the first word for the first vertex + words_per_vertex = 1 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.shading_enable); + num_vertices = rc.quad_polygon ? 4 : 3; + total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); + } + break; + + case Primitive::Line: + { + words_per_vertex = 1 + BoolToUInt8(rc.shading_enable); + if (rc.polyline) + { + // polyline goes until we hit the termination code + num_vertices = 0; + bool found_terminator = false; + for (size_t pos = 0; pos < m_GP0_command.size(); pos += words_per_vertex) + { + if (m_GP0_command[pos] == 0x55555555) + { + found_terminator = true; + break; + } + + num_vertices++; + } + if (!found_terminator) + return false; + } + else + { + num_vertices = 2; + } + + total_words = words_per_vertex * num_vertices + BoolToUInt8(!rc.shading_enable); + } + break; + + case Primitive::Rectangle: + { + words_per_vertex = + 1 + BoolToUInt8(rc.texture_enable) + BoolToUInt8(rc.rectangle_size == DrawRectangleSize::Variable); + num_vertices = 1; + total_words = words_per_vertex; + } + break; + + default: + UnreachableCode(); + return true; + } + + if (m_GP0_command.size() < total_words) return false; - Log_DebugPrintf("Render %s %s %s %s polygon (%u verts, %u words per vert)", - four_points ? "four-point" : "three-point", semi_transparent ? "semi-transparent" : "opaque", - textured ? "textured" : "non-textured", shaded ? "shaded" : "monochrome", ZeroExtend32(num_vertices), + static constexpr std::array primitive_names = {{"", "polygon", "line", "rectangle"}}; + + Log_DebugPrintf("Render %s %s %s %s %s (%u verts, %u words per vert)", rc.quad_polygon ? "four-point" : "three-point", + rc.transparency_enable ? "semi-transparent" : "opaque", + rc.texture_enable ? "textured" : "non-textured", rc.shading_enable ? "shaded" : "monochrome", + primitive_names[static_cast(rc.primitive.GetValue())], ZeroExtend32(num_vertices), ZeroExtend32(words_per_vertex)); + DispatchRenderCommand(rc, num_vertices); return true; } + +void GPU::DispatchRenderCommand(RenderCommand rc, u32 num_vertices) {} + +void GPU::FlushRender() {} \ No newline at end of file diff --git a/src/pse/gpu.h b/src/pse/gpu.h index 4e3ba49ad..7079a3994 100644 --- a/src/pse/gpu.h +++ b/src/pse/gpu.h @@ -10,10 +10,10 @@ class GPU { public: GPU(); - ~GPU(); + virtual ~GPU(); - bool Initialize(Bus* bus, DMA* dma); - void Reset(); + virtual bool Initialize(Bus* bus, DMA* dma); + virtual void Reset(); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); @@ -22,8 +22,21 @@ public: u32 DMARead(); void DMAWrite(u32 value); -private: - static constexpr u32 MAX_GP0_COMMAND_LENGTH = 12; + // gpu_hw_opengl.cpp + static std::unique_ptr CreateHardwareOpenGLRenderer(); + +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 s32 S11ToS32(u32 value) + { + if (value & (UINT16_C(1) << 10)) + return static_cast(UINT32_C(0xFFFFF800) | value); + else + return value; + } enum class DMADirection : u32 { @@ -33,6 +46,48 @@ private: GPUREADtoCPU = 3 }; + enum class Primitive : u8 + { + Reserved = 0, + Polygon = 1, + Line = 2, + Rectangle = 3 + }; + + enum class DrawRectangleSize : u8 + { + Variable = 0, + R1x1 = 1, + R8x8 = 2, + R16x16 = 3 + }; + + union RenderCommand + { + u32 bits; + + BitField color_for_first_vertex; + BitField texture_enable; // not valid for lines + BitField transparency_enable; + BitField rectangle_size; // only for rectangles + BitField quad_polygon; // only for polygons + BitField polyline; // only for lines + BitField shading_enable; // 0 - flat, 1 = gouroud + BitField primitive; + }; + + // TODO: Use BitField to do sign extending instead + union VertexPosition + { + u32 bits; + + BitField x_s11; + BitField y_s11; + + u32 x() const { return S11ToS32(x_s11); } + u32 y() const { return S11ToS32(y_s11); } + }; + void SoftReset(); void UpdateDMARequest(); u32 ReadGPUREAD(); @@ -40,7 +95,11 @@ private: void WriteGP1(u32 value); // Rendering commands, returns false if not enough data is provided - bool HandleRenderPolygonCommand(); + bool HandleRenderCommand(); + + // Rendering in the backend + virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices); + virtual void FlushRender(); Bus* m_bus = nullptr; DMA* m_dma = nullptr; @@ -97,6 +156,5 @@ private: s32 y; } m_drawing_offset = {}; - std::array m_GP0_command = {}; - u32 m_GP0_command_length = 0; + std::vector m_GP0_command; }; diff --git a/src/pse/gpu_hw.cpp b/src/pse/gpu_hw.cpp new file mode 100644 index 000000000..0acaa93c1 --- /dev/null +++ b/src/pse/gpu_hw.cpp @@ -0,0 +1,108 @@ +#include "gpu_hw.h" +#include "YBaseLib/Assert.h" +#include + +GPU_HW::GPU_HW() = default; + +GPU_HW::~GPU_HW() = default; + +void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices) +{ + switch (rc.primitive) + { + case Primitive::Polygon: + { + const u32 first_colour = m_GP0_command[0] & UINT32_C(0x00FFFFFF); + const bool shaded = rc.shading_enable; + 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()); + + u32 buffer_pos = 1; + for (u32 i = 0; i < num_vertices; i++) + { + HWVertex hw_vert; + hw_vert.color = (shaded && i > 0) ? (m_GP0_command[buffer_pos++] & UINT32_C(0x00FFFFFF)) : first_colour; + + const VertexPosition vp{m_GP0_command[buffer_pos++]}; + hw_vert.x = vp.x(); + hw_vert.y = vp.y(); + + if (textured) + hw_vert.texcoord = (m_GP0_command[buffer_pos++] & UINT32_C(0x0000FFFF)); + + m_vertex_staging.push_back(hw_vert); + } + } + break; + + default: + UnreachableCode(); + break; + } +} + +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"; + + ss << R"( +in uivec2 a_position; +in uint a_texcoord; +in vec4 a_color; + +out vec4 v_color; +#if TEXTURED + out vec2 v_texcoord; +#endif + +void main() +{ + // -1024..+1023 -> -1..1 + float gl_x = (a_position.x < 0) ? (float(a_position.x) / 1024.0) : (float(a_position.x) / 1023.0); + float gl_y = (a_position.y < 0) ? -(float(a_position.y) / 1024.0) : -(float(a_position.y) / 1023.0); + gl_Position = vec4(gl_x, gl_y, 0.0, 1.0); + + v_color = a_color; + #if TEXTURED + v_texcoord = a_texcoord; + #endif +} +)"; + + return ss.str(); +} + +std::string GPU_HW::GenerateFragmentShader(bool textured) +{ + std::stringstream ss; + ss << "#version 330 core\n"; + if (textured) + ss << "#define TEXTURED 1\n"; + else + ss << "/* #define TEXTURED 0 */\n"; + + ss << R"( +in vec4 v_color; +#if TEXTURED + in vec2 v_texcoord; +#endif + +out vec4 ocol0; + +void main() +{ + //ocol0 = v_color; + ocol0 = vec4(1.0, 0.5, 0.5, 1.0); +} +)"; + + return ss.str(); +} diff --git a/src/pse/gpu_hw.h b/src/pse/gpu_hw.h new file mode 100644 index 000000000..227fe5cc3 --- /dev/null +++ b/src/pse/gpu_hw.h @@ -0,0 +1,28 @@ +#pragma once +#include "gpu.h" +#include +#include + +class GPU_HW : public GPU +{ +public: + GPU_HW(); + virtual ~GPU_HW(); + +protected: + struct HWVertex + { + s32 x; + s32 y; + u32 color; + u32 texcoord; + }; + + void LoadVertices(RenderCommand rc, u32 num_vertices); + + std::string GenerateVertexShader(bool textured); + std::string GenerateFragmentShader(bool textured); + + std::vector m_vertex_staging; +}; + diff --git a/src/pse/gpu_hw_opengl.cpp b/src/pse/gpu_hw_opengl.cpp new file mode 100644 index 000000000..bd7374dd7 --- /dev/null +++ b/src/pse/gpu_hw_opengl.cpp @@ -0,0 +1,187 @@ +#include "gpu_hw_opengl.h" +#include "YBaseLib/Assert.h" +#include "YBaseLib/Log.h" +Log_SetChannel(GPU_HW_OpenGL); + +GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {} + +GPU_HW_OpenGL::~GPU_HW_OpenGL() +{ + DestroyFramebuffer(); +} + +bool GPU_HW_OpenGL::Initialize(Bus* bus, DMA* dma) +{ + if (!GPU_HW::Initialize(bus, dma)) + return false; + + CreateFramebuffer(); + return true; +} + +void GPU_HW_OpenGL::Reset() +{ + GPU_HW::Reset(); + + ClearFramebuffer(); +} + +void GPU_HW_OpenGL::CreateFramebuffer() +{ + glGenTextures(1, &m_framebuffer_texture_id); + glBindTexture(GL_TEXTURE_2D, m_framebuffer_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VRAM_WIDTH, VRAM_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + + glGenFramebuffers(1, &m_framebuffer_fbo_id); + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_framebuffer_texture_id, 0); + Assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +} + +void GPU_HW_OpenGL::ClearFramebuffer() +{ + // TODO: get rid of the FBO switches + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GPU_HW_OpenGL::DestroyFramebuffer() +{ + glDeleteFramebuffers(1, &m_framebuffer_fbo_id); + m_framebuffer_fbo_id = 0; + + glDeleteTextures(1, &m_framebuffer_texture_id); + m_framebuffer_texture_id = 0; +} + +void GPU_HW_OpenGL::DispatchRenderCommand(RenderCommand rc, u32 num_vertices) +{ + LoadVertices(rc, num_vertices); + if (m_vertex_staging.empty()) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id); + + if (rc.texture_enable) + m_texture_program.Bind(); + else + m_color_program.Bind(); + + glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, static_cast(sizeof(HWVertex) * m_vertex_staging.size()), + m_vertex_staging.data(), GL_STREAM_DRAW); + glDrawArrays(rc.quad_polygon ? GL_TRIANGLE_STRIP : GL_TRIANGLES, 0, static_cast(m_vertex_staging.size())); +} + +void GPU_HW_OpenGL::FlushRender() +{ + if (m_vertex_staging.empty()) + return; + + m_vertex_staging.clear(); +} + +std::unique_ptr GPU::CreateHardwareOpenGLRenderer() +{ + return std::make_unique(); +} + +void GPU_HW_OpenGL::Program::Bind() +{ + glUseProgram(program_id); +} + +static GLuint CompileShader(GLenum type, const std::string& source) +{ + GLuint id = glCreateShader(type); + + std::array sources = {{source.c_str()}}; + std::array source_lengths = {{static_cast(source.size())}}; + glShaderSource(id, static_cast(source.size()), sources.data(), source_lengths.data()); + + GLint status = GL_FALSE; + glGetShaderiv(id, GL_COMPILE_STATUS, &status); + + GLint info_log_length = 0; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length); + + if (status == GL_FALSE || info_log_length > 0) + { + std::string info_log; + info_log.resize(info_log_length + 1); + glGetShaderInfoLog(id, info_log_length, &info_log_length, info_log.data()); + + if (status == GL_TRUE) + { + Log_ErrorPrintf("Shader compiled with warnings:\n%s", info_log.c_str()); + } + else + { + Log_ErrorPrintf("Shader failed to compile:\n%s", info_log.c_str()); + glDeleteShader(id); + return 0; + } + } + + return id; +} + +bool GPU_HW_OpenGL::Program::Compile(const std::string& vertex_shader, const std::string& fragment_shader) +{ + GLuint vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader); + if (vertex_shader_id == 0) + return false; + + GLuint fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader); + if (fragment_shader_id == 0) + { + glDeleteShader(vertex_shader_id); + return false; + } + + program_id = glCreateProgram(); + glAttachShader(program_id, vertex_shader_id); + glAttachShader(program_id, fragment_shader_id); + + glBindAttribLocation(program_id, 0, "a_position"); + glBindAttribLocation(program_id, 1, "a_texcoord"); + glBindAttribLocation(program_id, 2, "a_color"); + glBindFragDataLocation(program_id, 0, "ocol0"); + + glLinkProgram(program_id); + + glDeleteShader(vertex_shader_id); + glDeleteShader(fragment_shader_id); + + GLint status = GL_FALSE; + glGetProgramiv(program_id, GL_LINK_STATUS, &status); + + GLint info_log_length = 0; + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length); + + if (status == GL_FALSE || info_log_length > 0) + { + std::string info_log; + info_log.resize(info_log_length + 1); + glGetProgramInfoLog(program_id, info_log_length, &info_log_length, info_log.data()); + + if (status == GL_TRUE) + { + Log_ErrorPrintf("Program linked with warnings:\n%s", info_log.c_str()); + } + else + { + Log_ErrorPrintf("Program failed to link:\n%s", info_log.c_str()); + glDeleteProgram(program_id); + program_id = 0; + return false; + } + } + + return true; +} diff --git a/src/pse/gpu_hw_opengl.h b/src/pse/gpu_hw_opengl.h new file mode 100644 index 000000000..8275c7e87 --- /dev/null +++ b/src/pse/gpu_hw_opengl.h @@ -0,0 +1,44 @@ +#pragma once +#include "gpu_hw.h" +#include "glad.h" +#include + +class GPU_HW_OpenGL : public GPU_HW +{ +public: + GPU_HW_OpenGL(); + ~GPU_HW_OpenGL() override; + + bool Initialize(Bus* bus, DMA* dma) override; + void Reset() override; + +protected: + virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices) override; + virtual void FlushRender() override; + +private: + void CreateFramebuffer(); + void ClearFramebuffer(); + void DestroyFramebuffer(); + + void CreateVertexBuffer(); + + GLuint m_framebuffer_texture_id = 0; + GLuint m_framebuffer_fbo_id = 0; + + GLuint m_vertex_buffer = 0; + GLuint m_vao_id = 0; + + struct Program + { + GLuint program_id = 0; + + bool IsValid() const { return program_id != 0; } + void Bind(); + bool Compile(const std::string& vertex_shader, const std::string& fragment_shader); + }; + + Program m_texture_program; + Program m_color_program; +}; + diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index ac31a0ee2..3cfb8382b 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -40,6 +40,8 @@ + + @@ -49,6 +51,8 @@ + + @@ -211,7 +215,7 @@ ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -232,7 +236,7 @@ ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -253,7 +257,7 @@ ENABLE_VOODOO=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -276,7 +280,7 @@ ENABLE_VOODOO=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -298,7 +302,7 @@ MaxSpeed true ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -320,7 +324,7 @@ MaxSpeed true ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -343,7 +347,7 @@ MaxSpeed true ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -365,7 +369,7 @@ MaxSpeed true ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 71fa7c85d..3aff24430 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -7,6 +7,8 @@ + + @@ -18,6 +20,8 @@ + + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 3a7842190..81daff37e 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -1,21 +1,32 @@ #include "system.h" +#include "bus.h" +#include "cpu_core.h" +#include "dma.h" +#include "gpu.h" -System::System() = default; +System::System() +{ + m_cpu = std::make_unique(); + m_bus = std::make_unique(); + m_dma = std::make_unique(); + m_gpu = std::make_unique(); + // m_gpu = GPU::CreateHardwareOpenGLRenderer(); +} System::~System() = default; bool System::Initialize() { - if (!m_cpu.Initialize(&m_bus)) + if (!m_cpu->Initialize(m_bus.get())) return false; - if (!m_bus.Initialize(this, &m_dma, &m_gpu)) + if (!m_bus->Initialize(this, m_dma.get(), m_gpu.get())) return false; - if (!m_dma.Initialize(&m_bus, &m_gpu)) + if (!m_dma->Initialize(m_bus.get(), m_gpu.get())) return false; - if (!m_gpu.Initialize(&m_bus, &m_dma)) + if (!m_gpu->Initialize(m_bus.get(), m_dma.get())) return false; return true; @@ -23,13 +34,13 @@ bool System::Initialize() void System::Reset() { - m_cpu.Reset(); - m_bus.Reset(); - m_dma.Reset(); - m_gpu.Reset(); + m_cpu->Reset(); + m_bus->Reset(); + m_dma->Reset(); + m_gpu->Reset(); } void System::RunFrame() { - m_cpu.Execute(); + m_cpu->Execute(); } diff --git a/src/pse/system.h b/src/pse/system.h index c81e23e08..bbebfc1c3 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -1,10 +1,15 @@ #pragma once -#include "bus.h" -#include "dma.h" -#include "gpu.h" -#include "cpu_core.h" #include "types.h" +namespace CPU +{ +class Core; +} + +class Bus; +class DMA; +class GPU; + class System { public: @@ -17,8 +22,8 @@ public: void RunFrame(); private: - CPU::Core m_cpu; - Bus m_bus; - DMA m_dma; - GPU m_gpu; + std::unique_ptr m_cpu; + std::unique_ptr m_bus; + std::unique_ptr m_dma; + std::unique_ptr m_gpu; };