set up renderer

This commit is contained in:
Matthew 2025-08-17 15:09:40 +10:00
parent 8a01e2d1b9
commit 487b402b9f
10 changed files with 336 additions and 69 deletions

BIN
assets/gui.frag.spv Normal file

Binary file not shown.

BIN
assets/gui.vert.spv Normal file

Binary file not shown.

BIN
assets/pc-9800.ttf Normal file

Binary file not shown.

@ -1 +1 @@
Subproject commit 38e4e44b4dbecd8b0cbe878adb4f8da397a1ae45 Subproject commit c0d9de9c4e7fd33e3329745496e601431f6e2970

@ -1 +1 @@
Subproject commit 1290c5f45ac49cc5534817e8d6b0adae4ac058af Subproject commit 16820bdde294d59425fda6e115ae7b2d1e532e51

240
src/editor/editor.d Normal file
View File

@ -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;
}

View File

@ -1,76 +1,20 @@
import aliases; import platform;
import std.stdio; import editor;
import buffer;
import util;
void main(string[] argv) 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."; HandleEvents(&window);
string test_str_2 = "This is an additional string. "; if (window.close)
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)
{ {
assert(buf.data[count] == ch, "Buffer doesn't match test_str_2"); break;
count += 1;
} }
foreach(i, ch; test_str) Cycle(&editor);
{
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');
}
}
} }
} }

18
src/shaders/gui.frag.glsl Normal file
View File

@ -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;
}

9
src/shaders/gui.layout Normal file
View File

@ -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;

56
src/shaders/gui.vert.glsl Normal file
View File

@ -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);
}