GPU: More work on OpenGL renderer

This commit is contained in:
Connor McLaughlin 2019-09-13 00:18:13 +10:00
parent 4706a906d5
commit aea7a18ac2
20 changed files with 655 additions and 412 deletions

View file

@ -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" />

View file

@ -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
View 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
View 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
View 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
View 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

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);
}
)";

View file

@ -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);

View file

@ -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;
}

View file

@ -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
View 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;
};

View file

@ -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" />

View file

@ -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" />

View file

@ -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;

View file

@ -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;