src/platform/qt/ShaderSelector.cpp (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ShaderSelector.h"
7
8#include "ConfigController.h"
9#include "GBAApp.h"
10#include "Display.h"
11#include "VFileDevice.h"
12
13#include <QCheckBox>
14#include <QDir>
15#include <QDoubleSpinBox>
16#include <QFileDialog>
17#include <QFormLayout>
18#include <QGridLayout>
19#include <QSpinBox>
20
21#include <mgba/core/version.h>
22#include <mgba-util/vfs.h>
23#include "platform/video-backend.h"
24
25#if defined(BUILD_GL) || defined(BUILD_GLES2)
26
27#if !defined(_WIN32) || defined(USE_EPOXY)
28#include "platform/opengl/gles2.h"
29#endif
30
31using namespace QGBA;
32
33ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidget* parent)
34 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
35 , m_display(display)
36 , m_config(config)
37 , m_shaderPath(config->getOption("shader"))
38{
39 m_ui.setupUi(this);
40
41 refreshShaders();
42
43 connect(m_ui.load, &QAbstractButton::clicked, this, &ShaderSelector::selectShader);
44 connect(m_ui.unload, &QAbstractButton::clicked, this, &ShaderSelector::clearShader);
45 connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &ShaderSelector::buttonPressed);
46}
47
48ShaderSelector::~ShaderSelector() {
49 clear();
50}
51
52void ShaderSelector::clear() {
53 m_ui.shaderName->setText(tr("No shader active"));
54 m_ui.description->clear();
55 m_ui.author->clear();
56
57 while (QWidget* page = m_ui.passes->widget(0)) {
58 m_ui.passes->removeTab(0);
59 delete page;
60 }
61}
62
63void ShaderSelector::selectShader() {
64 QDir path(GBAApp::dataDir());
65 path.cd(QLatin1String("shaders"));
66 QString name = GBAApp::app()->getOpenDirectoryName(this, tr("Load shader"), path.absolutePath());
67 if (!name.isNull()) {
68 loadShader(name);
69 refreshShaders();
70 }
71}
72
73void ShaderSelector::loadShader(const QString& path) {
74 VDir* shader = VFileDevice::openDir(path);
75 if (!shader) {
76 shader = VFileDevice::openArchive(path);
77 }
78 if (!shader) {
79 return;
80 }
81 m_display->setShaders(shader);
82 shader->close(shader);
83 m_shaderPath = path;
84 m_config->setOption("shader", m_shaderPath);
85}
86
87void ShaderSelector::clearShader() {
88 m_display->clearShaders();
89 refreshShaders();
90 m_shaderPath = "";
91 m_config->setOption("shader", m_shaderPath);
92}
93
94void ShaderSelector::refreshShaders() {
95 clear();
96 m_shaders = m_display->shaders();
97 if (!m_shaders) {
98 return;
99 }
100 if (m_shaders->name) {
101 m_ui.shaderName->setText(m_shaders->name);
102 } else {
103 m_ui.shaderName->setText(tr("No shader loaded"));
104 }
105 if (m_shaders->description) {
106 m_ui.description->setText(m_shaders->description);
107 } else {
108 m_ui.description->clear();
109 }
110 if (m_shaders->author) {
111 m_ui.author->setText(tr("by %1").arg(m_shaders->author));
112 } else {
113 m_ui.author->clear();
114 }
115
116 disconnect(this, &ShaderSelector::saved, 0, 0);
117 disconnect(this, &ShaderSelector::reset, 0, 0);
118 disconnect(this, &ShaderSelector::resetToDefault, 0, 0);
119
120#if !defined(_WIN32) || defined(USE_EPOXY)
121 if (m_shaders->preprocessShader) {
122 m_ui.passes->addTab(makePage(static_cast<mGLES2Shader*>(m_shaders->preprocessShader), "default", 0), tr("Preprocessing"));
123 }
124 mGLES2Shader* shaders = static_cast<mGLES2Shader*>(m_shaders->passes);
125 QFileInfo fi(m_shaderPath);
126 for (size_t p = 0; p < m_shaders->nPasses; ++p) {
127 QWidget* page = makePage(&shaders[p], fi.baseName(), p);
128 if (page) {
129 m_ui.passes->addTab(page, tr("Pass %1").arg(p + 1));
130 }
131 }
132#endif
133}
134
135void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, const QString& name, float* value, float min, float max, int y, int x) {
136 QDoubleSpinBox* f = new QDoubleSpinBox;
137 f->setDecimals(3);
138 if (min < max) {
139 f->setMinimum(min);
140 f->setMaximum(max);
141 }
142 float def = *value;
143 bool ok = false;
144 float v = m_config->getQtOption(name, section).toFloat(&ok);
145 if (ok) {
146 *value = v;
147 }
148 f->setValue(*value);
149 f->setSingleStep(0.001);
150 f->setAccelerated(true);
151 settings->addWidget(f, y, x);
152 connect(f, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [value](double v) {
153 *value = v;
154 });
155 connect(this, &ShaderSelector::saved, [this, section, name, f]() {
156 m_config->setQtOption(name, f->value(), section);
157 });
158 connect(this, &ShaderSelector::reset, [this, section, name, f]() {
159 bool ok = false;
160 float v = m_config->getQtOption(name, section).toFloat(&ok);
161 if (ok) {
162 f->setValue(v);
163 }
164 });
165 connect(this, &ShaderSelector::resetToDefault, [def, section, name, f]() {
166 f->setValue(def);
167 });
168}
169
170void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, const QString& name, int* value, int min, int max, int y, int x) {
171 QSpinBox* i = new QSpinBox;
172 if (min < max) {
173 i->setMinimum(min);
174 i->setMaximum(max);
175 }
176 int def = *value;
177 bool ok = false;
178 int v = m_config->getQtOption(name, section).toInt(&ok);
179 if (ok) {
180 *value = v;
181 }
182 i->setValue(*value);
183 i->setSingleStep(1);
184 i->setAccelerated(true);
185 settings->addWidget(i, y, x);
186 connect(i, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [value](int v) {
187 *value = v;
188 });
189 connect(this, &ShaderSelector::saved, [this, section, name, i]() {
190 m_config->setQtOption(name, i->value(), section);
191 });
192 connect(this, &ShaderSelector::reset, [this, section, name, i]() {
193 bool ok = false;
194 int v = m_config->getQtOption(name, section).toInt(&ok);
195 if (ok) {
196 i->setValue(v);
197 }
198 });
199 connect(this, &ShaderSelector::resetToDefault, [def, section, name, i]() {
200 i->setValue(def);
201 });
202}
203
204QWidget* ShaderSelector::makePage(mGLES2Shader* shader, const QString& name, int pass) {
205#if !defined(_WIN32) || defined(USE_EPOXY)
206 if (!shader->nUniforms) {
207 return nullptr;
208 }
209 QWidget* page = new QWidget;
210 QFormLayout* layout = new QFormLayout;
211 page->setLayout(layout);
212 for (size_t u = 0 ; u < shader->nUniforms; ++u) {
213 QGridLayout* settings = new QGridLayout;
214 mGLES2Uniform* uniform = &shader->uniforms[u];
215 QString section = QString("shader.%1.%2").arg(name).arg(pass);
216 QString name = QLatin1String(uniform->name);
217 switch (uniform->type) {
218 case GL_FLOAT:
219 addUniform(settings, section, name, &uniform->value.f, uniform->min.f, uniform->max.f, 0, 0);
220 break;
221 case GL_FLOAT_VEC2:
222 addUniform(settings, section, name + "[0]", &uniform->value.fvec2[0], uniform->min.fvec2[0], uniform->max.fvec2[0], 0, 0);
223 addUniform(settings, section, name + "[1]", &uniform->value.fvec2[1], uniform->min.fvec2[1], uniform->max.fvec2[1], 0, 1);
224 break;
225 case GL_FLOAT_VEC3:
226 addUniform(settings, section, name + "[0]", &uniform->value.fvec3[0], uniform->min.fvec3[0], uniform->max.fvec3[0], 0, 0);
227 addUniform(settings, section, name + "[1]", &uniform->value.fvec3[1], uniform->min.fvec3[1], uniform->max.fvec3[1], 0, 1);
228 addUniform(settings, section, name + "[2]", &uniform->value.fvec3[2], uniform->min.fvec3[2], uniform->max.fvec3[2], 0, 2);
229 break;
230 case GL_FLOAT_VEC4:
231 addUniform(settings, section, name + "[0]", &uniform->value.fvec4[0], uniform->min.fvec4[0], uniform->max.fvec4[0], 0, 0);
232 addUniform(settings, section, name + "[1]", &uniform->value.fvec4[1], uniform->min.fvec4[1], uniform->max.fvec4[1], 0, 1);
233 addUniform(settings, section, name + "[2]", &uniform->value.fvec4[2], uniform->min.fvec4[2], uniform->max.fvec4[2], 0, 2);
234 addUniform(settings, section, name + "[3]", &uniform->value.fvec4[3], uniform->min.fvec4[3], uniform->max.fvec4[3], 0, 3);
235 break;
236 case GL_INT:
237 addUniform(settings, section, name, &uniform->value.i, uniform->min.i, uniform->max.i, 0, 0);
238 break;
239 case GL_INT_VEC2:
240 addUniform(settings, section, name + "[0]", &uniform->value.ivec2[0], uniform->min.ivec2[0], uniform->max.ivec2[0], 0, 0);
241 addUniform(settings, section, name + "[1]", &uniform->value.ivec2[1], uniform->min.ivec2[1], uniform->max.ivec2[1], 0, 1);
242 break;
243 case GL_INT_VEC3:
244 addUniform(settings, section, name + "[0]", &uniform->value.ivec3[0], uniform->min.ivec3[0], uniform->max.ivec3[0], 0, 0);
245 addUniform(settings, section, name + "[1]", &uniform->value.ivec3[1], uniform->min.ivec3[1], uniform->max.ivec3[1], 0, 1);
246 addUniform(settings, section, name + "[2]", &uniform->value.ivec3[2], uniform->min.ivec3[2], uniform->max.ivec3[2], 0, 2);
247 break;
248 case GL_INT_VEC4:
249 addUniform(settings, section, name + "[0]", &uniform->value.ivec4[0], uniform->min.ivec4[0], uniform->max.ivec4[0], 0, 0);
250 addUniform(settings, section, name + "[1]", &uniform->value.ivec4[1], uniform->min.ivec4[1], uniform->max.ivec4[1], 0, 1);
251 addUniform(settings, section, name + "[2]", &uniform->value.ivec4[2], uniform->min.ivec4[2], uniform->max.ivec4[2], 0, 2);
252 addUniform(settings, section, name + "[3]", &uniform->value.ivec4[3], uniform->min.ivec4[3], uniform->max.ivec4[3], 0, 3);
253 break;
254 }
255 layout->addRow(shader->uniforms[u].readableName, settings);
256 }
257 return page;
258#else
259 return nullptr;
260#endif
261}
262
263void ShaderSelector::buttonPressed(QAbstractButton* button) {
264 switch (m_ui.buttonBox->standardButton(button)) {
265 case QDialogButtonBox::Reset:
266 emit reset();
267 break;
268 case QDialogButtonBox::Ok:
269 emit saved();
270 close();
271 break;
272 case QDialogButtonBox::RestoreDefaults:
273 emit resetToDefault();
274 break;
275 default:
276 break;
277 }
278}
279
280#endif