2025-10-05 14:27:05 +01:00
|
|
|
// @Todo: These should move to draw/core.c
|
|
|
|
|
//
|
2025-10-04 21:42:04 +01:00
|
|
|
void G_ImagesLoad(G_State *game) {
|
|
|
|
|
M_TempScope(0, 0) {
|
2025-10-05 14:27:05 +01:00
|
|
|
D_Context *draw = &game->draw;
|
|
|
|
|
|
2025-10-04 23:46:13 +01:00
|
|
|
Str8 exe_path = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE);
|
2025-10-05 00:24:51 +01:00
|
|
|
Str8 path = Sf(temp.arena, "%.*s/assets", Sv(exe_path));
|
2025-10-04 23:46:13 +01:00
|
|
|
|
|
|
|
|
FS_List assets = FS_PathList(temp.arena, path);
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
Vk_Buffer *staging = &draw->staging;
|
|
|
|
|
staging->size = MB(256);
|
|
|
|
|
staging->usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
|
|
|
staging->host_visible = true;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
Vk_BufferCreate(staging);
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
U8 *base = staging->data;
|
2025-10-04 17:24:30 +01:00
|
|
|
U64 offset = 0;
|
|
|
|
|
|
|
|
|
|
Vk_CommandBuffer *cmds = Vk_CommandBufferPush();
|
|
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
// We reserve the first texture for the "white" texture
|
|
|
|
|
draw->n_images = 1;
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
for (FS_Entry *it = assets.first; it != 0; it = it->next) {
|
|
|
|
|
if (Str8_EndsWith(it->basename, S("png"))) {
|
2025-10-05 14:27:05 +01:00
|
|
|
draw->n_images += 1;
|
2025-10-04 17:24:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkBufferImageCopy copy = { 0 };
|
2025-10-05 14:27:05 +01:00
|
|
|
draw->images = M_ArenaPush(game->arena, D_Image, .count = draw->n_images);
|
|
|
|
|
draw->n_images = 1;
|
|
|
|
|
|
|
|
|
|
// Upload the white texture
|
|
|
|
|
{
|
|
|
|
|
D_Image *white = &draw->images[0];
|
|
|
|
|
U32 white_data[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
|
|
|
|
|
|
|
|
|
|
M_CopySize(base, white_data, sizeof(white_data));
|
|
|
|
|
|
|
|
|
|
copy.bufferOffset = offset;
|
|
|
|
|
copy.bufferRowLength = 0;
|
|
|
|
|
copy.bufferImageHeight = 0;
|
|
|
|
|
|
|
|
|
|
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
copy.imageSubresource.mipLevel = 0;
|
|
|
|
|
copy.imageSubresource.baseArrayLayer = 0;
|
|
|
|
|
copy.imageSubresource.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
copy.imageExtent.width = 2;
|
|
|
|
|
copy.imageExtent.height = 2;
|
|
|
|
|
copy.imageExtent.depth = 1;
|
|
|
|
|
|
|
|
|
|
base += sizeof(white_data);
|
|
|
|
|
offset += sizeof(white_data);
|
|
|
|
|
|
|
|
|
|
white->name = S("_WHITE");
|
|
|
|
|
|
|
|
|
|
white->image.width = 2;
|
|
|
|
|
white->image.height = 2;
|
|
|
|
|
white->image.format = VK_FORMAT_R8G8B8A8_SRGB;
|
|
|
|
|
white->image.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
|
|
|
|
|
|
Vk_ImageCreate(&white->image);
|
|
|
|
|
|
|
|
|
|
// We could combine all of these 'pre-transfer' and 'post-transfer' layers into one
|
|
|
|
|
// batch, it would simply mean doing three loops over the images and setting them all up
|
|
|
|
|
// and submitting them in one go. It doesn't really matter for now
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier2 transfer = { 0 };
|
|
|
|
|
VkImageMemoryBarrier2 shader_read = { 0 };
|
|
|
|
|
|
|
|
|
|
transfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
transfer.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
|
|
|
|
|
transfer.srcAccessMask = VK_ACCESS_2_NONE;
|
|
|
|
|
transfer.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
|
|
|
|
transfer.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
|
|
|
|
transfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
transfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
|
transfer.image = white->image.handle;
|
|
|
|
|
|
|
|
|
|
transfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
transfer.subresourceRange.layerCount = 1;
|
|
|
|
|
transfer.subresourceRange.levelCount = 1;
|
|
|
|
|
|
|
|
|
|
shader_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
shader_read.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
|
|
|
|
shader_read.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
|
|
|
|
shader_read.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
|
|
|
|
|
shader_read.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
|
|
|
|
|
shader_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
|
shader_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
|
shader_read.image = white->image.handle;
|
|
|
|
|
|
|
|
|
|
shader_read.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
shader_read.subresourceRange.layerCount = 1;
|
|
|
|
|
shader_read.subresourceRange.levelCount = 1;
|
|
|
|
|
|
|
|
|
|
VkDependencyInfo dep = { 0 };
|
|
|
|
|
dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
|
|
|
|
|
|
|
|
|
dep.imageMemoryBarrierCount = 1;
|
|
|
|
|
dep.pImageMemoryBarriers = &transfer;
|
|
|
|
|
|
|
|
|
|
vk.CmdPipelineBarrier2(cmds->handle, &dep);
|
|
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
vk.CmdCopyBufferToImage(cmds->handle, staging->handle, white->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
2025-10-05 14:27:05 +01:00
|
|
|
|
|
|
|
|
dep.pImageMemoryBarriers = &shader_read;
|
|
|
|
|
|
|
|
|
|
vk.CmdPipelineBarrier2(cmds->handle, &dep);
|
|
|
|
|
}
|
2025-10-04 17:24:30 +01:00
|
|
|
|
|
|
|
|
// Image upload is sbi_load -> copy to staging -> upload to gpu texture
|
|
|
|
|
|
|
|
|
|
for (FS_Entry *it = assets.first; it != 0; it = it->next) {
|
|
|
|
|
if (Str8_EndsWith(it->basename, S("png"))) {
|
|
|
|
|
S32 w, h, c;
|
|
|
|
|
stbi_uc *data = stbi_load((const char *) it->path.data, &w, &h, &c, 4);
|
|
|
|
|
|
|
|
|
|
if (data) {
|
2025-10-05 14:27:05 +01:00
|
|
|
D_Image *image = &draw->images[draw->n_images];
|
2025-10-04 17:24:30 +01:00
|
|
|
|
|
|
|
|
U64 image_sz = 4 * w * h;
|
|
|
|
|
|
|
|
|
|
M_CopySize(base, data, image_sz);
|
|
|
|
|
|
|
|
|
|
copy.bufferOffset = offset;
|
|
|
|
|
copy.bufferRowLength = 0;
|
|
|
|
|
copy.bufferImageHeight = 0;
|
|
|
|
|
|
|
|
|
|
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
copy.imageSubresource.mipLevel = 0;
|
|
|
|
|
copy.imageSubresource.baseArrayLayer = 0;
|
|
|
|
|
copy.imageSubresource.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
copy.imageExtent.width = w;
|
|
|
|
|
copy.imageExtent.height = h;
|
|
|
|
|
copy.imageExtent.depth = 1;
|
|
|
|
|
|
|
|
|
|
base += image_sz;
|
|
|
|
|
offset += image_sz;
|
|
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
Assert(offset <= staging->size);
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
draw->n_images += 1;
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-04 21:42:04 +01:00
|
|
|
image->name = Str8_Copy(game->arena, Str8_RemoveAfterLast(it->basename, '.'));
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-04 23:46:13 +01:00
|
|
|
printf("[Info] :: Loaded %.*s from %.*s\n", Sv(image->name), Sv(it->basename));
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
image->image.width = w;
|
|
|
|
|
image->image.height = h;
|
|
|
|
|
|
|
|
|
|
image->image.format = VK_FORMAT_R8G8B8A8_SRGB;
|
|
|
|
|
image->image.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
|
|
|
|
|
|
|
|
Vk_ImageCreate(&image->image);
|
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier2 transfer = { 0 };
|
|
|
|
|
VkImageMemoryBarrier2 shader_read = { 0 };
|
|
|
|
|
|
|
|
|
|
transfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
transfer.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
|
|
|
|
|
transfer.srcAccessMask = VK_ACCESS_2_NONE;
|
|
|
|
|
transfer.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
|
|
|
|
transfer.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
|
|
|
|
transfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
transfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
|
transfer.image = image->image.handle;
|
|
|
|
|
|
|
|
|
|
transfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
transfer.subresourceRange.layerCount = 1;
|
|
|
|
|
transfer.subresourceRange.levelCount = 1;
|
|
|
|
|
|
|
|
|
|
shader_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
shader_read.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
|
|
|
|
|
shader_read.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
|
|
|
|
shader_read.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
|
|
|
|
|
shader_read.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
|
|
|
|
|
shader_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
|
shader_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
|
shader_read.image = image->image.handle;
|
|
|
|
|
|
|
|
|
|
shader_read.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
shader_read.subresourceRange.layerCount = 1;
|
|
|
|
|
shader_read.subresourceRange.levelCount = 1;
|
|
|
|
|
|
|
|
|
|
VkDependencyInfo dep = { 0 };
|
|
|
|
|
dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
|
|
|
|
|
|
|
|
|
dep.imageMemoryBarrierCount = 1;
|
|
|
|
|
dep.pImageMemoryBarriers = &transfer;
|
|
|
|
|
|
|
|
|
|
vk.CmdPipelineBarrier2(cmds->handle, &dep);
|
|
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
vk.CmdCopyBufferToImage(cmds->handle, staging->handle, image->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
2025-10-04 17:24:30 +01:00
|
|
|
|
|
|
|
|
dep.pImageMemoryBarriers = &shader_read;
|
|
|
|
|
|
|
|
|
|
vk.CmdPipelineBarrier2(cmds->handle, &dep);
|
|
|
|
|
|
|
|
|
|
stbi_image_free(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vk_CommandBufferSubmit(cmds, true /* wait */);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-04 21:42:04 +01:00
|
|
|
|
|
|
|
|
void G_PipelinesLoad(G_State *game) {
|
2025-10-05 14:27:05 +01:00
|
|
|
D_Context *draw = &game->draw;
|
|
|
|
|
|
|
|
|
|
draw->pipelines = M_ArenaPush(game->arena, Vk_Pipeline, .count = 1);
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
Vk_Pipeline *basic = &draw->pipelines[0];
|
2025-10-04 21:42:04 +01:00
|
|
|
|
|
|
|
|
VkShaderModule vshader = 0, fshader = 0;
|
|
|
|
|
M_TempScope(0, 0) {
|
2025-10-04 23:46:13 +01:00
|
|
|
Str8 exe_path = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE);
|
|
|
|
|
Str8 vshader_code = FS_ReadEntireFile(temp.arena, Sf(temp.arena, "%.*s/assets/shaders/basic.vert.spv", Sv(exe_path)));
|
|
|
|
|
Str8 fshader_code = FS_ReadEntireFile(temp.arena, Sf(temp.arena, "%.*s/assets/shaders/basic.frag.spv", Sv(exe_path)));
|
2025-10-04 21:42:04 +01:00
|
|
|
|
|
|
|
|
VkShaderModuleCreateInfo create_info = { 0 };
|
|
|
|
|
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
|
|
|
create_info.pCode = (U32 *) vshader_code.data;
|
|
|
|
|
create_info.codeSize = vshader_code.count;
|
|
|
|
|
|
|
|
|
|
vk.CreateShaderModule(vk.device, &create_info, 0, &vshader);
|
|
|
|
|
|
|
|
|
|
create_info.pCode = (U32 *) fshader_code.data;
|
|
|
|
|
create_info.codeSize = fshader_code.count;
|
|
|
|
|
|
|
|
|
|
vk.CreateShaderModule(vk.device, &create_info, 0, &fshader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create pipeline layout, its insane what you have to do for this because the ALREADY KNOW
|
|
|
|
|
// all of this information
|
|
|
|
|
{
|
|
|
|
|
VkDescriptorSetLayoutBinding bindings[2] = { 0 };
|
|
|
|
|
|
|
|
|
|
bindings[0].binding = 0;
|
|
|
|
|
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
|
|
|
bindings[0].descriptorCount = 1;
|
|
|
|
|
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
|
|
|
|
|
bindings[1].binding = 1;
|
|
|
|
|
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
2025-10-05 21:11:18 +01:00
|
|
|
bindings[1].descriptorCount = game->draw.n_images + game->draw.n_fonts;
|
2025-10-04 21:42:04 +01:00
|
|
|
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
|
|
|
|
|
|
VkDescriptorSetLayoutCreateInfo set_info = { 0 };
|
|
|
|
|
set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
2025-10-05 14:27:05 +01:00
|
|
|
set_info.bindingCount = ArraySize(bindings);
|
2025-10-04 21:42:04 +01:00
|
|
|
set_info.pBindings = bindings;
|
|
|
|
|
|
|
|
|
|
vk.CreateDescriptorSetLayout(vk.device, &set_info, 0, &basic->layout.set);
|
|
|
|
|
|
2025-10-05 02:40:59 +01:00
|
|
|
VkPushConstantRange push_range = { 0 };
|
|
|
|
|
push_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
push_range.offset = 0;
|
|
|
|
|
push_range.size = 128;
|
|
|
|
|
|
2025-10-04 21:42:04 +01:00
|
|
|
VkPipelineLayoutCreateInfo layout_create = { 0 };
|
2025-10-05 02:40:59 +01:00
|
|
|
layout_create.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
|
|
|
layout_create.setLayoutCount = 1;
|
|
|
|
|
layout_create.pSetLayouts = &basic->layout.set;
|
|
|
|
|
layout_create.pushConstantRangeCount = 1;
|
|
|
|
|
layout_create.pPushConstantRanges = &push_range;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
|
|
|
|
vk.CreatePipelineLayout(vk.device, &layout_create, 0, &basic->layout.pipeline);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic->targets.items[0] = vk.swapchain.format.format;
|
|
|
|
|
basic->targets.count = 1;
|
|
|
|
|
|
|
|
|
|
basic->shaders.count = 2;
|
|
|
|
|
|
|
|
|
|
basic->shaders.items[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
basic->shaders.items[0].handle = vshader;
|
|
|
|
|
|
|
|
|
|
basic->shaders.items[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
|
basic->shaders.items[1].handle = fshader;
|
|
|
|
|
|
|
|
|
|
Vk_PipelineCreate(basic);
|
|
|
|
|
}
|
2025-10-04 21:58:14 +01:00
|
|
|
|
2025-10-05 15:17:37 +01:00
|
|
|
void G_CalculateCamera(G_Camera *camera, F32 aspect) {
|
2025-10-05 02:40:59 +01:00
|
|
|
Mat4x4FInv proj = M4x4F_Perspective(camera->fov, aspect, camera->nearp, camera->farp);
|
|
|
|
|
Mat4x4FInv view = M4x4F_CameraView(camera->x, camera->y, camera->z, camera->p);
|
|
|
|
|
|
|
|
|
|
camera->proj.fwd = M4x4F_Mul(proj.fwd, view.fwd);
|
|
|
|
|
camera->proj.inv = M4x4F_Mul(view.inv, proj.inv);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
V3f G_CameraUnprojectAt(G_Camera *camera, V2f clip, F32 z) {
|
|
|
|
|
V3f dir = V3f_Sub(camera->p, V3f_Scale(camera->z, z));
|
|
|
|
|
V4f z_dist = V4F(dir.x, dir.y, dir.z, 1.0f);
|
|
|
|
|
V4f persp = M4x4F_VMul4(camera->proj.fwd, z_dist);
|
|
|
|
|
|
|
|
|
|
clip = V2f_Scale(clip, persp.w);
|
|
|
|
|
|
|
|
|
|
V4f world = M4x4F_VMul4(camera->proj.inv, V4F(clip.x, clip.y, persp.z, persp.w));
|
|
|
|
|
|
|
|
|
|
V3f result = world.xyz;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V3f G_CameraUnproject(G_Camera *camera, V2f clip) {
|
|
|
|
|
V3f result = G_CameraUnprojectAt(camera, clip, camera->p.z);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
R3f G_CameraBounds(G_Camera *camera) {
|
|
|
|
|
R3f result;
|
|
|
|
|
result.min = G_CameraUnproject(camera, V2F(-1, -1));
|
|
|
|
|
result.max = G_CameraUnproject(camera, V2F( 1, 1));
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 21:58:14 +01:00
|
|
|
#include "impl/aabb.c"
|
|
|
|
|
#include "impl/nav.c"
|
|
|
|
|
#include "impl/player.c"
|