Minor lifting in the render side, getting ready to just hunker down and take notes on whats next

* VEFontCache needs to get fixed up (possibly bring back ELFHash)
  * Problably going to do conversion early to odin's array and map usage
* Need get the quad tree setup for the ui so that I can do fast and efficient traversal for the layer based rendering.
This commit is contained in:
2024-06-24 16:36:22 -04:00
parent 0d9623c340
commit c93c0ed567
9 changed files with 440 additions and 396 deletions

View File

@ -325,7 +325,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
ui_startup( & workspace.ui, cache_allocator = persistent_slab_allocator() )
}
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum.txt", allocator = persistent_slab_allocator())
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem_Ispsum (197).txt", allocator = persistent_slab_allocator())
alloc_error : AllocatorError; success : bool
debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() )

View File

@ -19,133 +19,6 @@ RenderState :: struct {
pass_actions : PassActions,
}
#region("Helpers")
draw_filled_circle :: proc(x, y, radius: f32, edges: int)
{
if edges < 3 do return // Need at least 3 edges to form a shape
triangles := make([]gp.Triangle, edges)
center := gp.Point{x, y}
edge_quotient := 1 / f32(edges)
angle_factor := 2 * math.PI * edge_quotient
for edge_id in 0..< edges
{
angle1 := f32(edge_id ) * angle_factor
angle2 := f32(edge_id +1) * angle_factor
p1 := gp.Point{
x + radius * math.cos(angle1),
y + radius * math.sin(angle1),
}
p2 := gp.Point{
x + radius * math.cos(angle2),
y + radius * math.sin(angle2),
}
triangles[edge_id] = gp.Triangle{center, p1, p2}
}
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
}
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
using rect
render_set_color( color )
size := max - min
position := min
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
}
draw_rect_border :: proc( rect : Range2, border_width: f32)
{
rect_size := rect.max - rect.min
border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5)
top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width }
bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width }
left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
borders := []gp.Rect{ top, bottom, left, right }
gp.draw_filled_rects( raw_data(borders), u32(len(borders)) )
}
// Draw text using a string and normalized render coordinates
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 )
{
state := get_state(); using state
width := app_window.extent.x * 2
height := app_window.extent.y * 2
ve_id := font_provider_resolve_draw_id( id, size )
color_norm := normalize_rgba8(color)
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale )
return
}
// Draw text using a string and extent-based screen coordinates
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
{
// profile(#procedure)
state := get_state(); using state
screen_size := app_window.extent * 2
render_pos := screen_to_render_pos(pos)
normalized_pos := render_pos * (1.0 / screen_size)
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
}
draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White )
{
// profile(#procedure)
cam_offset := Vec2 {
cam.position.x,
cam.position.y,
}
pos_offset := pos + cam_offset * cam.zoom
cam_zoom_ratio := 1 / cam.zoom
state := get_state(); using state
screen_size := app_window.extent * 2
render_pos := screen_to_render_pos(pos_offset)
normalized_pos := render_pos * (1.0 / screen_size)
draw_text_string_pos_norm( content, id, size, normalized_pos, color, cam.zoom )
}
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
render_flush_gp :: #force_inline proc()
{
gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
gp.flush()
gfx.end_pass()
}
@(deferred_none=gp.reset_transform)
render_set_camera :: #force_inline proc( cam : Camera )
{
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
gp.scale( cam.zoom, cam.zoom )
}
render_set_color :: #force_inline proc( color : RGBA8 ) {
color := normalize_rgba8(color);
gp.set_color( color.r, color.g, color.b, color.a )
}
render_set_view_space :: #force_inline proc( extent : Extents2 )
{
size := extent * 2
gp.viewport(0, 0, i32(size.x), i32(size.y))
gp.project( -extent.x, extent.x, extent.y, -extent.y )
}
#endregion("Helpers")
render :: proc()
{
profile(#procedure)
@ -224,59 +97,15 @@ render_mode_2d_workspace :: proc()
render_set_camera(cam)
ui := & project.workspace.ui
render_list := array_to_slice( ui.render_list )
text_enqueued : b32 = false
shape_enqueued : b32 = false
for entry, id in render_list
{
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
if !already_passed_signal && entry.layer_signal
{
profile("render ui layer")
render_flush_gp()
if text_enqueued do render_text_layer()
continue
}
using entry
profile("enqueue box")
GP_Render:
{
// profile("draw_shapes")
if style.bg_color.a != 0
{
draw_rect( bounds, style.bg_color )
shape_enqueued = true
}
if style.border_color.a != 0 && border_width > 0 {
render_set_color( style.border_color )
draw_rect_border( bounds, border_width )
shape_enqueued = true
}
if debug.draw_ui_box_bounds_points
{
render_set_color(Color_Red)
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
render_set_color(Color_Blue)
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
shape_enqueued = true
}
}
if len(text.str) > 0 && style.font.key != 0 {
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam, style.text_color )
text_enqueued = true
}
when UI_Render_Method == .Layers {
render_list := array_to_slice( ui.render_list )
render_ui_via_box_list( render_list, & cam )
}
when UI_Render_Method == .Depth_First
{
render_ui_via_box_tree( ui.root, & cam )
}
if shape_enqueued do render_flush_gp()
if text_enqueued do render_text_layer()
}
render_mode_screenspace :: proc()
@ -441,69 +270,19 @@ render_screen_ui :: proc()
text_enqueued : b32 = false
shape_enqueued : b32 = false
render_list := array_to_slice( ui.render_list )
for entry, id in render_list
{
if entry.layer_signal
{
profile("render ui layer")
render_flush_gp()
if text_enqueued do render_text_layer()
continue
}
using entry
profile("enqueue box")
GP_Render:
{
profile("draw_shapes")
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
using rect
render_set_color( color )
size := max - min
position := min
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
}
if style.bg_color.a != 0
{
draw_rect( bounds, style.bg_color )
shape_enqueued = true
}
if style.border_color.a != 0 && border_width > 0 {
render_set_color( style.border_color )
draw_rect_border( bounds, border_width )
shape_enqueued = true
}
if debug.draw_ui_box_bounds_points
{
render_set_color(Color_Red)
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
render_set_color(Color_Blue)
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
shape_enqueued = true
}
}
if len(text.str) > 0 && style.font.key != 0 {
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
text_enqueued = true
}
when UI_Render_Method == .Layers {
render_list := array_to_slice( ui.render_list )
render_ui_via_box_list( render_list )
}
when UI_Render_Method == .Depth_First
{
render_ui_via_box_tree( ui.root )
}
if shape_enqueued do render_flush_gp()
if text_enqueued do render_text_layer()
}
render_text_layer :: proc()
{
profile("VEFontCache: render text layer")
// profile("VEFontCache: render text layer")
Bindings :: gfx.Bindings
Range :: gfx.Range
@ -681,3 +460,254 @@ render_text_layer :: proc()
gfx.end_pass()
}
}
render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil )
{
debug := get_state().debug
default_font := get_state().default_font
for box := root.first; box != nil; box = ui_box_tranverse_next_depth_based( box )
{
text_enqueued : b32 = false
shape_enqueued : b32 = false
border_width := box.layout.border_width
computed := box.computed
font_size := box.layout.font_size
style := box.style
text := box.text
using computed
// profile("enqueue box")
GP_Render:
{
// profile("draw_shapes")
if style.bg_color.a != 0
{
draw_rect( bounds, style.bg_color )
shape_enqueued = true
}
if style.border_color.a != 0 && border_width > 0 {
render_set_color( style.border_color )
draw_rect_border( bounds, border_width )
shape_enqueued = true
}
if debug.draw_ui_box_bounds_points
{
render_set_color(Color_Red)
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
render_set_color(Color_Blue)
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
shape_enqueued = true
}
}
if len(text.str) > 0 && style.font.key != 0 {
if cam != nil {
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam^, style.text_color )
}
else {
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
}
text_enqueued = true
}
if shape_enqueued do render_flush_gp()
if text_enqueued do render_text_layer()
}
}
render_ui_via_box_list :: proc( render_list : []UI_RenderBoxInfo, cam : ^Camera = nil )
{
debug := get_state().debug
default_font := get_state().default_font
text_enqueued : b32 = false
shape_enqueued : b32 = false
for entry, id in render_list
{
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
if !already_passed_signal && entry.layer_signal
{
profile("render ui layer")
render_flush_gp()
if text_enqueued do render_text_layer()
continue
}
using entry
profile("enqueue box")
GP_Render:
{
// profile("draw_shapes")
if style.bg_color.a != 0
{
draw_rect( bounds, style.bg_color )
shape_enqueued = true
}
if style.border_color.a != 0 && border_width > 0 {
render_set_color( style.border_color )
draw_rect_border( bounds, border_width )
shape_enqueued = true
}
if debug.draw_ui_box_bounds_points
{
render_set_color(Color_Red)
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
render_set_color(Color_Blue)
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
shape_enqueued = true
}
}
if len(text.str) > 0 && style.font.key != 0 {
if cam != nil {
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam^, style.text_color )
}
else {
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
}
text_enqueued = true
}
}
if shape_enqueued do render_flush_gp()
if text_enqueued do render_text_layer()
}
#region("Helpers")
draw_filled_circle :: proc(x, y, radius: f32, edges: int)
{
if edges < 3 do return // Need at least 3 edges to form a shape
triangles := make([]gp.Triangle, edges)
center := gp.Point{x, y}
edge_quotient := 1 / f32(edges)
angle_factor := 2 * math.PI * edge_quotient
for edge_id in 0..< edges
{
angle1 := f32(edge_id ) * angle_factor
angle2 := f32(edge_id +1) * angle_factor
p1 := gp.Point{
x + radius * math.cos(angle1),
y + radius * math.sin(angle1),
}
p2 := gp.Point{
x + radius * math.cos(angle2),
y + radius * math.sin(angle2),
}
triangles[edge_id] = gp.Triangle{center, p1, p2}
}
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
}
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
using rect
render_set_color( color )
size := max - min
position := min
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
}
draw_rect_border :: proc( rect : Range2, border_width: f32)
{
rect_size := rect.max - rect.min
border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5)
top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width }
bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width }
left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
borders := []gp.Rect{ top, bottom, left, right }
gp.draw_filled_rects( raw_data(borders), u32(len(borders)) )
}
// Draw text using a string and normalized render coordinates
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 )
{
state := get_state(); using state
width := app_window.extent.x * 2
height := app_window.extent.y * 2
ve_id := font_provider_resolve_draw_id( id, size )
color_norm := normalize_rgba8(color)
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale )
return
}
// Draw text using a string and extent-based screen coordinates
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
{
// profile(#procedure)
state := get_state(); using state
screen_size := app_window.extent * 2
render_pos := screen_to_render_pos(pos)
normalized_pos := render_pos * (1.0 / screen_size)
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
}
draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White )
{
// profile(#procedure)
cam_offset := Vec2 {
cam.position.x,
cam.position.y,
}
pos_offset := pos + cam_offset * cam.zoom
cam_zoom_ratio := 1 / cam.zoom
state := get_state(); using state
screen_size := app_window.extent * 2
render_pos := screen_to_render_pos(pos_offset)
normalized_pos := render_pos * (1.0 / screen_size)
draw_text_string_pos_norm( content, id, size * cam.zoom, normalized_pos, color, cam.zoom )
}
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
render_flush_gp :: #force_inline proc()
{
gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
gp.flush()
gfx.end_pass()
}
@(deferred_none=gp.reset_transform)
render_set_camera :: #force_inline proc( cam : Camera )
{
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
gp.scale( cam.zoom, cam.zoom )
}
render_set_color :: #force_inline proc( color : RGBA8 ) {
color := normalize_rgba8(color);
gp.set_color( color.r, color.g, color.b, color.a )
}
render_set_view_space :: #force_inline proc( extent : Extents2 )
{
size := extent * 2
gp.viewport(0, 0, i32(size.x), i32(size.y))
gp.project( -extent.x, extent.x, extent.y, -extent.y )
}
#endregion("Helpers")

View File

@ -167,6 +167,7 @@ update :: proc( delta_time : f64 ) -> b32
config.cam_max_zoom = 30
config.cam_zoom_sensitivity_digital = 0.04
// config.cam_min_zoom = 0.04
config.cam_zoom_sensitivity_smooth = 0.02
config.cam_zoom_mode = .Smooth
switch config.cam_zoom_mode
{