OpenGL: Shader loading
jump to
@@ -298,6 +298,7 @@ }
void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) { _lookupCharValue(config, "bios", &opts->bios); + _lookupCharValue(config, "shader", &opts->shader); _lookupIntValue(config, "logLevel", &opts->logLevel); _lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "volume", &opts->volume);@@ -358,6 +359,7 @@ }
void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* opts) { ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios); + ConfigurationSetValue(&config->defaultsTable, 0, "shader", opts->shader); ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios); ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios); ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);@@ -403,5 +405,7 @@ }
void GBAConfigFreeOpts(struct GBAOptions* opts) { free(opts->bios); + free(opts->shader); opts->bios = 0; + opts->shader = 0; }
@@ -38,6 +38,7 @@ int height;
bool lockAspectRatio; bool resampleVideo; bool suspendScreensaver; + char* shader; int volume; bool mute;
@@ -6,6 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gles2.h" #include "gba/video.h" +#include "util/configuration.h" +#include "util/vfs.h" + +#define MAX_PASSES 8 static const char* const _vertexShader = "attribute vec4 position;\n"@@ -352,3 +356,129 @@ return;
} context->shaders = 0; } + +static bool _lookupIntValue(const struct Configuration* config, const char* section, const char* key, int* out) { + const char* charValue = ConfigurationGetValue(config, section, key); + if (!charValue) { + return false; + } + char* end; + unsigned long value = strtol(charValue, &end, 10); + if (*end) { + return false; + } + *out = value; + return true; +} + +bool GBAGLES2ShaderLoad(struct GBAGLES2Shader** shaders, size_t* nShaders, struct GBAGLES2ShaderMetadata* metadata, struct VDir* dir) { + struct VFile* manifest = dir->openFile(dir, "manifest.ini", O_RDONLY); + if (!manifest) { + return false; + } + bool success = false; + struct Configuration description; + ConfigurationInit(&description); + if (ConfigurationReadVFile(&description, manifest)) { + int inShaders; + success = _lookupIntValue(&description, "shader", "passes", &inShaders); + if (inShaders > MAX_PASSES || inShaders < 1) { + success = false; + } + if (success) { + if (metadata) { + metadata->name = ConfigurationGetValue(&description, "shader", "name"); + if (metadata->name) { + metadata->name = strdup(metadata->name); + } + metadata->author = ConfigurationGetValue(&description, "shader", "author"); + if (metadata->author) { + metadata->author = strdup(metadata->author); + } + metadata->description = ConfigurationGetValue(&description, "shader", "description"); + if (metadata->description) { + metadata->description = strdup(metadata->description); + } + } + struct GBAGLES2Shader* shaderBlock = malloc(sizeof(struct GBAGLES2Shader) * inShaders); + int n; + for (n = 0; n < inShaders; ++n) { + char passName[12]; + snprintf(passName, sizeof(passName), "pass.%u", n); + const char* fs = ConfigurationGetValue(&description, passName, "fragmentShader"); + const char* vs = ConfigurationGetValue(&description, passName, "vertexShader"); + if (fs && (fs[0] == '.' || strstr(fs, PATH_SEP))) { + success = false; + break; + } + if (vs && (vs[0] == '.' || strstr(vs, PATH_SEP))) { + success = false; + break; + } + char* fssrc = 0; + char* vssrc = 0; + if (fs) { + struct VFile* fsf = dir->openFile(dir, fs, O_RDONLY); + if (!fsf) { + success = false; + break; + } + fssrc = malloc(fsf->size(fsf)); + fsf->read(fsf, fssrc, fsf->size(fsf)); + fsf->close(fsf); + } + if (vs) { + struct VFile* vsf = dir->openFile(dir, vs, O_RDONLY); + if (!vsf) { + success = false; + free(fssrc); + break; + } + vssrc = malloc(vsf->size(vsf)); + vsf->read(vsf, vssrc, vsf->size(vsf)); + vsf->close(vsf); + } + int width = 0; + int height = 0; + _lookupIntValue(&description, passName, "width", &width); + _lookupIntValue(&description, passName, "height", &height); + GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, 0, 0); + int b = 0; + _lookupIntValue(&description, passName, "blend", &b); + if (b) { + shaderBlock[n].blend = b; + } + b = 0; + _lookupIntValue(&description, passName, "filter", &b); + if (b) { + shaderBlock[n].filter = b; + } + free(fssrc); + free(vssrc); + } + if (success) { + *nShaders = inShaders; + *shaders = shaderBlock; + } else { + inShaders = n; + for (n = 0; n < inShaders; ++n) { + GBAGLES2ShaderDeinit(&shaderBlock[n]); + } + } + } + } + ConfigurationDeinit(&description); + return success; +} + +void GBAGLES2ShaderFree(struct GBAGLES2Shader* shaders, size_t nShaders) { + size_t n; + for (n = 0; n < nShaders; ++n) { + GBAGLES2ShaderDeinit(&shaders[n]); + size_t u; + for (u = 0; u < shaders[n].nUniforms; ++u) { + free((void*) shaders[n].uniforms[u].name); + } + } + free(shaders); +}
@@ -84,11 +84,21 @@ struct GBAGLES2Shader* shaders;
size_t nShaders; }; +struct GBAGLES2ShaderMetadata { + const char* name; + const char* author; + const char* description; +}; + void GBAGLES2ContextCreate(struct GBAGLES2Context*); void GBAGLES2ShaderInit(struct GBAGLES2Shader*, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms); void GBAGLES2ShaderDeinit(struct GBAGLES2Shader*); void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*, size_t nShaders); void GBAGLES2ShaderDetach(struct GBAGLES2Context*); + +struct VDir; +bool GBAGLES2ShaderLoad(struct GBAGLES2Shader**, size_t* nShaders, struct GBAGLES2ShaderMetadata*, struct VDir*); +void GBAGLES2ShaderFree(struct GBAGLES2Shader*, size_t nShaders); #endif
@@ -11,6 +11,7 @@
#include "MessagePainter.h" struct GBAThread; +struct VDir; namespace QGBA {@@ -34,6 +35,7 @@ bool isAspectRatioLocked() const { return m_lockAspectRatio; }
bool isFiltered() const { return m_filter; } virtual bool isDrawing() const = 0; + virtual bool supportsShaders() const = 0; signals: void showCursor();@@ -48,6 +50,7 @@ virtual void forceDraw() = 0;
virtual void lockAspectRatio(bool lock); virtual void filter(bool filter); virtual void framePosted(const uint32_t*) = 0; + virtual void setShaders(struct VDir*) = 0; void showMessage(const QString& message);
@@ -40,6 +40,10 @@ DisplayGL::~DisplayGL() {
delete m_painter; } +bool DisplayGL::supportsShaders() const { + return m_painter->supportsShaders(); +} + void DisplayGL::startDrawing(GBAThread* thread) { if (m_drawThread) { return;@@ -133,6 +137,10 @@ QMetaObject::invokeMethod(m_painter, "draw");
} } +void DisplayGL::setShaders(struct VDir* shaders) { + QMetaObject::invokeMethod(m_painter, "setShaders", Q_ARG(struct VDir*, shaders)); +} + void DisplayGL::resizeEvent(QResizeEvent* event) { Display::resizeEvent(event); resizePainter();@@ -148,7 +156,10 @@
PainterGL::PainterGL(QGLWidget* parent, QGLFormat::OpenGLVersionFlags glVersion) : m_gl(parent) , m_active(false) + , m_started(false) , m_context(nullptr) + , m_shaders(nullptr) + , m_nShaders(0) , m_messagePainter(nullptr) { #ifdef BUILD_GL@@ -163,6 +174,7 @@ if (glVersion & QGLFormat::OpenGL_Version_3_0) {
gl2Backend = new GBAGLES2Context; GBAGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; + m_supportsShaders = true; } else { #else {@@ -170,6 +182,7 @@ #endif
glBackend = new GBAGLContext; GBAGLContextCreate(glBackend); m_backend = &glBackend->d; + m_supportsShaders = false; } m_backend->swap = [](VideoBackend* v) { PainterGL* painter = static_cast<PainterGL*>(v->user);@@ -205,21 +218,21 @@ }
void PainterGL::resize(const QSize& size) { m_size = size; - if (m_active) { + if (m_started && !m_active) { forceDraw(); } } void PainterGL::lockAspectRatio(bool lock) { m_backend->lockAspectRatio = lock; - if (m_active) { + if (m_started && !m_active) { forceDraw(); } } void PainterGL::filter(bool filter) { m_backend->filter = filter; - if (m_active) { + if (m_started && !m_active) { forceDraw(); } }@@ -230,8 +243,16 @@ #if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent(); #endif m_backend->init(m_backend, reinterpret_cast<WHandle>(m_gl->winId())); + +#if !defined(_WIN32) || defined(USE_EPOXY) + if (m_shaders) { + GBAGLES2ShaderAttach(reinterpret_cast<GBAGLES2Context*>(m_backend), m_shaders, m_nShaders); + } +#endif + m_gl->doneCurrent(); m_active = true; + m_started = true; } void PainterGL::draw() {@@ -262,6 +283,7 @@ }
void PainterGL::stop() { m_active = false; + m_started = false; m_gl->makeCurrent(); #if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent();@@ -331,3 +353,23 @@ m_backend->postFrame(m_backend, buffer);
} m_mutex.unlock(); } + +void PainterGL::setShaders(struct VDir* dir) { + if (!supportsShaders()) { + return; + } +#if !defined(_WIN32) || defined(USE_EPOXY) + m_gl->makeCurrent(); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + if (m_shaders) { + GBAGLES2ShaderDetach(reinterpret_cast<GBAGLES2Context*>(m_backend)); + } + GBAGLES2ShaderLoad(&m_shaders, &m_nShaders, nullptr, dir); + if (m_started) { + GBAGLES2ShaderAttach(reinterpret_cast<GBAGLES2Context*>(m_backend), m_shaders, m_nShaders); + } + m_gl->doneCurrent(); +#endif +}
@@ -22,6 +22,7 @@ #include <QTimer>
struct GBAThread; struct VideoBackend; +struct GBAGLES2Shader; namespace QGBA {@@ -44,6 +45,7 @@ DisplayGL(const QGLFormat& format, QWidget* parent = nullptr);
~DisplayGL(); bool isDrawing() const override { return m_isDrawing; } + bool supportsShaders() const override; public slots: void startDrawing(GBAThread* context) override;@@ -54,6 +56,7 @@ void forceDraw() override;
void lockAspectRatio(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; + void setShaders(struct VDir*) override; protected: virtual void paintEvent(QPaintEvent*) override {}@@ -80,6 +83,8 @@ void setContext(GBAThread*);
void setMessagePainter(MessagePainter*); void enqueue(const uint32_t* backing); + bool supportsShaders() const { return m_supportsShaders; } + public slots: void forceDraw(); void draw();@@ -91,6 +96,8 @@ void resize(const QSize& size);
void lockAspectRatio(bool lock); void filter(bool filter); + void setShaders(struct VDir*); + private: void performDraw(); void dequeue();@@ -102,7 +109,11 @@ QPainter m_painter;
QMutex m_mutex; QGLWidget* m_gl; bool m_active; + bool m_started; GBAThread* m_context; + bool m_supportsShaders; + GBAGLES2Shader* m_shaders; + size_t m_nShaders; VideoBackend* m_backend; QSize m_size; MessagePainter* m_messagePainter;
@@ -22,6 +22,7 @@ public:
DisplayQt(QWidget* parent = nullptr); bool isDrawing() const override { return m_isDrawing; } + bool supportsShaders() const override { return 0; } public slots: void startDrawing(GBAThread* context) override;@@ -32,6 +33,7 @@ void forceDraw() override { update(); }
void lockAspectRatio(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; + void setShaders(struct VDir*) override {} protected: virtual void paintEvent(QPaintEvent*) override;
@@ -39,6 +39,7 @@ #include "VideoView.h"
extern "C" { #include "platform/commandline.h" +#include "util/vfs.h" } using namespace QGBA;@@ -221,6 +222,14 @@ }
if (opts->fullscreen) { enterFullScreen(); + } + + if (opts->shader) { + struct VDir* shader = VDirOpen(opts->shader); + if (shader) { + m_display->setShaders(shader); + shader->close(shader); + } } m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
@@ -136,12 +136,16 @@ return 0;
} bool ConfigurationRead(struct Configuration* configuration, const char* path) { - HashTableClear(&configuration->root); - HashTableClear(&configuration->sections); struct VFile* vf = VFileOpen(path, O_RDONLY); if (!vf) { return false; } + return ConfigurationReadVFile(configuration, vf); +} + +bool ConfigurationReadVFile(struct Configuration* configuration, struct VFile* vf) { + HashTableClear(&configuration->root); + HashTableClear(&configuration->sections); return ini_parse_stream(_vfgets, vf, _iniRead, configuration) == 0; }
@@ -29,6 +29,7 @@
void ConfigurationClearValue(struct Configuration*, const char* section, const char* key); bool ConfigurationRead(struct Configuration*, const char* path); +bool ConfigurationReadVFile(struct Configuration*, struct VFile* vf); bool ConfigurationWrite(const struct Configuration*, const char* path); bool ConfigurationWriteSection(const struct Configuration*, const char* path, const char* section);