Massive renaming
This commit is contained in:
175
draw/draw.odin
175
draw/draw.odin
@@ -36,14 +36,14 @@ BLUE :: Color{0, 0, 255, 255}
|
|||||||
BLANK :: Color{0, 0, 0, 0}
|
BLANK :: Color{0, 0, 0, 0}
|
||||||
|
|
||||||
// Convert clay.Color ([4]c.float in 0–255 range) to Color.
|
// Convert clay.Color ([4]c.float in 0–255 range) to Color.
|
||||||
color_from_clay :: proc(cc: clay.Color) -> Color {
|
color_from_clay :: proc(clay_color: clay.Color) -> Color {
|
||||||
return Color{u8(cc[0]), u8(cc[1]), u8(cc[2]), u8(cc[3])}
|
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).
|
// Convert Color to [4]f32 in 0.0–1.0 range. Useful for SDL interop (e.g. clear color).
|
||||||
color_to_f32 :: proc(c: Color) -> [4]f32 {
|
color_to_f32 :: proc(color: Color) -> [4]f32 {
|
||||||
INV :: 1.0 / 255.0
|
INV :: 1.0 / 255.0
|
||||||
return {f32(c[0]) * INV, f32(c[1]) * INV, f32(c[2]) * INV, f32(c[3]) * INV}
|
return {f32(color[0]) * INV, f32(color[1]) * INV, f32(color[2]) * INV, f32(color[3]) * INV}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -51,10 +51,10 @@ color_to_f32 :: proc(c: Color) -> [4]f32 {
|
|||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Rectangle :: struct {
|
Rectangle :: struct {
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
w: f32,
|
width: f32,
|
||||||
h: f32,
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Sub_Batch_Kind :: enum u8 {
|
Sub_Batch_Kind :: enum u8 {
|
||||||
@@ -102,7 +102,7 @@ Global :: struct {
|
|||||||
tmp_primitives: [dynamic]Primitive,
|
tmp_primitives: [dynamic]Primitive,
|
||||||
tmp_sub_batches: [dynamic]Sub_Batch,
|
tmp_sub_batches: [dynamic]Sub_Batch,
|
||||||
tmp_uncached_text: [dynamic]^sdl_ttf.Text, // Uncached TTF_Text objects to destroy after end()
|
tmp_uncached_text: [dynamic]^sdl_ttf.Text, // Uncached TTF_Text objects to destroy after end()
|
||||||
clay_mem: [^]u8,
|
clay_memory: [^]u8,
|
||||||
msaa_texture: ^sdl.GPUTexture,
|
msaa_texture: ^sdl.GPUTexture,
|
||||||
curr_layer_index: uint,
|
curr_layer_index: uint,
|
||||||
max_layers: int,
|
max_layers: int,
|
||||||
@@ -114,8 +114,8 @@ Global :: struct {
|
|||||||
max_primitives: int,
|
max_primitives: int,
|
||||||
max_sub_batches: int,
|
max_sub_batches: int,
|
||||||
dpi_scaling: f32,
|
dpi_scaling: f32,
|
||||||
msaa_w: u32,
|
msaa_width: u32,
|
||||||
msaa_h: u32,
|
msaa_height: u32,
|
||||||
sample_count: sdl.GPUSampleCount,
|
sample_count: sdl.GPUSampleCount,
|
||||||
clay_z_index: i16,
|
clay_z_index: i16,
|
||||||
cleared: bool,
|
cleared: bool,
|
||||||
@@ -174,13 +174,13 @@ init :: proc(
|
|||||||
tmp_uncached_text = make([dynamic]^sdl_ttf.Text, 0, 16, allocator = allocator),
|
tmp_uncached_text = make([dynamic]^sdl_ttf.Text, 0, 16, allocator = allocator),
|
||||||
odin_context = odin_context,
|
odin_context = odin_context,
|
||||||
dpi_scaling = sdl.GetWindowDisplayScale(window),
|
dpi_scaling = sdl.GetWindowDisplayScale(window),
|
||||||
clay_mem = make([^]u8, min_memory_size, allocator = allocator),
|
clay_memory = make([^]u8, min_memory_size, allocator = allocator),
|
||||||
sample_count = resolved_sample_count,
|
sample_count = resolved_sample_count,
|
||||||
pipeline_2d_base = pipeline,
|
pipeline_2d_base = pipeline,
|
||||||
text_cache = text_cache,
|
text_cache = text_cache,
|
||||||
}
|
}
|
||||||
log.debug("Window DPI scaling:", GLOB.dpi_scaling)
|
log.debug("Window DPI scaling:", GLOB.dpi_scaling)
|
||||||
arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, GLOB.clay_mem)
|
arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, GLOB.clay_memory)
|
||||||
window_width, window_height: c.int
|
window_width, window_height: c.int
|
||||||
sdl.GetWindowSize(window, &window_width, &window_height)
|
sdl.GetWindowSize(window, &window_width, &window_height)
|
||||||
|
|
||||||
@@ -219,9 +219,9 @@ destroy :: proc(device: ^sdl.GPUDevice, allocator := context.allocator) {
|
|||||||
delete(GLOB.tmp_text_batches)
|
delete(GLOB.tmp_text_batches)
|
||||||
delete(GLOB.tmp_primitives)
|
delete(GLOB.tmp_primitives)
|
||||||
delete(GLOB.tmp_sub_batches)
|
delete(GLOB.tmp_sub_batches)
|
||||||
for t in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(t)
|
for ttf_text in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(ttf_text)
|
||||||
delete(GLOB.tmp_uncached_text)
|
delete(GLOB.tmp_uncached_text)
|
||||||
free(GLOB.clay_mem, allocator)
|
free(GLOB.clay_memory, allocator)
|
||||||
if GLOB.msaa_texture != nil {
|
if GLOB.msaa_texture != nil {
|
||||||
sdl.ReleaseGPUTexture(device, GLOB.msaa_texture)
|
sdl.ReleaseGPUTexture(device, GLOB.msaa_texture)
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ clear_global :: proc() {
|
|||||||
GLOB.clay_z_index = 0
|
GLOB.clay_z_index = 0
|
||||||
GLOB.cleared = false
|
GLOB.cleared = false
|
||||||
// Destroy uncached TTF_Text objects from the previous frame (after end() has submitted draw data)
|
// Destroy uncached TTF_Text objects from the previous frame (after end() has submitted draw data)
|
||||||
for t in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(t)
|
for ttf_text in GLOB.tmp_uncached_text do sdl_ttf.DestroyText(ttf_text)
|
||||||
clear(&GLOB.tmp_uncached_text)
|
clear(&GLOB.tmp_uncached_text)
|
||||||
clear(&GLOB.layers)
|
clear(&GLOB.layers)
|
||||||
clear(&GLOB.scissors)
|
clear(&GLOB.scissors)
|
||||||
@@ -260,12 +260,12 @@ measure_text_clay :: proc "c" (
|
|||||||
context = GLOB.odin_context
|
context = GLOB.odin_context
|
||||||
text := string(text.chars[:text.length])
|
text := string(text.chars[:text.length])
|
||||||
c_text := strings.clone_to_cstring(text, context.temp_allocator)
|
c_text := strings.clone_to_cstring(text, context.temp_allocator)
|
||||||
w, h: c.int
|
width, height: c.int
|
||||||
if !sdl_ttf.GetStringSize(get_font(config.fontId, config.fontSize), c_text, 0, &w, &h) {
|
if !sdl_ttf.GetStringSize(get_font(config.fontId, config.fontSize), c_text, 0, &width, &height) {
|
||||||
log.panicf("Failed to measure text: %s", sdl.GetError())
|
log.panicf("Failed to measure text: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
|
|
||||||
return clay.Dimensions{width = f32(w) / GLOB.dpi_scaling, height = f32(h) / GLOB.dpi_scaling}
|
return clay.Dimensions{width = f32(width) / GLOB.dpi_scaling, height = f32(height) / GLOB.dpi_scaling}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -282,8 +282,8 @@ begin :: proc(bounds: Rectangle) -> ^Layer {
|
|||||||
bounds = sdl.Rect {
|
bounds = sdl.Rect {
|
||||||
x = i32(bounds.x * GLOB.dpi_scaling),
|
x = i32(bounds.x * GLOB.dpi_scaling),
|
||||||
y = i32(bounds.y * GLOB.dpi_scaling),
|
y = i32(bounds.y * GLOB.dpi_scaling),
|
||||||
w = i32(bounds.w * GLOB.dpi_scaling),
|
w = i32(bounds.width * GLOB.dpi_scaling),
|
||||||
h = i32(bounds.h * GLOB.dpi_scaling),
|
h = i32(bounds.height * GLOB.dpi_scaling),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
append(&GLOB.scissors, scissor)
|
append(&GLOB.scissors, scissor)
|
||||||
@@ -313,8 +313,8 @@ new_layer :: proc(prev_layer: ^Layer, bounds: Rectangle) -> ^Layer {
|
|||||||
bounds = sdl.Rect {
|
bounds = sdl.Rect {
|
||||||
x = i32(bounds.x * GLOB.dpi_scaling),
|
x = i32(bounds.x * GLOB.dpi_scaling),
|
||||||
y = i32(bounds.y * GLOB.dpi_scaling),
|
y = i32(bounds.y * GLOB.dpi_scaling),
|
||||||
w = i32(bounds.w * GLOB.dpi_scaling),
|
w = i32(bounds.width * GLOB.dpi_scaling),
|
||||||
h = i32(bounds.h * GLOB.dpi_scaling),
|
h = i32(bounds.height * GLOB.dpi_scaling),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
append(&GLOB.scissors, scissor)
|
append(&GLOB.scissors, scissor)
|
||||||
@@ -344,8 +344,8 @@ prepare_sdf_primitive :: proc(layer: ^Layer, prim: Primitive) {
|
|||||||
|
|
||||||
// Submit a text element to the given layer for rendering.
|
// Submit a text element to the given layer for rendering.
|
||||||
// Copies SDL_ttf vertices directly (with baked position) and copies indices for indexed drawing.
|
// Copies SDL_ttf vertices directly (with baked position) and copies indices for indexed drawing.
|
||||||
prepare_text :: proc(layer: ^Layer, txt: Text) {
|
prepare_text :: proc(layer: ^Layer, text: Text) {
|
||||||
data := sdl_ttf.GetGPUTextDrawData(txt.ref)
|
data := sdl_ttf.GetGPUTextDrawData(text.sdl_text)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return // nil is normal for empty text
|
return // nil is normal for empty text
|
||||||
}
|
}
|
||||||
@@ -363,9 +363,9 @@ prepare_text :: proc(layer: ^Layer, txt: Text) {
|
|||||||
append(
|
append(
|
||||||
&GLOB.tmp_text_verts,
|
&GLOB.tmp_text_verts,
|
||||||
Vertex {
|
Vertex {
|
||||||
position = {pos.x + txt.position[0] * GLOB.dpi_scaling, -pos.y + txt.position[1] * GLOB.dpi_scaling},
|
position = {pos.x + text.position[0] * GLOB.dpi_scaling, -pos.y + text.position[1] * GLOB.dpi_scaling},
|
||||||
uv = {uv.x, uv.y},
|
uv = {uv.x, uv.y},
|
||||||
color = txt.color,
|
color = text.color,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -396,8 +396,8 @@ prepare_text :: proc(layer: ^Layer, txt: Text) {
|
|||||||
// Used by the high-level `text` proc when rotation or a non-zero origin is specified.
|
// Used by the high-level `text` proc when rotation or a non-zero origin is specified.
|
||||||
// NOTE: xform must be in physical (DPI-scaled) pixel space — the caller pre-scales
|
// NOTE: xform must be in physical (DPI-scaled) pixel space — the caller pre-scales
|
||||||
// pos and origin by GLOB.dpi_scaling before building the transform.
|
// pos and origin by GLOB.dpi_scaling before building the transform.
|
||||||
prepare_text_transformed :: proc(layer: ^Layer, txt: Text, xform: Transform_2D) {
|
prepare_text_transformed :: proc(layer: ^Layer, text: Text, transform: Transform_2D) {
|
||||||
data := sdl_ttf.GetGPUTextDrawData(txt.ref)
|
data := sdl_ttf.GetGPUTextDrawData(text.sdl_text)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -416,7 +416,7 @@ prepare_text_transformed :: proc(layer: ^Layer, txt: Text, xform: Transform_2D)
|
|||||||
// so we apply directly — no per-vertex DPI divide/multiply.
|
// so we apply directly — no per-vertex DPI divide/multiply.
|
||||||
append(
|
append(
|
||||||
&GLOB.tmp_text_verts,
|
&GLOB.tmp_text_verts,
|
||||||
Vertex{position = apply_transform(xform, {pos.x, -pos.y}), uv = {uv.x, uv.y}, color = txt.color},
|
Vertex{position = apply_transform(transform, {pos.x, -pos.y}), uv = {uv.x, uv.y}, color = text.color},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,10 +501,10 @@ prepare_clay_batch :: proc(
|
|||||||
|
|
||||||
// Translate bounding box of the primitive by the layer position
|
// Translate bounding box of the primitive by the layer position
|
||||||
bounds := Rectangle {
|
bounds := Rectangle {
|
||||||
x = render_command.boundingBox.x + layer.bounds.x,
|
x = render_command.boundingBox.x + layer.bounds.x,
|
||||||
y = render_command.boundingBox.y + layer.bounds.y,
|
y = render_command.boundingBox.y + layer.bounds.y,
|
||||||
w = render_command.boundingBox.width,
|
width = render_command.boundingBox.width,
|
||||||
h = render_command.boundingBox.height,
|
height = render_command.boundingBox.height,
|
||||||
}
|
}
|
||||||
|
|
||||||
if render_command.zIndex > GLOB.clay_z_index {
|
if render_command.zIndex > GLOB.clay_z_index {
|
||||||
@@ -532,9 +532,7 @@ prepare_clay_batch :: proc(
|
|||||||
prepare_text(layer, Text{sdl_text, {bounds.x, bounds.y}, color_from_clay(render_data.textColor)})
|
prepare_text(layer, Text{sdl_text, {bounds.x, bounds.y}, color_from_clay(render_data.textColor)})
|
||||||
case clay.RenderCommandType.Image:
|
case clay.RenderCommandType.Image:
|
||||||
case clay.RenderCommandType.ScissorStart:
|
case clay.RenderCommandType.ScissorStart:
|
||||||
if bounds.w == 0 || bounds.h == 0 {
|
if bounds.width == 0 || bounds.height == 0 do continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
curr_scissor := &GLOB.scissors[layer.scissor_start + layer.scissor_len - 1]
|
curr_scissor := &GLOB.scissors[layer.scissor_start + layer.scissor_len - 1]
|
||||||
|
|
||||||
@@ -545,8 +543,8 @@ prepare_clay_batch :: proc(
|
|||||||
bounds = sdl.Rect {
|
bounds = sdl.Rect {
|
||||||
c.int(bounds.x * GLOB.dpi_scaling),
|
c.int(bounds.x * GLOB.dpi_scaling),
|
||||||
c.int(bounds.y * GLOB.dpi_scaling),
|
c.int(bounds.y * GLOB.dpi_scaling),
|
||||||
c.int(bounds.w * GLOB.dpi_scaling),
|
c.int(bounds.width * GLOB.dpi_scaling),
|
||||||
c.int(bounds.h * GLOB.dpi_scaling),
|
c.int(bounds.height * GLOB.dpi_scaling),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
append(&GLOB.scissors, new)
|
append(&GLOB.scissors, new)
|
||||||
@@ -555,8 +553,8 @@ prepare_clay_batch :: proc(
|
|||||||
curr_scissor.bounds = sdl.Rect {
|
curr_scissor.bounds = sdl.Rect {
|
||||||
c.int(bounds.x * GLOB.dpi_scaling),
|
c.int(bounds.x * GLOB.dpi_scaling),
|
||||||
c.int(bounds.y * GLOB.dpi_scaling),
|
c.int(bounds.y * GLOB.dpi_scaling),
|
||||||
c.int(bounds.w * GLOB.dpi_scaling),
|
c.int(bounds.width * GLOB.dpi_scaling),
|
||||||
c.int(bounds.h * GLOB.dpi_scaling),
|
c.int(bounds.height * GLOB.dpi_scaling),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case clay.RenderCommandType.ScissorEnd:
|
case clay.RenderCommandType.ScissorEnd:
|
||||||
@@ -575,13 +573,13 @@ prepare_clay_batch :: proc(
|
|||||||
render_data := render_command.renderData.border
|
render_data := render_command.renderData.border
|
||||||
cr := render_data.cornerRadius
|
cr := render_data.cornerRadius
|
||||||
color := color_from_clay(render_data.color)
|
color := color_from_clay(render_data.color)
|
||||||
thick := f32(render_data.width.top)
|
thickness := f32(render_data.width.top)
|
||||||
radii := [4]f32{cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft}
|
radii := [4]f32{cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft}
|
||||||
|
|
||||||
if radii == {0, 0, 0, 0} {
|
if radii == {0, 0, 0, 0} {
|
||||||
rectangle_lines(layer, bounds, color, thick)
|
rectangle_lines(layer, bounds, color, thickness)
|
||||||
} else {
|
} else {
|
||||||
rectangle_corners_lines(layer, bounds, radii, color, thick)
|
rectangle_corners_lines(layer, bounds, radii, color, thickness)
|
||||||
}
|
}
|
||||||
case clay.RenderCommandType.Custom:
|
case clay.RenderCommandType.Custom:
|
||||||
}
|
}
|
||||||
@@ -601,8 +599,8 @@ end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = BL
|
|||||||
sdl.EndGPUCopyPass(copy_pass)
|
sdl.EndGPUCopyPass(copy_pass)
|
||||||
|
|
||||||
swapchain_texture: ^sdl.GPUTexture
|
swapchain_texture: ^sdl.GPUTexture
|
||||||
w, h: u32
|
width, height: u32
|
||||||
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &w, &h) {
|
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &width, &height) {
|
||||||
log.panicf("Failed to acquire swapchain texture: %s", sdl.GetError())
|
log.panicf("Failed to acquire swapchain texture: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,16 +616,16 @@ end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = BL
|
|||||||
render_texture := swapchain_texture
|
render_texture := swapchain_texture
|
||||||
|
|
||||||
if use_msaa {
|
if use_msaa {
|
||||||
ensure_msaa_texture(device, sdl.GetGPUSwapchainTextureFormat(device, window), w, h)
|
ensure_msaa_texture(device, sdl.GetGPUSwapchainTextureFormat(device, window), width, height)
|
||||||
render_texture = GLOB.msaa_texture
|
render_texture = GLOB.msaa_texture
|
||||||
}
|
}
|
||||||
|
|
||||||
cc := color_to_f32(clear_color)
|
clear_color_f32 := color_to_f32(clear_color)
|
||||||
|
|
||||||
// Draw layers. One render pass per layer; sub-batches draw in submission order within each scissor.
|
// Draw layers. One render pass per layer; sub-batches draw in submission order within each scissor.
|
||||||
for &layer, index in GLOB.layers {
|
for &layer, index in GLOB.layers {
|
||||||
log.debug("Drawing layer", index)
|
log.debug("Drawing layer", index)
|
||||||
draw_layer(device, window, cmd_buffer, render_texture, w, h, cc, &layer)
|
draw_layer(device, window, cmd_buffer, render_texture, width, height, clear_color_f32, &layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve MSAA render texture to the swapchain.
|
// Resolve MSAA render texture to the swapchain.
|
||||||
@@ -659,15 +657,15 @@ end :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, clear_color: Color = BL
|
|||||||
max_sample_count :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> sdl.GPUSampleCount {
|
max_sample_count :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> sdl.GPUSampleCount {
|
||||||
format := sdl.GetGPUSwapchainTextureFormat(device, window)
|
format := sdl.GetGPUSwapchainTextureFormat(device, window)
|
||||||
counts := [?]sdl.GPUSampleCount{._8, ._4, ._2}
|
counts := [?]sdl.GPUSampleCount{._8, ._4, ._2}
|
||||||
for sc in counts {
|
for count in counts {
|
||||||
if sdl.GPUTextureSupportsSampleCount(device, format, sc) do return sc
|
if sdl.GPUTextureSupportsSampleCount(device, format, count) do return count
|
||||||
}
|
}
|
||||||
return ._1
|
return ._1
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
ensure_msaa_texture :: proc(device: ^sdl.GPUDevice, format: sdl.GPUTextureFormat, w, h: u32) {
|
ensure_msaa_texture :: proc(device: ^sdl.GPUDevice, format: sdl.GPUTextureFormat, width, height: u32) {
|
||||||
if GLOB.msaa_texture != nil && GLOB.msaa_w == w && GLOB.msaa_h == h {
|
if GLOB.msaa_texture != nil && GLOB.msaa_width == width && GLOB.msaa_height == height {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if GLOB.msaa_texture != nil {
|
if GLOB.msaa_texture != nil {
|
||||||
@@ -679,18 +677,18 @@ ensure_msaa_texture :: proc(device: ^sdl.GPUDevice, format: sdl.GPUTextureFormat
|
|||||||
type = .D2,
|
type = .D2,
|
||||||
format = format,
|
format = format,
|
||||||
usage = {.COLOR_TARGET},
|
usage = {.COLOR_TARGET},
|
||||||
width = w,
|
width = width,
|
||||||
height = h,
|
height = height,
|
||||||
layer_count_or_depth = 1,
|
layer_count_or_depth = 1,
|
||||||
num_levels = 1,
|
num_levels = 1,
|
||||||
sample_count = GLOB.sample_count,
|
sample_count = GLOB.sample_count,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if GLOB.msaa_texture == nil {
|
if GLOB.msaa_texture == nil {
|
||||||
log.panicf("Failed to create MSAA texture (%dx%d): %s", w, h, sdl.GetError())
|
log.panicf("Failed to create MSAA texture (%dx%d): %s", width, height, sdl.GetError())
|
||||||
}
|
}
|
||||||
GLOB.msaa_w = w
|
GLOB.msaa_width = width
|
||||||
GLOB.msaa_h = h
|
GLOB.msaa_height = height
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -718,9 +716,21 @@ Vertex_Uniforms :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push projection, dpi scale, and rendering mode as a single uniform block (slot 0).
|
// Push projection, dpi scale, and rendering mode as a single uniform block (slot 0).
|
||||||
push_globals :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, w: f32, h: f32, mode: Draw_Mode = .Tessellated) {
|
push_globals :: proc(
|
||||||
|
cmd_buffer: ^sdl.GPUCommandBuffer,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
mode: Draw_Mode = .Tessellated,
|
||||||
|
) {
|
||||||
globals := Vertex_Uniforms {
|
globals := Vertex_Uniforms {
|
||||||
projection = ortho_rh(left = 0.0, top = 0.0, right = f32(w), bottom = f32(h), near = -1.0, far = 1.0),
|
projection = ortho_rh(
|
||||||
|
left = 0.0,
|
||||||
|
top = 0.0,
|
||||||
|
right = f32(width),
|
||||||
|
bottom = f32(height),
|
||||||
|
near = -1.0,
|
||||||
|
far = 1.0,
|
||||||
|
),
|
||||||
scale = GLOB.dpi_scaling,
|
scale = GLOB.dpi_scaling,
|
||||||
mode = mode,
|
mode = mode,
|
||||||
}
|
}
|
||||||
@@ -818,23 +828,26 @@ Transform_2D :: struct {
|
|||||||
// origin – pivot point in local space (measured from the shape's natural reference point).
|
// origin – pivot point in local space (measured from the shape's natural reference point).
|
||||||
// rotation_deg – rotation in degrees, counter-clockwise.
|
// rotation_deg – rotation in degrees, counter-clockwise.
|
||||||
//
|
//
|
||||||
build_pivot_rot :: proc(pos: [2]f32, origin: [2]f32, rotation_deg: f32) -> Transform_2D {
|
build_pivot_rotation :: proc(position: [2]f32, origin: [2]f32, rotation_deg: f32) -> Transform_2D {
|
||||||
rad := math.to_radians(rotation_deg)
|
radians := math.to_radians(rotation_deg)
|
||||||
c := math.cos(rad)
|
cos_angle := math.cos(radians)
|
||||||
s := math.sin(rad)
|
sin_angle := math.sin(radians)
|
||||||
return Transform_2D {
|
return Transform_2D {
|
||||||
m00 = c,
|
m00 = cos_angle,
|
||||||
m01 = -s,
|
m01 = -sin_angle,
|
||||||
m10 = s,
|
m10 = sin_angle,
|
||||||
m11 = c,
|
m11 = cos_angle,
|
||||||
tx = pos.x - (c * origin.x - s * origin.y),
|
tx = position.x - (cos_angle * origin.x - sin_angle * origin.y),
|
||||||
ty = pos.y - (s * origin.x + c * origin.y),
|
ty = position.y - (sin_angle * origin.x + cos_angle * origin.y),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the transform to a local-space point, producing a world-space point.
|
// Apply the transform to a local-space point, producing a world-space point.
|
||||||
apply_transform :: #force_inline proc(t: Transform_2D, p: [2]f32) -> [2]f32 {
|
apply_transform :: #force_inline proc(transform: Transform_2D, point: [2]f32) -> [2]f32 {
|
||||||
return {t.m00 * p.x + t.m01 * p.y + t.tx, t.m10 * p.x + t.m11 * p.y + t.ty}
|
return {
|
||||||
|
transform.m00 * point.x + transform.m01 * point.y + transform.tx,
|
||||||
|
transform.m10 * point.x + transform.m11 * point.y + transform.ty,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast-path check callers use BEFORE building a transform.
|
// Fast-path check callers use BEFORE building a transform.
|
||||||
@@ -849,55 +862,55 @@ needs_transform :: #force_inline proc(origin: [2]f32, rotation: f32) -> bool {
|
|||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
center_of :: proc {
|
center_of :: proc {
|
||||||
center_of_rect,
|
center_of_rectangle,
|
||||||
center_of_triangle,
|
center_of_triangle,
|
||||||
center_of_text,
|
center_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
top_left_of :: proc {
|
top_left_of :: proc {
|
||||||
top_left_of_rect,
|
top_left_of_rectangle,
|
||||||
top_left_of_triangle,
|
top_left_of_triangle,
|
||||||
top_left_of_text,
|
top_left_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
top_of :: proc {
|
top_of :: proc {
|
||||||
top_of_rect,
|
top_of_rectangle,
|
||||||
top_of_triangle,
|
top_of_triangle,
|
||||||
top_of_text,
|
top_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
top_right_of :: proc {
|
top_right_of :: proc {
|
||||||
top_right_of_rect,
|
top_right_of_rectangle,
|
||||||
top_right_of_triangle,
|
top_right_of_triangle,
|
||||||
top_right_of_text,
|
top_right_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
left_of :: proc {
|
left_of :: proc {
|
||||||
left_of_rect,
|
left_of_rectangle,
|
||||||
left_of_triangle,
|
left_of_triangle,
|
||||||
left_of_text,
|
left_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
right_of :: proc {
|
right_of :: proc {
|
||||||
right_of_rect,
|
right_of_rectangle,
|
||||||
right_of_triangle,
|
right_of_triangle,
|
||||||
right_of_text,
|
right_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_left_of :: proc {
|
bottom_left_of :: proc {
|
||||||
bottom_left_of_rect,
|
bottom_left_of_rectangle,
|
||||||
bottom_left_of_triangle,
|
bottom_left_of_triangle,
|
||||||
bottom_left_of_text,
|
bottom_left_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_of :: proc {
|
bottom_of :: proc {
|
||||||
bottom_of_rect,
|
bottom_of_rectangle,
|
||||||
bottom_of_triangle,
|
bottom_of_triangle,
|
||||||
bottom_of_text,
|
bottom_of_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_right_of :: proc {
|
bottom_right_of :: proc {
|
||||||
bottom_right_of_rect,
|
bottom_right_of_rectangle,
|
||||||
bottom_right_of_triangle,
|
bottom_right_of_triangle,
|
||||||
bottom_right_of_text,
|
bottom_right_of_text,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ hellope_shapes :: proc() {
|
|||||||
if ev.type == .QUIT do return
|
if ev.type == .QUIT do return
|
||||||
}
|
}
|
||||||
spin_angle += 1
|
spin_angle += 1
|
||||||
base_layer := draw.begin({w = 500, h = 500})
|
base_layer := draw.begin({width = 500, height = 500})
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
draw.rectangle(base_layer, {0, 0, 500, 500}, {40, 40, 40, 255})
|
draw.rectangle(base_layer, {0, 0, 500, 500}, {40, 40, 40, 255})
|
||||||
|
|
||||||
// ----- Shapes without rotation (existing demo) -----
|
// ----- Shapes without rotation (existing demo) -----
|
||||||
draw.rectangle(base_layer, {20, 20, 200, 120}, {80, 120, 200, 255})
|
draw.rectangle(base_layer, {20, 20, 200, 120}, {80, 120, 200, 255})
|
||||||
draw.rectangle_lines(base_layer, {20, 20, 200, 120}, draw.WHITE, thick = 2)
|
draw.rectangle_lines(base_layer, {20, 20, 200, 120}, draw.WHITE, thickness = 2)
|
||||||
draw.rectangle(base_layer, {240, 20, 240, 120}, {200, 80, 80, 255}, roundness = 0.3)
|
draw.rectangle(base_layer, {240, 20, 240, 120}, {200, 80, 80, 255}, roundness = 0.3)
|
||||||
draw.rectangle_gradient(
|
draw.rectangle_gradient(
|
||||||
base_layer,
|
base_layer,
|
||||||
@@ -57,7 +57,7 @@ hellope_shapes :: proc() {
|
|||||||
base_layer,
|
base_layer,
|
||||||
rect,
|
rect,
|
||||||
draw.WHITE,
|
draw.WHITE,
|
||||||
thick = 2,
|
thickness = 2,
|
||||||
origin = draw.center_of(rect),
|
origin = draw.center_of(rect),
|
||||||
rotation = spin_angle,
|
rotation = spin_angle,
|
||||||
)
|
)
|
||||||
@@ -100,8 +100,8 @@ hellope_shapes :: proc() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Polygon rotating around its center (already had rotation; now with origin for orbit)
|
// Polygon rotating around its center (already had rotation; now with origin for orbit)
|
||||||
draw.poly(base_layer, {460, 450}, 6, 30, {180, 100, 220, 255}, rotation = spin_angle)
|
draw.polygon(base_layer, {460, 450}, 6, 30, {180, 100, 220, 255}, rotation = spin_angle)
|
||||||
draw.poly_lines(base_layer, {460, 450}, 6, 30, draw.WHITE, rotation = spin_angle, thick = 2)
|
draw.polygon_lines(base_layer, {460, 450}, 6, 30, draw.WHITE, rotation = spin_angle, thickness = 2)
|
||||||
|
|
||||||
draw.end(gpu, window)
|
draw.end(gpu, window)
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ hellope_text :: proc() {
|
|||||||
if ev.type == .QUIT do return
|
if ev.type == .QUIT do return
|
||||||
}
|
}
|
||||||
spin_angle += 0.5
|
spin_angle += 0.5
|
||||||
base_layer := draw.begin({w = 600, h = 600})
|
base_layer := draw.begin({width = 600, height = 600})
|
||||||
|
|
||||||
// Grey background
|
// Grey background
|
||||||
draw.rectangle(base_layer, {0, 0, 600, 600}, {127, 127, 127, 255})
|
draw.rectangle(base_layer, {0, 0, 600, 600}, {127, 127, 127, 255})
|
||||||
@@ -217,8 +217,8 @@ hellope_clay :: proc() {
|
|||||||
for sdl.PollEvent(&ev) {
|
for sdl.PollEvent(&ev) {
|
||||||
if ev.type == .QUIT do return
|
if ev.type == .QUIT do return
|
||||||
}
|
}
|
||||||
base_layer := draw.begin({w = 500, h = 500})
|
base_layer := draw.begin({width = 500, height = 500})
|
||||||
clay.SetLayoutDimensions({width = base_layer.bounds.w, height = base_layer.bounds.h})
|
clay.SetLayoutDimensions({width = base_layer.bounds.width, height = base_layer.bounds.height})
|
||||||
clay.BeginLayout()
|
clay.BeginLayout()
|
||||||
if clay.UI()(
|
if clay.UI()(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -240,31 +240,31 @@ create_pipeline_2d_base :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create vertex buffer
|
// Create vertex buffer
|
||||||
vb_ok: bool
|
vert_buf_ok: bool
|
||||||
pipeline.vertex_buffer, vb_ok = create_buffer(
|
pipeline.vertex_buffer, vert_buf_ok = create_buffer(
|
||||||
device,
|
device,
|
||||||
size_of(Vertex) * BUFFER_INIT_SIZE,
|
size_of(Vertex) * BUFFER_INIT_SIZE,
|
||||||
sdl.GPUBufferUsageFlags{.VERTEX},
|
sdl.GPUBufferUsageFlags{.VERTEX},
|
||||||
)
|
)
|
||||||
if !vb_ok do return pipeline, false
|
if !vert_buf_ok do return pipeline, false
|
||||||
|
|
||||||
// Create index buffer (used by text)
|
// Create index buffer (used by text)
|
||||||
ib_ok: bool
|
idx_buf_ok: bool
|
||||||
pipeline.index_buffer, ib_ok = create_buffer(
|
pipeline.index_buffer, idx_buf_ok = create_buffer(
|
||||||
device,
|
device,
|
||||||
size_of(c.int) * BUFFER_INIT_SIZE,
|
size_of(c.int) * BUFFER_INIT_SIZE,
|
||||||
sdl.GPUBufferUsageFlags{.INDEX},
|
sdl.GPUBufferUsageFlags{.INDEX},
|
||||||
)
|
)
|
||||||
if !ib_ok do return pipeline, false
|
if !idx_buf_ok do return pipeline, false
|
||||||
|
|
||||||
// Create primitive storage buffer (used by SDF instanced drawing)
|
// Create primitive storage buffer (used by SDF instanced drawing)
|
||||||
pb_ok: bool
|
prim_buf_ok: bool
|
||||||
pipeline.primitive_buffer, pb_ok = create_buffer(
|
pipeline.primitive_buffer, prim_buf_ok = create_buffer(
|
||||||
device,
|
device,
|
||||||
size_of(Primitive) * BUFFER_INIT_SIZE,
|
size_of(Primitive) * BUFFER_INIT_SIZE,
|
||||||
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
||||||
)
|
)
|
||||||
if !pb_ok do return pipeline, false
|
if !prim_buf_ok do return pipeline, false
|
||||||
|
|
||||||
// Create static 6-vertex unit quad buffer (two triangles, TRIANGLELIST)
|
// Create static 6-vertex unit quad buffer (two triangles, TRIANGLELIST)
|
||||||
pipeline.unit_quad_buffer = sdl.CreateGPUBuffer(
|
pipeline.unit_quad_buffer = sdl.CreateGPUBuffer(
|
||||||
@@ -297,23 +297,23 @@ create_pipeline_2d_base :: proc(
|
|||||||
|
|
||||||
// Upload white pixel and unit quad data in a single command buffer
|
// Upload white pixel and unit quad data in a single command buffer
|
||||||
white_pixel := [4]u8{255, 255, 255, 255}
|
white_pixel := [4]u8{255, 255, 255, 255}
|
||||||
white_transfer := sdl.CreateGPUTransferBuffer(
|
white_transfer_buf := sdl.CreateGPUTransferBuffer(
|
||||||
device,
|
device,
|
||||||
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(white_pixel)},
|
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(white_pixel)},
|
||||||
)
|
)
|
||||||
if white_transfer == nil {
|
if white_transfer_buf == nil {
|
||||||
log.errorf("Failed to create white pixel transfer buffer: %s", sdl.GetError())
|
log.errorf("Failed to create white pixel transfer buffer: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
defer sdl.ReleaseGPUTransferBuffer(device, white_transfer)
|
defer sdl.ReleaseGPUTransferBuffer(device, white_transfer_buf)
|
||||||
|
|
||||||
white_ptr := sdl.MapGPUTransferBuffer(device, white_transfer, false)
|
white_ptr := sdl.MapGPUTransferBuffer(device, white_transfer_buf, false)
|
||||||
if white_ptr == nil {
|
if white_ptr == nil {
|
||||||
log.errorf("Failed to map white pixel transfer buffer: %s", sdl.GetError())
|
log.errorf("Failed to map white pixel transfer buffer: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
mem.copy(white_ptr, &white_pixel, size_of(white_pixel))
|
mem.copy(white_ptr, &white_pixel, size_of(white_pixel))
|
||||||
sdl.UnmapGPUTransferBuffer(device, white_transfer)
|
sdl.UnmapGPUTransferBuffer(device, white_transfer_buf)
|
||||||
|
|
||||||
quad_verts := [6]Vertex {
|
quad_verts := [6]Vertex {
|
||||||
{position = {0, 0}},
|
{position = {0, 0}},
|
||||||
@@ -323,47 +323,47 @@ create_pipeline_2d_base :: proc(
|
|||||||
{position = {1, 0}},
|
{position = {1, 0}},
|
||||||
{position = {1, 1}},
|
{position = {1, 1}},
|
||||||
}
|
}
|
||||||
quad_transfer := sdl.CreateGPUTransferBuffer(
|
quad_transfer_buf := sdl.CreateGPUTransferBuffer(
|
||||||
device,
|
device,
|
||||||
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(quad_verts)},
|
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(quad_verts)},
|
||||||
)
|
)
|
||||||
if quad_transfer == nil {
|
if quad_transfer_buf == nil {
|
||||||
log.errorf("Failed to create unit quad transfer buffer: %s", sdl.GetError())
|
log.errorf("Failed to create unit quad transfer buffer: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
defer sdl.ReleaseGPUTransferBuffer(device, quad_transfer)
|
defer sdl.ReleaseGPUTransferBuffer(device, quad_transfer_buf)
|
||||||
|
|
||||||
quad_ptr := sdl.MapGPUTransferBuffer(device, quad_transfer, false)
|
quad_ptr := sdl.MapGPUTransferBuffer(device, quad_transfer_buf, false)
|
||||||
if quad_ptr == nil {
|
if quad_ptr == nil {
|
||||||
log.errorf("Failed to map unit quad transfer buffer: %s", sdl.GetError())
|
log.errorf("Failed to map unit quad transfer buffer: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
mem.copy(quad_ptr, &quad_verts, size_of(quad_verts))
|
mem.copy(quad_ptr, &quad_verts, size_of(quad_verts))
|
||||||
sdl.UnmapGPUTransferBuffer(device, quad_transfer)
|
sdl.UnmapGPUTransferBuffer(device, quad_transfer_buf)
|
||||||
|
|
||||||
upload_cmd := sdl.AcquireGPUCommandBuffer(device)
|
upload_cmd_buffer := sdl.AcquireGPUCommandBuffer(device)
|
||||||
if upload_cmd == nil {
|
if upload_cmd_buffer == nil {
|
||||||
log.errorf("Failed to acquire command buffer for init upload: %s", sdl.GetError())
|
log.errorf("Failed to acquire command buffer for init upload: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
upload_pass := sdl.BeginGPUCopyPass(upload_cmd)
|
upload_pass := sdl.BeginGPUCopyPass(upload_cmd_buffer)
|
||||||
|
|
||||||
sdl.UploadToGPUTexture(
|
sdl.UploadToGPUTexture(
|
||||||
upload_pass,
|
upload_pass,
|
||||||
sdl.GPUTextureTransferInfo{transfer_buffer = white_transfer},
|
sdl.GPUTextureTransferInfo{transfer_buffer = white_transfer_buf},
|
||||||
sdl.GPUTextureRegion{texture = pipeline.white_texture, w = 1, h = 1, d = 1},
|
sdl.GPUTextureRegion{texture = pipeline.white_texture, w = 1, h = 1, d = 1},
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
sdl.UploadToGPUBuffer(
|
sdl.UploadToGPUBuffer(
|
||||||
upload_pass,
|
upload_pass,
|
||||||
sdl.GPUTransferBufferLocation{transfer_buffer = quad_transfer},
|
sdl.GPUTransferBufferLocation{transfer_buffer = quad_transfer_buf},
|
||||||
sdl.GPUBufferRegion{buffer = pipeline.unit_quad_buffer, offset = 0, size = size_of(quad_verts)},
|
sdl.GPUBufferRegion{buffer = pipeline.unit_quad_buffer, offset = 0, size = size_of(quad_verts)},
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
sdl.EndGPUCopyPass(upload_pass)
|
sdl.EndGPUCopyPass(upload_pass)
|
||||||
if !sdl.SubmitGPUCommandBuffer(upload_cmd) {
|
if !sdl.SubmitGPUCommandBuffer(upload_cmd_buffer) {
|
||||||
log.errorf("Failed to submit init upload command buffer: %s", sdl.GetError())
|
log.errorf("Failed to submit init upload command buffer: %s", sdl.GetError())
|
||||||
return pipeline, false
|
return pipeline, false
|
||||||
}
|
}
|
||||||
@@ -410,16 +410,16 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
|||||||
sdl.GPUBufferUsageFlags{.VERTEX},
|
sdl.GPUBufferUsageFlags{.VERTEX},
|
||||||
)
|
)
|
||||||
|
|
||||||
v_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.vertex_buffer.transfer, false)
|
vert_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.vertex_buffer.transfer, false)
|
||||||
if v_array == nil {
|
if vert_array == nil {
|
||||||
log.panicf("Failed to map vertex transfer buffer: %s", sdl.GetError())
|
log.panicf("Failed to map vertex transfer buffer: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
if shape_vert_size > 0 {
|
if shape_vert_size > 0 {
|
||||||
mem.copy(v_array, raw_data(GLOB.tmp_shape_verts), int(shape_vert_size))
|
mem.copy(vert_array, raw_data(GLOB.tmp_shape_verts), int(shape_vert_size))
|
||||||
}
|
}
|
||||||
if text_vert_size > 0 {
|
if text_vert_size > 0 {
|
||||||
mem.copy(
|
mem.copy(
|
||||||
rawptr(uintptr(v_array) + uintptr(shape_vert_size)),
|
rawptr(uintptr(vert_array) + uintptr(shape_vert_size)),
|
||||||
raw_data(GLOB.tmp_text_verts),
|
raw_data(GLOB.tmp_text_verts),
|
||||||
int(text_vert_size),
|
int(text_vert_size),
|
||||||
)
|
)
|
||||||
@@ -446,11 +446,11 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
|||||||
sdl.GPUBufferUsageFlags{.INDEX},
|
sdl.GPUBufferUsageFlags{.INDEX},
|
||||||
)
|
)
|
||||||
|
|
||||||
i_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer, false)
|
idx_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer, false)
|
||||||
if i_array == nil {
|
if idx_array == nil {
|
||||||
log.panicf("Failed to map index transfer buffer: %s", sdl.GetError())
|
log.panicf("Failed to map index transfer buffer: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
mem.copy(i_array, raw_data(GLOB.tmp_text_indices), int(index_size))
|
mem.copy(idx_array, raw_data(GLOB.tmp_text_indices), int(index_size))
|
||||||
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer)
|
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer)
|
||||||
|
|
||||||
sdl.UploadToGPUBuffer(
|
sdl.UploadToGPUBuffer(
|
||||||
@@ -473,11 +473,11 @@ upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
|||||||
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
||||||
)
|
)
|
||||||
|
|
||||||
p_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer, false)
|
prim_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer, false)
|
||||||
if p_array == nil {
|
if prim_array == nil {
|
||||||
log.panicf("Failed to map primitive transfer buffer: %s", sdl.GetError())
|
log.panicf("Failed to map primitive transfer buffer: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
mem.copy(p_array, raw_data(GLOB.tmp_primitives), int(prim_size))
|
mem.copy(prim_array, raw_data(GLOB.tmp_primitives), int(prim_size))
|
||||||
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer)
|
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer)
|
||||||
|
|
||||||
sdl.UploadToGPUBuffer(
|
sdl.UploadToGPUBuffer(
|
||||||
@@ -495,8 +495,8 @@ draw_layer :: proc(
|
|||||||
window: ^sdl.Window,
|
window: ^sdl.Window,
|
||||||
cmd_buffer: ^sdl.GPUCommandBuffer,
|
cmd_buffer: ^sdl.GPUCommandBuffer,
|
||||||
render_texture: ^sdl.GPUTexture,
|
render_texture: ^sdl.GPUTexture,
|
||||||
swapchain_w: u32,
|
swapchain_width: u32,
|
||||||
swapchain_h: u32,
|
swapchain_height: u32,
|
||||||
clear_color: [4]f32,
|
clear_color: [4]f32,
|
||||||
layer: ^Layer,
|
layer: ^Layer,
|
||||||
) {
|
) {
|
||||||
@@ -550,19 +550,19 @@ draw_layer :: proc(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Shorthand aliases for frequently-used pipeline resources
|
// Shorthand aliases for frequently-used pipeline resources
|
||||||
main_vbuf := GLOB.pipeline_2d_base.vertex_buffer.gpu
|
main_vert_buf := GLOB.pipeline_2d_base.vertex_buffer.gpu
|
||||||
unit_quad := GLOB.pipeline_2d_base.unit_quad_buffer
|
unit_quad := GLOB.pipeline_2d_base.unit_quad_buffer
|
||||||
white := GLOB.pipeline_2d_base.white_texture
|
white_texture := GLOB.pipeline_2d_base.white_texture
|
||||||
sampler := GLOB.pipeline_2d_base.sampler
|
sampler := GLOB.pipeline_2d_base.sampler
|
||||||
w := f32(swapchain_w)
|
width := f32(swapchain_width)
|
||||||
h := f32(swapchain_h)
|
height := f32(swapchain_height)
|
||||||
|
|
||||||
// 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, width, height, .Tessellated)
|
||||||
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
|
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vert_buf, offset = 0}, 1)
|
||||||
|
|
||||||
current_mode: Draw_Mode = .Tessellated
|
current_mode: Draw_Mode = .Tessellated
|
||||||
current_vbuf := main_vbuf
|
current_vert_buf := main_vert_buf
|
||||||
current_atlas: ^sdl.GPUTexture
|
current_atlas: ^sdl.GPUTexture
|
||||||
|
|
||||||
// Text vertices live after shape vertices in the GPU vertex buffer
|
// Text vertices live after shape vertices in the GPU vertex buffer
|
||||||
@@ -575,69 +575,69 @@ draw_layer :: proc(
|
|||||||
switch batch.kind {
|
switch batch.kind {
|
||||||
case .Shapes:
|
case .Shapes:
|
||||||
if current_mode != .Tessellated {
|
if current_mode != .Tessellated {
|
||||||
push_globals(cmd_buffer, w, h, .Tessellated)
|
push_globals(cmd_buffer, width, height, .Tessellated)
|
||||||
current_mode = .Tessellated
|
current_mode = .Tessellated
|
||||||
}
|
}
|
||||||
if current_vbuf != main_vbuf {
|
if current_vert_buf != main_vert_buf {
|
||||||
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
|
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vert_buf, offset = 0}, 1)
|
||||||
current_vbuf = main_vbuf
|
current_vert_buf = main_vert_buf
|
||||||
}
|
}
|
||||||
if current_atlas != white {
|
if current_atlas != white_texture {
|
||||||
sdl.BindGPUFragmentSamplers(
|
sdl.BindGPUFragmentSamplers(
|
||||||
render_pass,
|
render_pass,
|
||||||
0,
|
0,
|
||||||
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler},
|
&sdl.GPUTextureSamplerBinding{texture = white_texture, sampler = sampler},
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
current_atlas = white
|
current_atlas = white_texture
|
||||||
}
|
}
|
||||||
sdl.DrawGPUPrimitives(render_pass, batch.count, 1, batch.offset, 0)
|
sdl.DrawGPUPrimitives(render_pass, batch.count, 1, batch.offset, 0)
|
||||||
|
|
||||||
case .Text:
|
case .Text:
|
||||||
if current_mode != .Tessellated {
|
if current_mode != .Tessellated {
|
||||||
push_globals(cmd_buffer, w, h, .Tessellated)
|
push_globals(cmd_buffer, width, height, .Tessellated)
|
||||||
current_mode = .Tessellated
|
current_mode = .Tessellated
|
||||||
}
|
}
|
||||||
if current_vbuf != main_vbuf {
|
if current_vert_buf != main_vert_buf {
|
||||||
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1)
|
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vert_buf, offset = 0}, 1)
|
||||||
current_vbuf = main_vbuf
|
current_vert_buf = main_vert_buf
|
||||||
}
|
}
|
||||||
chunk := &GLOB.tmp_text_batches[batch.offset]
|
text_batch := &GLOB.tmp_text_batches[batch.offset]
|
||||||
if current_atlas != chunk.atlas_texture {
|
if current_atlas != text_batch.atlas_texture {
|
||||||
sdl.BindGPUFragmentSamplers(
|
sdl.BindGPUFragmentSamplers(
|
||||||
render_pass,
|
render_pass,
|
||||||
0,
|
0,
|
||||||
&sdl.GPUTextureSamplerBinding{texture = chunk.atlas_texture, sampler = sampler},
|
&sdl.GPUTextureSamplerBinding{texture = text_batch.atlas_texture, sampler = sampler},
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
current_atlas = chunk.atlas_texture
|
current_atlas = text_batch.atlas_texture
|
||||||
}
|
}
|
||||||
sdl.DrawGPUIndexedPrimitives(
|
sdl.DrawGPUIndexedPrimitives(
|
||||||
render_pass,
|
render_pass,
|
||||||
chunk.index_count,
|
text_batch.index_count,
|
||||||
1,
|
1,
|
||||||
chunk.index_start,
|
text_batch.index_start,
|
||||||
i32(text_vertex_gpu_base + chunk.vertex_start),
|
i32(text_vertex_gpu_base + text_batch.vertex_start),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
case .SDF:
|
case .SDF:
|
||||||
if current_mode != .SDF {
|
if current_mode != .SDF {
|
||||||
push_globals(cmd_buffer, w, h, .SDF)
|
push_globals(cmd_buffer, width, height, .SDF)
|
||||||
current_mode = .SDF
|
current_mode = .SDF
|
||||||
}
|
}
|
||||||
if current_vbuf != unit_quad {
|
if current_vert_buf != unit_quad {
|
||||||
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = unit_quad, offset = 0}, 1)
|
sdl.BindGPUVertexBuffers(render_pass, 0, &sdl.GPUBufferBinding{buffer = unit_quad, offset = 0}, 1)
|
||||||
current_vbuf = unit_quad
|
current_vert_buf = unit_quad
|
||||||
}
|
}
|
||||||
if current_atlas != white {
|
if current_atlas != white_texture {
|
||||||
sdl.BindGPUFragmentSamplers(
|
sdl.BindGPUFragmentSamplers(
|
||||||
render_pass,
|
render_pass,
|
||||||
0,
|
0,
|
||||||
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler},
|
&sdl.GPUTextureSamplerBinding{texture = white_texture, sampler = sampler},
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
current_atlas = white
|
current_atlas = white_texture
|
||||||
}
|
}
|
||||||
sdl.DrawGPUPrimitives(render_pass, 6, batch.count, 0, batch.offset)
|
sdl.DrawGPUPrimitives(render_pass, 6, batch.count, 0, batch.offset)
|
||||||
}
|
}
|
||||||
|
|||||||
668
draw/shapes.odin
668
draw/shapes.odin
File diff suppressed because it is too large
Load Diff
@@ -69,7 +69,7 @@ register_font :: proc(bytes: []u8) -> (id: Font_Id, ok: bool) #optional_ok {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text :: struct {
|
Text :: struct {
|
||||||
ref: ^sdl_ttf.Text,
|
sdl_text: ^sdl_ttf.Text,
|
||||||
position: [2]f32,
|
position: [2]f32,
|
||||||
color: Color,
|
color: Color,
|
||||||
}
|
}
|
||||||
@@ -81,8 +81,8 @@ Text :: struct {
|
|||||||
// Hash a string to a u32 cache key using the same Jenkins one-at-a-time algorithm as Clay.
|
// Hash a string to a u32 cache key using the same Jenkins one-at-a-time algorithm as Clay.
|
||||||
// This means Clay element IDs and user-chosen string IDs share the same keyspace — same
|
// This means Clay element IDs and user-chosen string IDs share the same keyspace — same
|
||||||
// string produces the same cache key regardless of whether it came from Clay or user code.
|
// string produces the same cache key regardless of whether it came from Clay or user code.
|
||||||
text_cache_hash :: #force_inline proc(s: string) -> u32 {
|
text_cache_hash :: #force_inline proc(text_string: string) -> u32 {
|
||||||
return hash.jenkins(transmute([]u8)s) + 1 // +1 reserves 0 as "no entry" (matches Clay convention)
|
return hash.jenkins(transmute([]u8)text_string) + 1 // +1 reserves 0 as "no entry" (matches Clay convention)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared cache lookup/create/update logic used by both the `text` proc and the Clay render path.
|
// Shared cache lookup/create/update logic used by both the `text` proc and the Clay render path.
|
||||||
@@ -125,8 +125,8 @@ cache_get_or_update :: proc(cache_id: u32, c_str: cstring, font: ^sdl_ttf.Font)
|
|||||||
// `rotation` is in degrees, counter-clockwise.
|
// `rotation` is in degrees, counter-clockwise.
|
||||||
text :: proc(
|
text :: proc(
|
||||||
layer: ^Layer,
|
layer: ^Layer,
|
||||||
str: string,
|
text_string: string,
|
||||||
pos: [2]f32,
|
position: [2]f32,
|
||||||
font_id: Font_Id,
|
font_id: Font_Id,
|
||||||
font_size: u16 = 44,
|
font_size: u16 = 44,
|
||||||
color: Color = BLACK,
|
color: Color = BLACK,
|
||||||
@@ -135,14 +135,14 @@ text :: proc(
|
|||||||
id: Maybe(string) = nil,
|
id: Maybe(string) = nil,
|
||||||
temp_allocator := context.temp_allocator,
|
temp_allocator := context.temp_allocator,
|
||||||
) {
|
) {
|
||||||
c_str := strings.clone_to_cstring(str, temp_allocator)
|
c_str := strings.clone_to_cstring(text_string, temp_allocator)
|
||||||
|
|
||||||
sdl_text: ^sdl_ttf.Text
|
sdl_text: ^sdl_ttf.Text
|
||||||
cached := false
|
cached := false
|
||||||
|
|
||||||
if id_str, ok := id.?; ok {
|
if id_string, ok := id.?; ok {
|
||||||
cached = true
|
cached = true
|
||||||
sdl_text = cache_get_or_update(text_cache_hash(id_str), c_str, get_font(font_id, font_size))
|
sdl_text = cache_get_or_update(text_cache_hash(id_string), c_str, get_font(font_id, font_size))
|
||||||
} else {
|
} else {
|
||||||
sdl_text = sdl_ttf.CreateText(GLOB.text_cache.engine, get_font(font_id, font_size), c_str, 0)
|
sdl_text = sdl_ttf.CreateText(GLOB.text_cache.engine, get_font(font_id, font_size), c_str, 0)
|
||||||
if sdl_text == nil {
|
if sdl_text == nil {
|
||||||
@@ -151,11 +151,11 @@ text :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if needs_transform(origin, rotation) {
|
if needs_transform(origin, rotation) {
|
||||||
dpi := GLOB.dpi_scaling
|
dpi_scale := GLOB.dpi_scaling
|
||||||
xform := build_pivot_rot(pos * dpi, origin * dpi, rotation)
|
transform := build_pivot_rotation(position * dpi_scale, origin * dpi_scale, rotation)
|
||||||
prepare_text_transformed(layer, Text{sdl_text, {0, 0}, color}, xform)
|
prepare_text_transformed(layer, Text{sdl_text, {0, 0}, color}, transform)
|
||||||
} else {
|
} else {
|
||||||
prepare_text(layer, Text{sdl_text, pos, color})
|
prepare_text(layer, Text{sdl_text, position, color})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cached {
|
if !cached {
|
||||||
@@ -171,64 +171,64 @@ text :: proc(
|
|||||||
|
|
||||||
// Measure a string in logical pixels (pre-DPI-scaling) using the same font backend as the renderer.
|
// Measure a string in logical pixels (pre-DPI-scaling) using the same font backend as the renderer.
|
||||||
measure_text :: proc(
|
measure_text :: proc(
|
||||||
str: string,
|
text_string: string,
|
||||||
font_id: Font_Id,
|
font_id: Font_Id,
|
||||||
font_size: u16 = 44,
|
font_size: u16 = 44,
|
||||||
allocator := context.temp_allocator,
|
allocator := context.temp_allocator,
|
||||||
) -> [2]f32 {
|
) -> [2]f32 {
|
||||||
c_str := strings.clone_to_cstring(str, allocator)
|
c_str := strings.clone_to_cstring(text_string, allocator)
|
||||||
w, h: c.int
|
width, height: c.int
|
||||||
if !sdl_ttf.GetStringSize(get_font(font_id, font_size), c_str, 0, &w, &h) {
|
if !sdl_ttf.GetStringSize(get_font(font_id, font_size), c_str, 0, &width, &height) {
|
||||||
log.panicf("Failed to measure text: %s", sdl.GetError())
|
log.panicf("Failed to measure text: %s", sdl.GetError())
|
||||||
}
|
}
|
||||||
return {f32(w) / GLOB.dpi_scaling, f32(h) / GLOB.dpi_scaling}
|
return {f32(width) / GLOB.dpi_scaling, f32(height) / GLOB.dpi_scaling}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Text anchor helpers -----------
|
// ----- Text anchor helpers -----------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
center_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
center_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return size * 0.5
|
return size * 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
top_left_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
top_left_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
return {0, 0}
|
return {0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
top_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
top_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {size.x * 0.5, 0}
|
return {size.x * 0.5, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
top_right_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
top_right_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {size.x, 0}
|
return {size.x, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
left_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
left_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {0, size.y * 0.5}
|
return {0, size.y * 0.5}
|
||||||
}
|
}
|
||||||
|
|
||||||
right_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
right_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {size.x, size.y * 0.5}
|
return {size.x, size.y * 0.5}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_left_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
bottom_left_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {0, size.y}
|
return {0, size.y}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
bottom_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return {size.x * 0.5, size.y}
|
return {size.x * 0.5, size.y}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_right_of_text :: proc(str: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
bottom_right_of_text :: proc(text_string: string, font_id: Font_Id, font_size: u16 = 44) -> [2]f32 {
|
||||||
size := measure_text(str, font_id, font_size)
|
size := measure_text(text_string, font_id, font_size)
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user