set up nogc

This commit is contained in:
matthew 2025-08-14 17:16:04 +10:00
parent 48493f91bb
commit f601729468
14 changed files with 252 additions and 145 deletions

Binary file not shown.

Binary file not shown.

View File

@ -9,13 +9,13 @@
"targetPath": "build", "targetPath": "build",
"sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a", "build/libcglm.a"], "sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a", "build/libcglm.a"],
"sourceFiles-windows": [], "sourceFiles-windows": [],
"importPaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"], "importPaths": ["src/gears", "src/shared", "external/xxhash", "external/inteli"],
"sourcePaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"], "sourcePaths": ["src/gears", "src/shared", "external/xxhash", "external/inteli"],
"libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++", "xcb-xfixes", "freetype"], "libs-linux": ["xcb", "X11", "X11-xcb", "vulkan", "stdc++", "xcb-xfixes", "freetype"],
"libs-windows": [], "libs-windows": [],
"preGenerateCommands-linux": ["./build.sh"], "preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [], "preGenerateCommands-windows": [],
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2"], "dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-vgc"],
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"] "dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
}, },
{ {

View File

@ -83,6 +83,12 @@ struct Game
UIPushConst ui_pc; UIPushConst ui_pc;
u8[] font_data;
FontAtlasBuf atlas_buf;
FontFace font;
Buffer temp_buffer;
Timer timer; Timer timer;
} }
@ -99,8 +105,9 @@ InitGame(PlatformWindow* window)
UVec2 ext = GetExtent(&g.rd); UVec2 ext = GetExtent(&g.rd);
DescLayoutBinding[] layout_bindings = [ DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL }, { binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
{ binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
]; ];
g.ui_desc_layout = CreateDescSetLayout(&g.rd, layout_bindings); g.ui_desc_layout = CreateDescSetLayout(&g.rd, layout_bindings);
@ -110,11 +117,13 @@ InitGame(PlatformWindow* window)
u8[16*16*4] white_tex; u8[16*16*4] white_tex;
white_tex[] = u8.max; white_tex[] = u8.max;
CreateImageView(&g.rd, &g.font_tex, FONT_ATLAS_TEST.atlas.width, FONT_ATLAS_TEST.atlas.height, 4, FONT_ATLAS_TEST.data); Attribute[5] attributes = [
// TODO: fix buffer overflow between two textures { binding: 0, location: 0, format: FMT.RG_F32, offset: UIVertex.dst_start.offsetof },
//CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex); { binding: 0, location: 1, format: FMT.RG_F32, offset: UIVertex.dst_end.offsetof },
{ binding: 0, location: 2, format: FMT.RG_F32, offset: UIVertex.src_start.offsetof },
WriteGUI(&g.rd, g.ui_desc_set, &g.font_tex); { binding: 0, location: 3, format: FMT.RG_F32, offset: UIVertex.src_end.offsetof },
{ binding: 0, location: 4, format: FMT.RGBA_F32, offset: UIVertex.col.offsetof },
];
GfxPipelineInfo ui_info = { GfxPipelineInfo ui_info = {
vertex_shader: "shaders/gui.vert.spv", vertex_shader: "shaders/gui.vert.spv",
@ -122,13 +131,7 @@ InitGame(PlatformWindow* window)
input_rate: IR.Instance, input_rate: IR.Instance,
input_rate_stride: UIVertex.sizeof, input_rate_stride: UIVertex.sizeof,
layout: g.ui_layout, layout: g.ui_layout,
vertex_attributes: [ vertex_attributes: attributes,
{ binding: 0, location: 0, format: FMT.RG_F32, offset: UIVertex.dst_start.offsetof },
{ binding: 0, location: 1, format: FMT.RG_F32, offset: UIVertex.dst_end.offsetof },
{ binding: 0, location: 2, format: FMT.RG_F32, offset: UIVertex.src_start.offsetof },
{ binding: 0, location: 3, format: FMT.RG_F32, offset: UIVertex.src_end.offsetof },
{ binding: 0, location: 4, format: FMT.RGBA_F32, offset: UIVertex.col.offsetof },
],
}; };
g.ui_pipeline = CreateGraphicsPipeline(&g.rd, &ui_info); g.ui_pipeline = CreateGraphicsPipeline(&g.rd, &ui_info);
@ -139,7 +142,16 @@ InitGame(PlatformWindow* window)
g.ui_vertex_buf = GetUIVertexBuffer(&g.rd); g.ui_vertex_buf = GetUIVertexBuffer(&g.rd);
g.ui_index_buf = GetUIIndexBuffer(&g.rd); g.ui_index_buf = GetUIIndexBuffer(&g.rd);
WaitForTransfers(&g.rd); u8[] font_data = LoadAssetData(&g.arena, "fonts/NuberNextCondensed-DemiBold.otf");
g.font = OpenFont(font_data);
g.atlas_buf = CreateAtlas(&g.arena, g.font, 32, 256);
CreateImageView(&g.rd, &g.font_tex, g.atlas_buf.atlas.width, g.atlas_buf.atlas.height, 4, g.atlas_buf.data);
CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex);
CreateBuffer(&g.rd, &g.temp_buffer, BT.Uniform, f32.sizeof*2, false, false);
WriteGUI(&g.rd, g.ui_desc_set, &g.font_tex, &g.temp_buffer);
Reset(&g.frame_arena); Reset(&g.frame_arena);
@ -171,8 +183,22 @@ Cycle(Game* g)
Reset(&g.frame_arena); Reset(&g.frame_arena);
static bool alt = false;
if (alt)
{
f32[2] scale = [2.0, 2.0];
assert(Transfer(&g.rd, &g.temp_buffer, scale), "Cycle Transfer failed");
}
else
{
f32[2] scale = [1.0, 1.0];
assert(Transfer(&g.rd, &g.temp_buffer, scale), "Cycle Transfer failed");
}
alt = !alt;
DrawRect(g, 500.0, 500.0, 800.0, 800.0, Vec4(0.2, 0.3, 0.7, 1.0)); DrawRect(g, 500.0, 500.0, 800.0, 800.0, Vec4(0.2, 0.3, 0.7, 1.0));
DrawText(g, 200.0, 200.0, 128.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); DrawText(g, 200.0, 200.0, 16.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
BeginFrame(&g.rd); BeginFrame(&g.rd);
@ -318,10 +344,10 @@ void
DrawText(Game* g, f32 x, f32 y, f32 px, string str) DrawText(Game* g, f32 x, f32 y, f32 px, string str)
{ {
f32 x_pos = x; f32 x_pos = x;
f32 scale = px / FONT_ATLAS_TEST.atlas.size; f32 scale = px / g.atlas_buf.atlas.size;
foreach(ch; str) foreach(ch; str)
{ {
foreach(glyph; FONT_ATLAS_TEST.atlas.glyphs) foreach(glyph; g.atlas_buf.atlas.glyphs)
{ {
if (ch == glyph.ch) if (ch == glyph.ch)
{ {

View File

@ -8,6 +8,7 @@ import util;
import core.simd; import core.simd;
import math; import math;
import core.stdc.string : memcpy; import core.stdc.string : memcpy;
import fonts;
// TODO: // TODO:
// 1. Determine how to better handle inputs // 1. Determine how to better handle inputs
@ -16,11 +17,7 @@ import core.stdc.string : memcpy;
void main(string[] argv) void main(string[] argv)
{ {
Arena arena = CreateArena(MB(32));
InitFreeType(); InitFreeType();
u8[] font_data = LoadAssetData(&arena, "fonts/NuberNextCondensed-DemiBold.otf");
FontFace font = OpenFont(font_data);
FONT_ATLAS_TEST = CreateAtlas(&arena, font, 128.0, 2048);
PlatformWindow window = CreateWindow("Video Game", 1920, 1080); PlatformWindow window = CreateWindow("Video Game", 1920, 1080);
Game g = InitGame(&window); Game g = InitGame(&window);

View File

@ -5,6 +5,8 @@ import core.memory;
import core.thread.osthread; import core.thread.osthread;
import core.time; import core.time;
@nogc:
const WINDOW_EDGE_BUFFER = 50; const WINDOW_EDGE_BUFFER = 50;
enum Input enum Input
@ -40,6 +42,7 @@ alias KBI = Input;
version(linux) version(linux)
{ {
import core.sys.posix.dlfcn; import core.sys.posix.dlfcn;
import core.sys.posix.sys.mman;
struct InputEvent struct InputEvent
{ {
@ -130,7 +133,7 @@ CreateWindow(string name, u16 width, u16 height)
XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY; XCB_EVENT_MASK_STRUCTURE_NOTIFY;
i32[] val_win = [window.screen.black_pixel, event_mask]; i32[2] val_win = [window.screen.black_pixel, event_mask];
i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
window.window = xcb_generate_id(window.conn); window.window = xcb_generate_id(window.conn);
@ -599,6 +602,18 @@ case XK_g:
} }
} }
void*
MemAlloc(u64 size)
{
return mmap(null, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
}
void
MemFree(void* ptr, u64 size)
{
assert(munmap(ptr, size) == 0, "MemFree failure");
}
} }
version(Windows) version(Windows)

View File

@ -535,7 +535,7 @@ InitConversionPipeline(Vulkan* vk)
{ {
Push(vk, SI.Pipelines); Push(vk, SI.Pipelines);
VkDescriptorSetLayoutBinding[] layout_bindings = [ VkDescriptorSetLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_COMPUTE_BIT }, { binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_COMPUTE_BIT },
{ binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_COMPUTE_BIT }, { binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_COMPUTE_BIT },
]; ];
@ -545,19 +545,20 @@ InitConversionPipeline(Vulkan* vk)
vk.conv_pipeline_layout = CreatePipelineLayout(vk, vk.conv_desc_layout, ConvPushConst.sizeof, true); vk.conv_pipeline_layout = CreatePipelineLayout(vk, vk.conv_desc_layout, ConvPushConst.sizeof, true);
u32 channels = 1; u32 channels = 1;
SpecEntry[1] entries = [
{
constantID: 0,
size: u32.sizeof,
offset: 0,
}
];
CompPipelineInfo conv_info = { CompPipelineInfo conv_info = {
shader: "shaders/convert.comp.spv", shader: "shaders/convert.comp.spv",
layout: vk.conv_pipeline_layout, layout: vk.conv_pipeline_layout,
spec: { spec: {
data: &channels, data: &channels,
size: u32.sizeof, size: u32.sizeof,
entries: [ entries: entries,
{
constantID: 0,
size: u32.sizeof,
offset: 0,
}
],
}, },
}; };
@ -599,11 +600,12 @@ CreateBuffer(Vulkan* vk, Buffer* buf, BufferType type, u64 size, bool host_visib
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; alloc_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
} }
u32[2] indices = [vk.queues.gfx_index, vk.queues.tfer_index];
if (vk.queues.gfx_index != vk.queues.tfer_index) if (vk.queues.gfx_index != vk.queues.tfer_index)
{ {
buffer_info.sharingMode = VK_SHARING_MODE_CONCURRENT; buffer_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
buffer_info.queueFamilyIndexCount = 2; buffer_info.queueFamilyIndexCount = 2;
buffer_info.pQueueFamilyIndices = cast(const u32*)[vk.queues.gfx_index, vk.queues.tfer_index]; buffer_info.pQueueFamilyIndices = indices.ptr;
} }
VmaAllocationInfo vma_info; VmaAllocationInfo vma_info;
@ -731,7 +733,7 @@ BeginRendering(Vulkan* vk)
VkImage image = CurrentImage(vk); VkImage image = CurrentImage(vk);
Transition(vk.cmds[vk.frame_index], image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); Transition(vk.cmds[vk.frame_index], image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkClearValue[] clear_color = [ VkClearValue[2] clear_color = [
{ {
color: { color: {
float32: [0.0, 0.0, 0.0, 1.0], float32: [0.0, 0.0, 0.0, 1.0],
@ -900,7 +902,7 @@ DrawIndexed(Vulkan* vk, u32 index_count, u32 instance_count, u32 index_offset)
} }
bool bool
ImmSubmit(Vulkan* vk, void delegate() fn) ImmSubmitStart(Vulkan* vk)
{ {
VkResult result = vkWaitForFences(vk.device, 1, &vk.imm_fence, true, 999999999); VkResult result = vkWaitForFences(vk.device, 1, &vk.imm_fence, true, 999999999);
bool success = VkCheck("ImmSubmit failure: vkWaitForFences error", result); bool success = VkCheck("ImmSubmit failure: vkWaitForFences error", result);
@ -917,7 +919,6 @@ ImmSubmit(Vulkan* vk, void delegate() fn)
success = VkCheck("ImmSubmit failure: vkResetCommandBuffer error", result); success = VkCheck("ImmSubmit failure: vkResetCommandBuffer error", result);
} }
bool imm_started;
if (success) if (success)
{ {
VkCommandBufferBeginInfo cmd_info = { VkCommandBufferBeginInfo cmd_info = {
@ -926,37 +927,74 @@ ImmSubmit(Vulkan* vk, void delegate() fn)
}; };
result = vkBeginCommandBuffer(vk.imm_cmd, &cmd_info); result = vkBeginCommandBuffer(vk.imm_cmd, &cmd_info);
imm_started = success = VkCheck("ImmSubmit failure: vkBeginCommandBuffer error", result); success = VkCheck("ImmSubmit failure: vkBeginCommandBuffer error", result);
} }
return success;
}
bool
ImmSubmitFinish(Vulkan* vk)
{
VkCommandBufferSubmitInfo cmd_info = {
sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
commandBuffer: vk.imm_cmd,
};
VkSubmitInfo2 submit_info = {
sType: VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
commandBufferInfoCount: 1,
pCommandBufferInfos: &cmd_info,
};
VkResult result = vkQueueSubmit2(vk.tfer_queue, 1, &submit_info, vk.imm_fence);
return VkCheck("ImmSubmit failure: vkQueueSubmit2 error", result);
}
bool
ImmSubmit(Vulkan* vk, Image* image, VkBufferImageCopy copy, void function(Vulkan*, Image*, VkBufferImageCopy) fn)
{
bool success = ImmSubmitStart(vk);
if (success) if (success)
{ {
fn(); fn(vk, image, copy);
result = vkEndCommandBuffer(vk.imm_cmd); VkResult result = vkEndCommandBuffer(vk.imm_cmd);
success = VkCheck("ImmSubmit failure: vkEndCommandBuffer error", result); success = VkCheck("ImmSubmit failure: vkEndCommandBuffer error", result);
} }
if (success) if (success)
{ {
VkCommandBufferSubmitInfo cmd_info = { success = ImmSubmitFinish(vk);
sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
commandBuffer: vk.imm_cmd,
};
VkSubmitInfo2 submit_info = {
sType: VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
commandBufferInfoCount: 1,
pCommandBufferInfos: &cmd_info,
};
result = vkQueueSubmit2(vk.tfer_queue, 1, &submit_info, vk.imm_fence);
success = VkCheck("ImmSubmit failure: vkQueueSubmit2 error", result);
} }
return success; return success;
} }
bool
ImmSubmit(Vulkan* vk, Buffer* buf, VkBufferCopy copy, void function(Vulkan*, Buffer*, VkBufferCopy) fn)
{
bool success = ImmSubmitStart(vk);
if (success)
{
fn(vk, buf, copy);
VkResult result = vkEndCommandBuffer(vk.imm_cmd);
success = VkCheck("ImmSubmit failure: vkEndCommandBuffer error", result);
}
if (success)
{
success = ImmSubmitFinish(vk);
}
return success;
}
bool bool
TransferAssets(Vulkan* vk) TransferAssets(Vulkan* vk)
{ {
@ -1120,11 +1158,12 @@ CreateImageView(Vulkan* vk, ImageView* view, u32 w, u32 h, Format format, ImageU
}, },
}; };
u32[2] indices = [vk.gfx_index, vk.tfer_index];
if (vk.gfx_index != vk.tfer_index) if (vk.gfx_index != vk.tfer_index)
{ {
image_info.sharingMode = VK_SHARING_MODE_CONCURRENT; image_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
image_info.queueFamilyIndexCount = 2; image_info.queueFamilyIndexCount = 2;
image_info.pQueueFamilyIndices = cast(const u32*)[vk.gfx_index, vk.tfer_index]; image_info.pQueueFamilyIndices = indices.ptr;
} }
VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, &view.alloc, null); VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, &view.alloc, null);
@ -1225,22 +1264,24 @@ Transfer(Vulkan* vk, Buffer* buf, u8[] data)
vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length]; vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length];
auto fn = delegate() auto fn = function(Vulkan* vk, Buffer* buf, VkBufferCopy copy)
{ {
VkBufferCopy copy = {
srcOffset: 0,
dstOffset: copied,
size: copy_length,
};
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy); vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
}; };
success = ImmSubmit(vk, fn); VkBufferCopy copy = {
srcOffset: 0,
dstOffset: copied,
size: copy_length,
};
success = ImmSubmit(vk, buf, copy, fn);
copied += copy_length; copied += copy_length;
} }
WaitForTransfers(vk);
return success; return success;
} }
@ -1254,20 +1295,22 @@ Transfer(T)(Vulkan* vk, Buffer* buf, T* ptr)
{ {
memcpy(vk.transfer_buf.data.ptr, ptr, T.sizeof); memcpy(vk.transfer_buf.data.ptr, ptr, T.sizeof);
auto fn = delegate() auto fn = function(Vulkan* vk, Buffer* buf, VkBufferCopy copy)
{ {
VkBufferCopy copy = {
srcOffset: 0,
dstOffset: 0,
size: T.sizeof,
};
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy); vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
}; };
success = ImmSubmit(vk, fn); VkBufferCopy copy = {
srcOffset: 0,
dstOffset: 0,
size: T.sizeof,
};
success = ImmSubmit(vk, buf, copy, fn);
} }
WaitForTransfers(vk);
return success; return success;
} }
@ -1299,23 +1342,8 @@ Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h)
vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length]; vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length];
auto fn = delegate() auto fn = function(Vulkan* vk, Image* image, VkBufferImageCopy copy)
{ {
VkBufferImageCopy copy = {
bufferRowLength: w,
bufferImageHeight: h,
imageSubresource: {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
layerCount: 1,
},
imageExtent: {
width: w,
height: h,
depth: 1,
},
bufferOffset: copied,
};
Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(vk.imm_cmd, vk.transfer_buf.buffer, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy); vkCmdCopyBufferToImage(vk.imm_cmd, vk.transfer_buf.buffer, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
@ -1323,11 +1351,28 @@ Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h)
Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}; };
success = ImmSubmit(vk, fn); VkBufferImageCopy copy = {
bufferRowLength: w,
bufferImageHeight: h,
imageSubresource: {
aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
layerCount: 1,
},
imageExtent: {
width: w,
height: h,
depth: 1,
},
bufferOffset: copied,
};
success = ImmSubmit(vk, image, copy, fn);
copied += copy_length; copied += copy_length;
} }
WaitForTransfers(vk);
return success; return success;
} }
@ -1577,7 +1622,7 @@ BuildShader(Vulkan* vk, u8[] bytes)
void void
InitFramebufferAndRenderPass(Vulkan* vk) InitFramebufferAndRenderPass(Vulkan* vk)
{ {
VkAttachmentDescription[] attach_descriptions = [ VkAttachmentDescription[2] attach_descriptions = [
{ {
format: vk.draw_image.format, format: vk.draw_image.format,
samples: VK_SAMPLE_COUNT_1_BIT, samples: VK_SAMPLE_COUNT_1_BIT,
@ -1666,7 +1711,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
pipeline.type = VK_PIPELINE_BIND_POINT_GRAPHICS; pipeline.type = VK_PIPELINE_BIND_POINT_GRAPHICS;
pipeline.layout = build_info.layout; pipeline.layout = build_info.layout;
VkDynamicState[] dyn_state = [ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR ]; VkDynamicState[2] dyn_state = [ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR ];
VkPipelineDynamicStateCreateInfo dyn_info = { VkPipelineDynamicStateCreateInfo dyn_info = {
sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
@ -1753,7 +1798,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
scissorCount: 1, scissorCount: 1,
}; };
VkPipelineShaderStageCreateInfo[] shader_info = [ VkPipelineShaderStageCreateInfo[2] shader_info = [
{ {
sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
stage: VK_SHADER_STAGE_FRAGMENT_BIT, stage: VK_SHADER_STAGE_FRAGMENT_BIT,
@ -2047,7 +2092,7 @@ InitDescriptors(Vulkan* vk)
void void
PushDescriptorPool(Vulkan* vk) PushDescriptorPool(Vulkan* vk)
{ {
VkDescriptorPoolSize[] pool_sizes = [ VkDescriptorPoolSize[7] pool_sizes = [
{ type: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 4096 }, { type: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 4096 },
{ type: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 4096 }, { type: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 4096 },
{ type: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, descriptorCount: 4096 }, { type: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, descriptorCount: 4096 },
@ -2104,7 +2149,7 @@ InitGlobalDescSet(Vulkan* vk)
if (success) if (success)
{ {
DescLayoutBinding[] layout_bindings = [ DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL }, { binding: 0, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
{ binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL }, { binding: 1, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
]; ];
@ -2135,14 +2180,20 @@ InitGlobalDescSet(Vulkan* vk)
} }
void void
WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas) WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas, Buffer* buf)
{ {
VkDescriptorImageInfo image_info = { VkDescriptorImageInfo image_info = {
imageView: atlas.view, imageView: atlas.view,
imageLayout: atlas.layout, imageLayout: atlas.layout,
}; };
VkWriteDescriptorSet[] writes = [ VkDescriptorBufferInfo buf_info = {
buffer: buf.buffer,
range: buf.size,
offset: 0,
};
VkWriteDescriptorSet[2] writes = [
{ {
sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
dstSet: set.handle, dstSet: set.handle,
@ -2150,6 +2201,14 @@ WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas)
descriptorCount: 1, descriptorCount: 1,
descriptorType: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
pImageInfo: &image_info, pImageInfo: &image_info,
},
{
sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
dstSet: set.handle,
dstBinding: 1,
descriptorCount: 1,
descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
pBufferInfo: &buf_info,
} }
]; ];

View File

@ -2,6 +2,8 @@ public import includes;
import platform; import platform;
import vulkan : Vulkan, VULKAN_LIBS; import vulkan : Vulkan, VULKAN_LIBS;
@nogc:
// Global Functions // Global Functions
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = null; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = null;

View File

@ -1,5 +1,9 @@
layout (set = 1, binding = 0) uniform texture2D SpriteAtlas; layout (set = 1, binding = 0) uniform texture2D SpriteAtlas;
layout (set = 1, binding = 1) uniform Scale {
vec2 factor;
} S;
layout (push_constant) uniform Constants { layout (push_constant) uniform Constants {
vec2 res; vec2 res;
} PC; } PC;

View File

@ -50,8 +50,8 @@ void main()
FragData.color = in_col; FragData.color = in_col;
FragData.uv = uvs[gl_VertexIndex] / tex_size; FragData.uv = uvs[gl_VertexIndex] / tex_size;
gl_Position = vec4(2 * dst_pos.x / PC.res.x - 1, gl_Position = vec4((2 * dst_pos.x / PC.res.x - 1) * S.factor.x,
2 * dst_pos.y / PC.res.y - 1, (2 * dst_pos.y / PC.res.y - 1) * S.factor.y,
0, 0,
1); 1);
} }

View File

@ -3,6 +3,7 @@ import math;
import std.stdio; import std.stdio;
import core.stdc.string : memset; import core.stdc.string : memset;
import core.memory; import core.memory;
import platform;
const DEFAULT_ALIGNMENT = (void *).sizeof * 2; const DEFAULT_ALIGNMENT = (void *).sizeof * 2;
@ -13,6 +14,32 @@ struct Arena
u64 pos; u64 pos;
}; };
T*
MAlloc(T)()
{
void* mem = MemAlloc(T.sizeof);
return cast(T*)mem;
}
T[]
MAllocArray(T)(u64 count)
{
void* mem = MemAlloc(T.sizeof * count);
return cast(T*)(mem)[0 .. count];
}
void
MFree(T)(T* ptr)
{
MemFree(cast(void*)ptr, T.sizeof);
}
void
MFreeArray(T)(T[] slice)
{
MemFree(cast(void*)slice.ptr, cast(u64)slice.length);
}
T* T*
Alloc(T)() Alloc(T)()
{ {

View File

@ -1,3 +1,5 @@
#pragma attribute(push, nogc, nothrow)
#ifdef __linux__ #ifdef __linux__
# include <xcb/xcb.h> # include <xcb/xcb.h>
# include <xcb/xfixes.h> # include <xcb/xfixes.h>

View File

@ -8,63 +8,38 @@ import core.simd;
import std.conv; import std.conv;
import std.string; import std.string;
enum AtlasType struct DynSlice(T)
{ {
None = 0, T[][] slices;
SoftMask, u32 length;
u32 capacity;
u32 grow_size;
} }
enum YOrigin DynSlice!(T)
CreateDynSlice(T)(u32 size)
{ {
None = 0, DynSlice!(T) dslice = {
Bottom, slices: MAllocArray!(T[])(size),
length: 0,
capacity: size,
grow_size: size,
};
dslice.slices[0] = MAllocArray!(T)(size);
return dslice;
} }
struct FontAtlas u32
Next(T)(DynSlice!(T)* slice)
{ {
AtlasType type; if (slice.length < slice.capacity)
f32 size; {
u32 width;
u32 height; }
YOrigin y_origin;
f32 em_size;
f32 line_height;
f32 ascender;
f32 descender;
f32 underline_y;
f32 underline_thickness;
Glyph[] glyphs;
}
struct Glyph return 0;
{
dchar ch;
f32 advance;
f32 plane_left;
f32 plane_bottom;
f32 plane_right;
f32 plane_top;
f32 atlas_left;
f32 atlas_bottom;
f32 atlas_right;
f32 atlas_top;
}
struct TrackedSlice(T)
{
T[] slice;
u32 free_index;
}
alias TSlice = TrackedSlice;
TSlice!(T)
InitTrackedSlice(T)(u32 length)
{
TSlice!(T) tslice;
tslice.slice = AllocArray!(T[])(length);
return tslice;
} }
void void