From f9dca4cdfe9369bfe4960fe83466bc272b7c0deb Mon Sep 17 00:00:00 2001 From: shan Date: Tue, 22 Jul 2025 20:59:52 -0700 Subject: [PATCH] Add proper layering support and support for non-clay layouts --- examples/main.odin | 63 ++++++++-- renderer/quad.odin | 59 +++++---- renderer/renderer.odin | 269 +++++++++++++++++++++++++---------------- renderer/text.odin | 30 +++-- 4 files changed, 268 insertions(+), 153 deletions(-) diff --git a/examples/main.odin b/examples/main.odin index b33d8d2..c4d4969 100644 --- a/examples/main.odin +++ b/examples/main.odin @@ -24,8 +24,6 @@ body_text := clay.TextElementConfig { } main :: proc() { - defer destroy() - when ODIN_DEBUG == true { context.logger = log.create_console_logger(lowest = .Debug) @@ -153,9 +151,12 @@ main :: proc() { last_frame_time = frame_time } + + destroy() } destroy :: proc() { + free_all(context.temp_allocator) renderer.destroy(device) sdl.ReleaseWindowFromGPUDevice(device, window) sdl.DestroyWindow(window) @@ -169,15 +170,22 @@ update :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, delta_time: u64) -> bool { mouse_flags := sdl.GetMouseState(&mouse_x, &mouse_y) width, height: c.int sdl.GetWindowSize(window, &width, &height) + window_bounds := renderer.Rectangle { + x = 0.0, + y = 0.0, + w = f32(width), + h = f32(height), + } - layer := renderer.begin_prepare() + layer := renderer.begin_prepare(window_bounds) // ===== Begin processing primitives for GPU upload ===== // Everything after begin_prepare() is uploaded in-order. We pass the layer down // until we need a new one, after which we call new_layer() - // Render raw primitive setup - //renderer.prepare_batch(device, window, cmd_buffer, &layer, &primitives) //TODO + // Process primitives on this layer + layout(layer) + // Process clay-specific primitives clay_layer_bounds := renderer.Rectangle { x = f32(width) / 2.0, y = 0.0, @@ -185,13 +193,10 @@ update :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, delta_time: u64) -> bool { h = f32(height), } // Create a new layer, because these two scenes cannot be renderer in the same batch due to overlap - layer = renderer.new_layer(&layer, clay_layer_bounds) + layer = renderer.new_layer(layer, clay_layer_bounds) clay_batch := clay_layout(clay_layer_bounds) renderer.prepare_clay_batch( - device, - window, - cmd_buffer, - &layer, + layer, {mouse_x, mouse_y}, mouse_flags, input.mouse_delta, @@ -200,7 +205,7 @@ update :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, delta_time: u64) -> bool { ) // This uploads the primitive data to the GPU - renderer.end_prepare(device, cmd_buffer, &layer) + renderer.end_prepare(device, cmd_buffer) return input.should_quit } @@ -258,7 +263,7 @@ clay_layout :: proc(bounds: renderer.Rectangle) -> renderer.ClayBatch { childAlignment = {x = .Center, y = .Center}, childGap = 32, }, - backgroundColor = {200.0, 200.0, 200.0, 255.0}, + backgroundColor = {200.0, 200.0, 200.0, 100.0}, }, ) { if clay.UI()( @@ -280,8 +285,42 @@ clay_layout :: proc(bounds: renderer.Rectangle) -> renderer.ClayBatch { ) { } + if clay.UI()( + { + id = clay.ID("RoundedRect2"), + backgroundColor = {255.0, 100.0, 100.0, 255.0}, + cornerRadius = clay.CornerRadius { + topLeft = 10, + topRight = 20, + bottomLeft = 40, + bottomRight = 0, + }, + border = clay.BorderElementConfig { + color = {0.0, 0.0, 0.0, 255.0}, + width = clay.BorderAll(5), + }, + layout = {sizing = {clay.SizingFixed(240), clay.SizingFixed(80)}}, + }, + ) { + } + clay.Text("Test Text", &body_text) } return renderer.ClayBatch{bounds, clay.EndLayout()} } + +layout :: proc(layer: ^renderer.Layer) { + bounds := layer.bounds + + test_quad := renderer.quad( + pos = {bounds.x + 200, bounds.y + 200}, + size = {bounds.w / 2.0, bounds.h / 2.0}, + color = {0.2, 0.2, 0.8, 1}, + corner_radii = {5, 10, 0, 20}, + border_color = {0, 0, 0, 1}, + border_width = 10, + ) + + renderer.prepare_quad(layer, test_quad) +} diff --git a/renderer/quad.odin b/renderer/quad.odin index 05e2871..1ad7033 100644 --- a/renderer/quad.odin +++ b/renderer/quad.odin @@ -5,8 +5,6 @@ import "core:mem" import "core:os" import sdl "vendor:sdl3" -tmp_quads: [dynamic]Quad - QuadPipeline :: struct { instance_buffer: Buffer, num_instances: u32, @@ -14,12 +12,30 @@ QuadPipeline :: struct { } Quad :: struct { - position_scale: [4]f32, - corner_radii: [4]f32, - color: [4]f32, - border_color: [4]f32, - border_width: f32, - _: [3]f32, + position_scale: [4]f32, + corner_radii: [4]f32, + color: [4]f32, + border_color: [4]f32, + border_width: f32, + _: [3]f32, +} + +quad :: proc( + pos: [2]f32, + size: [2]f32, + color: [4]f32, + corner_radii := [4]f32{0, 0, 0, 0}, + border_color := [4]f32{0, 0, 0, 0}, + border_width: f32 = 0, +) -> Quad { + return Quad { + {pos.x, pos.y, size.x, size.y}, + corner_radii, + color, + border_color, + border_width, + {0, 0, 0}, + } } @(private) @@ -47,11 +63,11 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua } frag_info := sdl.GPUShaderCreateInfo { - code_size = len(frag_raw), - code = raw_data(frag_raw), - entrypoint = ENTRY_POINT, - format = SHADER_TYPE, - stage = sdl.GPUShaderStage.FRAGMENT, + code_size = len(frag_raw), + code = raw_data(frag_raw), + entrypoint = ENTRY_POINT, + format = SHADER_TYPE, + stage = sdl.GPUShaderStage.FRAGMENT, } vert_shader := sdl.CreateGPUShader(device, vert_info) @@ -151,7 +167,7 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua instance_buffer := create_buffer( device, size_of(Quad) * BUFFER_INIT_SIZE, - sdl.GPUBufferUsageFlags { .VERTEX }, + sdl.GPUBufferUsageFlags{.VERTEX}, ) pipeline := QuadPipeline{instance_buffer, BUFFER_INIT_SIZE, sdl_pipeline} @@ -162,10 +178,11 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua @(private) upload_quads :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) { + using global num_quads := u32(len(tmp_quads)) size := num_quads * size_of(Quad) - resize_buffer(device, &quad_pipeline.instance_buffer, size, sdl.GPUBufferUsageFlags { .VERTEX }) + resize_buffer(device, &quad_pipeline.instance_buffer, size, sdl.GPUBufferUsageFlags{.VERTEX}) // Write data i_array := sdl.MapGPUTransferBuffer(device, quad_pipeline.instance_buffer.transfer, false) @@ -192,6 +209,8 @@ draw_quads :: proc( layer: ^Layer, load_op: sdl.GPULoadOp, ) { + using global + if layer.quad_len == 0 { return } @@ -219,17 +238,12 @@ draw_quads :: proc( quad_offset := layer.quad_instance_start - for &scissor, index in layer.scissors { + for &scissor, index in scissors[layer.scissor_start:][:layer.scissor_len] { if scissor.quad_len == 0 { continue } - if scissor.bounds.w == 0 || scissor.bounds.h == 0 { - sdl.SetGPUScissor(render_pass, sdl.Rect{0, 0, i32(swapchain_w), i32(swapchain_h)}) - } else { - sdl.SetGPUScissor(render_pass, scissor.bounds) - } - + sdl.SetGPUScissor(render_pass, scissor.bounds) sdl.DrawGPUPrimitives(render_pass, 6, scissor.quad_len, 0, quad_offset) quad_offset += scissor.quad_len } @@ -237,6 +251,7 @@ draw_quads :: proc( } destroy_quad_pipeline :: proc(device: ^sdl.GPUDevice) { + using global destroy_buffer(device, &quad_pipeline.instance_buffer) sdl.ReleaseGPUGraphicsPipeline(device, quad_pipeline.sdl_pipeline) } diff --git a/renderer/renderer.odin b/renderer/renderer.odin index 6bb6572..73ecbcb 100644 --- a/renderer/renderer.odin +++ b/renderer/renderer.odin @@ -18,33 +18,64 @@ when ODIN_OS == .Darwin { } BUFFER_INIT_SIZE: u32 : 256 +INITIAL_LAYER_SIZE :: 5 +INITIAL_SCISSOR_SIZE :: 10 -dpi_scaling: f32 = 1.0 -layers: [dynamic]Layer -quad_pipeline: QuadPipeline -text_pipeline: TextPipeline -odin_context: runtime.Context +Global :: struct { + dpi_scaling: f32, + curr_layer_index: uint, + layers: [dynamic]Layer, + max_layers: int, + scissors: [dynamic]Scissor, + max_scissors: int, + tmp_text: [dynamic]Text, + max_tmp_text: int, + tmp_quads: [dynamic]Quad, + max_tmp_quads: int, + clay_mem: [^]u8, + odin_context: runtime.Context, + quad_pipeline: QuadPipeline, + text_pipeline: TextPipeline, +} -// I need to make it so that I can -// a) Add primitives directly to a layer -// b) Create small nested clay-layouts that can be batched with other shit -// Some colletion of items, where its just primtiives, but collections of primitives? And each collection has a type, -// either raw or clay? Or something +// TODO every x frames nuke max values in case of edge cases where max gets set very high +// Called at the end of every frame +resize_global :: proc() { + using global -// Prepare - upload to GPU -// With clay commands, this requires converting to my custom primitive types -// With raw commands, this would require no conversion -// Need to go from UI declaration -> processing render cmds + if len(layers) > max_layers do max_layers = len(layers) + shrink(&layers, max_layers) + if len(scissors) > max_scissors do max_scissors = len(scissors) + shrink(&scissors, max_scissors) + if len(tmp_text) > max_tmp_text do max_tmp_text = len(tmp_text) + shrink(&tmp_text, max_tmp_text) + if len(tmp_quads) > max_tmp_quads do max_tmp_quads = len(tmp_quads) + shrink(&tmp_quads, max_tmp_quads) +} -// I want to process & upload in the same loop, don't want to add an additional pass where I transform -// clay cmds -> my primitives +destroy :: proc(device: ^sdl.GPUDevice) { + using global + delete(layers) + delete(scissors) + delete(tmp_text) + delete(tmp_quads) + free(clay_mem) + destroy_quad_pipeline(device) + destroy_text_pipeline(device) +} + +clear_global :: proc() { + using global + + curr_layer_index = 0 + clear(&layers) + clear(&scissors) + clear(&tmp_text) + clear(&tmp_quads) +} + +global: Global -// 1) I need to be able to process all clay cmds & all raw primitive cmds in a single iteration -// 2) I need to be able to define (at a layout level) which primitives can be rendered in the same batch -// -// Some kind of queue? Each UI element adds to a temp queue that is reset every frame -// Queue is like -// [ chunk of primitives ] [ chunk of clay primitives ] [ new layer indicator ] [ chunk ] Rectangle :: struct { x: f32, y: f32, @@ -62,8 +93,8 @@ Layer :: struct { text_vertex_len: u32, text_index_start: u32, text_index_len: u32, - curr_scissor_index: u32, - scissors: [dynamic]Scissor, + scissor_start: u32, + scissor_len: u32, } Scissor :: struct { @@ -82,23 +113,29 @@ init :: proc( window_height: f32, ctx: runtime.Context, ) { - odin_context = ctx - dpi_scaling = sdl.GetWindowDisplayScale(window) - log.debug("Window DPI scaling:", dpi_scaling) - min_memory_size: c.size_t = cast(c.size_t)clay.MinMemorySize() - memory := make([^]u8, min_memory_size) - arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, memory) + + global = Global { + layers = make([dynamic]Layer, 0, INITIAL_LAYER_SIZE), + scissors = make([dynamic]Scissor, 0, INITIAL_SCISSOR_SIZE), + tmp_quads = make([dynamic]Quad, 0, BUFFER_INIT_SIZE), + tmp_text = make([dynamic]Text, 0, BUFFER_INIT_SIZE), + odin_context = ctx, + dpi_scaling = sdl.GetWindowDisplayScale(window), + clay_mem = make([^]u8, min_memory_size), + quad_pipeline = create_quad_pipeline(device, window), + text_pipeline = create_text_pipeline(device, window), + } + log.debug("Window DPI scaling:", global.dpi_scaling) + arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, global.clay_mem) clay.Initialize(arena, {window_width, window_height}, {handler = clay_error_handler}) clay.SetMeasureTextFunction(measure_text, nil) - quad_pipeline = create_quad_pipeline(device, window) - text_pipeline = create_text_pipeline(device, window) } @(private = "file") clay_error_handler :: proc "c" (errorData: clay.ErrorData) { - context = odin_context + context = global.odin_context log.error("Clay error:", errorData.errorType, errorData.errorText) } @@ -108,6 +145,7 @@ measure_text :: proc "c" ( config: ^clay.TextElementConfig, user_data: rawptr, ) -> clay.Dimensions { + using global context = odin_context text := string(text.chars[:text.length]) c_text := strings.clone_to_cstring(text, context.temp_allocator) @@ -119,62 +157,79 @@ measure_text :: proc "c" ( return clay.Dimensions{width = f32(w) / dpi_scaling, height = f32(h) / dpi_scaling} } -destroy :: proc(device: ^sdl.GPUDevice) { - destroy_quad_pipeline(device) - destroy_text_pipeline(device) -} - /// Sets up renderer to begin upload to the GPU. Returns starting `Layer` to begin processing primitives for -begin_prepare :: proc() -> Layer { - // Prepare to upload to GPU - clear(&layers) - clear(&tmp_quads) - clear(&tmp_text) +begin_prepare :: proc(bounds: Rectangle) -> ^Layer { + using global + // Cleanup + clear_global() - tmp_quads = make([dynamic]Quad, 0, quad_pipeline.num_instances, context.temp_allocator) - tmp_text = make([dynamic]Text, 0, 20, context.temp_allocator) + // Begin new layer + // Start a new scissor + scissor := Scissor { + bounds = sdl.Rect { + x = i32(bounds.x * dpi_scaling), + y = i32(bounds.y * dpi_scaling), + w = i32(bounds.w * dpi_scaling), + h = i32(bounds.h * dpi_scaling), + }, + } + append(&scissors, scissor) layer := Layer { - scissors = make([dynamic]Scissor, 0, 10, context.temp_allocator), + bounds = bounds, + scissor_len = 1, } - - return layer + append(&layers, layer) + return &layers[curr_layer_index] } -/// Creates a new layer, appending the old one to `layers` -new_layer :: proc(layer: ^Layer, bounds: Rectangle) -> Layer { - append(&layers, layer^) +/// Creates a new layer +new_layer :: proc(old_layer: ^Layer, bounds: Rectangle) -> ^Layer { + using global layer := Layer { - bounds = bounds, - scissors = make([dynamic]Scissor, 0, 10, context.temp_allocator), + bounds = bounds, + quad_instance_start = old_layer.quad_instance_start + old_layer.quad_len, + text_instance_start = old_layer.text_instance_start + old_layer.text_instance_len, + text_vertex_start = old_layer.text_vertex_start + old_layer.text_vertex_len, + text_index_start = old_layer.text_index_start + old_layer.text_instance_len, + scissor_start = old_layer.scissor_start + old_layer.scissor_len, + scissor_len = 1, } + append(&layers, layer) + curr_layer_index += 1 - return layer + scissor := Scissor { + bounds = sdl.Rect { + x = i32(bounds.x * dpi_scaling), + y = i32(bounds.y * dpi_scaling), + w = i32(bounds.w * dpi_scaling), + h = i32(bounds.h * dpi_scaling), + }, + } + append(&scissors, scissor) + return &layers[curr_layer_index] } -end_prepare :: proc(device: ^sdl.GPUDevice, cmd_buffer: ^sdl.GPUCommandBuffer, layer: ^Layer) { - // Commit last layer worked on - append(&layers, layer^) - +end_prepare :: proc(device: ^sdl.GPUDevice, cmd_buffer: ^sdl.GPUCommandBuffer) { // Upload primitives to GPU copy_pass := sdl.BeginGPUCopyPass(cmd_buffer) upload_quads(device, copy_pass) upload_text(device, copy_pass) sdl.EndGPUCopyPass(copy_pass) + + // Resize my dynamic arrays + resize_global() } // ===== Built-in primitive processing ===== -//prepare_batch :: proc( -// device: ^sdl.GPUDevice, -// window: ^sdl.Window, -// cmd_buffer: ^sdl.GPUCommandBuffer, -// layer: ^Layer, -// primitives: ^[]Primitive, -//) { -// scissor := Scissor{} -// -// -//} +// TODO scissoring support if I need it for primitives +prepare_quad :: proc(layer: ^Layer, quad: Quad) { + using global + + append(&tmp_quads, quad) + layer.quad_len += 1 + scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1 +} // ====== Clay-specific processing ====== ClayBatch :: struct { @@ -184,9 +239,6 @@ ClayBatch :: struct { /// Upload data to the GPU prepare_clay_batch :: proc( - device: ^sdl.GPUDevice, - window: ^sdl.Window, - cmd_buffer: ^sdl.GPUCommandBuffer, layer: ^Layer, mouse_pos: [2]f32, mouse_flags: sdl.MouseButtonFlags, @@ -194,6 +246,8 @@ prepare_clay_batch :: proc( frame_time: f32, batch: ^ClayBatch, ) { + using global + // Update clay internals clay.SetPointerState( clay.Vector2{mouse_pos.x - layer.bounds.x, mouse_pos.y - layer.bounds.y}, @@ -201,8 +255,6 @@ prepare_clay_batch :: proc( ) clay.UpdateScrollContainers(true, transmute(clay.Vector2)mouse_wheel_delta, frame_time) - scissor := Scissor{} - // Parse render commands for i in 0 ..< int(batch.cmds.length) { render_command := clay.RenderCommandArray_Get(&batch.cmds, cast(i32)i) @@ -237,6 +289,9 @@ prepare_clay_batch :: proc( } data := sdl_ttf.GetGPUTextDrawData(sdl_text) + if data == nil { + log.error("Failed to find GPUTextDrawData for sdl_text:", c_text) + } if sdl_text == nil { log.error("Could not create SDL text:", sdl.GetError()) @@ -248,29 +303,39 @@ prepare_clay_batch :: proc( layer.text_instance_len += 1 layer.text_vertex_len += u32(data.num_vertices) layer.text_index_len += u32(data.num_indices) - scissor.text_len += 1 + scissors[layer.scissor_start + layer.scissor_len - 1].text_len += 1 } case clay.RenderCommandType.Image: case clay.RenderCommandType.ScissorStart: - bounds := sdl.Rect { - c.int(bounds.x * dpi_scaling), - c.int(bounds.y * dpi_scaling), - c.int(bounds.w * dpi_scaling), - c.int(bounds.h * dpi_scaling), - } - if scissor.quad_len != 0 || scissor.text_len != 0 { - new := new_scissor(&scissor) - append(&layer.scissors, scissor) - scissor = new + if bounds.w == 0 || bounds.h == 0 { + continue } - scissor.bounds = bounds - case clay.RenderCommandType.ScissorEnd: - if scissor.quad_len != 0 || scissor.text_len != 0 { - new := new_scissor(&scissor) - append(&layer.scissors, scissor) - scissor = new + curr_scissor := &scissors[layer.scissor_start + layer.scissor_len - 1] + + if curr_scissor.quad_len != 0 || curr_scissor.text_len != 0 { + // Scissor has some content, need to make a new scissor + new := Scissor { + quad_start = curr_scissor.quad_start + curr_scissor.quad_len, + text_start = curr_scissor.text_start + curr_scissor.text_len, + bounds = sdl.Rect { + c.int(bounds.x * dpi_scaling), + c.int(bounds.y * dpi_scaling), + c.int(bounds.w * dpi_scaling), + c.int(bounds.h * dpi_scaling), + }, + } + append(&scissors, new) + layer.scissor_len += 1 + } else { + curr_scissor.bounds = sdl.Rect { + c.int(bounds.x * dpi_scaling), + c.int(bounds.y * dpi_scaling), + c.int(bounds.w * dpi_scaling), + c.int(bounds.h * dpi_scaling), + } } + case clay.RenderCommandType.ScissorEnd: case clay.RenderCommandType.Rectangle: render_data := render_command.renderData.rectangle color := f32_color(render_data.backgroundColor) @@ -281,12 +346,12 @@ prepare_clay_batch :: proc( color = color, } append(&tmp_quads, quad) + layer.quad_len += 1 - scissor.quad_len += 1 + scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1 case clay.RenderCommandType.Border: render_data := render_command.renderData.border cr := render_data.cornerRadius - //TODO dedicated border pipeline quad := Quad { position_scale = {bounds.x, bounds.y, bounds.w, bounds.h}, corner_radii = {cr.bottomRight, cr.topRight, cr.bottomLeft, cr.topLeft}, @@ -299,18 +364,15 @@ prepare_clay_batch :: proc( // for our use case we can just chuck these in with the quad pipeline append(&tmp_quads, quad) layer.quad_len += 1 - scissor.quad_len += 1 + scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1 case clay.RenderCommandType.Custom: } } - - if scissor.quad_len != 0 || scissor.text_len != 0 { - append(&layer.scissors, scissor) - } } /// Render primitives draw :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, cmd_buffer: ^sdl.GPUCommandBuffer) { + using global swapchain_texture: ^sdl.GPUTexture w, h: u32 if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &w, &h) { @@ -335,7 +397,7 @@ draw :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, cmd_buffer: ^sdl.GPUCo index == 0 ? sdl.GPULoadOp.CLEAR : sdl.GPULoadOp.LOAD, ) draw_text(device, window, cmd_buffer, swapchain_texture, w, h, &layer) - //TODO draw other primitives in layer + //TODO draw other primitives in layer once I add support for them :) } } @@ -367,15 +429,8 @@ Globals :: struct { push_globals :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, w: f32, h: f32) { globals := Globals { ortho_rh(left = 0.0, top = 0.0, right = f32(w), bottom = f32(h), near = -1.0, far = 1.0), - dpi_scaling, + global.dpi_scaling, } sdl.PushGPUVertexUniformData(cmd_buffer, 0, &globals, size_of(Globals)) } - -new_scissor :: proc(old: ^Scissor) -> Scissor { - return Scissor { - quad_start = old.quad_start + old.quad_len, - text_start = old.text_start + old.text_len, - } -} diff --git a/renderer/text.odin b/renderer/text.odin index a744885..2b7c035 100644 --- a/renderer/text.odin +++ b/renderer/text.odin @@ -12,8 +12,6 @@ JETBRAINS_MONO_BOLD: u16 : 1 NUM_FONTS :: 2 MAX_FONT_SIZE :: 120 -tmp_text: [dynamic]Text - @(private = "file") jetbrains_mono_regular := #load("res/fonts/JetBrainsMono-Regular.ttf") @(private = "file") @@ -31,7 +29,7 @@ TextPipeline :: struct { } get_font :: proc(id: u16, size: u16) -> ^sdl_ttf.Font { - font := text_pipeline.fonts[id > 1 ? 0 : id][size > 0 ? size : 16] + font := global.text_pipeline.fonts[id > 1 ? 0 : id][size > 0 ? size : 16] if font == nil { log.debug("Font not found for size", size, "+ adding") @@ -45,8 +43,13 @@ get_font :: proc(id: u16, size: u16) -> ^sdl_ttf.Font { os.exit(1) } font = f - _ = sdl_ttf.SetFontSizeDPI(f, f32(size), 72 * i32(dpi_scaling), 72 * i32(dpi_scaling)) - text_pipeline.fonts[id][size] = f + _ = sdl_ttf.SetFontSizeDPI( + f, + f32(size), + 72 * i32(global.dpi_scaling), + 72 * i32(global.dpi_scaling), + ) + global.text_pipeline.fonts[id][size] = f } return font @@ -241,11 +244,14 @@ create_text_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Tex @(private) upload_text :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) { + using global + + // TODO maybe don't use tmp here vertices := make([dynamic]TextVert, 0, BUFFER_INIT_SIZE, context.temp_allocator) indices := make([dynamic]c.int, 0, BUFFER_INIT_SIZE, context.temp_allocator) instances := make([dynamic][2]f32, 0, BUFFER_INIT_SIZE, context.temp_allocator) - for &text, index in tmp_text { + for &text, index in global.tmp_text { append(&instances, text.position) data := sdl_ttf.GetGPUTextDrawData(text.ref) for data != nil { @@ -344,6 +350,7 @@ draw_text :: proc( swapchain_h: u32, layer: ^Layer, ) { + using global if layer.text_instance_len == 0 { return } @@ -382,16 +389,12 @@ draw_text :: proc( vertex_offset: i32 = i32(layer.text_vertex_start) instance_offset: u32 = layer.text_instance_start - for &scissor, index in layer.scissors { + for &scissor, index in scissors[layer.scissor_start:][:layer.scissor_len] { if scissor.text_len == 0 { continue } - if scissor.bounds.w == 0 || scissor.bounds.h == 0 { - sdl.SetGPUScissor(render_pass, sdl.Rect{0, 0, i32(swapchain_w), i32(swapchain_h)}) - } else { - sdl.SetGPUScissor(render_pass, scissor.bounds) - } + sdl.SetGPUScissor(render_pass, scissor.bounds) for &text in layer_text[scissor.text_start:scissor.text_start + scissor.text_len] { data := sdl_ttf.GetGPUTextDrawData(text.ref) @@ -433,7 +436,10 @@ draw_text :: proc( } destroy_text_pipeline :: proc(device: ^sdl.GPUDevice) { + using global destroy_buffer(device, &text_pipeline.vertex_buffer) destroy_buffer(device, &text_pipeline.index_buffer) + destroy_buffer(device, &text_pipeline.instance_buffer) + delete(text_pipeline.cache) sdl.ReleaseGPUGraphicsPipeline(device, text_pipeline.sdl_pipeline) }