fix renderer with compute/gfx pipelines, handle window events

This commit is contained in:
matthew 2025-07-12 17:20:47 +10:00
parent 42bef67206
commit 56cadf09df
6 changed files with 182 additions and 120 deletions

Binary file not shown.

View File

@ -14,6 +14,9 @@ void main()
while (true) while (true)
{ {
p.HandleEvents(&window);
if (window.close) break;
r.Cycle(&rd); r.Cycle(&rd);
} }
} }

View File

@ -16,6 +16,7 @@ struct Window
xcb_atom_t minimize_event; xcb_atom_t minimize_event;
u16 w; u16 w;
u16 h; u16 h;
bool close;
}; };
struct Library struct Library
@ -145,6 +146,40 @@ Window CreateWindow(string name, u16 width, u16 height)
return window; return window;
}; };
void
HandleEvents(Window* window)
{
xcb_generic_event_t* e;
do
{
e = xcb_poll_for_event(window.conn);
if (e)
{
switch (e.response_type & ~0x80)
{
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t* msg = cast(xcb_client_message_event_t*)e;
if (msg.window != window.window)
{
break;
}
if (msg.data.data32[0] == window.close_event)
{
window.close = true;
}
} break;
default:
break;
}
}
}
while (e);
}
Library LoadLibrary(string name) Library LoadLibrary(string name)
{ {
Library lib = { Library lib = {

View File

@ -75,6 +75,14 @@ Cycle(Renderer* rd)
if (success) if (success)
{ {
vk.Bind(&rd.vulkan, rd.compute_pipeline);
vk.PrepCompute(&rd.vulkan);
vk.Dispatch(&rd.vulkan);
vk.BeginRender(&rd.vulkan);
vk.Bind(&rd.vulkan, rd.triangle_pipeline); vk.Bind(&rd.vulkan, rd.triangle_pipeline);
vk.Draw(&rd.vulkan, 3, 1); vk.Draw(&rd.vulkan, 3, 1);

View File

@ -9,6 +9,7 @@ import u = util : HashTable, Result, Logf, Log;
import a = alloc; import a = alloc;
import p = platform; import p = platform;
import renderer; import renderer;
import std.math.rounding : Ceil = ceil;
bool g_VLAYER_SUPPORT = false; bool g_VLAYER_SUPPORT = false;
@ -119,7 +120,8 @@ struct Vulkan
a.Arena arena; a.Arena arena;
a.Arena[FRAME_OVERLAP] frame_arenas; a.Arena[FRAME_OVERLAP] frame_arenas;
u64 frame_no; u32 frame_index;
u32 semaphore_index;
u.SLList!(SI) cleanup_list; u.SLList!(SI) cleanup_list;
@ -146,8 +148,8 @@ struct Vulkan
VkCommandPool[FRAME_OVERLAP] cmd_pools; VkCommandPool[FRAME_OVERLAP] cmd_pools;
VkCommandBuffer[FRAME_OVERLAP] cmds; VkCommandBuffer[FRAME_OVERLAP] cmds;
VkSemaphore[] swapchain_sems; VkSemaphore[] submit_sems;
VkSemaphore[FRAME_OVERLAP] render_sems; VkSemaphore[FRAME_OVERLAP] acquire_sems;
VkFence[FRAME_OVERLAP] render_fences; VkFence[FRAME_OVERLAP] render_fences;
VkCommandPool imm_pool; VkCommandPool imm_pool;
@ -221,18 +223,6 @@ Init(p.Window* window, u64 permanent_mem, u64 frame_mem)
return result; return result;
} }
u64
FrameIndex(Vulkan* vk)
{
return vk.frame_no % FRAME_OVERLAP;
}
u64
SemIndex(Vulkan* vk)
{
return vk.frame_no % vk.present_images.length;
}
VkImage VkImage
CurrentImage(Vulkan* vk) CurrentImage(Vulkan* vk)
{ {
@ -242,22 +232,19 @@ CurrentImage(Vulkan* vk)
bool bool
BeginFrame(Vulkan* vk) BeginFrame(Vulkan* vk)
{ {
u64 index = FrameIndex(vk);
u64 sem_index = SemIndex(vk);
// TODO: move vkWaitForFences so it no longer holds up the frame, will need to change how fences are handled in regards to images though // TODO: move vkWaitForFences so it no longer holds up the frame, will need to change how fences are handled in regards to images though
VkResult result = vkWaitForFences(vk.device, 1, vk.render_fences.ptr + index, VK_TRUE, 1000000000); VkResult result = vkWaitForFences(vk.device, 1, vk.render_fences.ptr + vk.frame_index, VK_TRUE, 1000000000);
bool success = VkCheck("BeginFrame failure: vkWaitForFences error", result); bool success = VkCheck("BeginFrame failure: vkWaitForFences error", result);
if (success) if (success)
{ {
result = vkResetFences(vk.device, 1, vk.render_fences.ptr + index); result = vkResetFences(vk.device, 1, vk.render_fences.ptr + vk.frame_index);
success = VkCheck("BeginFrame failure: vkResetFences error", result); success = VkCheck("BeginFrame failure: vkResetFences error", result);
} }
if (success) if (success)
{ {
result = vkAcquireNextImageKHR(vk.device, vk.swapchain, 1000000000, vk.swapchain_sems[sem_index], null, &vk.image_index); result = vkAcquireNextImageKHR(vk.device, vk.swapchain, 1000000000, vk.acquire_sems[vk.frame_index], null, &vk.image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) if (result == VK_ERROR_OUT_OF_DATE_KHR)
{ {
RecreateSwapchain(vk); RecreateSwapchain(vk);
@ -270,7 +257,7 @@ BeginFrame(Vulkan* vk)
if (success) if (success)
{ {
result = vkResetCommandBuffer(vk.cmds[index], 0); result = vkResetCommandBuffer(vk.cmds[vk.frame_index], 0);
success = VkCheck("BeginFrame failure: vkResetCommandBuffer failure", result); success = VkCheck("BeginFrame failure: vkResetCommandBuffer failure", result);
} }
@ -281,28 +268,29 @@ BeginFrame(Vulkan* vk)
flags: VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, flags: VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
}; };
result = vkBeginCommandBuffer(vk.cmds[index], &cmd_info); result = vkBeginCommandBuffer(vk.cmds[vk.frame_index], &cmd_info);
success = VkCheck("BeginFrame failure: vkBeginCommandBuffer error", result); success = VkCheck("BeginFrame failure: vkBeginCommandBuffer error", result);
} }
if (success) return success;
{ }
Transition(vk.cmds[index], &vk.draw_image.base, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
Transition(vk.cmds[index], &vk.depth_image.base, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); void
BeginRender(Vulkan* vk)
{
// TODO: probably get rid of these
Transition(vk.cmds[vk.frame_index], &vk.draw_image.base, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
Transition(vk.cmds[vk.frame_index], &vk.depth_image.base, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
VkImage image = CurrentImage(vk); VkImage image = CurrentImage(vk);
Transition(vk.cmds[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);
f32[4] clear_col;
clear_col[] = 0.2;
VkRenderingAttachmentInfo col_attach = { VkRenderingAttachmentInfo col_attach = {
sType: VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, sType: VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
imageView: vk.draw_image.view, imageView: vk.draw_image.view,
imageLayout: vk.draw_image.base.layout, imageLayout: vk.draw_image.base.layout,
loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, loadOp: VK_ATTACHMENT_LOAD_OP_LOAD,
storeOp: VK_ATTACHMENT_STORE_OP_STORE, storeOp: VK_ATTACHMENT_STORE_OP_STORE,
clearValue: { color: { clear_col } },
}; };
VkRenderingAttachmentInfo depth_attach = { VkRenderingAttachmentInfo depth_attach = {
@ -327,36 +315,29 @@ BeginFrame(Vulkan* vk)
}, },
}; };
vkCmdBeginRendering(vk.cmds[index], &render_info); vkCmdBeginRendering(vk.cmds[vk.frame_index], &render_info);
vkCmdBindDescriptorSets( }
vk.cmds[index],
VK_PIPELINE_BIND_POINT_GRAPHICS,
vk.pipeline_layout,
0,
cast(u32)vk.desc_sets.length,
vk.desc_sets.ptr,
0,
null
);
}
return success; void
PrepCompute(Vulkan* vk)
{
Transition(vk.cmds[vk.frame_index], &vk.draw_image, VK_IMAGE_LAYOUT_GENERAL);
} }
bool bool
FinishFrame(Vulkan* vk) FinishFrame(Vulkan* vk)
{ {
scope(exit) vk.frame_no += 1; scope(exit) vk.frame_index = (vk.frame_index + 1) % FRAME_OVERLAP;
bool success = true; bool success = true;
u64 index = FrameIndex(vk);
u64 sem_index = SemIndex(vk);
VkImage image = CurrentImage(vk); VkImage image = CurrentImage(vk);
VkSemaphore acquire_sem = vk.acquire_sems[vk.frame_index];
VkSemaphore submit_sem = vk.submit_sems[vk.image_index];
vkCmdEndRendering(vk.cmds[index]); vkCmdEndRendering(vk.cmds[vk.frame_index]);
Transition(vk.cmds[index], &vk.draw_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); Transition(vk.cmds[vk.frame_index], &vk.draw_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkExtent2D extent = { VkExtent2D extent = {
width: vk.swapchain_extent.width, width: vk.swapchain_extent.width,
@ -364,30 +345,30 @@ FinishFrame(Vulkan* vk)
}; };
// TODO: Find out how to copy from same dimension images // TODO: Find out how to copy from same dimension images
Copy(vk.cmds[index], vk.draw_image.base.image, image, extent, extent); Copy(vk.cmds[vk.frame_index], vk.draw_image.base.image, image, extent, extent);
Transition(vk.cmds[index], image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); Transition(vk.cmds[vk.frame_index], image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
VkResult result = vkEndCommandBuffer(vk.cmds[index]); VkResult result = vkEndCommandBuffer(vk.cmds[vk.frame_index]);
success = VkCheck("FinishFrame failure: vkEndCommandBuffer error", result); success = VkCheck("FinishFrame failure: vkEndCommandBuffer error", result);
if (success) if (success)
{ {
VkCommandBufferSubmitInfo cmd_info = { VkCommandBufferSubmitInfo cmd_info = {
sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
commandBuffer: vk.cmds[index], commandBuffer: vk.cmds[vk.frame_index],
}; };
VkSemaphoreSubmitInfo wait_info = { VkSemaphoreSubmitInfo wait_info = {
sType: VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, sType: VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
semaphore: vk.swapchain_sems[sem_index], semaphore: acquire_sem,
stageMask: VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, stageMask: VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
value: 1, value: 1,
}; };
VkSemaphoreSubmitInfo signal_info = { VkSemaphoreSubmitInfo signal_info = {
sType: VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, sType: VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
semaphore: vk.render_sems[index], semaphore: submit_sem,
stageMask: VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, stageMask: VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT,
value: 1, value: 1,
}; };
@ -402,7 +383,7 @@ FinishFrame(Vulkan* vk)
pCommandBufferInfos: &cmd_info, pCommandBufferInfos: &cmd_info,
}; };
result = vkQueueSubmit2(vk.queues.gfx_queue, 1, &submit_info, vk.render_fences[index]); result = vkQueueSubmit2(vk.queues.gfx_queue, 1, &submit_info, vk.render_fences[vk.frame_index]);
success = VkCheck("FinishFrame failure: vkQueueSubmit2 error", result); success = VkCheck("FinishFrame failure: vkQueueSubmit2 error", result);
} }
@ -413,7 +394,7 @@ FinishFrame(Vulkan* vk)
swapchainCount: 1, swapchainCount: 1,
pSwapchains: &vk.swapchain, pSwapchains: &vk.swapchain,
waitSemaphoreCount: 1, waitSemaphoreCount: 1,
pWaitSemaphores: vk.render_sems.ptr + index, pWaitSemaphores: &submit_sem,
pImageIndices: &vk.image_index, pImageIndices: &vk.image_index,
}; };
@ -434,13 +415,13 @@ FinishFrame(Vulkan* vk)
void void
Draw(Vulkan* vk, u32 index_count, u32 instance_count) Draw(Vulkan* vk, u32 index_count, u32 instance_count)
{ {
vkCmdDraw(vk.cmds[FrameIndex(vk)], index_count, instance_count, 0, 0); vkCmdDraw(vk.cmds[vk.frame_index], index_count, instance_count, 0, 0);
} }
void void
DrawIndexed(Vulkan* vk, u32 index_count, u32 instance_count) DrawIndexed(Vulkan* vk, u32 index_count, u32 instance_count)
{ {
vkCmdDrawIndexed(vk.cmds[FrameIndex(vk)], index_count, instance_count, 0, 0, 0); vkCmdDrawIndexed(vk.cmds[vk.frame_index], index_count, instance_count, 0, 0, 0);
} }
pragma(inline): void pragma(inline): void
@ -487,9 +468,18 @@ Copy(VkCommandBuffer cmd, VkImage src, VkImage dst, VkExtent2D src_ext, VkExtent
void void
Bind(Vulkan* vk, PipelineHandle pipeline) Bind(Vulkan* vk, PipelineHandle pipeline)
{ {
u64 index = FrameIndex(vk); vkCmdBindPipeline(vk.cmds[vk.frame_index], pipeline.type, pipeline.handle);
vkCmdBindPipeline(vk.cmds[index], pipeline.type, pipeline.handle); vkCmdBindDescriptorSets(
vk.cmds[vk.frame_index],
pipeline.type,
vk.pipeline_layout,
0,
cast(u32)vk.desc_sets.length,
vk.desc_sets.ptr,
0,
null
);
VkViewport viewport = { VkViewport viewport = {
width: cast(f32)vk.swapchain_extent.width, width: cast(f32)vk.swapchain_extent.width,
@ -497,7 +487,7 @@ Bind(Vulkan* vk, PipelineHandle pipeline)
maxDepth: 1.0F, maxDepth: 1.0F,
}; };
vkCmdSetViewport(vk.cmds[index], 0, 1, &viewport); vkCmdSetViewport(vk.cmds[vk.frame_index], 0, 1, &viewport);
VkRect2D scissor = { VkRect2D scissor = {
extent: { extent: {
@ -506,7 +496,7 @@ Bind(Vulkan* vk, PipelineHandle pipeline)
}, },
}; };
vkCmdSetScissor(vk.cmds[index], 0, 1, &scissor); vkCmdSetScissor(vk.cmds[vk.frame_index], 0, 1, &scissor);
} }
pragma(inline): void pragma(inline): void
@ -953,21 +943,44 @@ InitDescriptors(Vulkan* vk)
sampler: vk.nearest_sampler, sampler: vk.nearest_sampler,
}; };
VkWriteDescriptorSet write = { VkDescriptorImageInfo draw_image_info = {
imageView: vk.draw_image.view,
imageLayout: VK_IMAGE_LAYOUT_GENERAL,
};
VkWriteDescriptorSet[] writes = [
{
sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
dstSet: vk.desc_sets[DT.Shared], dstSet: vk.desc_sets[DT.Shared],
dstBinding: 3, dstBinding: 3,
descriptorCount: 1, descriptorCount: 1,
descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER, descriptorType: VK_DESCRIPTOR_TYPE_SAMPLER,
pImageInfo: &sampler_info, pImageInfo: &sampler_info,
}; },
{
sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
dstSet: vk.desc_sets[DT.Shared],
dstBinding: 2,
descriptorCount: 1,
descriptorType: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
pImageInfo: &draw_image_info,
},
];
vkUpdateDescriptorSets(vk.device, 1, &write, 0, null); vkUpdateDescriptorSets(vk.device, cast(u32)writes.length, writes.ptr, 0, null);
} }
return success; return success;
} }
void
Dispatch(Vulkan* vk)
{
f32 w = Ceil(cast(f32)(vk.swapchain_extent.width) / 16.0F);
f32 h = Ceil(cast(f32)(vk.swapchain_extent.height) / 16.0F);
vkCmdDispatch(vk.cmds[vk.frame_index], cast(u32)w, cast(u32)h, 1);
}
bool bool
VkCheck(string message, VkResult result) VkCheck(string message, VkResult result)
{ {
@ -1010,13 +1023,17 @@ InitFrameStructures(Vulkan* vk)
level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
}; };
vk.swapchain_sems = a.AllocArray!(VkSemaphore)(arena, vk.present_images.length); u32 sem_count = cast(u32)vk.present_images.length;
vk.submit_sems = a.AllocArray!(VkSemaphore)(arena, sem_count);
foreach(i; 0 .. vk.swapchain_sems.length) foreach(i; 0 .. sem_count)
{ {
VkResult result = vkCreateSemaphore(vk.device, &sem_info, null, vk.swapchain_sems.ptr + i); if (success)
{
VkResult result = vkCreateSemaphore(vk.device, &sem_info, null, vk.submit_sems.ptr + i);
success = VkCheck("vkCreateSemaphore failure", result); success = VkCheck("vkCreateSemaphore failure", result);
} }
}
foreach(i; 0 .. FRAME_OVERLAP) foreach(i; 0 .. FRAME_OVERLAP)
{ {
@ -1043,7 +1060,7 @@ InitFrameStructures(Vulkan* vk)
if (success) if (success)
{ {
result = vkCreateSemaphore(vk.device, &sem_info, null, vk.render_sems.ptr + i); result = vkCreateSemaphore(vk.device, &sem_info, null, vk.acquire_sems.ptr + i);
success = VkCheck("vkCreateSemaphore failure", result); success = VkCheck("vkCreateSemaphore failure", result);
} }
} }
@ -1809,7 +1826,7 @@ DestroyFS(Vulkan* vk)
vkDestroyCommandPool(vk.device, vk.imm_pool, null); vkDestroyCommandPool(vk.device, vk.imm_pool, null);
} }
foreach(i, sem; vk.swapchain_sems) foreach(sem; vk.submit_sems)
{ {
if (sem) if (sem)
{ {
@ -1819,11 +1836,6 @@ DestroyFS(Vulkan* vk)
foreach(i; 0 .. FRAME_OVERLAP) foreach(i; 0 .. FRAME_OVERLAP)
{ {
if (vk.render_sems[i])
{
vkDestroySemaphore(vk.device, vk.render_sems[i], null);
}
if (vk.render_fences[i]) if (vk.render_fences[i])
{ {
vkDestroyFence(vk.device, vk.render_fences[i], null); vkDestroyFence(vk.device, vk.render_fences[i], null);
@ -1838,6 +1850,11 @@ DestroyFS(Vulkan* vk)
{ {
vkDestroyCommandPool(vk.device, vk.cmd_pools[i], null); vkDestroyCommandPool(vk.device, vk.cmd_pools[i], null);
} }
if (vk.acquire_sems[i])
{
vkDestroySemaphore(vk.device, vk.acquire_sems[i], null);
}
} }
} }
@ -1922,7 +1939,6 @@ version(Windows)
return success; return success;
} }
void void
EnableVLayers(Vulkan* vk) EnableVLayers(Vulkan* vk)
{ {

View File

@ -12,11 +12,11 @@ void main()
ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy); ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(DrawImage); ivec2 size = imageSize(DrawImage);
if (texel_coord.x < size. x && texel_coord.y < size.y) if (texel_coord.x < size.x && texel_coord.y < size.y)
{ {
vec4 color = vec4(0.0); vec4 color = vec4(0.0);
if (gl_GlobalInvocationID.x != 0 && gl_LocalInvocationID.y != 0) if (gl_LocalInvocationID.x != 0 && gl_LocalInvocationID.y != 0)
{ {
color.x = float(texel_coord.x) / size.x; color.x = float(texel_coord.x) / size.x;
color.y = float(texel_coord.y) / size.y; color.y = float(texel_coord.y) / size.y;