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",
"sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a", "build/libcglm.a"],
"sourceFiles-windows": [],
"importPaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"],
"sourcePaths": ["src/gears", "src/codegen", "src/shared", "external/xxhash", "external/inteli"],
"importPaths": ["src/gears", "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-windows": [],
"preGenerateCommands-linux": ["./build.sh"],
"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"]
},
{

View File

@ -83,6 +83,12 @@ struct Game
UIPushConst ui_pc;
u8[] font_data;
FontAtlasBuf atlas_buf;
FontFace font;
Buffer temp_buffer;
Timer timer;
}
@ -99,8 +105,9 @@ InitGame(PlatformWindow* window)
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: 1, descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
];
g.ui_desc_layout = CreateDescSetLayout(&g.rd, layout_bindings);
@ -110,11 +117,13 @@ InitGame(PlatformWindow* window)
u8[16*16*4] white_tex;
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);
// TODO: fix buffer overflow between two textures
//CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex);
WriteGUI(&g.rd, g.ui_desc_set, &g.font_tex);
Attribute[5] 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 },
];
GfxPipelineInfo ui_info = {
vertex_shader: "shaders/gui.vert.spv",
@ -122,13 +131,7 @@ InitGame(PlatformWindow* window)
input_rate: IR.Instance,
input_rate_stride: UIVertex.sizeof,
layout: g.ui_layout,
vertex_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 },
],
vertex_attributes: attributes,
};
g.ui_pipeline = CreateGraphicsPipeline(&g.rd, &ui_info);
@ -139,7 +142,16 @@ InitGame(PlatformWindow* window)
g.ui_vertex_buf = GetUIVertexBuffer(&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);
@ -171,8 +183,22 @@ Cycle(Game* g)
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));
DrawText(g, 200.0, 200.0, 128.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
DrawText(g, 200.0, 200.0, 16.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
BeginFrame(&g.rd);
@ -318,10 +344,10 @@ void
DrawText(Game* g, f32 x, f32 y, f32 px, string str)
{
f32 x_pos = x;
f32 scale = px / FONT_ATLAS_TEST.atlas.size;
f32 scale = px / g.atlas_buf.atlas.size;
foreach(ch; str)
{
foreach(glyph; FONT_ATLAS_TEST.atlas.glyphs)
foreach(glyph; g.atlas_buf.atlas.glyphs)
{
if (ch == glyph.ch)
{

View File

@ -8,6 +8,7 @@ import util;
import core.simd;
import math;
import core.stdc.string : memcpy;
import fonts;
// TODO:
// 1. Determine how to better handle inputs
@ -16,11 +17,7 @@ import core.stdc.string : memcpy;
void main(string[] argv)
{
Arena arena = CreateArena(MB(32));
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);
Game g = InitGame(&window);

View File

@ -5,6 +5,8 @@ import core.memory;
import core.thread.osthread;
import core.time;
@nogc:
const WINDOW_EDGE_BUFFER = 50;
enum Input
@ -40,6 +42,7 @@ alias KBI = Input;
version(linux)
{
import core.sys.posix.dlfcn;
import core.sys.posix.sys.mman;
struct InputEvent
{
@ -130,7 +133,7 @@ CreateWindow(string name, u16 width, u16 height)
XCB_EVENT_MASK_POINTER_MOTION |
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;
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)

View File

@ -535,7 +535,7 @@ InitConversionPipeline(Vulkan* vk)
{
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: 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);
u32 channels = 1;
SpecEntry[1] entries = [
{
constantID: 0,
size: u32.sizeof,
offset: 0,
}
];
CompPipelineInfo conv_info = {
shader: "shaders/convert.comp.spv",
layout: vk.conv_pipeline_layout,
spec: {
data: &channels,
size: u32.sizeof,
entries: [
{
constantID: 0,
size: u32.sizeof,
offset: 0,
}
],
entries: entries,
},
};
@ -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;
}
u32[2] indices = [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.queueFamilyIndexCount = 2;
buffer_info.pQueueFamilyIndices = cast(const u32*)[vk.queues.gfx_index, vk.queues.tfer_index];
buffer_info.pQueueFamilyIndices = indices.ptr;
}
VmaAllocationInfo vma_info;
@ -731,7 +733,7 @@ BeginRendering(Vulkan* vk)
VkImage image = CurrentImage(vk);
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: {
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
ImmSubmit(Vulkan* vk, void delegate() fn)
ImmSubmitStart(Vulkan* vk)
{
VkResult result = vkWaitForFences(vk.device, 1, &vk.imm_fence, true, 999999999);
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);
}
bool imm_started;
if (success)
{
VkCommandBufferBeginInfo cmd_info = {
@ -926,19 +927,15 @@ ImmSubmit(Vulkan* vk, void delegate() fn)
};
result = vkBeginCommandBuffer(vk.imm_cmd, &cmd_info);
imm_started = success = VkCheck("ImmSubmit failure: vkBeginCommandBuffer error", result);
success = VkCheck("ImmSubmit failure: vkBeginCommandBuffer error", result);
}
if (success)
{
fn();
return success;
}
result = vkEndCommandBuffer(vk.imm_cmd);
success = VkCheck("ImmSubmit failure: vkEndCommandBuffer error", result);
}
if (success)
{
bool
ImmSubmitFinish(Vulkan* vk)
{
VkCommandBufferSubmitInfo cmd_info = {
sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
commandBuffer: vk.imm_cmd,
@ -950,13 +947,54 @@ ImmSubmit(Vulkan* vk, void delegate() fn)
pCommandBufferInfos: &cmd_info,
};
result = vkQueueSubmit2(vk.tfer_queue, 1, &submit_info, vk.imm_fence);
success = VkCheck("ImmSubmit failure: vkQueueSubmit2 error", result);
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)
{
fn(vk, image, copy);
VkResult result = vkEndCommandBuffer(vk.imm_cmd);
success = VkCheck("ImmSubmit failure: vkEndCommandBuffer error", result);
}
if (success)
{
success = ImmSubmitFinish(vk);
}
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
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)
{
image_info.sharingMode = VK_SHARING_MODE_CONCURRENT;
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);
@ -1225,22 +1264,24 @@ Transfer(Vulkan* vk, Buffer* buf, u8[] data)
vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length];
auto fn = delegate()
auto fn = function(Vulkan* vk, Buffer* buf, VkBufferCopy copy)
{
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
};
VkBufferCopy copy = {
srcOffset: 0,
dstOffset: copied,
size: copy_length,
};
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
};
success = ImmSubmit(vk, fn);
success = ImmSubmit(vk, buf, copy, fn);
copied += copy_length;
}
WaitForTransfers(vk);
return success;
}
@ -1254,20 +1295,22 @@ Transfer(T)(Vulkan* vk, Buffer* buf, T* ptr)
{
memcpy(vk.transfer_buf.data.ptr, ptr, T.sizeof);
auto fn = delegate()
auto fn = function(Vulkan* vk, Buffer* buf, VkBufferCopy copy)
{
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
};
VkBufferCopy copy = {
srcOffset: 0,
dstOffset: 0,
size: T.sizeof,
};
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
};
success = ImmSubmit(vk, fn);
success = ImmSubmit(vk, buf, copy, fn);
}
WaitForTransfers(vk);
return success;
}
@ -1299,8 +1342,15 @@ Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h)
vk.transfer_buf.data[0 .. copy_length] = data[copied .. copy_length];
auto fn = delegate()
auto fn = function(Vulkan* vk, Image* image, VkBufferImageCopy copy)
{
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);
Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
};
VkBufferImageCopy copy = {
bufferRowLength: w,
bufferImageHeight: h,
@ -1316,18 +1366,13 @@ Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h)
bufferOffset: copied,
};
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);
Transition(vk.imm_cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
};
success = ImmSubmit(vk, fn);
success = ImmSubmit(vk, image, copy, fn);
copied += copy_length;
}
WaitForTransfers(vk);
return success;
}
@ -1577,7 +1622,7 @@ BuildShader(Vulkan* vk, u8[] bytes)
void
InitFramebufferAndRenderPass(Vulkan* vk)
{
VkAttachmentDescription[] attach_descriptions = [
VkAttachmentDescription[2] attach_descriptions = [
{
format: vk.draw_image.format,
samples: VK_SAMPLE_COUNT_1_BIT,
@ -1666,7 +1711,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
pipeline.type = VK_PIPELINE_BIND_POINT_GRAPHICS;
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 = {
sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
@ -1753,7 +1798,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info)
scissorCount: 1,
};
VkPipelineShaderStageCreateInfo[] shader_info = [
VkPipelineShaderStageCreateInfo[2] shader_info = [
{
sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
stage: VK_SHADER_STAGE_FRAGMENT_BIT,
@ -2047,7 +2092,7 @@ InitDescriptors(Vulkan* vk)
void
PushDescriptorPool(Vulkan* vk)
{
VkDescriptorPoolSize[] pool_sizes = [
VkDescriptorPoolSize[7] pool_sizes = [
{ type: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, descriptorCount: 4096 },
{ type: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, descriptorCount: 4096 },
{ type: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, descriptorCount: 4096 },
@ -2104,7 +2149,7 @@ InitGlobalDescSet(Vulkan* vk)
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: 1, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorCount: 1, stageFlags: VK_SHADER_STAGE_ALL },
];
@ -2135,14 +2180,20 @@ InitGlobalDescSet(Vulkan* vk)
}
void
WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas)
WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas, Buffer* buf)
{
VkDescriptorImageInfo image_info = {
imageView: atlas.view,
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,
dstSet: set.handle,
@ -2150,6 +2201,14 @@ WriteGUI(Vulkan* vk, DescSet set, ImageView* atlas)
descriptorCount: 1,
descriptorType: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
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 vulkan : Vulkan, VULKAN_LIBS;
@nogc:
// Global Functions
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = null;

View File

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

View File

@ -50,8 +50,8 @@ void main()
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,
gl_Position = vec4((2 * dst_pos.x / PC.res.x - 1) * S.factor.x,
(2 * dst_pos.y / PC.res.y - 1) * S.factor.y,
0,
1);
}

View File

@ -3,6 +3,7 @@ import math;
import std.stdio;
import core.stdc.string : memset;
import core.memory;
import platform;
const DEFAULT_ALIGNMENT = (void *).sizeof * 2;
@ -13,6 +14,32 @@ struct Arena
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*
Alloc(T)()
{

View File

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

View File

@ -8,63 +8,38 @@ import core.simd;
import std.conv;
import std.string;
enum AtlasType
struct DynSlice(T)
{
None = 0,
SoftMask,
T[][] slices;
u32 length;
u32 capacity;
u32 grow_size;
}
enum YOrigin
DynSlice!(T)
CreateDynSlice(T)(u32 size)
{
None = 0,
Bottom,
DynSlice!(T) dslice = {
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;
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;
}
if (slice.length < slice.capacity)
{
struct Glyph
{
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;
return 0;
}
void