mirror of https://github.com/mgba-emu/mgba.git
OpenGL, Qt: Add interframe blending
This commit is contained in:
parent
67c3f386a4
commit
47bf26ff73
1
CHANGES
1
CHANGES
|
@ -10,6 +10,7 @@ Features:
|
||||||
- Map viewer supports bitmapped GBA modes
|
- Map viewer supports bitmapped GBA modes
|
||||||
- OpenGL renderer with high-resolution upscaling support
|
- OpenGL renderer with high-resolution upscaling support
|
||||||
- Experimental high level "XQ" audio for most GBA games
|
- Experimental high level "XQ" audio for most GBA games
|
||||||
|
- Interframe blending for games that use flicker effects
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
||||||
- GBA: Reset now reloads multiboot ROMs
|
- GBA: Reset now reloads multiboot ROMs
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct mCoreOptions {
|
||||||
int height;
|
int height;
|
||||||
bool lockAspectRatio;
|
bool lockAspectRatio;
|
||||||
bool lockIntegerScaling;
|
bool lockIntegerScaling;
|
||||||
|
bool interframeBlending;
|
||||||
bool resampleVideo;
|
bool resampleVideo;
|
||||||
bool suspendScreensaver;
|
bool suspendScreensaver;
|
||||||
char* shader;
|
char* shader;
|
||||||
|
|
|
@ -354,6 +354,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
||||||
if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
|
if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
|
||||||
opts->lockIntegerScaling = fakeBool;
|
opts->lockIntegerScaling = fakeBool;
|
||||||
}
|
}
|
||||||
|
if (_lookupIntValue(config, "interframeBlending", &fakeBool)) {
|
||||||
|
opts->interframeBlending = fakeBool;
|
||||||
|
}
|
||||||
if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
|
if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
|
||||||
opts->resampleVideo = fakeBool;
|
opts->resampleVideo = fakeBool;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,20 @@ static const GLint _glTexCoords[] = {
|
||||||
static void mGLContextInit(struct VideoBackend* v, WHandle handle) {
|
static void mGLContextInit(struct VideoBackend* v, WHandle handle) {
|
||||||
UNUSED(handle);
|
UNUSED(handle);
|
||||||
struct mGLContext* context = (struct mGLContext*) v;
|
struct mGLContext* context = (struct mGLContext*) v;
|
||||||
glGenTextures(1, &context->tex);
|
glGenTextures(2, context->tex);
|
||||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
#endif
|
#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) {
|
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->width = width;
|
||||||
v->height = height;
|
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_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#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);
|
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) {
|
static void mGLContextDeinit(struct VideoBackend* v) {
|
||||||
struct mGLContext* context = (struct mGLContext*) 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) {
|
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);
|
glOrtho(0, v->width, v->height, 0, 0, 1);
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
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) {
|
if (v->filter) {
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
}
|
}
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mGLContextPostFrame(struct VideoBackend* v, const void* frame) {
|
void mGLContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||||
struct mGLContext* context = (struct mGLContext*) v;
|
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_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#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);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||||
|
|
|
@ -26,7 +26,8 @@ CXX_GUARD_START
|
||||||
struct mGLContext {
|
struct mGLContext {
|
||||||
struct VideoBackend d;
|
struct VideoBackend d;
|
||||||
|
|
||||||
GLuint tex;
|
GLuint tex[2];
|
||||||
|
int activeTex;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mGLContextCreate(struct mGLContext*);
|
void mGLContextCreate(struct mGLContext*);
|
||||||
|
|
|
@ -69,6 +69,16 @@ static const char* const _nullFragmentShader =
|
||||||
" gl_FragColor = color;\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[] = {
|
static const GLfloat _vertices[] = {
|
||||||
-1.f, -1.f,
|
-1.f, -1.f,
|
||||||
-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;
|
uniforms[3].max.fvec3[2] = 1.0f;
|
||||||
mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
|
mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
|
||||||
mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
|
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) {
|
if (context->initialShader.vao != (GLuint) -1) {
|
||||||
glBindVertexArray(context->initialShader.vao);
|
glBindVertexArray(context->initialShader.vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
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);
|
glBindVertexArray(context->finalShader.vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||||
glEnableVertexAttribArray(context->finalShader.positionLocation);
|
glBindVertexArray(context->interframeShader.vao);
|
||||||
glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +186,7 @@ static void mGLES2ContextDeinit(struct VideoBackend* v) {
|
||||||
glDeleteBuffers(1, &context->vbo);
|
glDeleteBuffers(1, &context->vbo);
|
||||||
mGLES2ShaderDeinit(&context->initialShader);
|
mGLES2ShaderDeinit(&context->initialShader);
|
||||||
mGLES2ShaderDeinit(&context->finalShader);
|
mGLES2ShaderDeinit(&context->finalShader);
|
||||||
|
mGLES2ShaderDeinit(&context->interframeShader);
|
||||||
free(context->initialShader.uniforms);
|
free(context->initialShader.uniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +337,11 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
||||||
|
|
||||||
context->finalShader.filter = v->filter;
|
context->finalShader.filter = v->filter;
|
||||||
_drawShader(context, &context->initialShader);
|
_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;
|
size_t n;
|
||||||
for (n = 0; n < context->nShaders; ++n) {
|
for (n = 0; n < context->nShaders; ++n) {
|
||||||
glViewport(0, 0, viewport[2], viewport[3]);
|
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]);
|
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||||
_drawShader(context, &context->finalShader);
|
_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);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
if (context->finalShader.vao != (GLuint) -1) {
|
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);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
if (shader->width > 0 && shader->height > 0) {
|
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);
|
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);
|
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);
|
const GLubyte* extensions = glGetString(GL_EXTENSIONS);
|
||||||
if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) {
|
if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) {
|
||||||
glGenVertexArrays(1, &shader->vao);
|
glGenVertexArrays(1, &shader->vao);
|
||||||
|
glBindVertexArray(shader->vao);
|
||||||
|
glEnableVertexAttribArray(shader->positionLocation);
|
||||||
|
glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||||
|
glBindVertexArray(0);
|
||||||
} else {
|
} else {
|
||||||
shader->vao = -1;
|
shader->vao = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct mGLES2Context {
|
||||||
|
|
||||||
struct mGLES2Shader initialShader;
|
struct mGLES2Shader initialShader;
|
||||||
struct mGLES2Shader finalShader;
|
struct mGLES2Shader finalShader;
|
||||||
|
struct mGLES2Shader interframeShader;
|
||||||
|
|
||||||
struct mGLES2Shader* shaders;
|
struct mGLES2Shader* shaders;
|
||||||
size_t nShaders;
|
size_t nShaders;
|
||||||
|
|
|
@ -119,6 +119,7 @@ ConfigController::ConfigController(QObject* parent)
|
||||||
m_opts.useBios = true;
|
m_opts.useBios = true;
|
||||||
m_opts.suspendScreensaver = true;
|
m_opts.suspendScreensaver = true;
|
||||||
m_opts.lockAspectRatio = true;
|
m_opts.lockAspectRatio = true;
|
||||||
|
m_opts.interframeBlending = false;
|
||||||
mCoreConfigLoad(&m_config);
|
mCoreConfigLoad(&m_config);
|
||||||
mCoreConfigLoadDefaults(&m_config, &m_opts);
|
mCoreConfigLoadDefaults(&m_config, &m_opts);
|
||||||
mCoreConfigSetDefaultIntValue(&m_config, "sgb.borders", 1);
|
mCoreConfigSetDefaultIntValue(&m_config, "sgb.borders", 1);
|
||||||
|
|
|
@ -70,6 +70,10 @@ void Display::lockIntegerScaling(bool lock) {
|
||||||
m_lockIntegerScaling = lock;
|
m_lockIntegerScaling = lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Display::interframeBlending(bool lock) {
|
||||||
|
m_interframeBlending = lock;
|
||||||
|
}
|
||||||
|
|
||||||
void Display::filter(bool filter) {
|
void Display::filter(bool filter) {
|
||||||
m_filter = filter;
|
m_filter = filter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
|
|
||||||
bool isAspectRatioLocked() const { return m_lockAspectRatio; }
|
bool isAspectRatioLocked() const { return m_lockAspectRatio; }
|
||||||
bool isIntegerScalingLocked() const { return m_lockIntegerScaling; }
|
bool isIntegerScalingLocked() const { return m_lockIntegerScaling; }
|
||||||
|
bool hasInterframeBlending() const { return m_interframeBlending; }
|
||||||
bool isFiltered() const { return m_filter; }
|
bool isFiltered() const { return m_filter; }
|
||||||
|
|
||||||
virtual void startDrawing(std::shared_ptr<CoreController>) = 0;
|
virtual void startDrawing(std::shared_ptr<CoreController>) = 0;
|
||||||
|
@ -62,6 +63,7 @@ public slots:
|
||||||
virtual void forceDraw() = 0;
|
virtual void forceDraw() = 0;
|
||||||
virtual void lockAspectRatio(bool lock);
|
virtual void lockAspectRatio(bool lock);
|
||||||
virtual void lockIntegerScaling(bool lock);
|
virtual void lockIntegerScaling(bool lock);
|
||||||
|
virtual void interframeBlending(bool enable);
|
||||||
virtual void filter(bool filter);
|
virtual void filter(bool filter);
|
||||||
virtual void framePosted() = 0;
|
virtual void framePosted() = 0;
|
||||||
virtual void setShaders(struct VDir*) = 0;
|
virtual void setShaders(struct VDir*) = 0;
|
||||||
|
@ -83,6 +85,7 @@ private:
|
||||||
MessagePainter m_messagePainter;
|
MessagePainter m_messagePainter;
|
||||||
bool m_lockAspectRatio = false;
|
bool m_lockAspectRatio = false;
|
||||||
bool m_lockIntegerScaling = false;
|
bool m_lockIntegerScaling = false;
|
||||||
|
bool m_interframeBlending = false;
|
||||||
bool m_filter = false;
|
bool m_filter = false;
|
||||||
QTimer m_mouseTimer;
|
QTimer m_mouseTimer;
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,6 +102,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
|
|
||||||
lockAspectRatio(isAspectRatioLocked());
|
lockAspectRatio(isAspectRatioLocked());
|
||||||
lockIntegerScaling(isIntegerScalingLocked());
|
lockIntegerScaling(isIntegerScalingLocked());
|
||||||
|
interframeBlending(hasInterframeBlending());
|
||||||
filter(isFiltered());
|
filter(isFiltered());
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||||
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF());
|
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) {
|
void DisplayGL::filter(bool filter) {
|
||||||
Display::filter(filter);
|
Display::filter(filter);
|
||||||
if (m_drawThread) {
|
if (m_drawThread) {
|
||||||
|
@ -276,6 +284,7 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent
|
||||||
m_backend->user = this;
|
m_backend->user = this;
|
||||||
m_backend->filter = false;
|
m_backend->filter = false;
|
||||||
m_backend->lockAspectRatio = false;
|
m_backend->lockAspectRatio = false;
|
||||||
|
m_backend->interframeBlending = false;
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
m_free.append(new uint32_t[1024 * 2048]);
|
m_free.append(new uint32_t[1024 * 2048]);
|
||||||
|
@ -344,6 +353,10 @@ void PainterGL::lockIntegerScaling(bool lock) {
|
||||||
resize(m_size);
|
resize(m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PainterGL::interframeBlending(bool enable) {
|
||||||
|
m_backend->interframeBlending = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void PainterGL::filter(bool filter) {
|
void PainterGL::filter(bool filter) {
|
||||||
m_backend->filter = filter;
|
m_backend->filter = filter;
|
||||||
if (m_started && !m_active) {
|
if (m_started && !m_active) {
|
||||||
|
@ -546,7 +559,7 @@ int PainterGL::glTex() {
|
||||||
#endif
|
#endif
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
mGLContext* glBackend = reinterpret_cast<mGLContext*>(m_backend);
|
mGLContext* glBackend = reinterpret_cast<mGLContext*>(m_backend);
|
||||||
return glBackend->tex;
|
return glBackend->tex[0];
|
||||||
#else
|
#else
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,6 +52,7 @@ public slots:
|
||||||
void forceDraw() override;
|
void forceDraw() override;
|
||||||
void lockAspectRatio(bool lock) override;
|
void lockAspectRatio(bool lock) override;
|
||||||
void lockIntegerScaling(bool lock) override;
|
void lockIntegerScaling(bool lock) override;
|
||||||
|
void interframeBlending(bool enable) override;
|
||||||
void filter(bool filter) override;
|
void filter(bool filter) override;
|
||||||
void framePosted() override;
|
void framePosted() override;
|
||||||
void setShaders(struct VDir*) override;
|
void setShaders(struct VDir*) override;
|
||||||
|
@ -96,6 +97,7 @@ public slots:
|
||||||
void resize(const QSize& size);
|
void resize(const QSize& size);
|
||||||
void lockAspectRatio(bool lock);
|
void lockAspectRatio(bool lock);
|
||||||
void lockIntegerScaling(bool lock);
|
void lockIntegerScaling(bool lock);
|
||||||
|
void interframeBlending(bool enable);
|
||||||
void filter(bool filter);
|
void filter(bool filter);
|
||||||
void resizeContext();
|
void resizeContext();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ void DisplayQt::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
m_width = size.width();
|
m_width = size.width();
|
||||||
m_height = size.height();
|
m_height = size.height();
|
||||||
m_backing = std::move(QImage());
|
m_backing = std::move(QImage());
|
||||||
|
m_oldBacking = std::move(QImage());
|
||||||
m_isDrawing = true;
|
m_isDrawing = true;
|
||||||
m_context = controller;
|
m_context = controller;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +45,11 @@ void DisplayQt::lockIntegerScaling(bool lock) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisplayQt::interframeBlending(bool lock) {
|
||||||
|
Display::interframeBlending(lock);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayQt::filter(bool filter) {
|
void DisplayQt::filter(bool filter) {
|
||||||
Display::filter(filter);
|
Display::filter(filter);
|
||||||
update();
|
update();
|
||||||
|
@ -55,6 +61,7 @@ void DisplayQt::framePosted() {
|
||||||
if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) {
|
if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_oldBacking = m_backing;
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#ifdef COLOR_5_6_5
|
||||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16);
|
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_RGB16);
|
||||||
|
@ -65,6 +72,9 @@ void DisplayQt::framePosted() {
|
||||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32);
|
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), m_width, m_height, QImage::Format_ARGB32);
|
||||||
m_backing = m_backing.convertToFormat(QImage::Format_RGB32);
|
m_backing = m_backing.convertToFormat(QImage::Format_RGB32);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef COLOR_5_6_5
|
||||||
|
m_backing = m_backing.rgbSwapped();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayQt::resizeContext() {
|
void DisplayQt::resizeContext() {
|
||||||
|
@ -75,6 +85,7 @@ void DisplayQt::resizeContext() {
|
||||||
if (m_width != size.width() || m_height != size.height()) {
|
if (m_width != size.width() || m_height != size.height()) {
|
||||||
m_width = size.width();
|
m_width = size.width();
|
||||||
m_height = size.height();
|
m_height = size.height();
|
||||||
|
m_oldBacking = std::move(QImage());
|
||||||
m_backing = 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.setWidth(ds.width() - ds.width() % m_width);
|
||||||
ds.setHeight(ds.height() - ds.height() % m_height);
|
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);
|
QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2);
|
||||||
QRect full(origin, ds);
|
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));
|
painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height));
|
||||||
#else
|
painter.setOpacity(1);
|
||||||
painter.drawImage(full, m_backing.rgbSwapped(), QRect(0, 0, m_width, m_height));
|
|
||||||
#endif
|
|
||||||
messagePainter()->paint(&painter);
|
messagePainter()->paint(&painter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ public slots:
|
||||||
void forceDraw() override { update(); }
|
void forceDraw() override { update(); }
|
||||||
void lockAspectRatio(bool lock) override;
|
void lockAspectRatio(bool lock) override;
|
||||||
void lockIntegerScaling(bool lock) override;
|
void lockIntegerScaling(bool lock) override;
|
||||||
|
void interframeBlending(bool enable) override;
|
||||||
void filter(bool filter) override;
|
void filter(bool filter) override;
|
||||||
void framePosted() override;
|
void framePosted() override;
|
||||||
void setShaders(struct VDir*) override {}
|
void setShaders(struct VDir*) override {}
|
||||||
|
@ -44,6 +45,7 @@ private:
|
||||||
unsigned m_width;
|
unsigned m_width;
|
||||||
unsigned m_height;
|
unsigned m_height;
|
||||||
QImage m_backing{nullptr};
|
QImage m_backing{nullptr};
|
||||||
|
QImage m_oldBacking{nullptr};
|
||||||
std::shared_ptr<CoreController> m_context = nullptr;
|
std::shared_ptr<CoreController> m_context = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -377,6 +377,7 @@ void SettingsView::updateConfig() {
|
||||||
saveSetting("autofireThreshold", m_ui.autofireThreshold);
|
saveSetting("autofireThreshold", m_ui.autofireThreshold);
|
||||||
saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
||||||
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
|
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
|
||||||
|
saveSetting("interframeBlending", m_ui.interframeBlending);
|
||||||
saveSetting("volume", m_ui.volume);
|
saveSetting("volume", m_ui.volume);
|
||||||
saveSetting("mute", m_ui.mute);
|
saveSetting("mute", m_ui.mute);
|
||||||
saveSetting("fastForwardVolume", m_ui.volumeFf);
|
saveSetting("fastForwardVolume", m_ui.volumeFf);
|
||||||
|
@ -534,6 +535,7 @@ void SettingsView::reloadConfig() {
|
||||||
loadSetting("autofireThreshold", m_ui.autofireThreshold);
|
loadSetting("autofireThreshold", m_ui.autofireThreshold);
|
||||||
loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
||||||
loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
|
loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
|
||||||
|
loadSetting("interframeBlending", m_ui.interframeBlending);
|
||||||
loadSetting("volume", m_ui.volume, 0x100);
|
loadSetting("volume", m_ui.volume, 0x100);
|
||||||
loadSetting("mute", m_ui.mute, false);
|
loadSetting("mute", m_ui.mute, false);
|
||||||
loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value());
|
loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value());
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>849</width>
|
<width>849</width>
|
||||||
<height>753</height>
|
<height>797</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -445,7 +445,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="14" column="1">
|
<item row="15" column="1">
|
||||||
<widget class="QCheckBox" name="resampleVideo">
|
<widget class="QCheckBox" name="resampleVideo">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Bilinear filtering</string>
|
<string>Bilinear filtering</string>
|
||||||
|
@ -459,6 +459,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="14" column="1">
|
||||||
|
<widget class="QCheckBox" name="interframeBlending">
|
||||||
|
<property name="text">
|
||||||
|
<string>Interframe blending</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="interface_2">
|
<widget class="QWidget" name="interface_2">
|
||||||
|
|
|
@ -719,6 +719,7 @@ void Window::gameStarted() {
|
||||||
m_screenWidget->setDimensions(size.width(), size.height());
|
m_screenWidget->setDimensions(size.width(), size.height());
|
||||||
m_config->updateOption("lockIntegerScaling");
|
m_config->updateOption("lockIntegerScaling");
|
||||||
m_config->updateOption("lockAspectRatio");
|
m_config->updateOption("lockAspectRatio");
|
||||||
|
m_config->updateOption("interframeBlending");
|
||||||
if (m_savedScale > 0) {
|
if (m_savedScale > 0) {
|
||||||
resizeFrame(size * m_savedScale);
|
resizeFrame(size * m_savedScale);
|
||||||
}
|
}
|
||||||
|
@ -883,6 +884,7 @@ void Window::reloadDisplayDriver() {
|
||||||
|
|
||||||
const mCoreOptions* opts = m_config->options();
|
const mCoreOptions* opts = m_config->options();
|
||||||
m_display->lockAspectRatio(opts->lockAspectRatio);
|
m_display->lockAspectRatio(opts->lockAspectRatio);
|
||||||
|
m_display->interframeBlending(opts->interframeBlending);
|
||||||
m_display->filter(opts->resampleVideo);
|
m_display->filter(opts->resampleVideo);
|
||||||
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||||
if (opts->shader) {
|
if (opts->shader) {
|
||||||
|
@ -1340,6 +1342,15 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}, this);
|
}, this);
|
||||||
m_config->updateOption("lockIntegerScaling");
|
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");
|
ConfigOption* resampleVideo = m_config->addOption("resampleVideo");
|
||||||
resampleVideo->addBoolean(tr("Bilinear filtering"), &m_actions, "av");
|
resampleVideo->addBoolean(tr("Bilinear filtering"), &m_actions, "av");
|
||||||
resampleVideo->connect([this](const QVariant& value) {
|
resampleVideo->connect([this](const QVariant& value) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) {
|
||||||
renderer->gl.d.user = renderer;
|
renderer->gl.d.user = renderer;
|
||||||
renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio;
|
renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio;
|
||||||
renderer->gl.d.lockIntegerScaling = renderer->lockIntegerScaling;
|
renderer->gl.d.lockIntegerScaling = renderer->lockIntegerScaling;
|
||||||
|
renderer->gl.d.interframeBlending = renderer->interframeBlending;
|
||||||
renderer->gl.d.filter = renderer->filter;
|
renderer->gl.d.filter = renderer->filter;
|
||||||
renderer->gl.d.swap = mSDLGLCommonSwap;
|
renderer->gl.d.swap = mSDLGLCommonSwap;
|
||||||
renderer->gl.d.init(&renderer->gl.d, 0);
|
renderer->gl.d.init(&renderer->gl.d, 0);
|
||||||
|
|
|
@ -136,6 +136,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio;
|
renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio;
|
||||||
renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling;
|
renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling;
|
||||||
|
renderer.interframeBlending = renderer.core->opts.interframeBlending;
|
||||||
renderer.filter = renderer.core->opts.resampleVideo;
|
renderer.filter = renderer.core->opts.resampleVideo;
|
||||||
|
|
||||||
if (!mSDLInit(&renderer)) {
|
if (!mSDLInit(&renderer)) {
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct mSDLRenderer {
|
||||||
|
|
||||||
bool lockAspectRatio;
|
bool lockAspectRatio;
|
||||||
bool lockIntegerScaling;
|
bool lockIntegerScaling;
|
||||||
|
bool interframeBlending;
|
||||||
bool filter;
|
bool filter;
|
||||||
|
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct VideoBackend {
|
||||||
bool filter;
|
bool filter;
|
||||||
bool lockAspectRatio;
|
bool lockAspectRatio;
|
||||||
bool lockIntegerScaling;
|
bool lockIntegerScaling;
|
||||||
|
bool interframeBlending;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoShader {
|
struct VideoShader {
|
||||||
|
|
Loading…
Reference in New Issue