all repos — mgba @ a8a73720830e4f7681f320b913ef72941b1e358e

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_GLES2)
 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	, m_shaderPath(config->getOption("shader"))
 37{
 38	m_ui.setupUi(this);
 39
 40	refreshShaders();
 41
 42	connect(m_ui.load, &QAbstractButton::clicked, this, &ShaderSelector::selectShader);
 43	connect(m_ui.unload, &QAbstractButton::clicked, this, &ShaderSelector::clearShader);
 44	connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &ShaderSelector::buttonPressed);
 45}
 46
 47ShaderSelector::~ShaderSelector() {
 48	clear();
 49}
 50
 51void ShaderSelector::clear() {
 52	m_ui.shaderName->setText(tr("No shader active"));
 53	m_ui.description->clear();
 54	m_ui.author->clear();
 55
 56	while (QWidget* page = m_ui.passes->widget(0)) {
 57		m_ui.passes->removeTab(0);
 58		delete page;
 59	}
 60}
 61
 62void ShaderSelector::selectShader() {
 63	QString path(GBAApp::dataDir());
 64	path += QLatin1String("/shaders");
 65	QFileDialog dialog(nullptr, tr("Load shader"), path);
 66	dialog.setFileMode(QFileDialog::Directory);
 67	dialog.exec();
 68	QStringList names = dialog.selectedFiles();
 69	if (names.count() == 1) {
 70		loadShader(names[0]);
 71		refreshShaders();
 72	}
 73}
 74
 75void ShaderSelector::loadShader(const QString& path) {
 76	VDir* shader = VFileDevice::openDir(path);
 77	if (!shader) {
 78		shader = VFileDevice::openArchive(path);
 79	}
 80	if (!shader) {
 81		return;
 82	}
 83	m_display->setShaders(shader);
 84	shader->close(shader);
 85	m_shaderPath = path;
 86	m_config->setOption("shader", m_shaderPath);
 87}
 88
 89void ShaderSelector::clearShader() {
 90	m_display->clearShaders();
 91	refreshShaders();
 92	m_shaderPath = "";
 93	m_config->setOption("shader", 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#if !defined(_WIN32) || defined(USE_EPOXY)
123	if (m_shaders->preprocessShader) {
124		m_ui.passes->addTab(makePage(static_cast<mGLES2Shader*>(m_shaders->preprocessShader), "default", 0), tr("Preprocessing"));
125	}
126	mGLES2Shader* shaders = static_cast<mGLES2Shader*>(m_shaders->passes);
127	QFileInfo fi(m_shaderPath);
128	for (size_t p = 0; p < m_shaders->nPasses; ++p) {
129		QWidget* page = makePage(&shaders[p], fi.baseName(), p);
130		if (page) {
131			m_ui.passes->addTab(page, tr("Pass %1").arg(p + 1));
132		}
133	}
134#endif
135}
136
137void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, const QString& name, float* value, float min, float max, int y, int x) {
138	QDoubleSpinBox* f = new QDoubleSpinBox;
139	f->setDecimals(3);
140	if (min < max) {
141		f->setMinimum(min);
142		f->setMaximum(max);
143	}
144	float def = *value;
145	bool ok = false;
146	float v = m_config->getQtOption(name, section).toFloat(&ok);
147	if (ok) {
148		*value = v;
149	}
150	f->setValue(*value);
151	f->setSingleStep(0.001);
152	f->setAccelerated(true);
153	settings->addWidget(f, y, x);
154	connect(f, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [value](double v) {
155		*value = v;
156	});
157	connect(this, &ShaderSelector::saved, [this, section, name, f]() {
158		m_config->setQtOption(name, f->value(), section);
159	});
160	connect(this, &ShaderSelector::reset, [this, section, name, f]() {
161		bool ok = false;
162		float v = m_config->getQtOption(name, section).toFloat(&ok);
163		if (ok) {
164			f->setValue(v);
165		}
166	});
167	connect(this, &ShaderSelector::resetToDefault, [def, section, name, f]() {
168		f->setValue(def);
169	});
170}
171
172void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, const QString& name, int* value, int min, int max, int y, int x) {
173	QSpinBox* i = new QSpinBox;
174	if (min < max) {
175		i->setMinimum(min);
176		i->setMaximum(max);
177	}
178	int def = *value;
179	bool ok = false;
180	int v = m_config->getQtOption(name, section).toInt(&ok);
181	if (ok) {
182		*value = v;
183	}
184	i->setValue(*value);
185	i->setSingleStep(1);
186	i->setAccelerated(true);
187	settings->addWidget(i, y, x);
188	connect(i, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [value](int v) {
189		*value = v;
190	});
191	connect(this, &ShaderSelector::saved, [this, section, name, i]() {
192		m_config->setQtOption(name, i->value(), section);
193	});
194	connect(this, &ShaderSelector::reset, [this, section, name, i]() {
195		bool ok = false;
196		int v = m_config->getQtOption(name, section).toInt(&ok);
197		if (ok) {
198			i->setValue(v);
199		}
200	});
201	connect(this, &ShaderSelector::resetToDefault, [def, section, name, i]() {
202		i->setValue(def);
203	});
204}
205
206QWidget* ShaderSelector::makePage(mGLES2Shader* shader, const QString& name, int pass) {
207#if !defined(_WIN32) || defined(USE_EPOXY)
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		mGLES2Uniform* 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#else
261	return nullptr;
262#endif
263}
264
265void ShaderSelector::buttonPressed(QAbstractButton* button) {
266	switch (m_ui.buttonBox->standardButton(button)) {
267	case QDialogButtonBox::Reset:
268		emit reset();
269		break;
270	case QDialogButtonBox::Ok:
271		emit saved();
272		close();
273		break;
274 	case QDialogButtonBox::RestoreDefaults:
275		emit resetToDefault();
276		break;
277	default:
278		break;
279	}
280}
281
282#endif