From bda7eae355bc39be1f8ed8032d322013b134eb1d Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Sat, 13 Jan 2024 23:50:39 -0700 Subject: [PATCH] Made pipelines create a second pipeline for offscreen rener of g_buffers --- env | 1 + shader_src/g_pos.frag | 4 + shader_src/g_pos.vert | 4 + src/main.c | 375 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 335 insertions(+), 49 deletions(-) create mode 100644 env create mode 100644 shader_src/g_pos.frag create mode 100644 shader_src/g_pos.vert diff --git a/env b/env new file mode 100644 index 0000000..3243ea1 --- /dev/null +++ b/env @@ -0,0 +1 @@ +MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 diff --git a/shader_src/g_pos.frag b/shader_src/g_pos.frag new file mode 100644 index 0000000..f0bcb49 --- /dev/null +++ b/shader_src/g_pos.frag @@ -0,0 +1,4 @@ +#version 450 + +void main() { +} diff --git a/shader_src/g_pos.vert b/shader_src/g_pos.vert new file mode 100644 index 0000000..f0bcb49 --- /dev/null +++ b/shader_src/g_pos.vert @@ -0,0 +1,4 @@ +#version 450 + +void main() { +} diff --git a/src/main.c b/src/main.c index e779fab..96950de 100644 --- a/src/main.c +++ b/src/main.c @@ -111,11 +111,7 @@ typedef struct GraphicsPipelineInfoStruct { VkDescriptorSetLayoutCreateInfo descriptor_info; - uint32_t input_bindings_count; - VkVertexInputBindingDescription* input_bindings; - - uint32_t input_attributes_count; - VkVertexInputAttributeDescription* input_attributes; + VkPipelineVertexInputStateCreateInfo input_info; uint32_t shader_stages_count; VkPipelineShaderStageCreateInfo* shader_stages; @@ -131,6 +127,7 @@ typedef struct GraphicsPipelineStruct { VkPipelineLayout layout; VkPipeline pipeline; + VkPipeline offscreen_pipeline; } GraphicsPipeline; typedef struct VulkanContextStruct { @@ -184,9 +181,11 @@ typedef struct VulkanContextStruct { uint32_t max_frames_in_flight; // Per frame objects + VkCommandBuffer* offscreen_command_buffers; VkCommandBuffer* swapchain_command_buffers; VkSemaphore* image_available_semaphores; VkSemaphore* render_finished_semaphores; + VkSemaphore* offscreen_complete_semaphores; VkFence* in_flight_fences; VkRenderPass render_pass; @@ -220,10 +219,6 @@ struct Vertex { vec3 color; }; -struct ModelUBO { - mat4 model; -}; - struct SceneUBO { mat4 test; }; @@ -1397,13 +1392,12 @@ void command_draw_object(Object object, VkCommandBuffer command_buffer) { vkCmdDrawIndexed(command_buffer, mesh->index_count, 1, 0, 0, 0); } -void command_draw_pipeline(GraphicsPipeline pipeline, uint32_t object_count, Object* objects, uint32_t frame_num, VkDescriptorSet* scene_descriptors, struct ScenePC* scene_constants, VkDeviceAddress object_buffer_addr, VkCommandBuffer command_buffer) { - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); - vkCmdPushConstants(command_buffer, pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct ScenePC), scene_constants); - vkCmdPushConstants(command_buffer, pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(struct ScenePC), sizeof(VkDeviceAddress), &object_buffer_addr); - - // Bind the scene descriptor - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, &scene_descriptors[frame_num], 0, 0); +void command_draw_pipeline(GraphicsPipeline pipeline, uint32_t object_count, Object* objects, uint32_t frame_num, VkCommandBuffer command_buffer, int offscreen) { + if(offscreen) { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.offscreen_pipeline); + } else { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); + } if(pipeline.descriptors != NULL) { vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 1, 1, &pipeline.descriptors[frame_num], 0, 0); @@ -1414,7 +1408,39 @@ void command_draw_pipeline(GraphicsPipeline pipeline, uint32_t object_count, Obj } } -VkResult command_draw_scene(uint32_t pipelines_count, GraphicsPipeline* pipelines, uint32_t* object_counts, Object** objects, uint32_t frame_num, VkDescriptorSet* scene_descriptors, struct ScenePC* scene_constants, VkCommandBuffer command_buffer, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent, VkDeviceAddress object_buffer_addr) { +// Setup a scene for vkCmdDrawIndexedIndirect +// Before the call is made the following needs to be ready: +// 1) Vertex data for entire scene in one vertex buffer +// 2) Index data for entire scene in one index buffer +// 3) Descriptor data in single descriptor set +// a) e.x. all textures for the draw call need to be in the bound pipeline descriptor set +// 4) Object Data populated per-instance into buffer which will be pushed by VkDeviceAddress +// 5) Buffer of draw command ordered by instance id(first is 0, increase by 1 every 'stride') +// +// Once that is setup, the call to vkCmdDrawIndexed will happen and then: +// 1) For each draw command read from the command buffer +// a) Consume the indices/vertices to run the vertex shaders +// i) Vertex shaders access the per-instance object data indexed by gl_instanceID +// ii) This object data can have indices for other descriptors +// x) e.x. a shader can read object data with int Z that tell it to use a sampler at binding Z +// +// If I want to start by not worrying about dynamic object load/unload then I can create the above buffers +// when loading the scene, and free them all at once +// +// If I want to start worrying about loading/unloading then I need to find a way to efficiently reconstruct +// these buffers on changes +// Ideas: +// - Mark objects as 'deleted' to cheaply cull during a compute cull/vertex shader +// - Would require only a 1-bit change per deletion +// - Would require defragmentation periodically(or whenever trying to add without space) + +VkResult create_scene(){ + + + return VK_SUCCESS; +} + +VkResult command_draw_scene(uint32_t pipelines_count, GraphicsPipeline* pipelines, uint32_t* object_counts, Object** objects, uint32_t frame_num, VkDescriptorSet* scene_descriptors, struct ScenePC* scene_constants, VkCommandBuffer command_buffer, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent, VkDeviceAddress object_buffer_addr, int offscreen) { VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = 0, @@ -1473,8 +1499,14 @@ VkResult command_draw_scene(uint32_t pipelines_count, GraphicsPipeline* pipeline }; vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdPushConstants(command_buffer, pipelines[0].layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(struct ScenePC), scene_constants); + vkCmdPushConstants(command_buffer, pipelines[0].layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(struct ScenePC), sizeof(VkDeviceAddress), &object_buffer_addr); + + // Bind the scene descriptor + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[0].layout, 0, 1, &scene_descriptors[frame_num], 0, 0); + for(uint i = 0; i < pipelines_count; i++) { - command_draw_pipeline(pipelines[i], object_counts[i], objects[i], frame_num, scene_descriptors, scene_constants, object_buffer_addr, command_buffer); + command_draw_pipeline(pipelines[i], object_counts[i], objects[i], frame_num, command_buffer, offscreen); } vkCmdEndRenderPass(command_buffer); @@ -1575,7 +1607,8 @@ Object create_renderable(Mesh* mesh, GraphicsPipeline* pipeline) { VkResult create_graphics_pipeline( VkDevice device, VkExtent2D extent, - VkRenderPass render_pass, + VkRenderPass draw_render_pass, + VkRenderPass offscreen_render_pass, GraphicsPipelineInfo pipeline_info, uint32_t max_frames_in_flight, GraphicsPipeline* out @@ -1685,14 +1718,6 @@ VkResult create_graphics_pipeline( .pDynamicStates = dynamic_states, }; - VkPipelineVertexInputStateCreateInfo vertex_input_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .vertexBindingDescriptionCount = pipeline_info.input_bindings_count, - .pVertexBindingDescriptions = pipeline_info.input_bindings, - .vertexAttributeDescriptionCount = pipeline_info.input_attributes_count, - .pVertexAttributeDescriptions = pipeline_info.input_attributes, - }; - VkPipelineInputAssemblyStateCreateInfo input_assembly_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, @@ -1784,11 +1809,11 @@ VkResult create_graphics_pipeline( .blendConstants[3] = 0.0f, }; - VkGraphicsPipelineCreateInfo info = { + VkGraphicsPipelineCreateInfo draw_pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = pipeline_info.shader_stages_count, .pStages = pipeline_info.shader_stages, - .pVertexInputState = &vertex_input_info, + .pVertexInputState = &pipeline_info.input_info, .pInputAssemblyState = &input_assembly_info, .pViewportState = &viewport_state, .pRasterizationState = &raster_info, @@ -1797,13 +1822,56 @@ VkResult create_graphics_pipeline( .pDepthStencilState = &depth_info, .pMultisampleState = &multisample_info, .layout = out->layout, - .renderPass = render_pass, + .renderPass = draw_render_pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; - result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &info, 0, &out->pipeline); + VkPipelineColorBlendAttachmentState offscreen_attachment_states[] = { + { + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + .blendEnable = VK_FALSE, + }, + }; + + VkPipelineColorBlendStateCreateInfo offscreen_blend_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = sizeof(offscreen_attachment_states)/sizeof(VkPipelineColorBlendAttachmentState), + .pAttachments = offscreen_attachment_states, + .blendConstants[0] = 0.0f, + .blendConstants[1] = 0.0f, + .blendConstants[2] = 0.0f, + .blendConstants[3] = 0.0f, + }; + + VkGraphicsPipelineCreateInfo offscreen_pipeline_info = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = pipeline_info.shader_stages_count, + .pStages = pipeline_info.shader_stages, // TODO: offscreen stages + .pVertexInputState = &pipeline_info.input_info, + .pInputAssemblyState = &input_assembly_info, + .pViewportState = &viewport_state, + .pRasterizationState = &raster_info, + .pColorBlendState = &offscreen_blend_info, + .pDynamicState = &dynamic_info, + .pDepthStencilState = &depth_info, + .pMultisampleState = &multisample_info, + .layout = out->layout, + .renderPass = offscreen_render_pass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + }; + + result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &out->pipeline); + if(result != VK_SUCCESS) { + return result; + } + + result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &offscreen_pipeline_info, 0, &out->offscreen_pipeline); if(result != VK_SUCCESS) { return result; } @@ -1811,7 +1879,7 @@ VkResult create_graphics_pipeline( return VK_SUCCESS; } -VkResult create_simple_mesh_pipeline(VkDevice device, VkExtent2D extent, VkRenderPass render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, GraphicsPipeline* out) { +VkResult create_simple_mesh_pipeline(VkDevice device, VkExtent2D extent, VkRenderPass render_pass, VkRenderPass offscreen_render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, GraphicsPipeline* out) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } @@ -1873,21 +1941,26 @@ VkResult create_simple_mesh_pipeline(VkDevice device, VkExtent2D extent, VkRende .pNext = NULL, }; + VkPipelineVertexInputStateCreateInfo input_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pVertexBindingDescriptions = bindings, + .vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), + .pVertexAttributeDescriptions = attributes, + .vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), + }; + GraphicsPipelineInfo pipeline_info = { .descriptor_info = set_info, .shader_stages_count = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), .shader_stages = shader_stages, .scene_layout = scene_layout, - .input_bindings = bindings, - .input_bindings_count = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), - .input_attributes = attributes, - .input_attributes_count = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), + .input_info = input_info, }; - return create_graphics_pipeline(device, extent, render_pass, pipeline_info, max_frames_in_flight, out); + return create_graphics_pipeline(device, extent, render_pass, offscreen_render_pass, pipeline_info, max_frames_in_flight, out); } -VkResult create_texture_mesh_pipeline(VkDevice device, VkPhysicalDeviceMemoryProperties memories, VkExtent2D extent, VkRenderPass render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, VkCommandPool transfer_pool, VkQueue transfer_queue, VkQueue graphics_queue, VkCommandPool graphics_pool, uint32_t transfer_family, uint32_t graphics_family, GraphicsPipeline* out) { +VkResult create_texture_mesh_pipeline(VkDevice device, VkPhysicalDeviceMemoryProperties memories, VkExtent2D extent, VkRenderPass render_pass, VkRenderPass offscreen_render_pass, VkDescriptorSetLayout scene_layout, uint32_t max_frames_in_flight, VkCommandPool transfer_pool, VkQueue transfer_queue, VkQueue graphics_queue, VkCommandPool graphics_pool, uint32_t transfer_family, uint32_t graphics_family, GraphicsPipeline* out) { if(out == NULL) { return VK_ERROR_VALIDATION_FAILED_EXT; } @@ -1968,15 +2041,20 @@ VkResult create_texture_mesh_pipeline(VkDevice device, VkPhysicalDeviceMemoryPro .pNext = &set_flags_info, }; + VkPipelineVertexInputStateCreateInfo input_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pVertexBindingDescriptions = bindings, + .vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), + .pVertexAttributeDescriptions = attributes, + .vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), + }; + GraphicsPipelineInfo pipeline_info = { .descriptor_info = set_info, .shader_stages_count = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), .shader_stages = shader_stages, .scene_layout = scene_layout, - .input_bindings = bindings, - .input_bindings_count = sizeof(bindings)/sizeof(VkVertexInputBindingDescription), - .input_attributes = attributes, - .input_attributes_count = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), + .input_info = input_info, }; GPUPage* memory = NULL; @@ -1985,7 +2063,7 @@ VkResult create_texture_mesh_pipeline(VkDevice device, VkPhysicalDeviceMemoryPro return result; } - result = create_graphics_pipeline(device, extent, render_pass, pipeline_info, max_frames_in_flight, out); + result = create_graphics_pipeline(device, extent, render_pass, offscreen_render_pass, pipeline_info, max_frames_in_flight, out); if(result != VK_SUCCESS) { return result; } @@ -2297,6 +2375,12 @@ VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) { context->swapchain_command_buffers = swapchain_command_buffers; } + context->offscreen_command_buffers = create_command_buffers(context->device, context->graphics_command_pool, context->max_frames_in_flight); + if(swapchain_command_buffers == VK_NULL_HANDLE) { + fprintf(stderr, "failed to create vulkan swapchain command buffer\n"); + return 0; + } + VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, max_frames_in_flight); if(ia_semaphores == 0) { fprintf(stderr, "failed to create vulkan image available semaphores\n"); @@ -2313,6 +2397,11 @@ VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) { context->render_finished_semaphores = rf_semaphores; } + context->offscreen_complete_semaphores = create_semaphores(context->device, 0, 1); + if(context->offscreen_complete_semaphores == NULL) { + return 0; + } + VkFence* if_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, max_frames_in_flight); if(if_fences == 0) { fprintf(stderr, "failed to create vulkan in flight fence\n"); @@ -2328,7 +2417,7 @@ VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) { VkImageCreateInfo g_pos_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .imageType = VK_IMAGE_TYPE_2D, .extent = { .width = context->swapchain_extent.width, @@ -2368,6 +2457,168 @@ VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) { return 0; } + VkExtent3D offscreen_extent = { + .width = context->swapchain_extent.width, + .height = context->swapchain_extent.height, + .depth = 1, + }; + + VkImageCreateInfo offscreen_depth_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .pNext = NULL, + .extent = offscreen_extent, + .mipLevels = 1, + .arrayLayers = 1, + .format = context->depth_format, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .samples = VK_SAMPLE_COUNT_1_BIT, + .flags = 0, + }; + + result = gpu_image_malloc(context->device, context->g_buffer_page, &offscreen_depth_info, &context->g_image_depth); + if(result != VK_SUCCESS) { + fprintf(stderr, "failed to allocate g_image_depth\n"); + return 0; + } + + VkImageViewCreateInfo g_depth_view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = context->g_image_depth.handle, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = context->depth_format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + result = vkCreateImageView(context->device, &g_depth_view_info, 0, &context->g_image_view_depth); + if(result != VK_SUCCESS) { + fprintf(stderr, "Failed to allocate g_image_view_depth\n"); + return 0; + } + + result = command_transition_image_layout(context->device, context->extra_graphics_pool, context->queues.graphics, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, context->g_image_depth.handle, 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, VK_IMAGE_ASPECT_DEPTH_BIT); + if(result != VK_SUCCESS) { + fprintf(stderr, "Failed to transition g_image_depth to VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL\n"); + return 0; + } + + VkAttachmentDescription offscreen_attachments[] = { + { + .format = VK_FORMAT_R16G16B16A16_SFLOAT, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .format = context->depth_format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + }, + }; + + VkAttachmentReference offscreen_attachment_refs[] = { + { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + } + }; + + VkAttachmentReference offscreen_depth_reference = { + .attachment = 1, + .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + }; + + VkSubpassDescription offscreen_pass_descs[] = { + { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = sizeof(offscreen_attachment_refs)/sizeof(VkAttachmentReference), + .pColorAttachments = offscreen_attachment_refs, + .pDepthStencilAttachment = &offscreen_depth_reference, + }, + }; + + VkSubpassDependency offscreen_pass_dependencies[] = { + { + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + }, + { + .srcSubpass = 0, + .dstSubpass = VK_SUBPASS_EXTERNAL, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + }, + }; + + VkRenderPassCreateInfo offscreen_pass_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = sizeof(offscreen_attachments)/sizeof(VkAttachmentDescription), + .pAttachments = offscreen_attachments, + .subpassCount = sizeof(offscreen_pass_descs)/sizeof(VkSubpassDescription), + .pSubpasses = offscreen_pass_descs, + .dependencyCount = sizeof(offscreen_pass_dependencies)/sizeof(VkSubpassDependency), + .pDependencies = offscreen_pass_dependencies, + }; + + result = vkCreateRenderPass(context->device, &offscreen_pass_info, 0, &context->g_renderpass); + if(result != VK_SUCCESS) { + fprintf(stderr, "Failed to create offscreen renderpass\n"); + return 0; + } + + VkImageView offscreen_fb_attachments[2] = { + context->g_image_view_position, + context->g_image_view_depth, + }; + + VkFramebufferCreateInfo offscreen_framebuffer_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = context->g_renderpass, + .attachmentCount = sizeof(offscreen_fb_attachments)/sizeof(VkImageView), + .pAttachments = offscreen_fb_attachments, + .width = context->swapchain_extent.width, + .height = context->swapchain_extent.height, + .layers = 1, + }; + + result = vkCreateFramebuffer(context->device, &offscreen_framebuffer_info, 0, &context->g_framebuffer); + if(result != VK_SUCCESS) { + fprintf(stderr, "failed to allocate offscreen framebuffer\n"); + return 0; + } + return context; } @@ -2740,7 +2991,7 @@ struct ScenePC get_scene_constants(vec3 world_position, versor world_rotation, f return constants; } -VkResult draw_frame(VulkanContext* context, SceneContext* scene, uint32_t materials_count, GraphicsPipeline* pipelines, uint32_t* objects_counts, Object** objects, VkDeviceAddress object_buffer_addr) { +VkResult draw_frame(VulkanContext* context, SceneContext* scene, uint32_t pipelines_count, GraphicsPipeline* pipelines, uint32_t* objects_counts, Object** objects, VkDeviceAddress object_buffer_addr) { struct ScenePC scene_constants = get_scene_constants(world_position, world_rotation, (float)context->swapchain_extent.width/(float)context->swapchain_extent.height, 0.01); VkResult result; @@ -2765,23 +3016,49 @@ VkResult draw_frame(VulkanContext* context, SceneContext* scene, uint32_t materi return result; } - result = command_draw_scene(materials_count, pipelines, objects_counts, objects, context->current_frame, scene->descriptors, &scene_constants, context->swapchain_command_buffers[context->current_frame], context->render_pass, context->swapchain_framebuffers[image_index], context->swapchain_extent, object_buffer_addr); + result = command_draw_scene(pipelines_count, pipelines, objects_counts, objects, context->current_frame, scene->descriptors, &scene_constants, context->swapchain_command_buffers[context->current_frame], context->render_pass, context->swapchain_framebuffers[image_index], context->swapchain_extent, object_buffer_addr, 0); + if(result != VK_SUCCESS) { + return result; + } + + result = vkResetCommandBuffer(context->offscreen_command_buffers[context->current_frame], 0); + if(result != VK_SUCCESS) { + return result; + } + + result = command_draw_scene(pipelines_count, pipelines, objects_counts, objects, context->current_frame, scene->descriptors, &scene_constants, context->offscreen_command_buffers[context->current_frame], context->g_renderpass, context->g_framebuffer, context->swapchain_extent, object_buffer_addr, 1); if(result != VK_SUCCESS) { return result; } VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - VkSubmitInfo submit_info = { + VkSubmitInfo offscreen_submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &context->image_available_semaphores[context->current_frame], .pWaitDstStageMask = wait_stages, .commandBufferCount = 1, + .pCommandBuffers = &context->offscreen_command_buffers[context->current_frame], + .signalSemaphoreCount = 1, + .pSignalSemaphores = &context->offscreen_complete_semaphores[0], + }; + + VkSubmitInfo submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &context->offscreen_complete_semaphores[0], + .pWaitDstStageMask = wait_stages, + .commandBufferCount = 1, .pCommandBuffers = &context->swapchain_command_buffers[context->current_frame], .signalSemaphoreCount = 1, .pSignalSemaphores = &context->render_finished_semaphores[context->current_frame], }; + result = vkQueueSubmit(context->queues.graphics, 1, &offscreen_submit_info, 0); + if(result != VK_SUCCESS) { + return result; + } + result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fences[context->current_frame]); if(result != VK_SUCCESS) { return result; @@ -2918,7 +3195,7 @@ void main_loop(PlyMesh ply_mesh, GLFWwindow* window, VulkanContext* context) { GraphicsPipeline simple_mesh_pipeline = {0}; GraphicsPipeline texture_mesh_pipeline = {0}; - VkResult result = create_simple_mesh_pipeline(context->device, context->swapchain_extent, context->render_pass, scene.descriptor_layout, context->max_frames_in_flight, &simple_mesh_pipeline); + VkResult result = create_simple_mesh_pipeline(context->device, context->swapchain_extent, context->render_pass, context->g_renderpass, scene.descriptor_layout, context->max_frames_in_flight, &simple_mesh_pipeline); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create simple mesh material: %s\n", string_VkResult(result)); return; @@ -2930,7 +3207,7 @@ void main_loop(PlyMesh ply_mesh, GLFWwindow* window, VulkanContext* context) { return; } - result = create_texture_mesh_pipeline(context->device, context->memories, context->swapchain_extent, context->render_pass, scene.descriptor_layout, context->max_frames_in_flight, context->transfer_command_pool, context->queues.transfer, context->queues.graphics, context->extra_graphics_pool, context->queue_indices.transfer_family, context->queue_indices.graphics_index, &texture_mesh_pipeline); + result = create_texture_mesh_pipeline(context->device, context->memories, context->swapchain_extent, context->render_pass, context->g_renderpass, scene.descriptor_layout, context->max_frames_in_flight, context->transfer_command_pool, context->queues.transfer, context->queues.graphics, context->extra_graphics_pool, context->queue_indices.transfer_family, context->queue_indices.graphics_index, &texture_mesh_pipeline); if(result != VK_SUCCESS) { fprintf(stderr, "failed to create texture mesh material\n"); return;