7#include <QRegularExpression>
13 QVBoxLayout *layout =
new QVBoxLayout(
this);
15 QLabel *instructionLabel =
new QLabel(
"Enter the name of the shader file:",
this);
16 layout->addWidget(instructionLabel);
22 QLabel *shaderTypeLabel =
new QLabel(
"Shader type:",
this);
23 layout->addWidget(shaderTypeLabel);
35 cacheShaderCheckBox->setToolTip(
"Create a texture-cache shader starter with frame-cache and spectrum-history sampling.");
38 connect(
shaderTypeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [
this](
int index) {
39 const bool createComputeShader = index == 1;
44 QHBoxLayout *buttonLayout =
new QHBoxLayout();
45 okButton =
new QPushButton(
"OK",
this);
53 layout->addLayout(buttonLayout);
56 setWindowTitle(
"Create New Shader");
63 if (shaderName.isEmpty()) {
64 QMessageBox::warning(
this,
"Warning",
"Please enter a shader name.");
68 if (shaderName.endsWith(
".glsl", Qt::CaseInsensitive) || shaderName.endsWith(
".frag", Qt::CaseInsensitive)) {
70 }
else if (shaderName.endsWith(
".comp", Qt::CaseInsensitive)) {
74 if (shaderName.isEmpty()) {
75 QMessageBox::warning(
this,
"Warning",
"Please enter a shader name.");
79 static const QRegularExpression safeName(QStringLiteral(
"^[A-Za-z0-9][A-Za-z0-9_.-]*$"));
80 if (!safeName.match(shaderName).hasMatch() || shaderName ==
"." || shaderName ==
"..") {
81 QMessageBox::warning(
this,
"Warning",
"Shader names may contain only letters, numbers, '.', '_', and '-'.");
87 if (createCacheShader && !shaderName.endsWith(
"_cache", Qt::CaseInsensitive)) {
88 shaderName +=
"_cache";
92 QString relativeShaderName = shaderName;
94 relativeShaderName = QStringLiteral(
"compute/") + shaderName;
95 const QString absoluteShaderName = QDir(
shaderPath).filePath(relativeShaderName);
96 if (QFileInfo::exists(absoluteShaderName)) {
97 QMessageBox::warning(
this,
"Shader Already Exists", QString(
"The shader already exists and was not changed:\n%1").arg(absoluteShaderName));
100 if (!QDir().mkpath(QFileInfo(absoluteShaderName).absolutePath())) {
101 QMessageBox::critical(
this,
"Error",
"Failed to create the shader directory.");
106 if (!
createShaderFile(absoluteShaderName, includeDefaultCode, createCacheShader, createComputeShader)) {
110 QString manifestError;
111 bool manifestCreated =
false;
113 QStringList manifestShaders;
115 QFile::remove(absoluteShaderName);
116 QMessageBox::critical(
this,
"Error", manifestError);
119 if (!manifestShaders.contains(relativeShaderName, Qt::CaseInsensitive)) {
120 manifestShaders.append(relativeShaderName);
127 if (!manifestCreated) {
128 QFile::remove(absoluteShaderName);
129 QMessageBox::critical(
this,
"Error", manifestError);
132 QMessageBox::information(
this,
"Success",
"Shader file created successfully.");
145uniform sampler2D samp;
148 color = texture(samp, tc);
156uniform sampler2D samp;
157uniform sampler2DArray history;
158uniform int history_head;
162#ifndef CACHE_HISTORY_LAYER
163#define CACHE_HISTORY_LAYER(index) ((history_head + (index)) % SIZE)
166uniform sampler1D spectrum0;
167uniform sampler1DArray spectrum_history;
168uniform int spectrum_history_head;
169uniform int spectrum_history_size;
170#ifndef SPECTRUM_HISTORY_LAYER
171#define SPECTRUM_HISTORY_LAYER(index) ((spectrum_history_head - ((index) % max(spectrum_history_size, 1)) + max(spectrum_history_size, 1)) % max(spectrum_history_size, 1))
174vec4 sample_cache(int index, vec2 uv) {
175 int cache_index = clamp(index, 0, SIZE - 1);
176 return texture(history,
177 vec3(uv, float(CACHE_HISTORY_LAYER(cache_index))));
180float sample_spectrum_history(int index, float frequency) {
181 int maximum_age = max(min(SIZE, spectrum_history_size) - 1, 0);
182 int spectrum_age = min(max(index, 0) + 1, maximum_age);
183 return texture(spectrum_history,
184 vec2(clamp(frequency, 0.0, 1.0),
185 float(SPECTRUM_HISTORY_LAYER(spectrum_age))))
190 vec4 live_frame = texture(samp, tc);
191 vec4 cached_frame = sample_cache(SIZE - 1, tc);
192 float live_energy = texture(spectrum0, 0.08).r;
193 float history_energy = sample_spectrum_history(0, 0.08);
194 float cache_mix = clamp(0.25 + history_energy * 0.5, 0.0, 0.75);
196 color = mix(live_frame, cached_frame, cache_mix);
197 color.rgb *= 1.0 + live_energy * 0.2;
202layout(local_size_x = 16, local_size_y = 16) in;
203layout(rgba16f, binding = 0) writeonly uniform image2D outputImage;
205uniform sampler2D samp;
206uniform vec2 iResolution;
209 ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
210 if (pixel.x >= int(iResolution.x) || pixel.y >= int(iResolution.y))
213 vec2 uv = (vec2(pixel) + 0.5) / iResolution;
214 imageStore(outputImage, pixel, textureLod(samp, uv, 0.0));
219layout(local_size_x = 16, local_size_y = 16) in;
220layout(rgba16f, binding = 0) writeonly uniform image2D outputImage;
222uniform sampler2D samp;
223uniform vec2 iResolution;
228#ifndef USE_HISTORY_TEXTURE_ARRAY
229#define USE_HISTORY_TEXTURE_ARRAY 0
232#if USE_HISTORY_TEXTURE_ARRAY
233uniform sampler2DArray history;
234uniform int history_head;
236vec4 sample_oldest_frame(vec2 uv) {
237 return textureLod(history, vec3(uv, float(history_head)), 0.0);
240uniform sampler2D textures[SIZE];
242vec4 sample_oldest_frame(vec2 uv) {
243 return textureLod(textures[0], uv, 0.0);
248 ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
249 if (pixel.x >= int(iResolution.x) || pixel.y >= int(iResolution.y))
252 vec2 uv = (vec2(pixel) + 0.5) / iResolution;
253 vec4 live_frame = textureLod(samp, uv, 0.0);
254 vec4 cached_frame = sample_oldest_frame(uv);
255 imageStore(outputImage, pixel, mix(live_frame, cached_frame, 0.5));
261layout(location = 0) in vec2 tc;
262layout(location = 0) out vec4 color;
263layout(set = 0, binding = 0) uniform sampler2D samp;
265layout(set = 0, binding = 1, std140) uniform SpriteExtended {
271 vec4 custom_uniforms[16];
276#define iResolution ext.u0.zw
277#define time_f ext.u2.y
280 color = texture(samp, tc);
286layout(location = 0) in vec2 tc;
287layout(location = 0) out vec4 color;
288layout(set = 0, binding = 0) uniform sampler2D samp;
289layout(set = 0, binding = 2) uniform sampler2DArray history;
291layout(set = 0, binding = 1, std140) uniform SpriteExtended {
297 vec4 custom_uniforms[16];
302#define history_head int(ext.u3.x + 0.5)
303#define history_size max(int(ext.u3.y), 1)
306 vec4 live_frame = texture(samp, tc);
307 int cache_layer = history_head % history_size;
308 vec4 cached_frame = texture(history, vec3(tc, float(cache_layer)));
309 color = mix(live_frame, cached_frame, 0.5);
315layout(local_size_x = 16, local_size_y = 16) in;
316layout(set = 0, binding = 5, rgba8) writeonly uniform image2D outputImage;
317layout(set = 0, binding = 0) uniform sampler2D samp;
319layout(set = 0, binding = 1, std140) uniform SpriteExtended {
325 vec4 custom_uniforms[16];
331 ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
332 ivec2 size = imageSize(outputImage);
333 if (any(greaterThanEqual(pixel, size)))
335 vec2 uv = (vec2(pixel) + 0.5) / vec2(size);
336 imageStore(outputImage, pixel, textureLod(samp, uv, 0.0));
342layout(local_size_x = 16, local_size_y = 16) in;
343layout(set = 0, binding = 5, rgba8) writeonly uniform image2D outputImage;
344layout(set = 0, binding = 0) uniform sampler2D samp;
345layout(set = 0, binding = 2) uniform sampler2DArray history;
347layout(set = 0, binding = 1, std140) uniform SpriteExtended {
353 vec4 custom_uniforms[16];
358#define history_head int(ext.u3.x + 0.5)
359#define history_size max(int(ext.u3.y), 1)
362 ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
363 ivec2 size = imageSize(outputImage);
364 if (any(greaterThanEqual(pixel, size)))
366 vec2 uv = (vec2(pixel) + 0.5) / vec2(size);
367 vec4 live_frame = textureLod(samp, uv, 0.0);
368 int cache_layer = history_head % history_size;
370 textureLod(history, vec3(uv, float(cache_layer)), 0.0);
371 imageStore(outputImage, pixel, mix(live_frame, cached_frame, 0.5));
377 QFile file(shaderName);
378 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
379 QTextStream out(&file);
380 if (includeDefaultCode) {
382 if (createComputeShader) {
383 out << (createCacheShader ? ACMXVK_COMPUTE_CACHE_SHADER_CODE : ACMXVK_COMPUTE_SHADER_CODE);
385 out << (createCacheShader ? ACMXVK_FRAGMENT_CACHE_SHADER_CODE : ACMXVK_FRAGMENT_SHADER_CODE);
387 }
else if (createComputeShader) {
388 out << (createCacheShader ? COMPUTE_CACHE_SHADER_CODE : COMPUTE_SHADER_CODE);
390 out << (createCacheShader ? CACHE_SHADER_CODE : DEFAULT_SHADER_CODE);
397 QMessageBox::critical(
this,
"Error",
"Failed to create shader file.");
QCheckBox * defaultCodeCheckBox
ShaderDialog(acmx2::Backend backend, QWidget *parent=nullptr)
void setShaderPath(const QString &path)
Set output directory used for generated shader files.
QPushButton * cancelButton
bool createShaderFile(const QString &shaderName, bool includeDefaultCode, bool createCacheShader, bool createComputeShader)
QLineEdit * shaderNameEdit
QCheckBox * cacheShaderCheckBox
void onCancelButtonClicked()
QComboBox * shaderTypeComboBox
bool create_acmxvk_source_manifest_for_shaders(const QString &rootDirectory, const QStringList &shaderFiles, const QString &outputPath, AcmxvkSourceManifestResult &result, QString &error)
Generate a source manifest from an explicit subset of files below root.
void applyCustomStyleIfEnabled(QWidget *widget)
bool load_shader_manifest(const QString &directory, QStringList &shaders, QString &error)
Load shader filenames from the preferred manifest in a directory.
bool append_shader_manifest(const QString &directory, const QString &shader, QString &error)
Add one shader to the preferred manifest if it is not already present.
constexpr const char * ACMXVK_COMPUTE_SHADER_CODE
constexpr const char * COMPUTE_CACHE_SHADER_CODE
constexpr const char * ACMXVK_FRAGMENT_CACHE_SHADER_CODE
constexpr const char * ACMXVK_FRAGMENT_SHADER_CODE
constexpr const char * COMPUTE_SHADER_CODE
constexpr const char * ACMXVK_COMPUTE_CACHE_SHADER_CODE
constexpr const char * CACHE_SHADER_CODE
constexpr const char * DEFAULT_SHADER_CODE
Optional JSON and legacy text shader-library manifests.
Dialog for creating a new shader file.