xbintsc 0.5.37 → 0.5.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +23 -7
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/html.js +98 -15
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +1 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +7 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
- package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
- package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
- package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
- package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
- package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
- package/dist/tests/e2e/gui-perf.test.js +283 -0
- package/dist/tests/e2e/gui-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl.test.js +4 -4
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +45 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +28 -2
- package/doc/gui/canvas.md +110 -27
- package/doc/gui/known-issues.md +32 -10
- package/doc/gui/rendering.md +77 -0
- package/doc/gui/runtime.md +18 -3
- package/doc/gui/webgl.md +58 -29
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +43 -9
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +25 -2
- package/doc/zh-CN/gui/canvas.md +95 -26
- package/doc/zh-CN/gui/known-issues.md +21 -4
- package/doc/zh-CN/gui/rendering.md +59 -0
- package/doc/zh-CN/gui/runtime.md +13 -3
- package/doc/zh-CN/gui/webgl.md +30 -8
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +32 -8
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +3 -1
- package/runtime/ext_gui/canvas.cpp +135 -89
- package/runtime/ext_gui/canvas.h +116 -0
- package/runtime/ext_gui/canvas_clip.cpp +143 -0
- package/runtime/ext_gui/canvas_composite.cpp +241 -0
- package/runtime/ext_gui/canvas_coverage.cpp +241 -0
- package/runtime/ext_gui/canvas_gradient.cpp +41 -4
- package/runtime/ext_gui/canvas_image_data.cpp +4 -0
- package/runtime/ext_gui/canvas_internal.h +34 -9
- package/runtime/ext_gui/canvas_raster.cpp +207 -178
- package/runtime/ext_gui/canvas_shadow.cpp +161 -0
- package/runtime/ext_gui/canvas_stroke.cpp +37 -9
- package/runtime/ext_gui/canvas_style.cpp +59 -0
- package/runtime/ext_gui/canvas_text.cpp +140 -0
- package/runtime/ext_gui/css.h +7 -0
- package/runtime/ext_gui/css_sheet.cpp +22 -0
- package/runtime/ext_gui/document.cpp +25 -1
- package/runtime/ext_gui/document.h +8 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
- package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
- package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
- package/runtime/ext_gui/dom_api_internal.h +36 -37
- package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
- package/runtime/ext_gui/dom_api_proto.cpp +18 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
- package/runtime/ext_gui/gradient_values.cpp +12 -1
- package/runtime/ext_gui/gui.cpp +5 -0
- package/runtime/ext_gui/gui_canvas.cpp +4 -0
- package/runtime/ext_gui/gui_engine.h +32 -0
- package/runtime/ext_gui/gui_events.cpp +182 -58
- package/runtime/ext_gui/gui_media.cpp +3 -0
- package/runtime/ext_gui/image.cpp +69 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
- package/runtime/ext_gui/layout.cpp +4 -0
- package/runtime/ext_gui/paint.cpp +19 -0
- package/runtime/ext_gui/paint_background.cpp +18 -1
- package/runtime/ext_gui/perf.cpp +171 -0
- package/runtime/ext_gui/perf.h +127 -0
- package/runtime/ext_gui/renderer.cpp +109 -41
- package/runtime/ext_gui/renderer.h +33 -9
- package/runtime/ext_gui/renderer_image.cpp +135 -65
- package/runtime/ext_gui/renderer_internal.h +15 -3
- package/runtime/ext_gui/renderer_text.cpp +24 -12
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/style.cpp +101 -30
- package/runtime/ext_gui/style.h +6 -3
- package/runtime/ext_gui/style_print.cpp +7 -1
- package/runtime/ext_gui/text.cpp +23 -4
- package/runtime/ext_gui/text.h +29 -0
- package/runtime/ext_gui/text_cache.cpp +118 -0
- package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
- package/runtime/ext_gui/webgl.h +48 -146
- package/runtime/ext_gui/webgl_blit.cpp +12 -1
- package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
- package/runtime/ext_gui/webgl_buffer.cpp +59 -1
- package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
- package/runtime/ext_gui/webgl_clear.cpp +3 -0
- package/runtime/ext_gui/webgl_context.cpp +29 -3
- package/runtime/ext_gui/webgl_draw.cpp +75 -270
- package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
- package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
- package/runtime/ext_gui/webgl_draw_state.h +130 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_objects.h +148 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
- package/runtime/ext_gui/webgl_program_internal.h +35 -0
- package/runtime/ext_gui/webgl_shader.cpp +34 -36
- package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
- package/runtime/ext_gui/window.cpp +56 -1
- package/runtime/rt.h +3 -0
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/module.ts +22 -7
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +100 -17
- package/src/extensions/gui/index.ts +1 -0
- package/src/extensions/gui/three.ts +4 -3
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — `#pragma unroll_loop_start` expansion.
|
|
3
|
+
*
|
|
4
|
+
* three.js marks its material light loops with
|
|
5
|
+
*
|
|
6
|
+
* #pragma unroll_loop_start
|
|
7
|
+
* for ( int i = 0; i < N; i ++ ) { ... UNROLLED_LOOP_INDEX ... [ i ] ... }
|
|
8
|
+
* #pragma unroll_loop_end
|
|
9
|
+
*
|
|
10
|
+
* and expands them in JavaScript before upload. On this runtime the expansion
|
|
11
|
+
* is best done here: the loop body indexes sampler arrays (`shadowMap[ i ]`),
|
|
12
|
+
* and Vulkan GLSL only accepts a *constant* sampler-array index. Expanding the
|
|
13
|
+
* pragma turns every `i` (and `UNROLLED_LOOP_INDEX`) into a literal, so the
|
|
14
|
+
* shadow-map chunks compile without relying on the regex engine or on three.js
|
|
15
|
+
* having unrolled them first. WebGL ignores the unknown pragmas either way.
|
|
16
|
+
*
|
|
17
|
+
* The transformation is deliberately small: only the `for (int i = A; i < B;
|
|
18
|
+
* i ++)` shape three.js emits is recognised. Anything else is left untouched.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
#include "webgl_glsl.h"
|
|
22
|
+
|
|
23
|
+
#include <cctype>
|
|
24
|
+
#include <cstdlib>
|
|
25
|
+
#include <string>
|
|
26
|
+
#include <vector>
|
|
27
|
+
|
|
28
|
+
namespace xtgui {
|
|
29
|
+
namespace {
|
|
30
|
+
|
|
31
|
+
const char *const kStartPragma = "#pragma unroll_loop_start";
|
|
32
|
+
const char *const kEndPragma = "#pragma unroll_loop_end";
|
|
33
|
+
|
|
34
|
+
/** Skip whitespace and comments starting at `at`. */
|
|
35
|
+
size_t skip_space(const std::string &text, size_t at) {
|
|
36
|
+
while (at < text.size()) {
|
|
37
|
+
const char c = text[at];
|
|
38
|
+
if (std::isspace((unsigned char)c) != 0) {
|
|
39
|
+
at++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (c == '/' && at + 1 < text.size() && text[at + 1] == '/') {
|
|
43
|
+
at += 2;
|
|
44
|
+
while (at < text.size() && text[at] != '\n') at++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (c == '/' && at + 1 < text.size() && text[at + 1] == '*') {
|
|
48
|
+
at += 2;
|
|
49
|
+
while (at + 1 < text.size() && !(text[at] == '*' && text[at + 1] == '/')) at++;
|
|
50
|
+
if (at + 1 < text.size()) at += 2;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
return at;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Index just past the end of a `//` / `#` line starting at `at`. */
|
|
59
|
+
size_t line_end(const std::string &text, size_t at) {
|
|
60
|
+
while (at < text.size() && text[at] != '\n') at++;
|
|
61
|
+
return at;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Match the `}` for the `{` at `open`, skipping comments. */
|
|
65
|
+
size_t matching_brace(const std::string &text, size_t open, size_t *out) {
|
|
66
|
+
int depth = 0;
|
|
67
|
+
for (size_t i = open; i < text.size(); i++) {
|
|
68
|
+
const char c = text[i];
|
|
69
|
+
if (c == '/' && i + 1 < text.size() && text[i + 1] == '/') {
|
|
70
|
+
i += 2;
|
|
71
|
+
while (i < text.size() && text[i] != '\n') i++;
|
|
72
|
+
if (i < text.size()) continue;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
if (c == '/' && i + 1 < text.size() && text[i + 1] == '*') {
|
|
76
|
+
i += 2;
|
|
77
|
+
while (i + 1 < text.size() && !(text[i] == '*' && text[i + 1] == '/')) i++;
|
|
78
|
+
if (i + 1 < text.size()) i++;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (c == '{') depth++;
|
|
82
|
+
if (c == '}') {
|
|
83
|
+
depth--;
|
|
84
|
+
if (depth == 0) {
|
|
85
|
+
*out = i;
|
|
86
|
+
return i;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return std::string::npos;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
struct LoopHeader {
|
|
94
|
+
std::string variable;
|
|
95
|
+
long start = 0;
|
|
96
|
+
long end = 0;
|
|
97
|
+
bool inclusive = false;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** Tokenise a for-header into identifiers, numbers and punctuation. */
|
|
101
|
+
std::vector<std::string> tokens(const std::string &text) {
|
|
102
|
+
std::vector<std::string> out;
|
|
103
|
+
size_t i = 0;
|
|
104
|
+
while (i < text.size()) {
|
|
105
|
+
const char c = text[i];
|
|
106
|
+
if (std::isspace((unsigned char)c) != 0) {
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (std::isalnum((unsigned char)c) != 0 || c == '_') {
|
|
111
|
+
size_t end = i + 1;
|
|
112
|
+
while (end < text.size() &&
|
|
113
|
+
(std::isalnum((unsigned char)text[end]) != 0 || text[end] == '_')) {
|
|
114
|
+
end++;
|
|
115
|
+
}
|
|
116
|
+
out.push_back(text.substr(i, end - i));
|
|
117
|
+
i = end;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if ((c == '+' && i + 1 < text.size() && text[i + 1] == '+') ||
|
|
121
|
+
(c == '-' && i + 1 < text.size() && text[i + 1] == '-')) {
|
|
122
|
+
out.push_back(text.substr(i, 2));
|
|
123
|
+
i += 2;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if ((c == '+' || c == '-' || c == '<' || c == '=' || c == '!') && i + 1 < text.size() &&
|
|
127
|
+
text[i + 1] == '=') {
|
|
128
|
+
out.push_back(text.substr(i, 2));
|
|
129
|
+
i += 2;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
out.push_back(std::string(1, c));
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
bool parse_header(const std::string &header, LoopHeader *out) {
|
|
139
|
+
std::vector<std::string> parts = tokens(header);
|
|
140
|
+
size_t at = 0;
|
|
141
|
+
/* Optional qualifiers (`int`, `uint`, `highp int`, …). */
|
|
142
|
+
while (at < parts.size() && (parts[at] == "int" || parts[at] == "uint" || parts[at] == "highp" ||
|
|
143
|
+
parts[at] == "mediump" || parts[at] == "lowp")) {
|
|
144
|
+
at++;
|
|
145
|
+
}
|
|
146
|
+
if (at >= parts.size()) return false;
|
|
147
|
+
out->variable = parts[at++];
|
|
148
|
+
if (at >= parts.size() || parts[at] != "=") return false;
|
|
149
|
+
at++;
|
|
150
|
+
if (at >= parts.size()) return false;
|
|
151
|
+
char *end = nullptr;
|
|
152
|
+
out->start = std::strtol(parts[at].c_str(), &end, 10);
|
|
153
|
+
if (end == nullptr || *end != 0) return false;
|
|
154
|
+
at++;
|
|
155
|
+
if (at >= parts.size() || parts[at] != ";") return false;
|
|
156
|
+
at++;
|
|
157
|
+
/* `i < N` / `i <= N`; the repeated variable may be spelled differently. */
|
|
158
|
+
if (at >= parts.size()) return false;
|
|
159
|
+
at++;
|
|
160
|
+
if (at >= parts.size()) return false;
|
|
161
|
+
out->inclusive = parts[at] == "<=";
|
|
162
|
+
if (!out->inclusive && parts[at] != "<") return false;
|
|
163
|
+
at++;
|
|
164
|
+
if (at >= parts.size()) return false;
|
|
165
|
+
out->end = std::strtol(parts[at].c_str(), &end, 10);
|
|
166
|
+
if (end == nullptr || *end != 0) return false;
|
|
167
|
+
at++;
|
|
168
|
+
if (at >= parts.size() || parts[at] != ";") return false;
|
|
169
|
+
at++;
|
|
170
|
+
if (at >= parts.size()) return false;
|
|
171
|
+
at++;
|
|
172
|
+
if (at >= parts.size() || parts[at] != "++") return false;
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Replace whole-word `name` with `value`. */
|
|
177
|
+
std::string substitute(const std::string &body, const std::string &name, long value) {
|
|
178
|
+
const std::string text = std::to_string(value);
|
|
179
|
+
std::string out;
|
|
180
|
+
out.reserve(body.size());
|
|
181
|
+
size_t i = 0;
|
|
182
|
+
while (i < body.size()) {
|
|
183
|
+
const char c = body[i];
|
|
184
|
+
if (std::isalpha((unsigned char)c) == 0 && c != '_') {
|
|
185
|
+
out += c;
|
|
186
|
+
i++;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
size_t end = i + 1;
|
|
190
|
+
while (end < body.size() &&
|
|
191
|
+
(std::isalnum((unsigned char)body[end]) != 0 || body[end] == '_')) {
|
|
192
|
+
end++;
|
|
193
|
+
}
|
|
194
|
+
const std::string word = body.substr(i, end - i);
|
|
195
|
+
if (word == name || word == "UNROLLED_LOOP_INDEX") {
|
|
196
|
+
out += text;
|
|
197
|
+
} else {
|
|
198
|
+
out += word;
|
|
199
|
+
}
|
|
200
|
+
i = end;
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
} // namespace
|
|
206
|
+
|
|
207
|
+
std::string unroll_glsl_pragmas(const std::string &source) {
|
|
208
|
+
if (source.find(kStartPragma) == std::string::npos) return source;
|
|
209
|
+
std::string result;
|
|
210
|
+
size_t cursor = 0;
|
|
211
|
+
while (cursor < source.size()) {
|
|
212
|
+
const size_t pragma = source.find(kStartPragma, cursor);
|
|
213
|
+
if (pragma == std::string::npos) {
|
|
214
|
+
result.append(source, cursor, std::string::npos);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
result.append(source, cursor, pragma - cursor);
|
|
218
|
+
size_t at = line_end(source, pragma + std::string(kStartPragma).size());
|
|
219
|
+
const size_t after_header = skip_space(source, at);
|
|
220
|
+
if (source.compare(after_header, 3, "for") != 0) {
|
|
221
|
+
/* Not the shape three.js emits: keep the pragma and move on. */
|
|
222
|
+
result.append(source, pragma, at - pragma);
|
|
223
|
+
cursor = at;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
size_t open_paren = skip_space(source, after_header + 3);
|
|
227
|
+
if (open_paren >= source.size() || source[open_paren] != '(') {
|
|
228
|
+
result.append(source, pragma, at - pragma);
|
|
229
|
+
cursor = at;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
int depth = 0;
|
|
233
|
+
size_t close_paren = open_paren;
|
|
234
|
+
for (; close_paren < source.size(); close_paren++) {
|
|
235
|
+
if (source[close_paren] == '(') depth++;
|
|
236
|
+
if (source[close_paren] == ')') {
|
|
237
|
+
depth--;
|
|
238
|
+
if (depth == 0) break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (close_paren >= source.size()) {
|
|
242
|
+
result.append(source, pragma, at - pragma);
|
|
243
|
+
cursor = at;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
LoopHeader loop;
|
|
247
|
+
if (!parse_header(source.substr(open_paren + 1, close_paren - open_paren - 1), &loop) ||
|
|
248
|
+
loop.end < loop.start || loop.end - loop.start > 4096) {
|
|
249
|
+
result.append(source, pragma, at - pragma);
|
|
250
|
+
cursor = at;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
size_t open_brace = skip_space(source, close_paren + 1);
|
|
254
|
+
if (open_brace >= source.size() || source[open_brace] != '{') {
|
|
255
|
+
result.append(source, pragma, at - pragma);
|
|
256
|
+
cursor = at;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
size_t close_brace = 0;
|
|
260
|
+
if (matching_brace(source, open_brace, &close_brace) == std::string::npos) {
|
|
261
|
+
result.append(source, pragma, at - pragma);
|
|
262
|
+
cursor = at;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
size_t end = skip_space(source, close_brace + 1);
|
|
266
|
+
if (source.compare(end, std::string(kEndPragma).size(), kEndPragma) != 0) {
|
|
267
|
+
result.append(source, pragma, at - pragma);
|
|
268
|
+
cursor = at;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
end = line_end(source, end + std::string(kEndPragma).size());
|
|
272
|
+
const std::string body = source.substr(open_brace + 1, close_brace - open_brace - 1);
|
|
273
|
+
const long last = loop.inclusive ? loop.end : loop.end - 1;
|
|
274
|
+
for (long i = loop.start; i <= last; i++) {
|
|
275
|
+
result += substitute(body, loop.variable, i);
|
|
276
|
+
result += '\n';
|
|
277
|
+
}
|
|
278
|
+
cursor = end;
|
|
279
|
+
}
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
} // namespace xtgui
|
|
@@ -36,6 +36,10 @@ bool cross_compile_msl(const std::vector<uint32_t> &spirv, bool fragment,
|
|
|
36
36
|
* 0..num_samplers-1, so any other allocation would sample the wrong
|
|
37
37
|
* texture (or none at all). */
|
|
38
38
|
options.enable_decoration_binding = true;
|
|
39
|
+
/* Sampler arrays (three.js shadow maps) need MSL 2.0: with the default
|
|
40
|
+
* 1.2 target SPIRV-Cross rejects "MSL 2.0 or greater is required for
|
|
41
|
+
* arrays of textures". Every Metal device SDL_GPU runs on supports 2.0. */
|
|
42
|
+
options.msl_version = spirv_cross::CompilerMSL::Options::make_msl_version(2, 0);
|
|
39
43
|
compiler.set_msl_options(options);
|
|
40
44
|
*source = compiler.compile();
|
|
41
45
|
*entry = compiler.get_cleansed_entry_point_name(
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — WebGL object records.
|
|
3
|
+
*
|
|
4
|
+
* Handles are integer ids, not pointers: a `deleteBuffer`/`deleteTexture` call
|
|
5
|
+
* must make an old JavaScript handle a no-op rather than a dangling pointer.
|
|
6
|
+
* Every record keeps its CPU-side data (buffer bytes, texture parameters) and
|
|
7
|
+
* the GPU handle it was last uploaded into.
|
|
8
|
+
*
|
|
9
|
+
* Split out of `webgl.h` to keep the context header under the repository's
|
|
10
|
+
* file-length budget; the context owns the tables of these records.
|
|
11
|
+
*/
|
|
12
|
+
#ifndef XT_GUI_WEBGL_OBJECTS_H
|
|
13
|
+
#define XT_GUI_WEBGL_OBJECTS_H
|
|
14
|
+
|
|
15
|
+
#include <cstdint>
|
|
16
|
+
#include <vector>
|
|
17
|
+
|
|
18
|
+
#include <SDL3/SDL.h>
|
|
19
|
+
|
|
20
|
+
#include "webgl2_constants.h"
|
|
21
|
+
#include "webgl_constants.h"
|
|
22
|
+
|
|
23
|
+
namespace xtgui {
|
|
24
|
+
|
|
25
|
+
constexpr int kWebGLMaxAttribs = 16;
|
|
26
|
+
constexpr int kWebGLMaxTextureUnits = 16;
|
|
27
|
+
constexpr int kWebGLMaxDrawBuffers = 4;
|
|
28
|
+
|
|
29
|
+
struct WebGLBuffer {
|
|
30
|
+
uint32_t id = 0;
|
|
31
|
+
std::vector<uint8_t> data;
|
|
32
|
+
SDL_GPUBuffer *gpu = nullptr;
|
|
33
|
+
uint32_t capacity = 0;
|
|
34
|
+
bool dirty = true;
|
|
35
|
+
/** Half-open byte range of `data` that changed since the last GPU upload. */
|
|
36
|
+
uint32_t dirty_lo = 0;
|
|
37
|
+
uint32_t dirty_hi = 0;
|
|
38
|
+
/** `bufferData` usage hint, reported by `getBufferParameter`. */
|
|
39
|
+
int usage = GL_STATIC_DRAW;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
struct WebGLTexture {
|
|
43
|
+
uint32_t id = 0;
|
|
44
|
+
int width = 0;
|
|
45
|
+
int height = 0;
|
|
46
|
+
int levels = 1;
|
|
47
|
+
/** The binding target this texture was created for. */
|
|
48
|
+
int target = GL_TEXTURE_2D;
|
|
49
|
+
/** Layers for a cube/2D-array texture, depth for a 3D texture. */
|
|
50
|
+
int layers = 1;
|
|
51
|
+
SDL_GPUTexture *gpu = nullptr;
|
|
52
|
+
SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
53
|
+
/** Depth(-stencil) formats can only be depth attachments. */
|
|
54
|
+
bool depth = false;
|
|
55
|
+
/** Block-compressed textures are sample-only, with one mip level. */
|
|
56
|
+
bool compressed = false;
|
|
57
|
+
/** Usage flags the current `gpu` texture was created with. */
|
|
58
|
+
uint32_t usage = 0;
|
|
59
|
+
bool as_target = false; // bound as a colour attachment
|
|
60
|
+
bool as_depth_target = false; // bound as a depth attachment
|
|
61
|
+
bool has_mipmaps = false;
|
|
62
|
+
int min_filter = 0;
|
|
63
|
+
int mag_filter = 0;
|
|
64
|
+
int wrap_s = 0;
|
|
65
|
+
int wrap_t = 0;
|
|
66
|
+
SDL_GPUSampler *sampler = nullptr;
|
|
67
|
+
/** Comparison sampler for a `sampler2DShadow` binding. */
|
|
68
|
+
SDL_GPUSampler *compare_sampler = nullptr;
|
|
69
|
+
bool compare_mode = false;
|
|
70
|
+
int compare_func = GL_LEQUAL;
|
|
71
|
+
bool dirty = false;
|
|
72
|
+
bool defined = false;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
struct WebGLRenderbuffer {
|
|
76
|
+
uint32_t id = 0;
|
|
77
|
+
int width = 0;
|
|
78
|
+
int height = 0;
|
|
79
|
+
bool stencil = false;
|
|
80
|
+
SDL_GPUTexture *depth = nullptr;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
struct WebGLFramebuffer {
|
|
84
|
+
uint32_t id = 0;
|
|
85
|
+
uint32_t color_textures[kWebGLMaxDrawBuffers] = {};
|
|
86
|
+
/** Layer rendered to for an array/cube colour attachment. */
|
|
87
|
+
int color_layers[kWebGLMaxDrawBuffers] = {};
|
|
88
|
+
/** `COLOR_ATTACHMENTi` or `NONE` per draw-buffer slot. */
|
|
89
|
+
int draw_buffers[kWebGLMaxDrawBuffers] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE, GL_NONE};
|
|
90
|
+
uint32_t depth_texture = 0;
|
|
91
|
+
uint32_t depth_renderbuffer = 0;
|
|
92
|
+
int width = 0;
|
|
93
|
+
int height = 0;
|
|
94
|
+
SDL_GPUTexture *depth = nullptr; // owned when no renderbuffer/texture supplies one
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** Per-attribute array state. */
|
|
98
|
+
struct WebGLAttribState {
|
|
99
|
+
bool enabled = false;
|
|
100
|
+
bool integer = false; // `vertexAttribIPointer` rather than `vertexAttribPointer`
|
|
101
|
+
uint32_t buffer = 0;
|
|
102
|
+
int size = 4;
|
|
103
|
+
int type = GL_FLOAT;
|
|
104
|
+
bool normalized = false;
|
|
105
|
+
int stride = 0;
|
|
106
|
+
int offset = 0;
|
|
107
|
+
int divisor = 0; // 0 = per vertex, 1 = per instance
|
|
108
|
+
float current[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
109
|
+
/** GPU buffer holding `current[]` while this attribute is disabled. It is
|
|
110
|
+
* created lazily and re-uploaded only when `attrib_value` changes, so a draw
|
|
111
|
+
* never creates a GPU buffer just to supply a constant attribute. */
|
|
112
|
+
SDL_GPUBuffer *constant_gpu = nullptr;
|
|
113
|
+
uint32_t constant_bytes = 0;
|
|
114
|
+
bool constant_dirty = true;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** A WebGL 2 vertex-array object; binding one restores this state. */
|
|
118
|
+
struct WebGLVertexArray {
|
|
119
|
+
uint32_t id = 0;
|
|
120
|
+
WebGLAttribState attribs[kWebGLMaxAttribs];
|
|
121
|
+
uint32_t element_buffer = 0;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** A WebGL 2 sampler object, bound to a texture unit with `bindSampler`. */
|
|
125
|
+
struct WebGLSampler {
|
|
126
|
+
uint32_t id = 0;
|
|
127
|
+
int min_filter = 0;
|
|
128
|
+
int mag_filter = 0;
|
|
129
|
+
int wrap_s = 0;
|
|
130
|
+
int wrap_t = 0;
|
|
131
|
+
/** `TEXTURE_COMPARE_MODE`/`TEXTURE_COMPARE_FUNC` (WebGL 2 sampler objects
|
|
132
|
+
* carry them too). */
|
|
133
|
+
bool compare_mode = false;
|
|
134
|
+
int compare_func = GL_LEQUAL;
|
|
135
|
+
SDL_GPUSampler *gpu = nullptr;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
struct WebGLQuery {
|
|
139
|
+
uint32_t id = 0;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
struct WebGLSync {
|
|
143
|
+
uint32_t id = 0;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
} // namespace xtgui
|
|
147
|
+
|
|
148
|
+
#endif /* XT_GUI_WEBGL_OBJECTS_H */
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
#include <vector>
|
|
17
17
|
|
|
18
18
|
#include "gui_internal.h"
|
|
19
|
+
#include "perf.h"
|
|
19
20
|
|
|
20
21
|
namespace xtgui {
|
|
21
22
|
namespace {
|
|
@@ -251,10 +252,8 @@ SDL_GPUBuffer *WebGLContext::upload_temp_buffer(const void *data, uint32_t size,
|
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
SDL_GPUGraphicsPipeline *WebGLContext::create_pipeline(
|
|
254
|
-
WebGLProgram *program, int mode, int
|
|
255
|
-
const SDL_GPUVertexAttribute *attrs, const SDL_GPUVertexBufferDescription *buffers
|
|
256
|
-
const std::string &key) {
|
|
257
|
-
(void)index_size;
|
|
255
|
+
WebGLProgram *program, int mode, int attribute_count, int buffer_count,
|
|
256
|
+
const SDL_GPUVertexAttribute *attrs, const SDL_GPUVertexBufferDescription *buffers) {
|
|
258
257
|
SDL_GPUColorTargetDescription color_targets[kWebGLMaxDrawBuffers];
|
|
259
258
|
const int color_target_count =
|
|
260
259
|
state_.color_target_count > 0 ? state_.color_target_count : 1;
|
|
@@ -318,9 +317,10 @@ SDL_GPUGraphicsPipeline *WebGLContext::create_pipeline(
|
|
|
318
317
|
set_error(GL_INVALID_OPERATION);
|
|
319
318
|
return nullptr;
|
|
320
319
|
}
|
|
321
|
-
program
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
320
|
+
/* Ownership stays with the caller: a program draw pushes the pipeline into
|
|
321
|
+
* its own list, an internal helper draw keeps it in the context. Releasing
|
|
322
|
+
* both would free it twice. */
|
|
323
|
+
g_perf_frame.webglPipelineMisses += 1;
|
|
324
|
+
return pipeline;}
|
|
325
325
|
|
|
326
326
|
} // namespace xtgui
|
|
@@ -10,6 +10,41 @@
|
|
|
10
10
|
namespace xtgui {
|
|
11
11
|
namespace webgl_detail {
|
|
12
12
|
|
|
13
|
+
/** The GL enum reported for a GLSL uniform type (`getActiveUniform`). */
|
|
14
|
+
inline int gl_type_of(const std::string &type) {
|
|
15
|
+
if (type == "float") return GL_FLOAT;
|
|
16
|
+
if (type == "vec2") return GL_FLOAT_VEC2;
|
|
17
|
+
if (type == "vec3") return GL_FLOAT_VEC3;
|
|
18
|
+
if (type == "vec4") return GL_FLOAT_VEC4;
|
|
19
|
+
if (type == "int" || type == "uint") return GL_INT;
|
|
20
|
+
if (type == "ivec2" || type == "uvec2") return GL_INT_VEC2;
|
|
21
|
+
if (type == "ivec3" || type == "uvec3") return GL_INT_VEC3;
|
|
22
|
+
if (type == "ivec4" || type == "uvec4") return GL_INT_VEC4;
|
|
23
|
+
if (type == "bool") return GL_BOOL;
|
|
24
|
+
if (type == "bvec2") return GL_BOOL_VEC2;
|
|
25
|
+
if (type == "bvec3") return GL_BOOL_VEC3;
|
|
26
|
+
if (type == "bvec4") return GL_BOOL_VEC4;
|
|
27
|
+
if (type == "mat2") return GL_FLOAT_MAT2;
|
|
28
|
+
if (type == "mat3") return GL_FLOAT_MAT3;
|
|
29
|
+
if (type == "mat4") return GL_FLOAT_MAT4;
|
|
30
|
+
if (type == "sampler2D") return GL_SAMPLER_2D;
|
|
31
|
+
if (type == "samplerCube") return GL_SAMPLER_CUBE;
|
|
32
|
+
if (type == "sampler3D") return GL_SAMPLER_3D;
|
|
33
|
+
if (type == "sampler2DArray") return GL_SAMPLER_2D_ARRAY;
|
|
34
|
+
if (type == "sampler2DShadow") return GL_SAMPLER_2D_SHADOW;
|
|
35
|
+
if (type == "samplerCubeShadow") return GL_SAMPLER_CUBE_SHADOW;
|
|
36
|
+
if (type == "sampler2DArrayShadow") return GL_SAMPLER_2D_ARRAY_SHADOW;
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** How many consecutive attribute/varying locations a type consumes. */
|
|
41
|
+
inline int location_slots(const std::string &type) {
|
|
42
|
+
if (type == "mat2") return 2;
|
|
43
|
+
if (type == "mat3") return 3;
|
|
44
|
+
if (type == "mat4") return 4;
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
13
48
|
/** Bytes a std140 block member occupies (the whole array for an array). */
|
|
14
49
|
int member_bytes(const GlslBlockMember &member);
|
|
15
50
|
|
|
@@ -22,36 +22,8 @@
|
|
|
22
22
|
namespace xtgui {
|
|
23
23
|
namespace {
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (type == "vec2") return GL_FLOAT_VEC2;
|
|
28
|
-
if (type == "vec3") return GL_FLOAT_VEC3;
|
|
29
|
-
if (type == "vec4") return GL_FLOAT_VEC4;
|
|
30
|
-
if (type == "int" || type == "uint") return GL_INT;
|
|
31
|
-
if (type == "ivec2" || type == "uvec2") return GL_INT_VEC2;
|
|
32
|
-
if (type == "ivec3" || type == "uvec3") return GL_INT_VEC3;
|
|
33
|
-
if (type == "ivec4" || type == "uvec4") return GL_INT_VEC4;
|
|
34
|
-
if (type == "bool") return GL_BOOL;
|
|
35
|
-
if (type == "bvec2") return GL_BOOL_VEC2;
|
|
36
|
-
if (type == "bvec3") return GL_BOOL_VEC3;
|
|
37
|
-
if (type == "bvec4") return GL_BOOL_VEC4;
|
|
38
|
-
if (type == "mat2") return GL_FLOAT_MAT2;
|
|
39
|
-
if (type == "mat3") return GL_FLOAT_MAT3;
|
|
40
|
-
if (type == "mat4") return GL_FLOAT_MAT4;
|
|
41
|
-
if (type == "sampler2D") return GL_SAMPLER_2D;
|
|
42
|
-
if (type == "samplerCube") return GL_SAMPLER_CUBE;
|
|
43
|
-
if (type == "sampler3D") return GL_SAMPLER_3D;
|
|
44
|
-
if (type == "sampler2DArray") return GL_SAMPLER_2D_ARRAY;
|
|
45
|
-
return 0;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** How many consecutive attribute/varying locations a type consumes. */
|
|
49
|
-
int location_slots(const std::string &type) {
|
|
50
|
-
if (type == "mat2") return 2;
|
|
51
|
-
if (type == "mat3") return 3;
|
|
52
|
-
if (type == "mat4") return 4;
|
|
53
|
-
return 1;
|
|
54
|
-
}
|
|
25
|
+
using webgl_detail::gl_type_of;
|
|
26
|
+
using webgl_detail::location_slots;
|
|
55
27
|
|
|
56
28
|
/** Merge uniforms declared by both stages, in declaration order. */
|
|
57
29
|
bool merge_uniforms(const GlslShaderInfo &vs, const GlslShaderInfo &fs,
|
|
@@ -174,8 +146,12 @@ bool WebGLContext::compile_shader(uint32_t id) {
|
|
|
174
146
|
WebGLShader *record = shader(id);
|
|
175
147
|
if (record == nullptr) return false;
|
|
176
148
|
const bool fragment = record->type == GL_FRAGMENT_SHADER;
|
|
149
|
+
/* Expand three.js's pragma-marked light loops first: a sampler-array index
|
|
150
|
+
* must be constant in Vulkan GLSL, and the expansion is the only place that
|
|
151
|
+
* knows the loop bounds. Scanning and rewriting then share one text. */
|
|
152
|
+
const std::string prepared = unroll_glsl_pragmas(record->source);
|
|
177
153
|
GlslShaderInfo info;
|
|
178
|
-
scan_glsl(
|
|
154
|
+
scan_glsl(prepared, fragment, &info);
|
|
179
155
|
if (!info.unsupported.empty()) {
|
|
180
156
|
record->log = info.unsupported;
|
|
181
157
|
record->compiled = false;
|
|
@@ -186,7 +162,7 @@ bool WebGLContext::compile_shader(uint32_t id) {
|
|
|
186
162
|
assign_locations(info.varyings, {}, &layout.varyings);
|
|
187
163
|
assign_locations(info.samplers, {}, &layout.samplers);
|
|
188
164
|
layout.block_members = info.uniforms;
|
|
189
|
-
const std::string source = rewrite_glsl(
|
|
165
|
+
const std::string source = rewrite_glsl(prepared, fragment, layout, info);
|
|
190
166
|
std::vector<uint32_t> spirv;
|
|
191
167
|
record->compiled = compile_glsl(source, fragment, &spirv, &record->log);
|
|
192
168
|
return record->compiled;
|
|
@@ -228,6 +204,9 @@ void WebGLContext::release_program_pipelines(WebGLProgram *program) {
|
|
|
228
204
|
if (program->fs_shader != nullptr) SDL_ReleaseGPUShader(guidev::g_device, program->fs_shader);
|
|
229
205
|
program->vs_shader = nullptr;
|
|
230
206
|
program->fs_shader = nullptr;
|
|
207
|
+
/* Prepared draws hold pipeline pointers too; a relink reuses the same program
|
|
208
|
+
* id, so they must not be served from the cache after this. */
|
|
209
|
+
clear_draw_cache();
|
|
231
210
|
}
|
|
232
211
|
|
|
233
212
|
void WebGLContext::delete_program(uint32_t id) {
|
|
@@ -289,10 +268,12 @@ bool WebGLContext::link_program(uint32_t id) {
|
|
|
289
268
|
return false;
|
|
290
269
|
}
|
|
291
270
|
|
|
271
|
+
const std::string prepared_vertex = unroll_glsl_pragmas(vertex->source);
|
|
272
|
+
const std::string prepared_fragment = unroll_glsl_pragmas(fragment->source);
|
|
292
273
|
GlslShaderInfo vs_info;
|
|
293
274
|
GlslShaderInfo fs_info;
|
|
294
|
-
scan_glsl(
|
|
295
|
-
scan_glsl(
|
|
275
|
+
scan_glsl(prepared_vertex, false, &vs_info);
|
|
276
|
+
scan_glsl(prepared_fragment, true, &fs_info);
|
|
296
277
|
if (!vs_info.unsupported.empty() || !fs_info.unsupported.empty()) {
|
|
297
278
|
record->log = vs_info.unsupported.empty() ? fs_info.unsupported : vs_info.unsupported;
|
|
298
279
|
record->linked = false;
|
|
@@ -361,8 +342,8 @@ bool WebGLContext::link_program(uint32_t id) {
|
|
|
361
342
|
}
|
|
362
343
|
fs_layout.varyings = vs_layout.varyings;
|
|
363
344
|
|
|
364
|
-
const std::string vs_source = rewrite_glsl(
|
|
365
|
-
const std::string fs_source = rewrite_glsl(
|
|
345
|
+
const std::string vs_source = rewrite_glsl(prepared_vertex, false, vs_layout, vs_info);
|
|
346
|
+
const std::string fs_source = rewrite_glsl(prepared_fragment, true, fs_layout, fs_info);
|
|
366
347
|
std::vector<uint32_t> vs_spirv;
|
|
367
348
|
std::vector<uint32_t> fs_spirv;
|
|
368
349
|
std::string log;
|
|
@@ -419,6 +400,19 @@ bool WebGLContext::link_program(uint32_t id) {
|
|
|
419
400
|
record->vs_sampler_uniforms.clear();
|
|
420
401
|
record->fs_sampler_uniforms.clear();
|
|
421
402
|
|
|
403
|
+
/* Resolve the active attributes once: a draw then iterates this plan instead
|
|
404
|
+
* of re-scanning `attribs` and hashing every attribute name again. */
|
|
405
|
+
record->draw_attribs.clear();
|
|
406
|
+
record->draw_attribs.reserve(record->attribs.size());
|
|
407
|
+
for (const GlslUniformDecl &decl : record->attribs) {
|
|
408
|
+
auto found = record->attrib_locations.find(decl.name);
|
|
409
|
+
if (found == record->attrib_locations.end()) continue;
|
|
410
|
+
WebGLDrawAttribPlan plan;
|
|
411
|
+
plan.location = found->second;
|
|
412
|
+
plan.type = decl.type;
|
|
413
|
+
record->draw_attribs.push_back(plan);
|
|
414
|
+
}
|
|
415
|
+
|
|
422
416
|
for (const GlslBlockMember &member : members) {
|
|
423
417
|
WebGLUniform uniform;
|
|
424
418
|
uniform.name = member.name;
|
|
@@ -436,14 +430,18 @@ bool WebGLContext::link_program(uint32_t id) {
|
|
|
436
430
|
|
|
437
431
|
auto add_sampler = [](WebGLProgram *program, std::vector<int> *stage,
|
|
438
432
|
const GlslUniformDecl &decl) {
|
|
433
|
+
const bool compare = decl.type == "sampler2DShadow" || decl.type == "samplerCubeShadow" ||
|
|
434
|
+
decl.type == "sampler2DArrayShadow";
|
|
439
435
|
int index = -1;
|
|
440
436
|
auto found = program->uniform_index.find(decl.name);
|
|
441
437
|
if (found != program->uniform_index.end()) {
|
|
442
438
|
index = found->second;
|
|
439
|
+
program->uniforms[(size_t)index].compare = compare;
|
|
443
440
|
} else {
|
|
444
441
|
WebGLUniform uniform;
|
|
445
442
|
uniform.name = decl.name;
|
|
446
443
|
uniform.sampler = true;
|
|
444
|
+
uniform.compare = compare;
|
|
447
445
|
uniform.unit = 0;
|
|
448
446
|
uniform.layout.gl_type = gl_type_of(decl.type);
|
|
449
447
|
index = (int)program->uniforms.size();
|