mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
91d2c7ec32
6
CHANGES
6
CHANGES
|
@ -30,6 +30,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
|
||||||
|
@ -41,6 +42,7 @@ Other fixes:
|
||||||
- Qt: Fix some Qt display driver race conditions
|
- Qt: Fix some Qt display driver race conditions
|
||||||
- Core: Improved lockstep driver reliability (Le Hoang Quyen)
|
- Core: Improved lockstep driver reliability (Le Hoang Quyen)
|
||||||
- Switch: Fix threading-related crash on second launch
|
- Switch: Fix threading-related crash on second launch
|
||||||
|
- Qt: Fix FPS target maxing out at 59.727 (fixes mgba.io/i/1421)
|
||||||
Misc:
|
Misc:
|
||||||
- GBA Savedata: EEPROM performance fixes
|
- GBA Savedata: EEPROM performance fixes
|
||||||
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||||
|
@ -60,6 +62,8 @@ Misc:
|
||||||
- Qt: Increase maximum magnifications and scaling
|
- Qt: Increase maximum magnifications and scaling
|
||||||
- Qt: Add native FPS button to settings view
|
- Qt: Add native FPS button to settings view
|
||||||
- Qt: Improve sync code
|
- Qt: Improve sync code
|
||||||
|
- Switch: Dynamic display resizing
|
||||||
|
- Qt: Make mute menu option also toggle fast-forward mute (fixes mgba.io/i/1424)
|
||||||
|
|
||||||
0.7.2: (2019-05-25)
|
0.7.2: (2019-05-25)
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
|
@ -73,7 +77,7 @@ Emulation fixes:
|
||||||
- GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743)
|
- GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743)
|
||||||
- GBA Video: Fix scanline cache with scale factor change edge cases
|
- GBA Video: Fix scanline cache with scale factor change edge cases
|
||||||
- GBA Video: Fix sprite mosaic clamping (fixes mgba.io/i/1008)
|
- GBA Video: Fix sprite mosaic clamping (fixes mgba.io/i/1008)
|
||||||
- GBA Video: Implement mosaic on transformed sprites (fixes mgba.io/b/9)
|
- GBA Video: Implement mosaic on transformed sprites (fixes mgba.io/b/5)
|
||||||
Other fixes:
|
Other fixes:
|
||||||
- 3DS: Ensure core 2 can be used for threaded renderer (fixes mgba.io/i/1371)
|
- 3DS: Ensure core 2 can be used for threaded renderer (fixes mgba.io/i/1371)
|
||||||
- All: Fix several memory leaks
|
- All: Fix several memory leaks
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -63,6 +63,9 @@ struct GBAVideoGLBackground {
|
||||||
int32_t refy;
|
int32_t refy;
|
||||||
|
|
||||||
struct GBAVideoGLAffine affine;
|
struct GBAVideoGLAffine affine;
|
||||||
|
|
||||||
|
GLint scanlineAffine[GBA_VIDEO_VERTICAL_PIXELS * 4];
|
||||||
|
GLint scanlineOffset[GBA_VIDEO_VERTICAL_PIXELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -80,8 +83,6 @@ enum {
|
||||||
GBA_GL_TEX_BACKDROP_COLOR,
|
GBA_GL_TEX_BACKDROP_COLOR,
|
||||||
GBA_GL_TEX_BACKDROP_FLAGS,
|
GBA_GL_TEX_BACKDROP_FLAGS,
|
||||||
GBA_GL_TEX_WINDOW,
|
GBA_GL_TEX_WINDOW,
|
||||||
GBA_GL_TEX_AFFINE_2,
|
|
||||||
GBA_GL_TEX_AFFINE_3,
|
|
||||||
GBA_GL_TEX_MAX
|
GBA_GL_TEX_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +112,12 @@ enum {
|
||||||
GBA_GL_OBJ_OBJWIN,
|
GBA_GL_OBJ_OBJWIN,
|
||||||
GBA_GL_OBJ_MOSAIC,
|
GBA_GL_OBJ_MOSAIC,
|
||||||
|
|
||||||
|
GBA_GL_WIN_DISPCNT = 2,
|
||||||
|
GBA_GL_WIN_BLEND,
|
||||||
|
GBA_GL_WIN_FLAGS,
|
||||||
|
GBA_GL_WIN_WIN0,
|
||||||
|
GBA_GL_WIN_WIN1,
|
||||||
|
|
||||||
GBA_GL_FINALIZE_SCALE = 2,
|
GBA_GL_FINALIZE_SCALE = 2,
|
||||||
GBA_GL_FINALIZE_LAYERS,
|
GBA_GL_FINALIZE_LAYERS,
|
||||||
GBA_GL_FINALIZE_FLAGS,
|
GBA_GL_FINALIZE_FLAGS,
|
||||||
|
@ -133,7 +140,6 @@ struct GBAVideoGLRenderer {
|
||||||
uint32_t* temporaryBuffer;
|
uint32_t* temporaryBuffer;
|
||||||
|
|
||||||
struct GBAVideoGLBackground bg[4];
|
struct GBAVideoGLBackground bg[4];
|
||||||
struct GBAVideoGLAffine affine[2][GBA_VIDEO_VERTICAL_PIXELS];
|
|
||||||
|
|
||||||
int oamMax;
|
int oamMax;
|
||||||
bool oamDirty;
|
bool oamDirty;
|
||||||
|
@ -145,10 +151,7 @@ struct GBAVideoGLRenderer {
|
||||||
|
|
||||||
GLuint outputTex;
|
GLuint outputTex;
|
||||||
|
|
||||||
#ifdef BUILD_GLES3
|
GLint shadowPalette[512];
|
||||||
uint16_t shadowPalette[512];
|
|
||||||
#endif
|
|
||||||
GLuint paletteTex;
|
|
||||||
bool paletteDirty;
|
bool paletteDirty;
|
||||||
|
|
||||||
GLuint vramTex;
|
GLuint vramTex;
|
||||||
|
@ -159,6 +162,7 @@ struct GBAVideoGLRenderer {
|
||||||
|
|
||||||
struct GBAVideoGLShader bgShader[6];
|
struct GBAVideoGLShader bgShader[6];
|
||||||
struct GBAVideoGLShader objShader[2];
|
struct GBAVideoGLShader objShader[2];
|
||||||
|
struct GBAVideoGLShader windowShader;
|
||||||
struct GBAVideoGLShader finalizeShader;
|
struct GBAVideoGLShader finalizeShader;
|
||||||
|
|
||||||
GBARegisterDISPCNT dispcnt;
|
GBARegisterDISPCNT dispcnt;
|
||||||
|
@ -175,11 +179,13 @@ struct GBAVideoGLRenderer {
|
||||||
GBAMosaicControl mosaic;
|
GBAMosaicControl mosaic;
|
||||||
|
|
||||||
struct GBAVideoGLWindowN {
|
struct GBAVideoGLWindowN {
|
||||||
struct GBAVideoWindowRegion h[2];
|
struct GBAVideoWindowRegion h;
|
||||||
struct GBAVideoWindowRegion v;
|
struct GBAVideoWindowRegion v;
|
||||||
GBAWindowControl control;
|
GBAWindowControl control;
|
||||||
} winN[2];
|
} winN[2];
|
||||||
|
|
||||||
|
GLint winNHistory[2][GBA_VIDEO_VERTICAL_PIXELS * 4];
|
||||||
|
|
||||||
GBAWindowControl winout;
|
GBAWindowControl winout;
|
||||||
GBAWindowControl objwin;
|
GBAWindowControl objwin;
|
||||||
|
|
||||||
|
|
18932
res/nointro.dat
18932
res/nointro.dat
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
||||||
.title = "Select SGB BIOS path",
|
.title = "Select SGB BIOS path",
|
||||||
.data = "sgb.bios",
|
.data = "sgb.bios",
|
||||||
};
|
};
|
||||||
|
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||||
|
.title = "Interframe blending",
|
||||||
|
.data = "interframeBlending",
|
||||||
|
.submenu = 0,
|
||||||
|
.state = false,
|
||||||
|
.validStates = (const char*[]) {
|
||||||
|
"Off", "On"
|
||||||
|
},
|
||||||
|
.nStates = 2
|
||||||
|
};
|
||||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||||
.title = "Enable SGB borders",
|
.title = "Enable SGB borders",
|
||||||
.data = "sgb.borders",
|
.data = "sgb.borders",
|
||||||
|
|
|
@ -219,6 +219,7 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) {
|
||||||
#ifndef DISABLE_THREADING
|
#ifndef DISABLE_THREADING
|
||||||
if (!runner->autosave.running) {
|
if (!runner->autosave.running) {
|
||||||
runner->autosave.running = true;
|
runner->autosave.running = true;
|
||||||
|
runner->autosave.core = NULL;
|
||||||
MutexInit(&runner->autosave.mutex);
|
MutexInit(&runner->autosave.mutex);
|
||||||
ConditionInit(&runner->autosave.cond);
|
ConditionInit(&runner->autosave.cond);
|
||||||
ThreadCreate(&runner->autosave.thread, mGUIAutosaveThread, &runner->autosave);
|
ThreadCreate(&runner->autosave.thread, mGUIAutosaveThread, &runner->autosave);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#ifndef DISABLE_THREADING
|
#ifndef DISABLE_THREADING
|
||||||
#include <mgba/feature/thread-proxy.h>
|
#include <mgba/feature/thread-proxy.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
#include <mgba/internal/gba/renderers/gl.h>
|
#include <mgba/internal/gba/renderers/gl.h>
|
||||||
#endif
|
#endif
|
||||||
#include <mgba/internal/gba/renderers/proxy.h>
|
#include <mgba/internal/gba/renderers/proxy.h>
|
||||||
|
@ -134,7 +134,7 @@ struct mVideoLogContext;
|
||||||
struct GBACore {
|
struct GBACore {
|
||||||
struct mCore d;
|
struct mCore d;
|
||||||
struct GBAVideoSoftwareRenderer renderer;
|
struct GBAVideoSoftwareRenderer renderer;
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
struct GBAVideoGLRenderer glRenderer;
|
struct GBAVideoGLRenderer glRenderer;
|
||||||
#endif
|
#endif
|
||||||
struct GBAVideoProxyRenderer proxyRenderer;
|
struct GBAVideoProxyRenderer proxyRenderer;
|
||||||
|
@ -184,7 +184,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
||||||
GBAVideoSoftwareRendererCreate(&gbacore->renderer);
|
GBAVideoSoftwareRendererCreate(&gbacore->renderer);
|
||||||
gbacore->renderer.outputBuffer = NULL;
|
gbacore->renderer.outputBuffer = NULL;
|
||||||
|
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
GBAVideoGLRendererCreate(&gbacore->glRenderer);
|
GBAVideoGLRendererCreate(&gbacore->glRenderer);
|
||||||
gbacore->glRenderer.outputTex = -1;
|
gbacore->glRenderer.outputTex = -1;
|
||||||
#endif
|
#endif
|
||||||
|
@ -237,7 +237,7 @@ static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature
|
||||||
UNUSED(core);
|
UNUSED(core);
|
||||||
switch (feature) {
|
switch (feature) {
|
||||||
case mCORE_FEATURE_OPENGL:
|
case mCORE_FEATURE_OPENGL:
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
@ -297,7 +297,7 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
struct GBACore* gbacore = (struct GBACore*) core;
|
struct GBACore* gbacore = (struct GBACore*) core;
|
||||||
int scale = gbacore->glRenderer.scale;
|
int scale = gbacore->glRenderer.scale;
|
||||||
#else
|
#else
|
||||||
|
@ -316,7 +316,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
|
static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
struct GBACore* gbacore = (struct GBACore*) core;
|
struct GBACore* gbacore = (struct GBACore*) core;
|
||||||
gbacore->glRenderer.outputTex = texid;
|
gbacore->glRenderer.outputTex = texid;
|
||||||
#else
|
#else
|
||||||
|
@ -453,7 +453,7 @@ static void _GBACoreReset(struct mCore* core) {
|
||||||
struct GBACore* gbacore = (struct GBACore*) core;
|
struct GBACore* gbacore = (struct GBACore*) core;
|
||||||
struct GBA* gba = (struct GBA*) core->board;
|
struct GBA* gba = (struct GBA*) core->board;
|
||||||
if (gbacore->renderer.outputBuffer
|
if (gbacore->renderer.outputBuffer
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
|| gbacore->glRenderer.outputTex != (unsigned) -1
|
|| gbacore->glRenderer.outputTex != (unsigned) -1
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
|
@ -462,7 +462,7 @@ static void _GBACoreReset(struct mCore* core) {
|
||||||
renderer = &gbacore->renderer.d;
|
renderer = &gbacore->renderer.d;
|
||||||
}
|
}
|
||||||
int fakeBool;
|
int fakeBool;
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
|
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
|
||||||
renderer = &gbacore->glRenderer.d;
|
renderer = &gbacore->glRenderer.d;
|
||||||
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
|
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
|
||||||
|
|
|
@ -52,9 +52,12 @@ struct GBAVideoGLUniform {
|
||||||
int type;
|
int type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define PALETTE_ENTRY "#define PALETTE_ENTRY(x) (vec3((ivec3(0x1F, 0x3E0, 0x7C00) & (x)) >> ivec3(0, 5, 10)) / 31.)\n"
|
||||||
|
|
||||||
static const GLchar* const _gles3Header =
|
static const GLchar* const _gles3Header =
|
||||||
"#version 300 es\n"
|
"#version 300 es\n"
|
||||||
"#define OUT(n) layout(location = n)\n"
|
"#define OUT(n) layout(location = n)\n"
|
||||||
|
PALETTE_ENTRY
|
||||||
"precision highp float;\n"
|
"precision highp float;\n"
|
||||||
"precision highp int;\n"
|
"precision highp int;\n"
|
||||||
"precision highp sampler2D;\n"
|
"precision highp sampler2D;\n"
|
||||||
|
@ -63,6 +66,7 @@ static const GLchar* const _gles3Header =
|
||||||
static const GLchar* const _gl3Header =
|
static const GLchar* const _gl3Header =
|
||||||
"#version 130\n"
|
"#version 130\n"
|
||||||
"#define OUT(n)\n"
|
"#define OUT(n)\n"
|
||||||
|
PALETTE_ENTRY
|
||||||
"precision highp float;\n";
|
"precision highp float;\n";
|
||||||
|
|
||||||
static const char* const _vertexShader =
|
static const char* const _vertexShader =
|
||||||
|
@ -82,11 +86,11 @@ static const char* const _renderTile16 =
|
||||||
" int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
|
" int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
|
||||||
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
||||||
" int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
|
" int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
|
||||||
" vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n"
|
|
||||||
" if (entry == 0) {\n"
|
" if (entry == 0) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" color.a = 1.;\n"
|
" int paletteEntry = palette[paletteId * 16 + entry];\n"
|
||||||
|
" vec4 color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n"
|
||||||
" return color;\n"
|
" return color;\n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
@ -96,11 +100,11 @@ static const char* const _renderTile256 =
|
||||||
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
||||||
" int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
|
" int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
|
||||||
" int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
|
" int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
|
||||||
" vec4 color = texelFetch(palette, ivec2(entry, pal2 + (paletteId & 16)), 0);\n"
|
|
||||||
" if ((pal2 | entry) == 0) {\n"
|
" if ((pal2 | entry) == 0) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" color.a = 1.;\n"
|
" int paletteEntry = palette[pal2 * 16 + entry];\n"
|
||||||
|
" vec4 color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n"
|
||||||
" return color;\n"
|
" return color;\n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
@ -121,11 +125,11 @@ static const struct GBAVideoGLUniform _uniformsMode0[] = {
|
||||||
static const char* const _renderMode0 =
|
static const char* const _renderMode0 =
|
||||||
"in vec2 texCoord;\n"
|
"in vec2 texCoord;\n"
|
||||||
"uniform sampler2D vram;\n"
|
"uniform sampler2D vram;\n"
|
||||||
"uniform sampler2D palette;\n"
|
"uniform int palette[256];\n"
|
||||||
"uniform int screenBase;\n"
|
"uniform int screenBase;\n"
|
||||||
"uniform int charBase;\n"
|
"uniform int charBase;\n"
|
||||||
"uniform int size;\n"
|
"uniform int size;\n"
|
||||||
"uniform ivec2 offset;\n"
|
"uniform int offset[160];\n"
|
||||||
"uniform ivec4 inflags;\n"
|
"uniform ivec4 inflags;\n"
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
@ -141,11 +145,17 @@ static const char* const _renderMode0 =
|
||||||
" if (mosaic.y > 1) {\n"
|
" if (mosaic.y > 1) {\n"
|
||||||
" coord.y -= coord.y % mosaic.y;\n"
|
" coord.y -= coord.y % mosaic.y;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" coord += offset;\n"
|
" coord += (ivec2(0x3FF, 0x3FF000) & offset[int(texCoord.y)]) >> ivec2(0, 12);\n"
|
||||||
|
" if (size == 3) {\n"
|
||||||
|
" coord.y += (coord.y & 256) << 1;\n"
|
||||||
|
" }\n"
|
||||||
|
" if (size != 2) {\n"
|
||||||
|
" coord.y &= ~256;\n"
|
||||||
|
" }\n"
|
||||||
" if ((size & 1) == 1) {\n"
|
" if ((size & 1) == 1) {\n"
|
||||||
" coord.y += coord.x & 256;\n"
|
" coord.y += coord.x & 256;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" coord.x &= 255;\n"
|
" coord &= ivec2(255, 511);\n"
|
||||||
" int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n"
|
" int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n"
|
||||||
" vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
|
" vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
|
||||||
" int tileFlags = int(map.g * 15.9);\n"
|
" int tileFlags = int(map.g * 15.9);\n"
|
||||||
|
@ -204,72 +214,25 @@ static const char* const _interpolate =
|
||||||
|
|
||||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]) {\n"
|
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]) {\n"
|
||||||
" int start = max(range.x, y - 3);\n"
|
" int start = max(range.x, y - 3);\n"
|
||||||
" ivec4 splitOffset[4];\n"
|
" mat[0] = transform[start + 0].xy;\n"
|
||||||
|
" aff[0] = transform[start + 0].zw;\n"
|
||||||
" mat[0] = texelFetch(transform, ivec2(0, start), 0).xz;\n"
|
" mat[1] = transform[start + 1].xy;\n"
|
||||||
" mat[1] = texelFetch(transform, ivec2(0, start + 1), 0).xz;\n"
|
" aff[1] = transform[start + 1].zw;\n"
|
||||||
" mat[2] = texelFetch(transform, ivec2(0, start + 2), 0).xz;\n"
|
" mat[2] = transform[start + 2].xy;\n"
|
||||||
" mat[3] = texelFetch(transform, ivec2(0, start + 3), 0).xz;\n"
|
" aff[2] = transform[start + 2].zw;\n"
|
||||||
|
" mat[3] = transform[start + 3].xy;\n"
|
||||||
" splitOffset[0] = texelFetch(transform, ivec2(1, start + 0), 0);\n"
|
" aff[3] = transform[start + 3].zw;\n"
|
||||||
" splitOffset[1] = texelFetch(transform, ivec2(1, start + 1), 0);\n"
|
|
||||||
" splitOffset[2] = texelFetch(transform, ivec2(1, start + 2), 0);\n"
|
|
||||||
" splitOffset[3] = texelFetch(transform, ivec2(1, start + 3), 0);\n"
|
|
||||||
|
|
||||||
" aff[0] = (splitOffset[0].xz & 0xFFFF) + (splitOffset[0].yw << 16);\n"
|
|
||||||
" aff[1] = (splitOffset[1].xz & 0xFFFF) + (splitOffset[1].yw << 16);\n"
|
|
||||||
" aff[2] = (splitOffset[2].xz & 0xFFFF) + (splitOffset[2].yw << 16);\n"
|
|
||||||
" aff[3] = (splitOffset[3].xz & 0xFFFF) + (splitOffset[3].yw << 16);\n"
|
|
||||||
|
|
||||||
" if (y - 3 < range.x) {\n"
|
|
||||||
" ivec2 tempMat[3];\n"
|
|
||||||
" ivec2 tempAff[3];\n"
|
|
||||||
" tempMat[0] = ivec2(interpolate(mat, -0.75));\n"
|
|
||||||
" tempMat[1] = ivec2(interpolate(mat, -0.5));\n"
|
|
||||||
" tempMat[2] = ivec2(interpolate(mat, -0.25));\n"
|
|
||||||
" tempAff[0] = ivec2(interpolate(aff, -0.75));\n"
|
|
||||||
" tempAff[1] = ivec2(interpolate(aff, -0.5));\n"
|
|
||||||
" tempAff[2] = ivec2(interpolate(aff, -0.25));\n"
|
|
||||||
" if (range.x == y) {\n"
|
|
||||||
" mat[3] = mat[0];\n"
|
|
||||||
" mat[2] = tempMat[2];\n"
|
|
||||||
" mat[1] = tempMat[1];\n"
|
|
||||||
" mat[0] = tempMat[0];\n"
|
|
||||||
" aff[3] = aff[0];\n"
|
|
||||||
" aff[2] = tempAff[2];\n"
|
|
||||||
" aff[1] = tempAff[1];\n"
|
|
||||||
" aff[0] = tempAff[0];\n"
|
|
||||||
" } else if (range.x == y - 1) {\n"
|
|
||||||
" mat[3] = mat[1];\n"
|
|
||||||
" mat[2] = mat[0];\n"
|
|
||||||
" mat[1] = tempMat[2];\n"
|
|
||||||
" mat[0] = tempMat[1];\n"
|
|
||||||
" aff[3] = aff[1];\n"
|
|
||||||
" aff[2] = aff[0];\n"
|
|
||||||
" aff[1] = tempAff[2];\n"
|
|
||||||
" aff[0] = tempAff[1];\n"
|
|
||||||
" } else if (range.x == y - 2) {\n"
|
|
||||||
" mat[3] = mat[2];\n"
|
|
||||||
" mat[2] = mat[1];\n"
|
|
||||||
" mat[1] = mat[0];\n"
|
|
||||||
" mat[0] = tempMat[0];\n"
|
|
||||||
" aff[3] = aff[2];\n"
|
|
||||||
" aff[2] = aff[1];\n"
|
|
||||||
" aff[1] = aff[0];\n"
|
|
||||||
" aff[0] = tempAff[0];\n"
|
|
||||||
" }\n"
|
|
||||||
" }\n"
|
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
static const char* const _renderMode2 =
|
static const char* const _renderMode2 =
|
||||||
"in vec2 texCoord;\n"
|
"in vec2 texCoord;\n"
|
||||||
"uniform sampler2D vram;\n"
|
"uniform sampler2D vram;\n"
|
||||||
"uniform sampler2D palette;\n"
|
"uniform int palette[256];\n"
|
||||||
"uniform int screenBase;\n"
|
"uniform int screenBase;\n"
|
||||||
"uniform int charBase;\n"
|
"uniform int charBase;\n"
|
||||||
"uniform int size;\n"
|
"uniform int size;\n"
|
||||||
"uniform ivec4 inflags;\n"
|
"uniform ivec4 inflags;\n"
|
||||||
"uniform isampler2D transform;\n"
|
"uniform ivec4 transform[160];\n"
|
||||||
"uniform ivec2 range;\n"
|
"uniform ivec2 range;\n"
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
@ -288,11 +251,11 @@ static const char* const _renderMode2 =
|
||||||
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
|
||||||
" int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
|
" int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
|
||||||
" int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
|
" int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
|
||||||
" vec4 color = texelFetch(palette, ivec2(entry, pal2), 0);\n"
|
|
||||||
" if ((pal2 | entry) == 0) {\n"
|
" if ((pal2 | entry) == 0) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" color.a = 1.;\n"
|
" int paletteEntry = palette[pal2 * 16 + entry];\n"
|
||||||
|
" vec4 color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n"
|
||||||
" return color;\n"
|
" return color;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
|
|
||||||
|
@ -308,7 +271,12 @@ static const char* const _renderMode2 =
|
||||||
" }\n"
|
" }\n"
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||||
" float y = fract(incoord.y);\n"
|
" float y = fract(incoord.y);\n"
|
||||||
" float lin = 0.75 + y * 0.25;\n"
|
" float start = 0.75;\n"
|
||||||
|
" if (int(incoord.y) - range.x < 4) {\n"
|
||||||
|
" y = incoord.y - float(range.x);\n"
|
||||||
|
" start = 0.;\n"
|
||||||
|
" }\n"
|
||||||
|
" float lin = start + y * 0.25;\n"
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
||||||
" color = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n"
|
" color = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n"
|
||||||
|
@ -335,7 +303,7 @@ static const char* const _renderMode35 =
|
||||||
"uniform int charBase;\n"
|
"uniform int charBase;\n"
|
||||||
"uniform ivec2 size;\n"
|
"uniform ivec2 size;\n"
|
||||||
"uniform ivec4 inflags;\n"
|
"uniform ivec4 inflags;\n"
|
||||||
"uniform isampler2D transform;\n"
|
"uniform ivec4 transform[160];\n"
|
||||||
"uniform ivec2 range;\n"
|
"uniform ivec2 range;\n"
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
@ -356,7 +324,12 @@ static const char* const _renderMode35 =
|
||||||
" }\n"
|
" }\n"
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||||
" float y = fract(incoord.y);\n"
|
" float y = fract(incoord.y);\n"
|
||||||
" float lin = 0.75 + y * 0.25;\n"
|
" float start = 0.75;\n"
|
||||||
|
" if (int(incoord.y) - range.x < 4) {\n"
|
||||||
|
" y = incoord.y - float(range.x);\n"
|
||||||
|
" start = 0.;\n"
|
||||||
|
" }\n"
|
||||||
|
" float lin = start + y * 0.25;\n"
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
||||||
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
||||||
|
@ -391,11 +364,11 @@ static const struct GBAVideoGLUniform _uniformsMode4[] = {
|
||||||
static const char* const _renderMode4 =
|
static const char* const _renderMode4 =
|
||||||
"in vec2 texCoord;\n"
|
"in vec2 texCoord;\n"
|
||||||
"uniform sampler2D vram;\n"
|
"uniform sampler2D vram;\n"
|
||||||
"uniform sampler2D palette;\n"
|
"uniform int palette[256];\n"
|
||||||
"uniform int charBase;\n"
|
"uniform int charBase;\n"
|
||||||
"uniform ivec2 size;\n"
|
"uniform ivec2 size;\n"
|
||||||
"uniform ivec4 inflags;\n"
|
"uniform ivec4 inflags;\n"
|
||||||
"uniform isampler2D transform;\n"
|
"uniform ivec4 transform[160];\n"
|
||||||
"uniform ivec2 range;\n"
|
"uniform ivec2 range;\n"
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
@ -416,7 +389,12 @@ static const char* const _renderMode4 =
|
||||||
" }\n"
|
" }\n"
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||||
" float y = fract(incoord.y);\n"
|
" float y = fract(incoord.y);\n"
|
||||||
" float lin = 0.75 + y * 0.25;\n"
|
" float start = 0.75;\n"
|
||||||
|
" if (int(incoord.y) - range.x < 4) {\n"
|
||||||
|
" y = incoord.y - float(range.x);\n"
|
||||||
|
" start = 0.;\n"
|
||||||
|
" }\n"
|
||||||
|
" float lin = start + y * 0.25;\n"
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
||||||
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
||||||
|
@ -429,8 +407,8 @@ static const char* const _renderMode4 =
|
||||||
" int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n"
|
" int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n"
|
||||||
" vec4 twoEntries = texelFetch(vram, ivec2((address >> 1) & 255, address >> 9), 0);\n"
|
" vec4 twoEntries = texelFetch(vram, ivec2((address >> 1) & 255, address >> 9), 0);\n"
|
||||||
" ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n"
|
" ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n"
|
||||||
" color = texelFetch(palette, entry, 0);\n"
|
" int paletteEntry = palette[entry.y * 16 + entry.x];\n"
|
||||||
" color.a = 1.;\n"
|
" color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n"
|
||||||
" flags = inflags;\n"
|
" flags = inflags;\n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
@ -453,7 +431,7 @@ static const struct GBAVideoGLUniform _uniformsObj[] = {
|
||||||
static const char* const _renderObj =
|
static const char* const _renderObj =
|
||||||
"in vec2 texCoord;\n"
|
"in vec2 texCoord;\n"
|
||||||
"uniform sampler2D vram;\n"
|
"uniform sampler2D vram;\n"
|
||||||
"uniform sampler2D palette;\n"
|
"uniform int palette[256];\n"
|
||||||
"uniform int charBase;\n"
|
"uniform int charBase;\n"
|
||||||
"uniform int stride;\n"
|
"uniform int stride;\n"
|
||||||
"uniform int localPalette;\n"
|
"uniform int localPalette;\n"
|
||||||
|
@ -485,16 +463,82 @@ static const char* const _renderObj =
|
||||||
" if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n"
|
" if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" vec4 pix = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, 16 + localPalette, coord & 7);\n"
|
" vec4 pix = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, localPalette, coord & 7);\n"
|
||||||
" if (objwin.x > 0) {\n"
|
|
||||||
" pix.a = 0.;\n"
|
|
||||||
" }\n"
|
|
||||||
" color = pix;\n"
|
" color = pix;\n"
|
||||||
" flags = inflags;\n"
|
" flags = inflags;\n"
|
||||||
" gl_FragDepth = float(flags.x) / 16.;\n"
|
" gl_FragDepth = float(flags.x) / 16.;\n"
|
||||||
" window = objwin.yzw;\n"
|
" window = objwin.yzw;\n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
static const struct GBAVideoGLUniform _uniformsWindow[] = {
|
||||||
|
{ "loc", GBA_GL_VS_LOC, },
|
||||||
|
{ "maxPos", GBA_GL_VS_MAXPOS, },
|
||||||
|
{ "dispcnt", GBA_GL_WIN_DISPCNT, },
|
||||||
|
{ "blend", GBA_GL_WIN_BLEND, },
|
||||||
|
{ "flags", GBA_GL_WIN_FLAGS, },
|
||||||
|
{ "win0", GBA_GL_WIN_WIN0, },
|
||||||
|
{ "win1", GBA_GL_WIN_WIN1, },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* const _renderWindow =
|
||||||
|
"in vec2 texCoord;\n"
|
||||||
|
"uniform int dispcnt;\n"
|
||||||
|
"uniform ivec2 blend;\n"
|
||||||
|
"uniform ivec3 flags;\n"
|
||||||
|
"uniform ivec4 win0[160];\n"
|
||||||
|
"uniform ivec4 win1[160];\n"
|
||||||
|
"OUT(0) out ivec3 window;\n"
|
||||||
|
|
||||||
|
"void crop(vec4 windowParams, int flags, inout ivec3 windowFlags) {\n"
|
||||||
|
" bvec4 compare = lessThan(texCoord.xxyy, windowParams);\n"
|
||||||
|
" compare = equal(compare, bvec4(true, false, true, false));\n"
|
||||||
|
" if (any(compare)) {\n"
|
||||||
|
" vec2 h = windowParams.xy;\n"
|
||||||
|
" vec2 v = windowParams.zw;\n"
|
||||||
|
" if (v.x > v.y) {\n"
|
||||||
|
" if (compare.z && compare.w) {\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" } else if (compare.z || compare.w) {\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" if (h.x > h.y) {\n"
|
||||||
|
" if (compare.x && compare.y) {\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" } else if (compare.x || compare.y) {\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" windowFlags.x = flags;\n"
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
"vec4 interpolate(ivec4 win[160]) {\n"
|
||||||
|
" vec4 bottom = vec4(win[int(texCoord.y) - 1]);\n"
|
||||||
|
" vec4 top = vec4(win[int(texCoord.y)]);\n"
|
||||||
|
" if (distance(top, bottom) > 40.) {\n"
|
||||||
|
" return top;\n"
|
||||||
|
" }\n"
|
||||||
|
" return vec4(mix(bottom.xy, top.xy, fract(texCoord.y)), top.zw);\n"
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
"void main() {\n"
|
||||||
|
" int dispflags = (dispcnt & 0x1F) | 0x20;\n"
|
||||||
|
" if ((dispcnt & 0xE0) == 0) {\n"
|
||||||
|
" window = ivec3(dispflags, blend);\n"
|
||||||
|
" } else {\n"
|
||||||
|
" ivec3 windowFlags = ivec3(flags.z, blend);\n"
|
||||||
|
" if ((dispcnt & 0x40) != 0) { \n"
|
||||||
|
" crop(interpolate(win1), flags.y, windowFlags);\n"
|
||||||
|
" }\n"
|
||||||
|
" if ((dispcnt & 0x20) != 0) { \n"
|
||||||
|
" crop(interpolate(win0), flags.x, windowFlags);\n"
|
||||||
|
" }\n"
|
||||||
|
" window = windowFlags;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
static const struct GBAVideoGLUniform _uniformsFinalize[] = {
|
static const struct GBAVideoGLUniform _uniformsFinalize[] = {
|
||||||
{ "loc", GBA_GL_VS_LOC, },
|
{ "loc", GBA_GL_VS_LOC, },
|
||||||
{ "maxPos", GBA_GL_VS_MAXPOS, },
|
{ "maxPos", GBA_GL_VS_MAXPOS, },
|
||||||
|
@ -571,12 +615,12 @@ static const char* const _finalize =
|
||||||
" topFlags.y &= ~1;\n"
|
" topFlags.y &= ~1;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
|
" if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
|
||||||
" topPixel *= float(topFlags.z) / 16.;\n"
|
" topPixel.rgb *= float(topFlags.z) / 16.;\n"
|
||||||
" topPixel += bottomPixel * float(windowFlags.y) / 16.;\n"
|
" topPixel.rgb += bottomPixel.rgb * float(windowFlags.y) / 16.;\n"
|
||||||
" } else if ((topFlags.y & 13) == 9) {\n"
|
" } else if ((topFlags.y & 13) == 9) {\n"
|
||||||
" topPixel += (1. - topPixel) * float(windowFlags.z) / 16.;\n"
|
" topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n"
|
||||||
" } else if ((topFlags.y & 13) == 13) {\n"
|
" } else if ((topFlags.y & 13) == 13) {\n"
|
||||||
" topPixel -= topPixel * float(windowFlags.z) / 16.;\n"
|
" topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" color = topPixel;\n"
|
" color = topPixel;\n"
|
||||||
"}";
|
"}";
|
||||||
|
@ -673,30 +717,12 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
||||||
glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
||||||
|
|
||||||
glGenTextures(1, &glRenderer->paletteTex);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
|
|
||||||
glGenTextures(1, &glRenderer->vramTex);
|
glGenTextures(1, &glRenderer->vramTex);
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
|
glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL);
|
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
|
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
|
||||||
_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||||
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
||||||
|
@ -749,8 +775,8 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
|
||||||
_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||||
_initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
_initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
}
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
char log[2048];
|
char log[2048];
|
||||||
const GLchar* shaderBuffer[4];
|
const GLchar* shaderBuffer[4];
|
||||||
|
@ -809,6 +835,12 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
|
glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
shaderBuffer[1] = _renderWindow;
|
||||||
|
_compileShader(glRenderer, &glRenderer->windowShader, shaderBuffer, 2, vs, _uniformsWindow, log);
|
||||||
|
#ifndef BUILD_GLES3
|
||||||
|
glBindFragDataLocation(glRenderer->windowShader.program, 0, "window");
|
||||||
|
#endif
|
||||||
|
|
||||||
shaderBuffer[1] = _finalize;
|
shaderBuffer[1] = _finalize;
|
||||||
_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
|
_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
|
||||||
|
|
||||||
|
@ -825,7 +857,6 @@ void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||||
}
|
}
|
||||||
glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
||||||
glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
||||||
glDeleteTextures(1, &glRenderer->paletteTex);
|
|
||||||
glDeleteTextures(1, &glRenderer->vramTex);
|
glDeleteTextures(1, &glRenderer->vramTex);
|
||||||
glDeleteBuffers(1, &glRenderer->vbo);
|
glDeleteBuffers(1, &glRenderer->vbo);
|
||||||
|
|
||||||
|
@ -849,14 +880,7 @@ void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||||
void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
|
void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
|
||||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||||
|
|
||||||
#ifdef BUILD_GLES3
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 512; ++i) {
|
|
||||||
renderer->writePalette(renderer, i << 1, renderer->palette[i]);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
glRenderer->paletteDirty = true;
|
glRenderer->paletteDirty = true;
|
||||||
#endif
|
|
||||||
glRenderer->vramDirty = 0xFFFFFF;
|
glRenderer->vramDirty = 0xFFFFFF;
|
||||||
glRenderer->firstAffine = -1;
|
glRenderer->firstAffine = -1;
|
||||||
glRenderer->firstY = -1;
|
glRenderer->firstY = -1;
|
||||||
|
@ -879,12 +903,8 @@ void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam)
|
||||||
|
|
||||||
void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||||
#ifdef BUILD_GLES3
|
|
||||||
glRenderer->shadowPalette[address >> 1] = ((value & 0x1F) << 11) | ((value & 0x3E0) << 1) | ((value & 0x7C00) >> 10);
|
|
||||||
#else
|
|
||||||
UNUSED(address);
|
UNUSED(address);
|
||||||
UNUSED(value);
|
UNUSED(value);
|
||||||
#endif
|
|
||||||
glRenderer->paletteDirty = true;
|
glRenderer->paletteDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -894,105 +914,175 @@ uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer,
|
||||||
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dirty = true;
|
bool dirty = false;
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case REG_DISPCNT:
|
case REG_DISPCNT:
|
||||||
value &= 0xFFF7;
|
value &= 0xFFF7;
|
||||||
|
dirty = true;
|
||||||
break;
|
break;
|
||||||
case REG_BG0CNT:
|
case REG_BG0CNT:
|
||||||
case REG_BG1CNT:
|
case REG_BG1CNT:
|
||||||
value &= 0xDFFF;
|
value &= 0xDFFF;
|
||||||
|
dirty = true;
|
||||||
break;
|
break;
|
||||||
case REG_BG0HOFS:
|
case REG_BG0HOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[0].x = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG0VOFS:
|
case REG_BG0VOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[0].y = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG1HOFS:
|
case REG_BG1HOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[1].x = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG1VOFS:
|
case REG_BG1VOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[1].y = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG2HOFS:
|
case REG_BG2HOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[2].x = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG2VOFS:
|
case REG_BG2VOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[2].y = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG3HOFS:
|
case REG_BG3HOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[3].x = value;
|
||||||
|
dirty = false;
|
||||||
|
break;
|
||||||
case REG_BG3VOFS:
|
case REG_BG3VOFS:
|
||||||
value &= 0x01FF;
|
value &= 0x01FF;
|
||||||
|
glRenderer->bg[3].y = value;
|
||||||
|
dirty = false;
|
||||||
break;
|
break;
|
||||||
case REG_BG2PA:
|
case REG_BG2PA:
|
||||||
glRenderer->bg[2].affine.dx = value;
|
glRenderer->bg[2].affine.dx = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2PB:
|
case REG_BG2PB:
|
||||||
glRenderer->bg[2].affine.dmx = value;
|
glRenderer->bg[2].affine.dmx = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2PC:
|
case REG_BG2PC:
|
||||||
glRenderer->bg[2].affine.dy = value;
|
glRenderer->bg[2].affine.dy = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2PD:
|
case REG_BG2PD:
|
||||||
glRenderer->bg[2].affine.dmy = value;
|
glRenderer->bg[2].affine.dmy = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2X_LO:
|
case REG_BG2X_LO:
|
||||||
GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
|
GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2X_HI:
|
case REG_BG2X_HI:
|
||||||
GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
|
GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2Y_LO:
|
case REG_BG2Y_LO:
|
||||||
GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
|
GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG2Y_HI:
|
case REG_BG2Y_HI:
|
||||||
GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
|
GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3PA:
|
case REG_BG3PA:
|
||||||
glRenderer->bg[3].affine.dx = value;
|
glRenderer->bg[3].affine.dx = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3PB:
|
case REG_BG3PB:
|
||||||
glRenderer->bg[3].affine.dmx = value;
|
glRenderer->bg[3].affine.dmx = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3PC:
|
case REG_BG3PC:
|
||||||
glRenderer->bg[3].affine.dy = value;
|
glRenderer->bg[3].affine.dy = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3PD:
|
case REG_BG3PD:
|
||||||
glRenderer->bg[3].affine.dmy = value;
|
glRenderer->bg[3].affine.dmy = value;
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3X_LO:
|
case REG_BG3X_LO:
|
||||||
GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
|
GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3X_HI:
|
case REG_BG3X_HI:
|
||||||
GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
|
GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3Y_LO:
|
case REG_BG3Y_LO:
|
||||||
GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
|
GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BG3Y_HI:
|
case REG_BG3Y_HI:
|
||||||
GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
|
GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
|
||||||
dirty = false;
|
|
||||||
break;
|
break;
|
||||||
case REG_BLDALPHA:
|
case REG_BLDALPHA:
|
||||||
value &= 0x1F1F;
|
value &= 0x1F1F;
|
||||||
|
dirty = true;
|
||||||
break;
|
break;
|
||||||
case REG_BLDY:
|
case REG_BLDY:
|
||||||
value &= 0x1F;
|
value &= 0x1F;
|
||||||
if (value > 0x10) {
|
if (value > 0x10) {
|
||||||
value = 0x10;
|
value = 0x10;
|
||||||
}
|
}
|
||||||
|
dirty = true;
|
||||||
|
break;
|
||||||
|
case REG_WIN0H:
|
||||||
|
glRenderer->winN[0].h.end = value;
|
||||||
|
glRenderer->winN[0].h.start = value >> 8;
|
||||||
|
if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h.start > glRenderer->winN[0].h.end) {
|
||||||
|
glRenderer->winN[0].h.start = 0;
|
||||||
|
}
|
||||||
|
if (glRenderer->winN[0].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
|
glRenderer->winN[0].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
|
glRenderer->winN[0].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REG_WIN1H:
|
||||||
|
glRenderer->winN[1].h.end = value;
|
||||||
|
glRenderer->winN[1].h.start = value >> 8;
|
||||||
|
if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h.start > glRenderer->winN[1].h.end) {
|
||||||
|
glRenderer->winN[1].h.start = 0;
|
||||||
|
}
|
||||||
|
if (glRenderer->winN[1].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
|
glRenderer->winN[1].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
|
glRenderer->winN[1].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REG_WIN0V:
|
||||||
|
glRenderer->winN[0].v.end = value;
|
||||||
|
glRenderer->winN[0].v.start = value >> 8;
|
||||||
|
if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
|
||||||
|
glRenderer->winN[0].v.start = 0;
|
||||||
|
}
|
||||||
|
if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
|
||||||
|
if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REG_WIN1V:
|
||||||
|
glRenderer->winN[1].v.end = value;
|
||||||
|
glRenderer->winN[1].v.start = value >> 8;
|
||||||
|
if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
|
||||||
|
glRenderer->winN[1].v.start = 0;
|
||||||
|
}
|
||||||
|
if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
|
||||||
|
if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
|
||||||
|
glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case REG_WININ:
|
case REG_WININ:
|
||||||
value &= 0x3F3F;
|
|
||||||
break;
|
|
||||||
case REG_WINOUT:
|
case REG_WINOUT:
|
||||||
value &= 0x3F3F;
|
value &= 0x3F3F;
|
||||||
|
dirty = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
dirty = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (glRenderer->shadowRegs[address >> 1] == value) {
|
if (glRenderer->shadowRegs[address >> 1] == value) {
|
||||||
|
@ -1024,30 +1114,6 @@ void _cleanRegister(struct GBAVideoGLRenderer* glRenderer, int address, uint16_t
|
||||||
case REG_BG3CNT:
|
case REG_BG3CNT:
|
||||||
GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
|
GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
|
||||||
break;
|
break;
|
||||||
case REG_BG0HOFS:
|
|
||||||
glRenderer->bg[0].x = value;
|
|
||||||
break;
|
|
||||||
case REG_BG0VOFS:
|
|
||||||
glRenderer->bg[0].y = value;
|
|
||||||
break;
|
|
||||||
case REG_BG1HOFS:
|
|
||||||
glRenderer->bg[1].x = value;
|
|
||||||
break;
|
|
||||||
case REG_BG1VOFS:
|
|
||||||
glRenderer->bg[1].y = value;
|
|
||||||
break;
|
|
||||||
case REG_BG2HOFS:
|
|
||||||
glRenderer->bg[2].x = value;
|
|
||||||
break;
|
|
||||||
case REG_BG2VOFS:
|
|
||||||
glRenderer->bg[2].y = value;
|
|
||||||
break;
|
|
||||||
case REG_BG3HOFS:
|
|
||||||
glRenderer->bg[3].x = value;
|
|
||||||
break;
|
|
||||||
case REG_BG3VOFS:
|
|
||||||
glRenderer->bg[3].y = value;
|
|
||||||
break;
|
|
||||||
case REG_BLDCNT:
|
case REG_BLDCNT:
|
||||||
GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
|
GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
|
||||||
value &= 0x3FFF;
|
value &= 0x3FFF;
|
||||||
|
@ -1066,58 +1132,6 @@ void _cleanRegister(struct GBAVideoGLRenderer* glRenderer, int address, uint16_t
|
||||||
case REG_BLDY:
|
case REG_BLDY:
|
||||||
glRenderer->bldy = value;
|
glRenderer->bldy = value;
|
||||||
break;
|
break;
|
||||||
case REG_WIN0H:
|
|
||||||
glRenderer->winN[0].h[0].end = value;
|
|
||||||
glRenderer->winN[0].h[0].start = value >> 8;
|
|
||||||
if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) {
|
|
||||||
glRenderer->winN[0].h[0].start = 0;
|
|
||||||
}
|
|
||||||
if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
|
||||||
glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
|
|
||||||
if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
|
||||||
glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case REG_WIN1H:
|
|
||||||
glRenderer->winN[1].h[0].end = value;
|
|
||||||
glRenderer->winN[1].h[0].start = value >> 8;
|
|
||||||
if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) {
|
|
||||||
glRenderer->winN[1].h[0].start = 0;
|
|
||||||
}
|
|
||||||
if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
|
||||||
glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
|
|
||||||
if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
|
|
||||||
glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case REG_WIN0V:
|
|
||||||
glRenderer->winN[0].v.end = value;
|
|
||||||
glRenderer->winN[0].v.start = value >> 8;
|
|
||||||
if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
|
|
||||||
glRenderer->winN[0].v.start = 0;
|
|
||||||
}
|
|
||||||
if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
|
|
||||||
glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
|
|
||||||
if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
|
|
||||||
glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case REG_WIN1V:
|
|
||||||
glRenderer->winN[1].v.end = value;
|
|
||||||
glRenderer->winN[1].v.start = value >> 8;
|
|
||||||
if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
|
|
||||||
glRenderer->winN[1].v.start = 0;
|
|
||||||
}
|
|
||||||
if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
|
|
||||||
glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
|
|
||||||
if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
|
|
||||||
glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case REG_WININ:
|
case REG_WININ:
|
||||||
glRenderer->winN[0].control = value;
|
glRenderer->winN[0].control = value;
|
||||||
glRenderer->winN[1].control = value >> 8;
|
glRenderer->winN[1].control = value >> 8;
|
||||||
|
@ -1239,8 +1253,6 @@ static bool _needsVramUpload(struct GBAVideoGLRenderer* renderer, int y) {
|
||||||
void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||||
|
|
||||||
memcpy(&glRenderer->affine[0][y], &glRenderer->bg[2].affine, sizeof(struct GBAVideoGLAffine));
|
|
||||||
memcpy(&glRenderer->affine[1][y], &glRenderer->bg[3].affine, sizeof(struct GBAVideoGLAffine));
|
|
||||||
if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
|
if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
|
||||||
if (glRenderer->firstAffine < 0) {
|
if (glRenderer->firstAffine < 0) {
|
||||||
glRenderer->firstAffine = y;
|
glRenderer->firstAffine = y;
|
||||||
|
@ -1259,9 +1271,6 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
glRenderer->firstY = y;
|
glRenderer->firstY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
|
|
||||||
memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 0x30; ++i) {
|
for (i = 0; i < 0x30; ++i) {
|
||||||
if (!(glRenderer->regsDirty & (1ULL << i))) {
|
if (!(glRenderer->regsDirty & (1ULL << i))) {
|
||||||
|
@ -1271,13 +1280,36 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
}
|
}
|
||||||
glRenderer->regsDirty = 0;
|
glRenderer->regsDirty = 0;
|
||||||
|
|
||||||
|
glRenderer->winNHistory[0][y * 4 + 0] = glRenderer->winN[0].h.start;
|
||||||
|
glRenderer->winNHistory[0][y * 4 + 1] = glRenderer->winN[0].h.end;
|
||||||
|
glRenderer->winNHistory[0][y * 4 + 2] = glRenderer->winN[0].v.start;
|
||||||
|
glRenderer->winNHistory[0][y * 4 + 3] = glRenderer->winN[0].v.end;
|
||||||
|
glRenderer->winNHistory[1][y * 4 + 0] = glRenderer->winN[1].h.start;
|
||||||
|
glRenderer->winNHistory[1][y * 4 + 1] = glRenderer->winN[1].h.end;
|
||||||
|
glRenderer->winNHistory[1][y * 4 + 2] = glRenderer->winN[1].v.start;
|
||||||
|
glRenderer->winNHistory[1][y * 4 + 3] = glRenderer->winN[1].v.end;
|
||||||
|
|
||||||
|
glRenderer->bg[0].scanlineOffset[y] = glRenderer->bg[0].x;
|
||||||
|
glRenderer->bg[0].scanlineOffset[y] |= glRenderer->bg[0].y << 12;
|
||||||
|
glRenderer->bg[1].scanlineOffset[y] = glRenderer->bg[1].x;
|
||||||
|
glRenderer->bg[1].scanlineOffset[y] |= glRenderer->bg[1].y << 12;
|
||||||
|
glRenderer->bg[2].scanlineOffset[y] = glRenderer->bg[2].x;
|
||||||
|
glRenderer->bg[2].scanlineOffset[y] |= glRenderer->bg[2].y << 12;
|
||||||
|
glRenderer->bg[2].scanlineAffine[y * 4] = glRenderer->bg[2].affine.dx;
|
||||||
|
glRenderer->bg[2].scanlineAffine[y * 4 + 1] = glRenderer->bg[2].affine.dy;
|
||||||
|
glRenderer->bg[2].scanlineAffine[y * 4 + 2] = glRenderer->bg[2].affine.sx;
|
||||||
|
glRenderer->bg[2].scanlineAffine[y * 4 + 3] = glRenderer->bg[2].affine.sy;
|
||||||
|
glRenderer->bg[3].scanlineOffset[y] = glRenderer->bg[3].x;
|
||||||
|
glRenderer->bg[3].scanlineOffset[y] |= glRenderer->bg[3].y << 12;
|
||||||
|
glRenderer->bg[3].scanlineAffine[y * 4] = glRenderer->bg[3].affine.dx;
|
||||||
|
glRenderer->bg[3].scanlineAffine[y * 4 + 1] = glRenderer->bg[3].affine.dy;
|
||||||
|
glRenderer->bg[3].scanlineAffine[y * 4 + 2] = glRenderer->bg[3].affine.sx;
|
||||||
|
glRenderer->bg[3].scanlineAffine[y * 4 + 3] = glRenderer->bg[3].affine.sy;
|
||||||
|
|
||||||
if (glRenderer->paletteDirty) {
|
if (glRenderer->paletteDirty) {
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
|
for (i = 0; i < 512; ++i) {
|
||||||
#ifdef BUILD_GLES3
|
glRenderer->shadowPalette[i] = glRenderer->d.palette[i];
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, 16, 32, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, glRenderer->shadowPalette);
|
}
|
||||||
#else
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
|
|
||||||
#endif
|
|
||||||
glRenderer->paletteDirty = false;
|
glRenderer->paletteDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1303,9 +1335,6 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y == 0) {
|
if (y == 0) {
|
||||||
memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
|
|
||||||
memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
|
|
||||||
|
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
glClearColor(0, 0, 0, 0);
|
glClearColor(0, 0, 0, 0);
|
||||||
#ifdef BUILD_GLES3
|
#ifdef BUILD_GLES3
|
||||||
|
@ -1333,27 +1362,20 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) {
|
void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) {
|
||||||
if (glRenderer->firstAffine >= 0) {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[0]);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[1]);
|
|
||||||
}
|
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->d.palette[0]);
|
uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->shadowPalette[0]);
|
||||||
glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS);
|
glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS);
|
||||||
glScissor(0, glRenderer->firstY, 1, y - glRenderer->firstY + 1);
|
glScissor(0, glRenderer->firstY, 1, y - glRenderer->firstY + 1);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
|
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
|
||||||
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
|
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
|
||||||
glClearBufferfv(GL_COLOR, 0, (GLfloat[]) { ((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f });
|
glClearBufferfv(GL_COLOR, 0, (GLfloat[]) { ((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 1.f });
|
||||||
glClearBufferiv(GL_COLOR, 1, (GLint[]) { 32, glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4), glRenderer->blda, 0 });
|
glClearBufferiv(GL_COLOR, 1, (GLint[]) { 32, glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4), glRenderer->blda, 0 });
|
||||||
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
||||||
|
|
||||||
GBAVideoGLRendererDrawWindow(glRenderer, y);
|
GBAVideoGLRendererDrawWindow(glRenderer, y);
|
||||||
if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
|
if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
|
||||||
int i;
|
int i;
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glDepthFunc(GL_LESS);
|
glDepthFunc(GL_LESS);
|
||||||
for (i = 0; i < glRenderer->oamMax; ++i) {
|
for (i = 0; i < glRenderer->oamMax; ++i) {
|
||||||
struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
|
struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
|
||||||
|
@ -1592,12 +1614,10 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
|
||||||
glBindVertexArray(shader->vao);
|
glBindVertexArray(shader->vao);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
|
glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
|
||||||
glActiveTexture(GL_TEXTURE0 + 1);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
|
|
||||||
glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0);
|
glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0);
|
||||||
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight);
|
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight);
|
||||||
glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0);
|
glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0);
|
||||||
glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1);
|
glUniform1iv(uniforms[GBA_GL_OBJ_PALETTE], 256, &renderer->shadowPalette[256]);
|
||||||
glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase);
|
glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase);
|
||||||
glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride);
|
glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride);
|
||||||
glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c));
|
glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c));
|
||||||
|
@ -1625,10 +1645,12 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
|
||||||
}
|
}
|
||||||
glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight);
|
glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight);
|
||||||
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) {
|
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) {
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
int window = renderer->objwin & 0x3F;
|
int window = renderer->objwin & 0x3F;
|
||||||
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 1, window, renderer->bldb, renderer->bldy);
|
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 1, window, renderer->bldb, renderer->bldy);
|
||||||
glDrawBuffers(3, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 });
|
glDrawBuffers(3, (GLenum[]) { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 });
|
||||||
} else {
|
} else {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0);
|
glUniform4i(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0);
|
||||||
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
|
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
|
||||||
}
|
}
|
||||||
|
@ -1650,11 +1672,9 @@ void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBa
|
||||||
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
|
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
|
glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
|
||||||
glActiveTexture(GL_TEXTURE0 + 1);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
|
|
||||||
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||||
glUniform1i(uniforms[GBA_GL_BG_VRAM], 0);
|
glUniform1i(uniforms[GBA_GL_BG_VRAM], 0);
|
||||||
glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1);
|
glUniform1iv(uniforms[GBA_GL_OBJ_PALETTE], 256, renderer->shadowPalette);
|
||||||
if (background->mosaic) {
|
if (background->mosaic) {
|
||||||
glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1);
|
glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1667,19 +1687,6 @@ void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBa
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
|
void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
|
||||||
int inY0 = renderer->firstY + background->y;
|
|
||||||
int yDiv = (((y + background->y) & ~0xFF) - background->y) & 0xFF;
|
|
||||||
int inY1 = yDiv + background->y;
|
|
||||||
int yBase0 = inY0 & 0xFF;
|
|
||||||
int yBase1 = inY1 & 0xFF;
|
|
||||||
if (background->size == 2) {
|
|
||||||
yBase0 += inY0 & 0x100;
|
|
||||||
yBase1 += inY1 & 0x100;
|
|
||||||
} else if (background->size == 3) {
|
|
||||||
yBase0 += (inY0 & 0x100) << 1;
|
|
||||||
yBase1 += (inY1 & 0x100) << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0];
|
const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0];
|
||||||
const GLuint* uniforms = shader->uniforms;
|
const GLuint* uniforms = shader->uniforms;
|
||||||
glUseProgram(shader->program);
|
glUseProgram(shader->program);
|
||||||
|
@ -1688,28 +1695,11 @@ void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer,
|
||||||
glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
|
glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
|
||||||
glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
|
glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
|
||||||
glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
|
glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
|
||||||
|
glUniform1iv(uniforms[GBA_GL_BG_OFFSET], GBA_VIDEO_VERTICAL_PIXELS, background->scanlineOffset);
|
||||||
|
|
||||||
if (yDiv > renderer->firstY) {
|
glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (y - renderer->firstY + 1) * renderer->scale);
|
||||||
int end = yDiv - 1;
|
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
|
||||||
if (end > y) {
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
end = y;
|
|
||||||
}
|
|
||||||
glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (end - renderer->firstY + 1) * renderer->scale);
|
|
||||||
glUniform2i(uniforms[GBA_GL_VS_LOC], end - renderer->firstY + 1, renderer->firstY);
|
|
||||||
glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase0 - renderer->firstY);
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y >= yDiv) {
|
|
||||||
int start = yDiv;
|
|
||||||
if (yDiv < renderer->firstY) {
|
|
||||||
start = renderer->firstY;
|
|
||||||
}
|
|
||||||
glScissor(0, start * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (y - start + 1) * renderer->scale);
|
|
||||||
glUniform2i(uniforms[GBA_GL_VS_LOC], y - start + 1, start);
|
|
||||||
glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase1 - yDiv);
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
||||||
}
|
}
|
||||||
|
@ -1719,9 +1709,7 @@ void _prepareTransform(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBac
|
||||||
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
|
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
|
||||||
glUniform2i(uniforms[GBA_GL_BG_RANGE], renderer->firstAffine, y);
|
glUniform2i(uniforms[GBA_GL_BG_RANGE], renderer->firstAffine, y);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + 2);
|
glUniform4iv(uniforms[GBA_GL_BG_TRANSFORM], GBA_VIDEO_VERTICAL_PIXELS, background->scanlineAffine);
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_AFFINE_2 + background->index - 2]);
|
|
||||||
glUniform1i(uniforms[GBA_GL_BG_TRANSFORM], 2);
|
|
||||||
_prepareBackground(renderer, background, uniforms);
|
_prepareBackground(renderer, background, uniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1780,68 +1768,23 @@ void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer,
|
||||||
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _scissorWindow(struct GBAVideoGLRenderer* renderer, int window, int start, int end, int y, int lines) {
|
|
||||||
if (start > end) {
|
|
||||||
_scissorWindow(renderer, window, start, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y, lines);
|
|
||||||
_scissorWindow(renderer, window, 0, end, y, lines);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
glScissor(start, y, end - start, lines);
|
|
||||||
glClearBufferiv(GL_COLOR, 0, (GLint[]) { window, renderer->bldb, renderer->bldy, 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _scissorWindowN(struct GBAVideoGLRenderer* renderer, const struct GBAVideoGLWindowN* window, const struct GBAVideoWindowRegion* y, int dispcnt) {
|
|
||||||
int sdelta = window->h[0].start - window->h[1].start;
|
|
||||||
int edelta = window->h[0].end - window->h[1].end;
|
|
||||||
int maxDelta = 0;
|
|
||||||
if (sdelta > maxDelta) {
|
|
||||||
maxDelta = sdelta;
|
|
||||||
} else if (-sdelta > maxDelta) {
|
|
||||||
maxDelta = -sdelta;
|
|
||||||
}
|
|
||||||
if (edelta > maxDelta) {
|
|
||||||
maxDelta = edelta;
|
|
||||||
} else if (-edelta > maxDelta) {
|
|
||||||
maxDelta = -edelta;
|
|
||||||
}
|
|
||||||
int startY = y->start;
|
|
||||||
int endY = y->end;
|
|
||||||
if (startY < window->v.start) {
|
|
||||||
startY = window->v.start;
|
|
||||||
}
|
|
||||||
if (endY >= window->v.end) {
|
|
||||||
endY = window->v.end - 1;
|
|
||||||
}
|
|
||||||
if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) {
|
|
||||||
_scissorWindow(renderer, window->control & dispcnt, window->h[0].start * renderer->scale, window->h[0].end * renderer->scale, startY * renderer->scale, (endY - startY + 1) * renderer->scale);
|
|
||||||
} else {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < renderer->scale * (endY - startY + 1); ++i) {
|
|
||||||
int start = window->h[1].start * renderer->scale + sdelta * i;
|
|
||||||
int end = window->h[1].end * renderer->scale + edelta * i;
|
|
||||||
_scissorWindow(renderer, window->control & dispcnt, start, end, startY * renderer->scale + i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
|
void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
|
||||||
|
const struct GBAVideoGLShader* shader = &renderer->windowShader;
|
||||||
|
const GLuint* uniforms = shader->uniforms;
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
|
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
|
||||||
int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20;
|
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
|
||||||
if (!(renderer->dispcnt & 0xE000)) {
|
glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1));
|
||||||
_scissorWindow(renderer, dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale);
|
glUseProgram(shader->program);
|
||||||
} else {
|
glBindVertexArray(shader->vao);
|
||||||
_scissorWindow(renderer, renderer->winout & dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale);
|
glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
|
||||||
struct GBAVideoWindowRegion yRegion = {
|
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||||
y,
|
glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8);
|
||||||
renderer->firstY
|
glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy);
|
||||||
};
|
glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout);
|
||||||
if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && renderer->firstY < renderer->winN[1].v.end) {
|
glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]);
|
||||||
_scissorWindowN(renderer, &renderer->winN[1], &yRegion, dispcnt);
|
glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]);
|
||||||
}
|
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
|
||||||
if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && renderer->firstY < renderer->winN[0].v.end) {
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
_scissorWindowN(renderer, &renderer->winN[0], &yRegion, dispcnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -89,7 +89,8 @@ static color_t* screenshotBuffer = NULL;
|
||||||
static struct mAVStream stream;
|
static struct mAVStream stream;
|
||||||
static int16_t* audioLeft = 0;
|
static int16_t* audioLeft = 0;
|
||||||
static size_t audioPos = 0;
|
static size_t audioPos = 0;
|
||||||
static C3D_Tex outputTexture;
|
static C3D_Tex outputTexture[2];
|
||||||
|
static int activeOutputTexture = 0;
|
||||||
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
|
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
|
||||||
static int bufferId = 0;
|
static int bufferId = 0;
|
||||||
static bool frameLimiter = true;
|
static bool frameLimiter = true;
|
||||||
|
@ -102,6 +103,7 @@ static bool frameStarted = false;
|
||||||
|
|
||||||
static C3D_RenderTarget* upscaleBuffer;
|
static C3D_RenderTarget* upscaleBuffer;
|
||||||
static C3D_Tex upscaleBufferTex;
|
static C3D_Tex upscaleBufferTex;
|
||||||
|
static bool interframeBlending = false;
|
||||||
|
|
||||||
static aptHookCookie cookie;
|
static aptHookCookie cookie;
|
||||||
static bool core2;
|
static bool core2;
|
||||||
|
@ -150,7 +152,8 @@ static void _cleanup(void) {
|
||||||
C3D_RenderTargetDelete(bottomScreen[1]);
|
C3D_RenderTargetDelete(bottomScreen[1]);
|
||||||
C3D_RenderTargetDelete(upscaleBuffer);
|
C3D_RenderTargetDelete(upscaleBuffer);
|
||||||
C3D_TexDelete(&upscaleBufferTex);
|
C3D_TexDelete(&upscaleBufferTex);
|
||||||
C3D_TexDelete(&outputTexture);
|
C3D_TexDelete(&outputTexture[0]);
|
||||||
|
C3D_TexDelete(&outputTexture[1]);
|
||||||
C3D_Fini();
|
C3D_Fini();
|
||||||
|
|
||||||
gfxExit();
|
gfxExit();
|
||||||
|
@ -374,6 +377,11 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fakeBool;
|
||||||
|
if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
|
||||||
|
interframeBlending = fakeBool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gameUnloaded(struct mGUIRunner* runner) {
|
static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||||
|
@ -404,7 +412,7 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawTex(struct mCore* core, bool faded) {
|
static void _drawTex(struct mCore* core, bool faded, bool both) {
|
||||||
unsigned screen_w, screen_h;
|
unsigned screen_w, screen_h;
|
||||||
switch (screenMode) {
|
switch (screenMode) {
|
||||||
case SM_PA_BOTTOM:
|
case SM_PA_BOTTOM:
|
||||||
|
@ -466,7 +474,6 @@ static void _drawTex(struct mCore* core, bool faded) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrActivateTexture(&outputTexture);
|
|
||||||
u32 color;
|
u32 color;
|
||||||
if (!faded) {
|
if (!faded) {
|
||||||
color = 0xFFFFFFFF;
|
color = 0xFFFFFFFF;
|
||||||
|
@ -502,7 +509,12 @@ static void _drawTex(struct mCore* core, bool faded) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
ctrActivateTexture(&outputTexture[activeOutputTexture]);
|
||||||
ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0);
|
ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0);
|
||||||
|
if (both) {
|
||||||
|
ctrActivateTexture(&outputTexture[activeOutputTexture ^ 1]);
|
||||||
|
ctrAddRectEx(color & 0x7FFFFFFF, x, y, w, h, 0, 0, corew, coreh, 0);
|
||||||
|
}
|
||||||
ctrFlushBatch();
|
ctrFlushBatch();
|
||||||
|
|
||||||
corew = w;
|
corew = w;
|
||||||
|
@ -546,9 +558,14 @@ static void _drawTex(struct mCore* core, bool faded) {
|
||||||
ctrFlushBatch();
|
ctrFlushBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _prepareForFrame(struct mGUIRunner* runner) {
|
||||||
|
UNUSED(runner);
|
||||||
|
activeOutputTexture ^= 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
UNUSED(runner);
|
UNUSED(runner);
|
||||||
C3D_Tex* tex = &outputTexture;
|
C3D_Tex* tex = &outputTexture[activeOutputTexture];
|
||||||
|
|
||||||
GSPGPU_FlushDataCache(outputBuffer, 256 * GBA_VIDEO_VERTICAL_PIXELS * 2);
|
GSPGPU_FlushDataCache(outputBuffer, 256 * GBA_VIDEO_VERTICAL_PIXELS * 2);
|
||||||
C3D_SyncDisplayTransfer(
|
C3D_SyncDisplayTransfer(
|
||||||
|
@ -563,11 +580,11 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
blip_clear(runner->core->getAudioChannel(runner->core, 1));
|
blip_clear(runner->core->getAudioChannel(runner->core, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawTex(runner->core, faded);
|
_drawTex(runner->core, faded, interframeBlending);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
|
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
|
||||||
C3D_Tex* tex = &outputTexture;
|
C3D_Tex* tex = &outputTexture[activeOutputTexture];
|
||||||
|
|
||||||
if (!screenshotBuffer) {
|
if (!screenshotBuffer) {
|
||||||
screenshotBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
|
screenshotBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
|
||||||
|
@ -586,7 +603,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un
|
||||||
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
||||||
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
||||||
|
|
||||||
_drawTex(runner->core, faded);
|
_drawTex(runner->core, faded, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
static uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
||||||
|
@ -805,25 +822,29 @@ int main() {
|
||||||
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true);
|
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true);
|
||||||
|
|
||||||
if (!_initGpu()) {
|
if (!_initGpu()) {
|
||||||
outputTexture.data = 0;
|
outputTexture[0].data = 0;
|
||||||
_cleanup();
|
_cleanup();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!C3D_TexInitVRAM(&outputTexture, 256, 256, GPU_RGB565)) {
|
|
||||||
_cleanup();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
C3D_TexSetWrap(&outputTexture, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
|
|
||||||
C3D_TexSetFilter(&outputTexture, GPU_NEAREST, GPU_NEAREST);
|
|
||||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR);
|
C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR);
|
||||||
void* outputTextureEnd = (u8*)outputTexture.data + 256 * 256 * 2;
|
|
||||||
|
|
||||||
// Zero texture data to make sure no garbage around the border interferes with filtering
|
int i;
|
||||||
GX_MemoryFill(
|
for (i = 0; i < 2; ++i) {
|
||||||
outputTexture.data, 0x0000, outputTextureEnd, GX_FILL_16BIT_DEPTH | GX_FILL_TRIGGER,
|
if (!C3D_TexInitVRAM(&outputTexture[i], 256, 256, GPU_RGB565)) {
|
||||||
NULL, 0, NULL, 0);
|
_cleanup();
|
||||||
gspWaitForPSC0();
|
return 1;
|
||||||
|
}
|
||||||
|
C3D_TexSetWrap(&outputTexture[i], GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
|
||||||
|
C3D_TexSetFilter(&outputTexture[i], GPU_NEAREST, GPU_NEAREST);
|
||||||
|
void* outputTextureEnd = (u8*)outputTexture[i].data + 256 * 256 * 2;
|
||||||
|
|
||||||
|
// Zero texture data to make sure no garbage around the border interferes with filtering
|
||||||
|
GX_MemoryFill(
|
||||||
|
outputTexture[i].data, 0x0000, outputTextureEnd, GX_FILL_16BIT_DEPTH | GX_FILL_TRIGGER,
|
||||||
|
NULL, 0, NULL, 0);
|
||||||
|
gspWaitForPSC0();
|
||||||
|
}
|
||||||
|
|
||||||
struct GUIFont* font = GUIFontCreate();
|
struct GUIFont* font = GUIFontCreate();
|
||||||
|
|
||||||
|
@ -938,7 +959,7 @@ int main() {
|
||||||
.teardown = 0,
|
.teardown = 0,
|
||||||
.gameLoaded = _gameLoaded,
|
.gameLoaded = _gameLoaded,
|
||||||
.gameUnloaded = _gameUnloaded,
|
.gameUnloaded = _gameUnloaded,
|
||||||
.prepareForFrame = 0,
|
.prepareForFrame = _prepareForFrame,
|
||||||
.drawFrame = _drawFrame,
|
.drawFrame = _drawFrame,
|
||||||
.drawScreenshot = _drawScreenshot,
|
.drawScreenshot = _drawScreenshot,
|
||||||
.paused = _gameUnloaded,
|
.paused = _gameUnloaded,
|
||||||
|
|
|
@ -17,13 +17,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) {
|
||||||
|
@ -31,7 +38,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);
|
||||||
|
@ -56,7 +76,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) {
|
||||||
|
@ -98,7 +118,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);
|
||||||
|
@ -107,11 +141,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,8 +26,9 @@ CXX_GUARD_START
|
||||||
struct mGLContext {
|
struct mGLContext {
|
||||||
struct VideoBackend d;
|
struct VideoBackend d;
|
||||||
|
|
||||||
GLuint tex;
|
GLuint tex[2];
|
||||||
GLint vertices[8];
|
GLint vertices[8];
|
||||||
|
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;
|
||||||
|
|
|
@ -52,8 +52,10 @@ static enum ScreenMode {
|
||||||
|
|
||||||
static void* outputBuffer;
|
static void* outputBuffer;
|
||||||
static vita2d_texture* tex;
|
static vita2d_texture* tex;
|
||||||
|
static vita2d_texture* oldTex;
|
||||||
static vita2d_texture* screenshot;
|
static vita2d_texture* screenshot;
|
||||||
static Thread audioThread;
|
static Thread audioThread;
|
||||||
|
static bool interframeBlending = false;
|
||||||
|
|
||||||
static struct mSceRotationSource {
|
static struct mSceRotationSource {
|
||||||
struct mRotationSource d;
|
struct mRotationSource d;
|
||||||
|
@ -322,6 +324,7 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||||
tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||||
|
oldTex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||||
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||||
|
|
||||||
outputBuffer = anonymousMemoryMap(256 * toPow2(height) * 4);
|
outputBuffer = anonymousMemoryMap(256 * toPow2(height) * 4);
|
||||||
|
@ -390,6 +393,11 @@ void mPSP2LoadROM(struct mGUIRunner* runner) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fakeBool;
|
||||||
|
if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
|
||||||
|
interframeBlending = fakeBool;
|
||||||
|
}
|
||||||
|
|
||||||
MutexInit(&audioContext.mutex);
|
MutexInit(&audioContext.mutex);
|
||||||
ConditionInit(&audioContext.cond);
|
ConditionInit(&audioContext.cond);
|
||||||
memset(audioContext.buffer, 0, sizeof(audioContext.buffer));
|
memset(audioContext.buffer, 0, sizeof(audioContext.buffer));
|
||||||
|
@ -451,18 +459,23 @@ void mPSP2Unpaused(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fakeBool;
|
||||||
|
mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool);
|
||||||
|
interframeBlending = fakeBool;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mPSP2Teardown(struct mGUIRunner* runner) {
|
void mPSP2Teardown(struct mGUIRunner* runner) {
|
||||||
UNUSED(runner);
|
UNUSED(runner);
|
||||||
CircleBufferDeinit(&rumble.history);
|
CircleBufferDeinit(&rumble.history);
|
||||||
vita2d_free_texture(tex);
|
vita2d_free_texture(tex);
|
||||||
|
vita2d_free_texture(oldTex);
|
||||||
vita2d_free_texture(screenshot);
|
vita2d_free_texture(screenshot);
|
||||||
mappedMemoryFree(outputBuffer, 256 * 256 * 4);
|
mappedMemoryFree(outputBuffer, 256 * 256 * 4);
|
||||||
frameLimiter = true;
|
frameLimiter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded) {
|
void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded, bool interframe) {
|
||||||
unsigned w = width;
|
unsigned w = width;
|
||||||
unsigned h = height;
|
unsigned h = height;
|
||||||
// Get greatest common divisor
|
// Get greatest common divisor
|
||||||
|
@ -477,10 +490,21 @@ void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded) {
|
||||||
float scalex;
|
float scalex;
|
||||||
float scaley;
|
float scaley;
|
||||||
|
|
||||||
|
unsigned tint = 0x1FFFFFFF;
|
||||||
|
if (!faded) {
|
||||||
|
if (interframe) {
|
||||||
|
tint |= 0x60000000;
|
||||||
|
} else {
|
||||||
|
tint |= 0xE0000000;
|
||||||
|
}
|
||||||
|
} else if (!interframe) {
|
||||||
|
tint |= 0x20000000;
|
||||||
|
}
|
||||||
|
|
||||||
switch (screenMode) {
|
switch (screenMode) {
|
||||||
case SM_BACKDROP:
|
case SM_BACKDROP:
|
||||||
default:
|
default:
|
||||||
vita2d_draw_texture_tint(backdrop, 0, 0, (faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
|
vita2d_draw_texture_tint(backdrop, 0, 0, tint);
|
||||||
// Fall through
|
// Fall through
|
||||||
case SM_PLAIN:
|
case SM_PLAIN:
|
||||||
w = 960 / width;
|
w = 960 / width;
|
||||||
|
@ -520,15 +544,20 @@ void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded) {
|
||||||
(960.0f - w) / 2.0f, (544.0f - h) / 2.0f,
|
(960.0f - w) / 2.0f, (544.0f - h) / 2.0f,
|
||||||
0, 0, width, height,
|
0, 0, width, height,
|
||||||
scalex, scaley,
|
scalex, scaley,
|
||||||
(faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
|
tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
|
void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||||
void* texpixels = vita2d_texture_get_datap(tex);
|
void* texpixels = vita2d_texture_get_datap(tex);
|
||||||
|
if (interframeBlending) {
|
||||||
|
void* oldTexpixels = vita2d_texture_get_datap(oldTex);
|
||||||
|
memcpy(oldTexpixels, texpixels, 256 * height * 4);
|
||||||
|
_drawTex(oldTex, width, height, faded, false);
|
||||||
|
}
|
||||||
memcpy(texpixels, outputBuffer, 256 * height * 4);
|
memcpy(texpixels, outputBuffer, 256 * height * 4);
|
||||||
_drawTex(tex, width, height, faded);
|
_drawTex(tex, width, height, faded, interframeBlending);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsigned width, unsigned height, bool faded) {
|
void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsigned width, unsigned height, bool faded) {
|
||||||
|
@ -538,7 +567,7 @@ void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsi
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
memcpy(&texpixels[256 * y], &pixels[width * y], width * 4);
|
memcpy(&texpixels[256 * y], &pixels[width * y], width * 4);
|
||||||
}
|
}
|
||||||
_drawTex(screenshot, width, height, faded);
|
_drawTex(screenshot, width, height, faded, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mPSP2IncrementScreenMode(struct mGUIRunner* runner) {
|
void mPSP2IncrementScreenMode(struct mGUIRunner* runner) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -87,6 +87,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,17 +42,20 @@ 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;
|
||||||
virtual bool isDrawing() const = 0;
|
virtual bool isDrawing() const = 0;
|
||||||
virtual bool supportsShaders() const = 0;
|
virtual bool supportsShaders() const = 0;
|
||||||
virtual VideoShader* shaders() = 0;
|
virtual VideoShader* shaders() = 0;
|
||||||
virtual VideoProxy* videoProxy() { return nullptr; }
|
|
||||||
virtual int framebufferHandle() { return -1; }
|
virtual int framebufferHandle() { return -1; }
|
||||||
|
|
||||||
QSize viewportSize();
|
QSize viewportSize();
|
||||||
|
|
||||||
|
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = proxy; }
|
||||||
|
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void showCursor();
|
void showCursor();
|
||||||
void hideCursor();
|
void hideCursor();
|
||||||
|
@ -64,6 +67,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;
|
||||||
|
@ -87,10 +91,12 @@ 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;
|
||||||
int m_coreWidth;
|
int m_coreWidth;
|
||||||
int m_coreHeight;
|
int m_coreHeight;
|
||||||
|
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
||||||
#endif
|
#endif
|
||||||
auto version = m_gl->format().version();
|
auto version = m_gl->format().version();
|
||||||
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
|
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
|
||||||
m_gl->doneCurrent();
|
|
||||||
|
|
||||||
if ((version == qMakePair(2, 1) && !extensions.contains("GL_ARB_framebuffer_object")) || version == qMakePair(2, 0)) {
|
if ((version == qMakePair(2, 1) && !extensions.contains("GL_ARB_framebuffer_object")) || version == qMakePair(2, 0)) {
|
||||||
QSurfaceFormat newFormat(format);
|
QSurfaceFormat newFormat(format);
|
||||||
|
@ -58,7 +57,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
||||||
m_gl->create();
|
m_gl->create();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_painter = new PainterGL(&m_videoProxy, windowHandle(), m_gl);
|
m_painter = new PainterGL(windowHandle(), m_gl);
|
||||||
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +98,15 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
m_gl->doneCurrent();
|
m_gl->doneCurrent();
|
||||||
m_gl->moveToThread(m_drawThread);
|
m_gl->moveToThread(m_drawThread);
|
||||||
m_painter->moveToThread(m_drawThread);
|
m_painter->moveToThread(m_drawThread);
|
||||||
m_videoProxy.moveToThread(m_drawThread);
|
if (videoProxy()) {
|
||||||
|
videoProxy()->moveToThread(m_drawThread);
|
||||||
|
}
|
||||||
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
||||||
m_drawThread->start();
|
m_drawThread->start();
|
||||||
|
|
||||||
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());
|
||||||
|
@ -168,6 +169,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) {
|
||||||
|
@ -214,21 +222,21 @@ void DisplayGL::resizePainter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoProxy* DisplayGL::videoProxy() {
|
void DisplayGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
||||||
if (supportsShaders()) {
|
Display::setVideoProxy(proxy);
|
||||||
return &m_videoProxy;
|
if (m_drawThread && proxy) {
|
||||||
|
proxy->moveToThread(m_drawThread);
|
||||||
}
|
}
|
||||||
return nullptr;
|
m_painter->setVideoProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DisplayGL::framebufferHandle() {
|
int DisplayGL::framebufferHandle() {
|
||||||
return m_painter->glTex();
|
return m_painter->glTex();
|
||||||
}
|
}
|
||||||
|
|
||||||
PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent)
|
PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent)
|
||||||
: m_gl(parent)
|
: m_gl(parent)
|
||||||
, m_surface(surface)
|
, m_surface(surface)
|
||||||
, m_videoProxy(proxy)
|
|
||||||
{
|
{
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
mGLContext* glBackend;
|
mGLContext* glBackend;
|
||||||
|
@ -275,11 +283,11 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent
|
||||||
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
|
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
m_gl->doneCurrent();
|
|
||||||
|
|
||||||
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[256 * 512]);
|
m_free.append(new uint32_t[256 * 512]);
|
||||||
|
@ -348,6 +356,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) {
|
||||||
|
@ -418,7 +430,9 @@ void PainterGL::stop() {
|
||||||
m_gl->moveToThread(m_surface->thread());
|
m_gl->moveToThread(m_surface->thread());
|
||||||
m_context.reset();
|
m_context.reset();
|
||||||
moveToThread(m_gl->thread());
|
moveToThread(m_gl->thread());
|
||||||
m_videoProxy->moveToThread(m_gl->thread());
|
if (m_videoProxy) {
|
||||||
|
m_videoProxy->moveToThread(m_gl->thread());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::pause() {
|
void PainterGL::pause() {
|
||||||
|
@ -509,6 +523,10 @@ void PainterGL::dequeueAll() {
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PainterGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
||||||
|
m_videoProxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
void PainterGL::setShaders(struct VDir* dir) {
|
void PainterGL::setShaders(struct VDir* dir) {
|
||||||
if (!supportsShaders()) {
|
if (!supportsShaders()) {
|
||||||
return;
|
return;
|
||||||
|
@ -550,7 +568,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
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
bool isDrawing() const override { return m_isDrawing; }
|
bool isDrawing() const override { return m_isDrawing; }
|
||||||
bool supportsShaders() const override;
|
bool supportsShaders() const override;
|
||||||
VideoShader* shaders() override;
|
VideoShader* shaders() override;
|
||||||
VideoProxy* videoProxy() override;
|
void setVideoProxy(std::shared_ptr<VideoProxy>) override;
|
||||||
int framebufferHandle() override;
|
int framebufferHandle() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -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;
|
||||||
|
@ -70,14 +71,13 @@ private:
|
||||||
PainterGL* m_painter;
|
PainterGL* m_painter;
|
||||||
QThread* m_drawThread = nullptr;
|
QThread* m_drawThread = nullptr;
|
||||||
std::shared_ptr<CoreController> m_context;
|
std::shared_ptr<CoreController> m_context;
|
||||||
VideoProxy m_videoProxy;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PainterGL : public QObject {
|
class PainterGL : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent);
|
PainterGL(QWindow* surface, QOpenGLContext* parent);
|
||||||
~PainterGL();
|
~PainterGL();
|
||||||
|
|
||||||
void setContext(std::shared_ptr<CoreController>);
|
void setContext(std::shared_ptr<CoreController>);
|
||||||
|
@ -86,6 +86,8 @@ public:
|
||||||
|
|
||||||
bool supportsShaders() const { return m_supportsShaders; }
|
bool supportsShaders() const { return m_supportsShaders; }
|
||||||
|
|
||||||
|
void setVideoProxy(std::shared_ptr<VideoProxy>);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void forceDraw();
|
void forceDraw();
|
||||||
void draw();
|
void draw();
|
||||||
|
@ -96,6 +98,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();
|
||||||
|
|
||||||
|
@ -131,7 +134,7 @@ private:
|
||||||
QTimer m_swapTimer{this};
|
QTimer m_swapTimer{this};
|
||||||
bool m_needsUnlock = false;
|
bool m_needsUnlock = false;
|
||||||
bool m_frameReady = false;
|
bool m_frameReady = false;
|
||||||
VideoProxy* m_videoProxy;
|
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ void DisplayQt::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
m_height = size.height();
|
m_height = size.height();
|
||||||
setSystemDimensions(m_width, m_height);
|
setSystemDimensions(m_width, m_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;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +46,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();
|
||||||
|
@ -56,6 +62,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);
|
||||||
|
@ -66,6 +73,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() {
|
||||||
|
@ -76,6 +86,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,10 +102,11 @@ void DisplayQt::paintEvent(QPaintEvent*) {
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -371,6 +371,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);
|
||||||
|
@ -407,7 +408,7 @@ void SettingsView::updateConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
double nativeFps = double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH);
|
double nativeFps = double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH);
|
||||||
if (nativeFps - m_ui.fpsTarget->value() < 0.0001) {
|
if (fabs(nativeFps - m_ui.fpsTarget->value()) < 0.0001) {
|
||||||
m_controller->setOption("fpsTarget", QVariant(nativeFps));
|
m_controller->setOption("fpsTarget", QVariant(nativeFps));
|
||||||
} else {
|
} else {
|
||||||
saveSetting("fpsTarget", m_ui.fpsTarget);
|
saveSetting("fpsTarget", m_ui.fpsTarget);
|
||||||
|
@ -533,6 +534,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">
|
||||||
|
|
|
@ -62,7 +62,6 @@ bool VideoProxy::writeData(const void* data, size_t length) {
|
||||||
m_fromThreadCond.wait(&m_mutex);
|
m_fromThreadCond.wait(&m_mutex);
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
}
|
}
|
||||||
emit dataAvailable();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +108,7 @@ void VideoProxy::wait() {
|
||||||
|
|
||||||
void VideoProxy::wake(int y) {
|
void VideoProxy::wake(int y) {
|
||||||
if ((y & 15) == 15) {
|
if ((y & 15) == 15) {
|
||||||
|
emit dataAvailable();
|
||||||
m_toThreadCond.wakeAll();
|
m_toThreadCond.wakeAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -783,6 +783,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);
|
||||||
}
|
}
|
||||||
|
@ -837,19 +838,12 @@ void Window::gameStarted() {
|
||||||
}
|
}
|
||||||
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_DISCORD_RPC
|
#ifdef USE_DISCORD_RPC
|
||||||
DiscordCoordinator::gameStarted(m_controller);
|
DiscordCoordinator::gameStarted(m_controller);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::gameStopped() {
|
void Window::gameStopped() {
|
||||||
m_controller.reset();
|
|
||||||
m_display->stopDrawing();
|
|
||||||
if (m_pendingClose) {
|
|
||||||
m_display.reset();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
for (Action* action : m_platformActions) {
|
for (Action* action : m_platformActions) {
|
||||||
action->setEnabled(true);
|
action->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
@ -884,6 +878,10 @@ void Window::gameStopped() {
|
||||||
m_audioProcessor.reset();
|
m_audioProcessor.reset();
|
||||||
}
|
}
|
||||||
m_display->stopDrawing();
|
m_display->stopDrawing();
|
||||||
|
|
||||||
|
m_controller.reset();
|
||||||
|
|
||||||
|
m_display->setVideoProxy({});
|
||||||
if (m_pendingClose) {
|
if (m_pendingClose) {
|
||||||
m_display.reset();
|
m_display.reset();
|
||||||
close();
|
close();
|
||||||
|
@ -952,6 +950,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) {
|
||||||
|
@ -1433,6 +1432,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) {
|
||||||
|
@ -1457,6 +1465,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
ConfigOption* mute = m_config->addOption("mute");
|
ConfigOption* mute = m_config->addOption("mute");
|
||||||
mute->addBoolean(tr("Mute"), &m_actions, "av");
|
mute->addBoolean(tr("Mute"), &m_actions, "av");
|
||||||
mute->connect([this](const QVariant& value) {
|
mute->connect([this](const QVariant& value) {
|
||||||
|
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
}, this);
|
}, this);
|
||||||
m_config->updateOption("mute");
|
m_config->updateOption("mute");
|
||||||
|
@ -1769,9 +1778,9 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && controller->supportsFeature(CoreController::Feature::OPENGL)) {
|
if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && controller->supportsFeature(CoreController::Feature::OPENGL)) {
|
||||||
if (m_display->videoProxy()) {
|
std::shared_ptr<VideoProxy> proxy = std::make_shared<VideoProxy>();
|
||||||
m_display->videoProxy()->attach(controller);
|
m_display->setVideoProxy(proxy);
|
||||||
}
|
proxy->attach(controller);
|
||||||
|
|
||||||
int fb = m_display->framebufferHandle();
|
int fb = m_display->framebufferHandle();
|
||||||
if (fb >= 0) {
|
if (fb >= 0) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
|
|
@ -114,7 +114,7 @@ struct GUIFont* GUIFontCreate(void) {
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
if (!_loadTexture("romfs:/font-new.png")) {
|
if (!_loadTexture("romfs:/font-new.png")) {
|
||||||
GUIFontDestroy(font);
|
GUIFontDestroy(font);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <GLES3/gl3.h>
|
#include <GLES3/gl3.h>
|
||||||
|
#include <GLES3/gl31.h>
|
||||||
|
|
||||||
#define AUTO_INPUT 0x4E585031
|
#define AUTO_INPUT 0x4E585031
|
||||||
#define SAMPLES 0x400
|
#define SAMPLES 0x400
|
||||||
|
@ -46,7 +47,7 @@ static const char* const _vertexShader =
|
||||||
"varying vec2 texCoord;\n"
|
"varying vec2 texCoord;\n"
|
||||||
|
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" vec2 ratio = insize / 256.0;\n"
|
" vec2 ratio = insize;\n"
|
||||||
" vec2 scaledOffset = offset * dims;\n"
|
" vec2 scaledOffset = offset * dims;\n"
|
||||||
" gl_Position = vec4(scaledOffset.x * 2.0 - dims.x, scaledOffset.y * -2.0 + dims.y, 0.0, 1.0);\n"
|
" gl_Position = vec4(scaledOffset.x * 2.0 - dims.x, scaledOffset.y * -2.0 + dims.y, 0.0, 1.0);\n"
|
||||||
" texCoord = offset * ratio;\n"
|
" texCoord = offset * ratio;\n"
|
||||||
|
@ -67,11 +68,13 @@ static GLuint program;
|
||||||
static GLuint vbo;
|
static GLuint vbo;
|
||||||
static GLuint vao;
|
static GLuint vao;
|
||||||
static GLuint pbo;
|
static GLuint pbo;
|
||||||
|
static GLuint copyFbo;
|
||||||
static GLuint texLocation;
|
static GLuint texLocation;
|
||||||
static GLuint dimsLocation;
|
static GLuint dimsLocation;
|
||||||
static GLuint insizeLocation;
|
static GLuint insizeLocation;
|
||||||
static GLuint colorLocation;
|
static GLuint colorLocation;
|
||||||
static GLuint tex;
|
static GLuint tex;
|
||||||
|
static GLuint oldTex;
|
||||||
|
|
||||||
static color_t* frameBuffer;
|
static color_t* frameBuffer;
|
||||||
static struct mAVStream stream;
|
static struct mAVStream stream;
|
||||||
|
@ -91,6 +94,11 @@ static unsigned framecount = 0;
|
||||||
static unsigned framecap = 10;
|
static unsigned framecap = 10;
|
||||||
static u32 vibrationDeviceHandles[4];
|
static u32 vibrationDeviceHandles[4];
|
||||||
static HidVibrationValue vibrationStop = { .freq_low = 160.f, .freq_high = 320.f };
|
static HidVibrationValue vibrationStop = { .freq_low = 160.f, .freq_high = 320.f };
|
||||||
|
static bool usePbo = true;
|
||||||
|
static u8 vmode;
|
||||||
|
static u32 vwidth;
|
||||||
|
static u32 vheight;
|
||||||
|
static bool interframeBlending = false;
|
||||||
|
|
||||||
static enum ScreenMode {
|
static enum ScreenMode {
|
||||||
SM_PA,
|
SM_PA,
|
||||||
|
@ -110,9 +118,9 @@ static bool initEgl() {
|
||||||
EGLConfig config;
|
EGLConfig config;
|
||||||
EGLint numConfigs;
|
EGLint numConfigs;
|
||||||
static const EGLint attributeList[] = {
|
static const EGLint attributeList[] = {
|
||||||
EGL_RED_SIZE, 1,
|
EGL_RED_SIZE, 8,
|
||||||
EGL_GREEN_SIZE, 1,
|
EGL_GREEN_SIZE, 8,
|
||||||
EGL_BLUE_SIZE, 1,
|
EGL_BLUE_SIZE, 8,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs);
|
eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs);
|
||||||
|
@ -126,7 +134,8 @@ static bool initEgl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLint contextAttributeList[] = {
|
EGLint contextAttributeList[] = {
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 3,
|
EGL_CONTEXT_MAJOR_VERSION, 3,
|
||||||
|
EGL_CONTEXT_MINOR_VERSION, 1,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
|
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
|
||||||
|
@ -165,6 +174,7 @@ static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawStart(void) {
|
static void _drawStart(void) {
|
||||||
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +252,18 @@ static void _setup(struct mGUIRunner* runner) {
|
||||||
_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_L, GBA_KEY_L);
|
_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_L, GBA_KEY_L);
|
||||||
_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R);
|
_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R);
|
||||||
|
|
||||||
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
|
int fakeBool;
|
||||||
|
if (mCoreConfigGetIntValue(&runner->config, "hwaccelVideo", &fakeBool) && fakeBool && runner->core->supportsFeature(runner->core, mCORE_FEATURE_OPENGL)) {
|
||||||
|
runner->core->setVideoGLTex(runner->core, tex);
|
||||||
|
usePbo = false;
|
||||||
|
} else {
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
|
||||||
|
usePbo = true;
|
||||||
|
}
|
||||||
|
|
||||||
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
|
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
|
||||||
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
|
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
|
||||||
runner->core->setAVStream(runner->core, &stream);
|
runner->core->setAVStream(runner->core, &stream);
|
||||||
|
@ -267,6 +288,11 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
screenMode = mode;
|
screenMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fakeBool;
|
||||||
|
if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
|
||||||
|
interframeBlending = fakeBool;
|
||||||
|
}
|
||||||
|
|
||||||
rumble.up = 0;
|
rumble.up = 0;
|
||||||
rumble.down = 0;
|
rumble.down = 0;
|
||||||
}
|
}
|
||||||
|
@ -280,14 +306,15 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||||
hidSendVibrationValues(vibrationDeviceHandles, values, 4);
|
hidSendVibrationValues(vibrationDeviceHandles, values, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) {
|
static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded, bool blendTop) {
|
||||||
|
glViewport(0, 1080 - vheight, vwidth, vheight);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
float aspectX = width / (float) runner->params.width;
|
float aspectX = width / (float) vwidth;
|
||||||
float aspectY = height / (float) runner->params.height;
|
float aspectY = height / (float) vheight;
|
||||||
float max = 1.f;
|
float max = 1.f;
|
||||||
switch (screenMode) {
|
switch (screenMode) {
|
||||||
case SM_PA:
|
case SM_PA:
|
||||||
|
@ -296,7 +323,10 @@ static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height,
|
||||||
} else {
|
} else {
|
||||||
max = floor(1.0 / aspectY);
|
max = floor(1.0 / aspectY);
|
||||||
}
|
}
|
||||||
break;
|
if (max >= 1.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Fall through
|
||||||
case SM_AF:
|
case SM_AF:
|
||||||
if (aspectX > aspectY) {
|
if (aspectX > aspectY) {
|
||||||
max = 1.0 / aspectX;
|
max = 1.0 / aspectX;
|
||||||
|
@ -315,26 +345,48 @@ static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height,
|
||||||
|
|
||||||
glUniform1i(texLocation, 0);
|
glUniform1i(texLocation, 0);
|
||||||
glUniform2f(dimsLocation, aspectX, aspectY);
|
glUniform2f(dimsLocation, aspectX, aspectY);
|
||||||
glUniform2f(insizeLocation, width, height);
|
if (usePbo) {
|
||||||
if (!faded) {
|
glUniform2f(insizeLocation, width / 256.f, height / 256.f);
|
||||||
glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
} else {
|
} else {
|
||||||
glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f);
|
glUniform2f(insizeLocation, 1, 1);
|
||||||
|
}
|
||||||
|
if (!faded) {
|
||||||
|
glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, blendTop ? 0.5f : 1.0f);
|
||||||
|
} else {
|
||||||
|
glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, blendTop ? 0.4f : 0.8f);
|
||||||
}
|
}
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glViewport(0, 1080 - runner->params.height, runner->params.width, runner->params.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _prepareForFrame(struct mGUIRunner* runner) {
|
static void _prepareForFrame(struct mGUIRunner* runner) {
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
if (interframeBlending) {
|
||||||
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, copyFbo);
|
||||||
if (frameBuffer) {
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
|
int width, height;
|
||||||
|
int format;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, oldTex);
|
||||||
|
glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, width, height, 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usePbo) {
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||||
|
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
|
||||||
|
if (frameBuffer) {
|
||||||
|
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
|
||||||
|
}
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
}
|
}
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
|
@ -346,15 +398,27 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
|
||||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
if (usePbo) {
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
} else if (!interframeBlending) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interframeBlending) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, oldTex);
|
||||||
|
_drawTex(runner, width, height, faded, false);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
_drawTex(runner, width, height, faded, true);
|
||||||
|
} else {
|
||||||
|
_drawTex(runner, width, height, faded, false);
|
||||||
|
}
|
||||||
|
|
||||||
_drawTex(runner, width, height, faded);
|
|
||||||
|
|
||||||
HidVibrationValue values[4];
|
HidVibrationValue values[4];
|
||||||
if (rumble.up) {
|
if (rumble.up) {
|
||||||
|
@ -380,7 +444,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||||
|
|
||||||
_drawTex(runner, width, height, faded);
|
_drawTex(runner, width, height, faded, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
static uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
||||||
|
@ -409,6 +473,19 @@ static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
||||||
|
|
||||||
static bool _running(struct mGUIRunner* runner) {
|
static bool _running(struct mGUIRunner* runner) {
|
||||||
UNUSED(runner);
|
UNUSED(runner);
|
||||||
|
u8 newMode = appletGetOperationMode();
|
||||||
|
if (newMode != vmode) {
|
||||||
|
if (newMode == AppletOperationMode_Docked) {
|
||||||
|
vwidth = 1920;
|
||||||
|
vheight = 1080;
|
||||||
|
} else {
|
||||||
|
vwidth = 1280;
|
||||||
|
vheight = 720;
|
||||||
|
}
|
||||||
|
nwindowSetCrop(nwindowGetDefault(), 0, 0, vwidth, vheight);
|
||||||
|
vmode = newMode;
|
||||||
|
}
|
||||||
|
|
||||||
return appletMainLoop();
|
return appletMainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +558,14 @@ static int _batteryState(void) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _guiPrepare(void) {
|
||||||
|
glViewport(0, 1080 - vheight, vwidth, vheight);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
NWindow* window = nwindowGetDefault();
|
||||||
|
nwindowSetDimensions(window, 1920, 1080);
|
||||||
|
|
||||||
socketInitializeDefault();
|
socketInitializeDefault();
|
||||||
nxlinkStdio();
|
nxlinkStdio();
|
||||||
initEgl();
|
initEgl();
|
||||||
|
@ -491,20 +575,33 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
struct GUIFont* font = GUIFontCreate();
|
struct GUIFont* font = GUIFontCreate();
|
||||||
|
|
||||||
u32 width = 1280;
|
vmode = appletGetOperationMode();
|
||||||
u32 height = 720;
|
if (vmode == AppletOperationMode_Docked) {
|
||||||
|
vwidth = 1920;
|
||||||
|
vheight = 1080;
|
||||||
|
} else {
|
||||||
|
vwidth = 1280;
|
||||||
|
vheight = 720;
|
||||||
|
}
|
||||||
|
nwindowSetCrop(window, 0, 0, vwidth, vheight);
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 1080 - vheight, vwidth, vheight);
|
||||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||||
|
|
||||||
glGenTextures(1, &tex);
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glGenTextures(1, &tex);
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
||||||
|
glGenTextures(1, &oldTex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, oldTex);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
glGenBuffers(1, &pbo);
|
glGenBuffers(1, &pbo);
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||||
|
@ -512,6 +609,15 @@ int main(int argc, char* argv[]) {
|
||||||
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
|
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
|
||||||
|
glGenFramebuffers(1, ©Fbo);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, oldTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, copyFbo);
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
program = glCreateProgram();
|
program = glCreateProgram();
|
||||||
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
@ -603,12 +709,12 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
struct mGUIRunner runner = {
|
struct mGUIRunner runner = {
|
||||||
.params = {
|
.params = {
|
||||||
width, height,
|
1280, 720,
|
||||||
font, "/",
|
font, "/",
|
||||||
_drawStart, _drawEnd,
|
_drawStart, _drawEnd,
|
||||||
_pollInput, _pollCursor,
|
_pollInput, _pollCursor,
|
||||||
_batteryState,
|
_batteryState,
|
||||||
NULL, NULL,
|
_guiPrepare, NULL,
|
||||||
},
|
},
|
||||||
.keySources = (struct GUIInputKeys[]) {
|
.keySources = (struct GUIInputKeys[]) {
|
||||||
{
|
{
|
||||||
|
@ -689,8 +795,42 @@ int main(int argc, char* argv[]) {
|
||||||
},
|
},
|
||||||
.nStates = 16
|
.nStates = 16
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.title = "GPU-accelerated renderer (experimental, requires game reload)",
|
||||||
|
.data = "hwaccelVideo",
|
||||||
|
.submenu = 0,
|
||||||
|
.state = 0,
|
||||||
|
.validStates = (const char*[]) {
|
||||||
|
"Off",
|
||||||
|
"On",
|
||||||
|
},
|
||||||
|
.nStates = 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Hi-res scaling (requires GPU rendering)",
|
||||||
|
.data = "videoScale",
|
||||||
|
.submenu = 0,
|
||||||
|
.state = 0,
|
||||||
|
.validStates = (const char*[]) {
|
||||||
|
"1x",
|
||||||
|
"2x",
|
||||||
|
"3x",
|
||||||
|
"4x",
|
||||||
|
"5x",
|
||||||
|
"6x",
|
||||||
|
},
|
||||||
|
.stateMappings = (const struct GUIVariant[]) {
|
||||||
|
GUI_V_U(1),
|
||||||
|
GUI_V_U(2),
|
||||||
|
GUI_V_U(3),
|
||||||
|
GUI_V_U(4),
|
||||||
|
GUI_V_U(5),
|
||||||
|
GUI_V_U(6),
|
||||||
|
},
|
||||||
|
.nStates = 6
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.nConfigExtra = 2,
|
.nConfigExtra = 4,
|
||||||
.setup = _setup,
|
.setup = _setup,
|
||||||
.teardown = NULL,
|
.teardown = NULL,
|
||||||
.gameLoaded = _gameLoaded,
|
.gameLoaded = _gameLoaded,
|
||||||
|
@ -737,7 +877,9 @@ int main(int argc, char* argv[]) {
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
glDeleteBuffers(1, &pbo);
|
glDeleteBuffers(1, &pbo);
|
||||||
|
|
||||||
|
glDeleteFramebuffers(1, ©Fbo);
|
||||||
glDeleteTextures(1, &tex);
|
glDeleteTextures(1, &tex);
|
||||||
|
glDeleteTextures(1, &oldTex);
|
||||||
glDeleteBuffers(1, &vbo);
|
glDeleteBuffers(1, &vbo);
|
||||||
glDeleteProgram(program);
|
glDeleteProgram(program);
|
||||||
glDeleteVertexArrays(1, &vao);
|
glDeleteVertexArrays(1, &vao);
|
||||||
|
@ -749,6 +891,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
psmExit();
|
psmExit();
|
||||||
audoutExit();
|
audoutExit();
|
||||||
|
romfsExit();
|
||||||
deinitEgl();
|
deinitEgl();
|
||||||
socketExit();
|
socketExit();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct VideoBackend {
|
||||||
bool filter;
|
bool filter;
|
||||||
bool lockAspectRatio;
|
bool lockAspectRatio;
|
||||||
bool lockIntegerScaling;
|
bool lockIntegerScaling;
|
||||||
|
bool interframeBlending;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoShader {
|
struct VideoShader {
|
||||||
|
|
|
@ -87,6 +87,7 @@ static void _setup(struct mGUIRunner* runner);
|
||||||
static void _gameLoaded(struct mGUIRunner* runner);
|
static void _gameLoaded(struct mGUIRunner* runner);
|
||||||
static void _gameUnloaded(struct mGUIRunner* runner);
|
static void _gameUnloaded(struct mGUIRunner* runner);
|
||||||
static void _unpaused(struct mGUIRunner* runner);
|
static void _unpaused(struct mGUIRunner* runner);
|
||||||
|
static void _prepareForFrame(struct mGUIRunner* runner);
|
||||||
static void _drawFrame(struct mGUIRunner* runner, bool faded);
|
static void _drawFrame(struct mGUIRunner* runner, bool faded);
|
||||||
static uint16_t _pollGameInput(struct mGUIRunner* runner);
|
static uint16_t _pollGameInput(struct mGUIRunner* runner);
|
||||||
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
|
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
|
||||||
|
@ -110,6 +111,8 @@ static uint16_t* texmem;
|
||||||
static GXTexObj tex;
|
static GXTexObj tex;
|
||||||
static uint16_t* rescaleTexmem;
|
static uint16_t* rescaleTexmem;
|
||||||
static GXTexObj rescaleTex;
|
static GXTexObj rescaleTex;
|
||||||
|
static uint16_t* interframeTexmem;
|
||||||
|
static GXTexObj interframeTex;
|
||||||
static int32_t tiltX;
|
static int32_t tiltX;
|
||||||
static int32_t tiltY;
|
static int32_t tiltY;
|
||||||
static int32_t gyroZ;
|
static int32_t gyroZ;
|
||||||
|
@ -118,6 +121,7 @@ static uint32_t referenceRetraceCount;
|
||||||
static bool frameLimiter = true;
|
static bool frameLimiter = true;
|
||||||
static int scaleFactor;
|
static int scaleFactor;
|
||||||
static unsigned corew, coreh;
|
static unsigned corew, coreh;
|
||||||
|
static bool interframeBlending = true;
|
||||||
|
|
||||||
uint32_t* romBuffer;
|
uint32_t* romBuffer;
|
||||||
size_t romBufferSize;
|
size_t romBufferSize;
|
||||||
|
@ -277,10 +281,16 @@ int main(int argc, char* argv[]) {
|
||||||
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
|
||||||
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
||||||
|
|
||||||
|
GX_SetNumTevStages(1);
|
||||||
GX_SetNumChans(1);
|
GX_SetNumChans(1);
|
||||||
GX_SetNumTexGens(1);
|
GX_SetNumTexGens(1);
|
||||||
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
||||||
|
GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0);
|
||||||
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
||||||
|
GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_DIVIDE_2, GX_TRUE, GX_TEVPREV);
|
||||||
|
GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
|
||||||
|
GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV);
|
||||||
|
GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
|
||||||
|
|
||||||
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
||||||
GX_InvVtxCache();
|
GX_InvVtxCache();
|
||||||
|
@ -298,6 +308,8 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
||||||
|
interframeTexmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
|
GX_InitTexObj(&interframeTex, interframeTexmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
||||||
rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
|
rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
|
||||||
GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
||||||
GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
|
GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
|
||||||
|
@ -479,12 +491,12 @@ int main(int argc, char* argv[]) {
|
||||||
.submenu = 0,
|
.submenu = 0,
|
||||||
.state = 7,
|
.state = 7,
|
||||||
.validStates = (const char*[]) {
|
.validStates = (const char*[]) {
|
||||||
"1/2x", "0.6x", "1/3x", "0.7x", "1/4x", "0.8x", "0.9x", "1.0x"
|
"1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
|
||||||
},
|
},
|
||||||
.stateMappings = (const struct GUIVariant[]) {
|
.stateMappings = (const struct GUIVariant[]) {
|
||||||
GUI_V_F(0.5f),
|
GUI_V_F(0.5f),
|
||||||
GUI_V_F(0.6f),
|
GUI_V_F(0.6f),
|
||||||
GUI_V_F(1.f / 3.f),
|
GUI_V_F(2.f / 3.f),
|
||||||
GUI_V_F(0.7f),
|
GUI_V_F(0.7f),
|
||||||
GUI_V_F(0.75f),
|
GUI_V_F(0.75f),
|
||||||
GUI_V_F(0.8f),
|
GUI_V_F(0.8f),
|
||||||
|
@ -499,12 +511,12 @@ int main(int argc, char* argv[]) {
|
||||||
.submenu = 0,
|
.submenu = 0,
|
||||||
.state = 6,
|
.state = 6,
|
||||||
.validStates = (const char*[]) {
|
.validStates = (const char*[]) {
|
||||||
"1/2x", "0.6x", "1/3x", "0.7x", "1/4x", "0.8x", "0.9x", "1.0x"
|
"1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
|
||||||
},
|
},
|
||||||
.stateMappings = (const struct GUIVariant[]) {
|
.stateMappings = (const struct GUIVariant[]) {
|
||||||
GUI_V_F(0.5f),
|
GUI_V_F(0.5f),
|
||||||
GUI_V_F(0.6f),
|
GUI_V_F(0.6f),
|
||||||
GUI_V_F(1.f / 3.f),
|
GUI_V_F(2.f / 3.f),
|
||||||
GUI_V_F(0.7f),
|
GUI_V_F(0.7f),
|
||||||
GUI_V_F(0.75f),
|
GUI_V_F(0.75f),
|
||||||
GUI_V_F(0.8f),
|
GUI_V_F(0.8f),
|
||||||
|
@ -519,7 +531,7 @@ int main(int argc, char* argv[]) {
|
||||||
.teardown = 0,
|
.teardown = 0,
|
||||||
.gameLoaded = _gameLoaded,
|
.gameLoaded = _gameLoaded,
|
||||||
.gameUnloaded = _gameUnloaded,
|
.gameUnloaded = _gameUnloaded,
|
||||||
.prepareForFrame = 0,
|
.prepareForFrame = _prepareForFrame,
|
||||||
.drawFrame = _drawFrame,
|
.drawFrame = _drawFrame,
|
||||||
.paused = _gameUnloaded,
|
.paused = _gameUnloaded,
|
||||||
.unpaused = _unpaused,
|
.unpaused = _unpaused,
|
||||||
|
@ -584,6 +596,7 @@ int main(int argc, char* argv[]) {
|
||||||
free(fifo);
|
free(fifo);
|
||||||
free(texmem);
|
free(texmem);
|
||||||
free(rescaleTexmem);
|
free(rescaleTexmem);
|
||||||
|
free(interframeTexmem);
|
||||||
|
|
||||||
free(outputBuffer);
|
free(outputBuffer);
|
||||||
GUIFontDestroy(font);
|
GUIFontDestroy(font);
|
||||||
|
@ -728,6 +741,7 @@ void _reproj2(int w, int h) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _guiPrepare(void) {
|
void _guiPrepare(void) {
|
||||||
|
GX_SetNumTevStages(1);
|
||||||
_reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
|
_reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,6 +825,7 @@ void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
|
memset(interframeTexmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
_unpaused(runner);
|
_unpaused(runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,6 +858,11 @@ void _unpaused(struct mGUIRunner* runner) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int fakeBool;
|
||||||
|
if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
|
||||||
|
interframeBlending = fakeBool;
|
||||||
|
}
|
||||||
|
|
||||||
float stretch;
|
float stretch;
|
||||||
if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
|
if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
|
||||||
wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
||||||
|
@ -852,6 +872,12 @@ void _unpaused(struct mGUIRunner* runner) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _prepareForFrame(struct mGUIRunner* runner) {
|
||||||
|
if (interframeBlending) {
|
||||||
|
memcpy(interframeTexmem, texmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
|
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
|
||||||
uint32_t color = 0xFFFFFF3F;
|
uint32_t color = 0xFFFFFF3F;
|
||||||
|
@ -870,14 +896,24 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
|
if (interframeBlending) {
|
||||||
|
DCFlushRange(interframeTexmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
|
|
||||||
if (faded) {
|
if (faded || interframeBlending) {
|
||||||
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
|
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
|
||||||
} else {
|
} else {
|
||||||
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
|
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
|
||||||
}
|
}
|
||||||
GX_InvalidateTexAll();
|
GX_InvalidateTexAll();
|
||||||
GX_LoadTexObj(&tex, GX_TEXMAP0);
|
if (interframeBlending) {
|
||||||
|
GX_LoadTexObj(&interframeTex, GX_TEXMAP0);
|
||||||
|
GX_LoadTexObj(&tex, GX_TEXMAP1);
|
||||||
|
GX_SetNumTevStages(2);
|
||||||
|
} else {
|
||||||
|
GX_LoadTexObj(&tex, GX_TEXMAP0);
|
||||||
|
GX_SetNumTevStages(1);
|
||||||
|
}
|
||||||
|
|
||||||
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
||||||
s16 vertWidth = corew;
|
s16 vertWidth = corew;
|
||||||
|
@ -910,6 +946,10 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
|
GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
|
||||||
GX_CopyTex(rescaleTexmem, GX_TRUE);
|
GX_CopyTex(rescaleTexmem, GX_TRUE);
|
||||||
GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
|
GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
|
||||||
|
GX_SetNumTevStages(1);
|
||||||
|
if (!faded) {
|
||||||
|
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int hfactor = (vmode->fbWidth * wStretch) / (corew * wAdjust);
|
int hfactor = (vmode->fbWidth * wStretch) / (corew * wAdjust);
|
||||||
|
|
Loading…
Reference in New Issue