#include "vulkan_common.h" #include "stb_image.h" #include namespace Donut::RHI { auto VulkanDevice::create_cubemap_from_hdri(const std::string& path) -> Ref { auto tex = create_ref(); tex->m_device = m_device; const VkMemoryPropertyFlags host_vis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; const uint32_t FACE = 1024; const VkFormat cube_fmt = VK_FORMAT_R16G16B16A16_SFLOAT; uint32_t CUBE_MIPS = 1; for (uint32_t s = FACE; s > 1; s >>= 1) ++CUBE_MIPS; VkImageCreateInfo cci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; cci.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; cci.imageType = VK_IMAGE_TYPE_2D; cci.format = cube_fmt; cci.extent = { FACE, FACE, 1 }; cci.mipLevels = CUBE_MIPS; cci.arrayLayers = 6; cci.samples = VK_SAMPLE_COUNT_1_BIT; cci.tiling = VK_IMAGE_TILING_OPTIMAL; cci.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; vkCreateImage(m_device, &cci, nullptr, &tex->m_image); VkMemoryRequirements creq{}; vkGetImageMemoryRequirements(m_device, tex->m_image, &creq); VkMemoryAllocateInfo cai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; cai.allocationSize = creq.size; cai.memoryTypeIndex = find_memory_type(creq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(m_device, &cai, nullptr, &tex->m_mem); vkBindImageMemory(m_device, tex->m_image, tex->m_mem, 0); VkImageViewCreateInfo cvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; cvci.image = tex->m_image; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = cube_fmt; cvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, CUBE_MIPS, 0, 6 }; vkCreateImageView(m_device, &cvci, nullptr, &tex->m_view); VkSamplerCreateInfo csm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; csm.magFilter = VK_FILTER_LINEAR; csm.minFilter = VK_FILTER_LINEAR; csm.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; csm.minLod = 0.0f; csm.maxLod = (float)CUBE_MIPS; csm.addressModeU = csm.addressModeV = csm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; vkCreateSampler(m_device, &csm, nullptr, &tex->m_sampler); int w = 0, h = 0, ch = 0; float* pixels = stbi_loadf(path.c_str(), &w, &h, &ch, 4); if (!pixels) { DONUT_WARN("Vulkan RHI: HDRI '{}' could not be loaded; using a dark background", path); VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; VkCommandBuffer cmd; vkAllocateCommandBuffers(m_device, &cbai, &cmd); VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(cmd, &bi); VkImageMemoryBarrier tb{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; tb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; tb.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; tb.image = tex->m_image; tb.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, CUBE_MIPS, 0, 6 }; tb.srcAccessMask = 0; tb.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &tb); VkClearColorValue dark{}; dark.float32[0] = 0.02f; dark.float32[1] = 0.02f; dark.float32[2] = 0.05f; dark.float32[3] = 1.0f; VkImageSubresourceRange rng{ VK_IMAGE_ASPECT_COLOR_BIT, 0, CUBE_MIPS, 0, 6 }; vkCmdClearColorImage(cmd, tex->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dark, 1, &rng); VkImageMemoryBarrier rb = tb; rb.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; rb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; rb.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; rb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &rb); vkEndCommandBuffer(cmd); VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd; vkQueueSubmit(m_graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(m_graphics_queue); vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd); return tex; } const VkFormat eq_fmt = VK_FORMAT_R16G16B16A16_SFLOAT; size_t texel_count = (size_t)w * h * 4; VkDeviceSize eq_size = (VkDeviceSize)texel_count * sizeof(uint16_t); VkBuffer eq_staging; VkDeviceMemory eq_staging_mem; create_buffer_raw(eq_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_vis, eq_staging, eq_staging_mem); void* mp = nullptr; vkMapMemory(m_device, eq_staging_mem, 0, eq_size, 0, &mp); uint16_t* dst = (uint16_t*)mp; for (size_t i = 0; i < texel_count; ++i) { __fp16 hf = (__fp16)pixels[i]; std::memcpy(&dst[i], &hf, sizeof(uint16_t)); } vkUnmapMemory(m_device, eq_staging_mem); stbi_image_free(pixels); VkImage eq_image; VkDeviceMemory eq_mem; VkImageCreateInfo eci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; eci.imageType = VK_IMAGE_TYPE_2D; eci.format = eq_fmt; eci.extent = { (uint32_t)w, (uint32_t)h, 1 }; eci.mipLevels = 1; eci.arrayLayers = 1; eci.samples = VK_SAMPLE_COUNT_1_BIT; eci.tiling = VK_IMAGE_TILING_OPTIMAL; eci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; vkCreateImage(m_device, &eci, nullptr, &eq_image); VkMemoryRequirements ereq{}; vkGetImageMemoryRequirements(m_device, eq_image, &ereq); VkMemoryAllocateInfo eai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; eai.allocationSize = ereq.size; eai.memoryTypeIndex = find_memory_type(ereq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(m_device, &eai, nullptr, &eq_mem); vkBindImageMemory(m_device, eq_image, eq_mem, 0); VkImageView eq_view; VkImageViewCreateInfo evci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; evci.image = eq_image; evci.viewType = VK_IMAGE_VIEW_TYPE_2D; evci.format = eq_fmt; evci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; vkCreateImageView(m_device, &evci, nullptr, &eq_view); VkSampler eq_sampler; VkSamplerCreateInfo esm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; esm.magFilter = VK_FILTER_LINEAR; esm.minFilter = VK_FILTER_LINEAR; esm.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; esm.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; esm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; vkCreateSampler(m_device, &esm, nullptr, &eq_sampler); VkImageView face_views[6]; for (uint32_t i = 0; i < 6; ++i) { VkImageViewCreateInfo fvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; fvci.image = tex->m_image; fvci.viewType = VK_IMAGE_VIEW_TYPE_2D; fvci.format = cube_fmt; fvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, i, 1 }; vkCreateImageView(m_device, &fvci, nullptr, &face_views[i]); } VkAttachmentDescription color{}; color.format = cube_fmt; color.samples = VK_SAMPLE_COUNT_1_BIT; color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE; color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &ref; VkSubpassDependency dep{}; dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL; dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dep.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; VkRenderPass rp; VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; rpci.attachmentCount = 1; rpci.pAttachments = &color; rpci.subpassCount = 1; rpci.pSubpasses = &subpass; rpci.dependencyCount = 1; rpci.pDependencies = &dep; vkCreateRenderPass(m_device, &rpci, nullptr, &rp); VkFramebuffer face_fb[6]; for (uint32_t i = 0; i < 6; ++i) { VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fbci.renderPass = rp; fbci.attachmentCount = 1; fbci.pAttachments = &face_views[i]; fbci.width = FACE; fbci.height = FACE; fbci.layers = 1; vkCreateFramebuffer(m_device, &fbci, nullptr, &face_fb[i]); } VkDescriptorSetLayoutBinding binds[2]{}; binds[0].binding = 0; binds[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[0].descriptorCount = 1; binds[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; binds[1].binding = 1; binds[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[1].descriptorCount = 1; binds[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayout set_layout; VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dslci.bindingCount = 2; dslci.pBindings = binds; vkCreateDescriptorSetLayout(m_device, &dslci, nullptr, &set_layout); VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 6 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6 } }; VkDescriptorPool pool; VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 6; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes; vkCreateDescriptorPool(m_device, &dpci, nullptr, &pool); VkShaderModule vmod, fmod; create_shader_module("assets/shaders/generated/equirect_to_cubemap.vertexMain.spv", vmod); create_shader_module("assets/shaders/generated/equirect_to_cubemap.fragmentMain.spv", fmod); VkPipelineLayout playout; VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &set_layout; vkCreatePipelineLayout(m_device, &plci, nullptr, &playout); VkPipelineShaderStageCreateInfo stages[2]{}; stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main"; stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main"; VkVertexInputBindingDescription vib{ 0, 12, VK_VERTEX_INPUT_RATE_VERTEX }; VkVertexInputAttributeDescription via{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }; VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib; vin.vertexAttributeDescriptionCount = 1; vin.pVertexAttributeDescriptions = &via; VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkViewport vp{ 0, 0, (float)FACE, (float)FACE, 0, 1 }; VkRect2D sc{ { 0, 0 }, { FACE, FACE } }; VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = ≻ VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba; VkPipeline pipeline; VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; gpci.stageCount = 2; gpci.pStages = stages; gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps; gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb; gpci.layout = playout; gpci.renderPass = rp; gpci.subpass = 0; vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline); vkDestroyShaderModule(m_device, vmod, nullptr); vkDestroyShaderModule(m_device, fmod, nullptr); float cube_verts[] = { -1,1,-1, -1,-1,-1, 1,-1,-1, 1,-1,-1, 1,1,-1, -1,1,-1, -1,-1,1, -1,-1,-1, -1,1,-1, -1,1,-1, -1,1,1, -1,-1,1, 1,-1,-1, 1,-1,1, 1,1,1, 1,1,1, 1,1,-1, 1,-1,-1, -1,-1,1, -1,1,1, 1,1,1, 1,1,1, 1,-1,1, -1,-1,1, -1,1,-1, 1,1,-1, 1,1,1, 1,1,1, -1,1,1, -1,1,-1, -1,-1,-1, -1,-1,1, 1,-1,-1, 1,-1,-1, -1,-1,1, 1,-1,1, }; VkBuffer cube_vb; VkDeviceMemory cube_vb_mem; create_buffer_raw(sizeof(cube_verts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, cube_vb, cube_vb_mem); vkMapMemory(m_device, cube_vb_mem, 0, sizeof(cube_verts), 0, &mp); std::memcpy(mp, cube_verts, sizeof(cube_verts)); vkUnmapMemory(m_device, cube_vb_mem); glm::mat4 proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f); proj[1][1] *= -1.0f; glm::mat4 views[6] = { glm::lookAt(glm::vec3(0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)), glm::lookAt(glm::vec3(0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)), glm::lookAt(glm::vec3(0), glm::vec3( 0, 1, 0), glm::vec3(0, 0, 1)), glm::lookAt(glm::vec3(0), glm::vec3( 0, -1, 0), glm::vec3(0, 0, -1)), glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, 1), glm::vec3(0, -1, 0)), glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, -1), glm::vec3(0, -1, 0)), }; VkBuffer ubo[6]; VkDeviceMemory ubo_mem[6]; VkDescriptorSet sets[6]; for (uint32_t i = 0; i < 6; ++i) { create_buffer_raw(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, ubo[i], ubo_mem[i]); glm::mat4 mats[2] = { glm::transpose(proj), glm::transpose(views[i]) }; vkMapMemory(m_device, ubo_mem[i], 0, 128, 0, &mp); std::memcpy(mp, mats, 128); vkUnmapMemory(m_device, ubo_mem[i]); VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &set_layout; vkAllocateDescriptorSets(m_device, &dsai, &sets[i]); VkDescriptorBufferInfo buf_info{ ubo[i], 0, VK_WHOLE_SIZE }; VkDescriptorImageInfo img_info{ eq_sampler, eq_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; VkWriteDescriptorSet ws[2]{}; ws[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[0].dstSet = sets[i]; ws[0].dstBinding = 0; ws[0].descriptorCount = 1; ws[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ws[0].pBufferInfo = &buf_info; ws[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[1].dstSet = sets[i]; ws[1].dstBinding = 1; ws[1].descriptorCount = 1; ws[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; ws[1].pImageInfo = &img_info; vkUpdateDescriptorSets(m_device, 2, ws, 0, nullptr); } VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; VkCommandBuffer cmd; vkAllocateCommandBuffers(m_device, &cbai, &cmd); VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(cmd, &bi); VkImageMemoryBarrier to_dst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; to_dst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; to_dst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; to_dst.image = eq_image; to_dst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; to_dst.srcAccessMask = 0; to_dst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_dst); VkBufferImageCopy copy{}; copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copy.imageExtent = { (uint32_t)w, (uint32_t)h, 1 }; vkCmdCopyBufferToImage(cmd, eq_staging, eq_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); VkImageMemoryBarrier to_read = to_dst; to_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; to_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; to_read.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; to_read.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_read); VkClearValue clear{}; clear.color = { { 0, 0, 0, 1 } }; for (uint32_t i = 0; i < 6; ++i) { VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; rpbi.renderPass = rp; rpbi.framebuffer = face_fb[i]; rpbi.renderArea = { { 0, 0 }, { FACE, FACE } }; rpbi.clearValueCount = 1; rpbi.pClearValues = &clear; vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, playout, 0, 1, &sets[i], 0, nullptr); VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &cube_vb, &off); vkCmdDraw(cmd, 36, 1, 0, 0); vkCmdEndRenderPass(cmd); } { VkImageMemoryBarrier src0{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; src0.image = tex->m_image; src0.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 }; src0.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; src0.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; src0.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; src0.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &src0); int32_t mipW = (int32_t)FACE, mipH = (int32_t)FACE; for (uint32_t m = 1; m < CUBE_MIPS; ++m) { int32_t nW = mipW > 1 ? mipW / 2 : 1, nH = mipH > 1 ? mipH / 2 : 1; VkImageMemoryBarrier bd{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; bd.image = tex->m_image; bd.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, m, 1, 0, 6 }; bd.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; bd.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; bd.srcAccessMask = 0; bd.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &bd); VkImageBlit blit{}; blit.srcOffsets[1] = { mipW, mipH, 1 }; blit.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, m - 1, 0, 6 }; blit.dstOffsets[1] = { nW, nH, 1 }; blit.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, m, 0, 6 }; vkCmdBlitImage(cmd, tex->m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tex->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); VkImageMemoryBarrier bs{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; bs.image = tex->m_image; bs.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, m, 1, 0, 6 }; bs.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; bs.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; bs.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; bs.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &bs); mipW = nW; mipH = nH; } VkImageMemoryBarrier fin{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; fin.image = tex->m_image; fin.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, CUBE_MIPS, 0, 6 }; fin.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; fin.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; fin.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; fin.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &fin); } vkEndCommandBuffer(cmd); VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd; vkQueueSubmit(m_graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(m_graphics_queue); vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd); for (uint32_t i = 0; i < 6; ++i) { vkDestroyBuffer(m_device, ubo[i], nullptr); vkFreeMemory(m_device, ubo_mem[i], nullptr); vkDestroyFramebuffer(m_device, face_fb[i], nullptr); vkDestroyImageView(m_device, face_views[i], nullptr); } vkDestroyBuffer(m_device, cube_vb, nullptr); vkFreeMemory(m_device, cube_vb_mem, nullptr); vkDestroyPipeline(m_device, pipeline, nullptr); vkDestroyPipelineLayout(m_device, playout, nullptr); vkDestroyDescriptorPool(m_device, pool, nullptr); vkDestroyDescriptorSetLayout(m_device, set_layout, nullptr); vkDestroyRenderPass(m_device, rp, nullptr); vkDestroySampler(m_device, eq_sampler, nullptr); vkDestroyImageView(m_device, eq_view, nullptr); vkDestroyImage(m_device, eq_image, nullptr); vkFreeMemory(m_device, eq_mem, nullptr); vkDestroyBuffer(m_device, eq_staging, nullptr); vkFreeMemory(m_device, eq_staging_mem, nullptr); DONUT_INFO("Vulkan RHI: HDRI cubemap built from {} ({}x{} equirect -> {}^2 cube)", path, w, h, (int)FACE); return tex; } }