Add texture mailbox support to opengl renderer.

This commit is contained in:
James Rowe 2019-09-16 20:37:43 -06:00
parent c2e7903825
commit 27d0fc64d0
3 changed files with 162 additions and 17 deletions

View file

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

View file

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

View file

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