+#ifndef COMPUTE_BINDINGS_INCLUDE
+#define COMPUTE_BINDINGS_INCLUDE
-struct Glyph {
- ivec2 atlas_min;
- ivec2 atlas_max;
+#include "primitive_2d.h"
- vec2 offset_min;
- vec2 offset_max;
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer PrimitiveInstances
+{
+ PrimitiveInstance values[];
};
-struct GlyphInstance {
- vec2 position;
- uint index;
- uint color;
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Rects
+{
+ Rect values[];
};
layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Glyphs
Glyph values[];
};
-layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer GlyphInstances
-{
- GlyphInstance values[];
-};
-
layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer TilesRead
{
uint values[];
uint num_primitives_1024;
uint tile_stride;
+ PrimitiveInstances primitive_instances;
+ Rects rects;
Glyphs glyphs;
- GlyphInstances glyph_instances;
TilesWrite tiles;
};
layout (set = 0, binding = 3, rgba16f) uniform readonly image2D ui_layer_read;
layout (set = 0, binding = 4, rgba16f) uniform readonly image2D color_layer;
layout (set = 0, binding = 5, rgba16f) uniform writeonly image2D composited_output;
+
+#endif
#extension GL_EXT_scalar_block_layout : require
#include "compute_bindings.h"
-#include "primitive_2d.h"
float srgb_oetf(float a) {
return (.0031308f >= a) ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f;
+#ifndef PRIMITIVE_2D_INCLUDE
+#define PRIMITIVE_2D_INCLUDE
+
const uint TILE_SIZE = 32;
const uint MAX_PRIMS = 1 << 18;
const uint TILE_BITMAP_L1_OFFSET = (TILE_BITMAP_RANGE_HI_OFFSET + 1);
const uint TILE_BITMAP_L0_OFFSET = (TILE_BITMAP_L1_OFFSET + TILE_BITMAP_L1_WORDS);
-bool test_glyph(uint index, vec2 tile_min, vec2 tile_max) {
- const GlyphInstance gi = uniforms.glyph_instances.values[index];
- const Glyph gl = uniforms.glyphs.values[gi.index];
- const vec2 glyph_min = gi.position + gl.offset_min;
- const vec2 glyph_max = gi.position + gl.offset_max;
- return !(any(lessThan(tile_max, glyph_min)) || any(greaterThan(tile_min, glyph_max)));
-}
+const uint PRIMITIVE_TYPE_RECT = 0;
+const uint PRIMITIVE_TYPE_GLYPH = 1;
+
+struct PrimitiveInstance {
+ uint packed;
+ uint color;
+ vec2 position;
+};
+
+struct Rect {
+ vec2 half_extent;
+ float border_width;
+ float border_radius;
+};
+
+struct Glyph {
+ ivec2 atlas_min;
+ ivec2 atlas_max;
+
+ vec2 offset_min;
+ vec2 offset_max;
+};
+
+#endif
\ No newline at end of file
#extension GL_KHR_shader_subgroup_vote : require
#include "compute_bindings.h"
-#include "primitive_2d.h"
const uint SUBGROUP_SIZE = 64;
const uint NUM_SUBGROUPS = 16;
for (uint i = 0; i < NUM_PRIMITIVES_WG; i += gl_SubgroupSize.x) {
const uint primitive_index = gl_WorkGroupID.x * NUM_PRIMITIVES_WG + i + gl_SubgroupInvocationID;
+ // Bounds for this primitive, any tiles which intersect this AABB will be written.
vec2 primitive_min = vec2(99999.9);
vec2 primitive_max = vec2(-99999.9);
if (primitive_index < uniforms.num_primitives) {
- const GlyphInstance gi = uniforms.glyph_instances.values[primitive_index];
- const Glyph gl = uniforms.glyphs.values[gi.index];
- primitive_min = gi.position + gl.offset_min;
- primitive_max = gi.position + gl.offset_max;
+ const PrimitiveInstance primitive_instance = uniforms.primitive_instances.values[primitive_index];
+ const uint type = bitfieldExtract(primitive_instance.packed, 30, 2);
+ const uint offset = bitfieldExtract(primitive_instance.packed, 0, 20);
+
+ for (;;) {
+ const uint scalar_type = subgroupBroadcastFirst(type);
+ [[branch]]
+ if (scalar_type == type) {
+ switch (type) {
+ case PRIMITIVE_TYPE_RECT:
+ const Rect rect = uniforms.rects.values[offset];
+ primitive_min = primitive_instance.position - rect.half_extent;
+ primitive_max = primitive_instance.position + rect.half_extent;
+ break;
+ case PRIMITIVE_TYPE_GLYPH:
+ const Glyph glyph = uniforms.glyphs.values[offset];
+ primitive_min = primitive_instance.position + glyph.offset_min;
+ primitive_max = primitive_instance.position + glyph.offset_max;
+ break;
+ }
+ break;
+ }
+ }
}
const vec2 primitives_min = subgroupMin(primitive_min);
for (int y = tile_start.y; y < tile_end.y; y++) {
for (int x = tile_start.x; x < tile_end.x; x++) {
const uvec2 tile_coord = uvec2(x, y);
- const uint tile_index = tile_coord.y * uniforms.tile_stride + tile_coord.x;
const vec2 tile_min = tile_coord * TILE_SIZE;
const vec2 tile_max = min(tile_min + TILE_SIZE, uniforms.screen_resolution);
continue;
}
+ const uint tile_index = tile_coord.y * uniforms.tile_stride + tile_coord.x;
+
if (ballot.x != 0) {
uniforms.tiles.values[tile_index * TILE_STRIDE + TILE_BITMAP_L0_OFFSET + gl_WorkGroupID.x * 32 + word_index + 0] = ballot.x;
}
#extension GL_KHR_shader_subgroup_ballot : require
#include "compute_bindings.h"
-#include "primitive_2d.h"
// TODO: Spec constant support for different subgroup sizes.
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
// Set bits in the L0 bitmap indicate binned primitives for this tile.
const uint primitive_index = index_l0 * 32 + j;
-
- const GlyphInstance gi = uniforms.glyph_instances.values[primitive_index];
- const Glyph gl = uniforms.glyphs.values[gi.index];
- const vec2 glyph_min = gi.position + gl.offset_min;
- const vec2 glyph_max = gi.position + gl.offset_max;
-
- [[branch]]
- if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) {
- const vec2 glyph_size = gl.offset_max - gl.offset_min;
- const vec2 uv = mix(gl.atlas_min, gl.atlas_max, (sample_center - glyph_min) / glyph_size) / uniforms.atlas_resolution;
- const vec4 color = unpackUnorm4x8(gi.color).bgra;
- const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r * color.a;
- accum.rgb = (coverage * color.rgb) + accum.rgb * (1.0 - coverage);
- accum.a = coverage + accum.a * (1.0 - coverage);
+ const PrimitiveInstance primitive_instance = uniforms.primitive_instances.values[primitive_index];
+ const uint type = bitfieldExtract(primitive_instance.packed, 30, 2);
+ const uint offset = bitfieldExtract(primitive_instance.packed, 0, 20);
+
+ switch (type) {
+ case PRIMITIVE_TYPE_RECT: {
+ const Rect rect = uniforms.rects.values[offset];
+ const vec2 rect_min = primitive_instance.position - rect.half_extent;
+ const vec2 rect_max = primitive_instance.position + rect.half_extent;
+ if (all(greaterThanEqual(sample_center, rect_min)) && all(lessThanEqual(sample_center, rect_max))) {
+ const vec4 color = unpackUnorm4x8(primitive_instance.color).bgra;
+ accum.rgb = color.rgb * color.a + accum.rgb * (1.0 - color.a);
+ accum.a = color.a + accum.a * (1.0 - color.a);
+ }
+ break;
+ }
+ case PRIMITIVE_TYPE_GLYPH: {
+ const Glyph glyph = uniforms.glyphs.values[offset];
+ const vec2 glyph_min = primitive_instance.position + glyph.offset_min;
+ const vec2 glyph_max = primitive_instance.position + glyph.offset_max;
+ if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) {
+ const vec2 glyph_size = glyph.offset_max - glyph.offset_min;
+ const vec2 uv = mix(glyph.atlas_min, glyph.atlas_max, (sample_center - glyph_min) / glyph_size) / uniforms.atlas_resolution;
+ const vec4 color = unpackUnorm4x8(primitive_instance.color).bgra;
+ const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r * color.a;
+ accum.rgb = color.rgb * coverage + accum.rgb * (1.0 - coverage);
+ accum.a = coverage + accum.a * (1.0 - coverage);
+ }
+ break;
+ }
}
}
}
use narcissus_core::{dds, Widen as _};
use pipelines::basic::BasicPipeline;
-use pipelines::{GlyphInstance, PrimitiveUniforms, TILE_SIZE, TILE_STRIDE};
+use pipelines::{PrimitiveInstance, PrimitiveUniforms, Rect, TILE_SIZE, TILE_STRIDE};
use renderdoc_sys as rdoc;
use fonts::{FontFamily, Fonts};
tmp_string: String,
- primitive_instances: Vec<GlyphInstance>,
+ primitive_instances: Vec<PrimitiveInstance>,
+ rects: Vec<Rect>,
}
impl<'a> UiState<'a> {
glyph_cache,
tmp_string: default(),
primitive_instances: vec![],
+ rects: vec![],
}
}
+ fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
+ let half_extent_x = width / 2.0;
+ let half_extent_y = height / 2.0;
+ let center_x = x + half_extent_x;
+ let center_y = y + half_extent_y;
+
+ let rect_index = self.rects.len() as u32;
+
+ self.rects.push(Rect {
+ half_extent_x,
+ half_extent_y,
+ border_width: 0.0,
+ border_radius: 0.0,
+ });
+
+ self.primitive_instances.push(PrimitiveInstance::rect(
+ rect_index, 0x4400ff00, center_x, center_y,
+ ))
+ }
+
fn text_fmt(
&mut self,
mut x: f32,
x += advance * scale;
- self.primitive_instances.push(GlyphInstance {
+ self.primitive_instances.push(PrimitiveInstance::glyph(
+ touched_glyph_index,
+ 0x880000ff,
x,
y,
- touched_glyph_index,
- color: 0x880000ff,
- });
+ ));
x += advance_width * scale;
}
microshades::PURPLE_RGBA_F32[3],
);
+ let primitive_instance_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ ui_state.primitive_instances.as_slice(),
+ );
let glyph_buffer = gpu.request_transient_buffer_with_data(
frame,
thread_token,
BufferUsageFlags::STORAGE,
touched_glyphs,
);
- let glyph_instance_buffer = gpu.request_transient_buffer_with_data(
+ let rect_buffer = gpu.request_transient_buffer_with_data(
frame,
thread_token,
BufferUsageFlags::STORAGE,
- ui_state.primitive_instances.as_slice(),
+ ui_state.rects.as_slice(),
);
let num_primitives = ui_state.primitive_instances.len() as u32;
tile_resolution_x: self.tile_resolution_x,
tile_resolution_y: self.tile_resolution_y,
tile_stride: self.tile_resolution_x,
+ primitives_instances_buffer: gpu
+ .get_buffer_address(primitive_instance_buffer.to_arg()),
glyphs_buffer: gpu.get_buffer_address(glyph_buffer.to_arg()),
- glyph_instances_buffer: gpu
- .get_buffer_address(glyph_instance_buffer.to_arg()),
+ rects_buffer: gpu.get_buffer_address(rect_buffer.to_arg()),
tiles_buffer: gpu.get_buffer_address(self.tiles_buffer.to_arg()),
},
);
let base_x = (base_x + 1.0) * 0.5;
let base_y = (base_y + 1.0) * 0.5;
+ for _ in 0..100 {
+ ui_state.rect(0.0, 0.0, width as f32, height as f32);
+ }
+
for i in 0..80 {
let i = i as f32;
ui_state.text_fmt(
);
}
+ ui_state.rect(base_x * 60.0, base_y * 60.0, 120.0, 120.0);
+ ui_state.rect(base_x * 500.0, base_y * 100.0, 120.0, 120.0);
+ ui_state.rect(base_x * 90.0, base_y * 290.0, 140.0, 120.0);
+ ui_state.rect(base_x * 800.0, base_y * 320.0, 120.0, 120.0);
+ ui_state.rect(base_x * 200.0, base_y * 200.0, 120.0, 120.0);
+ ui_state.rect(base_x * 300.0, base_y * 120.0, 120.0, 170.0);
+ ui_state.rect(base_x * 1000.0, base_y * 30.0, 50.0, 120.0);
+ ui_state.rect(base_x * 340.0, base_y * 400.0, 120.0, 110.0);
+ ui_state.rect(base_x * 290.0, base_y * 80.0, 120.0, 10.0);
+ ui_state.rect(base_x * 310.0, base_y * 190.0, 10.0, 120.0);
+
draw_state.draw(
thread_token,
frame,
pub num_primitives_1024: u32,
pub tile_stride: u32,
+ pub primitives_instances_buffer: u64,
+ pub rects_buffer: u64,
pub glyphs_buffer: u64,
- pub glyph_instances_buffer: u64,
pub tiles_buffer: u64,
}
+#[repr(u32)]
+pub enum PrimitiveType {
+ Rect,
+ Glyph,
+}
+
#[allow(unused)]
#[repr(C)]
-pub struct GlyphInstance {
+pub struct PrimitiveInstance {
+ pub packed: u32,
+ pub color: u32,
pub x: f32,
pub y: f32,
- pub touched_glyph_index: TouchedGlyphIndex,
- pub color: u32,
+}
+
+#[repr(C)]
+pub struct Rect {
+ pub half_extent_x: f32,
+ pub half_extent_y: f32,
+ pub border_width: f32,
+ pub border_radius: f32,
+}
+
+impl PrimitiveInstance {
+ #[inline(always)]
+ pub fn glyph(glyph_index: TouchedGlyphIndex, color: u32, x: f32, y: f32) -> Self {
+ let packed = glyph_index.as_u32() | ((PrimitiveType::Glyph as u32) << 30);
+ Self {
+ packed,
+ color,
+ x,
+ y,
+ }
+ }
+
+ #[inline(always)]
+ pub fn rect(rect_index: u32, color: u32, x: f32, y: f32) -> Self {
+ let packed = rect_index | ((PrimitiveType::Rect as u32) << 30);
+ Self {
+ packed,
+ color,
+ x,
+ y,
+ }
+ }
}