project-aster/samples/03_model_render/ibl_helpers.cpp

171 lines
6.0 KiB
C++

// =============================================
// Aster: ibl_helpers.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "ibl_helpers.h"
#include "asset_loader.h"
#include "device.h"
#include "gpu_resource_manager.h"
#include "helpers.h"
#include "image.h"
#include "pipeline_utils.h"
#include "EASTL/tuple.h"
constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrectToCube.cs.hlsl.spv";
TextureHandle
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;
StorageTextureCube cubeMap;
cubeMap.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, false, name ? name : "Env CubeMap");
StorageTextureHandle envStagingHandle = resMan->CommitStorageTexture(&cubeMap);
vk::ImageSubresourceRange cubeSubresRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 6,
};
vk::ImageMemoryBarrier2 readyToWriteBarrier = {
.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,
.image = cubeMap.m_Image,
.subresourceRange = cubeSubresRange,
};
vk::DependencyInfo readyToWriteDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &readyToWriteBarrier,
};
vk::ImageMemoryBarrier2 cubemapToRead = {
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
.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 = cubeMap.m_Image,
.subresourceRange = cubeSubresRange,
};
vk::DependencyInfo cubemapToReadDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &cubemapToRead,
};
struct WorkloadPushConstants
{
TextureHandle m_HdrEnvHandle;
StorageTextureHandle m_OutputTexture;
u32 m_CubeSide;
};
#pragma region Pipeline Creation etc
const auto shaderModule = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE);
const vk::PipelineShaderStageCreateInfo shaderStageCreateInfo = {
.stage = vk::ShaderStageFlagBits::eCompute,
.module = shaderModule,
.pName = "main",
};
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eCompute,
.offset = 0,
.size = sizeof(WorkloadPushConstants),
};
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 vk::ComputePipelineCreateInfo computePipelineCreateInfo = {
.stage = shaderStageCreateInfo,
.layout = pipelineLayout,
};
vk::Pipeline pipeline;
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, 1, &computePipelineCreateInfo,
nullptr, &pipeline));
pDevice->m_Device.destroy(shaderModule, nullptr);
#pragma endregion
WorkloadPushConstants pushConstants = {
.m_HdrEnvHandle = hdrEnv, .m_OutputTexture = envStagingHandle, .m_CubeSide = cubeSide};
resMan->Update();
auto cmd = assetLoader->m_CommandBuffer;
constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(cmd.begin(&beginInfo));
#if !defined(NDEBUG)
StackString<128> labelName = "Eqrect -> Cubemap: ";
labelName += name ? name : "<unknown env>";
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, pipeline);
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof pushConstants, &pushConstants);
cmd.dispatch(cubeSide / 16, cubeSide / 16, 6);
cmd.pipelineBarrier2(&cubemapToReadDependency);
#if !defined(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<u32>));
pDevice->m_Device.destroy(fence, nullptr);
AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {}));
cubeMap = {};
resMan->Release(&cubeMap, envStagingHandle);
pDevice->m_Device.destroy(pipeline, nullptr);
pDevice->m_Device.destroy(pipelineLayout, nullptr);
return resMan->CommitTexture(&cubeMap);
}