From: Josh Simmons Date: Sun, 17 Nov 2024 19:48:07 +0000 (+0100) Subject: shark: Add support for clipping X-Git-Url: https://git.nega.tv//gitweb.cgi?a=commitdiff_plain;h=be33df15daa3e86d514aa77103730b21ec9a654a;p=josh%2Fnarcissus shark: Add support for clipping --- diff --git a/engine/narcissus-gpu/src/backend/vulkan/mod.rs b/engine/narcissus-gpu/src/backend/vulkan/mod.rs index d0408ce..2d60c00 100644 --- a/engine/narcissus-gpu/src/backend/vulkan/mod.rs +++ b/engine/narcissus-gpu/src/backend/vulkan/mod.rs @@ -1200,6 +1200,8 @@ impl Device for VulkanDevice { 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, @@ -1217,6 +1219,7 @@ impl Device for VulkanDevice { address_mode_w: address_mode, compare_enable, compare_op, + unnormalized_coordinates, ..default() }, None, diff --git a/engine/narcissus-gpu/src/lib.rs b/engine/narcissus-gpu/src/lib.rs index 7cab81b..a2b2cab 100644 --- a/engine/narcissus-gpu/src/lib.rs +++ b/engine/narcissus-gpu/src/lib.rs @@ -332,6 +332,7 @@ pub struct SamplerDesc { pub mip_lod_bias: f32, pub min_lod: f32, pub max_lod: f32, + pub unnormalized_coordinates: bool, } #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/title/shark-shaders/shaders/basic.frag b/title/shark-shaders/shaders/basic.frag index 1693e95..23b4b03 100644 --- a/title/shark-shaders/shaders/basic.frag +++ b/title/shark-shaders/shaders/basic.frag @@ -1,7 +1,8 @@ #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; @@ -9,6 +10,6 @@ layout(location = 0) out vec4 out_color; 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); } diff --git a/title/shark-shaders/shaders/compute_bindings.h b/title/shark-shaders/shaders/bindings_compute.h similarity index 74% rename from title/shark-shaders/shaders/compute_bindings.h rename to title/shark-shaders/shaders/bindings_compute.h index dc694d5..5792348 100644 --- a/title/shark-shaders/shaders/compute_bindings.h +++ b/title/shark-shaders/shaders/bindings_compute.h @@ -1,7 +1,11 @@ #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; diff --git a/title/shark-shaders/shaders/bindings_graphics.h b/title/shark-shaders/shaders/bindings_graphics.h new file mode 100644 index 0000000..b9fb83c --- /dev/null +++ b/title/shark-shaders/shaders/bindings_graphics.h @@ -0,0 +1,11 @@ +#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 diff --git a/title/shark-shaders/shaders/composite.comp b/title/shark-shaders/shaders/composite.comp index 26be838..8c153c8 100644 --- a/title/shark-shaders/shaders/composite.comp +++ b/title/shark-shaders/shaders/composite.comp @@ -6,7 +6,7 @@ #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) { @@ -21,7 +21,7 @@ vec3 tony_mc_mapface(vec3 stimulus) { 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 { @@ -39,8 +39,8 @@ 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 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; diff --git a/title/shark-shaders/shaders/draw_2d.h b/title/shark-shaders/shaders/draw_2d.h index 37af77f..bcef539 100644 --- a/title/shark-shaders/shaders/draw_2d.h +++ b/title/shark-shaders/shaders/draw_2d.h @@ -7,8 +7,8 @@ const uint DRAW_2D_CMD_RECT = 0; const uint DRAW_2D_CMD_GLYPH = 1; struct Tile { - uint min_index; - uint max_index; + uint index_min; + uint index_max; }; struct Glyph { @@ -19,14 +19,19 @@ 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; @@ -34,15 +39,16 @@ struct Draw2dCmdRect { 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 @@ -50,13 +56,23 @@ Draw2dCmdRect decode_rect(Draw2dCmd cmd) { 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 diff --git a/title/shark-shaders/shaders/draw_2d_bin_0_clear.comp b/title/shark-shaders/shaders/draw_2d_bin_0_clear.comp index 9f3501b..f5fb9b9 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_0_clear.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_0_clear.comp @@ -13,13 +13,13 @@ #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; diff --git a/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp b/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp index ad4ad57..f3cbef0 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_1_scatter.comp @@ -13,20 +13,20 @@ #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; @@ -43,12 +43,13 @@ void main() { 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); @@ -56,13 +57,13 @@ void main() { 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; @@ -72,27 +73,29 @@ void main() { } } - // 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()) { @@ -103,8 +106,8 @@ void main() { 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; @@ -115,8 +118,8 @@ void main() { } } } 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; @@ -127,19 +130,19 @@ void main() { 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; diff --git a/title/shark-shaders/shaders/draw_2d_bin_2_sort.comp b/title/shark-shaders/shaders/draw_2d_bin_2_sort.comp index 5a99c22..3df9805 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_2_sort.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_2_sort.comp @@ -12,8 +12,6 @@ #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" @@ -22,15 +20,15 @@ layout(buffer_reference, std430, buffer_reference_align = 4) buffer VkDispatchIn 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; diff --git a/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp b/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp index 43fc0d9..46427d4 100644 --- a/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp +++ b/title/shark-shaders/shaders/draw_2d_bin_3_resolve.comp @@ -13,22 +13,20 @@ #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; @@ -37,13 +35,13 @@ void main() { 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; } @@ -61,7 +59,7 @@ void main() { } 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; @@ -86,37 +84,52 @@ void main() { 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; @@ -133,6 +146,6 @@ void main() { } } - 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; } diff --git a/title/shark-shaders/shaders/draw_2d_rasterize.comp b/title/shark-shaders/shaders/draw_2d_rasterize.comp index 4ba591c..3e9c1c4 100644 --- a/title/shark-shaders/shaders/draw_2d_rasterize.comp +++ b/title/shark-shaders/shaders/draw_2d_rasterize.comp @@ -10,24 +10,24 @@ #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 @@ -51,10 +51,10 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 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; @@ -90,23 +90,32 @@ void main() { 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)))) { @@ -119,21 +128,29 @@ void main() { 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? diff --git a/title/shark-shaders/src/pipelines.rs b/title/shark-shaders/src/pipelines.rs index b3c314b..4304d6e 100644 --- a/title/shark-shaders/src/pipelines.rs +++ b/title/shark-shaders/src/pipelines.rs @@ -38,24 +38,26 @@ const _: () = assert!(std::mem::size_of::() == 32); #[derive(Clone, Copy)] struct CmdGlyph { packed: u32, + index: u32, position: Vec2, color: u32, } -const _: () = assert!(std::mem::size_of::() == 16); +const _: () = assert!(std::mem::size_of::() == 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, @@ -67,11 +69,17 @@ const _: () = assert!(std::mem::size_of::() == 32); 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, }, @@ -80,8 +88,9 @@ impl Draw2dCmd { #[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, @@ -89,9 +98,11 @@ impl Draw2dCmd { ) -> 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, @@ -100,8 +111,15 @@ impl Draw2dCmd { } } +#[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 { @@ -113,8 +131,23 @@ 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, + } } } @@ -149,8 +182,6 @@ pub struct Draw2dClearConstants<'a> { #[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, @@ -158,6 +189,7 @@ pub struct Draw2dScatterConstants<'a> { 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>, } @@ -172,15 +204,11 @@ pub struct Draw2dSortConstants<'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>, @@ -189,14 +217,11 @@ pub struct Draw2dResolveConstants<'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>, @@ -266,7 +291,7 @@ pub struct Pipelines { 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 diff --git a/title/shark/src/main.rs b/title/shark/src/main.rs index 71bda09..b97833c 100644 --- a/title/shark/src/main.rs +++ b/title/shark/src/main.rs @@ -3,12 +3,12 @@ use std::ops::Index; 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, }; @@ -450,10 +450,15 @@ struct UiState<'a> { fonts: &'a Fonts<'a>, glyph_cache: GlyphCache<'a, Fonts<'a>>, + width: f32, + height: f32, scale: f32, tmp_string: String, + scissors: Vec, + scissor_stack: Vec, + draw_cmds: Vec, } @@ -464,33 +469,85 @@ impl<'a> UiState<'a> { 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, @@ -506,6 +563,8 @@ impl<'a> UiState<'a> { 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); @@ -539,6 +598,7 @@ impl<'a> UiState<'a> { x += advance * scale; self.draw_cmds.push(Draw2dCmd::glyph( + scissor_index, touched_glyph_index, microshades::GRAY_RGBA8[4].rotate_right(8), vec2(x, y), @@ -1319,7 +1379,13 @@ impl<'gpu> DrawState<'gpu> { ); 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, @@ -1365,6 +1431,7 @@ impl<'gpu> DrawState<'gpu> { ); 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 = @@ -1402,13 +1469,12 @@ impl<'gpu> DrawState<'gpu> { 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, }, @@ -1541,13 +1607,10 @@ impl<'gpu> DrawState<'gpu> { 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, @@ -1577,13 +1640,10 @@ impl<'gpu> DrawState<'gpu> { 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, @@ -1813,83 +1873,94 @@ pub fn main() { 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(