Qt: Begin work on shader selector
jump to
@@ -94,6 +94,7 @@ PaletteView.cpp
SavestateButton.cpp SensorView.cpp SettingsView.cpp + ShaderSelector.cpp ShortcutController.cpp ShortcutView.cpp Swatch.cpp@@ -113,6 +114,7 @@ OverrideView.ui
PaletteView.ui SensorView.ui SettingsView.ui + ShaderSelector.ui ShortcutView.ui VideoView.ui)
@@ -12,6 +12,7 @@ #include "MessagePainter.h"
struct GBAThread; struct VDir; +struct VideoShader; namespace QGBA {@@ -36,6 +37,7 @@ bool isFiltered() const { return m_filter; }
virtual bool isDrawing() const = 0; virtual bool supportsShaders() const = 0; + virtual VideoShader* shaders() = 0; signals: void showCursor();
@@ -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 @@ : m_gl(parent)
, 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 @@ delete[] item;
} delete m_backend; m_backend = nullptr; + if (m_shader.passes) { + GBAGLES2ShaderFree(&m_shader); + } } void PainterGL::setContext(GBAThread* context) {@@ -248,8 +260,8 @@ #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); + if (m_shader.passes) { + GBAGLES2ShaderAttach(reinterpret_cast<GBAGLES2Context*>(m_backend), static_cast<GBAGLES2Shader*>(m_shader.passes), m_shader.nPasses); } #endif@@ -366,13 +378,18 @@ m_gl->makeCurrent();
#if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent(); #endif - if (m_shaders) { + if (m_shader.passes) { GBAGLES2ShaderDetach(reinterpret_cast<GBAGLES2Context*>(m_backend)); + GBAGLES2ShaderFree(&m_shader); } - GBAGLES2ShaderLoad(&m_shaders, &m_nShaders, nullptr, dir); + GBAGLES2ShaderLoad(&m_shader, dir); if (m_started) { - GBAGLES2ShaderAttach(reinterpret_cast<GBAGLES2Context*>(m_backend), m_shaders, m_nShaders); + GBAGLES2ShaderAttach(reinterpret_cast<GBAGLES2Context*>(m_backend), static_cast<GBAGLES2Shader*>(m_shader.passes), m_shader.nPasses); } m_gl->doneCurrent(); #endif } + +VideoShader* PainterGL::shaders() { + return &m_shader; +}
@@ -19,9 +19,11 @@ #include <QQueue>
#include <QThread> #include <QTimer> +extern "C" { +#include "platform/video-backend.h" +} + struct GBAThread; -struct VideoBackend; -struct GBAGLES2Shader; namespace QGBA {@@ -45,6 +47,7 @@ ~DisplayGL();
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 @@ void lockAspectRatio(bool lock);
void filter(bool filter); void setShaders(struct VDir*); + VideoShader* shaders(); private: void performDraw();@@ -111,8 +115,7 @@ bool m_active;
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;
@@ -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;
@@ -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 <QCheckBox> +#include <QDoubleSpinBox> +#include <QFormLayout> +#include <QGridLayout> +#include <QSpinBox> + +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<GBAGLES2Shader*>(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<void (size_t, size_t)> 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<void (QDoubleSpinBox::*)(double)>(&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<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [value](int v) { + *value = v; + }); +}
@@ -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 <QDialog> + +#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
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ShaderSelector</class> + <widget class="QDialog" name="ShaderSelector"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Shaders</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Active Shader:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="shaderName"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="author"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="description"> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QTabWidget" name="passes"> + <property name="currentIndex"> + <number>-1</number> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ShaderSelector</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
@@ -33,6 +33,7 @@ #include "OverrideView.h"
#include "PaletteView.h" #include "SensorView.h" #include "SettingsView.h" +#include "ShaderSelector.h" #include "ShortcutController.h" #include "ShortcutView.h" #include "VideoView.h"@@ -393,6 +394,11 @@
void Window::openAboutScreen() { AboutScreen* about = new AboutScreen(); openView(about); +} + +void Window::openShaderWindow() { + ShaderSelector* shaderView = new ShaderSelector(m_display); + openView(shaderView); } #ifdef BUILD_SDL@@ -1047,6 +1053,13 @@ for (int i = 0; i <= 10; ++i) {
skip->addValue(QString::number(i), i, skipMenu); } 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();
@@ -88,6 +88,8 @@ void openIOViewer();
void openAboutScreen(); + void openShaderWindow(); + #ifdef BUILD_SDL void openGamepadWindow(); #endif