Add texture mailbox support to opengl renderer.
This commit is contained in:
parent
c2e7903825
commit
27d0fc64d0
3 changed files with 162 additions and 17 deletions
|
@ -19,21 +19,21 @@ class Backend;
|
|||
|
||||
class RendererBase : NonCopyable {
|
||||
public:
|
||||
/// Used to reference a framebuffer
|
||||
enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
|
||||
|
||||
explicit RendererBase(Frontend::EmuWindow& window);
|
||||
virtual ~RendererBase();
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
virtual void SwapBuffers() = 0;
|
||||
|
||||
/// Initialize the renderer
|
||||
virtual Core::System::ResultStatus Init() = 0;
|
||||
|
||||
/// Shutdown the renderer
|
||||
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)
|
||||
virtual void PrepareVideoDumping() = 0;
|
||||
|
||||
|
|
|
@ -28,8 +28,50 @@
|
|||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
struct Frame {
|
||||
GLuint index;
|
||||
GLsync render_sync;
|
||||
GLsync present_sync;
|
||||
};
|
||||
} // namespace Frontend
|
||||
|
||||
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"(
|
||||
in vec2 vert_position;
|
||||
in vec2 vert_tex_coord;
|
||||
|
@ -53,7 +95,7 @@ void main() {
|
|||
|
||||
static const char fragment_shader[] = R"(
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
uniform vec4 i_resolution;
|
||||
uniform vec4 o_resolution;
|
||||
|
@ -130,7 +172,10 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
|||
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;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
|
@ -230,20 +275,66 @@ void RendererOpenGL::SwapBuffers() {
|
|||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
current_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++;
|
||||
|
||||
Core::System::GetInstance().perf_stats->EndSystemFrame();
|
||||
|
||||
// Swap buffers
|
||||
render_window.PollEvents();
|
||||
render_window.SwapBuffers();
|
||||
|
||||
Core::System::GetInstance().frame_limiter.DoFrameLimiting(
|
||||
Core::System::GetInstance().CoreTiming().GetGlobalTimeUs());
|
||||
|
@ -388,6 +479,11 @@ void RendererOpenGL::InitOpenGLObjects() {
|
|||
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.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
|
||||
void RendererOpenGL::UpdateFramerate() {}
|
||||
|
||||
|
@ -766,7 +894,9 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
|
|||
|
||||
/// Initialize the renderer
|
||||
Core::System::ResultStatus RendererOpenGL::Init() {
|
||||
render_window.MakeCurrent();
|
||||
if (!gladLoadGL()) {
|
||||
return Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL33;
|
||||
}
|
||||
|
||||
if (GLAD_GL_KHR_debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
|
|
|
@ -36,20 +36,30 @@ struct ScreenInfo {
|
|||
TextureInfo texture;
|
||||
};
|
||||
|
||||
struct PresentationTexture {
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
OGLTexture texture;
|
||||
};
|
||||
|
||||
class RendererOpenGL : public RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Frontend::EmuWindow& window);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
void SwapBuffers() override;
|
||||
|
||||
/// Initialize the renderer
|
||||
Core::System::ResultStatus Init() override;
|
||||
|
||||
/// Shutdown the renderer
|
||||
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)
|
||||
void PrepareVideoDumping() override;
|
||||
|
||||
|
@ -117,6 +127,11 @@ private:
|
|||
std::array<OGLBuffer, 2> frame_dumping_pbos;
|
||||
GLuint current_pbo = 1;
|
||||
GLuint next_pbo = 0;
|
||||
|
||||
// Textures used for presentation
|
||||
OGLFramebuffer draw_framebuffer;
|
||||
OGLFramebuffer presentation_framebuffer;
|
||||
std::array<PresentationTexture, 3> presentation_textures{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
Loading…
Reference in a new issue