mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
GPU: More work on OpenGL renderer
This commit is contained in:
parent
4706a906d5
commit
aea7a18ac2
|
@ -43,6 +43,8 @@
|
|||
<ClInclude Include="display_renderer_gl.h" />
|
||||
<ClInclude Include="display_timing.h" />
|
||||
<ClInclude Include="fastjmp.h" />
|
||||
<ClInclude Include="gl_program.h" />
|
||||
<ClInclude Include="gl_texture.h" />
|
||||
<ClInclude Include="hdd_image.h" />
|
||||
<ClInclude Include="jit_code_buffer.h" />
|
||||
<ClInclude Include="object.h" />
|
||||
|
@ -59,6 +61,8 @@
|
|||
<ClCompile Include="display_renderer.cpp" />
|
||||
<ClCompile Include="display_renderer_gl.cpp" />
|
||||
<ClCompile Include="display_timing.cpp" />
|
||||
<ClCompile Include="gl_program.cpp" />
|
||||
<ClCompile Include="gl_texture.cpp" />
|
||||
<ClCompile Include="hdd_image.cpp" />
|
||||
<ClCompile Include="jit_code_buffer.cpp" />
|
||||
<ClCompile Include="object.cpp" />
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<ClInclude Include="display_timing.h" />
|
||||
<ClInclude Include="jit_code_buffer.h" />
|
||||
<ClInclude Include="state_wrapper.h" />
|
||||
<ClInclude Include="gl_program.h" />
|
||||
<ClInclude Include="gl_texture.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hdd_image.cpp" />
|
||||
|
@ -31,6 +33,8 @@
|
|||
<ClCompile Include="display_timing.cpp" />
|
||||
<ClCompile Include="jit_code_buffer.cpp" />
|
||||
<ClCompile Include="state_wrapper.cpp" />
|
||||
<ClCompile Include="gl_program.cpp" />
|
||||
<ClCompile Include="gl_texture.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="bitfield.natvis" />
|
||||
|
|
171
src/common/gl_program.cpp
Normal file
171
src/common/gl_program.cpp
Normal file
|
@ -0,0 +1,171 @@
|
|||
#include "gl_program.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include <array>
|
||||
Log_SetChannel(GL);
|
||||
|
||||
namespace GL {
|
||||
|
||||
Program::Program() = default;
|
||||
|
||||
Program::~Program()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
GLuint Program::CompileShader(GLenum type, const char* source)
|
||||
{
|
||||
GLuint id = glCreateShader(type);
|
||||
|
||||
std::array<const GLchar*, 1> sources = {{source}};
|
||||
std::array<GLint, 1> source_lengths = {{static_cast<GLint>(std::strlen(source))}};
|
||||
glShaderSource(id, static_cast<GLsizei>(sources.size()), sources.data(), source_lengths.data());
|
||||
glCompileShader(id);
|
||||
|
||||
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[0]);
|
||||
|
||||
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 Program::Compile(const char* vertex_shader, const char* 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;
|
||||
}
|
||||
|
||||
m_program_id = glCreateProgram();
|
||||
glAttachShader(m_program_id, vertex_shader_id);
|
||||
glAttachShader(m_program_id, fragment_shader_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::BindAttribute(GLuint index, const char* name)
|
||||
{
|
||||
glBindAttribLocation(m_program_id, index, name);
|
||||
}
|
||||
|
||||
void Program::BindDefaultAttributes()
|
||||
{
|
||||
BindAttribute(0, "a_position");
|
||||
BindAttribute(1, "a_texcoord");
|
||||
BindAttribute(2, "a_color");
|
||||
}
|
||||
|
||||
void Program::BindFragData(GLuint index /*= 0*/, const char* name /*= "ocol0"*/)
|
||||
{
|
||||
glBindFragDataLocation(m_program_id, index, name);
|
||||
}
|
||||
|
||||
bool Program::Link()
|
||||
{
|
||||
glLinkProgram(m_program_id);
|
||||
|
||||
glDeleteShader(m_vertex_shader_id);
|
||||
m_vertex_shader_id = 0;
|
||||
glDeleteShader(m_fragment_shader_id);
|
||||
m_fragment_shader_id = 0;
|
||||
|
||||
GLint status = GL_FALSE;
|
||||
glGetProgramiv(m_program_id, GL_LINK_STATUS, &status);
|
||||
|
||||
GLint info_log_length = 0;
|
||||
glGetProgramiv(m_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(m_program_id, info_log_length, &info_log_length, &info_log[0]);
|
||||
|
||||
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(m_program_id);
|
||||
m_program_id = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::Bind()
|
||||
{
|
||||
glUseProgram(m_program_id);
|
||||
}
|
||||
|
||||
void Program::Destroy()
|
||||
{
|
||||
if (m_vertex_shader_id != 0)
|
||||
{
|
||||
glDeleteShader(m_vertex_shader_id);
|
||||
m_vertex_shader_id = 0;
|
||||
}
|
||||
if (m_fragment_shader_id != 0)
|
||||
{
|
||||
glDeleteShader(m_fragment_shader_id);
|
||||
m_fragment_shader_id = 0;
|
||||
}
|
||||
if (m_program_id != 0)
|
||||
{
|
||||
glDeleteProgram(m_program_id);
|
||||
m_program_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 Program::RegisterUniform(const char* name)
|
||||
{
|
||||
u32 id = static_cast<u32>(m_uniform_locations.size());
|
||||
m_uniform_locations.push_back(glGetUniformLocation(m_program_id, name));
|
||||
return id;
|
||||
}
|
||||
|
||||
void Program::Uniform1ui(u32 index, u32 value)
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1ui(location, value);
|
||||
}
|
||||
|
||||
void Program::Uniform4f(u32 index, float x, float y, float z, float w)
|
||||
{
|
||||
Assert(index < m_uniform_locations.size());
|
||||
const int location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4f(location, x, y, z, w);
|
||||
}
|
||||
|
||||
} // namespace GL
|
42
src/common/gl_program.h
Normal file
42
src/common/gl_program.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include "glad.h"
|
||||
#include "types.h"
|
||||
#include <vector>
|
||||
|
||||
namespace GL {
|
||||
class Program
|
||||
{
|
||||
public:
|
||||
Program();
|
||||
~Program();
|
||||
|
||||
static GLuint CompileShader(GLenum type, const char* source);
|
||||
|
||||
bool IsVaild() const { return m_program_id != 0; }
|
||||
|
||||
bool Compile(const char* vertex_shader, const char* fragment_shader);
|
||||
|
||||
void BindAttribute(GLuint index, const char* name);
|
||||
void BindDefaultAttributes();
|
||||
|
||||
void BindFragData(GLuint index = 0, const char* name = "ocol0");
|
||||
|
||||
bool Link();
|
||||
|
||||
void Bind();
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
GLuint m_program_id = 0;
|
||||
GLuint m_vertex_shader_id = 0;
|
||||
GLuint m_fragment_shader_id = 0;
|
||||
|
||||
std::vector<GLint> m_uniform_locations;
|
||||
};
|
||||
|
||||
} // namespace GL
|
29
src/common/gl_texture.cpp
Normal file
29
src/common/gl_texture.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "gl_texture.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
Log_SetChannel(GL);
|
||||
|
||||
namespace GL {
|
||||
|
||||
Texture::Texture(u32 width, u32 height, GLenum format, GLenum type, const void* data /* = nullptr */,
|
||||
bool linear_filter /* = false */)
|
||||
: m_width(width), m_height(height)
|
||||
{
|
||||
glGenTextures(1, &m_id);
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures(1, &m_id);
|
||||
}
|
||||
|
||||
void Texture::Bind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
}
|
||||
|
||||
} // namespace GL
|
24
src/common/gl_texture.h
Normal file
24
src/common/gl_texture.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include "glad.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace GL {
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
Texture(u32 width, u32 height, GLenum format, GLenum type, const void* data = nullptr, bool linear_filter = false);
|
||||
~Texture();
|
||||
|
||||
GLuint GetGLId() const { return m_id; }
|
||||
u32 GetWidth() const { return m_width; }
|
||||
u32 GetHeight() const { return m_height; }
|
||||
|
||||
void Bind();
|
||||
|
||||
private:
|
||||
GLuint m_id;
|
||||
u32 m_width;
|
||||
u32 m_height;
|
||||
};
|
||||
|
||||
} // namespace GL
|
|
@ -7,6 +7,7 @@
|
|||
#include <SDL.h>
|
||||
#include <cstdio>
|
||||
|
||||
#if 0
|
||||
static int NoGUITest()
|
||||
{
|
||||
std::unique_ptr<System> system = std::make_unique<System>();
|
||||
|
@ -19,14 +20,17 @@ static int NoGUITest()
|
|||
system->RunFrame();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int Run(int argc, char* argv[])
|
||||
{
|
||||
#if 0
|
||||
if (argc < 2)
|
||||
{
|
||||
std::fprintf(stderr, "Usage: %s <path to system ini> [save state index]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// init sdl
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
||||
|
@ -48,7 +52,7 @@ static int Run(int argc, char* argv[])
|
|||
s32 state_index = -1;
|
||||
if (argc > 2)
|
||||
state_index = StringConverter::StringToInt32(argv[2]);
|
||||
if (!host_interface->InitializeSystem(argv[1], state_index))
|
||||
if (!host_interface->InitializeSystem("", state_index))
|
||||
{
|
||||
host_interface.reset();
|
||||
SDL_Quit();
|
||||
|
@ -79,6 +83,6 @@ int main(int argc, char* argv[])
|
|||
g_pLog->SetFilterLevel(LOGLEVEL_DEBUG);
|
||||
#endif
|
||||
|
||||
return NoGUITest();
|
||||
//return Run(argc, argv);
|
||||
//return NoGUITest();
|
||||
return Run(argc, argv);
|
||||
}
|
||||
|
|
|
@ -1,181 +1,162 @@
|
|||
#include "sdl_interface.h"
|
||||
#include "YBaseLib/ByteStream.h"
|
||||
#include "YBaseLib/Error.h"
|
||||
#include "common/display_renderer_d3d.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "pse/system.h"
|
||||
#include <SDL.h>
|
||||
#include <cinttypes>
|
||||
#include <glad.h>
|
||||
#ifdef Y_COMPILER_MSVC
|
||||
#include "imgui_impl_dx11.h"
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
Log_SetChannel(SDLInterface);
|
||||
|
||||
SDLInterface::SDLInterface(SDL_Window* window, std::unique_ptr<DisplayRenderer> display_renderer,
|
||||
std::unique_ptr<MixerType> mixer)
|
||||
: m_window(window), m_display_renderer(std::move(display_renderer)), m_mixer(std::move(mixer))
|
||||
{
|
||||
}
|
||||
SDLInterface::SDLInterface() = default;
|
||||
|
||||
SDLInterface::~SDLInterface()
|
||||
{
|
||||
m_mixer.reset();
|
||||
|
||||
switch (m_display_renderer->GetBackendType())
|
||||
if (m_gl_context)
|
||||
{
|
||||
#ifdef Y_COMPILER_MSVC
|
||||
case DisplayRenderer::BackendType::Direct3D:
|
||||
{
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
m_display_renderer.reset();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
if (m_display_vao != 0)
|
||||
glDeleteVertexArrays(1, &m_display_vao);
|
||||
|
||||
case DisplayRenderer::BackendType::OpenGL:
|
||||
{
|
||||
SDL_GLContext context = SDL_GL_GetCurrentContext();
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
m_display_renderer.reset();
|
||||
SDL_GL_MakeCurrent(nullptr, nullptr);
|
||||
SDL_GL_DeleteContext(context);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
ImGui::DestroyContext();
|
||||
m_display_renderer.reset();
|
||||
}
|
||||
break;
|
||||
m_display_program.Destroy();
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
SDL_GL_MakeCurrent(nullptr, nullptr);
|
||||
SDL_GL_DeleteContext(m_gl_context);
|
||||
}
|
||||
|
||||
if (m_window)
|
||||
SDL_DestroyWindow(m_window);
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLInterface> SDLInterface::Create(
|
||||
DisplayRenderer::BackendType display_renderer_backend /* = DisplayRenderer::GetDefaultBackendType() */)
|
||||
bool SDLInterface::CreateSDLWindow()
|
||||
{
|
||||
constexpr u32 DEFAULT_WINDOW_WIDTH = 900;
|
||||
constexpr u32 DEFAULT_WINDOW_HEIGHT = 700;
|
||||
constexpr u32 MAIN_MENU_BAR_HEIGHT = 20;
|
||||
|
||||
// Create window.
|
||||
u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if (display_renderer_backend == DisplayRenderer::BackendType::OpenGL)
|
||||
window_flags |= SDL_WINDOW_OPENGL;
|
||||
constexpr u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL;
|
||||
|
||||
auto window = std::unique_ptr<SDL_Window, void (*)(SDL_Window*)>(
|
||||
SDL_CreateWindow("PCE - Initializing...", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEFAULT_WINDOW_WIDTH,
|
||||
DEFAULT_WINDOW_HEIGHT, window_flags),
|
||||
[](SDL_Window* win) { SDL_DestroyWindow(win); });
|
||||
if (!window)
|
||||
m_window = SDL_CreateWindow("Some idea to emulate a system starting with a P", SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, window_flags);
|
||||
if (!m_window)
|
||||
{
|
||||
Panic("Failed to create window");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
DisplayRenderer::WindowHandleType window_handle = nullptr;
|
||||
if (display_renderer_backend == DisplayRenderer::BackendType::OpenGL)
|
||||
SDL_GetWindowSize(m_window, &m_window_width, &m_window_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* userParam)
|
||||
{
|
||||
Log_InfoPrintf("%s", message);
|
||||
}
|
||||
|
||||
bool SDLInterface::CreateGLContext()
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
m_gl_context = SDL_GL_CreateContext(m_window);
|
||||
if (!m_gl_context || SDL_GL_MakeCurrent(m_window, m_gl_context) != 0 || !gladLoadGL())
|
||||
{
|
||||
// We need a GL context. TODO: Move this to common.
|
||||
SDL_GLContext gl_context = SDL_GL_CreateContext(window.get());
|
||||
if (!gl_context || SDL_GL_MakeCurrent(window.get(), gl_context) != 0 || !gladLoadGL())
|
||||
{
|
||||
Panic("Failed to create GL context");
|
||||
return nullptr;
|
||||
}
|
||||
Panic("Failed to create GL context");
|
||||
return false;
|
||||
}
|
||||
#ifdef Y_COMPILER_MSVC
|
||||
if (display_renderer_backend == DisplayRenderer::BackendType::Direct3D)
|
||||
|
||||
if (GLAD_GL_KHR_debug)
|
||||
{
|
||||
// Get window handle from SDL window
|
||||
SDL_SysWMinfo info = {};
|
||||
SDL_VERSION(&info.version);
|
||||
if (!SDL_GetWindowWMInfo(window.get(), &info))
|
||||
{
|
||||
Panic("SDL_GetWindowWMInfo failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
window_handle = info.info.win.window;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create renderer.
|
||||
auto display_renderer =
|
||||
DisplayRenderer::Create(display_renderer_backend, window_handle, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
|
||||
if (!display_renderer)
|
||||
{
|
||||
Panic("Failed to create display");
|
||||
return nullptr;
|
||||
}
|
||||
display_renderer->SetTopPadding(MAIN_MENU_BAR_HEIGHT);
|
||||
|
||||
// Create audio renderer.
|
||||
auto mixer = MixerType::Create();
|
||||
if (!mixer)
|
||||
{
|
||||
Panic("Failed to create audio mixer");
|
||||
return nullptr;
|
||||
glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
|
||||
// Initialize imgui.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLInterface::CreateImGuiContext()
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
|
||||
switch (display_renderer->GetBackendType())
|
||||
{
|
||||
#ifdef Y_COMPILER_MSVC
|
||||
case DisplayRenderer::BackendType::Direct3D:
|
||||
{
|
||||
if (!ImGui_ImplSDL2_InitForD3D(window.get()) ||
|
||||
!ImGui_ImplDX11_Init(static_cast<DisplayRendererD3D*>(display_renderer.get())->GetD3DDevice(),
|
||||
static_cast<DisplayRendererD3D*>(display_renderer.get())->GetD3DContext()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (!ImGui_ImplSDL2_InitForOpenGL(m_window, m_gl_context) || !ImGui_ImplOpenGL3_Init())
|
||||
return false;
|
||||
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame(window.get());
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DisplayRenderer::BackendType::OpenGL:
|
||||
{
|
||||
if (!ImGui_ImplSDL2_InitForOpenGL(window.get(), SDL_GL_GetCurrentContext()) || !ImGui_ImplOpenGL3_Init())
|
||||
return nullptr;
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame(window.get());
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return std::make_unique<SDLInterface>(window.release(), std::move(display_renderer), std::move(mixer));
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame(m_window);
|
||||
ImGui::NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
TinyString SDLInterface::GetSaveStateFilename(u32 index)
|
||||
bool SDLInterface::CreateGLResources()
|
||||
{
|
||||
return TinyString::FromFormat("savestate_%u.bin", index);
|
||||
static constexpr char fullscreen_quad_vertex_shader[] = R"(
|
||||
#version 330 core
|
||||
|
||||
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;
|
||||
}
|
||||
)";
|
||||
|
||||
static constexpr char display_fragment_shader[] = R"(
|
||||
#version 330 core
|
||||
|
||||
uniform sampler2D samp0;
|
||||
|
||||
in vec2 v_tex0;
|
||||
out vec4 ocol0;
|
||||
|
||||
void main()
|
||||
{
|
||||
ocol0 = texture(samp0, v_tex0);
|
||||
}
|
||||
)";
|
||||
|
||||
if (!m_display_program.Compile(fullscreen_quad_vertex_shader, display_fragment_shader))
|
||||
return false;
|
||||
|
||||
m_display_program.BindFragData();
|
||||
if (!m_display_program.Link())
|
||||
return false;
|
||||
|
||||
m_display_program.RegisterUniform("samp0");
|
||||
m_display_program.Bind();
|
||||
m_display_program.Uniform1ui(0, 0);
|
||||
|
||||
glGenVertexArrays(1, &m_display_vao);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLInterface> SDLInterface::Create()
|
||||
{
|
||||
std::unique_ptr<SDLInterface> intf = std::make_unique<SDLInterface>();
|
||||
if (!intf->CreateSDLWindow() || !intf->CreateGLContext() || !intf->CreateImGuiContext() || !intf->CreateGLResources())
|
||||
return nullptr;
|
||||
|
||||
return intf;
|
||||
}
|
||||
|
||||
// TinyString SDLInterface::GetSaveStateFilename(u32 index)
|
||||
// {
|
||||
// return TinyString::FromFormat("savestate_%u.bin", index);
|
||||
// }
|
||||
|
||||
bool SDLInterface::InitializeSystem(const char* filename, s32 save_state_index /* = -1 */)
|
||||
{
|
||||
Error error;
|
||||
|
||||
m_system = std::make_unique<System>();
|
||||
m_system = std::make_unique<System>(this);
|
||||
if (!m_system->Initialize())
|
||||
{
|
||||
m_system.reset();
|
||||
|
@ -194,6 +175,8 @@ bool SDLInterface::InitializeSystem(const char* filename, s32 save_state_index /
|
|||
}
|
||||
#endif
|
||||
|
||||
m_system->Reset();
|
||||
|
||||
// Resume execution.
|
||||
m_running = true;
|
||||
return true;
|
||||
|
@ -232,88 +215,13 @@ bool SDLInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
|
||||
switch (event->type)
|
||||
{
|
||||
#if 0
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
u32 button = SDLButtonToHostButton(event->button.button);
|
||||
if (IsMouseGrabbed())
|
||||
{
|
||||
ExecuteMouseButtonChangeCallbacks(button, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
u32 button = SDLButtonToHostButton(event->button.button);
|
||||
if (IsMouseGrabbed())
|
||||
{
|
||||
ExecuteMouseButtonChangeCallbacks(button, false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Are we capturing the mouse?
|
||||
if (button == 0)
|
||||
GrabMouse();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
if (!IsMouseGrabbed())
|
||||
return false;
|
||||
|
||||
s32 dx = event->motion.xrel;
|
||||
s32 dy = event->motion.yrel;
|
||||
ExecuteMousePositionChangeCallbacks(dx, dy);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
// Release mouse key combination
|
||||
if (((event->key.keysym.sym == SDLK_LCTRL || event->key.keysym.sym == SDLK_RCTRL) &&
|
||||
(SDL_GetModState() & KMOD_ALT) != 0) ||
|
||||
((event->key.keysym.sym == SDLK_LALT || event->key.keysym.sym == SDLK_RALT) &&
|
||||
(SDL_GetModState() & KMOD_CTRL) != 0))
|
||||
{
|
||||
// But don't consume the key event.
|
||||
ReleaseMouse();
|
||||
}
|
||||
|
||||
// Create keyboard event.
|
||||
// TODO: Since we have crap in the input polling, we can't return true here.
|
||||
GenScanCode scancode;
|
||||
if (MapSDLScanCode(&scancode, event->key.keysym.scancode))
|
||||
{
|
||||
ExecuteKeyboardCallbacks(scancode, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
// Create keyboard event.
|
||||
// TODO: Since we have crap in the input polling, we can't return true here.
|
||||
GenScanCode scancode;
|
||||
if (MapSDLScanCode(&scancode, event->key.keysym.scancode))
|
||||
{
|
||||
ExecuteKeyboardCallbacks(scancode, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
|
||||
m_display_renderer->WindowResized(u32(event->window.data1), u32(event->window.data2));
|
||||
{
|
||||
m_window_width = event->window.data1;
|
||||
m_window_height = event->window.data2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -387,44 +295,39 @@ bool SDLInterface::PassEventToImGui(const SDL_Event* event)
|
|||
|
||||
void SDLInterface::Render()
|
||||
{
|
||||
if (!m_display_renderer->BeginFrame())
|
||||
return;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
m_display_renderer->RenderDisplays();
|
||||
RenderDisplay();
|
||||
|
||||
RenderImGui();
|
||||
|
||||
const DisplayRenderer::BackendType backend_type = m_display_renderer->GetBackendType();
|
||||
switch (backend_type)
|
||||
{
|
||||
#ifdef Y_COMPILER_MSVC
|
||||
case DisplayRenderer::BackendType::Direct3D:
|
||||
{
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
m_display_renderer->EndFrame();
|
||||
ImGui_ImplSDL2_NewFrame(m_window);
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
case DisplayRenderer::BackendType::OpenGL:
|
||||
{
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
m_display_renderer->EndFrame();
|
||||
SDL_GL_SwapWindow(m_window);
|
||||
ImGui_ImplSDL2_NewFrame(m_window);
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
}
|
||||
break;
|
||||
SDL_GL_SwapWindow(m_window);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ImGui_ImplSDL2_NewFrame(m_window);
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void SDLInterface::RenderDisplay()
|
||||
{
|
||||
if (!m_display_texture)
|
||||
return;
|
||||
|
||||
glViewport(0, 0, m_window_width, m_window_height);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
m_display_program.Bind();
|
||||
m_display_texture->Bind();
|
||||
glBindVertexArray(m_display_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
void SDLInterface::RenderImGui()
|
||||
{
|
||||
RenderMainMenuBar();
|
||||
|
@ -499,6 +402,18 @@ void SDLInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/)
|
|||
m_osd_messages.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
void SDLInterface::SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height)
|
||||
{
|
||||
m_display_texture = texture;
|
||||
m_display_texture_offset_x = 0;
|
||||
m_display_texture_offset_y = 0;
|
||||
m_display_texture_width = width;
|
||||
m_display_texture_height = height;
|
||||
m_display_texture_changed = true;
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
void SDLInterface::RenderOSDMessages()
|
||||
{
|
||||
constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
|
@ -561,6 +476,8 @@ void SDLInterface::DoSaveState(u32 index)
|
|||
|
||||
void SDLInterface::Run()
|
||||
{
|
||||
Timer last_render_time;
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
for (;;)
|
||||
|
@ -572,8 +489,12 @@ void SDLInterface::Run()
|
|||
break;
|
||||
}
|
||||
|
||||
m_system->RunFrame();
|
||||
Render();
|
||||
while (!m_display_texture_changed || last_render_time.GetTimeSeconds() < (1.0f / 60.0f))
|
||||
{
|
||||
m_system->RunFrame();
|
||||
}
|
||||
|
||||
// Render();
|
||||
last_render_time.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,43 +1,36 @@
|
|||
#pragma once
|
||||
#include "common/display_renderer.h"
|
||||
#include "pse-sdl/sdl_audio_mixer.h"
|
||||
#include "YBaseLib/String.h"
|
||||
#include "YBaseLib/Timer.h"
|
||||
#include "common/gl_program.h"
|
||||
#include "common/gl_texture.h"
|
||||
#include "pse/host_interface.h"
|
||||
#include <SDL.h>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
struct SDL_Window;
|
||||
union SDL_Event;
|
||||
|
||||
class System;
|
||||
|
||||
class SDLInterface
|
||||
class SDLInterface : public HostInterface
|
||||
{
|
||||
public:
|
||||
using MixerType = SDLAudioMixer;
|
||||
// using MixerType = Audio::NullMixer;
|
||||
|
||||
SDLInterface(SDL_Window* window, std::unique_ptr<DisplayRenderer> display_renderer,
|
||||
std::unique_ptr<MixerType> mixer);
|
||||
SDLInterface();
|
||||
~SDLInterface();
|
||||
|
||||
static std::unique_ptr<SDLInterface>
|
||||
Create(DisplayRenderer::BackendType display_renderer_backend = DisplayRenderer::GetDefaultBackendType());
|
||||
static std::unique_ptr<SDLInterface> Create();
|
||||
|
||||
static TinyString GetSaveStateFilename(u32 index);
|
||||
void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height) override;
|
||||
|
||||
bool InitializeSystem(const char* filename, s32 save_state_index = -1);
|
||||
void ReportMessage(const char* message) override;
|
||||
|
||||
DisplayRenderer* GetDisplayRenderer() const { return m_display_renderer.get(); }
|
||||
Audio::Mixer* GetAudioMixer() const { return m_mixer.get(); }
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
void AddOSDMessage(const char* message, float duration = 2.0f) override;
|
||||
|
||||
void ReportMessage(const char* message);
|
||||
bool InitializeSystem(const char* filename, s32 save_state_index /* = -1 */);
|
||||
|
||||
void Run();
|
||||
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
void AddOSDMessage(const char* message, float duration = 2.0f);
|
||||
|
||||
protected:
|
||||
private:
|
||||
struct OSDMessage
|
||||
{
|
||||
String text;
|
||||
|
@ -45,6 +38,11 @@ protected:
|
|||
float duration;
|
||||
};
|
||||
|
||||
bool CreateSDLWindow();
|
||||
bool CreateGLContext();
|
||||
bool CreateImGuiContext();
|
||||
bool CreateGLResources();
|
||||
|
||||
// We only pass mouse input through if it's grabbed
|
||||
bool IsWindowFullscreen() const;
|
||||
void RenderImGui();
|
||||
|
@ -54,14 +52,24 @@ protected:
|
|||
bool HandleSDLEvent(const SDL_Event* event);
|
||||
bool PassEventToImGui(const SDL_Event* event);
|
||||
void Render();
|
||||
void RenderDisplay();
|
||||
void RenderMainMenuBar();
|
||||
void RenderOSDMessages();
|
||||
|
||||
SDL_Window* m_window;
|
||||
SDL_Window* m_window = nullptr;
|
||||
SDL_GLContext m_gl_context = nullptr;
|
||||
int m_window_width = 0;
|
||||
int m_window_height = 0;
|
||||
|
||||
std::unique_ptr<DisplayRenderer> m_display_renderer;
|
||||
std::unique_ptr<MixerType> m_mixer;
|
||||
std::unique_ptr<System> m_system;
|
||||
GL::Program m_display_program;
|
||||
GLuint m_display_vao = 0;
|
||||
GL::Texture* m_display_texture = nullptr;
|
||||
u32 m_display_texture_offset_x = 0;
|
||||
u32 m_display_texture_offset_y = 0;
|
||||
u32 m_display_texture_width = 0;
|
||||
u32 m_display_texture_height = 0;
|
||||
bool m_display_texture_changed = false;
|
||||
|
||||
std::deque<OSDMessage> m_osd_messages;
|
||||
std::mutex m_osd_messages_lock;
|
||||
|
|
|
@ -8,8 +8,9 @@ GPU::GPU() = default;
|
|||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
bool GPU::Initialize(Bus* bus, DMA* dma)
|
||||
bool GPU::Initialize(System* system, Bus* bus, DMA* dma)
|
||||
{
|
||||
m_system = system;
|
||||
m_bus = bus;
|
||||
m_dma = dma;
|
||||
return true;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "types.h"
|
||||
#include <array>
|
||||
|
||||
class System;
|
||||
class Bus;
|
||||
class DMA;
|
||||
|
||||
|
@ -12,7 +13,7 @@ public:
|
|||
GPU();
|
||||
virtual ~GPU();
|
||||
|
||||
virtual bool Initialize(Bus* bus, DMA* dma);
|
||||
virtual bool Initialize(System* system, Bus* bus, DMA* dma);
|
||||
virtual void Reset();
|
||||
|
||||
u32 ReadRegister(u32 offset);
|
||||
|
@ -101,6 +102,7 @@ protected:
|
|||
virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices);
|
||||
virtual void FlushRender();
|
||||
|
||||
System* m_system = nullptr;
|
||||
Bus* m_bus = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices)
|
|||
|
||||
if (textured)
|
||||
hw_vert.texcoord = (m_GP0_command[buffer_pos++] & UINT32_C(0x0000FFFF));
|
||||
else
|
||||
hw_vert.texcoord = 0;
|
||||
|
||||
m_vertex_staging.push_back(hw_vert);
|
||||
}
|
||||
|
@ -44,6 +46,22 @@ void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices)
|
|||
}
|
||||
}
|
||||
|
||||
void GPU_HW::CalcViewport(int* x, int* y, int* width, int* height)
|
||||
{
|
||||
*x = m_drawing_offset.x;
|
||||
*y = m_drawing_offset.y;
|
||||
*width = std::max(static_cast<int>(VRAM_WIDTH - m_drawing_offset.x), 1);
|
||||
*height = std::max(static_cast<int>(VRAM_HEIGHT - m_drawing_offset.y), 1);
|
||||
}
|
||||
|
||||
void GPU_HW::CalcScissorRect(int* left, int* top, int* right, int* bottom)
|
||||
{
|
||||
*left = m_drawing_area.top_left_x;
|
||||
*right = m_drawing_area.bottom_right_x;
|
||||
*top = m_drawing_area.top_left_y;
|
||||
*bottom = m_drawing_area.bottom_right_y;
|
||||
}
|
||||
|
||||
std::string GPU_HW::GenerateVertexShader(bool textured)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -54,9 +72,9 @@ std::string GPU_HW::GenerateVertexShader(bool textured)
|
|||
ss << "/* #define TEXTURED 0 */\n";
|
||||
|
||||
ss << R"(
|
||||
in uivec2 a_position;
|
||||
in uint a_texcoord;
|
||||
in ivec2 a_position;
|
||||
in vec4 a_color;
|
||||
in uint a_texcoord;
|
||||
|
||||
out vec4 v_color;
|
||||
#if TEXTURED
|
||||
|
@ -65,14 +83,14 @@ out vec4 v_color;
|
|||
|
||||
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);
|
||||
// 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;
|
||||
gl_Position = vec4(pos_x, pos_y, 0.0, 1.0);
|
||||
|
||||
v_color = a_color;
|
||||
#if TEXTURED
|
||||
v_texcoord = a_texcoord;
|
||||
v_texcoord = vec2(float(a_texcoord & 0xFFu) / 256.0, float((a_texcoord >> 8) & 0xFFu) / 256.0);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
@ -99,8 +117,8 @@ out vec4 ocol0;
|
|||
|
||||
void main()
|
||||
{
|
||||
//ocol0 = v_color;
|
||||
ocol0 = vec4(1.0, 0.5, 0.5, 1.0);
|
||||
ocol0 = v_color;
|
||||
//ocol0 = vec4(1.0, 0.5, 0.5, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ protected:
|
|||
|
||||
void LoadVertices(RenderCommand rc, u32 num_vertices);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "gpu_hw_opengl.h"
|
||||
#include "YBaseLib/Assert.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "host_interface.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(GPU_HW_OpenGL);
|
||||
|
||||
GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {}
|
||||
|
@ -10,12 +12,16 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
|||
DestroyFramebuffer();
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::Initialize(Bus* bus, DMA* dma)
|
||||
bool GPU_HW_OpenGL::Initialize(System* system, Bus* bus, DMA* dma)
|
||||
{
|
||||
if (!GPU_HW::Initialize(bus, dma))
|
||||
if (!GPU_HW::Initialize(system, bus, dma))
|
||||
return false;
|
||||
|
||||
CreateFramebuffer();
|
||||
CreateVertexBuffer();
|
||||
if (!CompilePrograms())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -28,16 +34,12 @@ void GPU_HW_OpenGL::Reset()
|
|||
|
||||
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);
|
||||
m_framebuffer_texture =
|
||||
std::make_unique<GL::Texture>(VRAM_WIDTH, VRAM_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, false);
|
||||
|
||||
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);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_framebuffer_texture->GetGLId(), 0);
|
||||
Assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
}
|
||||
|
||||
|
@ -48,6 +50,8 @@ void GPU_HW_OpenGL::ClearFramebuffer()
|
|||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::DestroyFramebuffer()
|
||||
|
@ -55,27 +59,108 @@ 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;
|
||||
m_framebuffer_texture.reset();
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::CreateVertexBuffer()
|
||||
{
|
||||
glGenBuffers(1, &m_vertex_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, 128, nullptr, GL_STREAM_DRAW);
|
||||
|
||||
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);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
GL::Program& prog = texture_enable ? m_texture_program : m_color_program;
|
||||
if (!prog.Compile(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");
|
||||
|
||||
if (!prog.Link())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::SetProgram(bool texture_enable)
|
||||
{
|
||||
GL::Program& prog = texture_enable ? m_texture_program : m_color_program;
|
||||
if (!prog.IsVaild())
|
||||
return false;
|
||||
|
||||
prog.Bind();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::SetViewport()
|
||||
{
|
||||
int x, y, width, height;
|
||||
CalcViewport(&x, &y, &width, &height);
|
||||
|
||||
y = VRAM_HEIGHT - y - height;
|
||||
Log_DebugPrintf("SetViewport: Offset (%d,%d) Size (%d, %d)", x, y, width, height);
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::SetScissor() {}
|
||||
|
||||
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 (!SetProgram(rc.texture_enable))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to set GL program");
|
||||
m_vertex_staging.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (rc.texture_enable)
|
||||
m_texture_program.Bind();
|
||||
else
|
||||
m_color_program.Bind();
|
||||
SetViewport();
|
||||
|
||||
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);
|
||||
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)));
|
||||
|
||||
glDrawArrays(rc.quad_polygon ? GL_TRIANGLE_STRIP : GL_TRIANGLES, 0, static_cast<GLsizei>(m_vertex_staging.size()));
|
||||
|
||||
m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
m_vertex_staging.clear();
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::FlushRender()
|
||||
|
@ -90,98 +175,3 @@ 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;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#pragma once
|
||||
#include "gpu_hw.h"
|
||||
#include "common/gl_program.h"
|
||||
#include "common/gl_texture.h"
|
||||
#include "glad.h"
|
||||
#include "gpu_hw.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class GPU_HW_OpenGL : public GPU_HW
|
||||
{
|
||||
|
@ -9,7 +12,7 @@ public:
|
|||
GPU_HW_OpenGL();
|
||||
~GPU_HW_OpenGL() override;
|
||||
|
||||
bool Initialize(Bus* bus, DMA* dma) override;
|
||||
bool Initialize(System* system, Bus* bus, DMA* dma) override;
|
||||
void Reset() override;
|
||||
|
||||
protected:
|
||||
|
@ -23,22 +26,18 @@ private:
|
|||
|
||||
void CreateVertexBuffer();
|
||||
|
||||
GLuint m_framebuffer_texture_id = 0;
|
||||
bool CompilePrograms();
|
||||
|
||||
bool SetProgram(bool texture_enable);
|
||||
void SetViewport();
|
||||
void SetScissor();
|
||||
|
||||
std::unique_ptr<GL::Texture> m_framebuffer_texture;
|
||||
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;
|
||||
GL::Program m_texture_program;
|
||||
GL::Program m_color_program;
|
||||
};
|
||||
|
||||
|
|
16
src/pse/host_interface.h
Normal file
16
src/pse/host_interface.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
namespace GL {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
class HostInterface
|
||||
{
|
||||
public:
|
||||
virtual void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height) = 0;
|
||||
virtual void ReportMessage(const char* message) = 0;
|
||||
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
virtual void AddOSDMessage(const char* message, float duration = 2.0f) = 0;
|
||||
};
|
|
@ -53,6 +53,7 @@
|
|||
<ClInclude Include="gpu.h" />
|
||||
<ClInclude Include="gpu_hw.h" />
|
||||
<ClInclude Include="gpu_hw_opengl.h" />
|
||||
<ClInclude Include="host_interface.h" />
|
||||
<ClInclude Include="save_state_version.h" />
|
||||
<ClInclude Include="system.h" />
|
||||
<ClInclude Include="types.h" />
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<ClInclude Include="gpu.h" />
|
||||
<ClInclude Include="gpu_hw_opengl.h" />
|
||||
<ClInclude Include="gpu_hw.h" />
|
||||
<ClInclude Include="host_interface.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
#include "dma.h"
|
||||
#include "gpu.h"
|
||||
|
||||
System::System()
|
||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||
{
|
||||
m_cpu = std::make_unique<CPU::Core>();
|
||||
m_bus = std::make_unique<Bus>();
|
||||
m_dma = std::make_unique<DMA>();
|
||||
m_gpu = std::make_unique<GPU>();
|
||||
// m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||
// m_gpu = std::make_unique<GPU>();
|
||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||
}
|
||||
|
||||
System::~System() = default;
|
||||
|
@ -26,7 +26,7 @@ bool System::Initialize()
|
|||
if (!m_dma->Initialize(m_bus.get(), m_gpu.get()))
|
||||
return false;
|
||||
|
||||
if (!m_gpu->Initialize(m_bus.get(), m_dma.get()))
|
||||
if (!m_gpu->Initialize(this, m_bus.get(), m_dma.get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
class HostInterface;
|
||||
|
||||
namespace CPU
|
||||
{
|
||||
class Core;
|
||||
|
@ -13,15 +15,18 @@ class GPU;
|
|||
class System
|
||||
{
|
||||
public:
|
||||
System();
|
||||
System(HostInterface* host_interface);
|
||||
~System();
|
||||
|
||||
HostInterface* GetHostInterface() const { return m_host_interface; }
|
||||
|
||||
bool Initialize();
|
||||
void Reset();
|
||||
|
||||
void RunFrame();
|
||||
|
||||
private:
|
||||
HostInterface* m_host_interface;
|
||||
std::unique_ptr<CPU::Core> m_cpu;
|
||||
std::unique_ptr<Bus> m_bus;
|
||||
std::unique_ptr<DMA> m_dma;
|
||||
|
|
Loading…
Reference in a new issue