mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 17:56:17 +03:00
648 lines
36 KiB
C++
648 lines
36 KiB
C++
#include "Pch.h"
|
|
#include "../DeferredRenderer.h"
|
|
#include "../../Renderer.h"
|
|
#include "../../RenderSceneData.h"
|
|
#include "../../SceneTextures.h"
|
|
#include "../../../AssetSystem/TextureManager.h"
|
|
|
|
namespace Flower
|
|
{
|
|
static AutoCVarCmd cVarUpdateCloudNoise("cmd.Cloud.NoiseUpdate", "Update cloud noise lut.");
|
|
|
|
class VolumetricCloudPass : public PassInterface
|
|
{
|
|
public:
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
|
|
VkPipeline computeCloudPipeline = VK_NULL_HANDLE;
|
|
VkPipelineLayout computeCloudPipelineLayout = VK_NULL_HANDLE;
|
|
|
|
VkPipeline shadowMapPipeline = VK_NULL_HANDLE;
|
|
VkPipeline reconstructionPipeline = VK_NULL_HANDLE;
|
|
VkPipeline compositeCloudPipeline = VK_NULL_HANDLE;
|
|
|
|
VkPipeline envCapturePipeline = VK_NULL_HANDLE;
|
|
VkPipelineLayout envCapturePipelineLayout = VK_NULL_HANDLE;
|
|
public:
|
|
virtual void init() override
|
|
{
|
|
RHI::get()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 0) // imageHdrSceneColor
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 1) // inHdrSceneColor
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 2) // imageCloudRenderTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 3) // inCloudRenderTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 4) // inDepth
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 5) // inGBufferA
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 6) // inBasicNoise
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 7) // inDetailNoise
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 8) // inCloudWeather
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 9) // inCloudGradient
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 10) // inTransmittanceLut
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 11) // inFroxelScatter
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 12) // imageShadowMapCloud
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 13) // inShadowMapCloud
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 14) // imageCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 15) // inCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 16) // imageCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 17) // inCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 18) // imageCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 19) // inCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 20) // inCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 21) // inCloudReconstructionTexture
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GCommonShaderStage, 22) // imageCaptureEnv
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 23) // inDepth
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 24) // inCloudBottom
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 25) // inCloudTop
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 26) // inSkyView
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, GCommonShaderStage, 27) // inSDSMShadowDepth
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GCommonShaderStage, 28) // SSBOCascadeInfoBuffer
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
std::vector<VkDescriptorSetLayout> setLayouts =
|
|
{
|
|
setLayout // Owner layout.
|
|
, GetLayoutStatic(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) // viewData
|
|
, GetLayoutStatic(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) // frameData
|
|
, RHI::SamplerManager->getCommonDescriptorSetLayout() // Common samplers
|
|
, BlueNoiseMisc::getSetLayout() // Bluenoise
|
|
, StaticTexturesManager::get()->globalBlueNoise.spp_1_buffer.setLayouts, // All blue noise set layout is same.
|
|
};
|
|
|
|
// Cloud compute.
|
|
{
|
|
CHECK(computeCloudPipeline == VK_NULL_HANDLE);
|
|
CHECK(computeCloudPipelineLayout == VK_NULL_HANDLE);
|
|
|
|
auto shaderModule = RHI::ShaderManager->getShader("VolumetricCloudRayMarching.comp.spv", true);
|
|
|
|
// Vulkan build functions.
|
|
VkPipelineLayoutCreateInfo plci = RHIPipelineLayoutCreateInfo();
|
|
plci.setLayoutCount = (uint32_t)setLayouts.size();
|
|
plci.pSetLayouts = setLayouts.data();
|
|
computeCloudPipelineLayout = RHI::get()->createPipelineLayout(plci);
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = computeCloudPipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &computeCloudPipeline));
|
|
}
|
|
|
|
{
|
|
auto shaderModule = RHI::ShaderManager->getShader("Cloud_ShadowMap.comp.spv", true);
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = computeCloudPipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &shadowMapPipeline));
|
|
}
|
|
|
|
{
|
|
CHECK(reconstructionPipeline == VK_NULL_HANDLE);
|
|
|
|
auto shaderModule = RHI::ShaderManager->getShader("Cloud_Reconstruction.comp.spv", true);
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = computeCloudPipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &reconstructionPipeline));
|
|
}
|
|
|
|
// Cloud composition compute.
|
|
{
|
|
CHECK(compositeCloudPipeline == VK_NULL_HANDLE);
|
|
|
|
auto shaderModule = RHI::ShaderManager->getShader("VolumetricCompositeWithScreen.comp.spv", true);
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = computeCloudPipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &compositeCloudPipeline));
|
|
}
|
|
|
|
// Env capture pipeline compute.
|
|
{
|
|
CHECK(envCapturePipeline == VK_NULL_HANDLE);
|
|
CHECK(envCapturePipelineLayout == VK_NULL_HANDLE);
|
|
|
|
auto shaderModule = RHI::ShaderManager->getShader("AtmosphereEnvironmentCapture_Cloud.comp.spv", true);
|
|
|
|
// Vulkan build functions.
|
|
VkPipelineLayoutCreateInfo plci = RHIPipelineLayoutCreateInfo();
|
|
plci.setLayoutCount = (uint32_t)setLayouts.size();
|
|
plci.pSetLayouts = setLayouts.data();
|
|
envCapturePipelineLayout = RHI::get()->createPipelineLayout(plci);
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = envCapturePipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &envCapturePipeline));
|
|
}
|
|
}
|
|
|
|
virtual void release() override
|
|
{
|
|
RHISafeRelease(computeCloudPipeline);
|
|
RHISafeRelease(computeCloudPipelineLayout);
|
|
|
|
RHISafeRelease(compositeCloudPipeline);
|
|
RHISafeRelease(shadowMapPipeline);
|
|
RHISafeRelease(reconstructionPipeline);
|
|
|
|
RHISafeRelease(envCapturePipeline);
|
|
RHISafeRelease(envCapturePipelineLayout);
|
|
|
|
setLayout = VK_NULL_HANDLE;
|
|
}
|
|
};
|
|
|
|
void DeferredRenderer::renderVolumetricCloud(
|
|
VkCommandBuffer cmd,
|
|
Renderer* renderer,
|
|
SceneTextures* inTextures,
|
|
RenderSceneData* scene,
|
|
BufferParamRefPointer& viewData,
|
|
BufferParamRefPointer& frameData,
|
|
BlueNoiseMisc& inBlueNoise)
|
|
{
|
|
// Skip if no directional light.
|
|
if (scene->getImportanceLights().directionalLightCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CVarCmdHandle(cVarUpdateCloudNoise, [&]()
|
|
{
|
|
StaticTexturesManager::get()->rebuildCloudTexture(cmd);
|
|
});
|
|
|
|
auto& gbufferTranslucentMask = inTextures->getGbufferUpscaleReactive()->getImage();
|
|
|
|
|
|
|
|
auto& sceneColorHdr = inTextures->getHdrSceneColor()->getImage();
|
|
auto& sceneDepthZ = inTextures->getDepth()->getImage();
|
|
auto& gbufferA = inTextures->getGbufferA()->getImage();
|
|
|
|
auto& basicNoise = StaticTexturesManager::get()->getCloudBasicNoise()->getImage();
|
|
auto& detailNoise = StaticTexturesManager::get()->getCloudWorleyNoise()->getImage();
|
|
|
|
auto weatherTexture = TextureManager::get()->getImage(EngineTextures::GCloudWeatherUUID);
|
|
auto curlNoiseTexture = TextureManager::get()->getImage(EngineTextures::GCurlNoiseUUID);
|
|
|
|
// Quater resolution evaluate.
|
|
auto computeCloud = m_rtPool->createPoolImage(
|
|
"CloudCompute",
|
|
inTextures->getDepth()->getImage().getExtent().width / 4,
|
|
inTextures->getDepth()->getImage().getExtent().height / 4,
|
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
|
|
);
|
|
auto computeCloudDepth = m_rtPool->createPoolImage(
|
|
"CloudComputeDepth",
|
|
inTextures->getDepth()->getImage().getExtent().width / 4,
|
|
inTextures->getDepth()->getImage().getExtent().height / 4,
|
|
VK_FORMAT_R32_SFLOAT,
|
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
|
|
);
|
|
|
|
auto* pass = getPasses()->getPass<VolumetricCloudPass>();
|
|
|
|
VkDescriptorImageInfo hdrSceneColorImageInfo = RHIDescriptorImageInfoStorage(sceneColorHdr.getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo hdrSceneColorInfo = RHIDescriptorImageInfoSample(sceneColorHdr.getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo translucentMaskInfo = RHIDescriptorImageInfoStorage(gbufferTranslucentMask.getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo computeCloudImageInfo = RHIDescriptorImageInfoStorage(computeCloud->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo computeCloudInfo = RHIDescriptorImageInfoSample(computeCloud->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo computeCloudImageInfoDepth = RHIDescriptorImageInfoStorage(computeCloudDepth->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo computeCloudInfoDepth = RHIDescriptorImageInfoSample(computeCloudDepth->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo sceneDepthZInfo = RHIDescriptorImageInfoSample(sceneDepthZ.getView(RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT)));
|
|
|
|
VkDescriptorImageInfo sceneDepthZInfoPrev = sceneDepthZInfo;
|
|
if (m_prevDepth)
|
|
{
|
|
sceneDepthZInfoPrev = RHIDescriptorImageInfoSample(m_prevDepth->getImage().getView(RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT)));
|
|
}
|
|
|
|
VkDescriptorImageInfo gbufferAInfo = RHIDescriptorImageInfoSample(gbufferA.getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo basicNoiseInfo = RHIDescriptorImageInfoSample(basicNoise.getView(buildBasicImageSubresource(), VK_IMAGE_VIEW_TYPE_3D));
|
|
VkDescriptorImageInfo detailNoiseInfo = RHIDescriptorImageInfoSample(detailNoise.getView(buildBasicImageSubresource(), VK_IMAGE_VIEW_TYPE_3D));
|
|
|
|
VkDescriptorImageInfo weatherInfo = RHIDescriptorImageInfoSample(weatherTexture->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo gradientInfo = RHIDescriptorImageInfoSample(curlNoiseTexture->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
auto& transmittanceLut = inTextures->getAtmosphereTransmittance()->getImage();
|
|
auto& froxelScatterLut = inTextures->getAtmosphereFroxelScatter()->getImage();
|
|
transmittanceLut.transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
froxelScatterLut.transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
|
|
|
|
VkDescriptorImageInfo froxelScatterLutInfo = RHIDescriptorImageInfoSample(froxelScatterLut.getView(buildBasicImageSubresource(), VK_IMAGE_VIEW_TYPE_3D));
|
|
VkDescriptorImageInfo tansmittanceLutInfo = RHIDescriptorImageInfoSample(transmittanceLut.getView(buildBasicImageSubresource()));
|
|
|
|
auto cloudShadowMap = m_rtPool->createPoolImage(
|
|
"CloudShadowMap",
|
|
1u, // 768,
|
|
1u, // 768,
|
|
VK_FORMAT_R32_SFLOAT,
|
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
|
|
);
|
|
VkDescriptorImageInfo shadowMapImageInfo = RHIDescriptorImageInfoStorage(cloudShadowMap->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo shadowMapColorInfo = RHIDescriptorImageInfoSample(cloudShadowMap->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
auto newCloudReconstruction = m_rtPool->createPoolImage(
|
|
"NewCloudReconstruction",
|
|
sceneDepthZ.getExtent().width,
|
|
sceneDepthZ.getExtent().height,
|
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
|
|
auto newCloudReconstructionDepth = m_rtPool->createPoolImage(
|
|
"NewCloudReconstructionDepth",
|
|
sceneDepthZ.getExtent().width,
|
|
sceneDepthZ.getExtent().height,
|
|
VK_FORMAT_R32_SFLOAT,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
auto& envCapture = inTextures->getAtmosphereEnvCapture()->getImage();
|
|
|
|
auto captureViewRange = VkImageSubresourceRange
|
|
{
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 6
|
|
};
|
|
|
|
VkDescriptorImageInfo envCaptureImageInfo = RHIDescriptorImageInfoStorage(envCapture.getView(captureViewRange, VK_IMAGE_VIEW_TYPE_CUBE));
|
|
|
|
|
|
auto& skyViewLutCloudBottom = inTextures->getAtmosphereSkyViewCloudBottom()->getImage();
|
|
auto& skyViewLutCloudTop = inTextures->getAtmosphereSkyViewCloudTop()->getImage();
|
|
auto& skyViewLut = inTextures->getAtmosphereSkyView()->getImage();
|
|
|
|
|
|
|
|
VkDescriptorImageInfo skyViewLutInfoBottom = RHIDescriptorImageInfoSample(skyViewLutCloudBottom.getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo skyViewLutInfoTop = RHIDescriptorImageInfoSample(skyViewLutCloudTop.getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo skyViewLutInfo = RHIDescriptorImageInfoSample(skyViewLut.getView(buildBasicImageSubresource()));
|
|
if (!m_cloudReconstruction)
|
|
{
|
|
m_cloudReconstruction = m_rtPool->createPoolImage(
|
|
"CloudReconstruction",
|
|
sceneDepthZ.getExtent().width,
|
|
sceneDepthZ.getExtent().height,
|
|
VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
m_cloudReconstruction->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
if (!m_cloudReconstructionDepth)
|
|
{
|
|
m_cloudReconstructionDepth = m_rtPool->createPoolImage(
|
|
"CloudReconstructionDepth",
|
|
sceneDepthZ.getExtent().width,
|
|
sceneDepthZ.getExtent().height,
|
|
VK_FORMAT_R32_SFLOAT,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
m_cloudReconstructionDepth->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
|
|
VkDescriptorImageInfo reconstructImageInfo = RHIDescriptorImageInfoStorage(newCloudReconstruction->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo reconstructInfo = RHIDescriptorImageInfoSample(newCloudReconstruction->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo reconstructImageDepthInfo = RHIDescriptorImageInfoStorage(newCloudReconstructionDepth->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo reconstructDepthInfo = RHIDescriptorImageInfoSample(newCloudReconstructionDepth->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
VkDescriptorImageInfo hisRecInfo = RHIDescriptorImageInfoSample(m_cloudReconstruction->getImage().getView(buildBasicImageSubresource()));
|
|
VkDescriptorImageInfo hisRecDepthInfo = RHIDescriptorImageInfoSample(m_cloudReconstructionDepth->getImage().getView(buildBasicImageSubresource()));
|
|
|
|
|
|
auto& sdsmShadowDepth =
|
|
(m_cacheFrameData.bSdsmDraw > 0 && inTextures->isSDSMDepthExist()) ?
|
|
inTextures->getSDSMDepth()->getImage() :
|
|
inTextures->getDepth()->getImage();
|
|
auto cascadeInfoBuffer = scene->getCascadeInfoPtr();
|
|
|
|
VkDescriptorImageInfo sdsmDepthInfo = RHIDescriptorImageInfoSample(sdsmShadowDepth.getView(RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT)));
|
|
VkDescriptorBufferInfo cascadeBufferInfo = VkDescriptorBufferInfo{ .buffer = cascadeInfoBuffer->buffer.getBuffer()->getVkBuffer(), .offset = 0, .range = cascadeInfoBuffer->buffer.getBuffer()->getSize() };
|
|
|
|
std::vector<VkWriteDescriptorSet> writes
|
|
{
|
|
RHIPushWriteDescriptorSetImage(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &hdrSceneColorImageInfo),
|
|
RHIPushWriteDescriptorSetImage(1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &hdrSceneColorInfo),
|
|
RHIPushWriteDescriptorSetImage(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &computeCloudImageInfo),
|
|
RHIPushWriteDescriptorSetImage(3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &computeCloudInfo),
|
|
RHIPushWriteDescriptorSetImage(4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &sceneDepthZInfo),
|
|
RHIPushWriteDescriptorSetImage(5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &gbufferAInfo),
|
|
RHIPushWriteDescriptorSetImage(6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &basicNoiseInfo),
|
|
RHIPushWriteDescriptorSetImage(7, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &detailNoiseInfo),
|
|
RHIPushWriteDescriptorSetImage(8, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &weatherInfo),
|
|
RHIPushWriteDescriptorSetImage(9, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &gradientInfo),
|
|
RHIPushWriteDescriptorSetImage(10, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &tansmittanceLutInfo),
|
|
RHIPushWriteDescriptorSetImage(11, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &froxelScatterLutInfo),
|
|
RHIPushWriteDescriptorSetImage(12, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &shadowMapImageInfo),
|
|
RHIPushWriteDescriptorSetImage(13, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &shadowMapColorInfo),
|
|
RHIPushWriteDescriptorSetImage(14, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &reconstructImageInfo),
|
|
RHIPushWriteDescriptorSetImage(15, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &reconstructInfo),
|
|
RHIPushWriteDescriptorSetImage(16, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &computeCloudImageInfoDepth),
|
|
RHIPushWriteDescriptorSetImage(17, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &computeCloudInfoDepth),
|
|
RHIPushWriteDescriptorSetImage(18, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &reconstructImageDepthInfo),
|
|
RHIPushWriteDescriptorSetImage(19, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &reconstructDepthInfo),
|
|
RHIPushWriteDescriptorSetImage(20, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &hisRecInfo),
|
|
RHIPushWriteDescriptorSetImage(21, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &hisRecDepthInfo),
|
|
RHIPushWriteDescriptorSetImage(22, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &envCaptureImageInfo),
|
|
RHIPushWriteDescriptorSetImage(23, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &sceneDepthZInfoPrev),
|
|
RHIPushWriteDescriptorSetImage(24, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &skyViewLutInfoBottom),
|
|
RHIPushWriteDescriptorSetImage(25, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &skyViewLutInfoTop),
|
|
RHIPushWriteDescriptorSetImage(26, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &skyViewLutInfo),
|
|
RHIPushWriteDescriptorSetImage(27, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &sdsmDepthInfo),
|
|
RHIPushWriteDescriptorSetBuffer(28, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &cascadeBufferInfo),
|
|
};
|
|
|
|
std::vector<VkDescriptorSet> compPassSets =
|
|
{
|
|
viewData->buffer.getSet()
|
|
, frameData->buffer.getSet()
|
|
, RHI::SamplerManager->getCommonDescriptorSet()
|
|
, inBlueNoise.getSet()
|
|
, StaticTexturesManager::get()->globalBlueNoise.spp_1_buffer.set // 1spp is good.
|
|
};
|
|
// Push owner set #0.
|
|
RHI::PushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->computeCloudPipelineLayout, 0, uint32_t(writes.size()), writes.data());
|
|
|
|
// Set #1..3
|
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
pass->computeCloudPipelineLayout, 1,
|
|
(uint32_t)compPassSets.size(), compPassSets.data(),
|
|
0, nullptr
|
|
);
|
|
|
|
{
|
|
RHI::ScopePerframeMarker marker(cmd, "CloudCompute", { 1.0f, 1.0f, 0.0f, 1.0f });
|
|
|
|
computeCloud->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
computeCloudDepth->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->computeCloudPipeline);
|
|
|
|
vkCmdDispatch(cmd,
|
|
getGroupCount(computeCloud->getImage().getExtent().width, 8),
|
|
getGroupCount(computeCloud->getImage().getExtent().height, 8), 1);
|
|
|
|
computeCloud->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
computeCloudDepth->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
{
|
|
RHI::ScopePerframeMarker marker(cmd, "CloudReconstruction", { 1.0f, 1.0f, 0.0f, 1.0f });
|
|
|
|
newCloudReconstruction->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
newCloudReconstructionDepth->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->reconstructionPipeline);
|
|
|
|
vkCmdDispatch(cmd,
|
|
getGroupCount(newCloudReconstruction->getImage().getExtent().width, 8),
|
|
getGroupCount(newCloudReconstruction->getImage().getExtent().height, 8), 1);
|
|
|
|
newCloudReconstruction->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
newCloudReconstructionDepth->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
{
|
|
RHI::ScopePerframeMarker marker(cmd, "CloudComposite", { 1.0f, 1.0f, 0.0f, 1.0f });
|
|
gbufferTranslucentMask.transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
sceneColorHdr.transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->compositeCloudPipeline);
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(sceneColorHdr.getExtent().width, 8), getGroupCount(sceneColorHdr.getExtent().height, 8), 1);
|
|
sceneColorHdr.transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
gbufferTranslucentMask.transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
|
|
// Capture pass.
|
|
{
|
|
envCapture.transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, captureViewRange);
|
|
RHI::ScopePerframeMarker marker(cmd, "EnvCapture", { 1.0f, 1.0f, 0.0f, 1.0f });
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->envCapturePipeline);
|
|
|
|
// Push owner set #0.
|
|
RHI::PushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->envCapturePipelineLayout, 0, uint32_t(writes.size()), writes.data());
|
|
|
|
// Set #1..3
|
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
pass->envCapturePipelineLayout, 1,
|
|
(uint32_t)compPassSets.size(), compPassSets.data(),
|
|
0, nullptr
|
|
);
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(envCapture.getExtent().width, 8), getGroupCount(envCapture.getExtent().height, 8), 1);
|
|
envCapture.transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, captureViewRange);
|
|
}
|
|
|
|
m_cloudReconstruction = newCloudReconstruction;
|
|
m_cloudReconstructionDepth = newCloudReconstructionDepth;
|
|
|
|
m_gpuTimer.getTimeStamp(cmd, "Volumetric Cloud");
|
|
|
|
|
|
{
|
|
inTextures->bakeSkyIBL(cmd, m_tickCount % 6);
|
|
}
|
|
m_gpuTimer.getTimeStamp(cmd, "Skylight Bake");
|
|
}
|
|
|
|
|
|
class CloudNoiseComputePass : public PassInterface
|
|
{
|
|
public:
|
|
VkDescriptorSetLayout basicNoiseSetLayout = VK_NULL_HANDLE;
|
|
VkPipeline basicNoisePipeline = VK_NULL_HANDLE;
|
|
VkPipelineLayout basicNoisePipelineLayout = VK_NULL_HANDLE;
|
|
|
|
VkDescriptorSetLayout worleyNoiseSetLayout = VK_NULL_HANDLE;
|
|
VkPipeline worleyNoisePipeline = VK_NULL_HANDLE;
|
|
VkPipelineLayout worleyNoisePipelineLayout = VK_NULL_HANDLE;
|
|
|
|
public:
|
|
virtual void init() override
|
|
{
|
|
|
|
|
|
{
|
|
CHECK(basicNoiseSetLayout == VK_NULL_HANDLE);
|
|
CHECK(basicNoisePipeline == VK_NULL_HANDLE);
|
|
CHECK(basicNoisePipelineLayout == VK_NULL_HANDLE);
|
|
|
|
// Config code.
|
|
RHI::get()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT, 0) // outImage
|
|
.buildNoInfoPush(basicNoiseSetLayout);
|
|
|
|
std::vector<VkDescriptorSetLayout> setLayouts =
|
|
{
|
|
basicNoiseSetLayout, // Owner setlayout.
|
|
};
|
|
auto shaderModule = RHI::ShaderManager->getShader("VolumetricCloudNoiseBasic.comp.spv", true);
|
|
|
|
// Vulkan buid functions.
|
|
VkPipelineLayoutCreateInfo plci = RHIPipelineLayoutCreateInfo();
|
|
plci.setLayoutCount = (uint32_t)setLayouts.size();
|
|
plci.pSetLayouts = setLayouts.data();
|
|
basicNoisePipelineLayout = RHI::get()->createPipelineLayout(plci);
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = basicNoisePipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &basicNoisePipeline));
|
|
}
|
|
|
|
{
|
|
CHECK(worleyNoiseSetLayout == VK_NULL_HANDLE);
|
|
CHECK(worleyNoisePipeline == VK_NULL_HANDLE);
|
|
CHECK(worleyNoisePipelineLayout == VK_NULL_HANDLE);
|
|
|
|
// Config code.
|
|
RHI::get()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT, 0) // outImage
|
|
.buildNoInfoPush(worleyNoiseSetLayout);
|
|
|
|
std::vector<VkDescriptorSetLayout> setLayouts =
|
|
{
|
|
worleyNoiseSetLayout, // Owner setlayout.
|
|
};
|
|
auto shaderModule = RHI::ShaderManager->getShader("VolumetricCloudNoiseWorley.comp.spv", true);
|
|
|
|
// Vulkan buid functions.
|
|
VkPipelineLayoutCreateInfo plci = RHIPipelineLayoutCreateInfo();
|
|
plci.setLayoutCount = (uint32_t)setLayouts.size();
|
|
plci.pSetLayouts = setLayouts.data();
|
|
worleyNoisePipelineLayout = RHI::get()->createPipelineLayout(plci);
|
|
VkPipelineShaderStageCreateInfo shaderStageCI{};
|
|
shaderStageCI.module = shaderModule;
|
|
shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCI.pName = "main";
|
|
VkComputePipelineCreateInfo computePipelineCreateInfo{};
|
|
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
computePipelineCreateInfo.layout = worleyNoisePipelineLayout;
|
|
computePipelineCreateInfo.flags = 0;
|
|
computePipelineCreateInfo.stage = shaderStageCI;
|
|
RHICheck(vkCreateComputePipelines(RHI::Device, nullptr, 1, &computePipelineCreateInfo, nullptr, &worleyNoisePipeline));
|
|
}
|
|
}
|
|
|
|
virtual void release() override
|
|
{
|
|
RHISafeRelease(basicNoisePipeline);
|
|
RHISafeRelease(basicNoisePipelineLayout);
|
|
basicNoiseSetLayout = VK_NULL_HANDLE;
|
|
|
|
RHISafeRelease(worleyNoisePipeline);
|
|
RHISafeRelease(worleyNoisePipelineLayout);
|
|
worleyNoiseSetLayout = VK_NULL_HANDLE;
|
|
}
|
|
};
|
|
|
|
void StaticTextures::initCloudTexture(VkCommandBuffer cmd)
|
|
{
|
|
CHECK(m_cloudBasicNoise == nullptr);
|
|
m_cloudBasicNoise = m_rtPool->createPoolImage(
|
|
"CloudBasicNoise",
|
|
128u,
|
|
128u,
|
|
VK_FORMAT_R8_UNORM,
|
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
1,
|
|
128u
|
|
);
|
|
|
|
auto* pass = m_passCollector->getPass<CloudNoiseComputePass>();
|
|
m_cloudBasicNoise->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
{
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->basicNoisePipeline);
|
|
|
|
VkDescriptorImageInfo imageInfo = RHIDescriptorImageInfoStorage(m_cloudBasicNoise->getImage().getView(buildBasicImageSubresource(), VK_IMAGE_VIEW_TYPE_3D));
|
|
std::vector<VkWriteDescriptorSet> writes
|
|
{
|
|
RHIPushWriteDescriptorSetImage(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo),
|
|
};
|
|
|
|
// Push owner set #0.
|
|
RHI::PushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->basicNoisePipelineLayout, 0, uint32_t(writes.size()), writes.data());
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(m_cloudBasicNoise->getImage().getExtent().width, 8), getGroupCount(m_cloudBasicNoise->getImage().getExtent().height, 8), m_cloudBasicNoise->getImage().getExtent().depth);
|
|
}
|
|
m_cloudBasicNoise->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
|
|
CHECK(m_cloudWorleyNoise == nullptr);
|
|
m_cloudWorleyNoise = m_rtPool->createPoolImage(
|
|
"CloudWorleyNoise",
|
|
64u,
|
|
64u,
|
|
VK_FORMAT_R8_UNORM,
|
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
1,
|
|
64u
|
|
);
|
|
m_cloudWorleyNoise->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, buildBasicImageSubresource());
|
|
{
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->worleyNoisePipeline);
|
|
|
|
VkDescriptorImageInfo imageInfo = RHIDescriptorImageInfoStorage(m_cloudWorleyNoise->getImage().getView(buildBasicImageSubresource(), VK_IMAGE_VIEW_TYPE_3D));
|
|
std::vector<VkWriteDescriptorSet> writes
|
|
{
|
|
RHIPushWriteDescriptorSetImage(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo),
|
|
};
|
|
|
|
// Push owner set #0.
|
|
RHI::PushDescriptorSetKHR(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pass->worleyNoisePipelineLayout, 0, uint32_t(writes.size()), writes.data());
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(
|
|
m_cloudWorleyNoise->getImage().getExtent().width, 8),
|
|
getGroupCount(m_cloudWorleyNoise->getImage().getExtent().height, 8),
|
|
m_cloudWorleyNoise->getImage().getExtent().depth);
|
|
}
|
|
m_cloudWorleyNoise->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
} |