all repos — mgba @ daaa2fa5236df5e8976e7e0ba7ac39e0d9233422

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
 20extern "C" {
 21#include "core/version.h"
 22#include "platform/video-backend.h"
 23
 24#if !defined(_WIN32) || defined(USE_EPOXY)
 25#include "platform/opengl/gles2.h"
 26#endif
 27}
 28
 29using namespace QGBA;
 30
 31ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidget* parent)
 32	: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
 33	, m_display(display)
 34	, m_config(config)
 35	, m_shaderPath("")
 36{
 37	m_ui.setupUi(this);
 38
 39	refreshShaders();
 40
 41	connect(m_ui.load, SIGNAL(clicked()), this, SLOT(selectShader()));
 42	connect(m_ui.unload, SIGNAL(clicked()), this, SLOT(clearShader()));
 43	connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*)));
 44}
 45
 46ShaderSelector::~ShaderSelector() {
 47	clear();
 48}
 49
 50void ShaderSelector::clear() {
 51	m_ui.shaderName->setText(tr("No shader active"));
 52	m_ui.description->clear();
 53	m_ui.author->clear();
 54
 55	while (QWidget* page = m_ui.passes->widget(0)) {
 56		m_ui.passes->removeTab(0);
 57		delete page;
 58	}
 59}
 60
 61void ShaderSelector::selectShader() {
 62	QString path(GBAApp::dataDir());
 63	path += QLatin1String("/shaders");
 64	QFileDialog dialog(nullptr, tr("Load shader"), path, tr("%1 Shader (%.shader)").arg(projectName));
 65	dialog.setFileMode(QFileDialog::Directory);
 66	dialog.exec();
 67	QStringList names = dialog.selectedFiles();
 68	if (names.count() == 1) {
 69		loadShader(names[0]);
 70		refreshShaders();
 71	}
 72}
 73
 74void ShaderSelector::loadShader(const QString& path) {
 75	VDir* shader = VFileDevice::openDir(path);
 76	if (!shader) {
 77		shader = VFileDevice::openArchive(path);
 78	}
 79	if (!shader) {
 80		return;
 81	}
 82	m_display->setShaders(shader);
 83	shader->close(shader);
 84	m_shaderPath = path;
 85}
 86
 87void ShaderSelector::clearShader() {
 88	m_display->clearShaders();
 89	refreshShaders();
 90	m_shaderPath = "";
 91	m_config->setOption("shader", nullptr);
 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, SIGNAL(saved()), 0, 0);
117	disconnect(this, SIGNAL(reset()), 0, 0);
118	disconnect(this, SIGNAL(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::Save:
269		m_config->setOption("shader", m_shaderPath);
270		emit saved();
271		break;
272 	case QDialogButtonBox::RestoreDefaults:
273		emit resetToDefault();
274		break;
275	default:
276		break;
277	}
278}