prevent race condition around framebuffers

This commit is contained in:
RSDuck 2021-02-02 15:31:32 +01:00
parent b5e601bb88
commit 40aae154cf
4 changed files with 110 additions and 72 deletions

View File

@ -563,7 +563,7 @@ void SetRenderSettings(RenderSettings& settings);
void Stop(); void Stop();
void RenderFrame(); void RenderFrame();
void BindOutputTexture(); void BindOutputTexture(int buf);
} }
#endif #endif

View File

@ -49,8 +49,8 @@ struct CompVertex
CompVertex CompVertices[2 * 3*2]; CompVertex CompVertices[2 * 3*2];
GLuint CompScreenInputTex; GLuint CompScreenInputTex;
GLuint CompScreenOutputTex; GLuint CompScreenOutputTex[2];
GLuint CompScreenOutputFB; GLuint CompScreenOutputFB[2];
bool Init() bool Init()
@ -118,7 +118,7 @@ bool Init()
glEnableVertexAttribArray(1); // texcoord glEnableVertexAttribArray(1); // texcoord
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord))); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord)));
glGenFramebuffers(1, &CompScreenOutputFB); glGenFramebuffers(2, CompScreenOutputFB);
glGenTextures(1, &CompScreenInputTex); glGenTextures(1, &CompScreenInputTex);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -129,12 +129,15 @@ bool Init()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &CompScreenOutputTex); glGenTextures(2, CompScreenOutputTex);
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); for (int i = 0; i < 2; i++)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -143,9 +146,9 @@ bool Init()
void DeInit() void DeInit()
{ {
glDeleteFramebuffers(1, &CompScreenOutputFB); glDeleteFramebuffers(2, CompScreenOutputFB);
glDeleteTextures(1, &CompScreenInputTex); glDeleteTextures(1, &CompScreenInputTex);
glDeleteTextures(1, &CompScreenOutputTex); glDeleteTextures(2, CompScreenOutputTex);
glDeleteVertexArrays(1, &CompVertexArrayID); glDeleteVertexArrays(1, &CompVertexArrayID);
glDeleteBuffers(1, &CompVertexBufferID); glDeleteBuffers(1, &CompVertexBufferID);
@ -167,30 +170,41 @@ void SetRenderSettings(RenderSettings& settings)
ScreenW = 256 * scale; ScreenW = 256 * scale;
ScreenH = (384+2) * scale; ScreenH = (384+2) * scale;
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); for (int i = 0; i < 2; i++)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); {
// fill the padding glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]);
u8 zeroPixels[ScreenW*2*scale*4]; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
memset(zeroPixels, 0, sizeof(zeroPixels)); // fill the padding
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192*scale, ScreenW, 2*scale, GL_RGBA, GL_UNSIGNED_BYTE, zeroPixels); u8 zeroPixels[ScreenW*2*scale*4];
memset(zeroPixels, 0, sizeof(zeroPixels));
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192*scale, ScreenW, 2*scale, GL_RGBA, GL_UNSIGNED_BYTE, zeroPixels);
GLenum fbassign[] = {GL_COLOR_ATTACHMENT0}; GLenum fbassign[] = {GL_COLOR_ATTACHMENT0};
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB); glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB[i]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex[i], 0);
glDrawBuffers(1, fbassign); glDrawBuffers(1, fbassign);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Stop() void Stop()
{ {
RenderFrame(); for (int i = 0; i < 2; i++)
{
int frontbuf = GPU::FrontBuffer;
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]);
glClear(GL_COLOR_BUFFER_BIT);
}
} }
void RenderFrame() void RenderFrame()
{ {
int frontbuf = GPU::FrontBuffer;
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
@ -208,7 +222,6 @@ void RenderFrame()
// TODO: support setting this midframe, if ever needed // TODO: support setting this midframe, if ever needed
glUniform1i(Comp3DXPosLoc[0], ((int)GPU3D::RenderXPos << 23) >> 23); glUniform1i(Comp3DXPosLoc[0], ((int)GPU3D::RenderXPos << 23) >> 23);
int frontbuf = GPU::FrontBuffer;
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
@ -228,9 +241,9 @@ void RenderFrame()
glDrawArrays(GL_TRIANGLES, 0, 4*3); glDrawArrays(GL_TRIANGLES, 0, 4*3);
} }
void BindOutputTexture() void BindOutputTexture(int buf)
{ {
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex); glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[buf]);
} }
} }

View File

@ -490,13 +490,18 @@ void EmuThread::run()
// emulate // emulate
u32 nlines = NDS::RunFrame(); u32 nlines = NDS::RunFrame();
FrontBufferLock.lock();
#ifdef OGLRENDERER_ENABLED #ifdef OGLRENDERER_ENABLED
// this is hacky but this is the easiest way to call
// this function without dealling with a ton of
// macro mess
if (videoRenderer == 1) if (videoRenderer == 1)
epoxy_glFlush(); {
// this is hacky but this is the easiest way to call
// this function without dealling with a ton of
// macro mess
epoxy_glFinish();
}
#endif #endif
FrontBuffer = GPU::FrontBuffer;
FrontBufferLock.unlock();
#ifdef MELONCAP #ifdef MELONCAP
MelonCap::Update(); MelonCap::Update();
@ -824,11 +829,17 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
// fill background // fill background
painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0));
int frontbuf = GPU::FrontBuffer; emuThread->FrontBufferLock.lock();
if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) return; int frontbuf = emuThread->FrontBuffer;
if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1])
{
emuThread->FrontBufferLock.unlock();
return;
}
memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4);
memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4);
emuThread->FrontBufferLock.unlock();
painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0); painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0);
@ -988,53 +999,63 @@ void ScreenPanelGL::paintGL()
glViewport(0, 0, w*factor, h*factor); glViewport(0, 0, w*factor, h*factor);
screenShader->bind(); if (emuThread)
screenShader->setUniformValue("uScreenSize", (float)w*factor, (float)h*factor);
int frontbuf = GPU::FrontBuffer;
glActiveTexture(GL_TEXTURE0);
#ifdef OGLRENDERER_ENABLED
if (GPU::Renderer != 0)
{ {
// hardware-accelerated render screenShader->bind();
GPU::GLCompositor::BindOutputTexture();
}
else
#endif
{
// regular render
glBindTexture(GL_TEXTURE_2D, screenTexture);
if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) screenShader->setUniformValue("uScreenSize", (float)w*factor, (float)h*factor);
emuThread->FrontBufferLock.lock();
int frontbuf = emuThread->FrontBuffer;
glActiveTexture(GL_TEXTURE0);
#ifdef OGLRENDERER_ENABLED
if (GPU::Renderer != 0)
{ {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, // hardware-accelerated render
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); GPU::GLCompositor::BindOutputTexture(frontbuf);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA,
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]);
} }
else
#endif
{
// regular render
glBindTexture(GL_TEXTURE_2D, screenTexture);
if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1])
{
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA,
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA,
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]);
}
}
GLint filter = Config::ScreenFilter ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer);
glBindVertexArray(screenVertexArray);
GLint transloc = screenShader->uniformLocation("uTransform");
for (int i = 0; i < numScreens; i++)
{
glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[i]);
glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3);
}
screenShader->release();
} }
GLint filter = Config::ScreenFilter ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer);
glBindVertexArray(screenVertexArray);
GLint transloc = screenShader->uniformLocation("uTransform");
for (int i = 0; i < numScreens; i++)
{
glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[i]);
glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3);
}
screenShader->release();
OSD::Update(this); OSD::Update(this);
OSD::DrawGL(this, w*factor, h*factor); OSD::DrawGL(this, w*factor, h*factor);
if (emuThread)
{
glFinish();
emuThread->FrontBufferLock.unlock();
}
} }
void ScreenPanelGL::resizeEvent(QResizeEvent* event) void ScreenPanelGL::resizeEvent(QResizeEvent* event)

View File

@ -26,6 +26,7 @@
#include <QImage> #include <QImage>
#include <QActionGroup> #include <QActionGroup>
#include <QTimer> #include <QTimer>
#include <QMutex>
#include <QOffscreenSurface> #include <QOffscreenSurface>
#include <QOpenGLWidget> #include <QOpenGLWidget>
@ -59,6 +60,9 @@ public:
bool emuIsRunning(); bool emuIsRunning();
int FrontBuffer = 0;
QMutex FrontBufferLock;
signals: signals:
void windowUpdate(); void windowUpdate();
void windowTitleChange(QString title); void windowTitleChange(QString title);