diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1359c1caf..3fbad777d 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -94,6 +94,7 @@ set(SOURCE_FILES SavestateButton.cpp SensorView.cpp SettingsView.cpp + ShaderSelector.cpp ShortcutController.cpp ShortcutView.cpp Swatch.cpp @@ -113,6 +114,7 @@ qt5_wrap_ui(UI_FILES PaletteView.ui SensorView.ui SettingsView.ui + ShaderSelector.ui ShortcutView.ui VideoView.ui) diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index e6b8650f5..2b78ede40 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -12,6 +12,7 @@ struct GBAThread; struct VDir; +struct VideoShader; namespace QGBA { @@ -36,6 +37,7 @@ public: virtual bool isDrawing() const = 0; virtual bool supportsShaders() const = 0; + virtual VideoShader* shaders() = 0; signals: void showCursor(); diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 339f903ba..f5f0438c5 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -44,6 +44,16 @@ bool DisplayGL::supportsShaders() const { return m_painter->supportsShaders(); } +VideoShader* DisplayGL::shaders() { + VideoShader* shaders = nullptr; + if (m_drawThread) { + QMetaObject::invokeMethod(m_painter, "shaders", Qt::BlockingQueuedConnection, Q_RETURN_ARG(VideoShader*, shaders)); + } else { + shaders = m_painter->shaders(); + } + return shaders; +} + void DisplayGL::startDrawing(GBAThread* thread) { if (m_drawThread) { return; @@ -158,8 +168,7 @@ PainterGL::PainterGL(QGLWidget* parent, QGLFormat::OpenGLVersionFlags glVersion) , m_active(false) , m_started(false) , m_context(nullptr) - , m_shaders(nullptr) - , m_nShaders(0) + , m_shader{} , m_backend(nullptr) , m_messagePainter(nullptr) { @@ -209,6 +218,9 @@ PainterGL::~PainterGL() { } delete m_backend; m_backend = nullptr; + if (m_shader.passes) { + GBAGLES2ShaderFree(&m_shader); + } } void PainterGL::setContext(GBAThread* context) { @@ -248,8 +260,8 @@ void PainterGL::start() { m_backend->init(m_backend, reinterpret_cast(m_gl->winId())); #if !defined(_WIN32) || defined(USE_EPOXY) - if (m_shaders) { - GBAGLES2ShaderAttach(reinterpret_cast(m_backend), m_shaders, m_nShaders); + if (m_shader.passes) { + GBAGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); } #endif @@ -366,13 +378,18 @@ void PainterGL::setShaders(struct VDir* dir) { #if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent(); #endif - if (m_shaders) { + if (m_shader.passes) { GBAGLES2ShaderDetach(reinterpret_cast(m_backend)); + GBAGLES2ShaderFree(&m_shader); } - GBAGLES2ShaderLoad(&m_shaders, &m_nShaders, nullptr, dir); + GBAGLES2ShaderLoad(&m_shader, dir); if (m_started) { - GBAGLES2ShaderAttach(reinterpret_cast(m_backend), m_shaders, m_nShaders); + GBAGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); } m_gl->doneCurrent(); #endif } + +VideoShader* PainterGL::shaders() { + return &m_shader; +} diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 40b0da7e8..53e6db03b 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -19,9 +19,11 @@ #include #include +extern "C" { +#include "platform/video-backend.h" +} + struct GBAThread; -struct VideoBackend; -struct GBAGLES2Shader; namespace QGBA { @@ -45,6 +47,7 @@ public: bool isDrawing() const override { return m_isDrawing; } bool supportsShaders() const override; + VideoShader* shaders() override; public slots: void startDrawing(GBAThread* context) override; @@ -96,6 +99,7 @@ public slots: void filter(bool filter); void setShaders(struct VDir*); + VideoShader* shaders(); private: void performDraw(); @@ -111,8 +115,7 @@ private: bool m_started; GBAThread* m_context; bool m_supportsShaders; - GBAGLES2Shader* m_shaders; - size_t m_nShaders; + VideoShader m_shader; VideoBackend* m_backend; QSize m_size; MessagePainter* m_messagePainter; diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index 843c50c6f..e943706c6 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -22,7 +22,8 @@ public: DisplayQt(QWidget* parent = nullptr); bool isDrawing() const override { return m_isDrawing; } - bool supportsShaders() const override { return 0; } + bool supportsShaders() const override { return false; } + VideoShader* shaders() override { return nullptr; } public slots: void startDrawing(GBAThread* context) override; diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp new file mode 100644 index 000000000..3a1c3d5b3 --- /dev/null +++ b/src/platform/qt/ShaderSelector.cpp @@ -0,0 +1,144 @@ +/* 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 "ShaderSelector.h" + +#include "Display.h" + +#include +#include +#include +#include +#include + +extern "C" { +#include "platform/video-backend.h" + +#if !defined(_WIN32) || defined(USE_EPOXY) +#include "platform/opengl/gles2.h" +#endif +} + +using namespace QGBA; + +ShaderSelector::ShaderSelector(Display* display, QWidget* parent) + : QDialog(parent) + , m_display(display) +{ + m_ui.setupUi(this); + + refreshShaders(); +} + +ShaderSelector::~ShaderSelector() { + clear(); +} + +void ShaderSelector::clear() { + m_ui.shaderName->setText(tr("No shader active")); + m_ui.description->clear(); + m_ui.author->clear(); + + while (QWidget* page = m_ui.passes->widget(0)) { + m_ui.passes->removeTab(0); + delete page; + } +} + +void ShaderSelector::refreshShaders() { + clear(); + m_shaders = m_display->shaders(); + if (!m_shaders) { + return; + } + m_ui.shaderName->setText(m_shaders->name); + m_ui.description->setText(m_shaders->description); + m_ui.author->setText(tr("by %1").arg(m_shaders->author)); + +#if !defined(_WIN32) || defined(USE_EPOXY) + GBAGLES2Shader* shaders = static_cast(m_shaders->passes); + for (size_t p = 0; p < m_shaders->nPasses; ++p) { + QWidget* page = new QWidget; + QFormLayout* layout = new QFormLayout; + page->setLayout(layout); + for (size_t u = 0 ; u < shaders[p].nUniforms; ++u) { + QGridLayout* settings = new QGridLayout; + std::function bind; + GBAGLES2Uniform* uniform = &shaders[p].uniforms[u]; + switch (uniform->type) { + case GL_FLOAT: + addUniform(settings, &uniform->value.f, uniform->min.f, uniform->max.f, 0, 0); + break; + case GL_FLOAT_VEC2: + addUniform(settings, &uniform->value.fvec2[0], uniform->min.fvec2[0], uniform->max.fvec2[0], 0, 0); + addUniform(settings, &uniform->value.fvec2[1], uniform->min.fvec2[1], uniform->max.fvec2[1], 0, 1); + break; + case GL_FLOAT_VEC3: + addUniform(settings, &uniform->value.fvec3[0], uniform->min.fvec3[0], uniform->max.fvec3[0], 0, 0); + addUniform(settings, &uniform->value.fvec3[1], uniform->min.fvec3[1], uniform->max.fvec3[1], 0, 1); + addUniform(settings, &uniform->value.fvec3[2], uniform->min.fvec3[2], uniform->max.fvec3[2], 0, 2); + break; + case GL_FLOAT_VEC4: + addUniform(settings, &uniform->value.fvec4[0], uniform->min.fvec4[0], uniform->max.fvec4[0], 0, 0); + addUniform(settings, &uniform->value.fvec4[1], uniform->min.fvec4[1], uniform->max.fvec4[1], 0, 1); + addUniform(settings, &uniform->value.fvec4[2], uniform->min.fvec4[2], uniform->max.fvec4[2], 0, 2); + addUniform(settings, &uniform->value.fvec4[3], uniform->min.fvec4[3], uniform->max.fvec4[3], 0, 3); + break; + case GL_INT: + addUniform(settings, &uniform->value.i, uniform->min.i, uniform->max.i, 0, 0); + break; + case GL_INT_VEC2: + addUniform(settings, &uniform->value.ivec2[0], uniform->min.ivec2[0], uniform->max.ivec2[0], 0, 0); + addUniform(settings, &uniform->value.ivec2[1], uniform->min.ivec2[1], uniform->max.ivec2[1], 0, 1); + break; + case GL_INT_VEC3: + addUniform(settings, &uniform->value.ivec3[0], uniform->min.ivec3[0], uniform->max.ivec3[0], 0, 0); + addUniform(settings, &uniform->value.ivec3[1], uniform->min.ivec3[1], uniform->max.ivec3[1], 0, 1); + addUniform(settings, &uniform->value.ivec3[2], uniform->min.ivec3[2], uniform->max.ivec3[2], 0, 2); + break; + case GL_INT_VEC4: + addUniform(settings, &uniform->value.ivec4[0], uniform->min.ivec4[0], uniform->max.ivec4[0], 0, 0); + addUniform(settings, &uniform->value.ivec4[1], uniform->min.ivec4[1], uniform->max.ivec4[1], 0, 1); + addUniform(settings, &uniform->value.ivec4[2], uniform->min.ivec4[2], uniform->max.ivec4[2], 0, 2); + addUniform(settings, &uniform->value.ivec4[3], uniform->min.ivec4[3], uniform->max.ivec4[3], 0, 3); + break; + } + layout->addRow(shaders[p].uniforms[u].readableName, settings); + } + m_ui.passes->addTab(page, tr("Pass %1").arg(p + 1)); + } +#endif +} + +void ShaderSelector::addUniform(QGridLayout* settings, float* value, float min, float max, int y, int x) { + QDoubleSpinBox* f = new QDoubleSpinBox; + f->setDecimals(3); + if (min < max) { + f->setMinimum(min); + f->setMaximum(max); + } + f->setValue(*value); + f->setSingleStep(0.001); + f->setAccelerated(true); + settings->addWidget(f, y, x); + connect(f, static_cast(&QDoubleSpinBox::valueChanged), [value](double v) { + *value = v; + }); +} + +void ShaderSelector::addUniform(QGridLayout* settings, int* value, int min, int max, int y, int x) { + QSpinBox* i = new QSpinBox; + if (min < max) { + i->setMinimum(min); + i->setMaximum(max); + } + i->setValue(*value); + i->setSingleStep(1); + i->setAccelerated(true); + settings->addWidget(i, y, x); + connect(i, static_cast(&QSpinBox::valueChanged), [value](int v) { + *value = v; + }); +} diff --git a/src/platform/qt/ShaderSelector.h b/src/platform/qt/ShaderSelector.h new file mode 100644 index 000000000..38f3858f0 --- /dev/null +++ b/src/platform/qt/ShaderSelector.h @@ -0,0 +1,42 @@ +/* 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 QGBA_SHADER_SELECTOR_H +#define QGBA_SHADER_SELECTOR_H + +#include + +#include "ui_ShaderSelector.h" + +class QGridLayout; +struct VideoShader; + +namespace QGBA { + +class Display; + +class ShaderSelector : public QDialog { +Q_OBJECT + +public: + ShaderSelector(Display* display, QWidget* parent = nullptr); + ~ShaderSelector(); + +public slots: + void refreshShaders(); + void clear(); + +private: + void addUniform(QGridLayout*, float* value, float min, float max, int y, int x); + void addUniform(QGridLayout*, int* value, int min, int max, int y, int x); + + Ui::ShaderSelector m_ui; + Display* m_display; + VideoShader* m_shaders; +}; + +} + +#endif diff --git a/src/platform/qt/ShaderSelector.ui b/src/platform/qt/ShaderSelector.ui new file mode 100644 index 000000000..26a1ca329 --- /dev/null +++ b/src/platform/qt/ShaderSelector.ui @@ -0,0 +1,114 @@ + + + ShaderSelector + + + + 0 + 0 + 400 + 300 + + + + Shaders + + + + + + + + + 75 + true + + + + Active Shader: + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + + TextLabel + + + true + + + + + + + Qt::Horizontal + + + + + + + -1 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ShaderSelector + close() + + + 248 + 254 + + + 157 + 274 + + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 2f31026c3..ece84cb10 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -33,6 +33,7 @@ #include "PaletteView.h" #include "SensorView.h" #include "SettingsView.h" +#include "ShaderSelector.h" #include "ShortcutController.h" #include "ShortcutView.h" #include "VideoView.h" @@ -395,6 +396,11 @@ void Window::openAboutScreen() { openView(about); } +void Window::openShaderWindow() { + ShaderSelector* shaderView = new ShaderSelector(m_display); + openView(shaderView); +} + #ifdef BUILD_SDL void Window::openGamepadWindow() { const char* profile = m_inputController.profileForType(SDL_BINDING_BUTTON); @@ -1048,6 +1054,13 @@ void Window::setupMenu(QMenuBar* menubar) { } m_config->updateOption("frameskip"); + QAction* shaderView = new QAction(tr("Shader options..."), avMenu); + connect(shaderView, SIGNAL(triggered()), this, SLOT(openShaderWindow())); + if (!m_display->supportsShaders()) { + shaderView->setEnabled(false); + } + addControlledAction(avMenu, shaderView, "shaderSelector"); + avMenu->addSeparator(); QMenu* target = avMenu->addMenu(tr("FPS target")); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 9e8afec42..e01031968 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -88,6 +88,8 @@ public slots: void openAboutScreen(); + void openShaderWindow(); + #ifdef BUILD_SDL void openGamepadWindow(); #endif