all repos — mgba @ 20df8f3326347bdbabd1a0c4d7ca650f1d26b1b6

mGBA Game Boy Advance Emulator

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