mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 14:10:04 +00:00
Add texture mailbox support to opengl renderer.
This commit is contained in:
parent
c2e7903825
commit
27d0fc64d0
@ -19,21 +19,21 @@ class Backend;
|
|||||||
|
|
||||||
class RendererBase : NonCopyable {
|
class RendererBase : NonCopyable {
|
||||||
public:
|
public:
|
||||||
/// Used to reference a framebuffer
|
|
||||||
enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
|
|
||||||
|
|
||||||
explicit RendererBase(Frontend::EmuWindow& window);
|
explicit RendererBase(Frontend::EmuWindow& window);
|
||||||
virtual ~RendererBase();
|
virtual ~RendererBase();
|
||||||
|
|
||||||
/// Swap buffers (render frame)
|
|
||||||
virtual void SwapBuffers() = 0;
|
|
||||||
|
|
||||||
/// Initialize the renderer
|
/// Initialize the renderer
|
||||||
virtual Core::System::ResultStatus Init() = 0;
|
virtual Core::System::ResultStatus Init() = 0;
|
||||||
|
|
||||||
/// Shutdown the renderer
|
/// Shutdown the renderer
|
||||||
virtual void ShutDown() = 0;
|
virtual void ShutDown() = 0;
|
||||||
|
|
||||||
|
/// Finalize rendering the guest frame and draw into the presentation texture
|
||||||
|
virtual void SwapBuffers() = 0;
|
||||||
|
|
||||||
|
/// Draws the latest frame to the window (Renderer specific implementation)
|
||||||
|
virtual void Present() = 0;
|
||||||
|
|
||||||
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
||||||
virtual void PrepareVideoDumping() = 0;
|
virtual void PrepareVideoDumping() = 0;
|
||||||
|
|
||||||
|
@ -28,8 +28,50 @@
|
|||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
namespace Frontend {
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
GLuint index;
|
||||||
|
GLsync render_sync;
|
||||||
|
GLsync present_sync;
|
||||||
|
};
|
||||||
|
} // namespace Frontend
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
|
class OGLTextureMailbox : public Frontend::TextureMailbox {
|
||||||
|
public:
|
||||||
|
Frontend::Frame render_tex = {0, 0, 0}, present_tex = {1, 0, 0}, off_tex = {2, 0, 0};
|
||||||
|
bool swapped = false;
|
||||||
|
std::mutex swap_mutex{};
|
||||||
|
|
||||||
|
OGLTextureMailbox() = default;
|
||||||
|
|
||||||
|
~OGLTextureMailbox() = default;
|
||||||
|
|
||||||
|
Frontend::Frame& GetRenderFrame() {
|
||||||
|
return render_tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderComplete() {
|
||||||
|
std::scoped_lock lock(swap_mutex);
|
||||||
|
swapped = true;
|
||||||
|
std::swap(render_tex, off_tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Frontend::Frame& GetPresentationFrame() {
|
||||||
|
return present_tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PresentationComplete() {
|
||||||
|
std::scoped_lock lock(swap_mutex);
|
||||||
|
if (swapped) {
|
||||||
|
swapped = false;
|
||||||
|
std::swap(present_tex, off_tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const char vertex_shader[] = R"(
|
static const char vertex_shader[] = R"(
|
||||||
in vec2 vert_position;
|
in vec2 vert_position;
|
||||||
in vec2 vert_tex_coord;
|
in vec2 vert_tex_coord;
|
||||||
@ -53,7 +95,7 @@ void main() {
|
|||||||
|
|
||||||
static const char fragment_shader[] = R"(
|
static const char fragment_shader[] = R"(
|
||||||
in vec2 frag_tex_coord;
|
in vec2 frag_tex_coord;
|
||||||
out vec4 color;
|
layout(location = 0) out vec4 color;
|
||||||
|
|
||||||
uniform vec4 i_resolution;
|
uniform vec4 i_resolution;
|
||||||
uniform vec4 o_resolution;
|
uniform vec4 o_resolution;
|
||||||
@ -130,7 +172,10 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
|||||||
return matrix;
|
return matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window) : RendererBase{window} {}
|
RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window) : RendererBase{window} {
|
||||||
|
window.mailbox = std::make_unique<OGLTextureMailbox>();
|
||||||
|
}
|
||||||
|
|
||||||
RendererOpenGL::~RendererOpenGL() = default;
|
RendererOpenGL::~RendererOpenGL() = default;
|
||||||
|
|
||||||
/// Swap buffers (render frame)
|
/// Swap buffers (render frame)
|
||||||
@ -230,20 +275,66 @@ void RendererOpenGL::SwapBuffers() {
|
|||||||
|
|
||||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
||||||
current_pbo = (current_pbo + 1) % 2;
|
current_pbo = (current_pbo + 1) % 2;
|
||||||
next_pbo = (current_pbo + 1) % 2;
|
next_pbo = (current_pbo + 1) % 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawScreens(render_window.GetFramebufferLayout());
|
const auto& layout = render_window.GetFramebufferLayout();
|
||||||
|
auto& frame = render_window.mailbox->GetRenderFrame();
|
||||||
|
auto& presentation = presentation_textures[frame.index];
|
||||||
|
|
||||||
|
// Clean up sync objects before drawing
|
||||||
|
|
||||||
|
// INTEL driver workaround. We can't delete the previous render sync object until we are sure
|
||||||
|
// that the presentation is done
|
||||||
|
if (frame.present_sync) {
|
||||||
|
glClientWaitSync(frame.present_sync, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the draw fence if the frame wasn't presented
|
||||||
|
if (frame.render_sync) {
|
||||||
|
glDeleteSync(frame.render_sync);
|
||||||
|
frame.render_sync = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the presentation to be done
|
||||||
|
if (frame.present_sync) {
|
||||||
|
glWaitSync(frame.present_sync, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
glDeleteSync(frame.present_sync);
|
||||||
|
frame.present_sync = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recreate the presentation texture if the size of the window has changed
|
||||||
|
if (layout.width != presentation.width || layout.height != presentation.height) {
|
||||||
|
presentation.width = layout.width;
|
||||||
|
presentation.height = layout.height;
|
||||||
|
presentation.texture.Release();
|
||||||
|
presentation.texture.Create();
|
||||||
|
state.texture_units[0].texture_2d = presentation.texture.handle;
|
||||||
|
state.Apply();
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, layout.width, layout.height, 0, GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE, 0);
|
||||||
|
state.texture_units[0].texture_2d = 0;
|
||||||
|
state.Apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint render_texture = presentation.texture.handle;
|
||||||
|
state.draw.draw_framebuffer = draw_framebuffer.handle;
|
||||||
|
state.Apply();
|
||||||
|
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, render_texture, 0);
|
||||||
|
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
|
||||||
|
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
|
||||||
|
DrawScreens(layout);
|
||||||
|
// Create a fence for the frontend to wait on and swap this frame to OffTex
|
||||||
|
frame.render_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
glFlush();
|
||||||
|
render_window.mailbox->RenderComplete();
|
||||||
m_current_frame++;
|
m_current_frame++;
|
||||||
|
|
||||||
Core::System::GetInstance().perf_stats->EndSystemFrame();
|
Core::System::GetInstance().perf_stats->EndSystemFrame();
|
||||||
|
|
||||||
// Swap buffers
|
|
||||||
render_window.PollEvents();
|
render_window.PollEvents();
|
||||||
render_window.SwapBuffers();
|
|
||||||
|
|
||||||
Core::System::GetInstance().frame_limiter.DoFrameLimiting(
|
Core::System::GetInstance().frame_limiter.DoFrameLimiting(
|
||||||
Core::System::GetInstance().CoreTiming().GetGlobalTimeUs());
|
Core::System::GetInstance().CoreTiming().GetGlobalTimeUs());
|
||||||
@ -388,6 +479,11 @@ void RendererOpenGL::InitOpenGLObjects() {
|
|||||||
screen_info.display_texture = screen_info.texture.resource.handle;
|
screen_info.display_texture = screen_info.texture.resource.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw_framebuffer.Create();
|
||||||
|
presentation_framebuffer.Create();
|
||||||
|
presentation_textures[0].texture.Create();
|
||||||
|
presentation_textures[1].texture.Create();
|
||||||
|
presentation_textures[2].texture.Create();
|
||||||
state.texture_units[0].texture_2d = 0;
|
state.texture_units[0].texture_2d = 0;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
}
|
}
|
||||||
@ -669,6 +765,38 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::Present() {
|
||||||
|
const auto& layout = render_window.GetFramebufferLayout();
|
||||||
|
auto& frame = render_window.mailbox->GetPresentationFrame();
|
||||||
|
const auto& presentation = presentation_textures[frame.index];
|
||||||
|
const GLuint texture_handle = presentation.texture.handle;
|
||||||
|
|
||||||
|
glWaitSync(frame.render_sync, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
// INTEL workaround.
|
||||||
|
// Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
|
||||||
|
// it on the emulation thread without too much penalty
|
||||||
|
// glDeleteSync(frame.render_sync);
|
||||||
|
// frame.render_sync = 0;
|
||||||
|
|
||||||
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
|
0.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, presentation_framebuffer.handle);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture_handle);
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_handle,
|
||||||
|
0);
|
||||||
|
glBlitFramebuffer(0, 0, presentation.width, presentation.height, 0, 0, layout.width,
|
||||||
|
layout.height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
|
|
||||||
|
/* insert fence for the main thread to block on */
|
||||||
|
frame.present_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
glFlush();
|
||||||
|
render_window.mailbox->PresentationComplete();
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates the framerate
|
/// Updates the framerate
|
||||||
void RendererOpenGL::UpdateFramerate() {}
|
void RendererOpenGL::UpdateFramerate() {}
|
||||||
|
|
||||||
@ -766,7 +894,9 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
|
|||||||
|
|
||||||
/// Initialize the renderer
|
/// Initialize the renderer
|
||||||
Core::System::ResultStatus RendererOpenGL::Init() {
|
Core::System::ResultStatus RendererOpenGL::Init() {
|
||||||
render_window.MakeCurrent();
|
if (!gladLoadGL()) {
|
||||||
|
return Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33;
|
||||||
|
}
|
||||||
|
|
||||||
if (GLAD_GL_KHR_debug) {
|
if (GLAD_GL_KHR_debug) {
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
|
@ -36,20 +36,30 @@ struct ScreenInfo {
|
|||||||
TextureInfo texture;
|
TextureInfo texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PresentationTexture {
|
||||||
|
u32 width = 0;
|
||||||
|
u32 height = 0;
|
||||||
|
OGLTexture texture;
|
||||||
|
};
|
||||||
|
|
||||||
class RendererOpenGL : public RendererBase {
|
class RendererOpenGL : public RendererBase {
|
||||||
public:
|
public:
|
||||||
explicit RendererOpenGL(Frontend::EmuWindow& window);
|
explicit RendererOpenGL(Frontend::EmuWindow& window);
|
||||||
~RendererOpenGL() override;
|
~RendererOpenGL() override;
|
||||||
|
|
||||||
/// Swap buffers (render frame)
|
|
||||||
void SwapBuffers() override;
|
|
||||||
|
|
||||||
/// Initialize the renderer
|
/// Initialize the renderer
|
||||||
Core::System::ResultStatus Init() override;
|
Core::System::ResultStatus Init() override;
|
||||||
|
|
||||||
/// Shutdown the renderer
|
/// Shutdown the renderer
|
||||||
void ShutDown() override;
|
void ShutDown() override;
|
||||||
|
|
||||||
|
/// Finalizes rendering the guest frame
|
||||||
|
void SwapBuffers() override;
|
||||||
|
|
||||||
|
/// Draws the latest frame from texture mailbox to the currently bound draw framebuffer in this
|
||||||
|
/// context
|
||||||
|
void Present() override;
|
||||||
|
|
||||||
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
||||||
void PrepareVideoDumping() override;
|
void PrepareVideoDumping() override;
|
||||||
|
|
||||||
@ -117,6 +127,11 @@ private:
|
|||||||
std::array<OGLBuffer, 2> frame_dumping_pbos;
|
std::array<OGLBuffer, 2> frame_dumping_pbos;
|
||||||
GLuint current_pbo = 1;
|
GLuint current_pbo = 1;
|
||||||
GLuint next_pbo = 0;
|
GLuint next_pbo = 0;
|
||||||
|
|
||||||
|
// Textures used for presentation
|
||||||
|
OGLFramebuffer draw_framebuffer;
|
||||||
|
OGLFramebuffer presentation_framebuffer;
|
||||||
|
std::array<PresentationTexture, 3> presentation_textures{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
Loading…
Reference in New Issue
Block a user