Replaces the bindless `binding_array<texture_2d<f32>>` the renderer bound every texture through. That array needs `VK_EXT_descriptor_indexing`, which a real share of Android GPUs lack, so the old shape did not run there at all. The two things being bound want opposite treatment, so they are now split: - **Glyph atlas pages become layers of one `texture_2d_array`.** A glyph primitive carries a `layer` instead of a view/sampler index pair. A layer index is an ordinary sampling operand, so this needs nothing beyond plain Vulkan 1.0 / GLES. Growing the atlas recreates the array with headroom and `copy_texture_to_texture`s the old layers across, no readback. - **A standalone image gets its own texture and its own bind group,** and draws in its own call. It no longer needs a per-instance entry in `PrimitiveData`: the bind group has already picked the texture. `Primitives` keeps images in a list of their own as a result, with `PrimitiveChange::is_image` naming which list a renumbering belongs to -- the two have independent index spaces, so `(layer, inst_idx)` alone would collide between them. Two notes on judgement calls, since this slice was rebuilt on top of `main` rather than transplanted: - The source version renamed `GlyphEntry::is_colored` to `is_color` and added a second `IS_COLOR` flag constant beside the existing `GlyphEntry::IS_COLORED`. Both dropped: #10's naming and its `flags()` are kept, and UVs stay `Vec2` rather than going back to `[f32; 2]`. - `ImageGpu` no longer holds the `Texture` behind its view, which removes an `#[allow(dead_code)]`. A `TextureView` keeps its own reference to the texture, checked by rendering rather than assumed -- see below. ### Verification ``` cargo fmt --all --check cargo clippy --workspace --all-targets --locked -- -D warnings cargo test --workspace --locked ``` All clean; the 4 text-edit tests pass. The only clippy output is the pre-existing future-incompatibility notice about `naga`/`wgpu`/`winit`. Because this is a rendering change, it was also run for real rather than only compiled. The `tabs` example was rendered on this machine's GPU -- Venus onto an RX 7900 XT, confirmed from the loaded ICD (`libvulkan_virtio.so` on `/dev/dri/renderD128`) rather than assumed, since a failed Vulkan init here silently falls back to llvmpipe and would make the screenshots meaningless. Screenshots before and after the change are **byte-identical** (same md5) in two scenes: the default tab, which exercises text (the atlas path) and rects, and the image tab with a standalone image pushed at startup, which exercises the per-image bind group. The image-tab scene needed a temporary local edit to the example to push the image without a click; that edit is not part of this branch. The same comparison, re-run after dropping the `Texture` field, is still byte-identical -- which is the check that the view alone keeps it alive. --------- Co-authored-by: iris <2+iris@noreply.localhost> Reviewed-on: iris/iris#11 Reviewed-by: iris <2+iris@noreply.localhost> Co-authored-by: AIris <4+iris-ai@noreply.localhost>
40 lines
1.1 KiB
WebGPU Shading Language
40 lines
1.1 KiB
WebGPU Shading Language
struct Rect {
|
|
color: u32,
|
|
radius: f32,
|
|
thickness: f32,
|
|
inner_radius: f32,
|
|
}
|
|
|
|
@group(1) @binding(0)
|
|
var<storage> rects: array<Rect>;
|
|
|
|
@fragment
|
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
let rect = rects[in.idx];
|
|
var color = unpack4x8unorm(rect.color);
|
|
|
|
let edge = 0.5;
|
|
let size = in.bot_right - in.top_left;
|
|
let corner = size / 2.0;
|
|
let center = in.top_left + corner;
|
|
let pos = in.clip_position.xy;
|
|
|
|
let dist = distance_from_rect(pos, center, corner, rect.radius);
|
|
color.a *= 1.0 - smoothstep(-min(edge, rect.radius), edge, dist);
|
|
|
|
if rect.thickness > 0.0 {
|
|
let dist2 = distance_from_rect(pos, center, corner - rect.thickness, rect.inner_radius);
|
|
color.a *= smoothstep(-min(edge, rect.inner_radius), edge, dist2);
|
|
}
|
|
|
|
return masked(in, color);
|
|
}
|
|
|
|
fn distance_from_rect(pixel_pos: vec2<f32>, rect_center: vec2<f32>, rect_corner: vec2<f32>, radius: f32) -> f32 {
|
|
// vec from center to pixel
|
|
let p = pixel_pos - rect_center;
|
|
// vec from inner rect corner to pixel
|
|
let q = abs(p) - (rect_corner - radius);
|
|
return length(max(q, vec2(0.0))) - radius;
|
|
}
|