Added draw package as renderer focused on mixed use layout / 2D / 3D scene applications #7

Merged
zack merged 6 commits from draw into master 2026-04-20 20:14:57 +00:00
3 changed files with 167 additions and 180 deletions
Showing only changes of commit 30b72128b2 - Show all commits

View File

@@ -186,8 +186,8 @@ init :: proc(
return true return true
} }
// TODO every x frames nuke max values in case of edge cases where max gets set very high // TODO Either every x frames nuke max values in case of edge cases where max gets set very high
// Called at the end of every frame // or leave to application code to decide the right time for resize
resize_global :: proc() { resize_global :: proc() {
if len(GLOB.layers) > GLOB.max_layers do GLOB.max_layers = len(GLOB.layers) if len(GLOB.layers) > GLOB.max_layers do GLOB.max_layers = len(GLOB.layers)
shrink(&GLOB.layers, GLOB.max_layers) shrink(&GLOB.layers, GLOB.max_layers)
@@ -546,11 +546,7 @@ prepare_clay_batch :: proc(
} }
// Render primitives. clear_color is the background fill before any layers are drawn. // Render primitives. clear_color is the background fill before any layers are drawn.
end :: proc( end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = BLACK) {
device: ^sdl.GPUDevice,
window: ^sdl.Window,
clear_color: Color = BLACK,
) {
cmd_buffer := sdl.AcquireGPUCommandBuffer(device) cmd_buffer := sdl.AcquireGPUCommandBuffer(device)
if cmd_buffer == nil { if cmd_buffer == nil {
log.panicf("Failed to acquire GPU command buffer: %s", sdl.GetError()) log.panicf("Failed to acquire GPU command buffer: %s", sdl.GetError())
@@ -561,10 +557,6 @@ end :: proc(
upload(device, copy_pass) upload(device, copy_pass)
sdl.EndGPUCopyPass(copy_pass) sdl.EndGPUCopyPass(copy_pass)
// Resize dynamic arrays
// TODO: This should only be called occasionally, not every frame.
resize_global()
swapchain_texture: ^sdl.GPUTexture swapchain_texture: ^sdl.GPUTexture
w, h: u32 w, h: u32
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &w, &h) { if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &w, &h) {

View File

@@ -315,8 +315,12 @@ create_pipeline_2d_base :: proc(
sdl.UnmapGPUTransferBuffer(device, white_transfer) sdl.UnmapGPUTransferBuffer(device, white_transfer)
quad_verts := [6]Vertex { quad_verts := [6]Vertex {
{position = {0, 0}}, {position = {1, 0}}, {position = {0, 1}}, {position = {0, 0}},
{position = {0, 1}}, {position = {1, 0}}, {position = {1, 1}}, {position = {1, 0}},
{position = {0, 1}},
{position = {0, 1}},
{position = {1, 0}},
{position = {1, 1}},
} }
quad_transfer := sdl.CreateGPUTransferBuffer( quad_transfer := sdl.CreateGPUTransferBuffer(
device, device,
@@ -353,11 +357,7 @@ create_pipeline_2d_base :: proc(
sdl.UploadToGPUBuffer( sdl.UploadToGPUBuffer(
upload_pass, upload_pass,
sdl.GPUTransferBufferLocation{transfer_buffer = quad_transfer}, sdl.GPUTransferBufferLocation{transfer_buffer = quad_transfer},
sdl.GPUBufferRegion{ sdl.GPUBufferRegion{buffer = pipeline.unit_quad_buffer, offset = 0, size = size_of(quad_verts)},
buffer = pipeline.unit_quad_buffer,
offset = 0,
size = size_of(quad_verts),
},
false, false,
) )
@@ -428,11 +428,7 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
sdl.UploadToGPUBuffer( sdl.UploadToGPUBuffer(
pass, pass,
sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.vertex_buffer.transfer}, sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.vertex_buffer.transfer},
sdl.GPUBufferRegion{ sdl.GPUBufferRegion{buffer = GLOB.pipeline_2d_base.vertex_buffer.gpu, offset = 0, size = total_vert_size},
buffer = GLOB.pipeline_2d_base.vertex_buffer.gpu,
offset = 0,
size = total_vert_size,
},
false, false,
) )
} }
@@ -459,11 +455,7 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
sdl.UploadToGPUBuffer( sdl.UploadToGPUBuffer(
pass, pass,
sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.index_buffer.transfer}, sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.index_buffer.transfer},
sdl.GPUBufferRegion{ sdl.GPUBufferRegion{buffer = GLOB.pipeline_2d_base.index_buffer.gpu, offset = 0, size = index_size},
buffer = GLOB.pipeline_2d_base.index_buffer.gpu,
offset = 0,
size = index_size,
},
false, false,
) )
} }
@@ -480,9 +472,7 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ}, sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
) )
p_array := sdl.MapGPUTransferBuffer( p_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer, false)
device, GLOB.pipeline_2d_base.primitive_buffer.transfer, false,
)
if p_array == nil { if p_array == nil {
log.panicf("Failed to map primitive transfer buffer: %s", sdl.GetError()) log.panicf("Failed to map primitive transfer buffer: %s", sdl.GetError())
} }
@@ -491,14 +481,8 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
sdl.UploadToGPUBuffer( sdl.UploadToGPUBuffer(
pass, pass,
sdl.GPUTransferBufferLocation{ sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.primitive_buffer.transfer},
transfer_buffer = GLOB.pipeline_2d_base.primitive_buffer.transfer, sdl.GPUBufferRegion{buffer = GLOB.pipeline_2d_base.primitive_buffer.gpu, offset = 0, size = prim_size},
},
sdl.GPUBufferRegion{
buffer = GLOB.pipeline_2d_base.primitive_buffer.gpu,
offset = 0,
size = prim_size,
},
false, false,
) )
} }
@@ -521,9 +505,7 @@ draw_layer :: proc(
cmd_buffer, cmd_buffer,
&sdl.GPUColorTargetInfo { &sdl.GPUColorTargetInfo {
texture = render_texture, texture = render_texture,
clear_color = sdl.FColor { clear_color = sdl.FColor{clear_color[0], clear_color[1], clear_color[2], clear_color[3]},
clear_color[0], clear_color[1], clear_color[2], clear_color[3],
},
load_op = .CLEAR, load_op = .CLEAR,
store_op = .STORE, store_op = .STORE,
}, },
@@ -540,9 +522,7 @@ draw_layer :: proc(
cmd_buffer, cmd_buffer,
&sdl.GPUColorTargetInfo { &sdl.GPUColorTargetInfo {
texture = render_texture, texture = render_texture,
clear_color = sdl.FColor { clear_color = sdl.FColor{clear_color[0], clear_color[1], clear_color[2], clear_color[3]},
clear_color[0], clear_color[1], clear_color[2], clear_color[3],
},
load_op = GLOB.cleared ? .LOAD : .CLEAR, load_op = GLOB.cleared ? .LOAD : .CLEAR,
store_op = .STORE, store_op = .STORE,
}, },
@@ -578,9 +558,7 @@ draw_layer :: proc(
// Initial GPU state: tessellated mode, main vertex buffer, no atlas bound yet // Initial GPU state: tessellated mode, main vertex buffer, no atlas bound yet
push_globals(cmd_buffer, w, h, .Tessellated) push_globals(cmd_buffer, w, h, .Tessellated)
sdl.BindGPUVertexBuffers( sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
)
current_mode: Draw_Mode = .Tessellated current_mode: Draw_Mode = .Tessellated
current_vbuf := main_vbuf current_vbuf := main_vbuf
@@ -600,16 +578,15 @@ draw_layer :: proc(
current_mode = .Tessellated current_mode = .Tessellated
} }
if current_vbuf != main_vbuf { if current_vbuf != main_vbuf {
sdl.BindGPUVertexBuffers( sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
render_pass, 0,
&sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
)
current_vbuf = main_vbuf current_vbuf = main_vbuf
} }
if current_atlas != white { if current_atlas != white {
sdl.BindGPUFragmentSamplers( sdl.BindGPUFragmentSamplers(
render_pass, 0, render_pass,
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler}, 1, 0,
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler},
1,
) )
current_atlas = white current_atlas = white
} }
@@ -621,20 +598,15 @@ draw_layer :: proc(
current_mode = .Tessellated current_mode = .Tessellated
} }
if current_vbuf != main_vbuf { if current_vbuf != main_vbuf {
sdl.BindGPUVertexBuffers( sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
render_pass, 0,
&sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
)
current_vbuf = main_vbuf current_vbuf = main_vbuf
} }
chunk := &GLOB.tmp_text_batches[batch.offset] chunk := &GLOB.tmp_text_batches[batch.offset]
if current_atlas != chunk.atlas_texture { if current_atlas != chunk.atlas_texture {
sdl.BindGPUFragmentSamplers( sdl.BindGPUFragmentSamplers(
render_pass, 0, render_pass,
&sdl.GPUTextureSamplerBinding { 0,
texture = chunk.atlas_texture, &sdl.GPUTextureSamplerBinding{texture = chunk.atlas_texture, sampler = sampler},
sampler = sampler,
},
1, 1,
) )
current_atlas = chunk.atlas_texture current_atlas = chunk.atlas_texture
@@ -654,16 +626,15 @@ draw_layer :: proc(
current_mode = .SDF current_mode = .SDF
} }
if current_vbuf != unit_quad { if current_vbuf != unit_quad {
sdl.BindGPUVertexBuffers( sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = unit_quad, offset = 0}, 1)
render_pass, 0,
&sdl.GPUBufferBinding{buffer = unit_quad, offset = 0}, 1,
)
current_vbuf = unit_quad current_vbuf = unit_quad
} }
if current_atlas != white { if current_atlas != white {
sdl.BindGPUFragmentSamplers( sdl.BindGPUFragmentSamplers(
render_pass, 0, render_pass,
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler}, 1, 0,
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler},
1,
) )
current_atlas = white current_atlas = white
} }

View File

@@ -324,13 +324,7 @@ triangle_strip :: proc(
// ----- SDF drawing functions ---- // ----- SDF drawing functions ----
// Draw a rectangle with per-corner rounding radii via SDF. // Draw a rectangle with per-corner rounding radii via SDF.
rectangle_corners :: proc( rectangle_corners :: proc(layer: ^Layer, rect: Rectangle, radii: [4]f32, color: Color, soft_px: f32 = 1.0) {
layer: ^Layer,
rect: Rectangle,
radii: [4]f32,
color: Color,
soft_px: f32 = 1.0,
) {
max_radius := min(rect.w, rect.h) * 0.5 max_radius := min(rect.w, rect.h) * 0.5
tl := clamp(radii[0], 0, max_radius) tl := clamp(radii[0], 0, max_radius)
tr := clamp(radii[1], 0, max_radius) tr := clamp(radii[1], 0, max_radius)
@@ -387,13 +381,7 @@ rectangle_corners_lines :: proc(
} }
// Draw a rectangle with uniform corner rounding via SDF. // Draw a rectangle with uniform corner rounding via SDF.
rectangle_rounded :: proc( rectangle_rounded :: proc(layer: ^Layer, rect: Rectangle, roundness: f32, color: Color, soft_px: f32 = 1.0) {
layer: ^Layer,
rect: Rectangle,
roundness: f32,
color: Color,
soft_px: f32 = 1.0,
) {
cr := min(rect.w, rect.h) * clamp(roundness, 0, 1) * 0.5 cr := min(rect.w, rect.h) * clamp(roundness, 0, 1) * 0.5
if cr < 1 { if cr < 1 {
rectangle(layer, rect, color) rectangle(layer, rect, color)
@@ -425,12 +413,19 @@ circle :: proc(layer: ^Layer, center: [2]f32, radius: f32, color: Color, soft_px
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius - pad, center.y - radius - pad, bounds = {
center.x + radius + pad, center.y + radius + pad}, center.x - radius - pad,
center.y - radius - pad,
center.x + radius + pad,
center.y + radius + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.Circle, {}), kind_flags = pack_kind_flags(.Circle, {}),
} }
prim.params.circle = Circle_Params{radius = radius * dpi, soft_px = soft_px} prim.params.circle = Circle_Params {
radius = radius * dpi,
soft_px = soft_px,
}
prepare_sdf_primitive(layer, prim) prepare_sdf_primitive(layer, prim)
} }
@@ -447,35 +442,42 @@ circle_lines :: proc(
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius - pad, center.y - radius - pad, bounds = {
center.x + radius + pad, center.y + radius + pad}, center.x - radius - pad,
center.y - radius - pad,
center.x + radius + pad,
center.y + radius + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.Circle, {.Stroke}), kind_flags = pack_kind_flags(.Circle, {.Stroke}),
} }
prim.params.circle = Circle_Params { prim.params.circle = Circle_Params {
radius = radius * dpi, soft_px = soft_px, stroke_px = thick * dpi, radius = radius * dpi,
soft_px = soft_px,
stroke_px = thick * dpi,
} }
prepare_sdf_primitive(layer, prim) prepare_sdf_primitive(layer, prim)
} }
// Draw a filled ellipse via SDF. // Draw a filled ellipse via SDF.
ellipse :: proc( ellipse :: proc(layer: ^Layer, center: [2]f32, radius_h, radius_v: f32, color: Color, soft_px: f32 = 1.0) {
layer: ^Layer,
center: [2]f32,
radius_h, radius_v: f32,
color: Color,
soft_px: f32 = 1.0,
) {
pad := soft_px / GLOB.dpi_scaling pad := soft_px / GLOB.dpi_scaling
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius_h - pad, center.y - radius_v - pad, bounds = {
center.x + radius_h + pad, center.y + radius_v + pad}, center.x - radius_h - pad,
center.y - radius_v - pad,
center.x + radius_h + pad,
center.y + radius_v + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.Ellipse, {}), kind_flags = pack_kind_flags(.Ellipse, {}),
} }
prim.params.ellipse = Ellipse_Params{radii = {radius_h * dpi, radius_v * dpi}, soft_px = soft_px} prim.params.ellipse = Ellipse_Params {
radii = {radius_h * dpi, radius_v * dpi},
soft_px = soft_px,
}
prepare_sdf_primitive(layer, prim) prepare_sdf_primitive(layer, prim)
} }
@@ -494,13 +496,19 @@ ellipse_lines :: proc(
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius_h - pad, center.y - radius_v - pad, bounds = {
center.x + radius_h + pad, center.y + radius_v + pad}, center.x - radius_h - pad,
center.y - radius_v - pad,
center.x + radius_h + pad,
center.y + radius_v + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.Ellipse, {.Stroke}), kind_flags = pack_kind_flags(.Ellipse, {.Stroke}),
} }
prim.params.ellipse = Ellipse_Params { prim.params.ellipse = Ellipse_Params {
radii = {radius_h * dpi, radius_v * dpi}, soft_px = soft_px, stroke_px = thick * dpi, radii = {radius_h * dpi, radius_v * dpi},
soft_px = soft_px,
stroke_px = thick * dpi,
} }
prepare_sdf_primitive(layer, prim) prepare_sdf_primitive(layer, prim)
} }
@@ -518,8 +526,12 @@ ring :: proc(
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - outer_radius - pad, center.y - outer_radius - pad, bounds = {
center.x + outer_radius + pad, center.y + outer_radius + pad}, center.x - outer_radius - pad,
center.y - outer_radius - pad,
center.x + outer_radius + pad,
center.y + outer_radius + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.Ring_Arc, {}), kind_flags = pack_kind_flags(.Ring_Arc, {}),
} }
@@ -544,11 +556,27 @@ ring_lines :: proc(
soft_px: f32 = 1.0, soft_px: f32 = 1.0,
) { ) {
// Inner arc outline // Inner arc outline
ring(layer, center, max(0, inner_radius - thick * 0.5), inner_radius + thick * 0.5, ring(
start_angle, end_angle, color, soft_px) layer,
center,
max(0, inner_radius - thick * 0.5),
inner_radius + thick * 0.5,
start_angle,
end_angle,
color,
soft_px,
)
// Outer arc outline // Outer arc outline
ring(layer, center, max(0, outer_radius - thick * 0.5), outer_radius + thick * 0.5, ring(
start_angle, end_angle, color, soft_px) layer,
center,
max(0, outer_radius - thick * 0.5),
outer_radius + thick * 0.5,
start_angle,
end_angle,
color,
soft_px,
)
// Start cap // Start cap
start_rad := math.to_radians(start_angle) start_rad := math.to_radians(start_angle)
end_rad := math.to_radians(end_angle) end_rad := math.to_radians(end_angle)
@@ -562,13 +590,7 @@ ring_lines :: proc(
} }
// Draw a line segment via SDF. // Draw a line segment via SDF.
line :: proc( line :: proc(layer: ^Layer, start, end_pos: [2]f32, color: Color, thick: f32 = 1, soft_px: f32 = 1.0) {
layer: ^Layer,
start, end_pos: [2]f32,
color: Color,
thick: f32 = 1,
soft_px: f32 = 1.0,
) {
cap := thick * 0.5 + soft_px / GLOB.dpi_scaling cap := thick * 0.5 + soft_px / GLOB.dpi_scaling
min_x := min(start.x, end_pos.x) - cap min_x := min(start.x, end_pos.x) - cap
max_x := max(start.x, end_pos.x) + cap max_x := max(start.x, end_pos.x) + cap
@@ -595,13 +617,7 @@ line :: proc(
} }
// Draw a line strip via decomposed SDF segments. // Draw a line strip via decomposed SDF segments.
line_strip :: proc( line_strip :: proc(layer: ^Layer, points: [][2]f32, color: Color, thick: f32 = 1, soft_px: f32 = 1.0) {
layer: ^Layer,
points: [][2]f32,
color: Color,
thick: f32 = 1,
soft_px: f32 = 1.0,
) {
if len(points) < 2 do return if len(points) < 2 do return
for i in 0 ..< len(points) - 1 { for i in 0 ..< len(points) - 1 {
line(layer, points[i], points[i + 1], color, thick, soft_px) line(layer, points[i], points[i + 1], color, thick, soft_px)
@@ -623,8 +639,12 @@ poly :: proc(
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius - pad, center.y - radius - pad, bounds = {
center.x + radius + pad, center.y + radius + pad}, center.x - radius - pad,
center.y - radius - pad,
center.x + radius + pad,
center.y + radius + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.NGon, {}), kind_flags = pack_kind_flags(.NGon, {}),
} }
@@ -653,8 +673,12 @@ poly_lines :: proc(
dpi := GLOB.dpi_scaling dpi := GLOB.dpi_scaling
prim := Primitive { prim := Primitive {
bounds = {center.x - radius - pad, center.y - radius - pad, bounds = {
center.x + radius + pad, center.y + radius + pad}, center.x - radius - pad,
center.y - radius - pad,
center.x + radius + pad,
center.y + radius + pad,
},
color = color, color = color,
kind_flags = pack_kind_flags(.NGon, {.Stroke}), kind_flags = pack_kind_flags(.NGon, {.Stroke}),
} }