diff --git a/samples/00_util/gpu_resource_manager.cpp b/samples/00_util/gpu_resource_manager.cpp index f4bf604..a6bbf0f 100644 --- a/samples/00_util/gpu_resource_manager.cpp +++ b/samples/00_util/gpu_resource_manager.cpp @@ -22,7 +22,7 @@ TextureManager::Init(const u32 maxCapacity) TextureHandle TextureManager::Commit(Texture *texture) { - ERROR_IF(!texture || !texture->IsValid() || !texture->IsOwned(), "Texture must be valid and owned for commital") + ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for commital") THEN_ABORT(-1); if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index b5603c8..d7a93b4 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -19,9 +19,10 @@ add_executable(model_render model_render.cpp add_shader(model_render shader/model.vs.hlsl) add_shader(model_render shader/model.ps.hlsl) -add_shader(model_render shader/eqrectToCube.cs.hlsl) +add_shader(model_render shader/eqrect_to_cube.cs.hlsl) add_shader(model_render shader/background.vs.hlsl) add_shader(model_render shader/background.ps.hlsl) +add_shader(model_render shader/diffuse_irradiance.cs.hlsl) target_link_libraries(model_render PRIVATE aster_core) target_link_libraries(model_render PRIVATE util_helper) diff --git a/samples/03_model_render/ibl_helpers.cpp b/samples/03_model_render/ibl_helpers.cpp index edd8a64..4b235eb 100644 --- a/samples/03_model_render/ibl_helpers.cpp +++ b/samples/03_model_render/ibl_helpers.cpp @@ -5,27 +5,41 @@ #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" -#include "EASTL/tuple.h" -constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrectToCube.cs.hlsl.spv"; +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"; -TextureHandle +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; - StorageTextureCube cubeMap; - cubeMap.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, false, name ? name : "Env CubeMap"); - StorageTextureHandle envStagingHandle = resMan->CommitStorageTexture(&cubeMap); + 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, @@ -34,7 +48,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .layerCount = 6, }; - vk::ImageMemoryBarrier2 readyToWriteBarrier = { + vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = { .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, @@ -43,50 +57,73 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .newLayout = vk::ImageLayout::eGeneral, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = cubeMap.m_Image, .subresourceRange = cubeSubresRange, }; + eastl::fixed_vector readyToWriteBarriers(2, readyToWriteBarrierTemplate); + readyToWriteBarriers[0].image = skybox.m_Image; + readyToWriteBarriers[1].image = diffuseIrradiance.m_Image; + vk::DependencyInfo readyToWriteDependency = { - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &readyToWriteBarrier, + .imageMemoryBarrierCount = Cast(readyToWriteBarriers.size()), + .pImageMemoryBarriers = readyToWriteBarriers.data(), }; - vk::ImageMemoryBarrier2 cubemapToRead = { + + 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 skyboxWriteToReadDependency = { + .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 = cubeMap.m_Image, + .image = skybox.m_Image, .subresourceRange = cubeSubresRange, }; - vk::DependencyInfo cubemapToReadDependency = { + vk::DependencyInfo skyboxToSampleDependency = { .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &cubemapToRead, + .pImageMemoryBarriers = &skyboxToSampleBarrier, }; - struct WorkloadPushConstants +#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 - 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), + .size = eastl::max(sizeof(SkyboxPushConstants), sizeof(DiffuseIrradiancePushConstants)), }; vk::PipelineLayout pipelineLayout; @@ -98,21 +135,55 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 }; AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout)); - const vk::ComputePipelineCreateInfo computePipelineCreateInfo = { - .stage = shaderStageCreateInfo, - .layout = 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, + }, }; - vk::Pipeline pipeline; - AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, 1, &computePipelineCreateInfo, - nullptr, &pipeline)); + eastl::array pipelines; + static_assert(pipelines.size() == computePipelineCreateInfo.size()); + AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(), + computePipelineCreateInfo.data(), nullptr, + pipelines.data())); - pDevice->m_Device.destroy(shaderModule, nullptr); + vk::Pipeline eqRectToCubePipeline = pipelines[0]; + vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; + + for (auto &createInfos : computePipelineCreateInfo) + { + pDevice->m_Device.destroy(createInfos.stage.module, nullptr); + } #pragma endregion - WorkloadPushConstants pushConstants = { - .m_HdrEnvHandle = hdrEnv, .m_OutputTexture = envStagingHandle, .m_CubeSide = cubeSide}; + 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(); @@ -133,11 +204,19 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 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.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, + &skyboxPushConstant); cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); - cmd.pipelineBarrier2(&cubemapToReadDependency); + cmd.pipelineBarrier2(&skyboxWriteToReadDependency); + + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, + &diffuseIrradiancePushConstants); + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); + cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); + + cmd.pipelineBarrier2(&skyboxToSampleDependency); #if !defined(NDEBUG) cmd.endDebugUtilsLabelEXT(); @@ -161,10 +240,14 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {})); - cubeMap = {}; - resMan->Release(&cubeMap, envStagingHandle); - pDevice->m_Device.destroy(pipeline, nullptr); + skybox = {}; + resMan->Release(skyboxStorageHandle); + resMan->Release(diffuseIrradianceStorageHandle); + for (auto &pipeline : pipelines) + { + pDevice->m_Device.destroy(pipeline, nullptr); + } pDevice->m_Device.destroy(pipelineLayout, nullptr); - return resMan->CommitTexture(&cubeMap); -} + return {skyboxHandle, diffuseIrradianceHandle}; +} \ No newline at end of file diff --git a/samples/03_model_render/ibl_helpers.h b/samples/03_model_render/ibl_helpers.h index d3adcc4..2c742b7 100644 --- a/samples/03_model_render/ibl_helpers.h +++ b/samples/03_model_render/ibl_helpers.h @@ -8,10 +8,13 @@ #include "global.h" #include "gpu_resource_manager.h" +#include + struct Pipeline; struct Texture; struct TextureCube; struct AssetLoader; -TextureHandle CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, +eastl::tuple +CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, cstr name = nullptr); \ No newline at end of file diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 15c3384..42fb9df 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -18,10 +18,10 @@ #include "helpers.h" #include "light_manager.h" +#include "asset_loader.h" #include "gpu_resource_manager.h" #include "gui.h" #include "ibl_helpers.h" -#include "asset_loader.h" #include "pipeline_utils.h" #include @@ -180,7 +180,7 @@ main(int, char **) assetLoader.LoadHdrImage(&environment, BACKDROP_FILE); auto envHandle = resourceManager.CommitTexture(&environment); - TextureHandle texCube = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 1024, envHandle, "Cube Env"); + auto [texCube, diffuseIrr] = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 1024, envHandle, "Cube Env"); resourceManager.Release(envHandle); @@ -371,6 +371,7 @@ main(int, char **) queueAllocation.m_Family, graphicsQueue); bool rotating = false; bool lockToScreen = true; + bool showDiffuse = false; i32 height = Cast(internalResolution.height); f32 camPitch = glm::degrees(cameraController.m_Pitch); f32 camYaw = glm::degrees(cameraController.m_Yaw); @@ -445,6 +446,7 @@ main(int, char **) { cameraController.SetPosition(camPosition); } + gui::Checkbox("Show DiffIrr", &showDiffuse); gui::Checkbox("Rotate", &rotating); gui::PopItemWidth(); if (gui::Button("Exit")) @@ -546,8 +548,18 @@ main(int, char **) cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles, &model.m_Handles); pcbOffset += sizeof model.m_Handles; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube); - pcbOffset += sizeof texCube; + if (showDiffuse) + { + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, + &diffuseIrr); + pcbOffset += sizeof diffuseIrr; + } + else + { + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube); + pcbOffset += sizeof texCube; + } + static_assert(sizeof texCube == sizeof diffuseIrr); cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); pcbOffset += sizeof lightManager.m_MetaInfo; @@ -626,6 +638,7 @@ main(int, char **) AbortIfFailed(device.m_Device.waitIdle()); resourceManager.Release(texCube); + resourceManager.Release(diffuseIrr); pipelineCacheData = device.DumpPipelineCache(); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); diff --git a/samples/03_model_render/shader/bindless_structs.hlsli b/samples/03_model_render/shader/bindless_structs.hlsli index 2d0a75c..4dab07e 100644 --- a/samples/03_model_render/shader/bindless_structs.hlsli +++ b/samples/03_model_render/shader/bindless_structs.hlsli @@ -48,7 +48,9 @@ struct DirectionalLight #define INVALID_HANDLE 0xFFFFFFFF +static const float HALF_PI = 1.57079633f; static const float PI = 3.14159265f; +static const float TAU = 6.28318530f; [[vk::binding(0, 0)]] StructuredBuffer VertexBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer VertexDataBuffer[]; diff --git a/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl new file mode 100644 index 0000000..0023bec --- /dev/null +++ b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl @@ -0,0 +1,51 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint SkyboxHandle; + uint OutputTextureHandle; + int CubeSide; +}; + +[[vk::push_constant]] +Block pcb; + +/* +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| -z | 4 | -y | +| +z | 5 | -y | +*/ + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float3 Forward, Up, Right; + + Forward = GetLocalDir(pcb.CubeSide, GlobalInvocationID); + Up = float3(0.0f, 1.0f, 0.0f); // 0.01f offset to + Right = normalize(cross(Up, Forward)); + Up = normalize(cross(Forward, Right)); + + float3 Irradiance = 0.0f.xxx; + float3 IrrDirr = 0.0f.xxx; + float SampleStep = 0.025f; + float SampleCount = 0.0f; + + for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep) + { + for (float Zenith = 0.0f; Zenith < HALF_PI; Zenith += SampleStep) + { + float3 DirectionTanSpace = float3(sin(Zenith) * cos(Azimuth), sin(Zenith) * sin(Azimuth), cos(Zenith)); + float3 DirectionWorld = DirectionTanSpace.x * Right + DirectionTanSpace.y * Up + DirectionTanSpace.z * Forward; + Irradiance += TextureCubes[pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[pcb.SkyboxHandle], DirectionWorld, 0).xyz * (cos(Zenith) * sin(Zenith)); + SampleCount++; + } + } + + StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f); +} \ No newline at end of file diff --git a/samples/03_model_render/shader/eqrectToCube.cs.hlsl b/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl similarity index 55% rename from samples/03_model_render/shader/eqrectToCube.cs.hlsl rename to samples/03_model_render/shader/eqrect_to_cube.cs.hlsl index 9629338..580e0f6 100644 --- a/samples/03_model_render/shader/eqrectToCube.cs.hlsl +++ b/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl @@ -1,10 +1,10 @@ -#include "bindless_structs.hlsli" +#include "ibl_common.hlsli" struct Block { uint HdrEnvHandle; uint OutputTextureHandle; - int CubeSize; + int CubeSide; }; [[vk::push_constant]] @@ -33,23 +33,8 @@ float2 SampleSphericalMap(float3 v) [numthreads(16, 16, 1)] void main(uint3 GlobalInvocationID : SV_DispatchThreadID) { - float3 LocalDir; - float HalfSide = pcb.CubeSize / 2; - float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2)); + float3 LocalDir = GetLocalDir(pcb.CubeSide, GlobalInvocationID); - if (GlobalInvocationID.z < 2) - { - LocalDir = float3(AxisSign * HalfSide, HalfSide - GlobalInvocationID.y, (HalfSide - GlobalInvocationID.x) * AxisSign); - } - else if (GlobalInvocationID.z < 4) - { - LocalDir = float3(GlobalInvocationID.x - HalfSide, AxisSign * HalfSide, AxisSign * (GlobalInvocationID.y - HalfSide)); - } - else - { - LocalDir = float3((GlobalInvocationID.x - HalfSide) * AxisSign, HalfSide - GlobalInvocationID.y, HalfSide * AxisSign); - } - - float2 UV = SampleSphericalMap(normalize(LocalDir)); + float2 UV = SampleSphericalMap(LocalDir); StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0); } \ No newline at end of file diff --git a/samples/03_model_render/shader/ibl_common.hlsli b/samples/03_model_render/shader/ibl_common.hlsli new file mode 100644 index 0000000..2ad3436 --- /dev/null +++ b/samples/03_model_render/shader/ibl_common.hlsli @@ -0,0 +1,22 @@ +#include "bindless_structs.hlsli" + +float3 GetLocalDir(float Side, float3 GlobalInvocationID) +{ + float HalfSide = Side / 2; + float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2)); + + float3 LocalDir; + if (GlobalInvocationID.z < 2) + { + LocalDir = float3(AxisSign * HalfSide, HalfSide - GlobalInvocationID.y, (HalfSide - GlobalInvocationID.x) * AxisSign); + } + else if (GlobalInvocationID.z < 4) + { + LocalDir = float3(GlobalInvocationID.x - HalfSide, AxisSign * HalfSide, AxisSign * (GlobalInvocationID.y - HalfSide)); + } + else + { + LocalDir = float3((GlobalInvocationID.x - HalfSide) * AxisSign, HalfSide - GlobalInvocationID.y, HalfSide * AxisSign); + } + return normalize(LocalDir); +} \ No newline at end of file