diff --git a/CHANGES b/CHANGES index cf0bd2dbd..e41f1f538 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,7 @@ Features: - 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 diff --git a/include/mgba/core/config.h b/include/mgba/core/config.h index b2439c41e..da664ed47 100644 --- a/include/mgba/core/config.h +++ b/include/mgba/core/config.h @@ -42,6 +42,7 @@ struct mCoreOptions { int height; bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; bool resampleVideo; bool suspendScreensaver; char* shader; diff --git a/src/core/config.c b/src/core/config.c index 932c904c2..cd173dfb3 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -354,6 +354,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) { opts->lockIntegerScaling = fakeBool; } + if (_lookupIntValue(config, "interframeBlending", &fakeBool)) { + opts->interframeBlending = fakeBool; + } if (_lookupIntValue(config, "resampleVideo", &fakeBool)) { opts->resampleVideo = fakeBool; } diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index cfa05bea9..bb7afbc6f 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -24,13 +24,20 @@ static const GLint _glTexCoords[] = { 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 @@ static void mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsi 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 mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsi 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 @@ void mGLContextDrawFrame(struct VideoBackend* v) { 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 @@ void mGLContextDrawFrame(struct VideoBackend* v) { 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); diff --git a/src/platform/opengl/gl.h b/src/platform/opengl/gl.h index a6412e919..3ff528864 100644 --- a/src/platform/opengl/gl.h +++ b/src/platform/opengl/gl.h @@ -26,7 +26,8 @@ CXX_GUARD_START struct mGLContext { struct VideoBackend d; - GLuint tex; + GLuint tex[2]; + int activeTex; }; void mGLContextCreate(struct mGLContext*); diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index a9034204f..d26bb7a2c 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -69,6 +69,16 @@ static const char* const _nullFragmentShader = " 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 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) { 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 @@ static void mGLES2ContextDeinit(struct VideoBackend* v) { glDeleteBuffers(1, &context->vbo); mGLES2ShaderDeinit(&context->initialShader); mGLES2ShaderDeinit(&context->finalShader); + mGLES2ShaderDeinit(&context->interframeShader); free(context->initialShader.uniforms); } @@ -327,6 +337,11 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) { 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 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) { } 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 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f 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 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f 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; } diff --git a/src/platform/opengl/gles2.h b/src/platform/opengl/gles2.h index 59f3b7996..0655151e0 100644 --- a/src/platform/opengl/gles2.h +++ b/src/platform/opengl/gles2.h @@ -82,6 +82,7 @@ struct mGLES2Context { struct mGLES2Shader initialShader; struct mGLES2Shader finalShader; + struct mGLES2Shader interframeShader; struct mGLES2Shader* shaders; size_t nShaders; diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 969f0609f..d0794fa94 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -119,6 +119,7 @@ ConfigController::ConfigController(QObject* parent) 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); diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index afc0cc5cf..6a4d2bcb5 100644 --- a/src/platform/qt/Display.cpp +++ b/src/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; } diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index f00bdb2b1..a5c1d227a 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -42,6 +42,7 @@ public: 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) = 0; @@ -62,6 +63,7 @@ public slots: 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 @@ private: MessagePainter m_messagePainter; bool m_lockAspectRatio = false; bool m_lockIntegerScaling = false; + bool m_interframeBlending = false; bool m_filter = false; QTimer m_mouseTimer; }; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 135e591f8..709abd146 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -102,6 +102,7 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { lockAspectRatio(isAspectRatioLocked()); lockIntegerScaling(isIntegerScalingLocked()); + interframeBlending(hasInterframeBlending()); filter(isFiltered()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF()); @@ -164,6 +165,13 @@ void DisplayGL::lockIntegerScaling(bool lock) { } } +void DisplayGL::interframeBlending(bool enable) { + Display::interframeBlending(enable); + if (m_drawThread) { + QMetaObject::invokeMethod(m_painter, "interframeBlending", Q_ARG(bool, enable)); + } +} + void DisplayGL::filter(bool filter) { Display::filter(filter); if (m_drawThread) { @@ -276,6 +284,7 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent 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]); @@ -344,6 +353,10 @@ void PainterGL::lockIntegerScaling(bool lock) { resize(m_size); } +void PainterGL::interframeBlending(bool enable) { + m_backend->interframeBlending = enable; +} + void PainterGL::filter(bool filter) { m_backend->filter = filter; if (m_started && !m_active) { @@ -546,7 +559,7 @@ int PainterGL::glTex() { #endif #ifdef BUILD_GL mGLContext* glBackend = reinterpret_cast(m_backend); - return glBackend->tex; + return glBackend->tex[0]; #else return -1; #endif diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 2c54934f4..ff6d647b0 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -52,6 +52,7 @@ public slots: 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 @@ public slots: void resize(const QSize& size); void lockAspectRatio(bool lock); void lockIntegerScaling(bool lock); + void interframeBlending(bool enable); void filter(bool filter); void resizeContext(); diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index 6043a7399..e3c227c1b 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -25,6 +25,7 @@ void DisplayQt::startDrawing(std::shared_ptr controller) { m_width = size.width(); m_height = size.height(); m_backing = std::move(QImage()); + m_oldBacking = std::move(QImage()); m_isDrawing = true; m_context = controller; } @@ -44,6 +45,11 @@ void DisplayQt::lockIntegerScaling(bool lock) { update(); } +void DisplayQt::interframeBlending(bool lock) { + Display::interframeBlending(lock); + update(); +} + void DisplayQt::filter(bool filter) { Display::filter(filter); update(); @@ -55,6 +61,7 @@ void DisplayQt::framePosted() { if (const_cast(m_backing).bits() == reinterpret_cast(buffer)) { return; } + m_oldBacking = m_backing; #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 m_backing = QImage(reinterpret_cast(buffer), m_width, m_height, QImage::Format_RGB16); @@ -65,6 +72,9 @@ void DisplayQt::framePosted() { m_backing = QImage(reinterpret_cast(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 @@ void DisplayQt::resizeContext() { 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 @@ void DisplayQt::paintEvent(QPaintEvent*) { 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); } diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index 2d242fbe5..5795ccb45 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -30,6 +30,7 @@ public slots: 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 @@ private: unsigned m_width; unsigned m_height; QImage m_backing{nullptr}; + QImage m_oldBacking{nullptr}; std::shared_ptr m_context = nullptr; }; diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 2739f589a..75a639b8a 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -377,6 +377,7 @@ void SettingsView::updateConfig() { 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 @@ void SettingsView::reloadConfig() { 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()); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 1f51e5abf..d674dd15d 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -7,7 +7,7 @@ 0 0 849 - 753 + 797 @@ -445,7 +445,7 @@ - + Bilinear filtering @@ -459,6 +459,13 @@ + + + + Interframe blending + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 7cdc73a4d..ded67df94 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -719,6 +719,7 @@ void Window::gameStarted() { 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 @@ void Window::reloadDisplayDriver() { 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) { @@ -1340,6 +1342,15 @@ void Window::setupMenu(QMenuBar* menubar) { }, 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"); resampleVideo->connect([this](const QVariant& value) { diff --git a/src/platform/sdl/gl-sdl.c b/src/platform/sdl/gl-sdl.c index 38e0a2164..539ef88d0 100644 --- a/src/platform/sdl/gl-sdl.c +++ b/src/platform/sdl/gl-sdl.c @@ -35,6 +35,7 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) { 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); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index d82d4fb24..0bb340c23 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -136,6 +136,7 @@ int main(int argc, char** argv) { 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)) { diff --git a/src/platform/sdl/main.h b/src/platform/sdl/main.h index 5f286d5fd..d6148babf 100644 --- a/src/platform/sdl/main.h +++ b/src/platform/sdl/main.h @@ -64,6 +64,7 @@ struct mSDLRenderer { bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; bool filter; #ifdef BUILD_GL diff --git a/src/platform/video-backend.h b/src/platform/video-backend.h index f45a56dc5..6d714629a 100644 --- a/src/platform/video-backend.h +++ b/src/platform/video-backend.h @@ -36,6 +36,7 @@ struct VideoBackend { bool filter; bool lockAspectRatio; bool lockIntegerScaling; + bool interframeBlending; }; struct VideoShader {