chore: checkpoint web Blender migration source, tools and docs
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
mes123456
2026-11-10 11:18:01 +08:00
parent 122142c26c
commit f3e21c6b98
6887 changed files with 257154 additions and 12294 deletions

View File

@@ -29,12 +29,23 @@ uint gpu_index_load(uint element_index)
return element_index;
}
#ifdef GPU_WEBGL
uint raw_index = gpu_webgl_load_gpu_index_buf(gpu_index_16bit ? element_index >> 1u :
element_index);
#else
uint raw_index = gpu_index_buf[gpu_index_16bit ? element_index >> 1u : element_index];
#endif
if (gpu_index_16bit) {
raw_index = ((element_index & 1u) == 0u) ? (raw_index & 0xFFFFu) : (raw_index >> 16u);
}
#ifdef GPU_WEBGL
/* Preserve fixed restart before adding the compressed buffer's base index. */
if (raw_index == (gpu_index_16bit ? 0xFFFFu : 0xFFFFFFFFu)) {
return 0xFFFFFFFFu;
}
#endif
return raw_index + uint(gpu_index_base_index);
}

View File

@@ -69,14 +69,14 @@ uint rgb9e5_encode(float3 color)
{
rgb9e5_t result = rgb9e5_from_float3(color);
result.exp_shared <<= RGB9E5_MANTISSA_BITS * 3;
result.mantissa <<= RGB9E5_MANTISSA_BITS * uint3(0, 1, 2);
result.mantissa <<= uint(RGB9E5_MANTISSA_BITS) * uint3(0, 1, 2);
return result.mantissa.r | result.mantissa.g | result.mantissa.b | result.exp_shared;
}
float3 rgb9e5_decode(uint data)
{
int exp_shared = int(data >> (RGB9E5_MANTISSA_BITS * 3));
uint3 mantissa = (uint3(data) >> (RGB9E5_MANTISSA_BITS * uint3(0, 1, 2))) &
uint3 mantissa = (uint3(data) >> (uint(RGB9E5_MANTISSA_BITS) * uint3(0, 1, 2))) &
uint(MAX_RGB9E5_MANTISSA);
return float3(mantissa) * rgb9e5_exponent_factor(exp_shared);
}

View File

@@ -6,15 +6,20 @@
#include "gpu_shader_compat.hh"
#ifdef GPU_WEBGL
/* WebGL stores the single-mip tile map layers as rows. Integer fetches are unchanged. */
bool tiled_image_lookup(float3 &co, sampler2DArray ima, sampler2D map)
#else
bool tiled_image_lookup(float3 &co, sampler2DArray ima, sampler1DArray map)
#endif
{
float2 tile_pos = floor(co.xy);
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) {
if (tile_pos.x < 0.0f || tile_pos.y < 0.0f || tile_pos.x >= 10.0f) {
return false;
}
float tile = 10 * tile_pos.y + tile_pos.x;
if (tile >= textureSize(map, 0).x) {
float tile = 10.0f * tile_pos.y + tile_pos.x;
if (tile >= float(textureSize(map, 0).x)) {
return false;
}
/* Fetch tile information. */

View File

@@ -105,9 +105,9 @@ void main()
case SOCK_DISPLAY_SHAPE_VOLUME_GRID: {
constexpr float rect_side_length = 0.25f;
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
const float2 rect_corner = max(float2(rect_side_length), extrusion / 2.0f + oversize) +
float2 rect_corner = max(float2(rect_side_length), extrusion / 2.0f + oversize) +
finalOutlineThickness / 4.0f;
const float2 mirrored_uv = abs(abs(uv) - rect_corner);
float2 mirrored_uv = abs(abs(uv) - rect_corner);
distance_squared = square_sdf(mirrored_uv, rect_corner + finalOutlineThickness / 2.0f);
alpha_threshold = corner_rounding;
break;
@@ -115,9 +115,9 @@ void main()
case SOCK_DISPLAY_SHAPE_LIST: {
constexpr float2 rect_side_length = float2(0.5f, 0.25f);
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
const float2 rect_corner = max(rect_side_length, extrusion / 2.0f + oversize) +
float2 rect_corner = max(rect_side_length, extrusion / 2.0f + oversize) +
finalOutlineThickness / 4.0f;
const float2 mirrored_uv = float2(
float2 mirrored_uv = float2(
abs(uv.x), abs(abs(abs(uv.y) - rect_corner.y / 1.5f) - rect_corner.y / 1.5f));
distance_squared = square_sdf(
mirrored_uv, (rect_corner + finalOutlineThickness / 2.0f) / float2(1.0f, 1.5f));

View File

@@ -284,15 +284,16 @@ struct Resources {
InnerType v0, v1, v2, h0, h1, h2, out_pixel;
/* Reduce vertically up to 3 times (depending on kernel horizontal size) */
switch (kernel_size.x) {
case 3:
/* Keep the same sample order without switch fall-through for WGSL translation. */
{
if (kernel_size.x == 3) {
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(2, 2), load_from_shared);
ATTR_FALLTHROUGH;
v1 = load_sample(src_coord + int2(2, 1), load_from_shared); v0 = load_sample(src_coord + int2(2, 0), load_from_shared); break;
case 2:
v1 = load_sample(src_coord + int2(2, 1), load_from_shared);
ATTR_FALLTHROUGH;
v0 = load_sample(src_coord + int2(2, 0), load_from_shared); break;
case 1:
v0 = load_sample(src_coord + int2(2, 0), load_from_shared);
break;
@@ -308,15 +309,14 @@ struct Resources {
h2 = v0;
break;
}
ATTR_FALLTHROUGH;
case 2:
} if (kernel_size.x == 2 || kernel_size.x == 3) {
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(1, 2), load_from_shared);
ATTR_FALLTHROUGH;
v1 = load_sample(src_coord + int2(1, 1), load_from_shared); v0 = load_sample(src_coord + int2(1, 0), load_from_shared); break;
case 2:
v1 = load_sample(src_coord + int2(1, 1), load_from_shared);
ATTR_FALLTHROUGH;
v0 = load_sample(src_coord + int2(1, 0), load_from_shared); break;
case 1:
v0 = load_sample(src_coord + int2(1, 0), load_from_shared);
break;
@@ -332,15 +332,14 @@ struct Resources {
h1 = v0;
break;
}
ATTR_FALLTHROUGH;
case 1:
} if (kernel_size.x >= 1 && kernel_size.x <= 3) {
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(0, 2), load_from_shared);
ATTR_FALLTHROUGH;
v1 = load_sample(src_coord + int2(0, 1), load_from_shared); v0 = load_sample(src_coord + int2(0, 0), load_from_shared); break;
case 2:
v1 = load_sample(src_coord + int2(0, 1), load_from_shared);
ATTR_FALLTHROUGH;
v0 = load_sample(src_coord + int2(0, 0), load_from_shared); break;
case 1:
v0 = load_sample(src_coord + int2(0, 0), load_from_shared);
break;
@@ -356,6 +355,7 @@ struct Resources {
h0 = v0;
break;
}
}
}
/* Reduce up to 3 samples horizontally. */
@@ -527,7 +527,10 @@ void update_mipmaps([[global_invocation_id]] const uint3 global_id,
[[local_invocation_id]] const uint3 local_index,
[[resource_table]] Resources<format, SharedStorage, InnerType> &srt)
{
if (srt.num_levels == 1u) {
const bool two_levels = srt.num_levels != 1u;
int2 tile_index = int2(0);
bool use_bounds_check = false;
if (!two_levels) {
int2 kernel_size = kernel_size_from_input_size(srt.level_size(INPUT_LEVEL));
int2 dst_image_size = srt.level_size(INPUT_LEVEL + 1);
int2 dst_coord = int2(int(global_id.x) % dst_image_size.x,
@@ -547,32 +550,32 @@ void update_mipmaps([[global_invocation_id]] const uint3 global_id,
int2 tile_count;
tile_count.x = int(uint(level2_size.x + 7) / 8u);
tile_count.y = int(uint(level2_size.y + 7) / 8u);
int2 tile_index = int2(group_id.x % uint(tile_count.x), group_id.x / uint(tile_count.x));
tile_index = int2(group_id.x % uint(tile_count.x), group_id.x / uint(tile_count.x));
/* Determine if bounds checking is needed; this is only the case
* for tiles at the right or bottom fringe that might be cut off
* by the image border. Note that later, I use if statements rather
* than passing use_bounds_check directly to convince the compiler
* to inline everything. */
bool use_bounds_check = tile_index.x >= tile_count.x - 1 || tile_index.y >= tile_count.y - 1;
use_bounds_check = tile_index.x >= tile_count.x - 1 || tile_index.y >= tile_count.y - 1;
if (use_bounds_check) {
/* Compute the tile in level inputLevel_ + 1 that's needed to
* compute the above 8x8 tile. */
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), true);
barrier();
/* Compute the inputLevel_ + 2 tile of size 8x8, loading
* inputs from shared memory. */
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), true);
}
else {
/* Same but without bounds checking. */
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), false);
barrier();
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), false);
}
}
/* Every lane reaches the barrier before consuming intermediate shared pixels. */
barrier();
if (two_levels) {
if (use_bounds_check) { srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), true); }
else { srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), false); }
}
}
template struct Shared<float>;

View File

@@ -296,7 +296,9 @@ struct Resources {
bool is_tria = (vert_id > 3);
float2 pos;
VertOut vert_out = (is_tria) ? widget.do_tria(vert_id, pos) : widget.do_widget(vert_id, pos);
VertOut vert_out;
if (is_tria) { vert_out = widget.do_tria(vert_id, pos); }
else { vert_out = widget.do_widget(vert_id, pos); }
/* WORKAROUND: Quirk of current BSL implementation.
* Current implementation doesn't allow to assign the output struct at once. */

View File

@@ -16,6 +16,26 @@ struct VertIn {
float4 final_color;
};
float polyline_position_load(uint address)
{
#ifdef GPU_WEBGL
return uintBitsToFloat(gpu_webgl_load_pos(address));
#else
return pos[address];
#endif
}
#ifndef UNIFORM
float polyline_color_load(uint address)
{
# ifdef GPU_WEBGL
return uintBitsToFloat(gpu_webgl_load_color(address));
# else
return color[address];
# endif
}
#endif
VertIn input_assembly(uint in_vertex_id)
{
uint v_i = gpu_index_load(in_vertex_id);
@@ -24,12 +44,12 @@ VertIn input_assembly(uint in_vertex_id)
VertIn vert_in;
vert_in.ls_P = float3(0.0f, 0.0f, 0.0f);
/* Need to support 1, 2 and 3 dimensional input (sigh). */
vert_in.ls_P.x = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 0 + ofs];
vert_in.ls_P.x = polyline_position_load(gpu_attr_load_index(v_i, gpu_attr_0) + 0u + ofs);
if (gpu_attr_0_len >= 2) {
vert_in.ls_P.y = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 1 + ofs];
vert_in.ls_P.y = polyline_position_load(gpu_attr_load_index(v_i, gpu_attr_0) + 1u + ofs);
}
if (gpu_attr_0_len >= 3) {
vert_in.ls_P.z = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 2 + ofs];
vert_in.ls_P.z = polyline_position_load(gpu_attr_load_index(v_i, gpu_attr_0) + 2u + ofs);
}
if (gpu_attr_0_fetch_int) {
@@ -38,19 +58,26 @@ VertIn input_assembly(uint in_vertex_id)
#ifndef UNIFORM
vert_in.final_color = float4(0.0f, 0.0f, 0.0f, 1.0f);
/* Need to support 1, 2, 3 and 4 dimensional input (sigh). */
vert_in.final_color.x = color[gpu_attr_load_index(v_i, gpu_attr_1) + 0 + ofs];
if (gpu_attr_1_fetch_unorm8) {
# ifdef GPU_WEBGL
/* Decode packed bytes before float conversion, including bit patterns that encode NaNs. */
uint packed_color = gpu_webgl_load_color(gpu_attr_load_index(v_i, gpu_attr_1) + ofs);
vert_in.final_color = float4(gpu_attr_decode_uchar4_to_uint4(packed_color)) / 255.0f;
# else
vert_in.final_color.x = color[gpu_attr_load_index(v_i, gpu_attr_1) + ofs];
vert_in.final_color = unpackUnorm4x8(floatBitsToUint(vert_in.final_color.x));
# endif
}
else {
vert_in.final_color.x = polyline_color_load(gpu_attr_load_index(v_i, gpu_attr_1) + ofs);
if (gpu_attr_1_len >= 2) {
vert_in.final_color.y = color[gpu_attr_load_index(v_i, gpu_attr_1) + 1 + ofs];
vert_in.final_color.y = polyline_color_load(gpu_attr_load_index(v_i, gpu_attr_1) + 1u + ofs);
}
if (gpu_attr_1_len >= 3) {
vert_in.final_color.z = color[gpu_attr_load_index(v_i, gpu_attr_1) + 2 + ofs];
vert_in.final_color.z = polyline_color_load(gpu_attr_load_index(v_i, gpu_attr_1) + 2u + ofs);
}
if (gpu_attr_1_len >= 4) {
vert_in.final_color.w = color[gpu_attr_load_index(v_i, gpu_attr_1) + 3 + ofs];
vert_in.final_color.w = polyline_color_load(gpu_attr_load_index(v_i, gpu_attr_1) + 3u + ofs);
}
}
#endif
@@ -134,8 +161,15 @@ void do_vertex(const uint i,
geom_out.final_color = color;
#elif defined(FLAT)
# ifdef GPU_WEBGL
/* Triangle provoking-vertex rotation cannot choose the input line's color:
* all six expanded vertices carry the same flat value. Keep endpoint order
* (and thus clipping/winding) intact and select the native source explicitly. */
geom_out.final_color = geom_in[gpu_webgl_polyline_first_vertex ? 0 : 1].final_color;
# else
/* WATCH: Assuming last provoking vertex. */
geom_out.final_color = geom_in[1].final_color;
# endif
#elif defined(SMOOTH)
geom_out.final_color = geom_in[i].final_color;
@@ -201,10 +235,26 @@ void main()
output_invocation_count;
/* Used to wrap around for the line loop case. */
uint input_total_vertex_count = uint(gpu_vert_stride_count_offset.y);
uint index_start = uint(gpu_polyline_index_start);
uint first_vertex = index_start + in_primitive_first_vertex;
uint second_vertex = index_start + (in_primitive_first_vertex + 1u) % input_total_vertex_count;
#ifdef GPU_WEBGL
/* Expanded draws must discard restart segments before fetching their vertex attributes. */
if (gpu_index_load(first_vertex) == 0xFFFFFFFFu ||
gpu_index_load(second_vertex) == 0xFFFFFFFFu)
{
gl_Position = float4(2.0f, 2.0f, 2.0f, 1.0f);
final_color = float4(0.0f);
smoothline = 0.0f;
clip = 0.0f;
return;
}
#endif
VertIn vert_in[2];
vert_in[0] = input_assembly(in_primitive_first_vertex + 0u);
vert_in[1] = input_assembly((in_primitive_first_vertex + 1u) % input_total_vertex_count);
vert_in[0] = input_assembly(first_vertex);
vert_in[1] = input_assembly(second_vertex);
VertOut vert_out[2];
vert_out[0] = vertex_main(vert_in[0]);

View File

@@ -10,7 +10,7 @@ void main()
{
float phase = mod((gl_FragCoord.x + gl_FragCoord.y), float(size1 + size2));
if (phase < size1) {
if (phase < float(size1)) {
fragColor = color1;
}
else {

View File

@@ -46,13 +46,13 @@ void main()
outline_dist = max(outline_dist, absPos.x - radii[2]);
}
float alpha = 1 - smoothstep(thresholds[0], thresholds[1], abs(outline_dist));
float alpha = 1.0f - smoothstep(thresholds[0], thresholds[1], abs(outline_dist));
/* Inside the outline. */
if (outline_dist < 0) {
if (outline_dist < 0.0f) {
/* Middle dot */
if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) {
alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], length(absPos)));
alpha = max(alpha, 1.0f - smoothstep(thresholds[2], thresholds[3], length(absPos)));
}
/* Up and down arrow-like shading. */

View File

@@ -26,11 +26,11 @@ void main()
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
/* Align to pixel grid if the viewport size is known. */
if (ViewportSize.x > 0) {
if (ViewportSize.x > 0.0f) {
float2 scale = ViewportSize * 0.5f;
float2 px_pos = (gl_Position.xy + 1) * scale;
float2 px_pos = (gl_Position.xy + 1.0f) * scale;
float2 adj_pos = round(px_pos - 0.5f) + 0.5f;
gl_Position.xy = adj_pos / scale - 1;
gl_Position.xy = adj_pos / scale - 1.0f;
}
/* Pass through parameters. */
@@ -45,7 +45,7 @@ void main()
}
/* Size-dependent line thickness. */
float half_width = (0.06f + (size - 10) * 0.04f);
float half_width = (0.06f + (size - 10.0f) * 0.04f);
float line_width = half_width + line_falloff;
/* Outline thresholds. */
@@ -57,7 +57,7 @@ void main()
/* Extend the primitive size by half line width on either side; odd for symmetry. */
float ext_radius = round(0.5f * size) + thresholds.x;
gl_PointSize = ceil(ext_radius + thresholds.y) * 2 + 1;
gl_PointSize = ceil(ext_radius + thresholds.y) * 2.0f + 1.0f;
/* Diamond radius. */
radii[0] = ext_radius * diagonal_scale;

View File

@@ -83,7 +83,7 @@ void main()
uint shadow_type = glyph_flags & 0xFu;
/* Colored glyphs: do not do filtering or blurring. */
if (num_channels == 4) {
if (num_channels == 4u) {
fragColor.rgba = sample_glyph_rgba(uv_base).rgba;
fragColor.a *= color_flat.a;
return;
@@ -93,7 +93,7 @@ void main()
fragColor.rgb = color_flat.rgb;
if (shadow_type == 0) {
if (shadow_type == 0u) {
/* No blurring: just a bilinear sample. */
fragColor.a = sample_glyph_bilinear(bilin_f, uv_base);
}
@@ -106,7 +106,7 @@ void main()
int2 texel = int2(floor(uv_base)) - 1;
int frag_offset = glyph_offset + texel.y * glyph_dim.x + texel.x;
if (shadow_type == 6) {
if (shadow_type == 6u) {
/* 3x3 outline by dilation */
float maxval = 0.0f;
@@ -131,7 +131,7 @@ void main()
}
fragColor.a = maxval;
}
else if (shadow_type <= 4) {
else if (shadow_type <= 4u) {
/* 3x3 blur */
/* clang-format off */

View File

@@ -17,7 +17,7 @@ void main()
/* Depending on shadow outline / blur level, we might need to expand the quad. */
uint shadow_type = glyph_flags & 0xFu;
int interp_size = shadow_type > 4 ? 2 : (shadow_type > 0 ? 1 : 0);
int interp_size = shadow_type > 4u ? 2 : (shadow_type > 0u ? 1 : 0);
/* Quad expansion using instanced rendering. */
float x = float(gl_VertexID % 2);

View File

@@ -37,6 +37,7 @@ PUSH_CONSTANT(bool, lineSmooth)
STORAGE_BUF_FREQ(GPU_SSBO_POLYLINE_POS_BUF_SLOT, read, float, pos[], GEOMETRY)
PUSH_CONSTANT(int2, gpu_attr_0)
PUSH_CONSTANT(int3, gpu_vert_stride_count_offset)
PUSH_CONSTANT(int, gpu_polyline_index_start)
PUSH_CONSTANT(int, gpu_attr_0_len)
PUSH_CONSTANT(bool, gpu_attr_0_fetch_int)
PUSH_CONSTANT(bool, gpu_attr_1_fetch_unorm8)