diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index c519cc6bd..78d323a51 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -43,6 +43,8 @@ + + @@ -59,6 +61,8 @@ + + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 51f685c13..385089087 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -17,6 +17,8 @@ + + @@ -31,6 +33,8 @@ + + diff --git a/src/common/gl_program.cpp b/src/common/gl_program.cpp new file mode 100644 index 000000000..913b4d514 --- /dev/null +++ b/src/common/gl_program.cpp @@ -0,0 +1,171 @@ +#include "gl_program.h" +#include "YBaseLib/Log.h" +#include +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 sources = {{source}}; + std::array source_lengths = {{static_cast(std::strlen(source))}}; + glShaderSource(id, static_cast(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(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 \ No newline at end of file diff --git a/src/common/gl_program.h b/src/common/gl_program.h new file mode 100644 index 000000000..bd97de244 --- /dev/null +++ b/src/common/gl_program.h @@ -0,0 +1,42 @@ +#pragma once +#include "glad.h" +#include "types.h" +#include + +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 m_uniform_locations; +}; + +} // namespace GL \ No newline at end of file diff --git a/src/common/gl_texture.cpp b/src/common/gl_texture.cpp new file mode 100644 index 000000000..7640b26c7 --- /dev/null +++ b/src/common/gl_texture.cpp @@ -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 \ No newline at end of file diff --git a/src/common/gl_texture.h b/src/common/gl_texture.h new file mode 100644 index 000000000..4e4e56d17 --- /dev/null +++ b/src/common/gl_texture.h @@ -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 \ No newline at end of file diff --git a/src/pse-sdl/main.cpp b/src/pse-sdl/main.cpp index b985ba0f2..5783dc7df 100644 --- a/src/pse-sdl/main.cpp +++ b/src/pse-sdl/main.cpp @@ -7,6 +7,7 @@ #include #include +#if 0 static int NoGUITest() { std::unique_ptr system = std::make_unique(); @@ -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 [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); } diff --git a/src/pse-sdl/sdl_interface.cpp b/src/pse-sdl/sdl_interface.cpp index 39cb95eda..d8554034b 100644 --- a/src/pse-sdl/sdl_interface.cpp +++ b/src/pse-sdl/sdl_interface.cpp @@ -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 #include #include -#ifdef Y_COMPILER_MSVC -#include "imgui_impl_dx11.h" -#include -#endif +Log_SetChannel(SDLInterface); -SDLInterface::SDLInterface(SDL_Window* window, std::unique_ptr display_renderer, - std::unique_ptr 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::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_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(display_renderer.get())->GetD3DDevice(), - static_cast(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(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::Create() +{ + std::unique_ptr intf = std::make_unique(); + 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(); + m_system = std::make_unique(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(); } } - diff --git a/src/pse-sdl/sdl_interface.h b/src/pse-sdl/sdl_interface.h index 9c49c15af..17fed0e41 100644 --- a/src/pse-sdl/sdl_interface.h +++ b/src/pse-sdl/sdl_interface.h @@ -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 #include #include #include -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 display_renderer, - std::unique_ptr mixer); + SDLInterface(); ~SDLInterface(); - static std::unique_ptr - Create(DisplayRenderer::BackendType display_renderer_backend = DisplayRenderer::GetDefaultBackendType()); + static std::unique_ptr 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 m_display_renderer; - std::unique_ptr m_mixer; std::unique_ptr 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 m_osd_messages; std::mutex m_osd_messages_lock; diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 7cb0aa03b..61613d73f 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -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; diff --git a/src/pse/gpu.h b/src/pse/gpu.h index 7079a3994..252d91278 100644 --- a/src/pse/gpu.h +++ b/src/pse/gpu.h @@ -3,6 +3,7 @@ #include "types.h" #include +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; diff --git a/src/pse/gpu_hw.cpp b/src/pse/gpu_hw.cpp index 0acaa93c1..efb2c7b2c 100644 --- a/src/pse/gpu_hw.cpp +++ b/src/pse/gpu_hw.cpp @@ -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(VRAM_WIDTH - m_drawing_offset.x), 1); + *height = std::max(static_cast(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); } )"; diff --git a/src/pse/gpu_hw.h b/src/pse/gpu_hw.h index 227fe5cc3..28b05c405 100644 --- a/src/pse/gpu_hw.h +++ b/src/pse/gpu_hw.h @@ -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); diff --git a/src/pse/gpu_hw_opengl.cpp b/src/pse/gpu_hw_opengl.cpp index bd7374dd7..d6f5b20f3 100644 --- a/src/pse/gpu_hw_opengl.cpp +++ b/src/pse/gpu_hw_opengl.cpp @@ -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(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(offsetof(HWVertex, x))); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(HWVertex), + reinterpret_cast(offsetof(HWVertex, color))); + glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(HWVertex), reinterpret_cast(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(sizeof(HWVertex) * m_vertex_staging.size()), m_vertex_staging.data(), GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribIPointer(0, 2, GL_INT, sizeof(HWVertex), reinterpret_cast(offsetof(HWVertex, x))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(HWVertex), + reinterpret_cast(offsetof(HWVertex, color))); + glEnableVertexAttribArray(2); + glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(HWVertex), reinterpret_cast(offsetof(HWVertex, color))); + glDrawArrays(rc.quad_polygon ? GL_TRIANGLE_STRIP : GL_TRIANGLES, 0, static_cast(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::CreateHardwareOpenGLRenderer() { return std::make_unique(); } - -void GPU_HW_OpenGL::Program::Bind() -{ - glUseProgram(program_id); -} - -static GLuint CompileShader(GLenum type, const std::string& source) -{ - GLuint id = glCreateShader(type); - - std::array sources = {{source.c_str()}}; - std::array source_lengths = {{static_cast(source.size())}}; - glShaderSource(id, static_cast(source.size()), sources.data(), source_lengths.data()); - - GLint status = GL_FALSE; - glGetShaderiv(id, GL_COMPILE_STATUS, &status); - - GLint info_log_length = 0; - glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length); - - if (status == GL_FALSE || info_log_length > 0) - { - std::string info_log; - info_log.resize(info_log_length + 1); - glGetShaderInfoLog(id, info_log_length, &info_log_length, info_log.data()); - - if (status == GL_TRUE) - { - Log_ErrorPrintf("Shader compiled with warnings:\n%s", info_log.c_str()); - } - else - { - Log_ErrorPrintf("Shader failed to compile:\n%s", info_log.c_str()); - glDeleteShader(id); - return 0; - } - } - - return id; -} - -bool GPU_HW_OpenGL::Program::Compile(const std::string& vertex_shader, const std::string& fragment_shader) -{ - GLuint vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader); - if (vertex_shader_id == 0) - return false; - - GLuint fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader); - if (fragment_shader_id == 0) - { - glDeleteShader(vertex_shader_id); - return false; - } - - program_id = glCreateProgram(); - glAttachShader(program_id, vertex_shader_id); - glAttachShader(program_id, fragment_shader_id); - - glBindAttribLocation(program_id, 0, "a_position"); - glBindAttribLocation(program_id, 1, "a_texcoord"); - glBindAttribLocation(program_id, 2, "a_color"); - glBindFragDataLocation(program_id, 0, "ocol0"); - - glLinkProgram(program_id); - - glDeleteShader(vertex_shader_id); - glDeleteShader(fragment_shader_id); - - GLint status = GL_FALSE; - glGetProgramiv(program_id, GL_LINK_STATUS, &status); - - GLint info_log_length = 0; - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length); - - if (status == GL_FALSE || info_log_length > 0) - { - std::string info_log; - info_log.resize(info_log_length + 1); - glGetProgramInfoLog(program_id, info_log_length, &info_log_length, info_log.data()); - - if (status == GL_TRUE) - { - Log_ErrorPrintf("Program linked with warnings:\n%s", info_log.c_str()); - } - else - { - Log_ErrorPrintf("Program failed to link:\n%s", info_log.c_str()); - glDeleteProgram(program_id); - program_id = 0; - return false; - } - } - - return true; -} diff --git a/src/pse/gpu_hw_opengl.h b/src/pse/gpu_hw_opengl.h index 8275c7e87..cdb89403b 100644 --- a/src/pse/gpu_hw_opengl.h +++ b/src/pse/gpu_hw_opengl.h @@ -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 +#include 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 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; }; - diff --git a/src/pse/host_interface.h b/src/pse/host_interface.h new file mode 100644 index 000000000..cd571a034 --- /dev/null +++ b/src/pse/host_interface.h @@ -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; +}; diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 3cfb8382b..266d913ab 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -53,6 +53,7 @@ + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 3aff24430..41b27a8b9 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -22,6 +22,7 @@ + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 81daff37e..c255e09e5 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -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(); m_bus = std::make_unique(); m_dma = std::make_unique(); - m_gpu = std::make_unique(); - // m_gpu = GPU::CreateHardwareOpenGLRenderer(); + // m_gpu = std::make_unique(); + 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; diff --git a/src/pse/system.h b/src/pse/system.h index bbebfc1c3..b48baeffa 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -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 m_cpu; std::unique_ptr m_bus; std::unique_ptr m_dma;