xbintsc 0.3.46 → 0.3.61
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/AGENTS.md +95 -0
- package/README.md +25 -0
- package/README.zh-CN.md +23 -0
- package/dist/src/cli/hints.d.ts +54 -0
- package/dist/src/cli/hints.js +165 -0
- package/dist/src/cli/hints.js.map +1 -0
- package/dist/src/cli/main.js +73 -9
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +26 -0
- package/dist/src/codegen/generator/tables.js +64 -12
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/diagnostics/source-text.d.ts +22 -0
- package/dist/src/diagnostics/source-text.js +76 -0
- package/dist/src/diagnostics/source-text.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +2 -1
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/compiler.js +28 -3
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +7 -0
- package/dist/src/driver/config.js.map +1 -1
- package/dist/src/driver/win-entry.d.ts +21 -0
- package/dist/src/driver/win-entry.js +52 -0
- package/dist/src/driver/win-entry.js.map +1 -0
- package/dist/src/lexer/scanner/strings.js +16 -3
- package/dist/src/lexer/scanner/strings.js.map +1 -1
- package/dist/tests/cli/hints.test.d.ts +9 -0
- package/dist/tests/cli/hints.test.js +143 -0
- package/dist/tests/cli/hints.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +6 -4
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +17 -2
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-browser.test.d.ts +12 -0
- package/dist/tests/e2e/gui-example-browser.test.js +164 -0
- package/dist/tests/e2e/gui-example-browser.test.js.map +1 -0
- package/dist/tests/e2e/gui-example.test.d.ts +14 -0
- package/dist/tests/e2e/gui-example.test.js +76 -0
- package/dist/tests/e2e/gui-example.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +39 -4
- package/dist/tests/e2e/gui-helpers.js +38 -9
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-layout.test.js +155 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-lifecycle.test.js +10 -1
- package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -1
- package/dist/tests/e2e/gui-paint.test.js +44 -4
- package/dist/tests/e2e/gui-paint.test.js.map +1 -1
- package/dist/tests/e2e/gui-script.test.js +8 -4
- package/dist/tests/e2e/gui-script.test.js.map +1 -1
- package/dist/tests/helpers.js +3 -2
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +14 -2
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/DESIGN.md +117 -0
- package/doc/ai/README.md +63 -0
- package/doc/ai/build-recipe.md +137 -0
- package/doc/ai/cli.md +142 -0
- package/doc/ai/contributing.md +196 -0
- package/doc/ai/extensions.md +148 -0
- package/doc/ai/language-support.md +152 -0
- package/doc/ai/troubleshooting.md +163 -0
- package/doc/ai/zh-CN/README.md +56 -0
- package/doc/ai/zh-CN/build-recipe.md +132 -0
- package/doc/ai/zh-CN/cli.md +127 -0
- package/doc/ai/zh-CN/contributing.md +173 -0
- package/doc/ai/zh-CN/extensions.md +139 -0
- package/doc/ai/zh-CN/language-support.md +147 -0
- package/doc/ai/zh-CN/troubleshooting.md +150 -0
- package/doc/gui-scripts.md +350 -0
- package/doc/gui.md +752 -0
- package/doc/icon.md +265 -0
- package/doc/implemented.md +373 -0
- package/doc/node-implemented.md +588 -0
- package/doc/node-unimplemented.md +167 -0
- package/doc/post/announce.md +43 -0
- package/doc/requirements.md +145 -0
- package/doc/unimplemented.md +286 -0
- package/doc/xbintsc.config.schema.json +71 -0
- package/doc/zh-CN/DESIGN.md +104 -0
- package/doc/zh-CN/gui-scripts.md +329 -0
- package/doc/zh-CN/gui.md +656 -0
- package/doc/zh-CN/icon.md +241 -0
- package/doc/zh-CN/implemented.md +365 -0
- package/doc/zh-CN/node-implemented.md +533 -0
- package/doc/zh-CN/node-unimplemented.md +141 -0
- package/doc/zh-CN/plan-require-node-modules.md +284 -0
- package/doc/zh-CN/post/announce.md +47 -0
- package/doc/zh-CN/requirements.md +134 -0
- package/doc/zh-CN/unimplemented.md +247 -0
- package/llms.txt +45 -0
- package/package.json +4 -1
- package/runtime/ext_gui/dom_api.cpp +19 -0
- package/runtime/ext_gui/dom_api_node.cpp +4 -1
- package/runtime/ext_gui/dom_api_node_read.cpp +12 -0
- package/runtime/ext_gui/gui.cpp +45 -3
- package/runtime/ext_gui/gui_engine.h +15 -0
- package/runtime/ext_gui/gui_events.cpp +10 -3
- package/runtime/ext_gui/layout.cpp +6 -0
- package/runtime/ext_gui/layout.h +4 -0
- package/runtime/ext_gui/layout_flex.cpp +13 -5
- package/runtime/ext_gui/layout_flow.cpp +134 -4
- package/runtime/ext_gui/layout_flow_internal.h +41 -2
- package/runtime/ext_gui/layout_inline.cpp +11 -3
- package/runtime/ext_gui/paint.cpp +6 -0
- package/runtime/ext_gui/renderer.cpp +13 -11
- package/runtime/ext_gui/renderer_image.cpp +12 -8
- package/runtime/ext_gui/renderer_shaders.h +131 -4
- package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
- package/runtime/ext_gui/renderer_text.cpp +12 -8
- package/runtime/ext_gui/script.cpp +12 -2
- package/runtime/ext_gui/shaders.hlsl +98 -0
- package/runtime/ext_gui/spirv/fill.frag +19 -0
- package/runtime/ext_gui/spirv/fill.vert +42 -0
- package/runtime/ext_gui/spirv/image.frag +16 -0
- package/runtime/ext_gui/spirv/quad.vert +30 -0
- package/runtime/ext_gui/spirv/text.frag +16 -0
- package/runtime/ext_gui/text.cpp +14 -0
- package/runtime/ext_gui/window.cpp +12 -0
- package/runtime/rt.h +10 -0
- package/scripts/build-gui-shaders.mjs +204 -0
- package/scripts/build-gui.ts +35 -0
- package/scripts/check-file-length.ts +5 -1
- package/src/cli/hints.ts +194 -0
- package/src/cli/main.ts +82 -9
- package/src/codegen/generator/tables.ts +60 -14
- package/src/diagnostics/source-text.ts +78 -0
- package/src/driver/bundler/graph.ts +2 -1
- package/src/driver/compiler.ts +29 -3
- package/src/driver/config.ts +7 -0
- package/src/driver/win-entry.ts +59 -0
- package/src/lexer/scanner/strings.ts +16 -3
|
@@ -187,22 +187,26 @@ void flushAtlas(SDL_GPUDevice *device) {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
SDL_GPUGraphicsPipeline *createTextPipeline(SDL_GPUDevice *device, SDL_Window *reference) {
|
|
190
|
+
const ShaderSource text_vs = selectShader(device, ShaderProgram::Text, false);
|
|
191
|
+
const ShaderSource text_fs = selectShader(device, ShaderProgram::Text, true);
|
|
192
|
+
if (!text_vs.valid() || !text_fs.valid()) return nullptr;
|
|
193
|
+
|
|
190
194
|
SDL_GPUShaderCreateInfo vs_info;
|
|
191
195
|
memset(&vs_info, 0, sizeof(vs_info));
|
|
192
|
-
vs_info.code_size =
|
|
193
|
-
vs_info.code =
|
|
194
|
-
vs_info.entrypoint =
|
|
195
|
-
vs_info.format =
|
|
196
|
+
vs_info.code_size = text_vs.size;
|
|
197
|
+
vs_info.code = text_vs.code;
|
|
198
|
+
vs_info.entrypoint = text_vs.entry;
|
|
199
|
+
vs_info.format = text_vs.format;
|
|
196
200
|
vs_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
197
201
|
vs_info.num_uniform_buffers = 1;
|
|
198
202
|
SDL_GPUShader *vs = SDL_CreateGPUShader(device, &vs_info);
|
|
199
203
|
|
|
200
204
|
SDL_GPUShaderCreateInfo fs_info;
|
|
201
205
|
memset(&fs_info, 0, sizeof(fs_info));
|
|
202
|
-
fs_info.code_size =
|
|
203
|
-
fs_info.code =
|
|
204
|
-
fs_info.entrypoint =
|
|
205
|
-
fs_info.format =
|
|
206
|
+
fs_info.code_size = text_fs.size;
|
|
207
|
+
fs_info.code = text_fs.code;
|
|
208
|
+
fs_info.entrypoint = text_fs.entry;
|
|
209
|
+
fs_info.format = text_fs.format;
|
|
206
210
|
fs_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
207
211
|
fs_info.num_samplers = 1;
|
|
208
212
|
SDL_GPUShader *fs = SDL_CreateGPUShader(device, &fs_info);
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
#include <unordered_map>
|
|
16
16
|
#include <vector>
|
|
17
17
|
|
|
18
|
-
/** Script id -> AOT-compiled function value. Lives for the whole process
|
|
19
|
-
* runtime
|
|
18
|
+
/** Script id -> AOT-compiled function value. Lives for the whole process. The
|
|
19
|
+
* runtime collector is a mark-sweep over the heap, so these values are only
|
|
20
|
+
* safe while a root provider keeps them reachable — `xt_gui_script_gc_scan`
|
|
21
|
+
* below does that (without it a collection frees everything the script
|
|
22
|
+
* captured and the first frame after it crashes). */
|
|
20
23
|
static std::unordered_map<std::string, xt_value> &script_registry() {
|
|
21
24
|
static std::unordered_map<std::string, xt_value> registry;
|
|
22
25
|
return registry;
|
|
@@ -31,9 +34,16 @@ extern "C" xt_value xt_register_script(int32_t argc, xt_value *argv) {
|
|
|
31
34
|
script_registry()[std::string(id)] = fn;
|
|
32
35
|
}
|
|
33
36
|
}
|
|
37
|
+
/* Registration is idempotent; do it on first use so a program that never
|
|
38
|
+
* opens a window still keeps its scripts alive across a collection. */
|
|
39
|
+
xt_gc_register_root_provider(xt_gui_script_gc_scan);
|
|
34
40
|
return XT_UNDEFINED;
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
void xt_gui_script_gc_scan(void) {
|
|
44
|
+
for (const auto &entry : script_registry()) xt_gc_mark_value(entry.second);
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
/** Collect `data-xt-id` markers from `<script>` elements, in document order. */
|
|
38
48
|
static void collect_script_ids(const xtgui::Node *node, std::vector<std::string> &ids) {
|
|
39
49
|
if (node == nullptr) return;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — shader sources compiled ahead of time into the embedded
|
|
3
|
+
* blobs in `renderer_blobs.h`.
|
|
4
|
+
*
|
|
5
|
+
* Three programs, two entry points each:
|
|
6
|
+
* - `vs_main` / `fs_main` rounded-rectangle fills
|
|
7
|
+
* - `vs_text` / `fs_text` glyph quads (single-channel atlas)
|
|
8
|
+
* - `vs_text` / `fs_image` image quads (RGBA texture)
|
|
9
|
+
*
|
|
10
|
+
* `vs_main` and `vs_text` are identical in effect; they are kept separate so the
|
|
11
|
+
* Metal (MSL) path keeps its distinct entry-point names, and this file is the
|
|
12
|
+
* HLSL half of the pair. Vertex attribute semantics map 1:1 onto
|
|
13
|
+
* `SDL_GPUVertexAttribute.location`, and the push constant becomes `b0`.
|
|
14
|
+
*
|
|
15
|
+
* Compile with (Windows SDK `dxc`):
|
|
16
|
+
* dxc -T vs_6_0 -E vs_main -Fo fill_vs.dxil shaders.hlsl
|
|
17
|
+
* dxc -T ps_6_0 -E fs_main -Fo fill_fs.dxil shaders.hlsl
|
|
18
|
+
* dxc -T vs_6_0 -E vs_text -Fo text_vs.dxil shaders.hlsl
|
|
19
|
+
* dxc -T ps_6_0 -E fs_text -Fo text_fs.dxil shaders.hlsl
|
|
20
|
+
* dxc -T ps_6_0 -E fs_image -Fo image_fs.dxil shaders.hlsl
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
cbuffer Viewport : register(b0) { float2 viewport; };
|
|
24
|
+
|
|
25
|
+
/* -- fills ---------------------------------------------------------------- */
|
|
26
|
+
|
|
27
|
+
struct FillIn {
|
|
28
|
+
float2 position : TEXCOORD0;
|
|
29
|
+
float2 local : TEXCOORD1;
|
|
30
|
+
float2 halfsize : TEXCOORD2;
|
|
31
|
+
float radius : TEXCOORD3;
|
|
32
|
+
float4 color : TEXCOORD4;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
struct FillOut {
|
|
36
|
+
float4 position : SV_Position;
|
|
37
|
+
float2 local : TEXCOORD1;
|
|
38
|
+
float2 halfsize : TEXCOORD2;
|
|
39
|
+
float radius : TEXCOORD3;
|
|
40
|
+
float4 color : TEXCOORD4;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
FillOut vs_main(FillIn input) {
|
|
44
|
+
FillOut output;
|
|
45
|
+
float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0,
|
|
46
|
+
1.0 - input.position.y / viewport.y * 2.0);
|
|
47
|
+
output.position = float4(ndc, 0.0, 1.0);
|
|
48
|
+
output.local = input.local;
|
|
49
|
+
output.halfsize = input.halfsize;
|
|
50
|
+
output.radius = input.radius;
|
|
51
|
+
output.color = input.color;
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
float4 fs_main(FillOut input) : SV_Target {
|
|
56
|
+
float2 d = abs(input.local) - input.halfsize + input.radius;
|
|
57
|
+
float dist = min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - input.radius;
|
|
58
|
+
float aa = max(fwidth(dist), 0.0001);
|
|
59
|
+
float alpha = clamp(0.5 - dist / aa, 0.0, 1.0);
|
|
60
|
+
return float4(input.color.rgb, input.color.a * alpha);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* -- text and images ------------------------------------------------------ */
|
|
64
|
+
|
|
65
|
+
struct TextIn {
|
|
66
|
+
float2 position : TEXCOORD0;
|
|
67
|
+
float2 uv : TEXCOORD1;
|
|
68
|
+
float4 color : TEXCOORD2;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
struct TextOut {
|
|
72
|
+
float4 position : SV_Position;
|
|
73
|
+
float2 uv : TEXCOORD1;
|
|
74
|
+
float4 color : TEXCOORD2;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Texture2D atlas : register(t0);
|
|
78
|
+
SamplerState samp : register(s0);
|
|
79
|
+
|
|
80
|
+
TextOut vs_text(TextIn input) {
|
|
81
|
+
TextOut output;
|
|
82
|
+
float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0,
|
|
83
|
+
1.0 - input.position.y / viewport.y * 2.0);
|
|
84
|
+
output.position = float4(ndc, 0.0, 1.0);
|
|
85
|
+
output.uv = input.uv;
|
|
86
|
+
output.color = input.color;
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
float4 fs_text(TextOut input) : SV_Target {
|
|
91
|
+
float coverage = atlas.Sample(samp, input.uv).r;
|
|
92
|
+
return float4(input.color.rgb, input.color.a * coverage);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
float4 fs_image(TextOut input) : SV_Target {
|
|
96
|
+
float4 c = atlas.Sample(samp, input.uv);
|
|
97
|
+
return float4(c.rgb * input.color.rgb, c.a * input.color.a);
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — rounded-rectangle fill fragment shader (Vulkan/SPIR-V).
|
|
4
|
+
* The distance field mirrors the MSL/DXIL fill shaders. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_local;
|
|
7
|
+
layout(location = 1) in vec2 v_halfsize;
|
|
8
|
+
layout(location = 2) in float v_radius;
|
|
9
|
+
layout(location = 3) in vec4 v_color;
|
|
10
|
+
|
|
11
|
+
layout(location = 0) out vec4 out_color;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
vec2 d = abs(v_local) - v_halfsize + v_radius;
|
|
15
|
+
float dist = min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - v_radius;
|
|
16
|
+
float aa = max(fwidth(dist), 0.0001);
|
|
17
|
+
float alpha = clamp(0.5 - dist / aa, 0.0, 1.0);
|
|
18
|
+
out_color = vec4(v_color.rgb, v_color.a * alpha);
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — rounded-rectangle fill, Vulkan/SPIR-V path.
|
|
4
|
+
*
|
|
5
|
+
* The descriptor layout matches what SDL_GPU's Vulkan backend builds
|
|
6
|
+
* (`SDL_gpu_vulkan.c`, `CreateDescriptorSetLayout`): uniforms at
|
|
7
|
+
* `set=1, binding=0`, and (in the text/image shaders) samplers at
|
|
8
|
+
* `set=2, binding=0` with textures at `set=2, binding=1`.
|
|
9
|
+
*
|
|
10
|
+
* Vertex attributes (matching SDL_GPUVertexAttribute.location):
|
|
11
|
+
* 0 -> position (pixels, viewport-relative)
|
|
12
|
+
* 1 -> local (pixels, relative to the rectangle centre)
|
|
13
|
+
* 2 -> halfsize (rectangle half extents, pixels)
|
|
14
|
+
* 3 -> radius (pixels)
|
|
15
|
+
* 4 -> colour
|
|
16
|
+
* The push constant is the viewport size in logical pixels.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
layout(location = 0) in vec2 in_position;
|
|
20
|
+
layout(location = 1) in vec2 in_local;
|
|
21
|
+
layout(location = 2) in vec2 in_halfsize;
|
|
22
|
+
layout(location = 3) in float in_radius;
|
|
23
|
+
layout(location = 4) in vec4 in_color;
|
|
24
|
+
|
|
25
|
+
layout(location = 0) out vec2 v_local;
|
|
26
|
+
layout(location = 1) out vec2 v_halfsize;
|
|
27
|
+
layout(location = 2) out float v_radius;
|
|
28
|
+
layout(location = 3) out vec4 v_color;
|
|
29
|
+
|
|
30
|
+
layout(set = 1, binding = 0) uniform Viewport {
|
|
31
|
+
vec2 size;
|
|
32
|
+
} viewport;
|
|
33
|
+
|
|
34
|
+
void main() {
|
|
35
|
+
vec2 ndc = vec2(in_position.x / viewport.size.x * 2.0 - 1.0,
|
|
36
|
+
1.0 - in_position.y / viewport.size.y * 2.0);
|
|
37
|
+
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
38
|
+
v_local = in_local;
|
|
39
|
+
v_halfsize = in_halfsize;
|
|
40
|
+
v_radius = in_radius;
|
|
41
|
+
v_color = in_color;
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — image quads (Vulkan/SPIR-V): sample an RGBA texture and
|
|
4
|
+
* modulate it by the vertex colour. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_uv;
|
|
7
|
+
layout(location = 1) in vec4 v_color;
|
|
8
|
+
|
|
9
|
+
layout(location = 0) out vec4 out_color;
|
|
10
|
+
|
|
11
|
+
layout(set = 2, binding = 0) uniform sampler2D image;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
vec4 c = texture(image, v_uv);
|
|
15
|
+
out_color = vec4(c.rgb * v_color.rgb, c.a * v_color.a);
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — glyph and image quads, Vulkan/SPIR-V path.
|
|
4
|
+
*
|
|
5
|
+
* The vertex stage is shared by both: the fragment stage differs only in how it
|
|
6
|
+
* uses the sampled texture (`fs_text` reads the atlas' red channel, `fs_image`
|
|
7
|
+
* modulates an RGBA texture). Attributes:
|
|
8
|
+
* 0 -> position (pixels, viewport-relative)
|
|
9
|
+
* 1 -> uv (atlas coordinates in [0,1])
|
|
10
|
+
* 2 -> colour
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
layout(location = 0) in vec2 in_position;
|
|
14
|
+
layout(location = 1) in vec2 in_uv;
|
|
15
|
+
layout(location = 2) in vec4 in_color;
|
|
16
|
+
|
|
17
|
+
layout(location = 0) out vec2 v_uv;
|
|
18
|
+
layout(location = 1) out vec4 v_color;
|
|
19
|
+
|
|
20
|
+
layout(set = 1, binding = 0) uniform Viewport {
|
|
21
|
+
vec2 size;
|
|
22
|
+
} viewport;
|
|
23
|
+
|
|
24
|
+
void main() {
|
|
25
|
+
vec2 ndc = vec2(in_position.x / viewport.size.x * 2.0 - 1.0,
|
|
26
|
+
1.0 - in_position.y / viewport.size.y * 2.0);
|
|
27
|
+
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
28
|
+
v_uv = in_uv;
|
|
29
|
+
v_color = in_color;
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — glyph quads (Vulkan/SPIR-V): sample the single-channel
|
|
4
|
+
* atlas and modulate alpha by its coverage. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_uv;
|
|
7
|
+
layout(location = 1) in vec4 v_color;
|
|
8
|
+
|
|
9
|
+
layout(location = 0) out vec4 out_color;
|
|
10
|
+
|
|
11
|
+
layout(set = 2, binding = 0) uniform sampler2D atlas;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
float coverage = texture(atlas, v_uv).r;
|
|
15
|
+
out_color = vec4(v_color.rgb, v_color.a * coverage);
|
|
16
|
+
}
|
package/runtime/ext_gui/text.cpp
CHANGED
|
@@ -46,10 +46,21 @@ namespace {
|
|
|
46
46
|
const char *const kSansCandidates[] = {
|
|
47
47
|
/* Explicit override wins (also checked per family class below). */
|
|
48
48
|
nullptr,
|
|
49
|
+
/* Fonts with CJK coverage come first: the default face is a single face, so
|
|
50
|
+
* it has to be able to render Chinese/Japanese/Korean text as well as
|
|
51
|
+
* Latin (see "Implemented text" in doc/gui.md). */
|
|
52
|
+
"C:\\Windows\\Fonts\\msyh.ttc", /* Microsoft YaHei (Win 8.1+) */
|
|
53
|
+
"C:\\Windows\\Fonts\\msyh.ttf", /* Microsoft YaHei (Win 7) */
|
|
54
|
+
"C:\\Windows\\Fonts\\Deng.ttf", /* DengXian */
|
|
55
|
+
"C:\\Windows\\Fonts\\simsun.ttc", /* SimSun */
|
|
56
|
+
"/System/Library/Fonts/PingFang.ttc",
|
|
57
|
+
"/System/Library/Fonts/STHeiti Light.ttc",
|
|
49
58
|
"/System/Library/Fonts/Helvetica.ttc",
|
|
50
59
|
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
|
51
60
|
"/System/Library/Fonts/Supplemental/Verdana.ttf",
|
|
52
61
|
"/Library/Fonts/Arial.ttf",
|
|
62
|
+
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
|
63
|
+
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
|
53
64
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
54
65
|
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
|
55
66
|
"/usr/share/fonts/TTF/DejaVuSans.ttf",
|
|
@@ -60,10 +71,13 @@ const char *const kSansCandidates[] = {
|
|
|
60
71
|
|
|
61
72
|
const char *const kMonoCandidates[] = {
|
|
62
73
|
nullptr,
|
|
74
|
+
"C:\\Windows\\Fonts\\msyh.ttc", /* CJK-capable fallback (see above) */
|
|
75
|
+
"/System/Library/Fonts/PingFang.ttc",
|
|
63
76
|
"/System/Library/Fonts/Menlo.ttc",
|
|
64
77
|
"/System/Library/Fonts/Supplemental/Courier New.ttf",
|
|
65
78
|
"/System/Library/Fonts/Courier.ttc",
|
|
66
79
|
"/Library/Fonts/Courier New.ttf",
|
|
80
|
+
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
|
67
81
|
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
|
68
82
|
"/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
|
|
69
83
|
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
|
|
@@ -283,6 +283,17 @@ static xt_value win_is_open(xt_value self, xt_value env, int32_t argc, xt_value
|
|
|
283
283
|
return (win != NULL && win->open) ? XT_TRUE : XT_FALSE;
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
/** `/driver`: which SDL_GPU backend the device is running (`"vulkan"`,
|
|
287
|
+
* `"direct3d12"`, `"metal"`, …). The engine asks SDL for the best available
|
|
288
|
+
* one, so this is how a program (or a test) confirms the path in use. */
|
|
289
|
+
static xt_value win_driver(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
|
|
290
|
+
(void)self;
|
|
291
|
+
(void)env;
|
|
292
|
+
(void)argc;
|
|
293
|
+
(void)argv;
|
|
294
|
+
return xt_string_from_cstr(xt_gui_driver());
|
|
295
|
+
}
|
|
296
|
+
|
|
286
297
|
/** `requestAnimationFrame(fn)` -> id. The callback runs once on the next frame
|
|
287
298
|
* with the frame timestamp (ms) as its only argument. */
|
|
288
299
|
static xt_value win_request_animation_frame(xt_value self, xt_value env, int32_t argc,
|
|
@@ -455,6 +466,7 @@ xt_value xt_gui_window_proto(void) {
|
|
|
455
466
|
define_method(proto, "fontMetrics", (void *)win_font_metrics);
|
|
456
467
|
define_method(proto, "close", (void *)win_close);
|
|
457
468
|
define_method(proto, "isOpen", (void *)win_is_open);
|
|
469
|
+
define_method(proto, "driver", (void *)win_driver);
|
|
458
470
|
define_method(proto, "requestAnimationFrame", (void *)win_request_animation_frame);
|
|
459
471
|
define_method(proto, "cancelAnimationFrame", (void *)win_cancel_animation_frame);
|
|
460
472
|
define_method(proto, "on", (void *)win_on);
|
package/runtime/rt.h
CHANGED
|
@@ -371,6 +371,16 @@ size_t xt_heap_allocations(void);
|
|
|
371
371
|
*/
|
|
372
372
|
void xt_gc_add_root(xt_value *slot);
|
|
373
373
|
void xt_gc_add_root_range(xt_value *base, size_t count);
|
|
374
|
+
/**
|
|
375
|
+
* Register a provider that marks a subsystem's live set when the values are
|
|
376
|
+
* not reachable through value slots (the event loop's timers, a promise
|
|
377
|
+
* microtask queue, an extension that caches values in its own containers).
|
|
378
|
+
* Providers run during every collection and registration is idempotent.
|
|
379
|
+
*/
|
|
380
|
+
void xt_gc_register_root_provider(void (*provider)(void));
|
|
381
|
+
/** Mark `value` (and everything reachable from it) as live. Root providers use
|
|
382
|
+
* this; the collector also marks explicitly registered slots and the C stack. */
|
|
383
|
+
void xt_gc_mark_value(xt_value value);
|
|
374
384
|
|
|
375
385
|
#ifdef __cplusplus
|
|
376
386
|
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Compile the GUI engine's shaders ahead of time and embed them in
|
|
4
|
+
* `runtime/ext_gui/renderer_shaders_data.h`.
|
|
5
|
+
*
|
|
6
|
+
* SDL_GPU cannot compile GLSL or HLSL at runtime: each backend consumes its own
|
|
7
|
+
* binary format. The engine therefore ships:
|
|
8
|
+
*
|
|
9
|
+
* - SPIR-V, for Vulkan, compiled from `runtime/ext_gui/spirv/*.{vert,frag}`
|
|
10
|
+
* with `glslc` (from the Vulkan SDK); and
|
|
11
|
+
* - DXIL, for Direct3D 12, compiled from `runtime/ext_gui/shaders.hlsl` with
|
|
12
|
+
* `dxc` (inside the Windows SDK).
|
|
13
|
+
*
|
|
14
|
+
* Metal needs no blob: SDL compiles the MSL literals in `renderer_shaders.h` at
|
|
15
|
+
* runtime.
|
|
16
|
+
*
|
|
17
|
+
* The generated header is committed so a machine without either compiler (Linux,
|
|
18
|
+
* macOS CI) still builds the engine; only the corresponding backend is
|
|
19
|
+
* unavailable there.
|
|
20
|
+
*
|
|
21
|
+
* Environment:
|
|
22
|
+
* xbintsc_GLSLC explicit `glslc` path (default: newest `C:\VulkanSDK\*`)
|
|
23
|
+
* xbintsc_DXC explicit `dxc` path (default: newest Windows SDK)
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* node scripts/build-gui-shaders.mjs # regenerate the header
|
|
27
|
+
* node scripts/build-gui-shaders.mjs --check # fail if it is out of date
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
31
|
+
import { spawnSync } from "node:child_process";
|
|
32
|
+
import { dirname, join, resolve } from "node:path";
|
|
33
|
+
import { fileURLToPath } from "node:url";
|
|
34
|
+
|
|
35
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
36
|
+
const engineDir = join(root, "runtime", "ext_gui");
|
|
37
|
+
const glslDir = join(engineDir, "spirv");
|
|
38
|
+
const hlsl = join(engineDir, "shaders.hlsl");
|
|
39
|
+
const header = join(engineDir, "renderer_shaders_data.h");
|
|
40
|
+
const scratch = join(root, "build", "gui-shaders");
|
|
41
|
+
|
|
42
|
+
/** SPIR-V programs: GLSL source file and the symbol to emit. */
|
|
43
|
+
const SPIRV_PROGRAMS = [
|
|
44
|
+
{ source: "fill.vert", symbol: "kFillSpirvVs" },
|
|
45
|
+
{ source: "fill.frag", symbol: "kFillSpirvFs" },
|
|
46
|
+
{ source: "quad.vert", symbol: "kTextSpirvVs" },
|
|
47
|
+
{ source: "text.frag", symbol: "kTextSpirvFs" },
|
|
48
|
+
{ source: "image.frag", symbol: "kImageSpirvFs" },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/** DXIL programs: HLSL entry point, stage and the symbol to emit. */
|
|
52
|
+
const DXIL_PROGRAMS = [
|
|
53
|
+
{ target: "vs_6_0", entry: "vs_main", symbol: "kFillDxilVs" },
|
|
54
|
+
{ target: "ps_6_0", entry: "fs_main", symbol: "kFillDxilFs" },
|
|
55
|
+
{ target: "vs_6_0", entry: "vs_text", symbol: "kTextDxilVs" },
|
|
56
|
+
{ target: "ps_6_0", entry: "fs_text", symbol: "kTextDxilFs" },
|
|
57
|
+
{ target: "ps_6_0", entry: "fs_image", symbol: "kImageDxilFs" },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
function fail(message) {
|
|
61
|
+
console.error(`xbintsc: ${message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Newest version-suffixed directory under `base`, or undefined. */
|
|
66
|
+
function newestVersionDir(base, isVersion) {
|
|
67
|
+
if (!existsSync(base)) return undefined;
|
|
68
|
+
const versions = readdirSync(base)
|
|
69
|
+
.filter(isVersion)
|
|
70
|
+
.sort((a, b) => {
|
|
71
|
+
const pa = a.split(".").map(Number);
|
|
72
|
+
const pb = b.split(".").map(Number);
|
|
73
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
|
|
74
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
75
|
+
if (diff !== 0) return diff;
|
|
76
|
+
}
|
|
77
|
+
return 0;
|
|
78
|
+
})
|
|
79
|
+
.reverse();
|
|
80
|
+
return { base, versions };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Locate `glslc`: the explicit override, then the newest installed Vulkan SDK. */
|
|
84
|
+
function findGlslc() {
|
|
85
|
+
if (process.env.xbintsc_GLSLC) return process.env.xbintsc_GLSLC;
|
|
86
|
+
const found = newestVersionDir("C:\\VulkanSDK", (name) => /^\d+\.\d+\.\d+\.\d+$/.test(name));
|
|
87
|
+
if (!found) return undefined;
|
|
88
|
+
for (const version of found.versions) {
|
|
89
|
+
const bin = join(found.base, version, "Bin");
|
|
90
|
+
for (const name of ["glslc.exe", "glslc"]) {
|
|
91
|
+
if (existsSync(join(bin, name))) return join(bin, name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Locate `dxc`: the explicit override, then the newest Windows SDK. */
|
|
98
|
+
function findDxc() {
|
|
99
|
+
if (process.env.xbintsc_DXC) return process.env.xbintsc_DXC;
|
|
100
|
+
const found = newestVersionDir("C:\\Program Files (x86)\\Windows Kits\\10\\bin", (name) =>
|
|
101
|
+
/^\d+\.\d+\.\d+\.\d+$/.test(name),
|
|
102
|
+
);
|
|
103
|
+
if (!found) return undefined;
|
|
104
|
+
for (const version of found.versions) {
|
|
105
|
+
for (const arch of ["x64", "arm64"]) {
|
|
106
|
+
const candidate = join(found.base, version, arch, "dxc.exe");
|
|
107
|
+
if (existsSync(candidate)) return candidate;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Compile `args` with `tool`, failing with its output on a non-zero exit. */
|
|
114
|
+
function compile(tool, args, label) {
|
|
115
|
+
const result = spawnSync(tool, args, { encoding: "utf8" });
|
|
116
|
+
if (result.status !== 0) {
|
|
117
|
+
fail(`${label} failed:\n${result.stderr ?? ""}${result.stdout ?? ""}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Render one blob as a C++ array initializer, 16 bytes per line. */
|
|
122
|
+
function renderArray(symbol, bytes) {
|
|
123
|
+
const lines = [];
|
|
124
|
+
for (let i = 0; i < bytes.length; i += 16) {
|
|
125
|
+
lines.push(" " + Array.from(bytes.subarray(i, i + 16)).join(", ") + ",");
|
|
126
|
+
}
|
|
127
|
+
return [
|
|
128
|
+
`inline constexpr unsigned char ${symbol}[] = {`,
|
|
129
|
+
...lines,
|
|
130
|
+
"};",
|
|
131
|
+
`inline constexpr size_t ${symbol}Size = sizeof(${symbol});`,
|
|
132
|
+
].join("\n");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
mkdirSync(scratch, { recursive: true });
|
|
136
|
+
|
|
137
|
+
const blobs = [];
|
|
138
|
+
|
|
139
|
+
const glslc = findGlslc();
|
|
140
|
+
if (glslc === undefined) {
|
|
141
|
+
fail(
|
|
142
|
+
"no glslc found; install the Vulkan SDK (https://vulkan.lunarg.com) or set " +
|
|
143
|
+
`xbintsc_GLSLC.\nThe committed ${header} was not regenerated.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
for (const program of SPIRV_PROGRAMS) {
|
|
147
|
+
const output = join(scratch, `${program.symbol}.spv`);
|
|
148
|
+
compile(
|
|
149
|
+
glslc,
|
|
150
|
+
["-c", join(glslDir, program.source), "-o", output],
|
|
151
|
+
`glslc ${program.source}`,
|
|
152
|
+
);
|
|
153
|
+
blobs.push({ symbol: program.symbol, bytes: readFileSync(output) });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const dxc = findDxc();
|
|
157
|
+
if (dxc === undefined) {
|
|
158
|
+
fail(
|
|
159
|
+
"no dxc found; install the Windows SDK or set xbintsc_DXC.\n" +
|
|
160
|
+
`The committed ${header} was not regenerated.`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
for (const program of DXIL_PROGRAMS) {
|
|
164
|
+
const output = join(scratch, `${program.symbol}.dxil`);
|
|
165
|
+
compile(
|
|
166
|
+
dxc,
|
|
167
|
+
["-T", program.target, "-E", program.entry, "-Fo", output, hlsl],
|
|
168
|
+
`dxc ${program.target} ${program.entry}`,
|
|
169
|
+
);
|
|
170
|
+
blobs.push({ symbol: program.symbol, bytes: readFileSync(output) });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
174
|
+
|
|
175
|
+
const generated = [
|
|
176
|
+
"/*",
|
|
177
|
+
" * Generated by `scripts/build-gui-shaders.mjs` — do not edit.",
|
|
178
|
+
" *",
|
|
179
|
+
" * SPIR-V blobs (Vulkan) compiled from `runtime/ext_gui/spirv/` with glslc, and",
|
|
180
|
+
" * DXIL blobs (Direct3D 12) compiled from `runtime/ext_gui/shaders.hlsl` with dxc.",
|
|
181
|
+
" * The Metal path uses the MSL literals in `renderer_shaders.h` directly.",
|
|
182
|
+
" */",
|
|
183
|
+
"#ifndef XT_GUI_RENDERER_SHADERS_DATA_H",
|
|
184
|
+
"#define XT_GUI_RENDERER_SHADERS_DATA_H",
|
|
185
|
+
"",
|
|
186
|
+
"#include <stddef.h>",
|
|
187
|
+
"",
|
|
188
|
+
...blobs.map((blob) => renderArray(blob.symbol, blob.bytes)),
|
|
189
|
+
"",
|
|
190
|
+
"#endif /* XT_GUI_RENDERER_SHADERS_DATA_H */",
|
|
191
|
+
"",
|
|
192
|
+
].join("\n");
|
|
193
|
+
|
|
194
|
+
if (process.argv.includes("--check")) {
|
|
195
|
+
const current = existsSync(header) ? readFileSync(header, "utf8") : "";
|
|
196
|
+
if (current !== generated) {
|
|
197
|
+
fail(`${header} is out of date; run \`node scripts/build-gui-shaders.mjs\``);
|
|
198
|
+
}
|
|
199
|
+
console.log(`xbintsc: ${header} is up to date`);
|
|
200
|
+
} else {
|
|
201
|
+
writeFileSync(header, generated);
|
|
202
|
+
const total = blobs.reduce((sum, blob) => sum + blob.bytes.length, 0);
|
|
203
|
+
console.log(`xbintsc: wrote ${header} (${blobs.length} blobs, ${total} bytes)`);
|
|
204
|
+
}
|
package/scripts/build-gui.ts
CHANGED
|
@@ -53,6 +53,32 @@ const isWindows = process.platform === "win32";
|
|
|
53
53
|
const objectSuffix = isWindows ? ".obj" : ".o";
|
|
54
54
|
const output = join(outDir, isWindows ? "gui.lib" : "gui.a");
|
|
55
55
|
|
|
56
|
+
/* CMake generator and ABI settings used for every vendored library. Ninja is
|
|
57
|
+
* selected on Windows because the Visual Studio generator imports the
|
|
58
|
+
* machine-wide MSBuild/vcpkg integration, whose `LIBPATH` shadows the Windows
|
|
59
|
+
* SDK and makes the compiler probe fail with
|
|
60
|
+
* `LNK1104: cannot open file 'ucrtd.lib'`.
|
|
61
|
+
*
|
|
62
|
+
* `CMAKE_MSVC_RUNTIME_LIBRARY` is pinned to the *static* CRT (`MultiThreaded`),
|
|
63
|
+
* which is clang's Windows default (`-defaultlib:libcmt`) and what xbintsc uses
|
|
64
|
+
* for its own runtime objects. Building the vendored libraries against the DLL
|
|
65
|
+
* CRT instead fails the final link with
|
|
66
|
+
* `LNK2038: mismatch detected for 'RuntimeLibrary'`. The variable only takes
|
|
67
|
+
* effect when policy CMP0091 is NEW, but FreeType and HarfBuzz declare
|
|
68
|
+
* `cmake_minimum_required` below 3.15, which defaults the policy to OLD and
|
|
69
|
+
* silently ignores it — hence `CMAKE_POLICY_DEFAULT_CMP0091`. The C/C++ compilers
|
|
70
|
+
* are pinned to the same clang the engine is compiled with, so every object in
|
|
71
|
+
* `gui.a` shares one ABI and optimization behaviour. */
|
|
72
|
+
const cmakeGeneratorArgs: readonly string[] = isWindows
|
|
73
|
+
? [
|
|
74
|
+
"-G", "Ninja",
|
|
75
|
+
"-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded",
|
|
76
|
+
"-DCMAKE_POLICY_DEFAULT_CMP0091=NEW",
|
|
77
|
+
"-DCMAKE_C_COMPILER=clang",
|
|
78
|
+
"-DCMAKE_CXX_COMPILER=clang++",
|
|
79
|
+
]
|
|
80
|
+
: [];
|
|
81
|
+
|
|
56
82
|
const SDL_TAG = process.env.SDL3_TAG || "release-3.2.10";
|
|
57
83
|
const FREETYPE_VERSION = process.env.FREETYPE_VERSION || "2.13.3";
|
|
58
84
|
const HARFBUZZ_VERSION = process.env.HARFBUZZ_VERSION || "10.1.0";
|
|
@@ -178,6 +204,7 @@ if (!existingSdlArchive) {
|
|
|
178
204
|
const configureArgs = [
|
|
179
205
|
"-S", sdlSrc,
|
|
180
206
|
"-B", sdlBuild,
|
|
207
|
+
...cmakeGeneratorArgs,
|
|
181
208
|
"-DCMAKE_BUILD_TYPE=Release",
|
|
182
209
|
"-DSDL_SHARED=OFF",
|
|
183
210
|
"-DSDL_STATIC=ON",
|
|
@@ -220,6 +247,7 @@ if (!existsSync(freetypeArchive() ?? "")) {
|
|
|
220
247
|
run(cmake, [
|
|
221
248
|
"-S", freetypeSrc,
|
|
222
249
|
"-B", freetypeBuild,
|
|
250
|
+
...cmakeGeneratorArgs,
|
|
223
251
|
"-DCMAKE_BUILD_TYPE=Release",
|
|
224
252
|
"-DBUILD_SHARED_LIBS=OFF",
|
|
225
253
|
/* We only need TrueType/OpenType outlines; skip the optional codecs. */
|
|
@@ -240,6 +268,7 @@ if (!existsSync(harfbuzzArchive() ?? "")) {
|
|
|
240
268
|
run(cmake, [
|
|
241
269
|
"-S", harfbuzzSrc,
|
|
242
270
|
"-B", harfbuzzBuild,
|
|
271
|
+
...cmakeGeneratorArgs,
|
|
243
272
|
"-DCMAKE_BUILD_TYPE=Release",
|
|
244
273
|
"-DBUILD_SHARED_LIBS=OFF",
|
|
245
274
|
"-DHB_BUILD_UTILS=OFF",
|
|
@@ -272,6 +301,12 @@ const sources = readdirSync(engineDir)
|
|
|
272
301
|
|
|
273
302
|
const objects: string[] = [];
|
|
274
303
|
const cxxFlags = ["-std=c++17", "-O2", "-Wall", "-Wextra", ...includeFlags];
|
|
304
|
+
/* The engine is C++ and the MSVC C++ runtime must match the static C CRT that
|
|
305
|
+
* clang links by default, so let every engine object record `libcpmt` as a
|
|
306
|
+
* default library. The extension itself only contributes the DLL import library
|
|
307
|
+
* (`-lmsvcprt`, `src/extensions/gui/index.ts`), which is inconsistent with that
|
|
308
|
+
* CRT and makes the final link fail with `LNK2038`/unresolved `__imp_*`. */
|
|
309
|
+
if (isWindows) cxxFlags.push("-Xclang", "--dependent-lib=libcpmt");
|
|
275
310
|
/* The MSVC ABI rejects `-fPIC`; position independence is the default elsewhere. */
|
|
276
311
|
if (!isWindows) cxxFlags.splice(3, 0, "-fPIC");
|
|
277
312
|
for (const source of sources) {
|
|
@@ -38,6 +38,10 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
38
38
|
/** Vendored third-party sources that ship verbatim and are not ours to split. */
|
|
39
39
|
const VENDORED_FILES = new Set([join("runtime", "ext_gui", "stb_image.h")]);
|
|
40
40
|
|
|
41
|
+
/** Machine-generated files: they are reproduced from their sources by a script,
|
|
42
|
+
* so their size is not a sign of a module that needs splitting. */
|
|
43
|
+
const GENERATED_FILES = new Set([join("runtime", "ext_gui", "renderer_shaders_data.h")]);
|
|
44
|
+
|
|
41
45
|
interface Offense {
|
|
42
46
|
file: string;
|
|
43
47
|
lines: number;
|
|
@@ -65,7 +69,7 @@ function collect(dir: string, offenses: Offense[]): void {
|
|
|
65
69
|
const dot = entry.name.lastIndexOf(".");
|
|
66
70
|
if (dot < 0 || !CODE_EXTENSIONS.has(entry.name.slice(dot))) continue;
|
|
67
71
|
const file = relative(root, full);
|
|
68
|
-
if (VENDORED_FILES.has(file)) continue;
|
|
72
|
+
if (VENDORED_FILES.has(file) || GENERATED_FILES.has(file)) continue;
|
|
69
73
|
const lines = countLines(full);
|
|
70
74
|
if (lines > MAX_LINES) offenses.push({ file, lines });
|
|
71
75
|
}
|