Merge branch 'feature/canvas'

This commit is contained in:
Vicki Pfau 2023-03-19 02:25:31 -07:00
commit 96d0193136
47 changed files with 1197 additions and 283 deletions

View File

@ -740,6 +740,11 @@ elseif(BUILD_GLES2)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2")
endif()
if(USE_EPOXY OR BUILD_GL OR BUILD_GLES2)
# This file should probably go somewhere else
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/video-backend.c)
endif()
if(WIN32 AND NOT (LIBMGBA_ONLY OR SKIP_LIBRARY OR USE_EPOXY))
message(FATAL_ERROR "Windows requires epoxy module!")
endif()

View File

@ -0,0 +1,30 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GEOMETRY_H
#define GEOMETRY_H
#include <mgba-util/common.h>
CXX_GUARD_START
struct Size {
int width;
int height;
};
struct Rectangle {
int x;
int y;
int width;
int height;
};
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add);
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect);
CXX_GUARD_END
#endif

View File

@ -66,7 +66,11 @@ struct mCore {
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
void (*reloadConfigOption)(struct mCore*, const char* option, const struct mCoreConfig*);
void (*desiredVideoDimensions)(const struct mCore*, unsigned* width, unsigned* height);
void (*baseVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
void (*currentVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
unsigned (*videoScale)(const struct mCore*);
size_t (*screenRegions)(const struct mCore*, const struct mCoreScreenRegion**);
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
void (*setVideoGLTex)(struct mCore*, unsigned texid);

View File

@ -293,6 +293,15 @@ struct mCoreMemoryBlock {
uint32_t segmentStart;
};
struct mCoreScreenRegion {
size_t id;
const char* description;
int16_t x;
int16_t y;
int16_t w;
int16_t h;
};
enum mCoreRegisterType {
mCORE_REGISTER_GPR = 0,
mCORE_REGISTER_FPR,

View File

@ -22,6 +22,9 @@ enum {
GB_VIDEO_VBLANK_PIXELS = 10,
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
SGB_VIDEO_HORIZONTAL_PIXELS = 256,
SGB_VIDEO_VERTICAL_PIXELS = 224,
// TODO: Figure out exact lengths
GB_VIDEO_MODE_2_LENGTH = 80,
GB_VIDEO_MODE_3_LENGTH_BASE = 172,

View File

@ -361,7 +361,7 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
size_t stride;
const void* pixels = 0;
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
core->getPixels(core, &pixels, &stride);
png_structp png = PNGWriteOpen(vf);
png_infop info = PNGWriteHeader(png, width, height);

View File

@ -176,7 +176,7 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
mappedMemoryFree(state, stateSize);
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
png_structp png = PNGWriteOpen(vf);
png_infop info = PNGWriteHeader(png, width, height);
if (!png || !info) {
@ -529,7 +529,7 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
mappedMemoryFree(state, core->stateSize(core));
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
struct mStateExtdataItem item;
if (flags & SAVESTATE_SCREENSHOT && mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) {

View File

@ -60,6 +60,15 @@ static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
};
static const struct mCoreScreenRegion _GBScreenRegions[] = {
{ 0, "Screen", 0, 0, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS }
};
static const struct mCoreScreenRegion _SGBScreenRegions[] = {
{ 0, "Screen", (SGB_VIDEO_HORIZONTAL_PIXELS - GB_VIDEO_HORIZONTAL_PIXELS) / 2, (SGB_VIDEO_VERTICAL_PIXELS - GB_VIDEO_VERTICAL_PIXELS) / 2, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS },
{ 1, "Border", 0, 0, SGB_VIDEO_HORIZONTAL_PIXELS, SGB_VIDEO_VERTICAL_PIXELS },
};
static const struct mCoreRegisterInfo _GBRegisters[] = {
{ "b", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
{ "c", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
@ -355,14 +364,36 @@ static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, co
}
}
static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
static void _GBCoreBaseVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core);
*width = SGB_VIDEO_HORIZONTAL_PIXELS;
*height = SGB_VIDEO_VERTICAL_PIXELS;
}
static void _GBCoreCurrentVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
const struct GB* gb = core->board;
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
*width = GB_VIDEO_HORIZONTAL_PIXELS;
*height = GB_VIDEO_VERTICAL_PIXELS;
} else {
*width = 256;
*height = 224;
*width = SGB_VIDEO_HORIZONTAL_PIXELS;
*height = SGB_VIDEO_VERTICAL_PIXELS;
}
}
static unsigned _GBCoreVideoScale(const struct mCore* core) {
UNUSED(core);
return 1;
}
static size_t _GBCoreScreenRegions(const struct mCore* core, const struct mCoreScreenRegion** regions) {
const struct GB* gb = core->board;
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
*regions = _GBScreenRegions;
return 1;
} else {
*regions = _SGBScreenRegions;
return 2;
}
}
@ -424,7 +455,7 @@ static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
gb->stream = stream;
if (stream && stream->videoDimensionsChanged) {
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
stream->videoDimensionsChanged(stream, width, height);
}
if (stream && stream->audioRateChanged) {
@ -1232,7 +1263,10 @@ struct mCore* GBCoreCreate(void) {
core->setSync = _GBCoreSetSync;
core->loadConfig = _GBCoreLoadConfig;
core->reloadConfigOption = _GBCoreReloadConfigOption;
core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
core->baseVideoSize = _GBCoreBaseVideoSize;
core->currentVideoSize = _GBCoreCurrentVideoSize;
core->videoScale = _GBCoreVideoScale;
core->screenRegions = _GBCoreScreenRegions;
core->setVideoBuffer = _GBCoreSetVideoBuffer;
core->setVideoGLTex = _GBCoreSetVideoGLTex;
core->getPixels = _GBCoreGetPixels;

View File

@ -129,6 +129,10 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
{ GBA_REGION_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, GBA_SIZE_EEPROM, GBA_SIZE_EEPROM, mCORE_MEMORY_RW },
};
static const struct mCoreScreenRegion _GBAScreenRegions[] = {
{ 0, "Screen", 0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS }
};
static const struct mCoreRegisterInfo _GBARegisters[] = {
{ "r0", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
{ "r1", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
@ -422,19 +426,45 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
}
}
static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
static void _GBACoreBaseVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core);
*width = GBA_VIDEO_HORIZONTAL_PIXELS;
*height = GBA_VIDEO_VERTICAL_PIXELS;
}
static void _GBACoreCurrentVideoSize(const struct mCore* core, unsigned* width, unsigned* height) {
int scale = 1;
#ifdef BUILD_GLES3
const struct GBACore* gbacore = (const struct GBACore*) core;
int scale = gbacore->glRenderer.scale;
if (gbacore->glRenderer.outputTex != (unsigned) -1) {
scale = gbacore->glRenderer.scale;
}
#else
UNUSED(core);
int scale = 1;
#endif
*width = GBA_VIDEO_HORIZONTAL_PIXELS * scale;
*height = GBA_VIDEO_VERTICAL_PIXELS * scale;
}
static unsigned _GBACoreVideoScale(const struct mCore* core) {
#ifdef BUILD_GLES3
const struct GBACore* gbacore = (const struct GBACore*) core;
if (gbacore->glRenderer.outputTex != (unsigned) -1) {
return gbacore->glRenderer.scale;
}
#else
UNUSED(core);
#endif
return 1;
}
static size_t _GBACoreScreenRegions(const struct mCore* core, const struct mCoreScreenRegion** regions) {
UNUSED(core);
*regions = _GBAScreenRegions;
return 1;
}
static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->renderer.outputBuffer = buffer;
@ -500,7 +530,7 @@ static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
gba->stream = stream;
if (stream && stream->videoDimensionsChanged) {
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
stream->videoDimensionsChanged(stream, width, height);
}
if (stream && stream->audioRateChanged) {
@ -1358,7 +1388,10 @@ struct mCore* GBACoreCreate(void) {
core->setSync = _GBACoreSetSync;
core->loadConfig = _GBACoreLoadConfig;
core->reloadConfigOption = _GBACoreReloadConfigOption;
core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
core->baseVideoSize = _GBACoreBaseVideoSize;
core->currentVideoSize = _GBACoreCurrentVideoSize;
core->videoScale = _GBACoreVideoScale;
core->screenRegions = _GBACoreScreenRegions;
core->setVideoBuffer = _GBACoreSetVideoBuffer;
core->setVideoGLTex = _GBACoreSetVideoGLTex;
core->getPixels = _GBACoreGetPixels;

View File

@ -496,7 +496,7 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
int wide = isWide ? 2 : 1;
unsigned corew, coreh;
core->desiredVideoDimensions(core, &corew, &coreh);
core->currentVideoSize(core, &corew, &coreh);
int w = corew;
int h = coreh;

View File

@ -83,7 +83,7 @@ bool _mExampleRun(const struct mArguments* args, Socket client) {
// Get the dimensions required for this core and send them to the client.
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->baseVideoSize(core, &width, &height);
ssize_t bufferSize = width * height * BYTES_PER_PIXEL;
uint32_t sendNO;
sendNO = htonl(width);

View File

@ -414,19 +414,13 @@ void retro_get_system_info(struct retro_system_info* info) {
void retro_get_system_av_info(struct retro_system_av_info* info) {
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
info->geometry.base_width = width;
info->geometry.base_height = height;
#ifdef M_CORE_GB
if (core->platform(core) == mPLATFORM_GB) {
info->geometry.max_width = 256;
info->geometry.max_height = 224;
} else
#endif
{
info->geometry.max_width = width;
info->geometry.max_height = height;
}
core->baseVideoSize(core, &width, &height);
info->geometry.max_width = width;
info->geometry.max_height = height;
info->geometry.aspect_ratio = width / (double) height;
info->timing.fps = core->frequency(core) / (float) core->frameCycles(core);
@ -614,7 +608,7 @@ void retro_run(void) {
core->runFrame(core);
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256);
#ifdef M_CORE_GBA

View File

@ -68,7 +68,7 @@
outputBuffer = nil;
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->baseVideoSize(core, &width, &height);
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
core->setVideoBuffer(core, outputBuffer, width);
core->setAudioBufferSize(core, SAMPLES);
@ -143,14 +143,14 @@
- (OEIntRect)screenRect
{
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->currentVideoSize(core, &width, &height);
return OEIntRectMake(0, 0, width, height);
}
- (OEIntSize)bufferSize
{
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
core->baseVideoSize(core, &width, &height);
return OEIntSizeMake(width, height);
}

View File

@ -9,9 +9,9 @@
static const GLint _glVertices[] = {
0, 0,
256, 0,
256, 256,
0, 256
1, 0,
1, 1,
0, 1
};
static const GLint _glTexCoords[] = {
@ -21,76 +21,102 @@ static const GLint _glTexCoords[] = {
0, 1
};
static inline void _initTex(void) {
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
}
static void mGLContextInit(struct VideoBackend* v, WHandle handle) {
UNUSED(handle);
struct mGLContext* context = (struct mGLContext*) v;
v->width = 1;
v->height = 1;
memset(context->layerDims, 0, sizeof(context->layerDims));
memset(context->imageSizes, -1, sizeof(context->imageSizes));
glGenTextures(2, context->tex);
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#ifndef _WIN32
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
_initTex();
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
_initTex();
context->activeTex = 0;
glGenTextures(VIDEO_LAYER_MAX, context->tex);
int i;
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
glBindTexture(GL_TEXTURE_2D, context->layers[i]);
_initTex();
}
}
static void mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
static inline void _setTexDims(int width, int height) {
#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
}
static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
struct mGLContext* context = (struct mGLContext*) v;
if (width == v->width && height == v->height) {
if (layer >= VIDEO_LAYER_MAX) {
return;
}
v->width = width;
v->height = height;
context->layerDims[layer].x = dims->x;
context->layerDims[layer].y = dims->y;
if (dims->width == context->layerDims[layer].width && dims->height == context->layerDims[layer].height) {
return;
}
context->layerDims[layer].width = dims->width;
context->layerDims[layer].height = dims->height;
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
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
if (layer == VIDEO_LAYER_IMAGE) {
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
_setTexDims(dims->width, dims->height);
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
_setTexDims(dims->width, dims->height);
} else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
_setTexDims(dims->width, dims->height);
}
}
}
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
#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
static void mGLContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
memcpy(dims, &context->layerDims[layer], sizeof(*dims));
}
static void mGLContextDeinit(struct VideoBackend* v) {
struct mGLContext* context = (struct mGLContext*) v;
glDeleteTextures(2, context->tex);
glDeleteTextures(VIDEO_LAYER_MAX, context->layers);
}
static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
unsigned drawW = w;
unsigned drawH = h;
unsigned maxW;
unsigned maxH;
VideoBackendGetFrameSize(v, &maxW, &maxH);
if (v->lockAspectRatio) {
lockAspectRatioUInt(v->width, v->height, &drawW, &drawH);
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
}
if (v->lockIntegerScaling) {
lockIntegerRatioUInt(v->width, &drawW);
lockIntegerRatioUInt(v->height, &drawH);
lockIntegerRatioUInt(maxW, &drawW);
lockIntegerRatioUInt(maxH, &drawH);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@ -105,33 +131,7 @@ static void mGLContextClear(struct VideoBackend* v) {
glClear(GL_COLOR_BUFFER_BIT);
}
void mGLContextDrawFrame(struct VideoBackend* v) {
struct mGLContext* context = (struct mGLContext*) v;
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, v->width, v->height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (v->interframeBlending) {
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);
glEnable(GL_BLEND);
}
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
static void _setFilter(struct VideoBackend* v) {
if (v->filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -139,36 +139,147 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
static void _setFrame(struct Rectangle* dims, int frameW, int frameH) {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glScissor(viewport[0] + dims->x * viewport[2] / frameW,
viewport[1] + dims->y * viewport[3] / frameH,
dims->width * viewport[2] / frameW,
dims->height * viewport[3] / frameH);
glTranslatef(dims->x, dims->y, 0);
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
}
void mGLContextDrawFrame(struct VideoBackend* v) {
struct mGLContext* context = (struct mGLContext*) v;
glEnable(GL_TEXTURE_2D);
glEnable(GL_SCISSOR_TEST);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
unsigned frameW, frameH;
VideoBackendGetFrameSize(v, &frameW, &frameH);
glOrtho(0, frameW, frameH, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
int layer;
for (layer = 0; layer < VIDEO_LAYER_IMAGE; ++layer) {
if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) {
continue;
}
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
_setFilter(v);
glPushMatrix();
_setFrame(&context->layerDims[layer], frameW, frameH);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glPopMatrix();
}
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH);
if (v->interframeBlending) {
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
glBlendColor(1, 1, 1, 0.5);
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex ^ 1]);
_setFilter(v);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glEnable(GL_BLEND);
}
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
_setFilter(v);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_BLEND);
}
void mGLContextPostFrame(struct VideoBackend* v, const void* frame) {
static void mGLContextSetImageSize(struct VideoBackend* v, enum VideoLayer layer, int width, int height) {
struct mGLContext* context = (struct mGLContext*) v;
context->activeTex ^= 1;
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (width <= 0 || height <= 0) {
context->imageSizes[layer].width = -1;
context->imageSizes[layer].height = -1;
width = context->layerDims[layer].width;
height = context->layerDims[layer].height;
} else {
context->imageSizes[layer].width = width;
context->imageSizes[layer].height = height;
}
if (layer == VIDEO_LAYER_IMAGE) {
glBindTexture(GL_TEXTURE_2D, context->tex[0]);
_setTexDims(width, height);
glBindTexture(GL_TEXTURE_2D, context->tex[1]);
_setTexDims(width, height);
} else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
_setTexDims(width, height);
}
}
static void mGLContextImageSize(struct VideoBackend* v, enum VideoLayer layer, int* width, int* height) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
*width = context->layerDims[layer].width;
*height = context->layerDims[layer].height;
} else {
*width = context->imageSizes[layer].width;
*height = context->imageSizes[layer].height;
}
}
void mGLContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
struct mGLContext* context = (struct mGLContext*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (layer == VIDEO_LAYER_IMAGE) {
context->activeTex ^= 1;
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
} else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
}
int width = context->imageSizes[layer].width;
int height = context->imageSizes[layer].height;
if (width <= 0 || height <= 0) {
width = context->layerDims[layer].width;
height = context->layerDims[layer].height;
}
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
#endif
#elif defined(__BIG_ENDIAN__)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, frame);
#endif
}
void mGLContextCreate(struct mGLContext* context) {
context->d.init = mGLContextInit;
context->d.deinit = mGLContextDeinit;
context->d.setDimensions = mGLContextSetDimensions;
context->d.resized = mGLContextResized;
context->d.swap = 0;
context->d.setLayerDimensions = mGLContextSetLayerDimensions;
context->d.layerDimensions = mGLContextLayerDimensions;
context->d.contextResized = mGLContextResized;
context->d.swap = NULL;
context->d.clear = mGLContextClear;
context->d.postFrame = mGLContextPostFrame;
context->d.setImageSize = mGLContextSetImageSize;
context->d.imageSize = mGLContextImageSize;
context->d.setImage = mGLContextPostFrame;
context->d.drawFrame = mGLContextDrawFrame;
context->d.setMessage = 0;
context->d.clearMessage = 0;
}

View File

@ -26,8 +26,11 @@ CXX_GUARD_START
struct mGLContext {
struct VideoBackend d;
GLuint tex[2];
int activeTex;
GLuint tex[2];
GLuint layers[VIDEO_LAYER_MAX];
struct Rectangle layerDims[VIDEO_LAYER_MAX];
struct Size imageSizes[VIDEO_LAYER_MAX];
};
void mGLContextCreate(struct mGLContext*);

View File

@ -101,12 +101,15 @@ static const GLfloat _vertices[] = {
static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
UNUSED(handle);
struct mGLES2Context* context = (struct mGLES2Context*) v;
v->width = 1;
v->height = 1;
glGenTextures(1, &context->tex);
glBindTexture(GL_TEXTURE_2D, context->tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
memset(context->layerDims, 0, sizeof(context->layerDims));
glGenTextures(VIDEO_LAYER_MAX, context->tex);
int i;
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
glBindTexture(GL_TEXTURE_2D, context->tex[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glGenBuffers(1, &context->vbo);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
@ -177,15 +180,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
context->finalShader.tex = 0;
}
static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
if (width == v->width && height == v->height) {
return;
}
v->width = width;
v->height = height;
glBindTexture(GL_TEXTURE_2D, context->tex);
static inline void _setTexDims(int width, int height) {
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
@ -197,20 +192,54 @@ static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, u
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#endif
}
size_t n;
for (n = 0; n < context->nShaders; ++n) {
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
context->shaders[n].dirty = true;
static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (dims->width != context->layerDims[layer].width && dims->height != context->layerDims[layer].height) {
context->layerDims[layer].width = dims->width;
context->layerDims[layer].height = dims->height;
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
_setTexDims(dims->width, dims->height);
}
}
context->initialShader.dirty = true;
context->interframeShader.dirty = true;
context->layerDims[layer].x = dims->x;
context->layerDims[layer].y = dims->y;
unsigned newW;
unsigned newH;
VideoBackendGetFrameSize(v, &newW, &newH);
if (newW != context->width || newH != context->height) {
size_t n;
for (n = 0; n < context->nShaders; ++n) {
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
context->shaders[n].dirty = true;
}
}
context->initialShader.dirty = true;
context->interframeShader.dirty = true;
context->width = newW;
context->height = newH;
}
}
static void mGLES2ContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
memcpy(dims, &context->layerDims[layer], sizeof(*dims));
}
static void mGLES2ContextDeinit(struct VideoBackend* v) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
glDeleteTextures(1, &context->tex);
glDeleteTextures(VIDEO_LAYER_MAX, context->tex);
glDeleteBuffers(1, &context->vbo);
mGLES2ShaderDeinit(&context->initialShader);
mGLES2ShaderDeinit(&context->finalShader);
@ -222,12 +251,16 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
struct mGLES2Context* context = (struct mGLES2Context*) v;
unsigned drawW = w;
unsigned drawH = h;
unsigned maxW = context->width;
unsigned maxH = context->height;
if (v->lockAspectRatio) {
lockAspectRatioUInt(v->width, v->height, &drawW, &drawH);
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
}
if (v->lockIntegerScaling) {
lockIntegerRatioUInt(v->width, &drawW);
lockIntegerRatioUInt(v->height, &drawH);
lockIntegerRatioUInt(maxW, &drawW);
lockIntegerRatioUInt(maxH, &drawH);
}
size_t n;
for (n = 0; n < context->nShaders; ++n) {
@ -249,7 +282,7 @@ static void mGLES2ContextClear(struct VideoBackend* v) {
glClear(GL_COLOR_BUFFER_BIT);
}
void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
static void _drawShaderEx(struct mGLES2Context* context, struct mGLES2Shader* shader, int layer) {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
int drawW = shader->width;
@ -260,19 +293,19 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
drawW = viewport[2];
padW = viewport[0];
} else if (shader->width < 0) {
drawW = context->d.width * -shader->width;
drawW = context->width * -shader->width;
}
if (!drawH) {
drawH = viewport[3];
padH = viewport[1];
} else if (shader->height < 0) {
drawH = context->d.height * -shader->height;
drawH = context->height * -shader->height;
}
if (shader->integerScaling) {
padW = 0;
padH = 0;
drawW -= drawW % context->d.width;
drawH -= drawH % context->d.height;
drawW -= drawW % context->width;
drawH -= drawH % context->height;
}
if (shader->dirty) {
@ -286,22 +319,29 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
shader->dirty = false;
}
if (layer >= 0 && layer < VIDEO_LAYER_MAX) {
glViewport(context->layerDims[layer].x, context->layerDims[layer].y, context->layerDims[layer].width, context->layerDims[layer].height);
} else {
glViewport(padW, padH, drawW, drawH);
}
glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
glViewport(padW, padH, drawW, drawH);
if (shader->blend) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
if (layer <= VIDEO_LAYER_BACKGROUND) {
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
glUseProgram(shader->program);
glUniform1i(shader->texLocation, 0);
glUniform2f(shader->texSizeLocation, context->d.width, context->d.height);
glUniform2f(shader->texSizeLocation, context->width, context->height);
glUniform2f(shader->outputSizeLocation, drawW, drawH);
#ifdef BUILD_GLES3
if (shader->vao != (GLuint) -1) {
@ -368,21 +408,36 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
glBindTexture(GL_TEXTURE_2D, shader->tex);
}
static void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
_drawShaderEx(context, shader, -1);
}
void mGLES2ContextDrawFrame(struct VideoBackend* v) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, context->tex);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
context->finalShader.filter = v->filter;
_drawShader(context, &context->initialShader);
if (v->interframeBlending) {
context->interframeShader.blend = true;
glViewport(0, 0, viewport[2], viewport[3]);
_drawShader(context, &context->interframeShader);
int layer;
for (layer = 0; layer <= VIDEO_LAYER_IMAGE; ++layer) {
if (context->layerDims[layer].width < 1 || context->layerDims[layer].height < 1) {
continue;
}
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
_drawShaderEx(context, &context->initialShader, layer);
if (layer != VIDEO_LAYER_IMAGE) {
continue;
}
if (v->interframeBlending) {
context->interframeShader.blend = true;
glViewport(0, 0, viewport[2], viewport[3]);
_drawShader(context, &context->interframeShader);
}
}
size_t n;
for (n = 0; n < context->nShaders; ++n) {
glViewport(0, 0, viewport[2], viewport[3]);
@ -392,8 +447,8 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
_drawShader(context, &context->finalShader);
if (v->interframeBlending) {
context->interframeShader.blend = false;
glBindTexture(GL_TEXTURE_2D, context->tex);
_drawShader(context, &context->initialShader);
glBindTexture(GL_TEXTURE_2D, context->tex[VIDEO_LAYER_IMAGE]);
_drawShaderEx(context, &context->initialShader, VIDEO_LAYER_IMAGE);
glViewport(0, 0, viewport[2], viewport[3]);
_drawShader(context, &context->interframeShader);
}
@ -406,33 +461,79 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
#endif
}
void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
static void mGLES2ContextSetImageSize(struct VideoBackend* v, enum VideoLayer layer, int width, int height) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
glBindTexture(GL_TEXTURE_2D, context->tex);
if (layer >= VIDEO_LAYER_MAX) {
return;
}
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
if (width <= 0 || height <= 0) {
context->imageSizes[layer].width = -1;
context->imageSizes[layer].height = -1;
width = context->layerDims[layer].width;
height = context->layerDims[layer].height;
} else {
context->imageSizes[layer].width = width;
context->imageSizes[layer].height = height;
}
_setTexDims(width, height);
}
static void mGLES2ContextImageSize(struct VideoBackend* v, enum VideoLayer layer, int* width, int* height) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
if (context->imageSizes[layer].width <= 0 || context->imageSizes[layer].height <= 0) {
*width = context->layerDims[layer].width;
*height = context->layerDims[layer].height;
} else {
*width = context->imageSizes[layer].width;
*height = context->imageSizes[layer].height;
}
}
void mGLES2ContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
if (layer >= VIDEO_LAYER_MAX) {
return;
}
int width = context->imageSizes[layer].width;
int height = context->imageSizes[layer].height;
if (width <= 0 || height <= 0) {
width = context->layerDims[layer].width;
height = context->layerDims[layer].height;
}
glBindTexture(GL_TEXTURE_2D, context->tex[layer]);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
#endif
#elif defined(__BIG_ENDIAN__)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
#endif
}
void mGLES2ContextCreate(struct mGLES2Context* context) {
context->d.init = mGLES2ContextInit;
context->d.deinit = mGLES2ContextDeinit;
context->d.setDimensions = mGLES2ContextSetDimensions;
context->d.resized = mGLES2ContextResized;
context->d.swap = 0;
context->d.setLayerDimensions = mGLES2ContextSetLayerDimensions;
context->d.layerDimensions = mGLES2ContextLayerDimensions;
context->d.contextResized = mGLES2ContextResized;
context->d.swap = NULL;
context->d.clear = mGLES2ContextClear;
context->d.postFrame = mGLES2ContextPostFrame;
context->d.setImageSize = mGLES2ContextSetImageSize;
context->d.imageSize = mGLES2ContextImageSize;
context->d.setImage = mGLES2ContextPostFrame;
context->d.drawFrame = mGLES2ContextDrawFrame;
context->d.setMessage = 0;
context->d.clearMessage = 0;
context->shaders = 0;
context->nShaders = 0;
}

View File

@ -79,9 +79,14 @@ struct mGLES2Shader {
struct mGLES2Context {
struct VideoBackend d;
GLuint tex;
GLuint tex[VIDEO_LAYER_MAX];
GLuint vbo;
struct Rectangle layerDims[VIDEO_LAYER_MAX];
struct Size imageSizes[VIDEO_LAYER_MAX];
unsigned width;
unsigned height;
struct mGLES2Shader initialShader;
struct mGLES2Shader finalShader;
struct mGLES2Shader interframeShader;

View File

@ -323,7 +323,7 @@ void mPSP2Setup(struct mGUIRunner* runner) {
mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
runner->core->baseVideoSize(runner->core, &width, &height);
tex[0] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
tex[1] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
currentTex = 0;
@ -614,7 +614,7 @@ void mPSP2Swap(struct mGUIRunner* runner) {
void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
runner->core->currentVideoSize(runner->core, &width, &height);
if (interframeBlending) {
_drawTex(tex[!currentTex], width, height, faded, false);
}

View File

@ -235,7 +235,7 @@ class Core(object):
def desired_video_dimensions(self):
width = ffi.new("unsigned*")
height = ffi.new("unsigned*")
self._core.desiredVideoDimensions(self._core, width, height)
self._core.currentVideoSize(self._core, width, height)
return width[0], height[0]
@protected

View File

@ -266,11 +266,15 @@ mPlatform CoreController::platform() const {
QSize CoreController::screenDimensions() const {
unsigned width, height;
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
m_threadContext.core->currentVideoSize(m_threadContext.core, &width, &height);
return QSize(width, height);
}
unsigned CoreController::videoScale() const {
return m_threadContext.core->videoScale(m_threadContext.core);
}
void CoreController::loadConfig(ConfigController* config) {
Interrupter interrupter(this);
m_loadStateFlags = config->getOption("loadStateExtdata", m_loadStateFlags).toInt();
@ -1186,7 +1190,7 @@ int CoreController::updateAutofire() {
void CoreController::finishFrame() {
if (!m_hwaccel) {
unsigned width, height;
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
m_threadContext.core->currentVideoSize(m_threadContext.core, &width, &height);
QMutexLocker locker(&m_bufferMutex);
memcpy(m_completeBuffer.data(), m_activeBuffer.constData(), width * height * BYTES_PER_PIXEL);

View File

@ -94,6 +94,7 @@ public:
mPlatform platform() const;
QSize screenDimensions() const;
unsigned videoScale() const;
bool supportsFeature(Feature feature) const { return m_threadContext.core->supportsFeature(m_threadContext.core, static_cast<mCoreFeature>(feature)); }
bool hardwareAccelerated() const { return m_hwaccel; }

View File

@ -54,6 +54,8 @@ public:
virtual VideoShader* shaders() = 0;
virtual int framebufferHandle() { return -1; }
virtual void setVideoScale(int) {}
virtual void setBackgroundImage(const QImage&) = 0;
virtual QSize contentSize() const = 0;
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = proxy; }
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }

View File

@ -283,6 +283,8 @@ void DisplayGL::startThread(int from) {
show();
m_gl->reset();
}
QTimer::singleShot(8, this, &DisplayGL::updateContentSize);
}
bool DisplayGL::supportsFormat(const QSurfaceFormat& format) {
@ -370,12 +372,14 @@ void DisplayGL::unpauseDrawing() {
if (!m_gl && shouldDisableUpdates()) {
setUpdatesEnabled(false);
}
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
}
}
void DisplayGL::forceDraw() {
if (m_hasStarted) {
QMetaObject::invokeMethod(m_painter.get(), "forceDraw");
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
}
}
@ -438,6 +442,11 @@ void DisplayGL::setVideoScale(int scale) {
QMetaObject::invokeMethod(m_painter.get(), "resizeContext");
}
void DisplayGL::setBackgroundImage(const QImage& image) {
QMetaObject::invokeMethod(m_painter.get(), "setBackgroundImage", Q_ARG(const QImage&, image));
QMetaObject::invokeMethod(this, "updateContentSize", Qt::QueuedConnection);
}
void DisplayGL::resizeEvent(QResizeEvent* event) {
Display::resizeEvent(event);
resizePainter();
@ -491,6 +500,10 @@ void DisplayGL::setupProxyThread() {
m_proxyThread.start();
}
void DisplayGL::updateContentSize() {
QMetaObject::invokeMethod(m_painter.get(), "contentSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QSize, m_cachedContentSize));
}
int DisplayGL::framebufferHandle() {
return m_painter->glTex();
}
@ -669,13 +682,44 @@ void PainterGL::resizeContext() {
return;
}
dequeueAll(false);
m_backend->setDimensions(m_backend, size.width(), size.height());
Rectangle dims = {0, 0, size.width(), size.height()};
m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_IMAGE, &dims);
recenterLayers();
}
void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
m_messagePainter = messagePainter;
}
void PainterGL::recenterLayers() {
if (!m_context) {
return;
}
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND, VIDEO_LAYER_IMAGE};
Rectangle frame = {0};
unsigned scale = std::max(1U, m_context->videoScale());
for (VideoLayer l : centeredLayers) {
Rectangle dims{};
int width, height;
m_backend->imageSize(m_backend, l, &width, &height);
dims.width = width;
dims.height = height;
if (l != VIDEO_LAYER_IMAGE) {
dims.width *= scale;
dims.height *= scale;
m_backend->setLayerDimensions(m_backend, l, &dims);
}
RectangleUnion(&frame, &dims);
}
for (VideoLayer l : centeredLayers) {
Rectangle dims;
m_backend->layerDimensions(m_backend, l, &dims);
RectangleCenter(&frame, &dims);
m_backend->setLayerDimensions(m_backend, l, &dims);
}
}
void PainterGL::resize(const QSize& size) {
qreal r = m_window->devicePixelRatio();
m_size = size;
@ -844,9 +888,9 @@ void PainterGL::unpause() {
void PainterGL::performDraw() {
float r = m_window->devicePixelRatio();
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r);
if (m_buffer) {
m_backend->postFrame(m_backend, m_buffer);
m_backend->setImage(m_backend, VIDEO_LAYER_IMAGE, m_buffer);
}
m_backend->drawFrame(m_backend);
if (m_showOSD && m_messagePainter && m_paintDev && !glContextHasBug(OpenGLBug::IG4ICD_CRASH)) {
@ -902,7 +946,7 @@ void PainterGL::dequeue() {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (supportsShaders()) {
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
gl2Backend->tex = m_bridgeTexOut;
gl2Backend->tex[VIDEO_LAYER_IMAGE] = m_bridgeTexOut;
}
#endif
}
@ -996,11 +1040,18 @@ VideoShader* PainterGL::shaders() {
return &m_shader;
}
QSize PainterGL::contentSize() const {
unsigned width, height;
VideoBackendGetFrameSize(m_backend, &width, &height);
return {static_cast<int>(width > static_cast<unsigned>(INT_MAX) ? INT_MAX : width),
static_cast<int>(height > static_cast<unsigned>(INT_MAX) ? INT_MAX : height)};
}
int PainterGL::glTex() {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (supportsShaders()) {
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
return gl2Backend->tex;
return gl2Backend->tex[VIDEO_LAYER_IMAGE];
}
#endif
#ifdef BUILD_GL
@ -1036,6 +1087,27 @@ void PainterGL::updateFramebufferHandle() {
m_context->setFramebufferHandle(m_bridgeTexIn);
}
void PainterGL::setBackgroundImage(const QImage& image) {
if (!m_started) {
makeCurrent();
}
m_backend->setImageSize(m_backend, VIDEO_LAYER_BACKGROUND, image.width(), image.height());
recenterLayers();
if (!image.isNull()) {
m_background = image.convertToFormat(QImage::Format_RGB32);
m_background = m_background.rgbSwapped();
m_backend->setImage(m_backend, VIDEO_LAYER_BACKGROUND, m_background.constBits());
} else {
m_background = QImage();
}
if (!m_started) {
m_gl->doneCurrent();
}
}
void PainterGL::swapTex() {
if (!m_started) {
return;

View File

@ -94,6 +94,7 @@ public:
VideoShader* shaders() override;
void setVideoProxy(std::shared_ptr<VideoProxy>) override;
int framebufferHandle() override;
QSize contentSize() const override { return m_cachedContentSize; }
static bool supportsFormat(const QSurfaceFormat&);
@ -113,6 +114,7 @@ public slots:
void clearShaders() override;
void resizeContext() override;
void setVideoScale(int scale) override;
void setBackgroundImage(const QImage&) override;
protected:
virtual void paintEvent(QPaintEvent*) override { forceDraw(); }
@ -121,6 +123,7 @@ protected:
private slots:
void startThread(int);
void setupProxyThread();
void updateContentSize();
private:
void resizePainter();
@ -138,6 +141,7 @@ private:
mGLWidget* m_gl;
QOffscreenSurface m_proxySurface;
std::unique_ptr<QOpenGLContext> m_proxyContext;
QSize m_cachedContentSize;
};
class PainterGL : public QObject {
@ -184,10 +188,12 @@ public slots:
void filter(bool filter);
void resizeContext();
void updateFramebufferHandle();
void setBackgroundImage(const QImage&);
void setShaders(struct VDir*);
void clearShaders();
VideoShader* shaders();
QSize contentSize() const;
signals:
void created();
@ -202,6 +208,7 @@ private:
void performDraw();
void dequeue();
void dequeueAll(bool keep = false);
void recenterLayers();
std::array<std::array<uint32_t, 0x100000>, 3> m_buffers;
QList<uint32_t*> m_free;
@ -220,6 +227,7 @@ private:
QWindow* m_window;
QSurface* m_surface;
QSurfaceFormat m_format;
QImage m_background;
std::unique_ptr<QOpenGLPaintDevice> m_paintDev;
std::unique_ptr<QOpenGLContext> m_gl;
int m_finalTexIdx = 0;

View File

@ -92,21 +92,103 @@ void DisplayQt::resizeContext() {
}
}
void DisplayQt::setBackgroundImage(const QImage& image) {
m_background = image;
update();
}
void DisplayQt::paintEvent(QPaintEvent*) {
QPainter painter(this);
painter.fillRect(QRect(QPoint(), size()), Qt::black);
if (isFiltered()) {
painter.setRenderHint(QPainter::SmoothPixmapTransform);
}
QRect full(clampSize(QSize(m_width, m_height), size(), isAspectRatioLocked(), isIntegerScalingLocked()));
QRect bgRect(0, 0, m_background.width(), m_background.height());
QRect imRect(0, 0, m_width, m_height);
QSize outerFrame = contentSize();
if (bgRect.width() > imRect.width()) {
imRect.moveLeft(bgRect.width() - imRect.width());
} else {
bgRect.moveLeft(imRect.width() - bgRect.width());
}
if (bgRect.height() > imRect.height()) {
imRect.moveTop(bgRect.height() - imRect.height());
} else {
bgRect.moveTop(imRect.height() - bgRect.height());
}
QRect full(clampSize(outerFrame, size(), isAspectRatioLocked(), isIntegerScalingLocked()));
if (m_background.isNull()) {
imRect = full;
} else {
if (imRect.x()) {
imRect.moveLeft(imRect.x() * full.width() / bgRect.width() / 2);
imRect.setWidth(imRect.width() * full.width() / bgRect.width());
bgRect.setWidth(full.width());
} else {
bgRect.moveLeft(bgRect.x() * full.width() / imRect.width() / 2);
bgRect.setWidth(bgRect.width() * full.width() / imRect.width());
imRect.setWidth(full.width());
}
if (imRect.y()) {
imRect.moveTop(imRect.y() * full.height() / bgRect.height() / 2);
imRect.setHeight(imRect.height() * full.height() / bgRect.height());
bgRect.setHeight(full.height());
} else {
bgRect.moveTop(bgRect.y() * full.height() / imRect.height() / 2);
bgRect.setHeight(bgRect.height() * full.height() / imRect.height());
imRect.setHeight(full.height());
}
if (bgRect.right() > imRect.right()) {
if (bgRect.right() < full.right()) {
imRect.translate((full.right() - bgRect.right()), 0);
bgRect.translate((full.right() - bgRect.right()), 0);
}
} else {
if (imRect.right() < full.right()) {
bgRect.translate((full.right() - imRect.right()), 0);
imRect.translate((full.right() - imRect.right()), 0);
}
}
if (bgRect.bottom() > imRect.bottom()) {
if (bgRect.bottom() < full.bottom()) {
imRect.translate(0, (full.bottom() - bgRect.bottom()));
bgRect.translate(0, (full.bottom() - bgRect.bottom()));
}
} else {
if (imRect.bottom() < full.bottom()) {
bgRect.translate(0, (full.bottom() - imRect.bottom()));
imRect.translate(0, (full.bottom() - imRect.bottom()));
}
}
painter.drawImage(bgRect, m_background);
}
if (hasInterframeBlending()) {
painter.drawImage(full, m_oldBacking, QRect(0, 0, m_width, m_height));
painter.drawImage(imRect, 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(imRect, m_backing, QRect(0, 0, m_width, m_height));
painter.setOpacity(1);
if (isShowOSD() || isShowFrameCounter()) {
messagePainter()->paint(&painter);
}
}
QSize DisplayQt::contentSize() const {
QSize outerFrame(m_width, m_height);
if (m_background.width() > outerFrame.width()) {
outerFrame.setWidth(m_background.width());
}
if (m_background.height() > outerFrame.height()) {
outerFrame.setHeight(m_background.height());
}
return outerFrame;
}

View File

@ -22,6 +22,7 @@ public:
bool isDrawing() const override { return m_isDrawing; }
bool supportsShaders() const override { return false; }
VideoShader* shaders() override { return nullptr; }
QSize contentSize() const override;
public slots:
void stopDrawing() override;
@ -36,6 +37,7 @@ public slots:
void setShaders(struct VDir*) override {}
void clearShaders() override {}
void resizeContext() override;
void setBackgroundImage(const QImage&) override;
protected:
virtual void paintEvent(QPaintEvent*) override;
@ -46,6 +48,7 @@ private:
int m_height = -1;
QImage m_backing{nullptr};
QImage m_oldBacking{nullptr};
QImage m_background;
std::shared_ptr<CoreController> m_context = nullptr;
};

View File

@ -559,7 +559,7 @@ void FrameView::newVl() {
}
#endif
unsigned width, height;
m_vl->desiredVideoDimensions(m_vl, &width, &height);
m_vl->baseVideoSize(m_vl, &width, &height);
m_framebuffer = QImage(width, height, QImage::Format_RGBX8888);
m_vl->setVideoBuffer(m_vl, reinterpret_cast<color_t*>(m_framebuffer.bits()), width);
m_vl->reset(m_vl);

View File

@ -143,6 +143,9 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () {
selectPath(m_ui.cheatsPath, m_ui.cheatsSameDir);
});
connect(m_ui.bgImageBrowse, &QAbstractButton::pressed, [this] () {
selectImage(m_ui.bgImage);
});
connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared);
// TODO: Move to reloadConfig()
@ -445,6 +448,13 @@ void SettingsView::selectPath(QLineEdit* field, QCheckBox* sameDir) {
}
}
void SettingsView::selectImage(QLineEdit* field) {
QString path = GBAApp::app()->getOpenFileName(this, tr("Select image"), tr("Image file (*.png *.jpg *.jpeg)"));
if (!path.isNull()) {
field->setText(makePortablePath(path));
}
}
void SettingsView::updateConfig() {
saveSetting("gba.bios", m_ui.gbaBios);
saveSetting("gb.bios", m_ui.gbBios);
@ -501,6 +511,7 @@ void SettingsView::updateConfig() {
saveSetting("vbaBugCompat", m_ui.vbaBugCompat);
saveSetting("updateAutoCheck", m_ui.updateAutoCheck);
saveSetting("showFilenameInLibrary", m_ui.showFilenameInLibrary);
saveSetting("backgroundImage", m_ui.bgImage);
if (m_ui.audioBufferSize->currentText().toInt() > 8192) {
m_ui.audioBufferSize->setCurrentText("8192");
@ -725,6 +736,7 @@ void SettingsView::reloadConfig() {
loadSetting("vbaBugCompat", m_ui.vbaBugCompat, true);
loadSetting("updateAutoCheck", m_ui.updateAutoCheck);
loadSetting("showFilenameInLibrary", m_ui.showFilenameInLibrary);
loadSetting("backgroundImage", m_ui.bgImage);
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());

View File

@ -71,6 +71,7 @@ public slots:
private slots:
void selectBios(QLineEdit*);
void selectPath(QLineEdit*, QCheckBox*);
void selectImage(QLineEdit*);
void updateConfig();
void reloadConfig();
void updateChecked();

View File

@ -907,6 +907,41 @@
</item>
</layout>
</item>
<item row="19" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<widget class="QLineEdit" name="bgImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bgImageBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="18" column="0" colspan="2">
<widget class="Line" name="line_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="19" column="0">
<widget class="QLabel" name="label_53">
<property name="text">
<string>Custom border:</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="update">

View File

@ -1053,6 +1053,8 @@ void Window::reloadDisplayDriver() {
#elif defined(M_CORE_GBA)
m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
#endif
m_display->setBackgroundImage(QImage{m_config->getOption("backgroundImage")});
}
void Window::reloadAudioDriver() {
@ -1486,8 +1488,8 @@ void Window::setupMenu(QMenuBar* menubar) {
Action* setSize = m_frameSizes[i];
showNormal();
QSize size(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
if (m_controller) {
size = m_controller->screenDimensions();
if (m_display) {
size = m_display->contentSize();
}
size *= i;
m_savedScale = i;
@ -1875,6 +1877,13 @@ void Window::setupOptions() {
updateTitle();
}, this);
ConfigOption* backgroundImage = m_config->addOption("backgroundImage");
backgroundImage->connect([this](const QVariant& value) {
if (m_display) {
m_display->setBackgroundImage(QImage{value.toString()});
}
}, this);
m_config->updateOption("backgroundImage");
}
void Window::attachWidget(QWidget* widget) {

View File

@ -5,10 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "main.h"
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#include <mgba/core/version.h>
#ifdef USE_PNG
#include <mgba-util/png-io.h>
#include <mgba-util/vfs.h>
#endif
void mSDLGLDoViewport(int w, int h, struct VideoBackend* v) {
v->resized(v, w, h);
v->contextResized(v, w, h);
v->clear(v);
v->swap(v);
v->clear(v);
@ -24,6 +31,60 @@ void mSDLGLCommonSwap(struct VideoBackend* context) {
#endif
}
bool mSDLGLCommonLoadBackground(struct VideoBackend* context) {
#ifdef USE_PNG
struct mSDLRenderer* renderer = context->user;
const char* bgImage = mCoreConfigGetValue(&renderer->core->config, "backgroundImage");
if (!bgImage) {
return false;
}
struct VFile* vf = VFileOpen(bgImage, O_RDONLY);
if (!vf) {
return false;
}
bool ok = false;
png_structp png = PNGReadOpen(vf, 0);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (!png || !info || !end) {
goto done;
}
if (!PNGReadHeader(png, info)) {
goto done;
}
unsigned width = png_get_image_width(png, info);
unsigned height = png_get_image_height(png, info);
uint32_t* pixels = malloc(width * height * 4);
if (!pixels) {
goto done;
}
if (!PNGReadPixels(png, info, pixels, width, height, width) || !PNGReadFooter(png, end)) {
free(pixels);
goto done;
}
struct Rectangle dims = {
.width = width,
.height = height
};
context->setLayerDimensions(context, VIDEO_LAYER_BACKGROUND, &dims);
context->setImage(context, VIDEO_LAYER_BACKGROUND, pixels);
free(pixels);
ok = true;
done:
PNGReadClose(png, info, end);
vf->close(vf);
return ok;
#else
UNUSED(context);
return false;
#endif
}
bool mSDLGLCommonInit(struct mSDLRenderer* renderer) {
#ifndef COLOR_16_BIT
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@ -66,3 +127,61 @@ bool mSDLGLCommonInit(struct mSDLRenderer* renderer) {
#endif
return true;
}
void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user) {
struct mCoreThread* context = user;
SDL_Event event;
struct VideoBackend* v = renderer->backend;
if (mSDLGLCommonLoadBackground(v)) {
renderer->player.windowUpdated = true;
struct Rectangle frame;
VideoBackendGetFrame(v, &frame);
int i;
for (i = 0; i <= VIDEO_LAYER_IMAGE; ++i) {
struct Rectangle dims;
v->layerDimensions(v, i, &dims);
RectangleCenter(&frame, &dims);
v->setLayerDimensions(v, i, &dims);
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetWindowSize(renderer->window, frame.width * renderer->ratio, frame.height * renderer->ratio);
#endif
}
while (mCoreThreadIsActive(context)) {
while (SDL_PollEvent(&event)) {
mSDLHandleEvent(context, &renderer->player, &event);
// Event handling can change the size of the screen
if (renderer->player.windowUpdated) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
#else
renderer->viewportWidth = renderer->player.newWidth;
renderer->viewportHeight = renderer->player.newHeight;
mSDLGLCommonInit(renderer);
#endif
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
renderer->player.windowUpdated = 0;
}
}
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
struct Rectangle dims;
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
if (renderer->width != dims.width || renderer->height != dims.height) {
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
dims.width = renderer->width;
dims.height = renderer->height;
v->setLayerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
}
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
v->setImage(v, VIDEO_LAYER_IMAGE, renderer->outputBuffer);
}
mCoreSyncWaitFrameEnd(&context->impl->sync);
v->drawFrame(v);
v->swap(v);
}
}

View File

@ -15,6 +15,8 @@ struct mSDLRenderer;
void mSDLGLDoViewport(int w, int h, struct VideoBackend* v);
void mSDLGLCommonSwap(struct VideoBackend* context);
bool mSDLGLCommonInit(struct mSDLRenderer* renderer);
void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user);
bool mSDLGLCommonLoadBackground(struct VideoBackend* context);
CXX_GUARD_END

View File

@ -8,19 +8,17 @@
#include "gl-common.h"
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#include <mgba-util/math.h>
#include "platform/opengl/gl.h"
static bool mSDLGLInit(struct mSDLRenderer* renderer);
static void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user);
static void mSDLGLDeinit(struct mSDLRenderer* renderer);
void mSDLGLCreate(struct mSDLRenderer* renderer) {
renderer->init = mSDLGLInit;
renderer->deinit = mSDLGLDeinit;
renderer->runloop = mSDLGLRunloop;
renderer->runloop = mSDLGLCommonRunloop;
renderer->backend = &renderer->gl.d;
}
bool mSDLGLInit(struct mSDLRenderer* renderer) {
@ -37,48 +35,18 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) {
renderer->gl.d.filter = renderer->filter;
renderer->gl.d.swap = mSDLGLCommonSwap;
renderer->gl.d.init(&renderer->gl.d, 0);
renderer->gl.d.setDimensions(&renderer->gl.d, renderer->width, renderer->height);
struct Rectangle dims = {
.x = 0,
.y = 0,
.width = renderer->width,
.height = renderer->height
};
renderer->gl.d.setLayerDimensions(&renderer->gl.d, VIDEO_LAYER_IMAGE, &dims);
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl.d);
return true;
}
void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) {
struct mCoreThread* context = user;
SDL_Event event;
struct VideoBackend* v = &renderer->gl.d;
while (mCoreThreadIsActive(context)) {
while (SDL_PollEvent(&event)) {
mSDLHandleEvent(context, &renderer->player, &event);
// Event handling can change the size of the screen
if (renderer->player.windowUpdated) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
#else
renderer->viewportWidth = renderer->player.newWidth;
renderer->viewportHeight = renderer->player.newHeight;
mSDLGLCommonInit(renderer);
#endif
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
renderer->player.windowUpdated = 0;
}
}
renderer->core->desiredVideoDimensions(renderer->core, &renderer->width, &renderer->height);
if (renderer->width != v->width || renderer->height != v->height) {
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
v->setDimensions(v, renderer->width, renderer->height);
}
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
v->postFrame(v, renderer->outputBuffer);
}
mCoreSyncWaitFrameEnd(&context->impl->sync);
v->drawFrame(v);
v->swap(v);
}
}
void mSDLGLDeinit(struct mSDLRenderer* renderer) {
if (renderer->gl.d.deinit) {
renderer->gl.d.deinit(&renderer->gl.d);

View File

@ -11,20 +11,19 @@
#endif
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#ifdef __linux__
#include <malloc.h>
#endif
static bool mSDLGLES2Init(struct mSDLRenderer* renderer);
static void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user);
static void mSDLGLES2Deinit(struct mSDLRenderer* renderer);
void mSDLGLES2Create(struct mSDLRenderer* renderer) {
renderer->init = mSDLGLES2Init;
renderer->deinit = mSDLGLES2Deinit;
renderer->runloop = mSDLGLES2Runloop;
renderer->runloop = mSDLGLCommonRunloop;
renderer->backend = &renderer->gl2.d;
}
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
@ -50,43 +49,19 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
renderer->gl2.d.swap = mSDLGLCommonSwap;
#endif
renderer->gl2.d.init(&renderer->gl2.d, 0);
renderer->gl2.d.setDimensions(&renderer->gl2.d, renderer->width, renderer->height);
struct Rectangle dims = {
.x = 0,
.y = 0,
.width = renderer->width,
.height = renderer->height
};
renderer->gl2.d.setLayerDimensions(&renderer->gl2.d, VIDEO_LAYER_IMAGE, &dims);
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl2.d);
return true;
}
void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) {
struct mCoreThread* context = user;
SDL_Event event;
struct VideoBackend* v = &renderer->gl2.d;
while (mCoreThreadIsActive(context)) {
while (SDL_PollEvent(&event)) {
mSDLHandleEvent(context, &renderer->player, &event);
// Event handling can change the size of the screen
if (renderer->player.windowUpdated) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
#else
renderer->viewportWidth = renderer->player.newWidth;
renderer->viewportHeight = renderer->player.newHeight;
mSDLGLCommonInit(renderer);
#endif
mSDLGLDoViewport(renderer->viewportWidth, renderer->viewportHeight, v);
renderer->player.windowUpdated = 0;
}
}
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
v->postFrame(v, renderer->outputBuffer);
}
mCoreSyncWaitFrameEnd(&context->impl->sync);
v->drawFrame(v);
v->swap(v);
}
}
void mSDLGLES2Deinit(struct mSDLRenderer* renderer) {
if (renderer->gl2.d.deinit) {
renderer->gl2.d.deinit(&renderer->gl2.d);

View File

@ -107,7 +107,7 @@ int main(int argc, char** argv) {
return 1;
}
renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height);
renderer.core->baseVideoSize(renderer.core, &renderer.width, &renderer.height);
renderer.ratio = graphicsOpts.multiplier;
if (renderer.ratio == 0) {
renderer.ratio = 1;
@ -275,7 +275,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
if (!didFail) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
renderer->core->desiredVideoDimensions(renderer->core, &renderer->width, &renderer->height);
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
unsigned width = renderer->width * renderer->ratio;
unsigned height = renderer->height * renderer->ratio;
if (width != (unsigned) renderer->viewportWidth && height != (unsigned) renderer->viewportHeight) {

View File

@ -76,6 +76,8 @@ struct mSDLRenderer {
struct mGLES2Context gl2;
#endif
struct VideoBackend* backend;
#ifdef USE_PIXMAN
pixman_image_t* pix;
pixman_image_t* screenpix;

View File

@ -28,7 +28,7 @@ bool mSDLSWInit(struct mSDLRenderer* renderer) {
SDL_WM_SetCaption(projectName, "");
unsigned width, height;
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
renderer->core->baseVideoSize(renderer->core, &width, &height);
SDL_Surface* surface = SDL_GetVideoSurface();
SDL_LockSurface(surface);

View File

@ -21,7 +21,7 @@ void mSDLSWCreate(struct mSDLRenderer* renderer) {
bool mSDLSWInit(struct mSDLRenderer* renderer) {
unsigned width, height;
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
renderer->core->baseVideoSize(renderer->core, &width, &height);
renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen));
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
renderer->player.window = renderer->window;

View File

@ -477,7 +477,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
}
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
runner->core->currentVideoSize(runner->core, &width, &height);
glActiveTexture(GL_TEXTURE0);
if (usePbo) {
@ -530,7 +530,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un
glBindTexture(GL_TEXTURE_2D, screenshotTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
runner->core->desiredVideoDimensions(runner->core, &width, &height);
runner->core->currentVideoSize(runner->core, &width, &height);
glDisable(GL_BLEND);
bool wasPbo = usePbo;
usePbo = false;

View File

@ -1032,7 +1032,7 @@ void CInemaTestRun(struct CInemaTest* test) {
return;
}
struct CInemaImage image;
core->desiredVideoDimensions(core, &image.width, &image.height);
core->baseVideoSize(core, &image.width, &image.height);
ssize_t bufferSize = image.width * image.height * BYTES_PER_PIXEL;
image.data = malloc(bufferSize);
image.stride = image.width;
@ -1075,7 +1075,7 @@ void CInemaTestRun(struct CInemaTest* test) {
for (frame = 0; frame < skip; ++frame) {
core->runFrame(core);
}
core->desiredVideoDimensions(core, &image.width, &image.height);
core->currentVideoSize(core, &image.width, &image.height);
#ifdef USE_FFMPEG
struct FFmpegDecoder decoder;
@ -1139,7 +1139,7 @@ void CInemaTestRun(struct CInemaTest* test) {
break;
}
CIlog(3, "Test frame: %u\n", frameCounter);
core->desiredVideoDimensions(core, &image.width, &image.height);
core->currentVideoSize(core, &image.width, &image.height);
uint8_t* diff = NULL;
struct CInemaImage expected = {
.data = NULL,

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "video-backend.h"
void VideoBackendGetFrame(const struct VideoBackend* v, struct Rectangle* frame) {
memset(frame, 0, sizeof(*frame));
int i;
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
struct Rectangle dims;
v->layerDimensions(v, i, &dims);
RectangleUnion(frame, &dims);
}
}
void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) {
struct Rectangle frame;
VideoBackendGetFrame(v, &frame);
*width = frame.width;
*height = frame.height;
}

View File

@ -10,6 +10,8 @@
CXX_GUARD_START
#include <mgba-util/geometry.h>
#ifdef _WIN32
#include <windows.h>
typedef HWND WHandle;
@ -17,26 +19,34 @@ typedef HWND WHandle;
typedef void* WHandle;
#endif
enum VideoLayer {
VIDEO_LAYER_BACKGROUND = 0,
VIDEO_LAYER_BEZEL,
VIDEO_LAYER_IMAGE,
VIDEO_LAYER_OVERLAY,
VIDEO_LAYER_MAX
};
struct VideoBackend {
void (*init)(struct VideoBackend*, WHandle handle);
void (*deinit)(struct VideoBackend*);
void (*setDimensions)(struct VideoBackend*, unsigned width, unsigned height);
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct Rectangle*);
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct Rectangle*);
void (*swap)(struct VideoBackend*);
void (*clear)(struct VideoBackend*);
void (*resized)(struct VideoBackend*, unsigned w, unsigned h);
void (*postFrame)(struct VideoBackend*, const void* frame);
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
void (*setImageSize)(struct VideoBackend*, enum VideoLayer, int w, int h);
void (*imageSize)(struct VideoBackend*, enum VideoLayer, int* w, int* h);
void (*setImage)(struct VideoBackend*, enum VideoLayer, const void* frame);
void (*drawFrame)(struct VideoBackend*);
void (*setMessage)(struct VideoBackend*, const char* message);
void (*clearMessage)(struct VideoBackend*);
void* user;
unsigned width;
unsigned height;
bool filter;
bool lockAspectRatio;
bool lockIntegerScaling;
bool interframeBlending;
enum VideoLayer cropToLayer;
};
struct VideoShader {
@ -48,6 +58,9 @@ struct VideoShader {
size_t nPasses;
};
void VideoBackendGetFrame(const struct VideoBackend*, struct Rectangle* frame);
void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height);
CXX_GUARD_END
#endif

View File

@ -1511,7 +1511,7 @@ void _prepareForFrame(struct mGUIRunner* runner) {
}
void _drawFrame(struct mGUIRunner* runner, bool faded) {
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
runner->core->currentVideoSize(runner->core, &corew, &coreh);
uint32_t color = 0xFFFFFF3F;
if (!faded) {
color |= 0xC0;

View File

@ -16,6 +16,7 @@ set(SOURCE_FILES
convolve.c
elf-read.c
export.c
geometry.c
patch.c
patch-fast.c
patch-ips.c
@ -33,6 +34,7 @@ set(GUI_FILES
gui/menu.c)
set(TEST_FILES
test/geometry.c
test/sfo.c
test/string-parser.c
test/string-utf8.c

36
src/util/geometry.c Normal file
View File

@ -0,0 +1,36 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/geometry.h>
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add) {
int x0 = dst->x;
int y0 = dst->y;
int x1 = dst->x + dst->width;
int y1 = dst->y + dst->height;
if (add->x < x0) {
x0 = add->x;
}
if (add->y < y0) {
y0 = add->y;
}
if (add->x + add->width > x1) {
x1 = add->x + add->width;
}
if (add->y + add->height > y1) {
y1 = add->y + add->height;
}
dst->x = x0;
dst->y = y0;
dst->width = x1 - x0;
dst->height = y1 - y0;
}
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect) {
rect->x = ref->x + (ref->width - rect->width) / 2;
rect->y = ref->y + (ref->height - rect->height) / 2;
}

213
src/util/test/geometry.c Normal file
View File

@ -0,0 +1,213 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba-util/geometry.h>
M_TEST_DEFINE(unionRectOrigin) {
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 1,
.height = 1
};
struct Rectangle b = {
.x = 1,
.y = 1,
.width = 1,
.height = 1
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, 0);
assert_int_equal(a.y, 0);
assert_int_equal(a.width, 2);
assert_int_equal(a.height, 2);
}
M_TEST_DEFINE(unionRectOriginSwapped) {
struct Rectangle a = {
.x = 1,
.y = 1,
.width = 1,
.height = 1
};
struct Rectangle b = {
.x = 0,
.y = 0,
.width = 1,
.height = 1
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, 0);
assert_int_equal(a.y, 0);
assert_int_equal(a.width, 2);
assert_int_equal(a.height, 2);
}
M_TEST_DEFINE(unionRectNonOrigin) {
struct Rectangle a = {
.x = 1,
.y = 1,
.width = 1,
.height = 1
};
struct Rectangle b = {
.x = 2,
.y = 2,
.width = 1,
.height = 1
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, 1);
assert_int_equal(a.y, 1);
assert_int_equal(a.width, 2);
assert_int_equal(a.height, 2);
}
M_TEST_DEFINE(unionRectOverlapping) {
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 2,
.height = 2
};
struct Rectangle b = {
.x = 1,
.y = 1,
.width = 2,
.height = 2
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, 0);
assert_int_equal(a.y, 0);
assert_int_equal(a.width, 3);
assert_int_equal(a.height, 3);
}
M_TEST_DEFINE(unionRectSubRect) {
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 3,
.height = 3
};
struct Rectangle b = {
.x = 1,
.y = 1,
.width = 1,
.height = 1
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, 0);
assert_int_equal(a.y, 0);
assert_int_equal(a.width, 3);
assert_int_equal(a.height, 3);
}
M_TEST_DEFINE(unionRectNegativeOrigin) {
struct Rectangle a = {
.x = -1,
.y = -1,
.width = 1,
.height = 1
};
struct Rectangle b = {
.x = 0,
.y = 0,
.width = 1,
.height = 1
};
RectangleUnion(&a, &b);
assert_int_equal(a.x, -1);
assert_int_equal(a.y, -1);
assert_int_equal(a.width, 2);
assert_int_equal(a.height, 2);
}
M_TEST_DEFINE(centerRectBasic) {
struct Rectangle ref = {
.x = 0,
.y = 0,
.width = 4,
.height = 4
};
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 2,
.height = 2
};
RectangleCenter(&ref, &a);
assert_int_equal(a.x, 1);
assert_int_equal(a.y, 1);
}
M_TEST_DEFINE(centerRectRoundDown) {
struct Rectangle ref = {
.x = 0,
.y = 0,
.width = 4,
.height = 4
};
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 1,
.height = 1
};
RectangleCenter(&ref, &a);
assert_int_equal(a.x, 1);
assert_int_equal(a.y, 1);
}
M_TEST_DEFINE(centerRectRoundDown2) {
struct Rectangle ref = {
.x = 0,
.y = 0,
.width = 4,
.height = 4
};
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 3,
.height = 2
};
RectangleCenter(&ref, &a);
assert_int_equal(a.x, 0);
assert_int_equal(a.y, 1);
}
M_TEST_DEFINE(centerRectOffset) {
struct Rectangle ref = {
.x = 1,
.y = 1,
.width = 4,
.height = 4
};
struct Rectangle a = {
.x = 0,
.y = 0,
.width = 2,
.height = 2
};
RectangleCenter(&ref, &a);
assert_int_equal(a.x, 2);
assert_int_equal(a.y, 2);
}
M_TEST_SUITE_DEFINE(Geometry,
cmocka_unit_test(unionRectOrigin),
cmocka_unit_test(unionRectOriginSwapped),
cmocka_unit_test(unionRectNonOrigin),
cmocka_unit_test(unionRectOverlapping),
cmocka_unit_test(unionRectSubRect),
cmocka_unit_test(unionRectNegativeOrigin),
cmocka_unit_test(centerRectBasic),
cmocka_unit_test(centerRectRoundDown),
cmocka_unit_test(centerRectRoundDown2),
cmocka_unit_test(centerRectOffset),
)