all repos — mgba @ 47bf26ff73d9bdf8a044773b20fc816f6e823e7e

mGBA Game Boy Advance Emulator

OpenGL, Qt: Add interframe blending
Vicki Pfau vi@endrift.com
Mon, 27 May 2019 11:37:09 -0700
commit

47bf26ff73d9bdf8a044773b20fc816f6e823e7e

parent

67c3f386a4106d5d3017aea882f64dc0ae94a375

M CHANGESCHANGES

@@ -10,6 +10,7 @@ - Debugger: Add tracing to file

- Map viewer supports bitmapped GBA modes - OpenGL renderer with high-resolution upscaling support - Experimental high level "XQ" audio for most GBA games + - Interframe blending for games that use flicker effects Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs
M include/mgba/core/config.hinclude/mgba/core/config.h

@@ -42,6 +42,7 @@ int width;

int height; bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; bool resampleVideo; bool suspendScreensaver; char* shader;
M src/core/config.csrc/core/config.c

@@ -354,6 +354,9 @@ }

if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) { opts->lockIntegerScaling = fakeBool; } + if (_lookupIntValue(config, "interframeBlending", &fakeBool)) { + opts->interframeBlending = fakeBool; + } if (_lookupIntValue(config, "resampleVideo", &fakeBool)) { opts->resampleVideo = fakeBool; }
M src/platform/opengl/gl.csrc/platform/opengl/gl.c

@@ -24,13 +24,20 @@

static void mGLContextInit(struct VideoBackend* v, WHandle handle) { UNUSED(handle); struct mGLContext* context = (struct mGLContext*) v; - glGenTextures(1, &context->tex); - glBindTexture(GL_TEXTURE_2D, context->tex); + glGenTextures(2, context->tex); + glBindTexture(GL_TEXTURE_2D, context->tex[0]); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); #ifndef _WIN32 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #endif + glBindTexture(GL_TEXTURE_2D, context->tex[1]); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +#ifndef _WIN32 + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#endif + context->activeTex = 0; } static void mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {

@@ -38,7 +45,20 @@ struct mGLContext* context = (struct mGLContext*) v;

v->width = width; v->height = height; - glBindTexture(GL_TEXTURE_2D, context->tex); + glBindTexture(GL_TEXTURE_2D, context->tex[0]); +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0); +#endif +#elif defined(__BIG_ENDIAN__) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#endif + + glBindTexture(GL_TEXTURE_2D, context->tex[1]); #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);

@@ -54,7 +74,7 @@ }

static void mGLContextDeinit(struct VideoBackend* v) { struct mGLContext* context = (struct mGLContext*) v; - glDeleteTextures(1, &context->tex); + glDeleteTextures(2, context->tex); } static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {

@@ -96,7 +116,21 @@ glLoadIdentity();

glOrtho(0, v->width, v->height, 0, 0, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glBindTexture(GL_TEXTURE_2D, context->tex); + if (v->interframeBlending) { + glEnable(GL_BLEND); + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + glBlendColor(1, 1, 1, 0.5); + glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]); + if (v->filter) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); if (v->filter) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

@@ -105,11 +139,13 @@ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisable(GL_BLEND); } void mGLContextPostFrame(struct VideoBackend* v, const void* frame) { struct mGLContext* context = (struct mGLContext*) v; - glBindTexture(GL_TEXTURE_2D, context->tex); + context->activeTex ^= 1; + glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
M src/platform/opengl/gl.hsrc/platform/opengl/gl.h

@@ -26,7 +26,8 @@

struct mGLContext { struct VideoBackend d; - GLuint tex; + GLuint tex[2]; + int activeTex; }; void mGLContextCreate(struct mGLContext*);
M src/platform/opengl/gles2.csrc/platform/opengl/gles2.c

@@ -69,6 +69,16 @@ " color.a = 1.;\n"

" gl_FragColor = color;\n" "}"; +static const char* const _interframeFragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + + "void main() {\n" + " vec4 color = texture2D(tex, texCoord);\n" + " color.a = 0.5;\n" + " gl_FragColor = color;\n" + "}"; + static const GLfloat _vertices[] = { -1.f, -1.f, -1.f, 1.f,

@@ -133,16 +143,15 @@ uniforms[3].max.fvec3[1] = 1.0f;

uniforms[3].max.fvec3[2] = 1.0f; mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4); mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0); + mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0); if (context->initialShader.vao != (GLuint) -1) { glBindVertexArray(context->initialShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); - glEnableVertexAttribArray(context->initialShader.positionLocation); - glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindVertexArray(context->finalShader.vao); glBindBuffer(GL_ARRAY_BUFFER, context->vbo); - glEnableVertexAttribArray(context->finalShader.positionLocation); - glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glBindVertexArray(context->interframeShader.vao); + glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindVertexArray(0); }

@@ -177,6 +186,7 @@ glDeleteTextures(1, &context->tex);

glDeleteBuffers(1, &context->vbo); mGLES2ShaderDeinit(&context->initialShader); mGLES2ShaderDeinit(&context->finalShader); + mGLES2ShaderDeinit(&context->interframeShader); free(context->initialShader.uniforms); }

@@ -327,6 +337,11 @@ glGetIntegerv(GL_VIEWPORT, viewport);

context->finalShader.filter = v->filter; _drawShader(context, &context->initialShader); + if (v->interframeBlending) { + context->interframeShader.blend = true; + glViewport(0, 0, viewport[2], viewport[3]); + _drawShader(context, &context->interframeShader); + } size_t n; for (n = 0; n < context->nShaders; ++n) { glViewport(0, 0, viewport[2], viewport[3]);

@@ -334,6 +349,13 @@ _drawShader(context, &context->shaders[n]);

} glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); _drawShader(context, &context->finalShader); + if (v->interframeBlending) { + context->interframeShader.blend = false; + glBindTexture(GL_TEXTURE_2D, context->tex); + _drawShader(context, &context->initialShader); + glViewport(0, 0, viewport[2], viewport[3]); + _drawShader(context, &context->interframeShader); + } glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); if (context->finalShader.vao != (GLuint) -1) {

@@ -391,6 +413,8 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (shader->width > 0 && shader->height > 0) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0);

@@ -448,6 +472,10 @@

const GLubyte* extensions = glGetString(GL_EXTENSIONS); if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) { glGenVertexArrays(1, &shader->vao); + glBindVertexArray(shader->vao); + glEnableVertexAttribArray(shader->positionLocation); + glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glBindVertexArray(0); } else { shader->vao = -1; }
M src/platform/opengl/gles2.hsrc/platform/opengl/gles2.h

@@ -82,6 +82,7 @@ GLuint vbo;

struct mGLES2Shader initialShader; struct mGLES2Shader finalShader; + struct mGLES2Shader interframeShader; struct mGLES2Shader* shaders; size_t nShaders;
M src/platform/qt/ConfigController.cppsrc/platform/qt/ConfigController.cpp

@@ -119,6 +119,7 @@ m_opts.rewindBufferCapacity = 300;

m_opts.useBios = true; m_opts.suspendScreensaver = true; m_opts.lockAspectRatio = true; + m_opts.interframeBlending = false; mCoreConfigLoad(&m_config); mCoreConfigLoadDefaults(&m_config, &m_opts); mCoreConfigSetDefaultIntValue(&m_config, "sgb.borders", 1);
M src/platform/qt/Display.cppsrc/platform/qt/Display.cpp

@@ -70,6 +70,10 @@ void Display::lockIntegerScaling(bool lock) {

m_lockIntegerScaling = lock; } +void Display::interframeBlending(bool lock) { + m_interframeBlending = lock; +} + void Display::filter(bool filter) { m_filter = filter; }
M src/platform/qt/Display.hsrc/platform/qt/Display.h

@@ -42,6 +42,7 @@ static void setDriver(Driver driver) { s_driver = driver; }

bool isAspectRatioLocked() const { return m_lockAspectRatio; } bool isIntegerScalingLocked() const { return m_lockIntegerScaling; } + bool hasInterframeBlending() const { return m_interframeBlending; } bool isFiltered() const { return m_filter; } virtual void startDrawing(std::shared_ptr<CoreController>) = 0;

@@ -62,6 +63,7 @@ virtual void unpauseDrawing() = 0;

virtual void forceDraw() = 0; virtual void lockAspectRatio(bool lock); virtual void lockIntegerScaling(bool lock); + virtual void interframeBlending(bool enable); virtual void filter(bool filter); virtual void framePosted() = 0; virtual void setShaders(struct VDir*) = 0;

@@ -83,6 +85,7 @@

MessagePainter m_messagePainter; bool m_lockAspectRatio = false; bool m_lockIntegerScaling = false; + bool m_interframeBlending = false; bool m_filter = false; QTimer m_mouseTimer; };
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -102,6 +102,7 @@ m_drawThread->start();

lockAspectRatio(isAspectRatioLocked()); lockIntegerScaling(isIntegerScalingLocked()); + interframeBlending(hasInterframeBlending()); filter(isFiltered()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF());

@@ -161,6 +162,13 @@ void DisplayGL::lockIntegerScaling(bool lock) {

Display::lockIntegerScaling(lock); if (m_drawThread) { QMetaObject::invokeMethod(m_painter, "lockIntegerScaling", Q_ARG(bool, lock)); + } +} + +void DisplayGL::interframeBlending(bool enable) { + Display::interframeBlending(enable); + if (m_drawThread) { + QMetaObject::invokeMethod(m_painter, "interframeBlending", Q_ARG(bool, enable)); } }

@@ -276,6 +284,7 @@

m_backend->user = this; m_backend->filter = false; m_backend->lockAspectRatio = false; + m_backend->interframeBlending = false; for (int i = 0; i < 2; ++i) { m_free.append(new uint32_t[1024 * 2048]);

@@ -342,6 +351,10 @@

void PainterGL::lockIntegerScaling(bool lock) { m_backend->lockIntegerScaling = lock; resize(m_size); +} + +void PainterGL::interframeBlending(bool enable) { + m_backend->interframeBlending = enable; } void PainterGL::filter(bool filter) {

@@ -546,7 +559,7 @@ }

#endif #ifdef BUILD_GL mGLContext* glBackend = reinterpret_cast<mGLContext*>(m_backend); - return glBackend->tex; + return glBackend->tex[0]; #else return -1; #endif
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -52,6 +52,7 @@ void unpauseDrawing() override;

void forceDraw() override; void lockAspectRatio(bool lock) override; void lockIntegerScaling(bool lock) override; + void interframeBlending(bool enable) override; void filter(bool filter) override; void framePosted() override; void setShaders(struct VDir*) override;

@@ -96,6 +97,7 @@ void unpause();

void resize(const QSize& size); void lockAspectRatio(bool lock); void lockIntegerScaling(bool lock); + void interframeBlending(bool enable); void filter(bool filter); void resizeContext();
M src/platform/qt/DisplayQt.cppsrc/platform/qt/DisplayQt.cpp

@@ -25,6 +25,7 @@ QSize size = controller->screenDimensions();

m_width = size.width(); m_height = size.height(); m_backing = std::move(QImage()); + m_oldBacking = std::move(QImage()); m_isDrawing = true; m_context = controller; }

@@ -41,6 +42,11 @@ }

void DisplayQt::lockIntegerScaling(bool lock) { Display::lockIntegerScaling(lock); + update(); +} + +void DisplayQt::interframeBlending(bool lock) { + Display::interframeBlending(lock); update(); }

@@ -55,6 +61,7 @@ const color_t* buffer = m_context->drawContext();

if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) { return; } + m_oldBacking = m_backing; #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16);

@@ -65,6 +72,9 @@ #else

m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32); m_backing = m_backing.convertToFormat(QImage::Format_RGB32); #endif +#ifndef COLOR_5_6_5 + m_backing = m_backing.rgbSwapped(); +#endif } void DisplayQt::resizeContext() {

@@ -75,6 +85,7 @@ QSize size = m_context->screenDimensions();

if (m_width != size.width() || m_height != size.height()) { m_width = size.width(); m_height = size.height(); + m_oldBacking = std::move(QImage()); m_backing = std::move(QImage()); } }

@@ -98,13 +109,15 @@ if (isIntegerScalingLocked()) {

ds.setWidth(ds.width() - ds.width() % m_width); ds.setHeight(ds.height() - ds.height() % m_height); } +#warning TODO: Add interframeBlending QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2); QRect full(origin, ds); -#ifdef COLOR_5_6_5 + if (hasInterframeBlending()) { + painter.drawImage(full, m_oldBacking, QRect(0, 0, m_width, m_height)); + painter.setOpacity(0.5); + } painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height)); -#else - painter.drawImage(full, m_backing.rgbSwapped(), QRect(0, 0, m_width, m_height)); -#endif + painter.setOpacity(1); messagePainter()->paint(&painter); }
M src/platform/qt/DisplayQt.hsrc/platform/qt/DisplayQt.h

@@ -30,6 +30,7 @@ void unpauseDrawing() override { m_isDrawing = true; }

void forceDraw() override { update(); } void lockAspectRatio(bool lock) override; void lockIntegerScaling(bool lock) override; + void interframeBlending(bool enable) override; void filter(bool filter) override; void framePosted() override; void setShaders(struct VDir*) override {}

@@ -44,6 +45,7 @@ bool m_isDrawing = false;

unsigned m_width; unsigned m_height; QImage m_backing{nullptr}; + QImage m_oldBacking{nullptr}; std::shared_ptr<CoreController> m_context = nullptr; };
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -377,6 +377,7 @@ saveSetting("frameskip", m_ui.frameskip);

saveSetting("autofireThreshold", m_ui.autofireThreshold); saveSetting("lockAspectRatio", m_ui.lockAspectRatio); saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); + saveSetting("interframeBlending", m_ui.interframeBlending); saveSetting("volume", m_ui.volume); saveSetting("mute", m_ui.mute); saveSetting("fastForwardVolume", m_ui.volumeFf);

@@ -534,6 +535,7 @@ loadSetting("fpsTarget", m_ui.fpsTarget);

loadSetting("autofireThreshold", m_ui.autofireThreshold); loadSetting("lockAspectRatio", m_ui.lockAspectRatio); loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling); + loadSetting("interframeBlending", m_ui.interframeBlending); loadSetting("volume", m_ui.volume, 0x100); loadSetting("mute", m_ui.mute, false); loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value());
M src/platform/qt/SettingsView.uisrc/platform/qt/SettingsView.ui

@@ -7,7 +7,7 @@ <rect>

<x>0</x> <y>0</y> <width>849</width> - <height>753</height> + <height>797</height> </rect> </property> <property name="sizePolicy">

@@ -445,7 +445,7 @@ <string>Force integer scaling</string>

</property> </widget> </item> - <item row="14" column="1"> + <item row="15" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Bilinear filtering</string>

@@ -456,6 +456,13 @@ <item row="9" column="1">

<widget class="QPushButton" name="nativeGB"> <property name="text"> <string>Native (59.7275)</string> + </property> + </widget> + </item> + <item row="14" column="1"> + <widget class="QCheckBox" name="interframeBlending"> + <property name="text"> + <string>Interframe blending</string> </property> </widget> </item>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -719,6 +719,7 @@ QSize size = m_controller->screenDimensions();

m_screenWidget->setDimensions(size.width(), size.height()); m_config->updateOption("lockIntegerScaling"); m_config->updateOption("lockAspectRatio"); + m_config->updateOption("interframeBlending"); if (m_savedScale > 0) { resizeFrame(size * m_savedScale); }

@@ -883,6 +884,7 @@ });

const mCoreOptions* opts = m_config->options(); m_display->lockAspectRatio(opts->lockAspectRatio); + m_display->interframeBlending(opts->interframeBlending); m_display->filter(opts->resampleVideo); #if defined(BUILD_GL) || defined(BUILD_GLES2) if (opts->shader) {

@@ -1339,6 +1341,15 @@ m_screenWidget->setLockIntegerScaling(value.toBool());

} }, this); m_config->updateOption("lockIntegerScaling"); + + ConfigOption* interframeBlending = m_config->addOption("interframeBlending"); + interframeBlending->addBoolean(tr("Interframe blending"), &m_actions, "av"); + interframeBlending->connect([this](const QVariant& value) { + if (m_display) { + m_display->interframeBlending(value.toBool()); + } + }, this); + m_config->updateOption("interframeBlending"); ConfigOption* resampleVideo = m_config->addOption("resampleVideo"); resampleVideo->addBoolean(tr("Bilinear filtering"), &m_actions, "av");
M src/platform/sdl/gl-sdl.csrc/platform/sdl/gl-sdl.c

@@ -35,6 +35,7 @@ mGLContextCreate(&renderer->gl);

renderer->gl.d.user = renderer; renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio; renderer->gl.d.lockIntegerScaling = renderer->lockIntegerScaling; + renderer->gl.d.interframeBlending = renderer->interframeBlending; renderer->gl.d.filter = renderer->filter; renderer->gl.d.swap = mSDLGLCommonSwap; renderer->gl.d.init(&renderer->gl.d, 0);
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -136,6 +136,7 @@ renderer.player.windowUpdated = 0;

renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling; + renderer.interframeBlending = renderer.core->opts.interframeBlending; renderer.filter = renderer.core->opts.resampleVideo; if (!mSDLInit(&renderer)) {
M src/platform/sdl/main.hsrc/platform/sdl/main.h

@@ -64,6 +64,7 @@ int ratio;

bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; bool filter; #ifdef BUILD_GL
M src/platform/video-backend.hsrc/platform/video-backend.h

@@ -36,6 +36,7 @@

bool filter; bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; }; struct VideoShader {