mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
7427eedd2a
|
@ -58,7 +58,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")
|
||||
|
@ -452,7 +452,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)
|
||||
|
@ -467,9 +467,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)
|
||||
|
@ -479,6 +479,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)
|
||||
|
@ -745,12 +755,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)
|
||||
|
|
|
@ -92,7 +92,7 @@ Other Unix-like platforms, such as OpenBSD, are known to work as well, but are u
|
|||
|
||||
### System requirements
|
||||
|
||||
Requirements are minimal[<sup>[2]</sup>](#dscaveat). 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[<sup>[2]</sup>](#dscaveat). 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
|
||||
---------
|
||||
|
|
|
@ -50,6 +50,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;
|
||||
|
@ -67,12 +68,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);
|
||||
|
|
|
@ -80,6 +80,10 @@ enum mColorFormat {
|
|||
mCOLOR_ANY = -1
|
||||
};
|
||||
|
||||
enum mCoreFeature {
|
||||
mCORE_FEATURE_OPENGL = 1,
|
||||
};
|
||||
|
||||
struct mCoreCallbacks {
|
||||
void* context;
|
||||
void (*videoFrameStarted)(void* context);
|
||||
|
|
|
@ -29,6 +29,7 @@ struct mVideoThreadProxy {
|
|||
Condition toThreadCond;
|
||||
Mutex mutex;
|
||||
enum mVideoThreadProxyState threadState;
|
||||
enum mVideoLoggerEvent event;
|
||||
|
||||
struct RingFIFO dirtyQueue;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, int masterHeight, bool combinedObjSort);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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;
|
||||
|
@ -52,13 +47,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
|
||||
|
@ -90,20 +78,6 @@ struct WindowRegion {
|
|||
int 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;
|
||||
|
@ -138,7 +112,7 @@ struct GBAVideoSoftwareRenderer {
|
|||
unsigned target2Obj;
|
||||
unsigned target2Bd;
|
||||
bool blendDirty;
|
||||
enum BlendEffect blendEffect;
|
||||
enum GBAVideoBlendEffect blendEffect;
|
||||
color_t normalPalette[512];
|
||||
color_t variantPalette[512];
|
||||
color_t* objExtPalette;
|
||||
|
@ -151,8 +125,8 @@ struct GBAVideoSoftwareRenderer {
|
|||
GBAMosaicControl mosaic;
|
||||
|
||||
struct WindowN {
|
||||
struct WindowRegion h;
|
||||
struct WindowRegion v;
|
||||
struct GBAVideoWindowRegion h;
|
||||
struct GBAVideoWindowRegion v;
|
||||
struct WindowControl control;
|
||||
} winN[2];
|
||||
|
||||
|
@ -168,7 +142,7 @@ struct GBAVideoSoftwareRenderer {
|
|||
|
||||
int oamDirty;
|
||||
int oamMax;
|
||||
struct GBAVideoSoftwareSprite sprites[128];
|
||||
struct GBAVideoRendererSprite sprites[128];
|
||||
int tileStride;
|
||||
int bitmapStride;
|
||||
bool combinedObjSort;
|
||||
|
|
|
@ -35,19 +35,26 @@ enum {
|
|||
VRAM_BLOCK_MASK = 0x3FFF
|
||||
};
|
||||
|
||||
enum ObjMode {
|
||||
enum GBAVideoObjMode {
|
||||
OBJ_MODE_NORMAL = 0,
|
||||
OBJ_MODE_SEMITRANSPARENT = 1,
|
||||
OBJ_MODE_OBJWIN = 2,
|
||||
OBJ_MODE_BITMAP = 3
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -94,6 +101,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)
|
||||
|
@ -148,6 +160,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);
|
||||
|
|
|
@ -143,6 +143,16 @@ static enum mPlatform _DSCorePlatform(const struct mCore* core) {
|
|||
return PLATFORM_DS;
|
||||
}
|
||||
|
||||
static bool _DSCoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
|
||||
UNUSED(core);
|
||||
switch (feature) {
|
||||
case mCORE_FEATURE_OPENGL:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void _DSCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
|
||||
struct DS* ds = core->board;
|
||||
ds->sync = sync;
|
||||
|
@ -628,6 +638,7 @@ struct mCore* DSCoreCreate(void) {
|
|||
core->init = _DSCoreInit;
|
||||
core->deinit = _DSCoreDeinit;
|
||||
core->platform = _DSCorePlatform;
|
||||
core->supportsFeature = _DSCoreSupportsFeature;
|
||||
core->setSync = _DSCoreSetSync;
|
||||
core->loadConfig = _DSCoreLoadConfig;
|
||||
core->desiredVideoDimensions = _DSCoreDesiredVideoDimensions;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -147,6 +147,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;
|
||||
|
@ -242,6 +250,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);
|
||||
|
@ -906,10 +919,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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -127,6 +130,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;
|
||||
|
@ -155,6 +161,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;
|
||||
|
@ -171,6 +178,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
|
||||
|
@ -214,6 +226,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;
|
||||
|
@ -258,12 +284,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) {
|
||||
|
@ -273,14 +307,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) {
|
||||
|
@ -319,7 +363,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);
|
||||
}
|
||||
if (stream && stream->videoFrameRateChanged) {
|
||||
stream->videoFrameRateChanged(stream, core->frameCycles(core), core->frequency(core));
|
||||
|
@ -398,16 +444,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);
|
||||
}
|
||||
|
||||
|
@ -946,10 +1010,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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -119,7 +121,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) {
|
||||
|
@ -127,17 +133,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) {
|
||||
|
@ -165,6 +199,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);
|
||||
}
|
||||
|
@ -287,10 +326,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* 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 masterHeight, bool combinedObjSort) {
|
||||
int i;
|
||||
int p;
|
||||
int oamMax = 0;
|
||||
int maxP = 1;
|
||||
if (combinedObjSort) {
|
||||
maxP = 4;
|
||||
}
|
||||
for (p = 0; p < maxP; ++p) {
|
||||
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 (combinedObjSort && GBAObjAttributesCGetPriority(obj.c) != p) {
|
||||
continue;
|
||||
}
|
||||
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) < masterHeight || GBAObjAttributesAGetY(obj.a) + height >= 256) {
|
||||
int y = GBAObjAttributesAGetY(obj.a) + offsetY;
|
||||
sprites[oamMax].y = y;
|
||||
sprites[oamMax].endY = y + height;
|
||||
sprites[oamMax].obj = obj;
|
||||
++oamMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return oamMax;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,7 @@ 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);
|
||||
|
||||
|
@ -516,42 +516,6 @@ static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer,
|
|||
#endif
|
||||
}
|
||||
|
||||
static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
|
||||
int i;
|
||||
int p;
|
||||
int oamMax = 0;
|
||||
int maxP = 1;
|
||||
if (renderer->combinedObjSort) {
|
||||
maxP = 4;
|
||||
}
|
||||
for (p = 0; p < maxP; ++p) {
|
||||
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 (renderer->combinedObjSort && GBAObjAttributesCGetPriority(obj.c) != p) {
|
||||
continue;
|
||||
}
|
||||
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) < renderer->masterHeight || GBAObjAttributesAGetY(obj.a) + height >= 256) {
|
||||
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;
|
||||
|
||||
|
@ -837,7 +801,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);
|
||||
|
@ -967,14 +931,15 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere
|
|||
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->masterHeight, renderer->combinedObjSort);
|
||||
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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -48,17 +48,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)
|
||||
|
@ -119,6 +108,7 @@ set(SOURCE_FILES
|
|||
utils.cpp
|
||||
Window.cpp
|
||||
VFileDevice.cpp
|
||||
VideoProxy.cpp
|
||||
VideoView.cpp
|
||||
input/InputController.cpp
|
||||
input/InputIndex.cpp
|
||||
|
@ -247,7 +237,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)
|
||||
|
@ -293,7 +283,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)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -345,6 +340,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);
|
||||
}
|
||||
|
@ -788,6 +795,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_inputController->updateAutofire() | m_inputController->pollEvents();
|
||||
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
|
||||
|
@ -795,17 +812,18 @@ void CoreController::updateKeys() {
|
|||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -70,6 +74,8 @@ public:
|
|||
mPlatform platform() const;
|
||||
QSize screenDimensions() const;
|
||||
QPair<unsigned, unsigned> frameRate() 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*);
|
||||
|
||||
|
@ -151,6 +157,8 @@ public slots:
|
|||
void startVideoLog(const QString& path);
|
||||
void endVideoLog();
|
||||
|
||||
void setFramebufferHandle(int fb);
|
||||
|
||||
signals:
|
||||
void started();
|
||||
void paused();
|
||||
|
@ -184,6 +192,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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
QSize viewportSize();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -71,12 +79,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();
|
||||
|
||||
|
@ -99,6 +107,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();
|
||||
}
|
||||
|
@ -180,33 +193,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;
|
||||
|
@ -224,14 +249,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);
|
||||
}
|
||||
|
@ -243,7 +268,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 * 384]);
|
||||
m_free.append(new uint32_t[256 * 512]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,11 +279,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);
|
||||
}
|
||||
|
@ -267,6 +292,7 @@ PainterGL::~PainterGL() {
|
|||
m_gl->doneCurrent();
|
||||
free(m_backend);
|
||||
m_backend = nullptr;
|
||||
delete m_window;
|
||||
}
|
||||
|
||||
void PainterGL::setContext(std::shared_ptr<CoreController> context) {
|
||||
|
@ -279,13 +305,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) {
|
||||
|
@ -301,16 +332,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) {
|
||||
|
@ -321,18 +348,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;
|
||||
}
|
||||
|
@ -345,13 +371,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);
|
||||
}
|
||||
|
@ -366,7 +392,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);
|
||||
|
@ -375,17 +401,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() {
|
||||
|
@ -398,11 +421,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();
|
||||
|
@ -413,14 +432,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();
|
||||
}
|
||||
|
@ -432,8 +453,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();
|
||||
}
|
||||
|
||||
|
@ -442,7 +465,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);
|
||||
|
@ -454,11 +479,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);
|
||||
|
@ -467,7 +494,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
|
||||
}
|
||||
|
||||
|
@ -475,16 +504,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
|
||||
}
|
||||
|
||||
|
@ -492,4 +525,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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -395,6 +395,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");
|
||||
|
@ -459,6 +460,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());
|
||||
|
@ -540,6 +549,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());
|
||||
|
||||
|
@ -603,6 +614,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) {
|
||||
|
@ -659,9 +673,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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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">
|
||||
<property name="fieldGrowthPolicy">
|
||||
|
@ -1301,12 +1370,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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -52,6 +52,7 @@
|
|||
#include "ShaderSelector.h"
|
||||
#include "ShortcutController.h"
|
||||
#include "TileView.h"
|
||||
#include "VideoProxy.h"
|
||||
#include "VideoView.h"
|
||||
|
||||
#ifdef USE_DISCORD_RPC
|
||||
|
@ -508,6 +509,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
|
||||
|
@ -780,9 +782,6 @@ void Window::gameStarted() {
|
|||
if (m_savedScale > 0) {
|
||||
resizeFrame(size * m_savedScale);
|
||||
}
|
||||
if (!m_display) {
|
||||
reloadDisplayDriver();
|
||||
}
|
||||
attachWidget(m_display.get());
|
||||
setMouseTracking(true);
|
||||
m_display->setMinimumSize(size);
|
||||
|
@ -917,6 +916,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());
|
||||
}
|
||||
|
@ -1749,6 +1752,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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue