Some(SamplerCompareOp::GreaterEq) => (vk::Bool32::True, vk::CompareOp::GreaterOrEqual),
};
+ let unnormalized_coordinates = sampler_desc.unnormalized_coordinates.into();
+
let mut sampler = vk::Sampler::null();
vk_check!(self.device_fn.create_sampler(
self.device,
address_mode_w: address_mode,
compare_enable,
compare_op,
+ unnormalized_coordinates,
..default()
},
None,
pub mip_lod_bias: f32,
pub min_lod: f32,
pub max_lod: f32,
+ pub unnormalized_coordinates: bool,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#version 460
-layout(set = 0, binding = 0) uniform sampler bilinear_sampler;
-layout(set = 0, binding = 1) uniform texture2D albedo;
+#extension GL_GOOGLE_include_directive : require
+
+#include "bindings_graphics.h"
layout(location = 0) in vec2 tex_coord;
layout(location = 1) in vec3 normal;
void main() {
const float n_dot_l = max(dot(normal, vec3(0.0, 1.0, 0.0)), 0.1);
- const vec3 rgb = texture(sampler2D(albedo, bilinear_sampler), vec2(tex_coord.x, tex_coord.y)).rgb;
+ const vec3 rgb = texture(sampler2D(albedo, samplers[SAMPLER_BILINEAR]), vec2(tex_coord.x, tex_coord.y)).rgb;
out_color = vec4(rgb * n_dot_l, 1.0);
}
#ifndef COMPUTE_BINDINGS_INCLUDE
#define COMPUTE_BINDINGS_INCLUDE
-layout (set = 0, binding = 0) uniform sampler bilinear_sampler;
+const uint SAMPLER_BILINEAR = 0;
+const uint SAMPLER_BILINEAR_UNNORMALIZED = 1;
+const uint SAMPLER_COUNT = 2;
+
+layout (set = 0, binding = 0) uniform sampler samplers[SAMPLER_COUNT];
layout (set = 0, binding = 1) uniform texture3D tony_mc_mapface_lut;
layout (set = 0, binding = 2) uniform texture2D glyph_atlas;
layout (set = 0, binding = 3, rgba16f) uniform writeonly image2D ui_layer_write;
--- /dev/null
+#ifndef GRAPHICS_BINDINGS_INCLUDE
+#define GRAPHICS_BINDINGS_INCLUDE
+
+const uint SAMPLER_BILINEAR = 0;
+const uint SAMPLER_BILINEAR_UNNORMALIZED = 1;
+const uint SAMPLER_COUNT = 2;
+
+layout (set = 0, binding = 0) uniform sampler samplers[SAMPLER_COUNT];
+layout (set = 0, binding = 1) uniform texture2D albedo;
+
+#endif
#extension GL_EXT_buffer_reference2 : require
#extension GL_EXT_scalar_block_layout : require
-#include "compute_bindings.h"
+#include "bindings_compute.h"
#include "draw_2d.h"
float srgb_oetf(float a) {
const vec3 encoded = stimulus / (stimulus + 1.0);
const float LUT_DIMS = 48.0;
const vec3 uv = (encoded * ((LUT_DIMS - 1.0) / LUT_DIMS) + 0.5 / LUT_DIMS);
- return textureLod(sampler3D(tony_mc_mapface_lut, bilinear_sampler), uv, 0.0).rgb;
+ return textureLod(sampler3D(tony_mc_mapface_lut, samplers[SAMPLER_BILINEAR]), uv, 0.0).rgb;
}
struct CompositeConstants {
const uvec2 tile_coord = gl_WorkGroupID.xy / (TILE_SIZE / gl_WorkGroupSize.xy);
const uint tile_index = tile_coord.y * constants.tile_resolution.x + tile_coord.x;
- const uint lo = constants.tile_buffer.values[tile_index].min_index;
- const uint hi = constants.tile_buffer.values[tile_index].max_index;
+ const uint lo = constants.tile_buffer.values[tile_index].index_min;
+ const uint hi = constants.tile_buffer.values[tile_index].index_max;
// Display transform
const vec3 stimulus = imageLoad(color_layer, ivec2(gl_GlobalInvocationID.xy)).rgb;
const uint DRAW_2D_CMD_GLYPH = 1;
struct Tile {
- uint min_index;
- uint max_index;
+ uint index_min;
+ uint index_max;
};
struct Glyph {
vec2 offset_max;
};
-struct Draw2dCmd {
+struct Scissor {
+ vec2 offset_min;
+ vec2 offset_max;
+};
+
+struct Cmd {
uint packed_type;
uint words[7];
};
-struct Draw2dCmdRect {
- vec2 bounds_min;
- vec2 bounds_max;
+struct CmdRect {
+ vec2 position;
+ vec2 bound;
uint border_radii;
uint border_color;
uint background_color;
};
-struct Draw2dCmdGlyph {
+struct CmdGlyph {
+ uint index;
vec2 position;
uint color;
};
-Draw2dCmdRect decode_rect(Draw2dCmd cmd) {
- Draw2dCmdRect rect = {
- { uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1]) }, // bounds_min
- { uintBitsToFloat(cmd.words[2]), uintBitsToFloat(cmd.words[3]) }, // bounds_max
+CmdRect decode_rect(Cmd cmd) {
+ CmdRect rect = {
+ { uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1]) }, // position
+ { uintBitsToFloat(cmd.words[2]), uintBitsToFloat(cmd.words[3]) }, // bound
cmd.words[4], // border_radii
cmd.words[5], // border_color
cmd.words[6], // background_color
return rect;
}
-Draw2dCmdGlyph decode_glyph(in Draw2dCmd cmd) {
- return Draw2dCmdGlyph(vec2(uintBitsToFloat(cmd.words[0]), uintBitsToFloat(cmd.words[1])), cmd.words[2]);
+CmdGlyph decode_glyph(Cmd cmd) {
+ CmdGlyph glyph = {
+ cmd.words[0], // index
+ { uintBitsToFloat(cmd.words[1]), uintBitsToFloat(cmd.words[2]) }, // position
+ cmd.words[3], // color
+ };
+ return glyph;
}
-layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer Draw2dCommandRef
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer CommandRef
+{
+ Cmd values[];
+};
+
+layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer ScissorRef
{
- Draw2dCmd values[];
+ Scissor values[];
};
layout(buffer_reference, std430, buffer_reference_align = 16) readonly buffer GlyphRef
#include "draw_2d.h"
#include "radix_sort.h"
-struct Draw2dClearConstants {
+struct ClearConstants {
FinishedRef finished_buffer;
CoarseRef coarse_buffer;
};
-layout(std430, push_constant) uniform Draw2dClearConstantsBlock {
- Draw2dClearConstants constants;
+layout(std430, push_constant) uniform ClearConstantsBlock {
+ ClearConstants constants;
};
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
#include "draw_2d.h"
-struct Draw2dScatterConstants {
- uvec2 screen_resolution;
+struct ScatterConstants {
uvec2 tile_resolution;
uint draw_buffer_len;
uint coarse_buffer_len;
- Draw2dCommandRef draw_buffer;
+ CommandRef draw_buffer;
+ ScissorRef scissor_buffer;
GlyphRef glyph_buffer;
CoarseRef coarse_buffer;
};
-layout(std430, push_constant) uniform Draw2dScatterConstantsBlock {
- Draw2dScatterConstants constants;
+layout(std430, push_constant) uniform ScatterConstantsBlock {
+ ScatterConstants constants;
};
const uint MAX_TILES = 256;
const bool in_bounds = draw_index < constants.draw_buffer_len;
+ uint cmd_scissor = 0;
vec2 cmd_min = vec2(99999.9);
vec2 cmd_max = vec2(-99999.9);
if (in_bounds) {
const uint packed_type = constants.draw_buffer.values[draw_index].packed_type;
const uint cmd_type = packed_type >> 24;
- const uint cmd_packed = packed_type & 0xffffff;
+ cmd_scissor = packed_type & 0xffff;
for (;;) {
const uint scalar_type = subgroupBroadcastFirst(cmd_type);
if (scalar_type == cmd_type) {
switch (scalar_type) {
case DRAW_2D_CMD_RECT:
- const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]);
- cmd_min = cmd_rect.bounds_min;
- cmd_max = cmd_rect.bounds_max;
+ const CmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]);
+ cmd_min = cmd_rect.position;
+ cmd_max = cmd_rect.position + cmd_rect.bound;
break;
case DRAW_2D_CMD_GLYPH:
- const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]);
- const Glyph glyph = constants.glyph_buffer.values[cmd_packed];
+ const CmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]);
+ const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index];
cmd_min = cmd_glyph.position + glyph.offset_min;
cmd_max = cmd_glyph.position + glyph.offset_max;
break;
}
}
- // For any out-of-bounds draws, we'll get the defaults of 99999.9 and -99999.9, which will fail
- // here. Out-of-bounds draws are therefore off-screen. Well, so long as you don't have 27 4k
- // monitors arranged horizontally.
- const bool offscreen = any(greaterThanEqual(cmd_min, cmd_max)) || any(greaterThan(cmd_min, constants.screen_resolution)) || any(lessThan(cmd_max, vec2(0.0)));
+ const Scissor scissor = constants.scissor_buffer.values[cmd_scissor];
+
+ const bool out_of_bounds = any(greaterThanEqual(cmd_min, cmd_max)) || any(greaterThan(cmd_min, scissor.offset_max)) || any(lessThan(cmd_max, scissor.offset_min));
// Are all draws off-screen?
- if (subgroupAll(offscreen)) {
+ if (subgroupAll(out_of_bounds)) {
return;
}
+ cmd_min = max(cmd_min, scissor.offset_min);
+ cmd_max = min(cmd_max, scissor.offset_max);
+
// Make sure off-screen commands don't contribute to the bounds.
- const uvec2 cmds_min_tile = uvec2(clamp(subgroupMin(offscreen ? ivec2(999999) : ivec2(floor(cmd_min / TILE_SIZE))), ivec2(0), ivec2(constants.tile_resolution)));
- const uvec2 cmds_max_tile = uvec2(clamp(subgroupMax(offscreen ? ivec2(-999999) : ivec2(floor(cmd_max / TILE_SIZE))), ivec2(0), ivec2(constants.tile_resolution)));
- const uvec2 cmd_min_tile = uvec2(clamp(ivec2(floor(cmd_min / TILE_SIZE)), ivec2(0), ivec2(constants.tile_resolution)));
- const uvec2 cmd_max_tile = uvec2(clamp(ivec2(floor(cmd_max / TILE_SIZE)), ivec2(0), ivec2(constants.tile_resolution)));
+ const uvec2 cmds_tile_min = uvec2(clamp(subgroupMin(out_of_bounds ? ivec2(999999) : ivec2(floor(cmd_min / TILE_SIZE))), ivec2(0), constants.tile_resolution));
+ const uvec2 cmds_tile_max = uvec2(clamp(subgroupMax(out_of_bounds ? ivec2(-999999) : ivec2(floor(cmd_max / TILE_SIZE))), ivec2(0), constants.tile_resolution));
+ const uvec2 cmd_tile_min = uvec2(clamp(ivec2(floor(cmd_min / TILE_SIZE)), ivec2(0), constants.tile_resolution));
+ const uvec2 cmd_tile_max = uvec2(clamp(ivec2(floor(cmd_max / TILE_SIZE)), ivec2(0), constants.tile_resolution));
- const bool cmd_dominates_bounds = all(equal(cmd_min_tile, cmds_min_tile)) && all(equal(cmd_max_tile, cmds_max_tile));
+ const bool cmd_dominates_bounds = all(equal(cmd_tile_min, cmds_tile_min)) && all(equal(cmd_tile_max, cmds_tile_max));
const bool use_combined_bounds = subgroupAny(cmd_dominates_bounds);
if (use_combined_bounds) {
- const uvec2 tile_count = cmds_max_tile - cmds_min_tile + ivec2(1);
+ const uvec2 tile_count = cmds_tile_max - cmds_tile_min + ivec2(1);
uint offset;
if (subgroupElect()) {
for (uint i = 0; i < tile_count.y; i++) {
for (uint j = 0; j < tile_count.x; j += gl_SubgroupSize) {
const uint jj = j + gl_SubgroupInvocationID;
- const uint y = cmds_min_tile.y + i;
- const uint x = cmds_min_tile.x + jj;
+ const uint y = cmds_tile_min.y + i;
+ const uint x = cmds_tile_min.x + jj;
if (jj < tile_count.x) {
const uint packed = ((y & 0xff) << 24) | ((x & 0xff) << 16) | (gl_WorkGroupID.x & 0xffff);
const uint index = offset + i * tile_count.x + jj;
}
}
} else {
- const uint start = cmds_min_tile.y * BITMAP_STRIDE + cmds_min_tile.x / 32;
- const uint end = cmds_max_tile.y * BITMAP_STRIDE + cmds_max_tile.x / 32;
+ const uint start = cmds_tile_min.y * BITMAP_STRIDE + cmds_tile_min.x / 32;
+ const uint end = cmds_tile_max.y * BITMAP_STRIDE + cmds_tile_max.x / 32;
for (uint i = start; i <= end; i += gl_SubgroupSize) {
const uint ii = i + gl_SubgroupInvocationID;
subgroupBarrier();
- if (!offscreen) {
- const uint min_word = cmd_min_tile.x / 32;
- const uint max_word = cmd_max_tile.x / 32;
- const uint min_bit = cmd_min_tile.x & 31;
- const uint max_bit = cmd_max_tile.x & 31;
+ if (!out_of_bounds) {
+ const uint min_word = cmd_tile_min.x / 32;
+ const uint max_word = cmd_tile_max.x / 32;
+ const uint min_bit = cmd_tile_min.x & 31;
+ const uint max_bit = cmd_tile_max.x & 31;
const uint lsb = ~((1 << min_bit) - 1);
const uint msb = ((1 << max_bit) - 1) | 1 << max_bit;
if (min_word == max_word) {
- for (uint y = cmd_min_tile.y; y <= cmd_max_tile.y; y++) {
+ for (uint y = cmd_tile_min.y; y <= cmd_tile_max.y; y++) {
atomicOr(intersected_tiles[y * BITMAP_STRIDE + min_word], lsb & msb);
}
} else {
- for (uint y = cmd_min_tile.y; y <= cmd_max_tile.y; y++) {
+ for (uint y = cmd_tile_min.y; y <= cmd_tile_max.y; y++) {
atomicOr(intersected_tiles[y * BITMAP_STRIDE + min_word], lsb);
for (uint i = min_word + 1; i <= (max_word - 1); i++) {
intersected_tiles[y * BITMAP_STRIDE + i] = 0xffffffff;
#extension GL_KHR_shader_subgroup_shuffle_relative: enable
#extension GL_KHR_shader_subgroup_vote : require
-#include "compute_bindings.h"
-
#include "draw_2d.h"
#include "indirect.h"
#include "radix_sort.h"
VkDispatchIndirectCommand dimensions;
};
-struct Draw2dSortConstants {
+struct SortConstants {
uint coarse_buffer_len;
uint _pad;
VkDispatchIndirectCommandRef indirect_dispatch_buffer;
CoarseRef coarse_buffer;
};
-layout(std430, push_constant) uniform Draw2dSortConstantsBlock {
- Draw2dSortConstants constants;
+layout(std430, push_constant) uniform SortConstantsBlock {
+ SortConstants constants;
};
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
#include "draw_2d.h"
-struct Draw2dResolveConstants {
- uvec2 screen_resolution;
- uvec2 tile_resolution;
-
+struct ResolveConstants {
+ uint tile_stride;
uint draw_buffer_len;
- uint _pad;
- Draw2dCommandRef draw_buffer;
+ CommandRef draw_buffer;
+ ScissorRef scissor_buffer;
GlyphRef glyph_buffer;
CoarseRef coarse_buffer;
FineRef fine_buffer;
TileRef tile_buffer;
};
-layout(std430, push_constant) uniform Draw2dResolveConstantsBlock {
- Draw2dResolveConstants constants;
+layout(std430, push_constant) uniform ResolveConstantsBlock {
+ ResolveConstants constants;
};
layout (local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in;
const uint local_id = gl_SubgroupID * gl_SubgroupSize + gl_SubgroupInvocationID;
const uint x = gl_GlobalInvocationID.y;
const uint y = gl_GlobalInvocationID.z;
- const uint tile_offset = (constants.tile_resolution.x * y + x);
+ const uint tile_offset = constants.tile_stride * y + x;
const uint search = ((y & 0xff) << 24) | ((x & 0xff) << 16);
const uint count = constants.coarse_buffer.values[0];
if (count == 0) {
- constants.tile_buffer.values[tile_offset].min_index = 0;
- constants.tile_buffer.values[tile_offset].max_index = 0;
+ constants.tile_buffer.values[tile_offset].index_min = 0;
+ constants.tile_buffer.values[tile_offset].index_max = 0;
return;
}
}
const vec2 tile_min = uvec2(x, y) * TILE_SIZE;
- const vec2 tile_max = min(tile_min + TILE_SIZE, constants.screen_resolution);
+ const vec2 tile_max = tile_min + TILE_SIZE;
bool hit_opaque = false;
uint lo = base + 1;
const uint packed_type = constants.draw_buffer.values[draw_index].packed_type;
const uint cmd_type = packed_type >> 24;
- const uint cmd_packed = packed_type & 0xffffff;
-
- for (;;) {
- const uint scalar_type = subgroupBroadcastFirst(cmd_type);
- [[branch]]
- if (scalar_type == cmd_type) {
- switch (scalar_type) {
- case DRAW_2D_CMD_RECT:
- const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]);
- cmd_min = cmd_rect.bounds_min;
- cmd_max = cmd_rect.bounds_max;
- if ((cmd_rect.background_color & 0xff000000) == 0xff000000) {
- const float border_width = float(cmd_packed & 0xff);
- const vec4 border_radii = unpackUnorm4x8(cmd_rect.border_radii);
- const float max_border_radius = max(border_radii.x, max(border_radii.y, max(border_radii.z, border_radii.w))) * 255.0;
- const float shrink = (2.0 - sqrt(2.0)) * max_border_radius + (cmd_rect.border_color & 0xff000000) == 0xff000000 ? 0.0 : border_width;
- opaque_tile = all(greaterThanEqual(tile_min, cmd_min + shrink)) && all(lessThanEqual(tile_max, cmd_max - shrink));
- }
- break;
- case DRAW_2D_CMD_GLYPH:
- const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]);
- const Glyph glyph = constants.glyph_buffer.values[cmd_packed];
- cmd_min = cmd_glyph.position + glyph.offset_min;
- cmd_max = cmd_glyph.position + glyph.offset_max;
- break;
+ const uint cmd_scissor = packed_type & 0xffff;
+
+ const Scissor scissor = constants.scissor_buffer.values[cmd_scissor];
+
+ // If the tile doesn't intersect the scissor region it doesn't need to do work here.
+ if (any(lessThan(scissor.offset_max, tile_min)) || any(greaterThan(scissor.offset_min, tile_max))) {
+ intersects = false;
+ } else {
+ for (;;) {
+ const uint scalar_type = subgroupBroadcastFirst(cmd_type);
+ [[branch]]
+ if (scalar_type == cmd_type) {
+ switch (scalar_type) {
+ case DRAW_2D_CMD_RECT:
+ const CmdRect cmd_rect = decode_rect(constants.draw_buffer.values[draw_index]);
+ cmd_min = cmd_rect.position;
+ cmd_max = cmd_rect.position + cmd_rect.bound;
+
+ const bool background_opaque = (cmd_rect.background_color & 0xff000000) == 0xff000000;
+ if (background_opaque) {
+ const float border_width = float((packed_type >> 16) & 0xff);
+ const bool border_opaque = (cmd_rect.border_color & 0xff000000) == 0xff000000;
+ const vec4 border_radii = unpackUnorm4x8(cmd_rect.border_radii);
+ const float max_border_radius = max(border_radii.x, max(border_radii.y, max(border_radii.z, border_radii.w))) * 255.0;
+ const float shrink = ((2.0 - sqrt(2.0)) * max_border_radius) + (border_opaque ? 0.0 : border_width);
+
+ const vec2 cmd_shrunk_min = max(scissor.offset_min, cmd_min + shrink);
+ const vec2 cmd_shrunk_max = min(scissor.offset_max, cmd_max - shrink);
+ opaque_tile = all(greaterThan(cmd_shrunk_max, cmd_shrunk_min)) && all(greaterThan(tile_min, cmd_shrunk_min)) && all(lessThan(tile_max, cmd_shrunk_max));
+ }
+ break;
+ case DRAW_2D_CMD_GLYPH:
+ const CmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[draw_index]);
+ const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index];
+ cmd_min = cmd_glyph.position + glyph.offset_min;
+ cmd_max = cmd_glyph.position + glyph.offset_max;
+ break;
+ }
+ break;
}
- break;
}
- }
- intersects = !(any(lessThan(tile_max, cmd_min)) || any(greaterThan(tile_min, cmd_max)));
+ cmd_min = max(cmd_min, scissor.offset_min);
+ cmd_max = min(cmd_max, scissor.offset_max);
+ intersects = !(any(lessThan(tile_max, cmd_min)) || any(greaterThan(tile_min, cmd_max)));
+ }
}
uint intersects_mask = subgroupBallot(intersects).x;
}
}
- constants.tile_buffer.values[tile_offset].min_index = lo + 1;
- constants.tile_buffer.values[tile_offset].max_index = hi + 1;
+ constants.tile_buffer.values[tile_offset].index_min = lo + 1;
+ constants.tile_buffer.values[tile_offset].index_max = hi + 1;
}
#extension GL_KHR_shader_subgroup_vote : require
#extension GL_KHR_shader_subgroup_ballot : require
-#include "compute_bindings.h"
+#include "bindings_compute.h"
#include "draw_2d.h"
#include "sdf.h"
-struct Draw2dRasterizeConstants {
- uvec2 screen_resolution;
- uvec2 tile_resolution;
- uvec2 atlas_resolution;
+struct RasterizeConstants {
+ uint tile_stride;
+ uint _pad;
- Draw2dCommandRef draw_buffer;
+ CommandRef draw_buffer;
+ ScissorRef scissor_buffer;
GlyphRef glyph_buffer;
CoarseRef coarse_buffer;
FineRef fine_buffer;
TileRef tile_buffer;
};
-layout(std430, push_constant) uniform Draw2dRasterizeConstantsBlock {
- Draw2dRasterizeConstants constants;
+layout(std430, push_constant) uniform RasterizeConstantsBlock {
+ RasterizeConstants constants;
};
/// x = (((index >> 2) & 0x0007) & 0xFFFE) | index & 0x0001
void main() {
const uvec2 tile_coord = gl_WorkGroupID.xy / (TILE_SIZE / gl_WorkGroupSize.xy);
- const uint tile_index = tile_coord.y * constants.tile_resolution.x + tile_coord.x;
+ const uint tile_index = tile_coord.y * constants.tile_stride + tile_coord.x;
- const uint lo = constants.tile_buffer.values[tile_index].min_index;
- const uint hi = constants.tile_buffer.values[tile_index].max_index;
+ const uint lo = constants.tile_buffer.values[tile_index].index_min;
+ const uint hi = constants.tile_buffer.values[tile_index].index_max;
if (lo == hi) {
return;
const uint base_index = (constants.coarse_buffer.values[i] & 0xffff) * 32;
const uint packed_type = constants.draw_buffer.values[base_index + index].packed_type;
const uint cmd_type = packed_type >> 24;
- const uint cmd_packed = packed_type & 0xffffff;
+ const uint cmd_scissor = packed_type & 0xffff;
+
+ const Scissor scissor = constants.scissor_buffer.values[cmd_scissor];
vec4 primitive_color = vec4(0.0);
switch (cmd_type) {
case DRAW_2D_CMD_RECT:
- const Draw2dCmdRect cmd_rect = decode_rect(constants.draw_buffer.values[base_index + index]);
+ {
+ const CmdRect cmd_rect = decode_rect(constants.draw_buffer.values[base_index + index]);
+
+ const vec2 cmd_min = cmd_rect.position;
+ const vec2 cmd_max = cmd_rect.position + cmd_rect.bound;
- const float border_width = float(cmd_packed & 0xff);
+ const float border_width = float((packed_type >> 16) & 0xff);
const vec4 border_radii = unpackUnorm4x8(cmd_rect.border_radii) * 255.0;
const float max_border_radius = max(border_radii.x, max(border_radii.y, max(border_radii.z, border_radii.w)));
const float shrink = (2.0 - sqrt(2.0)) * max_border_radius;
- if (all(greaterThan(sample_center, cmd_rect.bounds_min + border_width + shrink)) && all(lessThan(sample_center, cmd_rect.bounds_max - border_width - shrink))) {
+ const vec2 cmd_min_clipped = max(scissor.offset_min, cmd_min + border_width + shrink);
+ const vec2 cmd_max_clipped = min(scissor.offset_max, cmd_max - border_width - shrink);
+
+ if (all(greaterThan(sample_center, cmd_min_clipped)) && all(lessThan(sample_center, cmd_max_clipped))) {
primitive_color = unpackUnorm4x8(cmd_rect.background_color).bgra;
} else {
- const vec2 b = (cmd_rect.bounds_max - cmd_rect.bounds_min) / 2.0;
- const vec2 p = cmd_rect.bounds_min + b - sample_center;
+ const vec2 b = cmd_rect.bound / 2.0;
+ const vec2 p = cmd_rect.position + b - sample_center;
float d;
if (all(equal(border_radii, vec4(0.0)))) {
const vec4 border_color = unpackUnorm4x8(cmd_rect.border_color).bgra;
primitive_color = mix(background_color, border_color, smoothstep(1.0, 0.0, 1.0 - d - border_width));
primitive_color = mix(primitive_color, vec4(0), smoothstep(1.0, 0.0, 1.0 - d));
+
+ const vec2 clip_b = (scissor.offset_max - scissor.offset_min) / 2.0;
+ const vec2 clip_p = scissor.offset_min + clip_b - sample_center;
+ d = max(d, sdf_box(clip_p, clip_b));
+ primitive_color = d < 0.0 ? primitive_color : vec4(0);
}
break;
+ }
case DRAW_2D_CMD_GLYPH:
- const Draw2dCmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[base_index + index]);
- const Glyph glyph = constants.glyph_buffer.values[cmd_packed];
- const vec2 glyph_min = cmd_glyph.position + glyph.offset_min;
- const vec2 glyph_max = cmd_glyph.position + glyph.offset_max;
- if (all(greaterThanEqual(sample_center, glyph_min)) && all(lessThanEqual(sample_center, glyph_max))) {
+ {
+ const CmdGlyph cmd_glyph = decode_glyph(constants.draw_buffer.values[base_index + index]);
+ const Glyph glyph = constants.glyph_buffer.values[cmd_glyph.index];
+ const vec2 cmd_min = cmd_glyph.position + glyph.offset_min;
+ const vec2 cmd_max = cmd_glyph.position + glyph.offset_max;
+ if (all(greaterThanEqual(sample_center, max(scissor.offset_min, cmd_min))) && all(lessThanEqual(sample_center, min(scissor.offset_max, cmd_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) / constants.atlas_resolution;
+ const vec2 uv = mix(glyph.atlas_min, glyph.atlas_max, (sample_center - cmd_min) / glyph_size);
const vec4 color = unpackUnorm4x8(cmd_glyph.color).bgra;
- const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r * color.a;
+ const float coverage = textureLod(sampler2D(glyph_atlas, samplers[SAMPLER_BILINEAR_UNNORMALIZED]), uv, 0.0).r * color.a;
primitive_color = color * coverage;
}
break;
+ }
}
// does it blend?
#[derive(Clone, Copy)]
struct CmdGlyph {
packed: u32,
+ index: u32,
position: Vec2,
color: u32,
}
-const _: () = assert!(std::mem::size_of::<CmdGlyph>() == 16);
+const _: () = assert!(std::mem::size_of::<CmdGlyph>() == 20);
#[repr(C)]
#[derive(Clone, Copy)]
struct CmdRect {
/// 31 . . . 0
- /// tttt tttt 0000 0000 0000 0000 bbbb bbbb
+ /// tttt tttt bbbb bbbb ssss ssss ssss ssss
///
/// t: Type
/// b: Border width
+ /// s: Scissor index
packed: u32,
- bounds_min: Vec2,
- bounds_max: Vec2,
+ position: Vec2,
+ bound: Vec2,
border_radii: u32,
border_color: u32,
impl Draw2dCmd {
#[inline(always)]
- pub fn glyph(touched_glyph_index: TouchedGlyphIndex, color: u32, position: Vec2) -> Self {
+ pub fn glyph(
+ scissor_index: u32,
+ touched_glyph_index: TouchedGlyphIndex,
+ color: u32,
+ position: Vec2,
+ ) -> Self {
+ let glyph_index = touched_glyph_index.as_u32();
Self {
glyph: CmdGlyph {
- packed: (Draw2dCmdType::Glyph as u32) << 24
- | (touched_glyph_index.as_u32() & 0xffffff),
+ packed: ((Draw2dCmdType::Glyph as u32) << 24) | (scissor_index & 0xffff),
+ index: glyph_index,
position,
color,
},
#[inline(always)]
pub fn rect(
- bounds_min: Vec2,
- bounds_max: Vec2,
+ scissor_index: u32,
+ position: Vec2,
+ bound: Vec2,
border_width: u8,
border_radii: [u8; 4],
border_color: u32,
) -> Self {
Self {
rect: CmdRect {
- packed: (Draw2dCmdType::Rect as u32) << 24 | border_width as u32,
- bounds_min,
- bounds_max,
+ packed: ((Draw2dCmdType::Rect as u32) << 24)
+ | ((border_width as u32) << 16)
+ | (scissor_index & 0xffff),
+ position,
+ bound,
border_radii: u32::from_ne_bytes(border_radii),
background_color,
border_color,
}
}
+#[repr(C)]
+pub struct Draw2dScissor {
+ pub offset_min: Vec2,
+ pub offset_max: Vec2,
+}
+
pub struct Samplers {
pub bilinear: Sampler,
+ pub bilinear_unnormalized: Sampler,
}
impl Samplers {
mip_lod_bias: 0.0,
min_lod: 0.0,
max_lod: 0.0,
+ unnormalized_coordinates: false,
});
- Samplers { bilinear }
+
+ let bilinear_unnormalized = gpu.create_sampler(&SamplerDesc {
+ filter: SamplerFilter::Bilinear,
+ address_mode: SamplerAddressMode::Clamp,
+ compare_op: None,
+ mip_lod_bias: 0.0,
+ min_lod: 0.0,
+ max_lod: 0.0,
+ unnormalized_coordinates: true,
+ });
+
+ Samplers {
+ bilinear,
+ bilinear_unnormalized,
+ }
}
}
#[repr(C)]
pub struct Draw2dScatterConstants<'a> {
- pub screen_resolution_x: u32,
- pub screen_resolution_y: u32,
pub tile_resolution_x: u32,
pub tile_resolution_y: u32,
pub coarse_buffer_len: u32,
pub draw_buffer_address: BufferAddress<'a>,
+ pub scissor_buffer_address: BufferAddress<'a>,
pub glyph_buffer_address: BufferAddress<'a>,
pub coarse_buffer_address: BufferAddress<'a>,
}
#[repr(C)]
pub struct Draw2dResolveConstants<'a> {
- pub screen_resolution_x: u32,
- pub screen_resolution_y: u32,
- pub tile_resolution_x: u32,
- pub tile_resolution_y: u32,
-
+ pub tile_stride: u32,
pub draw_buffer_len: u32,
- pub _pad: u32,
pub draw_buffer_address: BufferAddress<'a>,
+ pub scissor_buffer_address: BufferAddress<'a>,
pub glyph_buffer_address: BufferAddress<'a>,
pub coarse_buffer_address: BufferAddress<'a>,
pub fine_buffer_address: BufferAddress<'a>,
#[repr(C)]
pub struct Draw2dRasterizeConstants<'a> {
- pub screen_resolution_x: u32,
- pub screen_resolution_y: u32,
- pub tile_resolution_x: u32,
- pub tile_resolution_y: u32,
- pub atlas_resolution_x: u32,
- pub atlas_resolution_y: u32,
+ pub tile_stride: u32,
+ pub _pad: u32,
pub draw_buffer_address: BufferAddress<'a>,
+ pub scissor_buffer_address: BufferAddress<'a>,
pub glyph_buffer_address: BufferAddress<'a>,
pub coarse_buffer_address: BufferAddress<'a>,
pub fine_buffer_address: BufferAddress<'a>,
impl Pipelines {
pub fn load(gpu: &Gpu) -> Self {
let samplers = Samplers::load(gpu);
- let immutable_samplers = &[samplers.bilinear];
+ let immutable_samplers = &[samplers.bilinear, samplers.bilinear_unnormalized];
let graphics_bind_group_layout = gpu.create_bind_group_layout(&[
// Samplers
use std::path::Path;
use std::time::{Duration, Instant};
-use narcissus_core::dds;
+use narcissus_core::{dds, Widen};
use shark_shaders::pipelines::{
calculate_spine_size, BasicConstants, CompositeConstants, ComputeBinds, Draw2dClearConstants,
Draw2dCmd, Draw2dRasterizeConstants, Draw2dResolveConstants, Draw2dScatterConstants,
- Draw2dSortConstants, GraphicsBinds, Pipelines, RadixSortDownsweepConstants,
+ Draw2dScissor, Draw2dSortConstants, GraphicsBinds, Pipelines, RadixSortDownsweepConstants,
RadixSortUpsweepConstants, DRAW_2D_TILE_SIZE,
};
fonts: &'a Fonts<'a>,
glyph_cache: GlyphCache<'a, Fonts<'a>>,
+ width: f32,
+ height: f32,
scale: f32,
tmp_string: String,
+ scissors: Vec<Draw2dScissor>,
+ scissor_stack: Vec<u32>,
+
draw_cmds: Vec<Draw2dCmd>,
}
Self {
fonts,
glyph_cache,
+ width: 0.0,
+ height: 0.0,
scale: 1.0,
tmp_string: default(),
+ scissors: vec![],
+ scissor_stack: vec![],
+
draw_cmds: vec![],
}
}
+ fn begin_frame(&mut self, width: f32, height: f32, scale: f32) {
+ self.width = width;
+ self.height = height;
+ self.scale = scale;
+
+ self.draw_cmds.clear();
+
+ self.scissor_stack.clear();
+ self.scissors.clear();
+
+ // Scissor 0 is always the screen bounds.
+ self.scissors.push(Draw2dScissor {
+ offset_min: vec2(0.0, 0.0),
+ offset_max: vec2(width, height),
+ });
+ }
+
+ fn push_scissor(
+ &mut self,
+ mut offset_min: Vec2,
+ mut offset_max: Vec2,
+ intersect_with_current: bool,
+ ) {
+ if intersect_with_current {
+ let current_scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
+ let current_scissor = &self.scissors[current_scissor_index.widen()];
+ offset_min = Vec2::max(offset_min, current_scissor.offset_min);
+ offset_max = Vec2::min(offset_max, current_scissor.offset_max);
+ }
+
+ let scissor_index = self.scissors.len() as u32;
+ self.scissors.push(Draw2dScissor {
+ offset_min,
+ offset_max,
+ });
+ self.scissor_stack.push(scissor_index);
+ }
+
+ fn push_fullscreen_scissor(&mut self) {
+ // The fullscreen scissor is always at index 0
+ self.scissor_stack.push(0);
+ }
+
+ fn pop_scissor(&mut self) {
+ // It's invalid to pop more than we've pushed.
+ self.scissor_stack.pop().expect("unbalanced push / pop");
+ }
+
fn rect(
&mut self,
- position: Vec2,
- bounds: Vec2,
+ x: f32,
+ y: f32,
+ width: f32,
+ height: f32,
border_width: f32,
border_radii: [f32; 4],
border_color: u32,
background_color: u32,
) {
- let bounds = bounds * self.scale;
-
- let bounds_min = position;
- let bounds_max = position + bounds;
+ let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
- let border_width = (border_width * self.scale).clamp(0.0, 255.0).floor() as u8;
- let border_radii =
- border_radii.map(|radius| (radius * self.scale).clamp(0.0, 255.0).floor() as u8);
+ let border_width = border_width.clamp(0.0, 255.0).floor() as u8;
+ let border_radii = border_radii.map(|radius| radius.clamp(0.0, 255.0).floor() as u8);
self.draw_cmds.push(Draw2dCmd::rect(
- bounds_min,
- bounds_max,
+ scissor_index,
+ vec2(x, y),
+ vec2(width, height),
border_width,
border_radii,
border_color,
font_size_px: f32,
args: std::fmt::Arguments,
) -> f32 {
+ let scissor_index = self.scissor_stack.last().copied().unwrap_or(0);
+
let font = self.fonts.font(font_family);
let font_size_px = font_size_px * self.scale;
let scale = font.scale_for_size_px(font_size_px);
x += advance * scale;
self.draw_cmds.push(Draw2dCmd::glyph(
+ scissor_index,
touched_glyph_index,
microshades::GRAY_RGBA8[4].rotate_right(8),
vec2(x, y),
);
let draw_buffer_len = ui_state.draw_cmds.len() as u32;
- ui_state.draw_cmds.clear();
+
+ let scissor_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ ui_state.scissors.as_slice(),
+ );
let glyph_buffer = gpu.request_transient_buffer_with_data(
frame,
);
let draw_buffer_address = gpu.get_buffer_address(draw_buffer.to_arg());
+ let scissor_buffer_address = gpu.get_buffer_address(scissor_buffer.to_arg());
let glyph_buffer_address = gpu.get_buffer_address(glyph_buffer.to_arg());
let coarse_buffer_address = gpu.get_buffer_address(coarse_buffer.to_arg());
let indirect_dispatch_buffer_address =
ShaderStageFlags::COMPUTE,
0,
&Draw2dScatterConstants {
- screen_resolution_x: self.width,
- screen_resolution_y: self.height,
tile_resolution_x: self.tile_resolution_x,
tile_resolution_y: self.tile_resolution_y,
draw_buffer_len,
coarse_buffer_len: COARSE_BUFFER_LEN as u32,
draw_buffer_address,
+ scissor_buffer_address,
glyph_buffer_address,
coarse_buffer_address,
},
ShaderStageFlags::COMPUTE,
0,
&Draw2dResolveConstants {
- screen_resolution_x: self.width,
- screen_resolution_y: self.height,
- tile_resolution_x: self.tile_resolution_x,
- tile_resolution_y: self.tile_resolution_y,
+ tile_stride: self.tile_resolution_x,
draw_buffer_len,
- _pad: 0,
draw_buffer_address,
+ scissor_buffer_address,
glyph_buffer_address,
coarse_buffer_address,
fine_buffer_address: tmp_buffer_address,
ShaderStageFlags::COMPUTE,
0,
&Draw2dRasterizeConstants {
- screen_resolution_x: self.width,
- screen_resolution_y: self.height,
- tile_resolution_x: self.tile_resolution_x,
- tile_resolution_y: self.tile_resolution_y,
- atlas_resolution_x: atlas_width,
- atlas_resolution_y: atlas_height,
+ tile_stride: self.tile_resolution_x,
+ _pad: 0,
draw_buffer_address,
+ scissor_buffer_address,
glyph_buffer_address,
coarse_buffer_address,
fine_buffer_address: tmp_buffer_address,
tick_accumulator -= target_dt;
}
- ui_state.scale = ui_scale_override.unwrap_or(window_display_scale);
-
let draw_start = Instant::now();
let tick_duration = draw_start - tick_start;
- let (base_x, base_y) = sin_cos_pi_f32(game_state.time);
- let base_x = (base_x + 1.0) * 0.5 * ui_state.scale;
- let base_y = (base_y + 1.0) * 0.5 * ui_state.scale;
- for _ in 0..100 {
- ui_state.rect(
- vec2(100.0, 100.0),
- vec2(1000.0, 1000.0),
- 0.0,
- [25.0; 4],
- 0,
- 0x88442211,
- );
- }
+ ui_state.begin_frame(
+ width as f32,
+ height as f32,
+ ui_scale_override.unwrap_or(window_display_scale),
+ );
- let (s, _) = sin_cos_pi_f32(game_state.time * 0.1);
-
- let mut y = 8.0 * ui_state.scale;
- for _ in 0..224 {
- let vertical_advance = ui_state.text_fmt(
- 5.0,
- y,
- FontFamily::NotoSansJapanese,
- 16.0 + s * 8.0,
- format_args!(
- "お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████"
- ),
- );
- y += vertical_advance;
- }
+ {
+ let width = width as f32;
+ let height = height as f32;
+
+ let (s, c) = sin_cos_pi_f32(game_state.time * 0.1);
- for i in 0..500 {
- let (rect_x, rect_y) = sin_cos_pi_f32(game_state.time * 0.1 + i as f32 * 1.01);
+ let w = width / 5.0;
+ let h = height / 5.0;
+ let x = width / 2.0 + w * s;
+ let y = height / 2.0 + w * c;
+
+ ui_state.push_scissor(vec2(x - w, y - h), vec2(x + w, y + h), true);
ui_state.rect(
- (vec2(width as f32, height as f32) / 2.0)
- - 250.0
- - vec2(rect_x, rect_y) * 1000.0,
- vec2(400.0, 400.0),
- 10.0,
- [rect_x * 50.0, rect_y * 50.0, 25.0, 25.0],
- 0xffffffff,
- microshades::BLUE_RGBA8[4].rotate_right(8),
+ 0.0, 0.0, width, height, 0.0, [0.0; 4], 0xffffffff, 0xffffffff,
);
- }
+ ui_state.pop_scissor();
- ui_state.rect(
- vec2(base_x, base_y) * 60.0,
- vec2(400.0, 400.0),
- 0.0,
- [0.0; 4],
- 0,
- microshades::ORANGE_RGBA8[2].rotate_right(8),
- );
+ ui_state.push_scissor(vec2(x - w, y - h), vec2(x + w, y + h), true);
- y = base_y * 150.0;
- for i in 0..10 {
- if i & 1 != 0 {
- y += ui_state.text_fmt(
- base_x * 100.0 - 5.0,
- y,
- FontFamily::RobotoRegular,
- 20.0,
- format_args!("tick: {:?}", tick_duration),
- );
- } else {
- y += ui_state.text_fmt(
- base_x * 100.0 - 5.0,
- y,
- FontFamily::RobotoRegular,
- 20.0,
- format_args!("draw: {:?}", draw_duration),
+ let mut y = 8.0 * ui_state.scale;
+ for i in 0..224 {
+ if i & 1 == 0 {
+ ui_state.push_fullscreen_scissor();
+ }
+ let vertical_advance = ui_state.text_fmt(
+ 5.0,
+ y,
+ FontFamily::NotoSansJapanese,
+ 16.0 + s * 8.0,
+ format_args!(
+ "お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████お握り The Quick Brown Fox Jumped Over The Lazy Dog. ████████"
+ ),
+ );
+ y += vertical_advance;
+ if i & 1 == 0 {
+ ui_state.pop_scissor();
+ }
+ }
+
+ ui_state.pop_scissor();
+
+ for i in 0..500 {
+ let (s, c) = sin_cos_pi_f32(game_state.time * 0.1 + i as f32 * 0.01);
+
+ let x = width / 2.0 + w * s;
+ let y = height / 2.0 + w * c;
+ ui_state.rect(
+ x - 200.0,
+ y - 200.0,
+ 400.0,
+ 400.0,
+ 100.0,
+ [100.0, 50.0, 25.0, 0.0],
+ 0x33333333,
+ microshades::BLUE_RGBA8[4].rotate_right(8),
);
}
+
+ let x = 10.0 * ui_state.scale;
+ let mut y = 20.0 * ui_state.scale;
+ for i in 0..10 {
+ if i & 1 != 0 {
+ y += ui_state.text_fmt(
+ x,
+ y,
+ FontFamily::RobotoRegular,
+ 20.0,
+ format_args!("this tick: {:?}", tick_duration),
+ );
+ } else {
+ y += ui_state.text_fmt(
+ x,
+ y,
+ FontFamily::NotoSansJapanese,
+ 20.0,
+ format_args!("last draw: {:?}", draw_duration),
+ );
+ }
+ }
}
draw_state.draw(