Commit: 9011f7a3136ffa3d72c60471c500c459a7f2a65f
Parent: 7455a1e17c31ec3561c15ec2a86f8c861d0bd84b
Author: Randy Palamar
Date:   Thu, 21 Aug 2025 15:49:53 -0600
core: don't crash on invalid shader reload
unfortunately vulkan doesn't have a 0 pipeline so some other way
of indicating that a shader is broken is needed
Diffstat:
| M | common.c | | | 84 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- | 
1 file changed, 49 insertions(+), 35 deletions(-)
diff --git a/common.c b/common.c
@@ -262,16 +262,30 @@ vulkan_pipeline_from_shader_text(VulkanPipeline *vp, VkDevice device, OS *os, Ar
 		.subpass             = 0,
 	};
 
-	vkDestroyPipeline(device, vp->pipeline, 0);
-
 	if (valid) {
-		vkCreateGraphicsPipelines(device, 0, 1, &pipeline_info, 0, &vp->pipeline);
+		/* TODO(rnp): have a set of default shaders for when compilation fails.
+		 * Vulkan has no concept of a 0 shader which does nothing. Visual indication
+		 * of brokeness is desirable for debugging */
+
+		/* NOTE(rnp): this could be fancier to avoid it but for now we are okay
+		 * with a stall when shaders are reloading */
+		switch (vp->kind) {
+		case VulkanPipelineKind_Render:{
+			vkWaitForFences(device, countof(vp->render.command_buffer_fences),
+			                vp->render.command_buffer_fences, 1, U64_MAX);
+		}break;
+		case VulkanPipelineKind_Compute:{
+			vkWaitForFences(device, countof(vp->compute.command_buffer_fences),
+			                vp->compute.command_buffer_fences, 1, U64_MAX);
+		}break;
+		InvalidDefaultCase;
+		}
 
+		vkDestroyPipeline(device, vp->pipeline, 0);
+		vkCreateGraphicsPipelines(device, 0, 1, &pipeline_info, 0, &vp->pipeline);
 		Stream sb = arena_stream(arena);
 		stream_append_str8s(&sb, str8("loaded: "), name, str8("\n"));
 		os_write_file(os->error_handle, stream_to_str8(&sb));
-	} else {
-		vp->pipeline = 0;
 	}
 
 	for (s32 i = 0; i < count; i++)
@@ -756,6 +770,36 @@ init_viewer(ViewerContext *ctx)
 		                         surface_info.capabilities.currentTransform, image_count, &ctx->arena);
 	}
 
+	VkCommandPoolCreateInfo command_pool_info = {
+		.sType            = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+		.flags            = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+		.queueFamilyIndex = queue_family_index,
+	};
+
+	vkCreateCommandPool(v->device, &command_pool_info, 0, &v->command_pool);
+
+	VkCommandBufferAllocateInfo command_buffer_info = {
+		.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+		.commandPool        = v->command_pool,
+		.level              = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+		.commandBufferCount = countof(rp->render.command_buffers),
+	};
+	vkAllocateCommandBuffers(v->device, &command_buffer_info, rp->render.command_buffers);
+
+	VkSemaphoreCreateInfo semaphore_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
+	VkFenceCreateInfo fence_info = {
+		.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+		.flags = VK_FENCE_CREATE_SIGNALED_BIT,
+	};
+
+	for (u32 i = 0; i < sc->image_count; i++)
+		vkCreateSemaphore(v->device, &semaphore_info, 0, sc->render_complete_semaphores + i);
+
+	for EachElement(rp->render.command_buffer_fences, it) {
+		vkCreateSemaphore(v->device, &semaphore_info, 0, sc->image_available_semaphores + it);
+		vkCreateFence(v->device, &fence_info, 0, rp->render.command_buffer_fences + it);
+	}
+
 	ShaderReloadContext *render = push_struct(&ctx->arena, typeof(*render));
 	render->pipeline    = rp;
 	render->device      = v->device;
@@ -793,36 +837,6 @@ init_viewer(ViewerContext *ctx)
 	reload_shader(&ctx->os, render->path, (sptr)render, ctx->arena);
 	os_add_file_watch(&ctx->os, &ctx->arena, render->path, reload_shader, (sptr)render);
 
-	VkCommandPoolCreateInfo command_pool_info = {
-		.sType            = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
-		.flags            = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
-		.queueFamilyIndex = queue_family_index,
-	};
-
-	vkCreateCommandPool(v->device, &command_pool_info, 0, &v->command_pool);
-
-	VkCommandBufferAllocateInfo command_buffer_info = {
-		.sType              = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
-		.commandPool        = v->command_pool,
-		.level              = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
-		.commandBufferCount = countof(rp->render.command_buffers),
-	};
-	vkAllocateCommandBuffers(v->device, &command_buffer_info, rp->render.command_buffers);
-
-	VkSemaphoreCreateInfo semaphore_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
-	VkFenceCreateInfo fence_info = {
-		.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
-		.flags = VK_FENCE_CREATE_SIGNALED_BIT,
-	};
-
-	for (u32 i = 0; i < sc->image_count; i++)
-		vkCreateSemaphore(v->device, &semaphore_info, 0, sc->render_complete_semaphores + i);
-
-	for EachElement(rp->render.command_buffer_fences, it) {
-		vkCreateSemaphore(v->device, &semaphore_info, 0, sc->image_available_semaphores + it);
-		vkCreateFence(v->device, &fence_info, 0, rp->render.command_buffer_fences + it);
-	}
-
 	glfwSetWindowUserPointer(ctx->window, ctx);
 	glfwSetKeyCallback(ctx->window, key_callback);