diff --git a/assets/gui.frag.spv b/assets/gui.frag.spv new file mode 100644 index 0000000..6568e89 Binary files /dev/null and b/assets/gui.frag.spv differ diff --git a/assets/gui.vert.spv b/assets/gui.vert.spv new file mode 100644 index 0000000..fb83d4f Binary files /dev/null and b/assets/gui.vert.spv differ diff --git a/assets/pc-9800.ttf b/assets/pc-9800.ttf new file mode 100644 index 0000000..a74b1e2 Binary files /dev/null and b/assets/pc-9800.ttf differ diff --git a/src/DLibs b/src/DLibs index 38e4e44..c0d9de9 160000 --- a/src/DLibs +++ b/src/DLibs @@ -1 +1 @@ -Subproject commit 38e4e44b4dbecd8b0cbe878adb4f8da397a1ae45 +Subproject commit c0d9de9c4e7fd33e3329745496e601431f6e2970 diff --git a/src/VulkanRenderer b/src/VulkanRenderer index 1290c5f..16820bd 160000 --- a/src/VulkanRenderer +++ b/src/VulkanRenderer @@ -1 +1 @@ -Subproject commit 1290c5f45ac49cc5534817e8d6b0adae4ac058af +Subproject commit 16820bdde294d59425fda6e115ae7b2d1e532e51 diff --git a/src/editor/editor.d b/src/editor/editor.d new file mode 100644 index 0000000..c008624 --- /dev/null +++ b/src/editor/editor.d @@ -0,0 +1,240 @@ +import aliases; +import util; +import platform; +import fonts; +import vulkan; +import math; +import alloc; + +import std.stdio; +import std.exception; + +const UI_COUNT = 50000; + +struct Editor +{ + Arena arena; + Arena temp_arena; + PlatformWindow* window; + + Renderer rd; + ImageView font_atlas; + Pipeline pipeline; + DescSetLayout desc_set_layout; + DescSet desc_set; + PipelineLayout pipeline_layout; + PushConst pc; + MappedBuffer!(Vertex) m_vertex_buf; + MappedBuffer!(u32) m_index_buf; + Vertex[] vertices; + u32[] indices; + u32 ui_count; + + Buffer[] buffers; + u8[][] buffer_names; + + u8[] font_data; + FontFace font; + FontAtlasBuf atlas_buf; +} + +struct PushConst +{ + Vec2 res; +} + +struct Vertex +{ + Vec2 dst_start; + Vec2 dst_end; + Vec2 src_start; + Vec2 src_end; + Vec4 col; +} + +void +Cycle(Editor* ed) +{ + ed.ui_count = 0; + + Reset(&ed.temp_arena); + + DrawText(ed, 200.0, 200.0, 16.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + BeginFrame(&ed.rd); + + UVec2 ext = UVec2(GetExtent(&ed.rd)); + ed.pc.res.x = cast(f32)ext.x; + ed.pc.res.y = cast(f32)ext.y; + + BeginRendering(&ed.rd); + + PushConstants(&ed.rd, ed.pipeline, &ed.pc); + + Bind(&ed.rd, ed.pipeline, ed.desc_set); + + BindBuffers(&ed.rd, &ed.m_index_buf, &ed.m_vertex_buf); + + DrawIndexed(&ed.rd, 6, ed.ui_count, 0); + + FinishRendering(&ed.rd); + + SubmitAndPresent(&ed.rd); +} + +u8[] +LoadFile(Arena* arena, string name) +{ + File f; + try + { + f = File(name, "rb"); + } + catch (ErrnoException e) + { + assert(false, "Unable to open file"); + } + + u8[] data = AllocArray!(u8)(arena, f.size()); + f.rawRead(data); + f.close(); + + return data; +} + +Editor +CreateEditor(PlatformWindow* window) +{ + InitFreeType(); + + version(linux) + { + PlatformHandles handles = { + window: window.window, + conn: window.conn, + }; + } + + version(Windows) + { + + } + + Arena arena = CreateArena(MB(32)); + + u8[] font_data = LoadFile(&arena, "assets/pc-9800.ttf"); + FontFace font = OpenFont(font_data); + FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256); + + Editor editor = { + window: window, + arena: arena, + temp_arena: CreateArena(MB(8)), + rd: InitRenderer(handles, MB(32), MB(16)), + font_data: font_data, + font: font, + atlas_buf: atlas_buf, + }; + + DescLayoutBinding[2] layout_bindings = [ + { binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All }, + { binding: 1, descriptorType: DT.Storage, descriptorCount: 1, stageFlags: SS.All }, + ]; + + editor.desc_set_layout = CreateDescSetLayout(&editor.rd, layout_bindings); + editor.desc_set = AllocDescSet(&editor.rd, editor.desc_set_layout); + editor.pipeline_layout = CreatePipelineLayout(&editor.rd, editor.desc_set_layout, PushConst.sizeof); + + Attribute[5] attributes = [ + { binding: 0, location: 0, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof }, + { binding: 0, location: 1, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof }, + { binding: 0, location: 2, format: FMT.RG_F32, offset: Vertex.src_start.offsetof }, + { binding: 0, location: 3, format: FMT.RG_F32, offset: Vertex.src_end.offsetof }, + { binding: 0, location: 4, format: FMT.RGBA_F32, offset: Vertex.col.offsetof }, + ]; + + GfxPipelineInfo ui_info = { + vertex_shader: LoadFile(&editor.temp_arena, "assets/gui.vert.spv"), + frag_shader: LoadFile(&editor.temp_arena, "assets/gui.frag.spv"), + input_rate: IR.Instance, + input_rate_stride: Vertex.sizeof, + layout: editor.pipeline_layout, + vertex_attributes: attributes, + }; + + assert(CreateGraphicsPipeline(&editor.rd, &editor.pipeline, &ui_info), "Unable to build UI pipeline"); + + editor.m_vertex_buf = CreateMappedBuffer!(Vertex)(&editor.rd, BT.Vertex, Vertex.sizeof * UI_COUNT); + editor.m_index_buf = CreateMappedBuffer!(u32)(&editor.rd, BT.Index, u32.sizeof * UI_COUNT); + editor.vertices = editor.m_vertex_buf.data; + editor.indices = editor.m_index_buf.data; + + CreateImageView(&editor.rd, &editor.font_atlas, editor.atlas_buf.atlas.width, editor.atlas_buf.atlas.height, 4, editor.atlas_buf.data); + + Write(&editor.rd, editor.desc_set, &editor.font_atlas, 0, DT.Image); + + Reset(&editor.temp_arena); + + return editor; +} + +void +DrawText(Editor* ed, f32 x, f32 y, f32 px, string str) +{ + u8[] str_buf = (cast(u8*)str.ptr)[0 .. str.length]; + DrawText(ed, x, y, px, str_buf); +} + +void +DrawText(Editor* ed, f32 x, f32 y, f32 px, u8[] str) +{ + f32 x_pos = x; + f32 scale = px / ed.atlas_buf.atlas.size; + foreach(ch; str) + { + foreach(glyph; ed.atlas_buf.atlas.glyphs) + { + if (ch == glyph.ch) + { + Vertex* v = ed.vertices.ptr + ed.ui_count; + + f32 r = glyph.plane_right * scale; + f32 l = glyph.plane_left * scale; + f32 w = r - l; + f32 h = (glyph.plane_bottom - glyph.plane_top) * scale; + f32 y_pos = glyph.plane_top * scale; + + v.dst_start.x = x_pos + l; + v.dst_start.y = y + h - y_pos; + v.dst_end.x = x_pos + w + l; + v.dst_end.y = y - y_pos; + + v.src_start.x = glyph.atlas_left; + v.src_start.y = glyph.atlas_top; + v.src_end.x = glyph.atlas_right; + v.src_end.y = glyph.atlas_bottom; + + v.col = Vec4(1.0, 1.0, 1.0, 1.0); + + x_pos += glyph.advance * scale; + + AddUIIndices(ed); + + break; + } + } + } +} + +void +AddUIIndices(Editor* ed) +{ + ed.indices[0] = 0; + ed.indices[1] = 1; + ed.indices[2] = 2; + ed.indices[3] = 2; + ed.indices[4] = 1; + ed.indices[5] = 3; + + ed.ui_count += 1; +} diff --git a/src/editor/main.d b/src/editor/main.d index 29d755c..6004b0f 100644 --- a/src/editor/main.d +++ b/src/editor/main.d @@ -1,76 +1,20 @@ -import aliases; -import std.stdio; -import buffer; -import util; +import platform; +import editor; void main(string[] argv) { - // Inserts + PlatformWindow window = CreateWindow("Editor", 1920, 1080); + + Editor editor = CreateEditor(&window); + + while (true) { - string test_str = "This is a test string for testing the GapBuffer struct."; - string test_str_2 = "This is an additional string. "; - u8[] data = (cast(u8*)test_str.ptr)[0 .. test_str.length]; - GapBuffer buf = CreateGapBuffer(data); - - u8[] insert = (cast(u8*)test_str_2.ptr)[0 .. test_str_2.length]; - Insert(&buf, insert, insert.length, 0); - - u8[][] line_buf = new u8[][](1, 1024); - GetLines(&buf, line_buf, 0); - - u64 count = 0; - foreach(i, ch; test_str_2) + HandleEvents(&window); + if (window.close) { - assert(buf.data[count] == ch, "Buffer doesn't match test_str_2"); - count += 1; + break; } - foreach(i, ch; test_str) - { - assert(buf.data[count] == ch, "Buffer doesn't match test_str"); - count += 1; - } - - string test_str_3 = "not "; - insert = (cast(u8*)test_str_3.ptr)[0 .. test_str_3.length]; - Insert(&buf, insert, insert.length, 8); - GetLines(&buf, line_buf, 0); - - string result = "This is not an additional string. This is a test string for testing the GapBuffer struct."; - foreach(i, ch; result) - { - assert(buf.data[i] == ch, "Buffer doesn't match result"); - } - } - - // Grow Buffer - { - u8[] data; - GapBuffer buf = CreateGapBuffer(data); - - foreach(i; 0 .. buf.data.length) - { - Insert(&buf, ['a'], 1, 0); - } - - foreach(i; 0 .. 1024) - { - Insert(&buf, ['b'], 1, 0); - } - - u8[][] line_buf = new u8[][](1, 8096); - GetLines(&buf, line_buf, 0); - - foreach(i; 0 .. 5120) - { - if (i < 1024) - { - assert(line_buf[0][i] == 'b'); - } - else - { - assert(line_buf[0][i] == 'a'); - } - } + Cycle(&editor); } } diff --git a/src/shaders/gui.frag.glsl b/src/shaders/gui.frag.glsl new file mode 100644 index 0000000..60666e9 --- /dev/null +++ b/src/shaders/gui.frag.glsl @@ -0,0 +1,18 @@ +#version 460 + +#extension GL_GOOGLE_include_directive : require + +#include "gui.layout" + +layout (location = 0) in struct FragDataIn { + vec4 color; + vec2 uv; +} FragData; + +layout (location = 0) out vec4 FragColor; + +void main() +{ + vec4 tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FragData.uv); + FragColor = FragData.color * tex_color; +} diff --git a/src/shaders/gui.layout b/src/shaders/gui.layout new file mode 100644 index 0000000..543f030 --- /dev/null +++ b/src/shaders/gui.layout @@ -0,0 +1,9 @@ +layout (rgba16f, set = 0, binding = 0) uniform image2D DrawImage; + +layout (set = 0, binding = 1) uniform sampler SamplerNearest; + +layout (set = 1, binding = 0) uniform texture2D SpriteAtlas; + +layout (push_constant) uniform Constants { + vec2 res; +} PC; diff --git a/src/shaders/gui.vert.glsl b/src/shaders/gui.vert.glsl new file mode 100644 index 0000000..ea2b1bc --- /dev/null +++ b/src/shaders/gui.vert.glsl @@ -0,0 +1,56 @@ +#version 460 + +#extension GL_GOOGLE_include_directive : require + +#include "gui.layout" + +layout (location = 0) in vec2 in_dst_start; +layout (location = 1) in vec2 in_dst_end; +layout (location = 2) in vec2 in_src_start; +layout (location = 3) in vec2 in_src_end; +layout (location = 4) in vec4 in_col; + +layout (location = 0) out struct FragDataOut { + vec4 color; + vec2 uv; +} FragData; + +vec2 Vertices[4] = vec2[4]( + vec2(-1.0, +1.0), + vec2(-1.0, -1.0), + vec2(+1.0, +1.0), + vec2(+1.0, -1.0) +); + +vec2 rotate(vec2 coords, float theta) +{ + return mat2(cos(theta), sin(theta), -sin(theta), cos(theta)) * coords; +} + +void main() +{ + ivec2 tex_size = textureSize(sampler2D(SpriteAtlas, SamplerNearest), 0); + + vec2 dst_half_size = (in_dst_end - in_dst_start) / 2; + vec2 dst_center = (in_dst_end + in_dst_start) / 2; + vec2 dst_pos = (Vertices[gl_VertexIndex] * dst_half_size + dst_center); + + vec2 src_half_size = (in_src_end - in_src_start) / 2; + vec2 src_center = (in_src_end + in_src_start) / 2; + vec2 src_pos = (Vertices[gl_VertexIndex] * src_half_size + src_center); + + vec2 uvs[4] = vec2[4]( + vec2(in_src_start.x, in_src_start.y), + vec2(in_src_start.x, in_src_end.y), + vec2(in_src_end.x, in_src_start.y), + vec2(in_src_end.x, in_src_end.y) + ); + + FragData.color = in_col; + FragData.uv = uvs[gl_VertexIndex] / tex_size; + + gl_Position = vec4(2 * dst_pos.x / PC.res.x - 1, + 2 * dst_pos.y / PC.res.y - 1, + 0, + 1); +}