Added full clay border support to draw (#28)
Co-authored-by: Zachary Levy <zachary@sunforge.is> Reviewed-on: #28
This commit was merged in pull request #28.
This commit is contained in:
+4
-407
@@ -67,12 +67,9 @@ import "base:runtime"
|
||||
import "core:c"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import sdl "vendor:sdl3"
|
||||
import sdl_ttf "vendor:sdl3/ttf"
|
||||
|
||||
import clay "../vendor/clay"
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Shader format ------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -171,6 +168,7 @@ Global :: struct {
|
||||
|
||||
// -- Clay (once per frame in prepare_clay_batch) --
|
||||
clay_memory: [^]u8, // Raw memory block backing Clay's internal arena.
|
||||
clay_merge_open_stack: [dynamic]Clay_Merge_Candidate, // Pending Rectangle/Image primitives waiting for a matching Border to merge with.
|
||||
|
||||
// -- Text (occasional — font registration and text cache lookups) --
|
||||
text_cache: Text_Cache, // Font registry, SDL_ttf engine, and cached TTF_Text objects.
|
||||
@@ -266,11 +264,6 @@ Brush :: union {
|
||||
Texture_Fill,
|
||||
}
|
||||
|
||||
// Convert clay.Color ([4]c.float in 0–255 range) to Color.
|
||||
color_from_clay :: #force_inline proc(clay_color: clay.Color) -> Color {
|
||||
return Color{u8(clay_color[0]), u8(clay_color[1]), u8(clay_color[2]), u8(clay_color[3])}
|
||||
}
|
||||
|
||||
// Convert Color to [4]f32 in 0.0–1.0 range. Useful for SDL interop (e.g. clear color).
|
||||
color_to_f32 :: proc(color: Color) -> [4]f32 {
|
||||
INV :: 1.0 / 255.0
|
||||
@@ -346,8 +339,6 @@ init :: proc(
|
||||
) -> (
|
||||
ok: bool,
|
||||
) {
|
||||
min_memory_size: c.size_t = cast(c.size_t)clay.MinMemorySize()
|
||||
|
||||
core, core_ok := create_core_2d(device, window)
|
||||
if !core_ok {
|
||||
return false
|
||||
@@ -394,7 +385,6 @@ init :: proc(
|
||||
pending_text_releases = make([dynamic]^sdl_ttf.Text, 0, 16, allocator = allocator),
|
||||
odin_context = odin_context,
|
||||
dpi_scaling = sdl.GetWindowDisplayScale(window),
|
||||
clay_memory = make([^]u8, min_memory_size, allocator = allocator),
|
||||
core_2d = core,
|
||||
backdrop = backdrop,
|
||||
text_cache = text_cache,
|
||||
@@ -403,12 +393,7 @@ init :: proc(
|
||||
// Reserve slot 0 for INVALID_TEXTURE
|
||||
append(&GLOB.texture_slots, Texture_Slot{})
|
||||
log.debug("Window DPI scaling:", GLOB.dpi_scaling)
|
||||
arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, GLOB.clay_memory)
|
||||
window_width, window_height: c.int
|
||||
sdl.GetWindowSize(window, &window_width, &window_height)
|
||||
|
||||
clay.Initialize(arena, {f32(window_width), f32(window_height)}, {handler = clay_error_handler})
|
||||
clay.SetMeasureTextFunction(measure_text_clay, nil)
|
||||
init_clay(window, allocator)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -447,7 +432,7 @@ destroy :: proc(device: ^sdl.GPUDevice, allocator := context.allocator) {
|
||||
delete(GLOB.tmp_gaussian_blur_primitives)
|
||||
for ttf_text in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(ttf_text)
|
||||
delete(GLOB.tmp_uncached_text)
|
||||
free(GLOB.clay_memory, allocator)
|
||||
destroy_clay(allocator)
|
||||
process_pending_texture_releases()
|
||||
destroy_all_textures()
|
||||
destroy_sampler_pool()
|
||||
@@ -467,7 +452,6 @@ clear_global :: proc() {
|
||||
clear(&GLOB.pending_text_releases)
|
||||
|
||||
GLOB.curr_layer_index = 0
|
||||
GLOB.clay_z_index = 0
|
||||
GLOB.cleared = false
|
||||
GLOB.open_backdrop_layer = nil
|
||||
// Destroy uncached TTF_Text objects from the previous frame (after end() has submitted draw data)
|
||||
@@ -482,6 +466,7 @@ clear_global :: proc() {
|
||||
clear(&GLOB.tmp_primitives)
|
||||
clear(&GLOB.tmp_sub_batches)
|
||||
clear(&GLOB.tmp_gaussian_blur_primitives)
|
||||
clear_clay_per_frame()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -725,394 +710,6 @@ append_or_extend_sub_batch :: proc(
|
||||
layer.sub_batch_len += 1
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Clay ------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@(private = "file")
|
||||
clay_error_handler :: proc "c" (errorData: clay.ErrorData) {
|
||||
context = GLOB.odin_context
|
||||
log.error("Clay error:", errorData.errorType, errorData.errorText)
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
measure_text_clay :: proc "c" (
|
||||
text: clay.StringSlice,
|
||||
config: ^clay.TextElementConfig,
|
||||
user_data: rawptr,
|
||||
) -> clay.Dimensions {
|
||||
context = GLOB.odin_context
|
||||
text := string(text.chars[:text.length])
|
||||
c_text := strings.clone_to_cstring(text, context.temp_allocator)
|
||||
defer delete(c_text, context.temp_allocator)
|
||||
width, height: c.int
|
||||
if !sdl_ttf.GetStringSize(get_font(config.fontId, config.fontSize), c_text, 0, &width, &height) {
|
||||
log.panicf("Failed to measure text: %s", sdl.GetError())
|
||||
}
|
||||
|
||||
return clay.Dimensions{width = f32(width) / GLOB.dpi_scaling, height = f32(height) / GLOB.dpi_scaling}
|
||||
}
|
||||
|
||||
// Called for each Clay `RenderCommandType.Custom` render command that
|
||||
// `prepare_clay_batch` encounters and which is NOT a levlib-managed variant
|
||||
// (e.g. `Backdrop_Marker`).
|
||||
//
|
||||
// - `layer` is the layer the command belongs to (post-z-index promotion).
|
||||
// - `bounds` is already translated into the active layer's coordinate system
|
||||
// and pre-DPI, matching what the built-in shape procs expect.
|
||||
// - `render_data` is Clay's `CustomRenderData` for the element, exposing
|
||||
// `backgroundColor` and `cornerRadius`. Its `customData` field has been
|
||||
// unwrapped from the `Clay_Custom` envelope: it points at the user's own
|
||||
// data (the value the user wrote into the `rawptr` variant), not at the
|
||||
// `Clay_Custom` itself. If the union was zero-init (no variant set) or
|
||||
// `customData` was originally nil, the callback receives nil.
|
||||
//
|
||||
// The callback must not call `new_layer` or `prepare_clay_batch`.
|
||||
Custom_Draw :: #type proc(layer: ^Layer, bounds: Rectangle, render_data: clay.CustomRenderData)
|
||||
|
||||
ClayBatch :: struct {
|
||||
bounds: Rectangle,
|
||||
cmds: clay.ClayArray(clay.RenderCommand),
|
||||
}
|
||||
|
||||
// Discriminated sum of everything `clay.CustomElementConfig.customData` is allowed to point
|
||||
// at. levlib-defined variants (currently just `Backdrop_Marker`) are recognized by
|
||||
// `prepare_clay_batch` and routed to the appropriate internal path; the `rawptr` variant is
|
||||
// the escape hatch for user-defined custom drawing — `prepare_clay_batch` unwraps it before
|
||||
// invoking `custom_draw` so the callback sees the user's pointer in `render_data.customData`
|
||||
// exactly as if no wrapper were involved.
|
||||
//
|
||||
// Contract: `customData`, when non-nil, MUST point at storage holding a `Clay_Custom`
|
||||
// value. The user owns that storage; its lifetime must span the Clay layout call and the
|
||||
// matching `prepare_clay_batch` call. Pointing `customData` at a bare user struct violates
|
||||
// the contract — the dispatcher will read its first bytes as a union tag and either route
|
||||
// the draw incorrectly or panic on type assertion. There is no recovery path; this is a
|
||||
// strict-discipline API by design.
|
||||
//
|
||||
// Construction notes (Odin implicit-conversion rules):
|
||||
// - Backdrop variant: `bd: Clay_Custom = Backdrop_Marker{...}` works directly.
|
||||
// Variant-to-union conversion is implicit.
|
||||
// - User pointer: `up: Clay_Custom = rawptr(&my_struct)` — the explicit `rawptr(...)` is
|
||||
// required because Odin does not chain `^T -> rawptr -> Clay_Custom` implicitly. A bare
|
||||
// `up: Clay_Custom = &my_struct` is a compile error.
|
||||
Clay_Custom :: union {
|
||||
Backdrop_Marker,
|
||||
rawptr,
|
||||
}
|
||||
|
||||
// Per-primitive parameters for a backdrop blur dispatched through the Clay integration.
|
||||
// Embedded as a `Clay_Custom` variant; `prepare_clay_batch` walks the command stream,
|
||||
// opens/closes a backdrop scope around contiguous backdrop runs, and feeds these to
|
||||
// `backdrop_blur` via `dispatch_clay_backdrop`. The discriminant is the union tag — no
|
||||
// in-band magic field needed (compiler-enforced).
|
||||
Backdrop_Marker :: struct {
|
||||
sigma: f32,
|
||||
tint: Color,
|
||||
radii: Rectangle_Radii,
|
||||
feather_ppx: f32,
|
||||
}
|
||||
|
||||
// Returns true if this Clay render command represents a backdrop primitive — i.e. its
|
||||
// `customData` points at a `Clay_Custom` whose active variant is `Backdrop_Marker`.
|
||||
is_clay_backdrop :: proc(cmd: ^clay.RenderCommand) -> bool {
|
||||
if cmd.commandType != .Custom do return false
|
||||
p := cmd.renderData.custom.customData
|
||||
if p == nil do return false
|
||||
_, ok := (^Clay_Custom)(p).(Backdrop_Marker)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Dispatch a single non-backdrop Clay render command to the appropriate `draw` primitive.
|
||||
// Extracted from the main `prepare_clay_batch` walk so that the deferred-buffer flush path
|
||||
// can replay commands accumulated during an open backdrop scope without duplicating the
|
||||
// per-command lowering code.
|
||||
//INTERNAL
|
||||
dispatch_clay_command :: proc(
|
||||
layer: ^Layer,
|
||||
render_command: ^clay.RenderCommand,
|
||||
custom_draw: Custom_Draw,
|
||||
temp_allocator: runtime.Allocator,
|
||||
) {
|
||||
// Translate bounding box of the primitive by the layer position
|
||||
bounds := Rectangle {
|
||||
x = render_command.boundingBox.x + layer.bounds.x,
|
||||
y = render_command.boundingBox.y + layer.bounds.y,
|
||||
width = render_command.boundingBox.width,
|
||||
height = render_command.boundingBox.height,
|
||||
}
|
||||
|
||||
switch render_command.commandType {
|
||||
case clay.RenderCommandType.None:
|
||||
log.errorf(
|
||||
"Received render command with type None. This generally means we're in some kind of fucked up state.",
|
||||
)
|
||||
case clay.RenderCommandType.Text:
|
||||
render_data := render_command.renderData.text
|
||||
txt := string(render_data.stringContents.chars[:render_data.stringContents.length])
|
||||
c_text := strings.clone_to_cstring(txt, temp_allocator)
|
||||
defer delete(c_text, temp_allocator)
|
||||
// Clay render-command IDs are derived via Clay's internal HashNumber (Jenkins-family)
|
||||
// and namespaced with .Clay so they can never collide with user-provided custom text IDs.
|
||||
sdl_text := cache_get_or_update(
|
||||
Cache_Key{render_command.id, .Clay},
|
||||
c_text,
|
||||
get_font(render_data.fontId, render_data.fontSize),
|
||||
)
|
||||
prepare_text(layer, Text{sdl_text, {bounds.x, bounds.y}, color_from_clay(render_data.textColor)})
|
||||
case clay.RenderCommandType.Image:
|
||||
// Any texture
|
||||
render_data := render_command.renderData.image
|
||||
if render_data.imageData == nil do return
|
||||
img_data := (^Clay_Image_Data)(render_data.imageData)^
|
||||
cr := render_data.cornerRadius
|
||||
radii := Rectangle_Radii {
|
||||
top_left = cr.topLeft,
|
||||
top_right = cr.topRight,
|
||||
bottom_right = cr.bottomRight,
|
||||
bottom_left = cr.bottomLeft,
|
||||
}
|
||||
|
||||
// Background color behind the image (Clay allows it)
|
||||
bg := color_from_clay(render_data.backgroundColor)
|
||||
if bg.a > 0 {
|
||||
rectangle(layer, bounds, bg, radii = radii)
|
||||
}
|
||||
|
||||
// Compute fit UVs
|
||||
uv, sampler, inner := fit_params(img_data.fit, bounds, img_data.texture_id)
|
||||
|
||||
// Draw the image
|
||||
rectangle(
|
||||
layer,
|
||||
inner,
|
||||
Texture_Fill{id = img_data.texture_id, tint = img_data.tint, uv_rect = uv, sampler = sampler},
|
||||
radii = radii,
|
||||
)
|
||||
case clay.RenderCommandType.ScissorStart:
|
||||
if bounds.width == 0 || bounds.height == 0 do return
|
||||
|
||||
curr_scissor := &GLOB.scissors[layer.scissor_start + layer.scissor_len - 1]
|
||||
|
||||
if curr_scissor.sub_batch_len != 0 {
|
||||
// Scissor has some content, need to make a new scissor
|
||||
new := Scissor {
|
||||
sub_batch_start = curr_scissor.sub_batch_start + curr_scissor.sub_batch_len,
|
||||
bounds = sdl.Rect {
|
||||
c.int(bounds.x * GLOB.dpi_scaling),
|
||||
c.int(bounds.y * GLOB.dpi_scaling),
|
||||
c.int(bounds.width * GLOB.dpi_scaling),
|
||||
c.int(bounds.height * GLOB.dpi_scaling),
|
||||
},
|
||||
}
|
||||
append(&GLOB.scissors, new)
|
||||
layer.scissor_len += 1
|
||||
} else {
|
||||
curr_scissor.bounds = sdl.Rect {
|
||||
c.int(bounds.x * GLOB.dpi_scaling),
|
||||
c.int(bounds.y * GLOB.dpi_scaling),
|
||||
c.int(bounds.width * GLOB.dpi_scaling),
|
||||
c.int(bounds.height * GLOB.dpi_scaling),
|
||||
}
|
||||
}
|
||||
case clay.RenderCommandType.ScissorEnd:
|
||||
case clay.RenderCommandType.OverlayColorStart, clay.RenderCommandType.OverlayColorEnd:
|
||||
unimplemented("Clay overlays not supported yet...")
|
||||
case clay.RenderCommandType.Rectangle:
|
||||
render_data := render_command.renderData.rectangle
|
||||
cr := render_data.cornerRadius
|
||||
color := color_from_clay(render_data.backgroundColor)
|
||||
radii := Rectangle_Radii {
|
||||
top_left = cr.topLeft,
|
||||
top_right = cr.topRight,
|
||||
bottom_right = cr.bottomRight,
|
||||
bottom_left = cr.bottomLeft,
|
||||
}
|
||||
|
||||
rectangle(layer, bounds, color, radii = radii)
|
||||
case clay.RenderCommandType.Border:
|
||||
render_data := render_command.renderData.border
|
||||
cr := render_data.cornerRadius
|
||||
color := color_from_clay(render_data.color)
|
||||
thickness := f32(render_data.width.top)
|
||||
radii := Rectangle_Radii {
|
||||
top_left = cr.topLeft,
|
||||
top_right = cr.topRight,
|
||||
bottom_right = cr.bottomRight,
|
||||
bottom_left = cr.bottomLeft,
|
||||
}
|
||||
|
||||
rectangle(layer, bounds, BLANK, outline_color = color, outline_width = thickness, radii = radii)
|
||||
case clay.RenderCommandType.Custom:
|
||||
// Copy the CustomRenderData by value so we can patch its `customData` field for the
|
||||
// user callback without mutating Clay-owned memory. After unwrapping, the callback
|
||||
// sees its own pointer in `render_data.customData`, identical to what it would see
|
||||
// if `Clay_Custom` did not exist as an intermediary.
|
||||
patched := render_command.renderData.custom
|
||||
// Default to nil so a zero-init `Clay_Custom` (no variant set) and an originally-nil
|
||||
// `customData` both surface to the callback as `customData = nil`.
|
||||
patched.customData = nil
|
||||
if custom_data_pointer := render_command.renderData.custom.customData; custom_data_pointer != nil {
|
||||
switch custom_value in (^Clay_Custom)(custom_data_pointer)^ {
|
||||
case Backdrop_Marker: // The walker pre-filters backdrops into `dispatch_clay_backdrop` and never feeds
|
||||
// them here; reaching this branch means either the walker logic is broken or the
|
||||
// `Clay_Custom` variant tag mutated between the walker's `is_clay_backdrop` check
|
||||
// and this re-check (heap corruption / lifetime bug in user-managed customData
|
||||
// memory). Both are renderer-level bugs that warrant a hard failure rather than a
|
||||
// silently-dropped panel.
|
||||
log.panicf(
|
||||
"backdrop marker reached dispatch_clay_command; either the prepare_clay_batch walker is misrouting commands or the customData pointee at %p was mutated mid-frame",
|
||||
render_command.renderData.custom.customData,
|
||||
)
|
||||
case rawptr: patched.customData = custom_value
|
||||
}
|
||||
}
|
||||
if custom_draw != nil {
|
||||
custom_draw(layer, bounds, patched)
|
||||
} else if patched.customData != nil {
|
||||
log.panicf(
|
||||
"Received clay render command of type custom with non-nil user data but no custom_draw proc provided.",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch a single backdrop Clay render command to `backdrop_blur` on the active layer.
|
||||
// Caller guarantees:
|
||||
// - a backdrop scope is open on `layer` so the underlying `append_or_extend_sub_batch`
|
||||
// contract assertion is satisfied;
|
||||
// - the command's `customData` points at a `Clay_Custom` whose active variant is
|
||||
// `Backdrop_Marker` (the walker has already verified this via `is_clay_backdrop`).
|
||||
//INTERNAL
|
||||
dispatch_clay_backdrop :: proc(layer: ^Layer, cmd: ^clay.RenderCommand) {
|
||||
bounds := Rectangle {
|
||||
x = cmd.boundingBox.x + layer.bounds.x,
|
||||
y = cmd.boundingBox.y + layer.bounds.y,
|
||||
width = cmd.boundingBox.width,
|
||||
height = cmd.boundingBox.height,
|
||||
}
|
||||
// Type-asserting form (no `, ok`): panics loudly if the variant tag changed since
|
||||
// `is_clay_backdrop`, which is the desired tripwire for a heap-corruption bug in
|
||||
// user-managed customData.
|
||||
marker := (^Clay_Custom)(cmd.renderData.custom.customData).(Backdrop_Marker)
|
||||
backdrop_blur(
|
||||
layer,
|
||||
bounds,
|
||||
gaussian_sigma = marker.sigma,
|
||||
tint = marker.tint,
|
||||
radii = marker.radii,
|
||||
feather_ppx = marker.feather_ppx,
|
||||
)
|
||||
}
|
||||
|
||||
// Close the in-flight backdrop scope (if open) and replay every command accumulated in the
|
||||
// deferred index buffer. Ordering: end_backdrop first so deferred non-backdrop draws land
|
||||
// at submission position relative to the bracket they followed (the bracket is now closed,
|
||||
// so these draws render after it). Used at every zIndex transition and at end of stream.
|
||||
//INTERNAL
|
||||
flush_deferred_and_close_backdrop_scope :: proc(
|
||||
layer: ^Layer,
|
||||
batch: ^ClayBatch,
|
||||
deferred_indices: ^[dynamic]i32,
|
||||
backdrop_scope_open: ^bool,
|
||||
custom_draw: Custom_Draw,
|
||||
temp_allocator: runtime.Allocator,
|
||||
) {
|
||||
if backdrop_scope_open^ {
|
||||
end_backdrop(layer)
|
||||
backdrop_scope_open^ = false
|
||||
}
|
||||
for index in deferred_indices^ {
|
||||
cmd := clay.RenderCommandArray_Get(&batch.cmds, index)
|
||||
dispatch_clay_command(layer, cmd, custom_draw, temp_allocator)
|
||||
}
|
||||
clear(deferred_indices)
|
||||
}
|
||||
|
||||
// Process Clay render commands into shape, text, and backdrop primitives.
|
||||
//
|
||||
// Single-walk dispatcher with a deferred buffer. The walk does three things per command:
|
||||
// 1. zIndex transitions: close the in-flight scope, flush any deferred non-backdrop
|
||||
// commands into the current layer, then open a new layer seeded with `base_layer.bounds`
|
||||
// (NOT the bumping element's bounds — Clay's floating elements with `clipTo = .None`
|
||||
// should not be over-clipped, and `clipTo = .AttachedParent` floating elements get a
|
||||
// Clay-emitted ScissorStart immediately afterward that narrows correctly).
|
||||
// 2. Backdrop commands: open a scope on first encounter (extending it on subsequent ones),
|
||||
// then dispatch the backdrop_blur call.
|
||||
// 3. Non-backdrop commands during an open scope: append to the deferred buffer for replay
|
||||
// after the scope closes. The buffer holds command indices, not pointers, so it stays
|
||||
// valid even if the underlying ClayArray reallocates.
|
||||
// At end of stream, flush whatever remains.
|
||||
prepare_clay_batch :: proc(
|
||||
base_layer: ^Layer,
|
||||
batch: ^ClayBatch,
|
||||
mouse_wheel_delta: [2]f32,
|
||||
frame_time: f32 = 0,
|
||||
custom_draw: Custom_Draw = nil,
|
||||
temp_allocator := context.temp_allocator,
|
||||
) {
|
||||
mouse_pos: [2]f32
|
||||
mouse_flags := sdl.GetMouseState(&mouse_pos.x, &mouse_pos.y)
|
||||
|
||||
// Update clay internals
|
||||
clay.SetPointerState(
|
||||
clay.Vector2{mouse_pos.x - base_layer.bounds.x, mouse_pos.y - base_layer.bounds.y},
|
||||
.LEFT in mouse_flags,
|
||||
)
|
||||
clay.UpdateScrollContainers(true, mouse_wheel_delta, frame_time)
|
||||
|
||||
layer := base_layer
|
||||
command_count := int(batch.cmds.length)
|
||||
deferred_indices := make([dynamic]i32, 0, 16, temp_allocator)
|
||||
backdrop_scope_open := false
|
||||
// Seed from GLOB.clay_z_index so multi-batch frames preserve the original semantics: a
|
||||
// later call to `prepare_clay_batch` doesn't re-trigger layer splits for zIndex values
|
||||
// the previous batch already saw.
|
||||
previous_z_index := GLOB.clay_z_index
|
||||
|
||||
for i in 0 ..< command_count {
|
||||
cmd := clay.RenderCommandArray_Get(&batch.cmds, i32(i))
|
||||
|
||||
// zIndex transition: close out current stratum, create new layer, continue.
|
||||
if cmd.zIndex > previous_z_index {
|
||||
log.debug("Higher zIndex found, creating new layer & setting z_index to", cmd.zIndex)
|
||||
flush_deferred_and_close_backdrop_scope(
|
||||
layer,
|
||||
batch,
|
||||
&deferred_indices,
|
||||
&backdrop_scope_open,
|
||||
custom_draw,
|
||||
temp_allocator,
|
||||
)
|
||||
layer = new_layer(layer, base_layer.bounds)
|
||||
previous_z_index = cmd.zIndex
|
||||
// Keep GLOB.clay_z_index in sync for any external readers (debug tooling, etc.).
|
||||
GLOB.clay_z_index = cmd.zIndex
|
||||
}
|
||||
|
||||
if is_clay_backdrop(cmd) {
|
||||
if !backdrop_scope_open {
|
||||
begin_backdrop(layer)
|
||||
backdrop_scope_open = true
|
||||
}
|
||||
dispatch_clay_backdrop(layer, cmd)
|
||||
} else if backdrop_scope_open {
|
||||
append(&deferred_indices, i32(i))
|
||||
} else {
|
||||
dispatch_clay_command(layer, cmd, custom_draw, temp_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
// End-of-stream: flush whatever remains.
|
||||
flush_deferred_and_close_backdrop_scope(
|
||||
layer,
|
||||
batch,
|
||||
&deferred_indices,
|
||||
&backdrop_scope_open,
|
||||
custom_draw,
|
||||
temp_allocator,
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// ----- Buffer ------------
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user