all repos — mgba @ 6e03d88818e414f25c100155a0163f65a2afd885

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