diff --git a/assets/gui.frag.spv b/assets/gui.frag.spv index 339a727..c23da8b 100644 Binary files a/assets/gui.frag.spv and b/assets/gui.frag.spv differ diff --git a/assets/gui.vert.spv b/assets/gui.vert.spv index d5ad7a1..fb74b31 100644 Binary files a/assets/gui.vert.spv and b/assets/gui.vert.spv differ diff --git a/src/VulkanRenderer b/src/VulkanRenderer index b3639d9..11cf96b 160000 --- a/src/VulkanRenderer +++ b/src/VulkanRenderer @@ -1 +1 @@ -Subproject commit b3639d9c884c09fb98b750ba613118d633470312 +Subproject commit 11cf96b799c6ccee3c4f569e89c263d11c4d5ff8 diff --git a/src/editor/editor.d b/src/editor/editor.d index 35d478e..83f561c 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -192,12 +192,12 @@ Cycle(EditorCtx* ctx, Inputs* inputs) Push!("bg_col", true)(ui_ctx, white); Push!("text_col", true)(ui_ctx, black[0]); Push!("padding", true)(ui_ctx, Vec2(8.0)); - UIItem* text2 = MakeItem("A different line of text for testing purposes##text", UIF.DrawBackground|UIF.DrawText|UIF.TextWrap); + UIItem* text2 = MakeItem("A different line of text for testing purposes##text2", UIF.DrawBackground|UIF.DrawText|UIF.TextWrap); Pop!("parent")(ui_ctx); Pop!("parent")(ui_ctx); - Push!("size_info", true)(ui_ctx, MakeUISizeX(ST.Pixels, 2.0)); + Push!("size_info", true)(ui_ctx, MakeUISizeX(ST.Pixels, 100.0)); Push!("bg_col", true)(ui_ctx, black); UIItem* sep = MakeItem("###sep", UIF.Draggable|UIF.DrawBackground|UIF.ResizeAdjacent); diff --git a/src/editor/ui.d b/src/editor/ui.d index 81ce0da..8bc7cd9 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -66,6 +66,7 @@ Vec2 g_padding_default = Vec2(0.0); f32 g_text_scale_default = 1.0; Vec2 g_scroll_target_default = Vec2(0.0); Vec2 g_scroll_clamp_default = Vec2(0.0); +Vec2 g_view_offset_default = Vec2(0.0); alias g_parent_default = g_UI_NIL; const UI_COUNT = 5000; @@ -115,9 +116,12 @@ enum UIFlags RightAlignText = 1<<14, CenterAlignText = 1<<15, // todo TextWrap = 1<<16, - PortalView = 1<<17, // Stencil mask this area, no bounds checking children within (for views to drag and view elements) + PortalViewX = 1<<17, // Stencil mask this area, no bounds checking children within (for views to drag and view elements) + PortalViewY = 1<<18, - Clamp = UIFlags.ClampX | UIFlags.ClampY, + Clamp = UIFlags.ClampX | UIFlags.ClampY, + PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY, + Scroll = UIFlags.ScrollX | UIFlags.ScrollY, } alias UIF = UIFlags; @@ -242,8 +246,8 @@ struct UICtx mixin UICtxParameter!(Vec4, "text_hl_col"); mixin UICtxParameter!(Vec2[2], "scroll_clamp"); mixin UICtxParameter!(Vec2, "scroll_target"); - mixin UICtxParameter!(Vec2, "fixed_pos"); mixin UICtxParameter!(Vec2, "padding"); + mixin UICtxParameter!(Vec2, "view_offset"); mixin UICtxParameter!(UIItem*, "parent"); mixin UICtxParameter!(Axis2D, "layout_axis"); mixin UICtxParameter!(f32, "corner_radius"); @@ -316,6 +320,8 @@ struct UIItem Vec2 scroll_offset; mixin UIItemParameters!(); + + bool rendered; } enum SizeType @@ -342,6 +348,7 @@ struct UIBuffer Vertex[] vtx; u32[] idx; u32 count; + u32 vtx_offset; } struct GlyphBounds @@ -493,21 +500,20 @@ InitUICtx(PlatformWindow* window) ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout); ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof); - Attribute[14] attributes = [ - { binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0}, - { binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1}, - { binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2}, - { binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3}, - { binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof }, - { binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof }, - { binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof }, - { binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof }, - { binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof }, - { binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof }, - { binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof }, - { binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof }, - { binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.z_index.offsetof }, - { binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof }, + Attribute[13] attributes = [ + { binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0 }, + { binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1 }, + { binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2 }, + { binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3 }, + { binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof }, + { binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof }, + { binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof }, + { binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof }, + { binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof } , + { binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof }, + { binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof }, + { binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof }, + { binding: 0, location: 12, format: FMT.R_U32, offset: Vertex.texture.offsetof }, ]; GfxPipelineInfo ui_info = { @@ -683,6 +689,7 @@ InitSingleLine: item.last_frame = ctx.frame; item.padding += item.border_thickness; + item.rendered = false; return item; } @@ -748,6 +755,22 @@ PushUIEvent(UICtx* ctx, UIInput input) DLLPush(&ctx.events, ev, null); } +void +DrawUI(UICtx* ctx) +{ + version(ENABLE_RENDERER) + { + UIBuffer* b = ctx.buffers.ptr + ctx.f_idx; + if(b.vtx_offset != b.count) + { + u32 count = b.count - b.vtx_offset; + BindBuffers(&ctx.rd, &b.m_idx, &b.m_vtx); + DrawIndexed(&ctx.rd, 6, count, 0, 0, b.vtx_offset); + b.vtx_offset += count; + } + } +} + void BeginUI(Inputs* inputs) { @@ -842,7 +865,8 @@ BeginUI(Inputs* inputs) } memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count); - ctx.buffers[ctx.f_idx].count = 0; + ctx.buffers[ctx.f_idx].count = 0; + ctx.buffers[ctx.f_idx].vtx_offset = 0; // Root Item UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)]; @@ -989,6 +1013,30 @@ EndUI() break; } } + + if(excess > 0.0009) + { + for(UIItem* c = item.last; !Nil(c); c = c.prev) + { + f32 reduced = Min(excess, c.size[axis]); + excess -= reduced; + c.size.v[axis] -= reduced; + + if(excess < 0.0009) + { + break; + } + } + + assert(excess < 0.0009); + } + } + } + else + { + for(UIItem* c = item.first; !Nil(c); c = c.next) + { + c.size.v[axis] = c.size.v[axis] > size ? size : c.size.v[axis]; } } } @@ -1017,6 +1065,28 @@ EndUI() assert(!isNaN(item.rect.p0.v[axis])); assert(!isNaN(item.rect.p1.v[axis])); + debug + { + bool portal = cast(bool)(item.parent.flags & (UIF.PortalViewX << axis)); + + if(!portal && item != ctx.root) + { + UIItem* p = item.parent; + f32 i0 = item.rect.p0.v[axis]; + f32 i1 = item.rect.p1.v[axis]; + f32 p0 = item.parent.rect.p0.v[axis] + item.parent.border_thickness; + f32 p1 = item.parent.rect.p1.v[axis] - item.parent.border_thickness; + + bool in_bounds_start = i0 >= p0; + bool in_bounds_end = i1 <= p1; + if(!in_bounds_start || !in_bounds_end) + { + Errf("[%s] failed bounds check, item bounds: [%s] parent bounds: [%s]", item.key.hash_text, [i0, i1], [p0, p1]); + } + assert(in_bounds_start && in_bounds_end); + } + } + next_pos = item.parent.layout_axis == axis ? end_pos : pos; if(!Nil(item.first)) @@ -1051,13 +1121,12 @@ EndUI() RenderItems(ctx.root); RenderItems(ctx.window_root); - version(ENABLE_RENDERER) with(ctx) + version(ENABLE_RENDERER) { - BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx); - DrawIndexed(&rd, 6, buffers[f_idx].count, 0); + DrawUI(ctx); - FinishRendering(&rd); - SubmitAndPresent(&rd); + FinishRendering(&ctx.rd); + SubmitAndPresent(&ctx.rd); } if(!Nil(ctx.drag_item)) @@ -1073,69 +1142,116 @@ EndUI() } } +void +RenderItem(UICtx* ctx, UIItem* item) +{ + if(item.rendered) return; + + // Doesn't really support nesting scissors, will maybe change this later. + bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX); + bool scissor_y = cast(bool)(item.flags & UIF.PortalViewY); + if(scissor_x || scissor_y) + { + DrawUI(ctx); + + u32 x = cast(u32)(scissor_x ? floor(item.rect.p0.x) : ctx.res.x); + u32 y = cast(u32)(scissor_y ? floor(item.rect.p0.y) : ctx.res.y); + u32 w = cast(u32)(scissor_x ? floor(item.rect.p1.x) - x : ctx.res.x); + u32 h = cast(u32)(scissor_y ? floor(item.rect.p1.y) - y : ctx.res.y); + SetScissor(&ctx.rd, x, y, w, h); + } + + if(item.flags & UIF.DrawBackground) + { + // DrawRect + Vertex* v = GetVertex(ctx); + v.dst_start = item.rect.p0 + item.border_thickness; + v.dst_end = item.rect.p1 - item.border_thickness; + v.cols = item.bg_col; + v.corner_radius = item.corner_radius; + v.bounds = ItemBounds(item.parent); + + AddVertexCount(ctx); + } + + if(item.flags & UIF.DrawBorder) + { + Vertex* v = GetVertex(ctx); + v.dst_start = item.rect.p0; + v.dst_end = item.rect.p1; + v.cols = item.border_col; + v.corner_radius = item.corner_radius; + v.border_thickness = item.border_thickness; + v.edge_softness = item.edge_softness; + v.bounds = ItemBounds(item.parent); + + AddVertexCount(ctx); + } + + if(item.flags & UIF.DrawText || item.display_string) + { + if(item.flags & UIF.CenterAlignText) + { + // TODO + } + else + { + FontAtlas* atl = &ctx.atlas_buf.atlas; + + f32 y_pos = item.rect.p0.y + item.padding.y; + + foreach(i; 0 .. item.text_lines.length) + { + string str = item.text_lines[i]; + + f32 x_pos = item.flags & UIF.RightAlignText ? item.rect.p1.x - item.padding.x - CalcTextWidth(str) : + item.rect.p0.x + item.padding.x; + + x_pos = clamp(x_pos, item.rect.p0.x, item.rect.p1.x); + + foreach(j; 0 .. str.length) + { + u8 ch = str[j]; + Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; + DrawGlyph(item, g, &x_pos, y_pos); + } + + y_pos += TEXT_SIZE; + } + } + } + + if(scissor_x || scissor_y) + { + UIItem* first = item.first; + UIItem* last = item.last; + + item.first = item.last = g_UI_NIL; + + UIItem* next = Recurse!(true)(item, g_UI_NIL); + + item.first = first; + item.last = last; + + for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL)) + { + RenderItem(ctx, d); + d.rendered = true; + } + + DrawUI(ctx); + + ResetScissor(&ctx.rd); + } +} + void RenderItems(UIItem* root) { UICtx* ctx = GetCtx(); for(UIItem* item = root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) { - if(item.flags & UIF.DrawBackground) - { - // DrawRect - Vertex* v = GetVertex(ctx); - v.dst_start = item.rect.p0 + item.border_thickness; - v.dst_end = item.rect.p1 - item.border_thickness; - v.cols = item.bg_col; - v.corner_radius = item.corner_radius; - v.bounds = ItemBounds(item.parent); - - AddVertexCount(ctx); - } - - if(item.flags & UIF.DrawBorder) - { - Vertex* v = GetVertex(ctx); - v.dst_start = item.rect.p0; - v.dst_end = item.rect.p1; - v.cols = item.border_col; - v.corner_radius = item.corner_radius; - v.border_thickness = item.border_thickness; - v.edge_softness = item.edge_softness; - v.bounds = ItemBounds(item.parent); - - AddVertexCount(ctx); - } - - if(item.flags & UIF.DrawText || item.display_string) - { - if(item.flags & UIF.CenterAlignText) - { - // TODO - } - else - { - FontAtlas* atl = &ctx.atlas_buf.atlas; - - f32 y_pos = item.rect.p0.y + item.padding.y; - - foreach(i; 0 .. item.text_lines.length) - { - string str = item.text_lines[i]; - - f32 x_pos = item.flags & UIF.RightAlignText ? item.rect.p1.x - item.padding.x - CalcTextWidth(str) : - item.rect.p0.x + item.padding.x; - - foreach(j; 0 .. str.length) - { - u8 ch = str[j]; - Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; - DrawGlyph(item, g, &x_pos, y_pos); - } - - y_pos += TEXT_SIZE; - } - } - } + RenderItem(ctx, item); } } @@ -1568,17 +1684,10 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y) v.src_end = Vec2(gb.atlas_r, gb.atlas_b); } - /* f32 end_x = *x_pos + advance; f32 width = end_x - *x_pos; - f32 bound_x = item.flags & UIF.RightAlignText ? item.rect.p0.x : item.rect.p1.x; - if(item.flags & UIF.RightAlignText && bound_x > *x_pos) - { - f32 cull_pct = (bound_x - *x_pos) / width; - v.dst_start.x += (v.dst_end.x - v.dst_start.x) * cull_pct; - v.src_start.x += (v.src_end.x - v.src_start.x) * cull_pct; - } - else if(!(item.flags & UIF.RightAlignText) && end_x > bound_x) + f32 bound_x = item.rect.p1.x; + if(end_x > bound_x) { f32 cull_pct = (end_x - bound_x) / width; v.dst_end.x -= (v.dst_end.x - v.dst_start.x) * cull_pct; @@ -1601,7 +1710,6 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y) v.pos[i].end[axis] = v.pos[i].start.v[axis] > v.pos[i].end.v[axis] ? v.pos[i].start.v[axis] : v.pos[i].end.v[axis]; } } - */ AddVertexCount(ctx); diff --git a/src/editor/views.d b/src/editor/views.d index f42a734..40a2961 100644 --- a/src/editor/views.d +++ b/src/editor/views.d @@ -23,7 +23,7 @@ EditorView(Editor* ed) UIItem* editor = Get(ed_key); u64 frame_line_offset = ed.line_offset; - u64 frame_view_offset = editor.scroll_offset%TEXT_SIZE; + f32 frame_view_offset = editor.scroll_offset.y%TEXT_SIZE; Push!("border_col" )(ctx, Vec4Arr(Vec4(1.0))); @@ -72,8 +72,8 @@ EditorView(Editor* ed) Push!("parent" )(ctx, line_count); Push!("text_col" )(ctx, Vec4(1.0)); - u64 i = prev_offset; - for(LineBuffer* lb = GetLine(&ed.buf, i); i < prev_offset+view_lines; i += 1) + u64 i = frame_line_offset; + for(LineBuffer* lb = GetLine(&ed.buf, i); i < frame_line_offset+view_lines; i += 1) { } diff --git a/src/shaders/gui.frag.glsl b/src/shaders/gui.frag.glsl index d84a0d5..5ae2b2a 100644 --- a/src/shaders/gui.frag.glsl +++ b/src/shaders/gui.frag.glsl @@ -14,8 +14,6 @@ layout (location = 1) in struct FragDataIn { vec2 dst_center; vec2 dst_half_size; vec2 sdf_sample_pos; - vec2 bounds_p0; - vec2 bounds_p1; float corner_radius; float softness; float raised; diff --git a/src/shaders/gui.vert.glsl b/src/shaders/gui.vert.glsl index eea89ed..e4919ab 100644 --- a/src/shaders/gui.vert.glsl +++ b/src/shaders/gui.vert.glsl @@ -8,16 +8,15 @@ layout (location = 0) in vec4 in_col_1; layout (location = 1) in vec4 in_col_2; layout (location = 2) in vec4 in_col_3; layout (location = 3) in vec4 in_col_4; -layout (location = 4) in vec4 in_bounds; -layout (location = 5) in vec2 in_dst_start; -layout (location = 6) in vec2 in_dst_end; -layout (location = 7) in vec2 in_src_start; -layout (location = 8) in vec2 in_src_end; -layout (location = 9) in float border_thickness; -layout (location = 10) in float corner_radius; -layout (location = 11) in float edge_softness; -layout (location = 12) in float raised; -layout (location = 13) in uint in_has_texture; +layout (location = 4) in vec2 in_dst_start; +layout (location = 5) in vec2 in_dst_end; +layout (location = 6) in vec2 in_src_start; +layout (location = 7) in vec2 in_src_end; +layout (location = 8) in float border_thickness; +layout (location = 9) in float corner_radius; +layout (location = 10) in float edge_softness; +layout (location = 11) in float raised; +layout (location = 12) in uint in_has_texture; layout (location = 0) flat out uint out_has_texture; @@ -91,6 +90,6 @@ void main() FragData.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size; out_has_texture = in_has_texture; - vec4 v_pos = PC.projection * vec4(pos.x, pos.y, z_index, 1); + vec4 v_pos = PC.projection * vec4(pos.x, pos.y, 0, 1); gl_Position = vec4(v_pos.x, v_pos.y, v_pos.z, 1); }