all repos — mgba @ c69c34ab2d419fa5ca605094e4afbb8f9e406a47

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