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:
+53
-64
@@ -598,6 +598,15 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
||||
|
||||
//----- Layer dispatch ----------------------------------
|
||||
|
||||
// Walk the layer's sub-batches, alternating between non-backdrop runs (rendered to
|
||||
// `render_texture` via `render_layer_sub_batch_range`) and backdrop runs (each closed by a
|
||||
// `begin_backdrop`/`end_backdrop` scope at submission time, dispatched here as one bracket
|
||||
// per run via `run_backdrop_bracket`).
|
||||
//
|
||||
// Multiple brackets per layer are allowed: each `begin_backdrop`/`end_backdrop` pair maps to
|
||||
// one contiguous .Backdrop run in the sub-batch list, and non-backdrop draws between scopes
|
||||
// render in their submission position relative to the brackets. This is the explicit-scope
|
||||
// model that replaces the legacy single-bracket-per-layer scheduler.
|
||||
//INTERNAL
|
||||
draw_layer :: proc(
|
||||
device: ^sdl.GPUDevice,
|
||||
@@ -628,53 +637,44 @@ draw_layer :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
bracket_start_abs := find_first_backdrop_in_layer(layer)
|
||||
layer_end_abs := int(layer.sub_batch_start + layer.sub_batch_len)
|
||||
// Walk sub-batches, alternating non-backdrop runs (rendered to render_texture) and
|
||||
// backdrop runs (dispatched to run_backdrop_bracket). Each backdrop run corresponds to a
|
||||
// single user-visible begin_backdrop/end_backdrop scope at submission time.
|
||||
layer_start := int(layer.sub_batch_start)
|
||||
layer_end := layer_start + int(layer.sub_batch_len)
|
||||
i := layer_start
|
||||
for i < layer_end {
|
||||
// Find next non-backdrop run [run_start, run_end).
|
||||
run_start := i
|
||||
for i < layer_end && GLOB.tmp_sub_batches[i].kind != .Backdrop do i += 1
|
||||
run_end := i
|
||||
if run_end > run_start {
|
||||
render_layer_sub_batch_range(
|
||||
cmd_buffer,
|
||||
render_texture,
|
||||
swapchain_width,
|
||||
swapchain_height,
|
||||
clear_color,
|
||||
layer,
|
||||
run_start,
|
||||
run_end,
|
||||
)
|
||||
}
|
||||
|
||||
if bracket_start_abs < 0 {
|
||||
// Fast path: no backdrop in this layer; render the whole sub-batch range in one pass.
|
||||
render_layer_sub_batch_range(
|
||||
cmd_buffer,
|
||||
render_texture,
|
||||
swapchain_width,
|
||||
swapchain_height,
|
||||
clear_color,
|
||||
layer,
|
||||
int(layer.sub_batch_start),
|
||||
layer_end_abs,
|
||||
)
|
||||
return
|
||||
// Find next backdrop run [backdrop_scope_start, backdrop_scope_end). Each run = one bracket.
|
||||
backdrop_scope_start := i
|
||||
for i < layer_end && GLOB.tmp_sub_batches[i].kind == .Backdrop do i += 1
|
||||
backdrop_scope_end := i
|
||||
if backdrop_scope_end > backdrop_scope_start {
|
||||
run_backdrop_bracket(
|
||||
cmd_buffer,
|
||||
u32(backdrop_scope_start),
|
||||
u32(backdrop_scope_end),
|
||||
swapchain_width,
|
||||
swapchain_height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Bracketed layer: Pass A → backdrop bracket → Pass B.
|
||||
// See README.md § "Backdrop pipeline" for the full ordering semantics.
|
||||
render_layer_sub_batch_range(
|
||||
cmd_buffer,
|
||||
render_texture,
|
||||
swapchain_width,
|
||||
swapchain_height,
|
||||
clear_color,
|
||||
layer,
|
||||
int(layer.sub_batch_start),
|
||||
bracket_start_abs,
|
||||
)
|
||||
|
||||
run_backdrop_bracket(cmd_buffer, layer, swapchain_width, swapchain_height)
|
||||
|
||||
// Pass B: render the [bracket_start_abs, layer_end_abs) range. .Backdrop sub-batches in
|
||||
// this range are dispatched by the bracket above and ignored here (the .Backdrop case in
|
||||
// the inner switch is a no-op). LOAD is implied because Pass A or the bracket's V-
|
||||
// composite has already touched render_texture.
|
||||
render_layer_sub_batch_range(
|
||||
cmd_buffer,
|
||||
render_texture,
|
||||
swapchain_width,
|
||||
swapchain_height,
|
||||
clear_color,
|
||||
layer,
|
||||
bracket_start_abs,
|
||||
layer_end_abs,
|
||||
)
|
||||
}
|
||||
|
||||
// Render a sub-range of a layer's sub-batches in a single render pass. Iterates the layer's
|
||||
@@ -682,8 +682,10 @@ draw_layer :: proc(
|
||||
// and `range_end_abs` parameters are absolute indices into GLOB.tmp_sub_batches; only sub-
|
||||
// batches within `[range_start_abs, range_end_abs)` are drawn.
|
||||
//
|
||||
// .Backdrop sub-batches in the range are always silently skipped — they are dispatched by
|
||||
// run_backdrop_bracket, not here. The empty .Backdrop case in the inner switch enforces this.
|
||||
// The caller (`draw_layer`) splits the layer into pure-kind runs before calling this proc,
|
||||
// so the range MUST NOT contain any .Backdrop sub-batches; backdrop dispatch is handled by
|
||||
// `run_backdrop_bracket`. The .Backdrop case in the inner switch is `unreachable()` to
|
||||
// surface contract violations as fast as possible.
|
||||
//
|
||||
// Render-pass setup mirrors the original draw_layer: clear-or-load based on GLOB.cleared,
|
||||
// pipeline + storage + index buffer bound up front, then per-batch state tracking. After this
|
||||
@@ -780,7 +782,7 @@ render_layer_sub_batch_range :: proc(
|
||||
|
||||
for abs_idx in effective_start ..< effective_end {
|
||||
batch := &GLOB.tmp_sub_batches[abs_idx]
|
||||
switch batch.kind {
|
||||
#partial switch batch.kind {
|
||||
case .Tessellated:
|
||||
if current_mode != .Tessellated {
|
||||
push_globals(cmd_buffer, width, height, .Tessellated)
|
||||
@@ -869,10 +871,7 @@ render_layer_sub_batch_range :: proc(
|
||||
}
|
||||
sdl.DrawGPUPrimitives(render_pass, 6, batch.count, 0, batch.offset)
|
||||
|
||||
case .Backdrop:
|
||||
// Always a no-op here. Backdrop sub-batches are dispatched by run_backdrop_bracket;
|
||||
// when this proc encounters one (only possible in Pass B, since Pass A and the no-
|
||||
// backdrop fast path both stop their range before any .Backdrop index), we skip it.
|
||||
case: log.panicf("Non core2d batch kind (%v) reached in core2d dispatch path.", batch.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -894,21 +893,11 @@ prepare_shape :: proc(layer: ^Layer, vertices: []Vertex_2D) {
|
||||
append_or_extend_sub_batch(scissor, layer, .Tessellated, offset, u32(len(vertices)))
|
||||
}
|
||||
|
||||
// Submit an SDF primitive to the given layer for rendering. Requires the caller to build a
|
||||
// Core_2D_Primitive directly, which is the internal GPU-layout struct.
|
||||
//INTERNAL
|
||||
prepare_sdf_primitive :: proc(layer: ^Layer, prim: Core_2D_Primitive) {
|
||||
offset := u32(len(GLOB.tmp_primitives))
|
||||
append(&GLOB.tmp_primitives, prim)
|
||||
scissor := &GLOB.scissors[layer.scissor_start + layer.scissor_len - 1]
|
||||
append_or_extend_sub_batch(scissor, layer, .SDF, offset, 1)
|
||||
}
|
||||
|
||||
// Submit an SDF primitive with optional texture binding.
|
||||
// The texture-aware counterpart of `prepare_sdf_primitive`; lets shape procs route a
|
||||
// texture_id and sampler into the sub-batch without growing the public API.
|
||||
//INTERNAL
|
||||
prepare_sdf_primitive_ex :: proc(
|
||||
prepare_sdf_primitive :: proc(
|
||||
layer: ^Layer,
|
||||
prim: Core_2D_Primitive,
|
||||
texture_id: Texture_Id = INVALID_TEXTURE,
|
||||
@@ -1409,7 +1398,7 @@ apply_brush_and_outline :: proc(
|
||||
}
|
||||
|
||||
prim.flags = pack_kind_flags(kind, flags)
|
||||
prepare_sdf_primitive_ex(layer, prim^, texture_id, sampler)
|
||||
prepare_sdf_primitive(layer, prim^, texture_id, sampler)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user