// ============================================= // Aster: ibl_helpers.cpp // Copyright (c) 2020-2024 Anish Bhobe // ============================================= #include "ibl_helpers.h" #include "EASTL/fixed_vector.h" #include "EASTL/tuple.h" #include "asset_loader.h" #include "device.h" #include "gpu_resource_manager.h" #include "helpers.h" #include "image.h" #include "pipeline_utils.h" constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv"; constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv"; eastl::tuple CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv, const cstr name) { GpuResourceManager *resMan = assetLoader->m_ResourceManager; const Device *pDevice = resMan->m_Device; StackString<64> skyboxName = "Skybox: "; StackString<64> diffuseIrradianceName = "DiffIrr: "; skyboxName += name ? name : ""; diffuseIrradianceName += name ? name : ""; StorageTextureCube skybox; skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, false, skyboxName.c_str()); TextureHandle skyboxHandle = resMan->CommitTexture(&skybox); StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox); StorageTextureCube diffuseIrradiance; diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, diffuseIrradianceName.c_str()); TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance); StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance); #pragma region Dependencies and Copies vk::ImageSubresourceRange cubeSubresRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 6, }; vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = { .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, .dstAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eGeneral, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .subresourceRange = cubeSubresRange, }; eastl::fixed_vector readyToWriteBarriers(2, readyToWriteBarrierTemplate); readyToWriteBarriers[0].image = skybox.m_Image; readyToWriteBarriers[1].image = diffuseIrradiance.m_Image; vk::DependencyInfo readyToWriteDependency = { .imageMemoryBarrierCount = Cast(readyToWriteBarriers.size()), .pImageMemoryBarriers = readyToWriteBarriers.data(), }; vk::ImageMemoryBarrier2 skyboxWriteToReadBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, .dstAccessMask = vk::AccessFlagBits2::eShaderStorageRead, .oldLayout = vk::ImageLayout::eGeneral, .newLayout = vk::ImageLayout::eGeneral, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = skybox.m_Image, .subresourceRange = cubeSubresRange, }; vk::DependencyInfo skyboxReadToWriteDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &skyboxWriteToReadBarrier, }; vk::ImageMemoryBarrier2 skyboxToSampleBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead, .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, .dstAccessMask = vk::AccessFlagBits2::eShaderSampledRead, .oldLayout = vk::ImageLayout::eGeneral, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = skybox.m_Image, .subresourceRange = cubeSubresRange, }; vk::DependencyInfo skyboxToSampleDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &skyboxToSampleBarrier, }; #pragma endregion struct SkyboxPushConstants { TextureHandle m_HdrEnvHandle; StorageTextureHandle m_OutputTexture; u32 m_CubeSide; }; struct DiffuseIrradiancePushConstants { TextureHandle m_SkyboxHandle; StorageTextureHandle m_OutputTexture; u32 m_CubeSide; }; #pragma region Pipeline Creation etc vk::PushConstantRange pcr = { .stageFlags = vk::ShaderStageFlagBits::eCompute, .offset = 0, .size = eastl::max(sizeof(SkyboxPushConstants), sizeof(DiffuseIrradiancePushConstants)), }; vk::PipelineLayout pipelineLayout; const vk::PipelineLayoutCreateInfo layoutCreateInfo = { .setLayoutCount = 1, .pSetLayouts = &resMan->m_SetLayout, .pushConstantRangeCount = 1, .pPushConstantRanges = &pcr, }; AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout)); const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); eastl::array computePipelineCreateInfo = { vk::ComputePipelineCreateInfo{ .stage = { .stage = vk::ShaderStageFlagBits::eCompute, .module = eqRectToCubeShader, .pName = "main", }, .layout = pipelineLayout, }, vk::ComputePipelineCreateInfo{ .stage = { .stage = vk::ShaderStageFlagBits::eCompute, .module = diffuseRadianceShader, .pName = "main", }, .layout = pipelineLayout, }, }; eastl::array pipelines; static_assert(pipelines.size() == computePipelineCreateInfo.size()); AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(), computePipelineCreateInfo.data(), nullptr, pipelines.data())); vk::Pipeline eqRectToCubePipeline = pipelines[0]; vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; for (auto &createInfos : computePipelineCreateInfo) { pDevice->m_Device.destroy(createInfos.stage.module, nullptr); } #pragma endregion SkyboxPushConstants skyboxPushConstant = { .m_HdrEnvHandle = hdrEnv, .m_OutputTexture = skyboxStorageHandle, .m_CubeSide = cubeSide, }; DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = { .m_SkyboxHandle = skyboxHandle, .m_OutputTexture = diffuseIrradianceStorageHandle, .m_CubeSide = diffuseIrradiance.m_Extent.width, }; resMan->Update(); auto cmd = assetLoader->m_CommandBuffer; constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; AbortIfFailed(cmd.begin(&beginInfo)); #if !defined(ASTER_NDEBUG) StackString<128> labelName = "Eqrect -> Cubemap: "; labelName += name ? name : ""; vk::DebugUtilsLabelEXT label = { .pLabelName = labelName.c_str(), .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, }; cmd.beginDebugUtilsLabelEXT(&label); #endif cmd.pipelineBarrier2(&readyToWriteDependency); cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, &skyboxPushConstant); cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); cmd.pipelineBarrier2(&skyboxReadToWriteDependency); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, &diffuseIrradiancePushConstants); cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); cmd.pipelineBarrier2(&skyboxToSampleDependency); #if !defined(ASTER_NDEBUG) cmd.endDebugUtilsLabelEXT(); #endif AbortIfFailed(cmd.end()); vk::SubmitInfo submitInfo = { .waitSemaphoreCount = 0, .pWaitDstStageMask = nullptr, .commandBufferCount = 1, .pCommandBuffers = &cmd, }; vk::Fence fence; vk::FenceCreateInfo fenceCreateInfo = {}; AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); AbortIfFailed(computeQueue.submit(1, &submitInfo, fence)); AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); pDevice->m_Device.destroy(fence, nullptr); AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {})); skybox = {}; resMan->Release(skyboxStorageHandle); resMan->Release(diffuseIrradianceStorageHandle); for (auto &pipeline : pipelines) { pDevice->m_Device.destroy(pipeline, nullptr); } pDevice->m_Device.destroy(pipelineLayout, nullptr); return {skyboxHandle, diffuseIrradianceHandle}; }