Backdrop scope implementation (#25)
Co-authored-by: Zachary Levy <zachary@sunforge.is> Reviewed-on: #25
This commit was merged in pull request #25.
This commit is contained in:
+333
-121
@@ -88,6 +88,10 @@ Global :: struct {
|
||||
clay_z_index: i16, // Tracks z-index for layer splitting during Clay batch processing.
|
||||
cleared: bool, // Whether the render target has been cleared this frame.
|
||||
|
||||
// Per-frame: which layer (if any) currently has an open begin_backdrop scope.
|
||||
// Reset to nil at frame start. end() panics if non-nil at frame end.
|
||||
open_backdrop_layer: ^Layer,
|
||||
|
||||
// -- Subsystems (accessed every draw_layer call) --
|
||||
core_2d: Core_2D, // The unified 2D GPU pipeline (shaders, buffers, samplers).
|
||||
backdrop: Backdrop, // Frosted-glass backdrop blur subsystem (downsample + blur PSOs, working textures).
|
||||
@@ -423,6 +427,7 @@ clear_global :: proc() {
|
||||
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)
|
||||
for ttf_text in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(ttf_text)
|
||||
clear(&GLOB.tmp_uncached_text)
|
||||
@@ -467,6 +472,10 @@ begin :: proc(bounds: Rectangle) -> ^Layer {
|
||||
|
||||
// Creates a new layer
|
||||
new_layer :: proc(prev_layer: ^Layer, bounds: Rectangle) -> ^Layer {
|
||||
if GLOB.open_backdrop_layer != nil {
|
||||
log.panicf("new_layer called while backdrop scope is open on layer %p", GLOB.open_backdrop_layer)
|
||||
}
|
||||
|
||||
layer := Layer {
|
||||
bounds = bounds,
|
||||
sub_batch_start = prev_layer.sub_batch_start + prev_layer.sub_batch_len,
|
||||
@@ -490,6 +499,46 @@ new_layer :: proc(prev_layer: ^Layer, bounds: Rectangle) -> ^Layer {
|
||||
return &GLOB.layers[GLOB.curr_layer_index]
|
||||
}
|
||||
|
||||
// Open a backdrop scope on `layer`. All subsequent draws on `layer` until the matching
|
||||
// `end_backdrop` must be backdrop primitives (currently only `backdrop_blur`). Non-backdrop
|
||||
// draws inside a scope, or backdrop draws outside one, panic.
|
||||
//
|
||||
// Bracket scheduling: each scope produces one bracket at render time. Within the scope,
|
||||
// per-sigma sub-batch coalescing still applies (two contiguous backdrop_blur calls with
|
||||
// the same sigma share an instanced composite draw and a single H+V blur pass pair).
|
||||
//
|
||||
// Multiple begin/end pairs per layer are allowed: each pair is its own bracket, and
|
||||
// non-backdrop draws between pairs render in their submission position relative to the
|
||||
// brackets. Use this for layered frost effects.
|
||||
begin_backdrop :: proc(layer: ^Layer) {
|
||||
if GLOB.open_backdrop_layer != nil {
|
||||
log.panicf("begin_backdrop called while a scope is already open on layer %p", GLOB.open_backdrop_layer)
|
||||
}
|
||||
GLOB.open_backdrop_layer = layer
|
||||
}
|
||||
|
||||
// Close the backdrop scope opened by `begin_backdrop`. Must be called on the same layer that
|
||||
// the scope was opened on; the layer pointer mismatch is a hard error rather than a silent
|
||||
// recovery to surface integration bugs early.
|
||||
end_backdrop :: proc(layer: ^Layer) {
|
||||
if GLOB.open_backdrop_layer != layer {
|
||||
log.panicf("end_backdrop on wrong layer (open=%p, ended=%p)", GLOB.open_backdrop_layer, layer)
|
||||
}
|
||||
GLOB.open_backdrop_layer = nil
|
||||
}
|
||||
|
||||
// Convenience wrapper for the common case of a backdrop scope tied to a block. Use with
|
||||
// defer-style block scoping:
|
||||
//
|
||||
// {
|
||||
// draw.backdrop_scope(layer)
|
||||
// draw.backdrop_blur(layer, ...)
|
||||
// } // end_backdrop fires automatically
|
||||
@(deferred_in = end_backdrop)
|
||||
backdrop_scope :: #force_inline proc(layer: ^Layer) {
|
||||
begin_backdrop(layer)
|
||||
}
|
||||
|
||||
// Render primitives. clear_color is the background fill before any layers are drawn.
|
||||
end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = DFT_CLEAR_COLOR) {
|
||||
cmd_buffer := sdl.AcquireGPUCommandBuffer(device)
|
||||
@@ -497,6 +546,13 @@ end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = DF
|
||||
log.panicf("Failed to acquire GPU command buffer: %s", sdl.GetError())
|
||||
}
|
||||
|
||||
if GLOB.open_backdrop_layer != nil {
|
||||
log.panicf(
|
||||
"end() called with open backdrop scope on layer %p; missing end_backdrop",
|
||||
GLOB.open_backdrop_layer,
|
||||
)
|
||||
}
|
||||
|
||||
// Pre-scan: if any layer this frame has a backdrop sub-batch, route the entire frame to
|
||||
// source_texture so the bracket can sample the pre-bracket framebuffer without a mid-
|
||||
// frame texture copy. Frames without any backdrop hit the existing fast path and never
|
||||
@@ -591,6 +647,15 @@ append_or_extend_sub_batch :: proc(
|
||||
sampler: Sampler_Preset = DFT_SAMPLER,
|
||||
gaussian_sigma: f32 = 0,
|
||||
) {
|
||||
// Scope contract: backdrops only inside a scope, non-backdrops only outside.
|
||||
in_scope := GLOB.open_backdrop_layer == layer
|
||||
if kind == .Backdrop && !in_scope {
|
||||
log.panic("backdrop draw outside begin_backdrop / end_backdrop scope")
|
||||
}
|
||||
if kind != .Backdrop && in_scope {
|
||||
log.panicf("non-backdrop draw of kind %v inside backdrop scope on layer %p", kind, layer)
|
||||
}
|
||||
|
||||
if scissor.sub_batch_len > 0 {
|
||||
last := &GLOB.tmp_sub_batches[scissor.sub_batch_start + scissor.sub_batch_len - 1]
|
||||
if last.kind == kind &&
|
||||
@@ -664,7 +729,232 @@ ClayBatch :: struct {
|
||||
cmds: clay.ClayArray(clay.RenderCommand),
|
||||
}
|
||||
|
||||
// Process Clay render commands into shape and text primitives.
|
||||
// Magic-number-tagged struct that user app data points at via Clay's customData field.
|
||||
// `prepare_clay_batch` recognizes these and routes them through a backdrop scope automatically.
|
||||
// The user populates a `Backdrop_Marker`, points `clay.CustomElementConfig.customData` at it,
|
||||
// and the integration walks the command stream, opening/closing scopes around contiguous
|
||||
// backdrop runs. Magic-number sentinel chosen over a separate userData flag so the marker
|
||||
// type stays self-describing in core dumps and in any non-Odin debugger view of the heap.
|
||||
Backdrop_Marker :: struct {
|
||||
magic: u32,
|
||||
sigma: f32,
|
||||
tint: Color,
|
||||
radii: Rectangle_Radii,
|
||||
feather_px: f32,
|
||||
}
|
||||
|
||||
// 'BDPT' in big-endian ASCII. Picked for greppability and to be obviously non-zero in
|
||||
// uninitialized memory; user code that forgets to set the magic field gets routed through
|
||||
// the regular custom_draw path and surfaces as "my custom draw never fired," not as a
|
||||
// silent backdrop schedule.
|
||||
BACKDROP_MARKER_MAGIC :: u32(0x42445054)
|
||||
|
||||
// Returns true if this Clay render command represents a backdrop primitive.
|
||||
// Identified by a magic-number sentinel in the first 4 bytes of customData.
|
||||
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
|
||||
return (^Backdrop_Marker)(p).magic == BACKDROP_MARKER_MAGIC
|
||||
}
|
||||
|
||||
// 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.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: if is_clay_backdrop(render_command) {
|
||||
// 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
|
||||
// `customData` pointee 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,
|
||||
)
|
||||
} else if custom_draw != nil {
|
||||
custom_draw(layer, bounds, render_command.renderData.custom)
|
||||
} else {
|
||||
log.panicf("Received clay render command of type custom 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.
|
||||
//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,
|
||||
}
|
||||
marker := (^Backdrop_Marker)(cmd.renderData.custom.customData)
|
||||
backdrop_blur(
|
||||
layer,
|
||||
bounds,
|
||||
gaussian_sigma = marker.sigma,
|
||||
tint = marker.tint,
|
||||
radii = marker.radii,
|
||||
feather_px = marker.feather_px,
|
||||
)
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -684,134 +974,56 @@ prepare_clay_batch :: proc(
|
||||
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
|
||||
|
||||
// Parse render commands
|
||||
for i in 0 ..< int(batch.cmds.length) {
|
||||
render_command := clay.RenderCommandArray_Get(&batch.cmds, cast(i32)i)
|
||||
for i in 0 ..< command_count {
|
||||
cmd := clay.RenderCommandArray_Get(&batch.cmds, i32(i))
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
if render_command.zIndex > GLOB.clay_z_index {
|
||||
log.debug("Higher zIndex found, creating new layer & setting z_index to", render_command.zIndex)
|
||||
layer = new_layer(layer, bounds)
|
||||
// Update bounds to new layer offset
|
||||
bounds.x = render_command.boundingBox.x + layer.bounds.x
|
||||
bounds.y = render_command.boundingBox.y + layer.bounds.y
|
||||
GLOB.clay_z_index = render_command.zIndex
|
||||
}
|
||||
|
||||
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 continue
|
||||
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(
|
||||
// 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,
|
||||
inner,
|
||||
Texture_Fill{id = img_data.texture_id, tint = img_data.tint, uv_rect = uv, sampler = sampler},
|
||||
radii = radii,
|
||||
batch,
|
||||
&deferred_indices,
|
||||
&backdrop_scope_open,
|
||||
custom_draw,
|
||||
temp_allocator,
|
||||
)
|
||||
case clay.RenderCommandType.ScissorStart:
|
||||
if bounds.width == 0 || bounds.height == 0 do continue
|
||||
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
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
if is_clay_backdrop(cmd) {
|
||||
if !backdrop_scope_open {
|
||||
begin_backdrop(layer)
|
||||
backdrop_scope_open = true
|
||||
}
|
||||
case clay.RenderCommandType.ScissorEnd:
|
||||
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: if custom_draw != nil {
|
||||
custom_draw(layer, bounds, render_command.renderData.custom)
|
||||
} else {
|
||||
log.error("Received clay render command of type custom but no custom_draw proc provided.")
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user