Merge commit 'a21d773ae8dd52b6e2e1e7b34f8bca4ec3d5b457'

This commit is contained in:
Jeffrey Pfau 2015-06-06 23:10:21 -07:00
commit 36daee6de3
9 changed files with 240 additions and 181 deletions

121
src/platform/opengl/gl.c Normal file
View File

@ -0,0 +1,121 @@
/* Copyright (c) 2013-2015 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 "gl.h"
#include "gba/video.h"
static const GLint _glVertices[] = {
0, 0,
256, 0,
256, 256,
0, 256
};
static const GLint _glTexCoords[] = {
0, 0,
1, 0,
1, 1,
0, 1
};
static void GBAGLContextInit(struct VideoBackend* v, WHandle handle) {
UNUSED(handle);
struct GBAGLContext* context = (struct GBAGLContext*) v;
glGenTextures(1, &context->tex);
glBindTexture(GL_TEXTURE_2D, context->tex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#ifndef _WIN32
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
#endif
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#endif
}
static void GBAGLContextDeinit(struct VideoBackend* v) {
struct GBAGLContext* context = (struct GBAGLContext*) v;
glDeleteTextures(1, &context->tex);
}
static void GBAGLContextResized(struct VideoBackend* v, int w, int h) {
int drawW = w;
int drawH = h;
if (v->lockAspectRatio) {
if (w * 2 > h * 3) {
drawW = h * 3 / 2;
} else if (w * 2 < h * 3) {
drawH = w * 2 / 3;
}
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
}
static void GBAGLContextClear(struct VideoBackend* v) {
UNUSED(v);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
void GBAGLContextDrawFrame(struct VideoBackend* v) {
struct GBAGLContext* context = (struct GBAGLContext*) v;
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, context->tex);
if (v->filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
void GBAGLContextPostFrame(struct VideoBackend* v, const void* frame) {
struct GBAGLContext* context = (struct GBAGLContext*) v;
glBindTexture(GL_TEXTURE_2D, context->tex);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
#endif
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
#endif
}
void GBAGLContextCreate(struct GBAGLContext* context) {
context->d.init = GBAGLContextInit;
context->d.deinit = GBAGLContextDeinit;
context->d.resized = GBAGLContextResized;
context->d.swap = 0;
context->d.clear = GBAGLContextClear;
context->d.postFrame = GBAGLContextPostFrame;
context->d.drawFrame = GBAGLContextDrawFrame;
context->d.setMessage = 0;
context->d.clearMessage = 0;
}

25
src/platform/opengl/gl.h Normal file
View File

@ -0,0 +1,25 @@
/* Copyright (c) 2013-2015 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 GL_H
#define GL_H
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include "platform/video-backend.h"
struct GBAGLContext {
struct VideoBackend d;
GLuint tex;
};
void GBAGLContextCreate(struct GBAGLContext*);
#endif

View File

@ -39,6 +39,8 @@ if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND OR NOT OPENGL_FOUND)
return()
endif()
list(APPEND PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c)
get_target_property(QT_TYPE Qt5::Core TYPE)
if(QT_TYPE STREQUAL STATIC_LIBRARY)
set(QT_STATIC ON)

View File

@ -14,20 +14,6 @@ extern "C" {
using namespace QGBA;
static const GLint _glVertices[] = {
0, 0,
256, 0,
256, 256,
0, 256
};
static const GLint _glTexCoords[] = {
0, 0,
1, 0,
1, 1,
0, 1
};
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
: Display(parent)
, m_gl(new EmptyGLWidget(format, this))
@ -154,10 +140,16 @@ PainterGL::PainterGL(QGLWidget* parent)
: m_gl(parent)
, m_drawTimer(nullptr)
, m_messageTimer(this)
, m_lockAspectRatio(false)
, m_filter(false)
, m_context(nullptr)
{
GBAGLContextCreate(&m_backend);
m_backend.d.swap = [](VideoBackend* v) {
PainterGL* painter = static_cast<PainterGL*>(v->user);
painter->m_gl->swapBuffers();
};
m_backend.d.user = this;
m_backend.d.filter = false;
m_backend.d.lockAspectRatio = false;
m_messageFont.setFamily("Source Code Pro");
m_messageFont.setStyleHint(QFont::Monospace);
m_messageFont.setPixelSize(13);
@ -174,16 +166,7 @@ void PainterGL::setContext(GBAThread* context) {
void PainterGL::setBacking(const uint32_t* backing) {
m_gl->makeCurrent();
glBindTexture(GL_TEXTURE_2D, m_tex);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, backing);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, backing);
#endif
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, backing);
#endif
m_backend.d.postFrame(&m_backend.d, backing);
m_gl->doneCurrent();
}
@ -193,43 +176,31 @@ void PainterGL::resize(const QSize& size) {
int h = m_size.height();
int drawW = w;
int drawH = h;
if (m_lockAspectRatio) {
if (m_backend.d.lockAspectRatio) {
if (w * 2 > h * 3) {
drawW = h * 3 / 2;
} else if (w * 2 < h * 3) {
drawH = w * 2 / 3;
}
}
m_viewport = QRect((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
m_painter.begin(m_gl->context()->device());
m_world.reset();
m_world.translate(m_viewport.x(), m_viewport.y());
m_world.translate((w - drawW) / 2, (h - drawH) / 2);
m_world.scale(qreal(drawW) / VIDEO_HORIZONTAL_PIXELS, qreal(drawH) / VIDEO_VERTICAL_PIXELS);
m_painter.setWorldTransform(m_world);
m_painter.setFont(m_messageFont);
m_message.prepare(m_world, m_messageFont);
m_painter.end();
if (m_drawTimer) {
forceDraw();
}
}
void PainterGL::lockAspectRatio(bool lock) {
m_lockAspectRatio = lock;
m_backend.d.lockAspectRatio = lock;
if (m_drawTimer) {
forceDraw();
}
}
void PainterGL::filter(bool filter) {
m_filter = filter;
m_gl->makeCurrent();
if (m_filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
m_gl->doneCurrent();
m_backend.d.filter = filter;
if (m_drawTimer) {
forceDraw();
}
@ -237,24 +208,7 @@ void PainterGL::filter(bool filter) {
void PainterGL::start() {
m_gl->makeCurrent();
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_tex);
glBindTexture(GL_TEXTURE_2D, m_tex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (m_filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
m_backend.d.init(&m_backend.d, (void*) m_gl->winId());
m_gl->doneCurrent();
m_drawTimer = new QTimer;
@ -267,19 +221,17 @@ void PainterGL::start() {
void PainterGL::draw() {
GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip);
m_painter.begin(m_gl->context()->device());
m_painter.setWorldTransform(m_world);
performDraw();
m_painter.end();
GBASyncWaitFrameEnd(&m_context->sync);
m_gl->swapBuffers();
m_backend.d.swap(&m_backend.d);
}
void PainterGL::forceDraw() {
m_painter.begin(m_gl->context()->device());
m_painter.setWorldTransform(m_world);
performDraw();
m_painter.end();
m_gl->swapBuffers();
m_backend.d.swap(&m_backend.d);
}
void PainterGL::stop() {
@ -287,10 +239,9 @@ void PainterGL::stop() {
delete m_drawTimer;
m_drawTimer = nullptr;
m_gl->makeCurrent();
glDeleteTextures(1, &m_tex);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
m_gl->swapBuffers();
m_backend.d.clear(&m_backend.d);
m_backend.d.swap(&m_backend.d);
m_backend.d.deinit(&m_backend.d);
m_gl->doneCurrent();
m_gl->context()->moveToThread(m_gl->thread());
moveToThread(m_gl->thread());
@ -309,21 +260,11 @@ void PainterGL::unpause() {
void PainterGL::performDraw() {
m_painter.beginNativePainting();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
QRect viewport(m_viewport.topLeft() * m_gl->devicePixelRatio(), m_viewport.size() * m_gl->devicePixelRatio());
glViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
if (m_context->sync.videoFrameWait) {
glFlush();
}
float r = m_gl->devicePixelRatio();
m_backend.d.resized(&m_backend.d, m_size.width() * r, m_size.height() * r);
m_backend.d.drawFrame(&m_backend.d);
m_painter.endNativePainting();
m_painter.setWorldTransform(m_world);
m_painter.setRenderHint(QPainter::Antialiasing);
m_painter.setFont(m_messageFont);
m_painter.setPen(Qt::black);

View File

@ -13,6 +13,10 @@
#include <QThread>
#include <QTimer>
extern "C" {
#include "platform/opengl/gl.h"
}
struct GBAThread;
namespace QGBA {
@ -94,11 +98,8 @@ private:
QTimer* m_drawTimer;
QTimer m_messageTimer;
GBAThread* m_context;
GLuint m_tex;
GBAGLContext m_backend;
QSize m_size;
bool m_lockAspectRatio;
bool m_filter;
QRect m_viewport;
QTransform m_world;
QFont m_messageFont;
};

View File

@ -67,6 +67,7 @@ else()
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl.c)
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)
add_definitions(-DBUILD_GL)
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})

View File

@ -6,47 +6,22 @@
#include "main.h"
#include "gba/supervisor/thread.h"
#include "platform/opengl/gl.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#ifdef BUILD_GL
static const GLint _glVertices[] = {
0, 0,
256, 0,
256, 256,
0, 256
};
static const GLint _glTexCoords[] = {
0, 0,
1, 0,
1, 1,
0, 1
};
#endif
static void _doViewport(int w, int h, struct SDLSoftwareRenderer* renderer) {
int drawW = w;
int drawH = h;
if (renderer->lockAspectRatio) {
if (w * 2 > h * 3) {
drawW = h * 3 / 2;
} else if (w * 2 < h * 3) {
drawH = w * 2 / 3;
}
}
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
glClear(GL_COLOR_BUFFER_BIT);
static void _sdlSwap(struct VideoBackend* context) {
struct SDLSoftwareRenderer* renderer = (struct SDLSoftwareRenderer*) context->user;
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GL_SwapWindow(renderer->window);
#else
SDL_GL_SwapBuffers();
#endif
glClear(GL_COLOR_BUFFER_BIT);
}
static void _doViewport(int w, int h, struct VideoBackend* v) {
v->resized(v, w, h);
v->clear(v);
v->swap(v);
v->clear(v);
}
static bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer);
@ -89,48 +64,24 @@ bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) {
#endif
#endif
renderer->d.outputBuffer = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
glGenTextures(1, &renderer->tex);
glBindTexture(GL_TEXTURE_2D, renderer->tex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
if (renderer->filter) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
#ifndef _WIN32
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
renderer->d.outputBuffer = malloc(256 * 256 * BYTES_PER_PIXEL);
renderer->d.outputBufferStride = 256;
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
#endif
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#endif
GBAGLContextCreate(&renderer->gl);
renderer->gl.d.user = renderer;
renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio;
renderer->gl.d.filter = renderer->filter;
renderer->gl.d.swap = _sdlSwap;
renderer->gl.d.init(&renderer->gl.d, 0);
_doViewport(renderer->viewportWidth, renderer->viewportHeight, renderer);
_doViewport(renderer->viewportWidth, renderer->viewportHeight, &renderer->gl.d);
return true;
}
void GBASDLGLRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer) {
SDL_Event event;
struct VideoBackend* v = &renderer->gl.d;
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_INT, 0, _glVertices);
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, 0, 1);
while (context->state < THREAD_EXITING) {
while (SDL_PollEvent(&event)) {
GBASDLHandleEvent(context, &renderer->player, &event);
@ -138,37 +89,24 @@ void GBASDLGLRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* rend
// Event handling can change the size of the screen
if (renderer->player.windowUpdated) {
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
_doViewport(renderer->viewportWidth, renderer->viewportHeight, renderer);
_doViewport(renderer->viewportWidth, renderer->viewportHeight, v);
renderer->player.windowUpdated = 0;
}
#endif
}
if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
glBindTexture(GL_TEXTURE_2D, renderer->tex);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, renderer->d.outputBuffer);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer);
#endif
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
#endif
if (context->sync.videoFrameWait) {
glFlush();
v->postFrame(v, renderer->d.outputBuffer);
}
}
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
v->drawFrame(v);
GBASyncWaitFrameEnd(&context->sync);
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GL_SwapWindow(renderer->window);
#else
SDL_GL_SwapBuffers();
#endif
v->swap(v);
}
}
void GBASDLGLDeinit(struct SDLSoftwareRenderer* renderer) {
if (renderer->gl.d.deinit) {
renderer->gl.d.deinit(&renderer->gl.d);
}
free(renderer->d.outputBuffer);
}

View File

@ -12,11 +12,7 @@
#include "sdl-events.h"
#ifdef BUILD_GL
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include "platform/opengl/gl.h"
#endif
#ifdef BUILD_RASPI
@ -59,7 +55,7 @@ struct SDLSoftwareRenderer {
bool filter;
#ifdef BUILD_GL
GLuint tex;
struct GBAGLContext gl;
#endif
#ifdef USE_PIXMAN

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2013-2015 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_BACKEND_H
#define VIDEO_BACKEND_H
#include "util/common.h"
#ifdef _WIN32
typedef HWND WHandle;
#else
typedef void* WHandle;
#endif
struct VideoBackend {
void (*init)(struct VideoBackend*, WHandle handle);
void (*deinit)(struct VideoBackend*);
void (*swap)(struct VideoBackend*);
void (*clear)(struct VideoBackend*);
void (*resized)(struct VideoBackend*, int w, int h);
void (*postFrame)(struct VideoBackend*, const void* frame);
void (*drawFrame)(struct VideoBackend*);
void (*setMessage)(struct VideoBackend*, const char* message);
void (*clearMessage)(struct VideoBackend*);
void* user;
bool filter;
bool lockAspectRatio;
};
#endif