ACMX 2.139.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
uniform-reference.cpp
Go to the documentation of this file.
2#include "custom_style.hpp"
3
4#include <QDialogButtonBox>
5#include <QHBoxLayout>
6#include <QHash>
7#include <QLabel>
8#include <QLineEdit>
9#include <QListWidget>
10#include <QPlainTextEdit>
11#include <QSplitter>
12#include <QVBoxLayout>
13
14namespace {
15 struct UniformInfo {
16 const char *name;
17 const char *type;
18 const char *category;
19 const char *availability;
20 const char *description;
21 const char *example;
22 };
23
24 constexpr UniformInfo UNIFORMS[] = {
25 {"samp", "sampler2D", "Core", "Always available", "The current video, camera, image, or previous multipass output. Texture coordinates normally use tc in the 0.0 to 1.0 range.", "vec4 source = texture(samp, tc);"},
26 {"alpha", "float", "Core", "Always available", "ACMX2's animated compatibility value. It can be used as a phase, blend amount after clamping, or animation control.", "float blend = clamp(alpha, 0.0, 1.0);"},
27 {"iTime", "float", "Time", "Always available", "Elapsed wall-clock time in seconds since rendering started.", "float wave = sin(iTime);"},
28 {"time_f", "float", "Time", "Always available", "Controllable shader time. It follows ACMX2 time pause, stepping, speed, and audio-reactive time controls.", "vec2 uv = tc + 0.02 * sin(time_f);"},
29 {"time_speed", "float", "Time", "Always available", "The current rate used to advance time_f.", "float speed = max(time_speed, 0.001);"},
30 {"iFrame", "int", "Time", "Always available", "The number of rendered frames since startup, wrapped before the signed integer limit.", "float alternate = float(iFrame % 2);"},
31 {"iTimeDelta", "float", "Time", "Always available", "Elapsed seconds since the preceding rendered frame.", "float frame_motion = velocity * iTimeDelta;"},
32 {"iFrameRate", "float", "Time", "Always available", "The current target or reported output frame rate.", "float frame_seconds = 1.0 / max(iFrameRate, 1.0);"},
33 {"iResolution", "vec2", "Viewport", "Always available", "Current render width and height in pixels. Divide x by y when correcting texture coordinates for aspect ratio.", "vec2 p = (tc - 0.5) * vec2(iResolution.x / iResolution.y, 1.0);"},
34 {"iMouse", "vec4", "Input", "Always available", "Mouse state in render coordinates: xy is the current position, z is 1 while the left button is held, and w is 1 while the right button is held.", "vec2 mouse_uv = iMouse.xy / max(iResolution, vec2(1.0));"},
35 {"iMouseClick", "vec2", "Input", "Always available", "Position in render pixels where the most recent left click began.", "vec2 click_uv = iMouseClick / max(iResolution, vec2(1.0));"},
36 {"iDate", "vec4", "Time", "Always available", "Local date and time as year, month, day, and seconds since midnight.", "float day_phase = iDate.w / 86400.0;"},
37 {"iChannelTime[4]", "float", "Compatibility", "Reserved Shadertoy compatibility", "Per-channel playback times. ACMX2 resolves these names for compatibility; channels without a separate clock remain at their default value.", "float channel_time = iChannelTime[0];"},
38 {"iChannelResolution[4]", "vec3", "Compatibility", "Reserved Shadertoy compatibility", "Per-channel texture dimensions. Channels without separately supplied dimensions remain at their default value.", "vec2 channel_size = iChannelResolution[0].xy;"},
39 {"mv_matrix", "mat4", "Transform", "Primarily used by the vertex stage", "The active model-view transform. The engine sets it for its 2D and 3D render paths.", "vec4 view_position = mv_matrix * position;"},
40 {"proj_matrix", "mat4", "Transform", "Primarily used by the vertex stage", "The active projection transform used with mv_matrix.", "gl_Position = proj_matrix * mv_matrix * position;"},
41
42 {"history", "sampler2DArray", "Frame cache", "Cache shader with texture-array cache enabled", "All cached video frames in one 2D array texture. Select a layer with the third texture coordinate; this is the preferred cache access method.", "vec4 old_frame = texture(history, vec3(tc, float(CACHE_HISTORY_LAYER(index))));"},
43 {"history_head", "int", "Frame cache", "Used with history", "Physical array layer corresponding to logical cache index zero. Map an index with (history_head + index) % SIZE.", "int layer = (history_head + index) % SIZE;"},
44 {"textures[SIZE]", "sampler2D", "Frame cache", "Cache shader using sampler-array mode", "Scalable array of individual cached-frame samplers. history is preferred when texture-array cache mode is enabled.", "vec4 old_frame = texture(textures[index], tc);"},
45 {"samp1", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 0. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp1, tc);"},
46 {"samp2", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 1. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp2, tc);"},
47 {"samp3", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 2. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp3, tc);"},
48 {"samp4", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 3. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp4, tc);"},
49 {"samp5", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 4. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp5, tc);"},
50 {"samp6", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 5. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp6, tc);"},
51 {"samp7", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 6. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp7, tc);"},
52 {"samp8", "sampler2D", "Frame cache", "Legacy cache binding", "Legacy cached-frame sampler for logical cache slot 7. Prefer history for new cache shaders.", "vec4 old_frame = texture(samp8, tc);"},
53
54 {"amp", "float", "Audio", "Requires an audio-enabled build and audio processing", "Current overall audio amplitude after ACMX2 sensitivity and startup-envelope scaling.", "float pulse = 1.0 + amp * 0.2;"},
55 {"uamp", "float", "Audio", "Requires an audio-enabled build and audio processing", "Compatibility value associated with the unscaled audio amplitude path.", "float raw_audio = uamp;"},
56 {"iamp", "float", "Audio", "Requires an audio-enabled build and audio processing", "Estimated dominant audio frequency in hertz.", "float normalized_frequency = iamp / max(iSampleRate, 1.0);"},
57 {"amp_peak", "float", "Audio", "Requires an audio-enabled build and audio processing", "Peak audio energy for sharp transients such as kicks and claps, scaled by sensitivity.", "float flash = smoothstep(0.7, 1.0, amp_peak);"},
58 {"amp_rms", "float", "Audio", "Requires an audio-enabled build and audio processing", "Root-mean-square audio energy, useful as a steadier loudness estimate.", "float loudness = amp_rms;"},
59 {"amp_smooth", "float", "Audio", "Requires an audio-enabled build and audio processing", "Smoothed amplitude that avoids abrupt frame-to-frame changes.", "float scale = 1.0 + amp_smooth * 0.1;"},
60 {"amp_low", "float", "Audio", "Requires an audio-enabled build and audio processing", "Low-frequency or bass-band energy, scaled by sensitivity.", "float bass = amp_low;"},
61 {"amp_mid", "float", "Audio", "Requires an audio-enabled build and audio processing", "Mid-frequency energy, scaled by sensitivity.", "float middle = amp_mid;"},
62 {"amp_high", "float", "Audio", "Requires an audio-enabled build and audio processing", "High-frequency or treble-band energy, scaled by sensitivity.", "float treble = amp_high;"},
63 {"iSampleRate", "float", "Audio", "Available in audio-enabled builds", "Audio capture sample rate in hertz, commonly 44100 or 48000.", "float nyquist = iSampleRate * 0.5;"},
64 {"spectrum", "sampler1D", "Audio", "Requires an audio-enabled build and audio processing", "Current FFT magnitudes. Coordinate 0 is DC and 1 is the Nyquist frequency; read energy from the red channel.", "float bass = texture(spectrum, 0.03).r;"},
65 {"spectrum0", "sampler1D", "Audio", "Requires an audio-enabled build and audio processing", "Alias of spectrum for the current FFT frame. It is the preferred live-spectrum name in cache shaders.", "float treble = texture(spectrum0, 0.75).r;"},
66 {"spectrum_history", "sampler1DArray", "Audio history", "Requires audio buffers enabled", "Rolling FFT history held in a 1D array texture. Sample it with vec2(frequency, physical layer) and read the red channel.", "float old_bass = texture(spectrum_history, vec2(0.03, float(layer))).r;"},
67 {"spectrum_history_head", "int", "Audio history", "Used with spectrum_history", "Physical spectrum-history layer containing the newest FFT frame.", "int layer = SPECTRUM_HISTORY_LAYER(age);"},
68 {"spectrum_history_size", "int", "Audio history", "Used with spectrum_history", "Runtime number of allocated FFT-history layers. Protect modulo operations with max(size, 1).", "int count = max(spectrum_history_size, 1);"},
69
70 {"value_alpha_r", "float", "acidcamGL compatibility", "Always available", "Animated red-channel compatibility value. It is also exposed as alpha_r.", "color.r *= value_alpha_r;"},
71 {"value_alpha_g", "float", "acidcamGL compatibility", "Always available", "Animated green-channel compatibility value. It is also exposed as alpha_g.", "color.g *= value_alpha_g;"},
72 {"value_alpha_b", "float", "acidcamGL compatibility", "Always available", "Animated blue-channel compatibility value. It is also exposed as alpha_b.", "color.b *= value_alpha_b;"},
73 {"alpha_r", "float", "acidcamGL compatibility", "Always available", "Alias of value_alpha_r for legacy shaders.", "color.r *= alpha_r;"},
74 {"alpha_g", "float", "acidcamGL compatibility", "Always available", "Alias of value_alpha_g for legacy shaders.", "color.g *= alpha_g;"},
75 {"alpha_b", "float", "acidcamGL compatibility", "Always available", "Alias of value_alpha_b for legacy shaders.", "color.b *= alpha_b;"},
76 {"alpha_value", "float", "acidcamGL compatibility", "Always available", "Alias of the engine's animated alpha compatibility value.", "float phase = alpha_value;"},
77 {"index_value", "float", "acidcamGL compatibility", "Always available", "Zero-based index of the active shader in the loaded library.", "float shader_seed = index_value;"},
78 {"optx", "vec4", "acidcamGL compatibility", "Always available", "Legacy option vector maintained by ACMX2; its default components are 0.5.", "vec2 option_pair = optx.xy;"},
79 {"random_var", "vec4", "acidcamGL compatibility", "Always available", "Four engine-generated random values refreshed with the legacy uniform animation state.", "vec2 jitter = random_var.xy / 255.0;"},
80 {"restore_black", "float", "acidcamGL compatibility", "Always available", "Legacy restore-black flag represented as 0.0 or 1.0.", "color.rgb *= 1.0 - restore_black;"},
81 {"inc_value", "vec4", "acidcamGL compatibility", "Always available", "Primary legacy increment vector maintained for acidcamGL shader compatibility.", "vec2 offset = inc_value.xy;"},
82 {"inc_valuex", "vec4", "acidcamGL compatibility", "Always available", "Secondary legacy increment vector maintained for acidcamGL shader compatibility.", "vec2 offset = inc_valuex.xy;"},
83
84 {"slider1", "float", "MIDI", "Requires a MIDI-enabled build", "Value from the MIDI control mapped to Slider 1, normalized to the 0.0 to 1.0 range.", "float amount = slider1;"},
85 {"slider2", "float", "MIDI", "Requires a MIDI-enabled build", "Value from the MIDI control mapped to Slider 2, normalized to the 0.0 to 1.0 range.", "float amount = slider2;"},
86 {"slider3", "float", "MIDI", "Requires a MIDI-enabled build", "Value from the MIDI control mapped to Slider 3, normalized to the 0.0 to 1.0 range.", "float amount = slider3;"},
87 {"slider4", "float", "MIDI", "Requires a MIDI-enabled build", "Value from the MIDI control mapped to Slider 4, normalized to the 0.0 to 1.0 range.", "float amount = slider4;"},
88 };
89
90 QString acmxvkDefine(const UniformInfo &uniform) {
91 static const QHash<QString, QString> DEFINES = {
92 {QStringLiteral("alpha"), QStringLiteral("ext.u0.x")},
93 {QStringLiteral("iTime"), QStringLiteral("ext.u0.y")},
94 {QStringLiteral("iResolution"), QStringLiteral("ext.u0.zw")},
95 {QStringLiteral("iMouse"), QStringLiteral("ext.mouse")},
96 {QStringLiteral("iTimeDelta"), QStringLiteral("ext.u1.x")},
97 {QStringLiteral("amp"), QStringLiteral("ext.u1.y")},
98 {QStringLiteral("iamp"), QStringLiteral("ext.u1.z")},
99 {QStringLiteral("iFrameRate"), QStringLiteral("ext.u1.w")},
100 {QStringLiteral("iFrame"), QStringLiteral("int(ext.u2.x)")},
101 {QStringLiteral("time_f"), QStringLiteral("ext.u2.y")},
102 {QStringLiteral("iSampleRate"), QStringLiteral("ext.u2.z")},
103 {QStringLiteral("amp_peak"), QStringLiteral("ext.u2.w")},
104 {QStringLiteral("history_head"), QStringLiteral("int(ext.u3.x)")},
105 {QStringLiteral("amp_rms"), QStringLiteral("ext.u3.z")},
106 {QStringLiteral("amp_smooth"), QStringLiteral("ext.u3.w")},
107 {QStringLiteral("amp_low"), QStringLiteral("ext.audio_bands.x")},
108 {QStringLiteral("amp_mid"), QStringLiteral("ext.audio_bands.y")},
109 {QStringLiteral("amp_high"), QStringLiteral("ext.audio_bands.z")},
110 {QStringLiteral("spectrum_history_head"), QStringLiteral("int(ext.audio_history.x)")},
111 {QStringLiteral("spectrum_history_size"), QStringLiteral("int(ext.audio_history.y)")},
112 {QStringLiteral("alpha_value"), QStringLiteral("ext.custom_uniforms[0].y")},
113 {QStringLiteral("alpha_r"), QStringLiteral("ext.custom_uniforms[0].z")},
114 {QStringLiteral("alpha_g"), QStringLiteral("ext.custom_uniforms[0].w")},
115 {QStringLiteral("alpha_b"), QStringLiteral("ext.custom_uniforms[1].x")},
116 {QStringLiteral("value_alpha_r"), QStringLiteral("ext.custom_uniforms[1].y")},
117 {QStringLiteral("value_alpha_g"), QStringLiteral("ext.custom_uniforms[1].z")},
118 {QStringLiteral("value_alpha_b"), QStringLiteral("ext.custom_uniforms[1].w")},
119 {QStringLiteral("index_value"), QStringLiteral("ext.custom_uniforms[2].x")},
120 {QStringLiteral("restore_black"), QStringLiteral("ext.custom_uniforms[2].y")},
121 {QStringLiteral("time_speed"), QStringLiteral("ext.custom_uniforms[3].y")},
122 {QStringLiteral("slider1"), QStringLiteral("ext.custom_uniforms[5].x")},
123 {QStringLiteral("slider2"), QStringLiteral("ext.custom_uniforms[5].y")},
124 {QStringLiteral("slider3"), QStringLiteral("ext.custom_uniforms[5].z")},
125 {QStringLiteral("slider4"), QStringLiteral("ext.custom_uniforms[5].w")},
126 };
127 const QString name = QString::fromLatin1(uniform.name);
128 const auto define = DEFINES.constFind(name);
129 if (define == DEFINES.constEnd())
130 return QString();
131 return QStringLiteral("#define %1 %2").arg(name, define.value());
132 }
133
134 QString detailsFor(const UniformInfo &uniform, acmx2::Backend backend) {
135 QString declaration = QStringLiteral("Declaration:\nuniform %1 %2;").arg(QString::fromLatin1(uniform.type), QString::fromLatin1(uniform.name));
136 if (backend == acmx2::Backend::Acmxvk) {
137 const QString define = acmxvkDefine(uniform);
138 if (!define.isEmpty()) {
139 declaration = QStringLiteral("ACMXVK alias (copy after the required "
140 "SpriteExtended block):\n%1")
141 .arg(define);
142 } else {
143 declaration = QStringLiteral("ACMXVK resource declaration:\nuniform %1 %2;\n\n"
144 "This value is not an alias within SpriteExtended, "
145 "so it does not use a #define.")
146 .arg(QString::fromLatin1(uniform.type), QString::fromLatin1(uniform.name));
147 }
148 }
149 return QStringLiteral("%1\n\nType: %2\nCategory: %3\nAvailability: %4\n\n%5\n\n%6\n\nExample:\n%7").arg(QString::fromLatin1(uniform.name), QString::fromLatin1(uniform.type), QString::fromLatin1(uniform.category), QString::fromLatin1(uniform.availability), declaration, QString::fromLatin1(uniform.description), QString::fromLatin1(uniform.example));
150 }
151} // namespace
152
153UniformReferenceDialog::UniformReferenceDialog(acmx2::Backend backend, QWidget *parent) : QDialog(parent), activeBackend(backend) {
154 setWindowTitle(tr("Built-in Uniform Reference"));
155 resize(900, 580);
156 setModal(false);
157
158 auto *mainLayout = new QVBoxLayout(this);
159 auto *introLabel = new QLabel(tr("Select a uniform to see its GLSL type, availability, and usage."), this);
160 introLabel->setWordWrap(true);
161 mainLayout->addWidget(introLabel);
162
163 searchEdit = new QLineEdit(this);
164 searchEdit->setPlaceholderText(tr("Filter uniforms..."));
165 searchEdit->setClearButtonEnabled(true);
166 mainLayout->addWidget(searchEdit);
167
168 auto *splitter = new QSplitter(Qt::Horizontal, this);
169 uniformList = new QListWidget(splitter);
170 uniformList->setMinimumWidth(260);
171 descriptionView = new QPlainTextEdit(splitter);
172 descriptionView->setReadOnly(true);
173 descriptionView->setLineWrapMode(QPlainTextEdit::WidgetWidth);
174 splitter->addWidget(uniformList);
175 splitter->addWidget(descriptionView);
176 splitter->setStretchFactor(0, 0);
177 splitter->setStretchFactor(1, 1);
178 mainLayout->addWidget(splitter, 1);
179
180 auto *buttons = new QDialogButtonBox(QDialogButtonBox::Close, this);
181 mainLayout->addWidget(buttons);
182
183 connect(searchEdit, &QLineEdit::textChanged, this, &UniformReferenceDialog::filterUniforms);
184 connect(uniformList, &QListWidget::currentItemChanged, this, [this](QListWidgetItem *current, QListWidgetItem *) { showUniformDetails(current); });
185 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::close);
186
189}
190
192 uniformList->clear();
193 for (const UniformInfo &uniform : UNIFORMS) {
194 auto *item = new QListWidgetItem(QString::fromLatin1(uniform.name), uniformList);
195 item->setData(Qt::UserRole, detailsFor(uniform, activeBackend));
196 item->setData(Qt::UserRole + 1, QStringLiteral("%1 %2 %3").arg(QString::fromLatin1(uniform.name), QString::fromLatin1(uniform.type), QString::fromLatin1(uniform.category)));
197 item->setToolTip(QString::fromLatin1(uniform.category));
198 }
199 if (uniformList->count() > 0)
200 uniformList->setCurrentRow(0);
201}
202
204 if (activeBackend == backend)
205 return;
206 activeBackend = backend;
208 filterUniforms(searchEdit->text());
209}
210
211void UniformReferenceDialog::filterUniforms(const QString &text) {
212 const QString filter = text.trimmed();
213 QListWidgetItem *firstVisible = nullptr;
214 for (int index = 0; index < uniformList->count(); ++index) {
215 QListWidgetItem *item = uniformList->item(index);
216 const bool hidden = !item->data(Qt::UserRole + 1).toString().contains(filter, Qt::CaseInsensitive);
217 item->setHidden(hidden);
218 if (!hidden && !firstVisible)
219 firstVisible = item;
220 }
221 if (firstVisible)
222 uniformList->setCurrentItem(firstVisible);
223 else
224 descriptionView->clear();
225}
226
227void UniformReferenceDialog::showUniformDetails(QListWidgetItem *current) { descriptionView->setPlainText(current ? current->data(Qt::UserRole).toString() : QString()); }
UniformReferenceDialog(acmx2::Backend backend, QWidget *parent=nullptr)
void filterUniforms(const QString &text)
void showUniformDetails(QListWidgetItem *current)
void setBackend(acmx2::Backend backend)
QPlainTextEdit * descriptionView
Backend
Definition backend.hpp:8
void applyCustomStyleIfEnabled(QWidget *widget)
QString acmxvkDefine(const UniformInfo &uniform)
QString detailsFor(const UniformInfo &uniform, acmx2::Backend backend)