Merge branch 'feature/hwrender'

This commit is contained in:
Vicki Pfau 2019-05-17 17:11:43 -07:00
commit 03c17cdc56
38 changed files with 2281 additions and 249 deletions

View File

@ -57,7 +57,7 @@ if(NOT LIBMGBA_ONLY)
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)")
set(BUILD_GL ON CACHE BOOL "Build with OpenGL")
set(BUILD_GLES2 OFF CACHE BOOL "Build with OpenGL|ES 2")
set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2")
set(BUILD_GLES3 OFF CACHE BOOL "Build with OpenGL|ES 3")
set(USE_EPOXY ON CACHE STRING "Build with libepoxy")
set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies")
@ -447,7 +447,7 @@ set(FEATURE_DEFINES)
set(FEATURE_FLAGS)
set(FEATURES)
set(ENABLES)
if(CMAKE_SYSTEM_NAME MATCHES .*BSD)
if(CMAKE_SYSTEM_NAME MATCHES ".*BSD|DragonFly")
set(LIBEDIT_LIBRARIES -ledit)
if (CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
list(APPEND LIBEDIT_LIBRARIES -ltermcap)
@ -462,9 +462,9 @@ if(BUILD_GL)
endif()
endif()
if(NOT BUILD_GL)
set(OPENGLE_LIBRARY "" CACHE PATH "" FORCE)
set(OPENGL_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(BUILD_GLES2 AND NOT BUILD_RASPI)
if(BUILD_GLES2 AND NOT BUILD_RASPI AND NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|Darwin|Linux|.*BSD|DragonFly|Haiku)$")
find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h)
find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM)
if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY)
@ -474,6 +474,16 @@ endif()
if(NOT BUILD_GLES2)
set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(BUILD_GL)
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
list(APPEND DEPENDENCY_LIB ${OPENGL_LIBRARY})
include_directories(${OPENGL_INCLUDE_DIR})
endif()
if(BUILD_GLES2)
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
list(APPEND DEPENDENCY_LIB ${OPENGLES2_LIBRARY})
include_directories(${OPENGLES2_INCLUDE_DIR})
endif()
if(BUILD_GLES3)
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
@ -727,12 +737,18 @@ if (USE_LZMA)
endif()
if(USE_EPOXY)
list(APPEND OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
add_definitions(-DBUILD_GL -DBUILD_GLES2)
list(APPEND FEATURES EPOXY)
include_directories(AFTER ${EPOXY_INCLUDE_DIRS})
link_directories(${EPOXY_LIBRARY_DIRS})
set(OPENGLES2_LIBRARY ${EPOXY_LIBRARIES})
list(APPEND DEPENDENCY_LIB ${EPOXY_LIBRARIES})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libepoxy0")
elseif(BUILD_GL)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgl1|libgles2")
elseif(BUILD_GLES2)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2")
endif()
if(USE_SQLITE3)

View File

@ -85,7 +85,7 @@ Other Unix-like platforms, such as OpenBSD, are known to work as well, but are u
### System requirements
Requirements are minimal. Any computer that can run Windows Vista or newer should be able to handle emulation. Support for OpenGL 1.1 or newer is also required.
Requirements are minimal. Any computer that can run Windows Vista or newer should be able to handle emulation. Support for OpenGL 1.1 or newer is also required, with OpenGL 3.0 or newer for shaders and advanced features.
Downloads
---------

View File

@ -47,6 +47,7 @@ struct mCore {
struct mTiming* timing;
struct mDebugger* debugger;
struct mDebuggerSymbols* symbolTable;
struct mVideoLogger* videoLogger;
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct mDirectorySet dirs;
@ -63,12 +64,14 @@ struct mCore {
void (*deinit)(struct mCore*);
enum mPlatform (*platform)(const struct mCore*);
bool (*supportsFeature)(const struct mCore*, enum mCoreFeature);
void (*setSync)(struct mCore*, struct mCoreSync*);
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height);
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
void (*setVideoGLTex)(struct mCore*, unsigned texid);
void (*getPixels)(struct mCore*, const void** buffer, size_t* stride);
void (*putPixels)(struct mCore*, const void* buffer, size_t stride);

View File

@ -80,6 +80,10 @@ enum mColorFormat {
mCOLOR_ANY = -1
};
enum mCoreFeature {
mCORE_FEATURE_OPENGL = 1,
};
struct mCoreCallbacks {
void* context;
void (*videoFrameStarted)(void* context);

View File

@ -29,6 +29,7 @@ struct mVideoThreadProxy {
Condition toThreadCond;
Mutex mutex;
enum mVideoThreadProxyState threadState;
enum mVideoLoggerEvent event;
struct RingFIFO dirtyQueue;
};

View File

@ -27,6 +27,14 @@ enum mVideoLoggerDirtyType {
DIRTY_BUFFER,
};
enum mVideoLoggerEvent {
LOGGER_EVENT_NONE = 0,
LOGGER_EVENT_INIT,
LOGGER_EVENT_DEINIT,
LOGGER_EVENT_RESET,
LOGGER_EVENT_GET_PIXELS,
};
struct mVideoLoggerDirtyInfo {
enum mVideoLoggerDirtyType type;
uint32_t address;
@ -38,6 +46,7 @@ struct VFile;
struct mVideoLogger {
bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length);
bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block);
void (*postEvent)(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
void* dataContext;
bool block;
@ -52,6 +61,7 @@ struct mVideoLogger {
void* context;
bool (*parsePacket)(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
void (*handleEvent)(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
uint16_t* (*vramBlock)(struct mVideoLogger* logger, uint32_t address);
size_t vramSize;
@ -64,6 +74,9 @@ struct mVideoLogger {
uint16_t* vram;
uint16_t* oam;
uint16_t* palette;
const void* pixelBuffer;
size_t pixelStride;
};
void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly);

View File

@ -0,0 +1,25 @@
/* Copyright (c) 2013-2019 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 GBA_RENDERER_COMMON_H
#define GBA_RENDERER_COMMON_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/internal/gba/video.h>
struct GBAVideoRendererSprite {
struct GBAObj obj;
int16_t y;
int16_t endY;
};
int GBAVideoRendererCleanOAM(struct GBAObj* oam, struct GBAVideoRendererSprite* sprites, int offsetY);
CXX_GUARD_END
#endif

View File

@ -0,0 +1,184 @@
/* Copyright (c) 2013-2019 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 VIDEO_GL_H
#define VIDEO_GL_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/core/core.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/renderers/common.h>
#include <mgba/internal/gba/video.h>
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef USE_EPOXY
#include <epoxy/gl.h>
#elif defined(BUILD_GL)
#ifdef __APPLE__
#include <OpenGL/gl3.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#else
#include <GLES3/gl3.h>
#endif
struct GBAVideoGLAffine {
int16_t dx;
int16_t dmx;
int16_t dy;
int16_t dmy;
int32_t sx;
int32_t sy;
};
struct GBAVideoGLBackground {
GLuint fbo;
GLuint tex;
GLuint flags;
unsigned index;
int enabled;
unsigned priority;
uint32_t charBase;
int mosaic;
int multipalette;
uint32_t screenBase;
int overflow;
int size;
int target1;
int target2;
uint16_t x;
uint16_t y;
int32_t refx;
int32_t refy;
struct GBAVideoGLAffine affine[4];
};
enum {
GBA_GL_FBO_OBJ = 0,
GBA_GL_FBO_WINDOW = 1,
GBA_GL_FBO_OUTPUT = 2,
GBA_GL_FBO_MAX
};
enum {
GBA_GL_TEX_OBJ_COLOR = 0,
GBA_GL_TEX_OBJ_FLAGS = 1,
GBA_GL_TEX_WINDOW = 6,
GBA_GL_TEX_MAX
};
enum {
GBA_GL_VS_LOC = 0,
GBA_GL_VS_MAXPOS,
GBA_GL_BG_VRAM = 2,
GBA_GL_BG_PALETTE,
GBA_GL_BG_SCREENBASE,
GBA_GL_BG_CHARBASE,
GBA_GL_BG_SIZE,
GBA_GL_BG_OFFSET,
GBA_GL_BG_INFLAGS,
GBA_GL_BG_TRANSFORM,
GBA_GL_OBJ_VRAM = 2,
GBA_GL_OBJ_PALETTE,
GBA_GL_OBJ_CHARBASE,
GBA_GL_OBJ_STRIDE,
GBA_GL_OBJ_LOCALPALETTE,
GBA_GL_OBJ_INFLAGS,
GBA_GL_OBJ_TRANSFORM,
GBA_GL_OBJ_DIMS,
GBA_GL_OBJ_OBJWIN,
GBA_GL_FINALIZE_SCALE = 2,
GBA_GL_FINALIZE_LAYERS,
GBA_GL_FINALIZE_FLAGS,
GBA_GL_FINALIZE_WINDOW,
GBA_GL_FINALIZE_BACKDROP,
GBA_GL_FINALIZE_BACKDROPFLAGS,
GBA_GL_UNIFORM_MAX = 12
};
struct GBAVideoGLShader {
GLuint program;
GLuint vao;
GLuint uniforms[GBA_GL_UNIFORM_MAX];
};
struct GBAVideoGLRenderer {
struct GBAVideoRenderer d;
uint32_t* temporaryBuffer;
struct GBAVideoGLBackground bg[4];
int oamMax;
bool oamDirty;
struct GBAVideoRendererSprite sprites[128];
GLuint fbo[GBA_GL_FBO_MAX];
GLuint layers[GBA_GL_TEX_MAX];
GLuint vbo;
GLuint outputTex;
#ifdef BUILD_GLES3
uint16_t shadowPalette[512];
#endif
GLuint paletteTex;
bool paletteDirty;
GLuint vramTex;
unsigned vramDirty;
struct GBAVideoGLShader bgShader[6];
struct GBAVideoGLShader objShader[2];
struct GBAVideoGLShader finalizeShader;
GBARegisterDISPCNT dispcnt;
unsigned target1Obj;
unsigned target1Bd;
unsigned target2Obj;
unsigned target2Bd;
enum GBAVideoBlendEffect blendEffect;
uint16_t blda;
uint16_t bldb;
uint16_t bldy;
GBAMosaicControl mosaic;
struct GBAVideoGLWindowN {
struct GBAVideoWindowRegion h;
struct GBAVideoWindowRegion v;
GBAWindowControl control;
} winN[2];
GBAWindowControl winout;
GBAWindowControl objwin;
int firstAffine;
int scale;
};
void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer);
#endif
CXX_GUARD_END
#endif

View File

@ -13,14 +13,9 @@ CXX_GUARD_START
#include <mgba/core/core.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/renderers/common.h>
#include <mgba/internal/gba/video.h>
struct GBAVideoSoftwareSprite {
struct GBAObj obj;
int y;
int endY;
};
struct GBAVideoSoftwareBackground {
unsigned index;
int enabled;
@ -49,13 +44,6 @@ struct GBAVideoSoftwareBackground {
int32_t offsetY;
};
enum BlendEffect {
BLEND_NONE = 0,
BLEND_ALPHA = 1,
BLEND_BRIGHTEN = 2,
BLEND_DARKEN = 3
};
enum {
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
@ -82,25 +70,6 @@ enum {
#define IS_WRITABLE(PIXEL) ((PIXEL) & 0xFE000000)
struct WindowRegion {
uint8_t end;
uint8_t start;
};
DECL_BITFIELD(GBAWindowControl, uint8_t);
DECL_BIT(GBAWindowControl, Bg0Enable, 0);
DECL_BIT(GBAWindowControl, Bg1Enable, 1);
DECL_BIT(GBAWindowControl, Bg2Enable, 2);
DECL_BIT(GBAWindowControl, Bg3Enable, 3);
DECL_BIT(GBAWindowControl, ObjEnable, 4);
DECL_BIT(GBAWindowControl, BlendEnable, 5);
DECL_BITFIELD(GBAMosaicControl, uint16_t);
DECL_BITS(GBAMosaicControl, BgH, 0, 4);
DECL_BITS(GBAMosaicControl, BgV, 4, 4);
DECL_BITS(GBAMosaicControl, ObjH, 8, 4);
DECL_BITS(GBAMosaicControl, ObjV, 12, 4);
struct WindowControl {
GBAWindowControl packed;
int8_t priority;
@ -133,7 +102,7 @@ struct GBAVideoSoftwareRenderer {
unsigned target2Obj;
unsigned target2Bd;
bool blendDirty;
enum BlendEffect blendEffect;
enum GBAVideoBlendEffect blendEffect;
color_t normalPalette[512];
color_t variantPalette[512];
@ -144,8 +113,8 @@ struct GBAVideoSoftwareRenderer {
GBAMosaicControl mosaic;
struct WindowN {
struct WindowRegion h;
struct WindowRegion v;
struct GBAVideoWindowRegion h;
struct GBAVideoWindowRegion v;
struct WindowControl control;
} winN[2];
@ -161,7 +130,7 @@ struct GBAVideoSoftwareRenderer {
int oamDirty;
int oamMax;
struct GBAVideoSoftwareSprite sprites[128];
struct GBAVideoRendererSprite sprites[128];
int16_t objOffsetX;
int16_t objOffsetY;

View File

@ -32,18 +32,25 @@ enum {
BASE_TILE = 0x00010000
};
enum ObjMode {
enum GBAVideoObjMode {
OBJ_MODE_NORMAL = 0,
OBJ_MODE_SEMITRANSPARENT = 1,
OBJ_MODE_OBJWIN = 2
};
enum ObjShape {
enum GBAVideoObjShape {
OBJ_SHAPE_SQUARE = 0,
OBJ_SHAPE_HORIZONTAL = 1,
OBJ_SHAPE_VERTICAL = 2
};
enum GBAVideoBlendEffect {
BLEND_NONE = 0,
BLEND_ALPHA = 1,
BLEND_BRIGHTEN = 2,
BLEND_DARKEN = 3
};
DECL_BITFIELD(GBAObjAttributesA, uint16_t);
DECL_BITS(GBAObjAttributesA, Y, 0, 8);
DECL_BIT(GBAObjAttributesA, Transformed, 8);
@ -90,6 +97,11 @@ union GBAOAM {
uint16_t raw[512];
};
struct GBAVideoWindowRegion {
uint8_t end;
uint8_t start;
};
#define GBA_TEXT_MAP_TILE(MAP) ((MAP) & 0x03FF)
#define GBA_TEXT_MAP_HFLIP(MAP) ((MAP) & 0x0400)
#define GBA_TEXT_MAP_VFLIP(MAP) ((MAP) & 0x0800)
@ -144,6 +156,20 @@ DECL_BIT(GBARegisterBLDCNT, Target2Bg3, 11);
DECL_BIT(GBARegisterBLDCNT, Target2Obj, 12);
DECL_BIT(GBARegisterBLDCNT, Target2Bd, 13);
DECL_BITFIELD(GBAWindowControl, uint8_t);
DECL_BIT(GBAWindowControl, Bg0Enable, 0);
DECL_BIT(GBAWindowControl, Bg1Enable, 1);
DECL_BIT(GBAWindowControl, Bg2Enable, 2);
DECL_BIT(GBAWindowControl, Bg3Enable, 3);
DECL_BIT(GBAWindowControl, ObjEnable, 4);
DECL_BIT(GBAWindowControl, BlendEnable, 5);
DECL_BITFIELD(GBAMosaicControl, uint16_t);
DECL_BITS(GBAMosaicControl, BgH, 0, 4);
DECL_BITS(GBAMosaicControl, BgV, 4, 4);
DECL_BITS(GBAMosaicControl, ObjH, 8, 4);
DECL_BITS(GBAMosaicControl, ObjV, 12, 4);
struct GBAVideoRenderer {
void (*init)(struct GBAVideoRenderer* renderer);
void (*reset)(struct GBAVideoRenderer* renderer);

View File

@ -18,6 +18,7 @@ static THREAD_ENTRY _proxyThread(void* renderer);
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent);
static void _lock(struct mVideoLogger* logger);
static void _unlock(struct mVideoLogger* logger);
@ -38,6 +39,7 @@ void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
renderer->d.writeData = _writeData;
renderer->d.readData = _readData;
renderer->d.postEvent = _postEvent;
}
void mVideoThreadProxyInit(struct mVideoLogger* logger) {
@ -131,6 +133,14 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo
return read;
}
static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
MutexLock(&proxyRenderer->mutex);
proxyRenderer->event = event;
ConditionWake(&proxyRenderer->toThreadCond);
MutexUnlock(&proxyRenderer->mutex);
}
static void _lock(struct mVideoLogger* logger) {
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
MutexLock(&proxyRenderer->mutex);
@ -172,13 +182,18 @@ static THREAD_ENTRY _proxyThread(void* logger) {
break;
}
proxyRenderer->threadState = PROXY_THREAD_BUSY;
MutexUnlock(&proxyRenderer->mutex);
if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
// FIFO was corrupted
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
if (proxyRenderer->event) {
proxyRenderer->d.handleEvent(&proxyRenderer->d, proxyRenderer->event);
proxyRenderer->event = 0;
} else {
MutexUnlock(&proxyRenderer->mutex);
if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
// FIFO was corrupted
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
}
MutexLock(&proxyRenderer->mutex);
}
MutexLock(&proxyRenderer->mutex);
ConditionWake(&proxyRenderer->fromThreadCond);
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
proxyRenderer->threadState = PROXY_THREAD_IDLE;

View File

@ -138,6 +138,14 @@ static enum mPlatform _GBCorePlatform(const struct mCore* core) {
return PLATFORM_GB;
}
static bool _GBCoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
UNUSED(core);
switch (feature) {
default:
return false;
}
}
static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
struct GB* gb = core->board;
gb->sync = sync;
@ -233,6 +241,11 @@ static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t st
gbcore->renderer.outputBufferStride = stride;
}
static void _GBCoreSetVideoGLTex(struct mCore* core, unsigned texid) {
UNUSED(core);
UNUSED(texid);
}
static void _GBCoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
struct GBCore* gbcore = (struct GBCore*) core;
gbcore->renderer.d.getPixels(&gbcore->renderer.d, stride, buffer);
@ -883,10 +896,12 @@ struct mCore* GBCoreCreate(void) {
core->init = _GBCoreInit;
core->deinit = _GBCoreDeinit;
core->platform = _GBCorePlatform;
core->supportsFeature = _GBCoreSupportsFeature;
core->setSync = _GBCoreSetSync;
core->loadConfig = _GBCoreLoadConfig;
core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
core->setVideoBuffer = _GBCoreSetVideoBuffer;
core->setVideoGLTex = _GBCoreSetVideoGLTex;
core->getPixels = _GBCoreGetPixels;
core->putPixels = _GBCorePutPixels;
core->getAudioChannel = _GBCoreGetAudioChannel;

View File

@ -283,11 +283,13 @@ static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
}
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->unlock(proxyRenderer->logger);
*pixels = proxyRenderer->logger->pixelBuffer;
*stride = proxyRenderer->logger->pixelStride;
} else {
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
}
}

View File

@ -17,6 +17,9 @@
#ifndef DISABLE_THREADING
#include <mgba/feature/thread-proxy.h>
#endif
#ifdef BUILD_GLES2
#include <mgba/internal/gba/renderers/gl.h>
#endif
#include <mgba/internal/gba/renderers/proxy.h>
#include <mgba/internal/gba/renderers/video-software.h>
#include <mgba/internal/gba/savedata.h>
@ -123,6 +126,9 @@ struct mVideoLogContext;
struct GBACore {
struct mCore d;
struct GBAVideoSoftwareRenderer renderer;
#ifdef BUILD_GLES2
struct GBAVideoGLRenderer glRenderer;
#endif
struct GBAVideoProxyRenderer proxyRenderer;
struct mVideoLogContext* logContext;
struct mCoreCallbacks logCallbacks;
@ -151,6 +157,7 @@ static bool _GBACoreInit(struct mCore* core) {
core->timing = &gba->timing;
core->debugger = NULL;
core->symbolTable = NULL;
core->videoLogger = NULL;
gbacore->overrides = NULL;
gbacore->debuggerPlatform = NULL;
gbacore->cheatDevice = NULL;
@ -167,6 +174,11 @@ static bool _GBACoreInit(struct mCore* core) {
GBAVideoSoftwareRendererCreate(&gbacore->renderer);
gbacore->renderer.outputBuffer = NULL;
#ifdef BUILD_GLES2
GBAVideoGLRendererCreate(&gbacore->glRenderer);
gbacore->glRenderer.outputTex = -1;
#endif
#ifndef DISABLE_THREADING
mVideoThreadProxyCreate(&gbacore->threadProxy);
#endif
@ -206,6 +218,20 @@ static enum mPlatform _GBACorePlatform(const struct mCore* core) {
return PLATFORM_GBA;
}
static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
UNUSED(core);
switch (feature) {
case mCORE_FEATURE_OPENGL:
#ifdef BUILD_GLES2
return true;
#else
return false;
#endif
default:
return false;
}
}
static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
struct GBA* gba = core->board;
gba->sync = sync;
@ -250,12 +276,20 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con
#ifndef DISABLE_THREADING
mCoreConfigCopyValue(&core->config, config, "threadedVideo");
#endif
mCoreConfigCopyValue(&core->config, config, "hwaccelVideo");
mCoreConfigCopyValue(&core->config, config, "videoScale");
}
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core);
*width = GBA_VIDEO_HORIZONTAL_PIXELS;
*height = GBA_VIDEO_VERTICAL_PIXELS;
#ifdef BUILD_GLES2
struct GBACore* gbacore = (struct GBACore*) core;
int scale = gbacore->glRenderer.scale;
#else
int scale = 1;
#endif
*width = GBA_VIDEO_HORIZONTAL_PIXELS * scale;
*height = GBA_VIDEO_VERTICAL_PIXELS * scale;
}
static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
@ -265,14 +299,24 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty));
}
static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
#ifdef BUILD_GLES2
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->renderer.d.getPixels(&gbacore->renderer.d, stride, buffer);
gbacore->glRenderer.outputTex = texid;
#else
UNUSED(core);
UNUSED(texid);
#endif
}
static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
struct GBA* gba = core->board;
gba->video.renderer->getPixels(gba->video.renderer, stride, buffer);
}
static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->renderer.d.putPixels(&gbacore->renderer.d, stride, buffer);
struct GBA* gba = core->board;
gba->video.renderer->putPixels(gba->video.renderer, stride, buffer);
}
static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
@ -311,7 +355,9 @@ static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
struct GBA* gba = core->board;
gba->stream = stream;
if (stream && stream->videoDimensionsChanged) {
stream->videoDimensionsChanged(stream, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
stream->videoDimensionsChanged(stream, width, height);
}
}
@ -387,16 +433,34 @@ static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChe
static void _GBACoreReset(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = (struct GBA*) core->board;
if (gbacore->renderer.outputBuffer) {
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
#ifndef DISABLE_THREADING
if (gbacore->renderer.outputBuffer
#ifdef BUILD_GLES2
|| gbacore->glRenderer.outputTex != (unsigned) -1
#endif
) {
struct GBAVideoRenderer* renderer;
if (gbacore->renderer.outputBuffer) {
renderer = &gbacore->renderer.d;
}
int fakeBool;
#ifdef BUILD_GLES2
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
renderer = &gbacore->glRenderer.d;
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
}
#endif
#ifndef DISABLE_THREADING
if (mCoreConfigGetIntValue(&core->config, "threadedVideo", &fakeBool) && fakeBool) {
gbacore->proxyRenderer.logger = &gbacore->threadProxy.d;
if (!core->videoLogger) {
core->videoLogger = &gbacore->threadProxy.d;
}
}
#endif
if (core->videoLogger) {
gbacore->proxyRenderer.logger = core->videoLogger;
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
renderer = &gbacore->proxyRenderer.d;
}
#endif
GBAVideoAssociateRenderer(&gba->video, renderer);
}
@ -924,10 +988,12 @@ struct mCore* GBACoreCreate(void) {
core->init = _GBACoreInit;
core->deinit = _GBACoreDeinit;
core->platform = _GBACorePlatform;
core->supportsFeature = _GBACoreSupportsFeature;
core->setSync = _GBACoreSetSync;
core->loadConfig = _GBACoreLoadConfig;
core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
core->setVideoBuffer = _GBACoreSetVideoBuffer;
core->setVideoGLTex = _GBACoreSetVideoGLTex;
core->getPixels = _GBACoreGetPixels;
core->putPixels = _GBACorePutPixels;
core->getAudioChannel = _GBACoreGetAudioChannel;

View File

@ -21,6 +21,7 @@ static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
@ -45,6 +46,7 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct
renderer->logger->context = renderer;
renderer->logger->parsePacket = _parsePacket;
renderer->logger->handleEvent = _handleEvent;
renderer->logger->vramBlock = _vramBlock;
renderer->logger->paletteSize = SIZE_PALETTE_RAM;
renderer->logger->vramSize = SIZE_VRAM;
@ -105,7 +107,11 @@ void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
_init(proxyRenderer);
_reset(proxyRenderer);
proxyRenderer->backend->init(proxyRenderer->backend);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->init(proxyRenderer->backend);
} else {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_INIT);
}
}
void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
@ -113,17 +119,45 @@ void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
_reset(proxyRenderer);
proxyRenderer->backend->reset(proxyRenderer->backend);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->reset(proxyRenderer->backend);
} else {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_RESET);
}
}
void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
proxyRenderer->backend->deinit(proxyRenderer->backend);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->deinit(proxyRenderer->backend);
} else {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
}
mVideoLoggerRendererDeinit(proxyRenderer->logger);
}
static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
switch (event) {
default:
break;
case LOGGER_EVENT_INIT:
proxyRenderer->backend->init(proxyRenderer->backend);
break;
case LOGGER_EVENT_DEINIT:
proxyRenderer->backend->deinit(proxyRenderer->backend);
break;
case LOGGER_EVENT_RESET:
proxyRenderer->backend->reset(proxyRenderer->backend);
break;
case LOGGER_EVENT_GET_PIXELS:
proxyRenderer->backend->getPixels(proxyRenderer->backend, &logger->pixelStride, &logger->pixelBuffer);
break;
}
}
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
switch (item->type) {
@ -151,6 +185,11 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
}
break;
case DIRTY_SCANLINE:
proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
}
@ -269,10 +308,13 @@ static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, si
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
}
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->unlock(proxyRenderer->logger);
*pixels = proxyRenderer->logger->pixelBuffer;
*stride = proxyRenderer->logger->pixelStride;
} else {
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
}
}

View File

@ -0,0 +1,33 @@
/* Copyright (c) 2013-2019 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/internal/gba/renderers/common.h>
#include <mgba/gba/interface.h>
int GBAVideoRendererCleanOAM(struct GBAObj* oam, struct GBAVideoRendererSprite* sprites, int offsetY) {
int i;
int oamMax = 0;
for (i = 0; i < 128; ++i) {
struct GBAObj obj;
LOAD_16LE(obj.a, 0, &oam[i].a);
LOAD_16LE(obj.b, 0, &oam[i].b);
LOAD_16LE(obj.c, 0, &oam[i].c);
if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1];
if (GBAObjAttributesAIsTransformed(obj.a)) {
height <<= GBAObjAttributesAGetDoubleSize(obj.a);
}
if (GBAObjAttributesAGetY(obj.a) < GBA_VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
int y = GBAObjAttributesAGetY(obj.a) + offsetY;
sprites[oamMax].y = y;
sprites[oamMax].endY = y + height;
sprites[oamMax].obj = obj;
++oamMax;
}
}
}
return oamMax;
}

1257
src/gba/renderers/gl.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,6 @@ static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackgroun
static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
@ -498,32 +497,6 @@ static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer,
#endif
}
static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
int i;
int oamMax = 0;
for (i = 0; i < 128; ++i) {
struct GBAObj obj;
LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1];
if (GBAObjAttributesAIsTransformed(obj.a)) {
height <<= GBAObjAttributesAGetDoubleSize(obj.a);
}
if (GBAObjAttributesAGetY(obj.a) < GBA_VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
int y = GBAObjAttributesAGetY(obj.a) + renderer->objOffsetY;
renderer->sprites[oamMax].y = y;
renderer->sprites[oamMax].endY = y + height;
renderer->sprites[oamMax].obj = obj;
++oamMax;
}
}
}
renderer->oamMax = oamMax;
renderer->oamDirty = 0;
}
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
@ -787,7 +760,7 @@ static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackgroun
}
static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
enum BlendEffect oldEffect = renderer->blendEffect;
enum GBAVideoBlendEffect oldEffect = renderer->blendEffect;
renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
@ -821,14 +794,15 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
int spriteLayers = 0;
if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
if (renderer->oamDirty) {
_cleanOAM(renderer);
renderer->oamMax = GBAVideoRendererCleanOAM(renderer->d.oam->obj, renderer->sprites, renderer->objOffsetY);
renderer->oamDirty = false;
}
renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
int mosaicY = y - (y % mosaicV);
int i;
for (i = 0; i < renderer->oamMax; ++i) {
struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
struct GBAVideoRendererSprite* sprite = &renderer->sprites[i];
int localY = y;
renderer->end = 0;
if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {

View File

@ -84,7 +84,9 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glClearColor(0.f, 0.f, 0.f, 1.f);
glGenBuffers(1, &context->vbo);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
struct mGLES2Uniform* uniforms = malloc(sizeof(struct mGLES2Uniform) * 4);
uniforms[0].name = "gamma";
@ -131,6 +133,15 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
uniforms[3].max.fvec3[2] = 1.0f;
mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
glBindVertexArray(context->initialShader.vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(context->finalShader.vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(0);
glDeleteFramebuffers(1, &context->finalShader.fbo);
glDeleteTextures(1, &context->finalShader.tex);
context->finalShader.fbo = 0;
@ -159,6 +170,7 @@ static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, u
static void mGLES2ContextDeinit(struct VideoBackend* v) {
struct mGLES2Context* context = (struct mGLES2Context*) v;
glDeleteTextures(1, &context->tex);
glDeleteBuffers(1, &context->vbo);
mGLES2ShaderDeinit(&context->initialShader);
mGLES2ShaderDeinit(&context->finalShader);
free(context->initialShader.uniforms);
@ -178,14 +190,13 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
drawW -= drawW % v->width;
drawH -= drawH % v->height;
}
glViewport(0, 0, w, h);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
}
static void mGLES2ContextClear(struct VideoBackend* v) {
UNUSED(v);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
@ -223,6 +234,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
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);
}
@ -239,8 +251,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
glUseProgram(shader->program);
glUniform1i(shader->texLocation, 0);
glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH);
glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
glEnableVertexAttribArray(shader->positionLocation);
glBindVertexArray(shader->vao);
size_t u;
for (u = 0; u < shader->nUniforms; ++u) {
struct mGLES2Uniform* uniform = &shader->uniforms[u];
@ -292,6 +303,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
break;
}
}
glEnableVertexAttribArray(shader->positionLocation);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindTexture(GL_TEXTURE_2D, shader->tex);
}
@ -420,6 +432,9 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
for (i = 0; i < shader->nUniforms; ++i) {
shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
}
glGenVertexArrays(1, &shader->vao);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
@ -428,6 +443,7 @@ void mGLES2ShaderDeinit(struct mGLES2Shader* shader) {
glDeleteShader(shader->fragmentShader);
glDeleteProgram(shader->program);
glDeleteFramebuffers(1, &shader->fbo);
glDeleteVertexArrays(1, &shader->vao);
}
void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) {
@ -442,8 +458,15 @@ void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shad
size_t i;
for (i = 0; i < nShaders; ++i) {
glBindFramebuffer(GL_FRAMEBUFFER, context->shaders[i].fbo);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(context->shaders[i].vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(context->shaders[i].positionLocation);
}
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

View File

@ -62,6 +62,7 @@ struct mGLES2Shader {
bool blend;
GLuint tex;
GLuint fbo;
GLuint vao;
GLuint fragmentShader;
GLuint vertexShader;
GLuint program;
@ -77,8 +78,7 @@ struct mGLES2Context {
struct VideoBackend d;
GLuint tex;
GLuint texLocation;
GLuint positionLocation;
GLuint vbo;
struct mGLES2Shader initialShader;
struct mGLES2Shader finalShader;

View File

@ -47,17 +47,6 @@ if(APPLE)
endif()
endif()
if(BUILD_GL)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c)
if(NOT WIN32 OR USE_EPOXY)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c)
endif()
endif()
if(BUILD_GLES2)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c)
endif()
get_target_property(QT_TYPE Qt5::Core TYPE)
if(QT_TYPE STREQUAL STATIC_LIBRARY)
set(QT_STATIC ON)
@ -121,6 +110,7 @@ set(SOURCE_FILES
utils.cpp
Window.cpp
VFileDevice.cpp
VideoProxy.cpp
VideoView.cpp)
set(UI_FILES
@ -244,7 +234,7 @@ if(NOT DEFINED DATADIR)
set(DATADIR ${CMAKE_INSTALL_DATADIR}/${BINARY_NAME})
endif()
endif()
if(BUILD_GL OR BUILD_GLES2)
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
endif()
install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
@ -290,7 +280,7 @@ add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}" COMPILE_OPTIONS "${FEATURE_FLAGS}")
list(APPEND QT_LIBRARIES Qt5::Widgets)
if(BUILD_GL OR BUILD_GLES2)
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
endif()
if(QT_STATIC)

View File

@ -40,16 +40,6 @@ CoreController::CoreController(mCore* core, QObject* parent)
m_threadContext.core = core;
m_threadContext.userData = this;
QSize size(256, 512);
m_buffers[0].resize(size.width() * size.height() * sizeof(color_t));
m_buffers[1].resize(size.width() * size.height() * sizeof(color_t));
m_buffers[0].fill(0xFF);
m_buffers[1].fill(0xFF);
m_activeBuffer = &m_buffers[0];
m_completeBuffer = m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());
m_resetActions.append([this]() {
if (m_autoload) {
mCoreLoadState(m_threadContext.core, 0, m_loadStateFlags);
@ -91,8 +81,10 @@ CoreController::CoreController(mCore* core, QObject* parent)
controller->m_resetActions.clear();
controller->m_activeBuffer = &controller->m_buffers[0];
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer->data()), controller->screenDimensions().width());
if (!controller->m_hwaccel) {
controller->m_activeBuffer = &controller->m_buffers[0];
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer->data()), controller->screenDimensions().width());
}
controller->finishFrame();
};
@ -211,6 +203,9 @@ CoreController::~CoreController() {
const color_t* CoreController::drawContext() {
QMutexLocker locker(&m_mutex);
if (m_hwaccel) {
return nullptr;
}
return reinterpret_cast<const color_t*>(m_completeBuffer.constData());
}
@ -340,6 +335,18 @@ void CoreController::setLogger(LogController* logger) {
}
void CoreController::start() {
if (!m_hwaccel) {
QSize size(1024, 2048);
m_buffers[0].resize(size.width() * size.height() * sizeof(color_t));
m_buffers[1].resize(size.width() * size.height() * sizeof(color_t));
m_buffers[0].fill(0xFF);
m_buffers[1].fill(0xFF);
m_activeBuffer = &m_buffers[0];
m_completeBuffer = m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());
}
if (!m_patched) {
mCoreAutoloadPatch(m_threadContext.core);
}
@ -800,6 +807,16 @@ void CoreController::endVideoLog() {
m_vl = nullptr;
}
void CoreController::setFramebufferHandle(int fb) {
Interrupter interrupter(this);
if (fb < 0) {
m_hwaccel = false;
} else {
m_threadContext.core->setVideoGLTex(m_threadContext.core, fb);
m_hwaccel = true;
}
}
void CoreController::updateKeys() {
int activeKeys = m_activeKeys | updateAutofire() | m_inputController->pollEvents();
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
@ -823,17 +840,18 @@ int CoreController::updateAutofire() {
void CoreController::finishFrame() {
QMutexLocker locker(&m_mutex);
memcpy(m_completeBuffer.data(), m_activeBuffer->constData(), m_activeBuffer->size());
if (!m_hwaccel) {
memcpy(m_completeBuffer.data(), m_activeBuffer->constData(), m_activeBuffer->size());
// TODO: Generalize this to triple buffering?
m_activeBuffer = &m_buffers[0];
if (m_activeBuffer == m_completeBuffer) {
m_activeBuffer = &m_buffers[1];
// TODO: Generalize this to triple buffering?
m_activeBuffer = &m_buffers[0];
if (m_activeBuffer == m_completeBuffer) {
m_activeBuffer = &m_buffers[1];
}
// Copy contents to avoid issues when doing frameskip
memcpy(m_activeBuffer->data(), m_completeBuffer.constData(), m_activeBuffer->size());
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width());
}
// Copy contents to avoid issues when doing frameskip
memcpy(m_activeBuffer->data(), m_completeBuffer.constData(), m_activeBuffer->size());
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width());
for (auto& action : m_frameActions) {
action();
}

View File

@ -46,6 +46,10 @@ public:
static const bool VIDEO_SYNC = false;
static const bool AUDIO_SYNC = true;
enum class Feature {
OPENGL = mCORE_FEATURE_OPENGL,
};
class Interrupter {
public:
Interrupter(CoreController*, bool fromThread = false);
@ -69,6 +73,8 @@ public:
mPlatform platform() const;
QSize screenDimensions() const;
bool supportsFeature(Feature feature) const { return m_threadContext.core->supportsFeature(m_threadContext.core, static_cast<mCoreFeature>(feature)); }
bool hardwareAccelerated() const { return m_hwaccel; }
void loadConfig(ConfigController*);
@ -154,6 +160,8 @@ public slots:
void startVideoLog(const QString& path);
void endVideoLog();
void setFramebufferHandle(int fb);
signals:
void started();
void paused();
@ -188,6 +196,7 @@ private:
QByteArray m_buffers[2];
QByteArray* m_activeBuffer;
QByteArray m_completeBuffer;
bool m_hwaccel = false;
std::unique_ptr<mCacheSet> m_cacheSet;
std::unique_ptr<Override> m_override;

View File

@ -18,13 +18,15 @@ Display::Driver Display::s_driver = Display::Driver::QT;
Display* Display::create(QWidget* parent) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
QSurfaceFormat format;
format.setSwapInterval(1);
format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
#endif
switch (s_driver) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
case Driver::OPENGL:
format.setVersion(3, 0);
return new DisplayGL(format, parent);
#endif
#ifdef BUILD_GL

View File

@ -19,6 +19,7 @@ struct VideoShader;
namespace QGBA {
class CoreController;
class VideoProxy;
class Display : public QWidget {
Q_OBJECT
@ -47,6 +48,8 @@ public:
virtual bool isDrawing() const = 0;
virtual bool supportsShaders() const = 0;
virtual VideoShader* shaders() = 0;
virtual VideoProxy* videoProxy() { return nullptr; }
virtual int framebufferHandle() { return -1; }
signals:
void showCursor();

View File

@ -10,15 +10,18 @@
#include "CoreController.h"
#include <QApplication>
#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QResizeEvent>
#include <QTimer>
#include <QWindow>
#include <mgba/core/core.h>
#include <mgba-util/math.h>
#ifdef BUILD_GL
#include "platform/opengl/gl.h"
#endif
#if !defined(_WIN32) || defined(USE_EPOXY)
#ifdef BUILD_GLES2
#include "platform/opengl/gles2.h"
#ifdef _WIN32
#include <epoxy/wgl.h>
@ -27,22 +30,27 @@
using namespace QGBA;
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
: Display(parent)
, m_gl(nullptr)
{
// This can spontaneously re-enter into this->resizeEvent before creation is done, so we
// need to make sure it's initialized to nullptr before we assign the new object to it
m_gl = new EmptyGLWidget(format, this);
m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl);
m_gl->setMouseTracking(true);
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
m_gl = new QOpenGLContext;
m_gl->setFormat(format);
m_gl->create();
setAttribute(Qt::WA_NativeWindow);
m_painter = new PainterGL(&m_videoProxy, windowHandle(), m_gl);
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
connect(&m_videoProxy, &VideoProxy::dataAvailable, &m_videoProxy, &VideoProxy::processData);
connect(&m_videoProxy, &VideoProxy::eventPosted, &m_videoProxy, &VideoProxy::handleEvent);
}
DisplayGL::~DisplayGL() {
stopDrawing();
delete m_painter;
delete m_gl;
}
bool DisplayGL::supportsShaders() const {
@ -68,12 +76,12 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
m_painter->setMessagePainter(messagePainter());
m_context = controller;
m_painter->resize(size());
m_gl->move(0, 0);
m_drawThread = new QThread(this);
m_drawThread->setObjectName("Painter Thread");
m_gl->context()->doneCurrent();
m_gl->context()->moveToThread(m_drawThread);
m_gl->doneCurrent();
m_gl->moveToThread(m_drawThread);
m_painter->moveToThread(m_drawThread);
m_videoProxy.moveToThread(m_drawThread);
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
m_drawThread->start();
@ -95,6 +103,11 @@ void DisplayGL::stopDrawing() {
QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
m_drawThread->exit();
m_drawThread = nullptr;
m_gl->makeCurrent(windowHandle());
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
}
m_context.reset();
}
@ -176,33 +189,45 @@ void DisplayGL::resizeEvent(QResizeEvent* event) {
}
void DisplayGL::resizePainter() {
if (m_gl) {
m_gl->resize(size());
}
if (m_drawThread) {
QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));
}
}
PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
VideoProxy* DisplayGL::videoProxy() {
if (supportsShaders()) {
return &m_videoProxy;
}
return nullptr;
}
int DisplayGL::framebufferHandle() {
return m_painter->glTex();
}
PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent)
: m_gl(parent)
, m_surface(surface)
, m_videoProxy(proxy)
{
#ifdef BUILD_GL
mGLContext* glBackend;
#endif
#if !defined(_WIN32) || defined(USE_EPOXY)
#ifdef BUILD_GLES2
mGLES2Context* gl2Backend;
#endif
m_gl->makeCurrent();
m_gl->makeCurrent(m_surface);
m_window = new QOpenGLPaintDevice;
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
int majorVersion = m_gl->format().majorVersion();
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
#if !defined(_WIN32) || defined(USE_EPOXY)
if (extensions.contains("GL_ARB_framebuffer_object") && majorVersion >= 2) {
#ifdef BUILD_GLES2
if ((majorVersion == 2 && extensions.contains("GL_ARB_framebuffer_object")) || majorVersion > 2) {
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
mGLES2ContextCreate(gl2Backend);
m_backend = &gl2Backend->d;
@ -220,14 +245,14 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
#endif
m_backend->swap = [](VideoBackend* v) {
PainterGL* painter = static_cast<PainterGL*>(v->user);
if (!painter->m_gl->isVisible()) {
if (!painter->m_gl->isValid()) {
return;
}
painter->m_gl->swapBuffers();
painter->m_gl->swapBuffers(painter->m_gl->surface());
};
m_backend->init(m_backend, reinterpret_cast<WHandle>(m_gl->winId()));
#if !defined(_WIN32) || defined(USE_EPOXY)
m_backend->init(m_backend, 0);
#ifdef BUILD_GLES2
if (m_supportsShaders) {
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
}
@ -239,7 +264,7 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
m_backend->lockAspectRatio = false;
for (int i = 0; i < 2; ++i) {
m_free.append(new uint32_t[256 * 256]);
m_free.append(new uint32_t[1024 * 2048]);
}
}
@ -250,11 +275,11 @@ PainterGL::~PainterGL() {
for (auto item : m_free) {
delete[] item;
}
m_gl->makeCurrent();
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
#if !defined(_WIN32) || defined(USE_EPOXY)
#ifdef BUILD_GLES2
if (m_shader.passes) {
mGLES2ShaderFree(&m_shader);
}
@ -263,6 +288,7 @@ PainterGL::~PainterGL() {
m_gl->doneCurrent();
free(m_backend);
m_backend = nullptr;
delete m_window;
}
void PainterGL::setContext(std::shared_ptr<CoreController> context) {
@ -275,13 +301,18 @@ void PainterGL::resizeContext() {
return;
}
m_gl->makeCurrent();
if (!m_active) {
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
epoxy_handle_external_wglMakeCurrent();
#endif
}
QSize size = m_context->screenDimensions();
m_backend->setDimensions(m_backend, size.width(), size.height());
m_gl->doneCurrent();
if (!m_active) {
m_gl->doneCurrent();
}
}
void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
@ -297,16 +328,12 @@ void PainterGL::resize(const QSize& size) {
void PainterGL::lockAspectRatio(bool lock) {
m_backend->lockAspectRatio = lock;
if (m_started && !m_active) {
forceDraw();
}
resize(m_size);
}
void PainterGL::lockIntegerScaling(bool lock) {
m_backend->lockIntegerScaling = lock;
if (m_started && !m_active) {
forceDraw();
}
resize(m_size);
}
void PainterGL::filter(bool filter) {
@ -317,18 +344,17 @@ void PainterGL::filter(bool filter) {
}
void PainterGL::start() {
m_gl->makeCurrent();
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
#if !defined(_WIN32) || defined(USE_EPOXY)
#ifdef BUILD_GLES2
if (m_supportsShaders && m_shader.passes) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
}
#endif
m_gl->doneCurrent();
m_active = true;
m_started = true;
}
@ -341,13 +367,13 @@ void PainterGL::draw() {
if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) {
dequeue();
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
m_painter.begin(m_gl->context()->device());
m_painter.begin(m_window);
performDraw();
m_painter.end();
m_backend->swap(m_backend);
if (!m_delayTimer.isValid()) {
m_delayTimer.start();
} else {
} else if (m_gl->format().swapInterval()) {
while (m_delayTimer.elapsed() < 15) {
QThread::usleep(100);
}
@ -362,7 +388,7 @@ void PainterGL::draw() {
}
void PainterGL::forceDraw() {
m_painter.begin(m_gl->context()->device());
m_painter.begin(m_window);
performDraw();
m_painter.end();
m_backend->swap(m_backend);
@ -371,17 +397,14 @@ void PainterGL::forceDraw() {
void PainterGL::stop() {
m_active = false;
m_started = false;
m_gl->makeCurrent();
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
dequeueAll();
m_backend->clear(m_backend);
m_backend->swap(m_backend);
m_gl->doneCurrent();
m_gl->context()->moveToThread(m_gl->thread());
m_gl->moveToThread(m_surface->thread());
m_context.reset();
moveToThread(m_gl->thread());
m_videoProxy->moveToThread(m_gl->thread());
}
void PainterGL::pause() {
@ -394,11 +417,7 @@ void PainterGL::unpause() {
void PainterGL::performDraw() {
m_painter.beginNativePainting();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
float r = m_gl->devicePixelRatioF();
#else
float r = m_gl->devicePixelRatio();
#endif
float r = m_surface->devicePixelRatio();
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
m_backend->drawFrame(m_backend);
m_painter.endNativePainting();
@ -409,14 +428,16 @@ void PainterGL::performDraw() {
void PainterGL::enqueue(const uint32_t* backing) {
m_mutex.lock();
uint32_t* buffer;
if (m_free.isEmpty()) {
buffer = m_queue.dequeue();
} else {
buffer = m_free.takeLast();
uint32_t* buffer = nullptr;
if (backing) {
if (m_free.isEmpty()) {
buffer = m_queue.dequeue();
} else {
buffer = m_free.takeLast();
}
QSize size = m_context->screenDimensions();
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
}
QSize size = m_context->screenDimensions();
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
m_queue.enqueue(buffer);
m_mutex.unlock();
}
@ -428,8 +449,10 @@ void PainterGL::dequeue() {
return;
}
uint32_t* buffer = m_queue.dequeue();
m_backend->postFrame(m_backend, buffer);
m_free.append(buffer);
if (buffer) {
m_backend->postFrame(m_backend, buffer);
m_free.append(buffer);
}
m_mutex.unlock();
}
@ -438,7 +461,9 @@ void PainterGL::dequeueAll() {
m_mutex.lock();
while (!m_queue.isEmpty()) {
buffer = m_queue.dequeue();
m_free.append(buffer);
if (buffer) {
m_free.append(buffer);
}
}
if (buffer) {
m_backend->postFrame(m_backend, buffer);
@ -450,11 +475,13 @@ void PainterGL::setShaders(struct VDir* dir) {
if (!supportsShaders()) {
return;
}
#if !defined(_WIN32) || defined(USE_EPOXY)
m_gl->makeCurrent();
#ifdef BUILD_GLES2
if (!m_active) {
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
epoxy_handle_external_wglMakeCurrent();
#endif
}
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
@ -463,7 +490,9 @@ void PainterGL::setShaders(struct VDir* dir) {
if (m_started) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
}
m_gl->doneCurrent();
if (!m_active) {
m_gl->doneCurrent();
}
#endif
}
@ -471,16 +500,20 @@ void PainterGL::clearShaders() {
if (!supportsShaders()) {
return;
}
#if !defined(_WIN32) || defined(USE_EPOXY)
m_gl->makeCurrent();
#ifdef BUILD_GLES2
if (!m_active) {
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
epoxy_handle_external_wglMakeCurrent();
#endif
}
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
}
m_gl->doneCurrent();
if (!m_active) {
m_gl->doneCurrent();
}
#endif
}
@ -488,4 +521,19 @@ VideoShader* PainterGL::shaders() {
return &m_shader;
}
int PainterGL::glTex() {
#ifdef BUILD_GLES2
if (supportsShaders()) {
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
return gl2Backend->tex;
}
#endif
#ifdef BUILD_GL
mGLContext* glBackend = reinterpret_cast<mGLContext*>(m_backend);
return glBackend->tex;
#else
return -1;
#endif
}
#endif

View File

@ -17,38 +17,33 @@
#endif
#include <QElapsedTimer>
#include <QGLWidget>
#include <QOpenGLContext>
#include <QList>
#include <QMouseEvent>
#include <QPainter>
#include <QQueue>
#include <QThread>
#include "VideoProxy.h"
#include "platform/video-backend.h"
namespace QGBA {
class EmptyGLWidget : public QGLWidget {
public:
EmptyGLWidget(const QGLFormat& format, QWidget* parent) : QGLWidget(format, parent) { setAutoBufferSwap(false); }
protected:
void paintEvent(QPaintEvent* event) override { event->ignore(); }
void resizeEvent(QResizeEvent*) override {}
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
};
class PainterGL;
class DisplayGL : public Display {
Q_OBJECT
public:
DisplayGL(const QGLFormat& format, QWidget* parent = nullptr);
DisplayGL(const QSurfaceFormat& format, QWidget* parent = nullptr);
~DisplayGL();
void startDrawing(std::shared_ptr<CoreController>) override;
bool isDrawing() const override { return m_isDrawing; }
bool supportsShaders() const override;
VideoShader* shaders() override;
VideoProxy* videoProxy() override;
int framebufferHandle() override;
public slots:
void stopDrawing() override;
@ -71,17 +66,18 @@ private:
void resizePainter();
bool m_isDrawing = false;
QGLWidget* m_gl;
QOpenGLContext* m_gl;
PainterGL* m_painter;
QThread* m_drawThread = nullptr;
std::shared_ptr<CoreController> m_context;
VideoProxy m_videoProxy;
};
class PainterGL : public QObject {
Q_OBJECT
public:
PainterGL(int majorVersion, QGLWidget* parent);
PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent);
~PainterGL();
void setContext(std::shared_ptr<CoreController>);
@ -107,6 +103,8 @@ public slots:
void clearShaders();
VideoShader* shaders();
int glTex();
private:
void performDraw();
void dequeue();
@ -116,7 +114,9 @@ private:
QQueue<uint32_t*> m_queue;
QPainter m_painter;
QMutex m_mutex;
QGLWidget* m_gl;
QWindow* m_surface;
QPaintDevice* m_window;
QOpenGLContext* m_gl;
bool m_active = false;
bool m_started = false;
std::shared_ptr<CoreController> m_context = nullptr;
@ -126,6 +126,7 @@ private:
QSize m_size;
MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer;
VideoProxy* m_videoProxy;
};
}

View File

@ -401,6 +401,7 @@ void SettingsView::updateConfig() {
saveSetting("logToStdout", m_ui.logToStdout);
saveSetting("logFile", m_ui.logFile);
saveSetting("useDiscordPresence", m_ui.useDiscordPresence);
saveSetting("audioHle", m_ui.audioHle);
if (m_ui.fastForwardUnbounded->isChecked()) {
saveSetting("fastForwardRatio", "-1");
@ -465,6 +466,14 @@ void SettingsView::updateConfig() {
emit languageChanged();
}
int videoScale = m_controller->getOption("videoScale", 1).toInt();
int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt();
if (videoScale != m_ui.videoScale->value() || hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) {
emit videoRendererChanged();
}
saveSetting("videoScale", m_ui.videoScale);
saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex());
m_logModel.save(m_controller);
m_logModel.logger()->setLogFile(m_ui.logFile->text());
m_logModel.logger()->logToFile(m_ui.logToFile->isChecked());
@ -541,6 +550,8 @@ void SettingsView::reloadConfig() {
loadSetting("logToStdout", m_ui.logToStdout);
loadSetting("logFile", m_ui.logFile);
loadSetting("useDiscordPresence", m_ui.useDiscordPresence);
loadSetting("audioHle", m_ui.audioHle);
loadSetting("videoScale", m_ui.videoScale, 1);
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
@ -604,6 +615,9 @@ void SettingsView::reloadConfig() {
m_ui.cgbModel->setCurrentIndex(index >= 0 ? index : 0);
}
#endif
int hwaccelVideo = m_controller->getOption("hwaccelVideo", 0).toInt();
m_ui.hwaccelVideo->setCurrentIndex(hwaccelVideo);
}
void SettingsView::saveSetting(const char* key, const QAbstractButton* field) {
@ -660,9 +674,9 @@ void SettingsView::loadSetting(const char* key, QSlider* field, int defaultVal)
field->setValue(option.isNull() ? defaultVal : option.toInt());
}
void SettingsView::loadSetting(const char* key, QSpinBox* field) {
void SettingsView::loadSetting(const char* key, QSpinBox* field, int defaultVal) {
QString option = loadSetting(key);
field->setValue(option.toInt());
field->setValue(option.isNull() ? defaultVal : option.toInt());
}
QString SettingsView::loadSetting(const char* key) {

View File

@ -40,6 +40,7 @@ signals:
void displayDriverChanged();
void cameraDriverChanged();
void cameraChanged(const QByteArray&);
void videoRendererChanged();
void pathsChanged();
void languageChanged();
void libraryCleared();
@ -76,7 +77,7 @@ private:
void loadSetting(const char* key, QDoubleSpinBox*);
void loadSetting(const char* key, QLineEdit*);
void loadSetting(const char* key, QSlider*, int defaultVal = 0);
void loadSetting(const char* key, QSpinBox*);
void loadSetting(const char* key, QSpinBox*, int defaultVal = 0);
QString loadSetting(const char* key);
};

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>588</width>
<height>488</height>
<width>790</width>
<height>686</height>
</rect>
</property>
<property name="sizePolicy">
@ -33,14 +33,14 @@
<item row="1" column="0">
<widget class="QListWidget" name="tabs">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>140</width>
<width>200</width>
<height>16777215</height>
</size>
</property>
@ -62,6 +62,11 @@
<string>Emulation</string>
</property>
</item>
<item>
<property name="text">
<string>Enhancements</string>
</property>
</item>
<item>
<property name="text">
<string>BIOS</string>
@ -844,6 +849,70 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="enhancements">
<layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Video renderer:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="hwaccelVideo">
<item>
<property name="text">
<string>Software</string>
</property>
</item>
<item>
<property name="text">
<string>OpenGL</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="oglEnahance">
<property name="title">
<string>OpenGL enhancements</string>
</property>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_37">
<property name="text">
<string>High-resolution scale:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="videoScale">
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>13</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="audioHle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>XQ GBA audio (experimental)</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="bios">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
@ -1221,12 +1290,12 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="loggingView">
<attribute name="horizontalHeaderDefaultSectionSize">
<number>77</number>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>0</number>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>77</number>
</attribute>
</widget>
</item>
<item>

View File

@ -0,0 +1,106 @@
/* Copyright (c) 2013-2018 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 "VideoProxy.h"
#include "CoreController.h"
using namespace QGBA;
VideoProxy::VideoProxy() {
mVideoLoggerRendererCreate(&m_logger.d, false);
m_logger.d.block = true;
m_logger.d.init = &cbind<&VideoProxy::init>;
m_logger.d.reset = &cbind<&VideoProxy::reset>;
m_logger.d.deinit = &cbind<&VideoProxy::deinit>;
m_logger.d.lock = &cbind<&VideoProxy::lock>;
m_logger.d.unlock = &cbind<&VideoProxy::unlock>;
m_logger.d.wait = &cbind<&VideoProxy::wait>;
m_logger.d.wake = &callback<void, int>::func<&VideoProxy::wake>;
m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>;
m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>;
m_logger.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
}
void VideoProxy::attach(CoreController* controller) {
CoreController::Interrupter interrupter(controller);
controller->thread()->core->videoLogger = &m_logger.d;
}
void VideoProxy::processData() {
mVideoLoggerRendererRun(&m_logger.d, false);
m_fromThreadCond.wakeAll();
}
void VideoProxy::init() {
RingFIFOInit(&m_dirtyQueue, 0x80000);
}
void VideoProxy::reset() {
RingFIFOClear(&m_dirtyQueue);
}
void VideoProxy::deinit() {
RingFIFODeinit(&m_dirtyQueue);
}
bool VideoProxy::writeData(const void* data, size_t length) {
while (!RingFIFOWrite(&m_dirtyQueue, data, length)) {
emit dataAvailable();
m_mutex.lock();
m_toThreadCond.wakeAll();
m_fromThreadCond.wait(&m_mutex);
m_mutex.unlock();
}
emit dataAvailable();
return true;
}
bool VideoProxy::readData(void* data, size_t length, bool block) {
bool read = false;
while (true) {
read = RingFIFORead(&m_dirtyQueue, data, length);
if (!block || read) {
break;
}
m_mutex.lock();
m_fromThreadCond.wakeAll();
m_toThreadCond.wait(&m_mutex);
m_mutex.unlock();
}
return read;
}
void VideoProxy::postEvent(enum mVideoLoggerEvent event) {
emit eventPosted(event);
}
void VideoProxy::handleEvent(int event) {
m_logger.d.handleEvent(&m_logger.d, static_cast<enum mVideoLoggerEvent>(event));
}
void VideoProxy::lock() {
m_mutex.lock();
}
void VideoProxy::unlock() {
m_mutex.unlock();
}
void VideoProxy::wait() {
while (RingFIFOSize(&m_dirtyQueue)) {
emit dataAvailable();
m_toThreadCond.wakeAll();
m_fromThreadCond.wait(&m_mutex, 1);
}
}
void VideoProxy::wake(int y) {
if ((y & 15) == 15) {
m_toThreadCond.wakeAll();
}
}

View File

@ -0,0 +1,71 @@
/* Copyright (c) 2013-2018 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/. */
#pragma once
#include <QMutex>
#include <QObject>
#include <QWaitCondition>
#include <mgba/feature/video-logger.h>
#include <mgba-util/ring-fifo.h>
namespace QGBA {
class CoreController;
class VideoProxy : public QObject {
Q_OBJECT
public:
VideoProxy();
void attach(CoreController*);
signals:
void dataAvailable();
void eventPosted(int);
public slots:
void processData();
void handleEvent(int);
private:
void init();
void reset();
void deinit();
bool writeData(const void* data, size_t length);
bool readData(void* data, size_t length, bool block);
void postEvent(enum mVideoLoggerEvent event);
void lock();
void unlock();
void wait();
void wake(int y);
template<typename T, typename... A> struct callback {
using type = T (VideoProxy::*)(A...);
template<type F> static T func(mVideoLogger* logger, A... args) {
VideoProxy* proxy = reinterpret_cast<Logger*>(logger)->p;
return (proxy->*F)(args...);
}
};
template<void (VideoProxy::*F)()> static void cbind(mVideoLogger* logger) { callback<void>::func<F>(logger); }
struct Logger {
mVideoLogger d;
VideoProxy* p;
} m_logger = {{}, this};
RingFIFO m_dirtyQueue;
QMutex m_mutex;
QWaitCondition m_toThreadCond;
QWaitCondition m_fromThreadCond;
};
}

View File

@ -52,6 +52,7 @@
#include "ShaderSelector.h"
#include "ShortcutController.h"
#include "TileView.h"
#include "VideoProxy.h"
#include "VideoView.h"
#ifdef USE_DISCORD_RPC
@ -459,6 +460,7 @@ void Window::openSettingsWindow() {
connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver);
connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera);
connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig);
#ifdef USE_SQLITE3
@ -716,9 +718,6 @@ void Window::gameStarted() {
if (m_savedScale > 0) {
resizeFrame(size * m_savedScale);
}
if (!m_display) {
reloadDisplayDriver();
}
attachWidget(m_display.get());
m_display->setMinimumSize(size);
setFocus();
@ -853,6 +852,10 @@ void Window::unimplementedBiosCall(int call) {
void Window::reloadDisplayDriver() {
if (m_controller) {
if (m_controller->hardwareAccelerated()) {
mustRestart();
return;
}
m_display->stopDrawing();
detachWidget(m_display.get());
}
@ -1713,6 +1716,21 @@ void Window::setController(CoreController* controller, const QString& fname) {
appendMRU(fname);
}
if (!m_display) {
reloadDisplayDriver();
}
if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && controller->supportsFeature(CoreController::Feature::OPENGL)) {
if (m_display->videoProxy()) {
m_display->videoProxy()->attach(controller);
}
int fb = m_display->framebufferHandle();
if (fb >= 0) {
controller->setFramebufferHandle(fb);
}
}
m_controller = std::shared_ptr<CoreController>(controller);
m_inputController.recalibrateAxes();
m_controller->setInputController(&m_inputController);

View File

@ -17,6 +17,10 @@
#include <QLibraryInfo>
#include <QTranslator>
#ifdef BUILD_GLES2
#include <QSurfaceFormat>
#endif
#ifdef QT_STATIC
#include <QtPlugin>
#ifdef Q_OS_WIN
@ -65,6 +69,12 @@ int main(int argc, char* argv[]) {
QApplication::setApplicationName(projectName);
QApplication::setApplicationVersion(projectVersion);
#ifdef BUILD_GLES2
QSurfaceFormat format;
format.setVersion(3, 0);
QSurfaceFormat::setDefaultFormat(format);
#endif
GBAApp application(argc, argv, &configController);
#ifndef Q_OS_MAC

View File

@ -77,12 +77,11 @@ if(BUILD_PANDORA)
else()
if(BUILD_GL)
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-sdl.c)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c)
include_directories(${OPENGL_INCLUDE_DIR})
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c)
endif()
if(BUILD_GLES2)
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c)
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c)
include_directories(${OPENGLES2_INCLUDE_DIR})
endif()
if(NOT BUILD_GL AND NOT BUILD_GLES2)

View File

@ -35,7 +35,9 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
#endif
size_t size = renderer->width * renderer->height * BYTES_PER_PIXEL;
#ifndef __APPLE__
#ifdef _WIN32
renderer->outputBuffer = _aligned_malloc(size, 16);
#elif !defined(__APPLE__)
renderer->outputBuffer = memalign(16, size);
#else
posix_memalign((void**) &renderer->outputBuffer, 16, size);

View File

@ -90,6 +90,12 @@ int main(int argc, char** argv) {
freeArguments(&args);
return 1;
}
if (!renderer.core->init(renderer.core)) {
freeArguments(&args);
return 1;
}
renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height);
#ifdef BUILD_GL
mSDLGLCreate(&renderer);
@ -106,11 +112,6 @@ int main(int argc, char** argv) {
opts.width = renderer.width * renderer.ratio;
opts.height = renderer.height * renderer.ratio;
if (!renderer.core->init(renderer.core)) {
freeArguments(&args);
return 1;
}
struct mCheatDevice* device = NULL;
if (args.cheatsFile && (device = renderer.core->cheatDevice(renderer.core))) {
struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);

View File

@ -31,6 +31,7 @@ while [ $# -gt 0 ]; do
rmdep libav
rmdep libedit
rmdep libelf
rmdep libgl
rmdep libpng
rmdep libzip
rmdep libmagickwand
@ -45,6 +46,7 @@ while [ $# -gt 0 ]; do
rmdep libav
rmdep libedit
rmdep libelf
rmdep libgl
rmdep libpng
rmdep qt
rmdep libzip