mirror of https://github.com/stenzek/duckstation
GPU: Base work for hardware renderer
parent
c0853de6a6
commit
4706a906d5
@ -0,0 +1,108 @@
|
||||
#include "gpu_hw.h"
|
||||
#include "YBaseLib/Assert.h"
|
||||
#include <sstream>
|
||||
|
||||
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();
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "gpu.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<HWVertex> m_vertex_staging;
|
||||
};
|
||||
|
||||
@ -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<GLsizei>(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<GLsizei>(m_vertex_staging.size()));
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::FlushRender()
|
||||
{
|
||||
if (m_vertex_staging.empty())
|
||||
return;
|
||||
|
||||
m_vertex_staging.clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareOpenGLRenderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_OpenGL>();
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::Program::Bind()
|
||||
{
|
||||
glUseProgram(program_id);
|
||||
}
|
||||
|
||||
static GLuint CompileShader(GLenum type, const std::string& source)
|
||||
{
|
||||
GLuint id = glCreateShader(type);
|
||||
|
||||
std::array<const GLchar*, 1> sources = {{source.c_str()}};
|
||||
std::array<GLint, 1> source_lengths = {{static_cast<GLint>(source.size())}};
|
||||
glShaderSource(id, static_cast<GLsizei>(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;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "gpu_hw.h"
|
||||
#include "glad.h"
|
||||
#include <array>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue