Compute shader renderer (#2041)

* nothing works yet

* don't double buffer 3D framebuffers for the GL Renderer
looks like leftovers from when 3D+2D composition was done in the frontend

* oops

* it works!

* implement display capture for compute renderer
it's actually just all stolen from the regular OpenGL renderer

* fix bad indirect call

* handle cleanup properly

* add hires rendering to the compute shader renderer

* fix UB
also misc changes to use more unsigned multiplication
also fix framebuffer resize

* correct edge filling behaviour when AA is disabled

* fix full color textures

* fix edge marking (polygon id is 6-bit not 5)
also make the code a bit nicer

* take all edge cases into account for XMin/XMax calculation

* use hires coordinate again

* stop using fixed size buffers based on scale factor in shaders
this makes shader compile times tolerable on Wintel
- beginning of the shader cache
- increase size of tile idx in workdesc to 20 bits

* apparently & is not defined on bvec4
why does this even compile on Intel and Nvidia?

* put the texture cache into it's own file

* add compute shader renderer properly to the GUI
also add option to toggle using high resolution vertex coordinates

* unbind sampler object in compute shader renderer

* fix GetRangedBitMask for 64 bit aligned 64 bits
pretty embarassing

* convert NonStupidBitfield.h back to LF only new lines

* actually adapt to latest changes

* fix stupid merge

* actually make compute shader renderer work with newest changes

* show progress on shader compilation

* remove merge leftover
This commit is contained in:
RSDuck
2024-05-13 17:17:39 +02:00
committed by GitHub
parent c85a2103bb
commit 043244a56d
35 changed files with 4389 additions and 382 deletions

View File

@ -22,6 +22,7 @@
#include <inttypes.h>
#include "Platform.h"
#include "Config.h"
#include "GPU.h"
namespace Config
@ -59,6 +60,7 @@ bool Threaded3D;
int GL_ScaleFactor;
bool GL_BetterPolygons;
bool GL_HiresCoordinates;
bool LimitFPS;
int MaxFPS;
@ -246,11 +248,12 @@ ConfigEntry ConfigFile[] =
{"ScreenVSync", 1, &ScreenVSync, false, false},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, false},
{"3DRenderer", 0, &_3DRenderer, 0, false},
{"3DRenderer", 0, &_3DRenderer, renderer3D_Software, false},
{"Threaded3D", 1, &Threaded3D, true, false},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, false},
{"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false},
{"GL_HiresCoordinates", 1, &GL_HiresCoordinates, true, false},
{"LimitFPS", 1, &LimitFPS, true, false},
{"MaxFPS", 0, &MaxFPS, 1000, false},

View File

@ -51,6 +51,16 @@ enum
micInputType_MAX,
};
enum
{
renderer3D_Software = 0,
#ifdef OGLRENDERER_ENABLED
renderer3D_OpenGL,
renderer3D_OpenGLCompute,
#endif
renderer3D_Max,
};
namespace Config
{
@ -103,6 +113,7 @@ extern bool Threaded3D;
extern int GL_ScaleFactor;
extern bool GL_BetterPolygons;
extern bool GL_HiresCoordinates;
extern bool LimitFPS;
extern int MaxFPS;

View File

@ -52,10 +52,12 @@
#include "DSi_I2C.h"
#include "GPU3D_Soft.h"
#include "GPU3D_OpenGL.h"
#include "GPU3D_Compute.h"
#include "Savestate.h"
#include "ROMManager.h"
#include "EmuThread.h"
//#include "ArchiveUtil.h"
//#include "CameraManager.h"
@ -94,9 +96,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
}
std::unique_ptr<NDS> EmuThread::CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
) noexcept
std::unique_ptr<melonDS::NDSCart::CartCommon> &&ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon> &&gbacart) noexcept
{
auto arm7bios = ROMManager::LoadARM7BIOS();
if (!arm7bios)
@ -326,21 +327,12 @@ void EmuThread::run()
videoRenderer = 0;
}
if (videoRenderer == 0)
{ // If we're using the software renderer...
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(Config::Threaded3D != 0));
}
else
{
auto glrenderer = melonDS::GLRenderer::New();
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
updateRenderer();
Input::Init();
u32 nframes = 0;
double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
double lastTime = SDL_GetPerformanceCounter() * perfCountsSec;
double frameLimitError = 0.0;
double lastMeasureTime = lastTime;
@ -451,20 +443,9 @@ void EmuThread::run()
videoRenderer = 0;
}
videoRenderer = screenGL ? Config::_3DRenderer : 0;
updateRenderer();
videoSettingsDirty = false;
if (videoRenderer == 0)
{ // If we're using the software renderer...
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(Config::Threaded3D != 0));
}
else
{
auto glrenderer = melonDS::GLRenderer::New();
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
}
// process input and hotkeys
@ -512,7 +493,16 @@ void EmuThread::run()
// emulate
u32 nlines = NDS->RunFrame();
u32 nlines;
if (NDS->GPU.GetRenderer3D().NeedsShaderCompile())
{
compileShaders();
nlines = 0;
}
else
{
nlines = NDS->RunFrame();
}
if (ROMManager::NDSSave)
ROMManager::NDSSave->CheckFlush();
@ -750,3 +740,53 @@ bool EmuThread::emuIsActive()
{
return (RunningSomething == 1);
}
void EmuThread::updateRenderer()
{
if (videoRenderer != lastVideoRenderer)
{
printf("creating renderer %d\n", videoRenderer);
switch (videoRenderer)
{
case renderer3D_Software:
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>());
break;
case renderer3D_OpenGL:
NDS->GPU.SetRenderer3D(GLRenderer::New());
break;
case renderer3D_OpenGLCompute:
NDS->GPU.SetRenderer3D(ComputeRenderer::New());
break;
default: __builtin_unreachable();
}
}
lastVideoRenderer = videoRenderer;
switch (videoRenderer)
{
case renderer3D_Software:
static_cast<SoftRenderer&>(NDS->GPU.GetRenderer3D()).SetThreaded(Config::Threaded3D, NDS->GPU);
break;
case renderer3D_OpenGL:
static_cast<GLRenderer&>(NDS->GPU.GetRenderer3D()).SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
break;
case renderer3D_OpenGLCompute:
static_cast<ComputeRenderer&>(NDS->GPU.GetRenderer3D()).SetRenderSettings(Config::GL_ScaleFactor, Config::GL_HiresCoordinates);
break;
default: __builtin_unreachable();
}
}
void EmuThread::compileShaders()
{
int currentShader, shadersCount;
u64 startTime = SDL_GetPerformanceCounter();
// kind of hacky to look at the wallclock, though it is easier than
// than disabling vsync
do
{
NDS->GPU.GetRenderer3D().ShaderCompileStep(currentShader, shadersCount);
} while (NDS->GPU.GetRenderer3D().NeedsShaderCompile() &&
(SDL_GetPerformanceCounter() - startTime) * perfCountsSec < 1.0 / 6.0);
mainWindow->osdAddMessage(0, "Compiling shader %d/%d", currentShader+1, shadersCount);
}

View File

@ -94,6 +94,9 @@ signals:
void syncVolumeLevel();
private:
void updateRenderer();
void compileShaders();
std::unique_ptr<melonDS::NDS> CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
@ -127,8 +130,9 @@ private:
int autoScreenSizing;
int videoRenderer;
bool videoSettingsDirty;
int lastVideoRenderer = -1;
double perfCountsSec;
};
#endif // EMUTHREAD_H

View File

@ -709,19 +709,17 @@ void ScreenPanelGL::initOpenGL()
glContext->MakeCurrent();
OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader");
GLuint pid = screenShaderProgram[2];
glBindAttribLocation(pid, 0, "vPosition");
glBindAttribLocation(pid, 1, "vTexcoord");
glBindFragDataLocation(pid, 0, "oColor");
OpenGL::CompileVertexFragmentProgram(screenShaderProgram,
kScreenVS, kScreenFS,
"ScreenShader",
{{"vPosition", 0}, {"vTexcoord", 1}},
{{"oColor", 0}});
OpenGL::LinkShaderProgram(screenShaderProgram);
glUseProgram(screenShaderProgram);
glUniform1i(glGetUniformLocation(screenShaderProgram, "ScreenTex"), 0);
glUseProgram(pid);
glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0);
screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize");
screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform");
screenShaderScreenSizeULoc = glGetUniformLocation(screenShaderProgram, "uScreenSize");
screenShaderTransformULoc = glGetUniformLocation(screenShaderProgram, "uTransform");
// to prevent bleeding between both parts of the screen
// with bilinear filtering enabled
@ -769,21 +767,19 @@ void ScreenPanelGL::initOpenGL()
memset(zeroData, 0, sizeof(zeroData));
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData);
OpenGL::CompileVertexFragmentProgram(osdShader,
kScreenVS_OSD, kScreenFS_OSD,
"OSDShader",
{{"vPosition", 0}},
{{"oColor", 0}});
OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, osdShader, "OSDShader");
glUseProgram(osdShader);
glUniform1i(glGetUniformLocation(osdShader, "OSDTex"), 0);
pid = osdShader[2];
glBindAttribLocation(pid, 0, "vPosition");
glBindFragDataLocation(pid, 0, "oColor");
OpenGL::LinkShaderProgram(osdShader);
glUseProgram(pid);
glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0);
osdScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize");
osdPosULoc = glGetUniformLocation(pid, "uOSDPos");
osdSizeULoc = glGetUniformLocation(pid, "uOSDSize");
osdScaleFactorULoc = glGetUniformLocation(pid, "uScaleFactor");
osdScreenSizeULoc = glGetUniformLocation(osdShader, "uScreenSize");
osdPosULoc = glGetUniformLocation(osdShader, "uOSDPos");
osdSizeULoc = glGetUniformLocation(osdShader, "uOSDSize");
osdScaleFactorULoc = glGetUniformLocation(osdShader, "uScaleFactor");
const float osdvertices[6*2] =
{
@ -818,8 +814,7 @@ void ScreenPanelGL::deinitOpenGL()
glDeleteVertexArrays(1, &screenVertexArray);
glDeleteBuffers(1, &screenVertexBuffer);
OpenGL::DeleteShaderProgram(screenShaderProgram);
glDeleteProgram(screenShaderProgram);
for (const auto& [key, tex] : osdTextures)
{
@ -830,8 +825,7 @@ void ScreenPanelGL::deinitOpenGL()
glDeleteVertexArrays(1, &osdVertexArray);
glDeleteBuffers(1, &osdVertexBuffer);
OpenGL::DeleteShaderProgram(osdShader);
glDeleteProgram(osdShader);
glContext->DoneCurrent();
@ -885,7 +879,7 @@ void ScreenPanelGL::drawScreenGL()
glViewport(0, 0, w, h);
glUseProgram(screenShaderProgram[2]);
glUseProgram(screenShaderProgram);
glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor);
int frontbuf = emuThread->FrontBuffer;
@ -895,7 +889,7 @@ void ScreenPanelGL::drawScreenGL()
if (emuThread->NDS->GPU.GetRenderer3D().Accelerated)
{
// hardware-accelerated render
static_cast<GLRenderer&>(emuThread->NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf);
emuThread->NDS->GPU.GetRenderer3D().BindOutputTexture(frontbuf);
}
else
#endif
@ -936,7 +930,7 @@ void ScreenPanelGL::drawScreenGL()
u32 y = kOSDMargin;
glUseProgram(osdShader[2]);
glUseProgram(osdShader);
glUniform2f(osdScreenSizeULoc, w, h);
glUniform1f(osdScaleFactorULoc, factor);

View File

@ -172,7 +172,7 @@ private:
GLuint screenVertexBuffer, screenVertexArray;
GLuint screenTexture;
GLuint screenShaderProgram[3];
GLuint screenShaderProgram;
GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc;
QMutex screenSettingsLock;
@ -181,7 +181,7 @@ private:
int lastScreenWidth = -1, lastScreenHeight = -1;
GLuint osdShader[3];
GLuint osdShader;
GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc;
GLfloat osdScaleFactorULoc;
GLuint osdVertexArray;

View File

@ -23,6 +23,7 @@
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "GPU.h"
#include "VideoSettingsDialog.h"
#include "ui_VideoSettingsDialog.h"
@ -30,11 +31,20 @@
inline bool UsesGL()
{
return (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
return (Config::ScreenUseGL != 0) || (Config::_3DRenderer != renderer3D_Software);
}
VideoSettingsDialog* VideoSettingsDialog::currentDlg = nullptr;
void VideoSettingsDialog::setEnabled()
{
bool softwareRenderer = Config::_3DRenderer == renderer3D_Software;
ui->cbGLDisplay->setEnabled(softwareRenderer);
ui->cbSoftwareThreaded->setEnabled(softwareRenderer);
ui->cbxGLResolution->setEnabled(!softwareRenderer);
ui->cbBetterPolygons->setEnabled(Config::_3DRenderer == renderer3D_OpenGL);
ui->cbxComputeHiResCoords->setEnabled(Config::_3DRenderer == renderer3D_OpenGLCompute);
}
VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::VideoSettingsDialog)
{
@ -48,10 +58,12 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(
oldSoftThreaded = Config::Threaded3D;
oldGLScale = Config::GL_ScaleFactor;
oldGLBetterPolygons = Config::GL_BetterPolygons;
oldHiresCoordinates = Config::GL_HiresCoordinates;
grp3DRenderer = new QButtonGroup(this);
grp3DRenderer->addButton(ui->rb3DSoftware, 0);
grp3DRenderer->addButton(ui->rb3DOpenGL, 1);
grp3DRenderer->addButton(ui->rb3DSoftware, renderer3D_Software);
grp3DRenderer->addButton(ui->rb3DOpenGL, renderer3D_OpenGL);
grp3DRenderer->addButton(ui->rb3DCompute, renderer3D_OpenGLCompute);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int)));
#else
@ -75,25 +87,13 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(
ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1);
ui->cbBetterPolygons->setChecked(Config::GL_BetterPolygons != 0);
ui->cbxComputeHiResCoords->setChecked(Config::GL_HiresCoordinates != 0);
if (!Config::ScreenVSync)
ui->sbVSyncInterval->setEnabled(false);
setVsyncControlEnable(UsesGL());
if (Config::_3DRenderer == 0)
{
ui->cbGLDisplay->setEnabled(true);
ui->cbSoftwareThreaded->setEnabled(true);
ui->cbxGLResolution->setEnabled(false);
ui->cbBetterPolygons->setEnabled(false);
}
else
{
ui->cbGLDisplay->setEnabled(false);
ui->cbSoftwareThreaded->setEnabled(false);
ui->cbxGLResolution->setEnabled(true);
ui->cbBetterPolygons->setEnabled(true);
}
setEnabled();
}
VideoSettingsDialog::~VideoSettingsDialog()
@ -119,6 +119,7 @@ void VideoSettingsDialog::on_VideoSettingsDialog_rejected()
Config::Threaded3D = oldSoftThreaded;
Config::GL_ScaleFactor = oldGLScale;
Config::GL_BetterPolygons = oldGLBetterPolygons;
Config::GL_HiresCoordinates = oldHiresCoordinates;
emit updateVideoSettings(old_gl != UsesGL());
@ -133,31 +134,18 @@ void VideoSettingsDialog::setVsyncControlEnable(bool hasOGL)
void VideoSettingsDialog::onChange3DRenderer(int renderer)
{
bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
bool old_gl = UsesGL();
Config::_3DRenderer = renderer;
if (renderer == 0)
{
ui->cbGLDisplay->setEnabled(true);
ui->cbSoftwareThreaded->setEnabled(true);
ui->cbxGLResolution->setEnabled(false);
ui->cbBetterPolygons->setEnabled(false);
}
else
{
ui->cbGLDisplay->setEnabled(false);
ui->cbSoftwareThreaded->setEnabled(false);
ui->cbxGLResolution->setEnabled(true);
ui->cbBetterPolygons->setEnabled(true);
}
setEnabled();
emit updateVideoSettings(old_gl != UsesGL());
}
void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state)
{
bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
bool old_gl = UsesGL();
Config::ScreenUseGL = (state != 0);
@ -205,3 +193,10 @@ void VideoSettingsDialog::on_cbBetterPolygons_stateChanged(int state)
emit updateVideoSettings(false);
}
void VideoSettingsDialog::on_cbxComputeHiResCoords_stateChanged(int state)
{
Config::GL_HiresCoordinates = (state != 0);
emit updateVideoSettings(false);
}

View File

@ -65,10 +65,12 @@ private slots:
void on_cbxGLResolution_currentIndexChanged(int idx);
void on_cbBetterPolygons_stateChanged(int state);
void on_cbxComputeHiResCoords_stateChanged(int state);
void on_cbSoftwareThreaded_stateChanged(int state);
private:
void setVsyncControlEnable(bool hasOGL);
void setEnabled();
Ui::VideoSettingsDialog* ui;
@ -81,6 +83,7 @@ private:
int oldSoftThreaded;
int oldGLScale;
int oldGLBetterPolygons;
int oldHiresCoordinates;
};
#endif // VIDEOSETTINGSDIALOG_H

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>408</width>
<width>427</width>
<height>262</height>
</rect>
</property>
@ -24,7 +24,7 @@
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="horizontalSpacing">
<number>-1</number>
<number>6</number>
</property>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_3">
@ -39,13 +39,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="cbxGLResolution">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The resolution at which the 3D graphics will be rendered. Higher resolutions improve graphics quality when the main window is enlarged, but may also cause glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="cbBetterPolygons">
<property name="whatsThis">
@ -56,6 +49,20 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="cbxGLResolution">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The resolution at which the 3D graphics will be rendered. Higher resolutions improve graphics quality when the main window is enlarged, but may also cause glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="cbxComputeHiResCoords">
<property name="text">
<string>Use high resolution coordinates</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -94,23 +101,7 @@
<string>Display settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync interval:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QSpinBox" name="sbVSyncInterval">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -123,7 +114,7 @@
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="cbGLDisplay">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -133,17 +124,7 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="cbVSync">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using OpenGL, synchronize the video output to your monitor's refresh rate.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -159,13 +140,39 @@
</property>
</spacer>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="cbVSync">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using OpenGL, synchronize the video output to your monitor's refresh rate.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync interval:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRadioButton" name="rb3DOpenGL">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>OpenGL</string>
<string>OpenGL (Classic)</string>
</property>
</widget>
</item>
@ -186,6 +193,13 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="rb3DCompute">
<property name="text">
<string>OpenGL (Compute shader)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -2048,6 +2048,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange)
connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint()));
}
printf("update video settings\n");
videoSettingsDirty = true;
if (glchange)

View File

@ -175,10 +175,6 @@ bool camStarted[2];
//extern int AspectRatiosNum;
static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive)
{
return std::any_of(extensions.cbegin(), extensions.cend(), [&](const auto& ext) {
@ -339,10 +335,10 @@ int main(int argc, char** argv)
if (!Config::Load()) QMessageBox::critical(NULL, "melonDS", "Unable to write to config.\nPlease check the write permissions of the folder you placed melonDS in.");
#define SANITIZE(var, min, max) { var = std::clamp(var, min, max); }
#define SANITIZE(var, min, max) { var = std::clamp<int>(var, min, max); }
SANITIZE(Config::ConsoleType, 0, 1);
#ifdef OGLRENDERER_ENABLED
SANITIZE(Config::_3DRenderer, 0, 1); // 0 is the software renderer, 1 is the OpenGL renderer
SANITIZE(Config::_3DRenderer, 0, renderer3D_Max);
#else
SANITIZE(Config::_3DRenderer, 0, 0);
#endif