/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AZ { namespace Render { RPI::Ptr BloomCompositePass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew BloomCompositePass(descriptor); return AZStd::move(pass); } BloomCompositePass::BloomCompositePass(const RPI::PassDescriptor& descriptor) : ParentPass(descriptor) { // Load DownsampleMipChainPassData (shader asset) const RPI::DownsampleMipChainPassData* passData = RPI::PassUtils::GetPassData(descriptor); if (passData == nullptr) { AZ_Error("PassSystem", false, "[BloomCompositePass '%s']: Trying to construct without valid DownsampleMipChainPassData!", GetPathName().GetCStr()); return; } m_passData = *passData; } void BloomCompositePass::BuildInternal() { BuildChildPasses(); ParentPass::BuildInternal(); } void BloomCompositePass::FrameBeginInternal(FramePrepareParams params) { GetAttachmentInfo(); UpdateParameters(); UpdateChildren(); ParentPass::FrameBeginInternal(params); } void BloomCompositePass::GetAttachmentInfo() { AZ_Assert(GetInputCount() > 0, "[BloomCompositePass '%s']: must have an input", GetPathName().GetCStr()); // The Output attachment of composite pass is provided by downsample pass, because // composite pass as a parent pass is unable to bind attachment to slot by itself // in the pass file, which could result in errors during following passes' initialization AZ_Assert(GetInputOutputCount() > 0, "[BloomCompositePass '%s']: must have an output", GetPathName().GetCStr()); RPI::PassAttachment* inAttachment = GetInputBinding(0).m_attachment.get(); RPI::PassAttachment* outAttachment = GetInputOutputBinding(0).m_attachment.get(); if (inAttachment != nullptr && outAttachment != nullptr) { m_inputWidth = inAttachment->m_descriptor.m_image.m_size.m_width; m_inputHeight = inAttachment->m_descriptor.m_image.m_size.m_height; m_outputWidth = outAttachment->m_descriptor.m_image.m_size.m_width; m_outputHeight = outAttachment->m_descriptor.m_image.m_size.m_height; } } void BloomCompositePass::UpdateParameters() { RPI::Scene* scene = GetScene(); PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); RPI::ViewPtr view = scene->GetDefaultRenderPipeline()->GetDefaultView(); if (fp) { PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); if (postProcessSettings) { BloomSettings* bloomSettings = postProcessSettings->GetBloomSettings(); if (bloomSettings) { m_intensity = bloomSettings->GetIntensity(); m_enableBicubic = bloomSettings->GetBicubicEnabled(); m_tintData[0] = bloomSettings->GetTintStage0(); m_tintData[1] = bloomSettings->GetTintStage1(); m_tintData[2] = bloomSettings->GetTintStage2(); m_tintData[3] = bloomSettings->GetTintStage3(); m_tintData[4] = bloomSettings->GetTintStage4(); } } } } void BloomCompositePass::CreateBinding(BloomCompositeChildPass* pass, uint32_t mipLevel) { RPI::PassAttachmentBinding& parentInBinding = GetInputBinding(0); RPI::Ptr& parentInAttachment = parentInBinding.m_attachment; RPI::PassAttachmentBinding& parentInOutBinding = GetInputOutputBinding(0); RPI::Ptr& parentInOutAttachment = parentInOutBinding.m_attachment; // Create input binding, from downsampling pass RPI::PassAttachmentBinding inBinding; inBinding.m_name = "Input"; inBinding.m_shaderInputName = "m_inputTexture"; inBinding.m_slotType = RPI::PassSlotType::Input; inBinding.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Shader; inBinding.m_attachment = parentInAttachment; inBinding.m_connectedBinding = &parentInBinding; RHI::ImageViewDescriptor inViewDesc; inViewDesc.m_mipSliceMin = static_cast(mipLevel); inViewDesc.m_mipSliceMax = static_cast(mipLevel); inBinding.m_unifiedScopeDesc.SetAsImage(inViewDesc); pass->AddAttachmentBinding(inBinding); // Create output binding, owned by current pass RPI::PassAttachmentBinding outBinding; outBinding.m_name = "Output"; outBinding.m_shaderInputName = "m_outputTexture"; outBinding.m_slotType = RPI::PassSlotType::Output; outBinding.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Shader; outBinding.m_attachment = (mipLevel == 0) ? parentInOutAttachment : parentInAttachment; outBinding.m_connectedBinding = (mipLevel == 0) ? &parentInOutBinding : &parentInBinding; if (mipLevel != 0) { RHI::ImageViewDescriptor outViewDesc; outViewDesc.m_mipSliceMin = static_cast(mipLevel - 1); outViewDesc.m_mipSliceMax = static_cast(mipLevel - 1); outBinding.m_unifiedScopeDesc.SetAsImage(outViewDesc); } pass->AddAttachmentBinding(outBinding); } void BloomCompositePass::BuildChildPasses() { if (!m_children.empty()) { // In this case children are still exists but attachment binding is flushed out, so we rebind them again for (size_t childIndex = 0, mipLevel = m_children.size() - 1; childIndex < m_children.size(); ++childIndex, --mipLevel) { BloomCompositeChildPass* compositeChild = static_cast(m_children[childIndex].get()); CreateBinding(compositeChild, static_cast(mipLevel)); } } else { // Create children RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get(); for (size_t childIndex = 0, mipLevel = Render::Bloom::MaxStageCount - 1; childIndex < Render::Bloom::MaxStageCount; ++childIndex, --mipLevel) { RPI::PassDescriptor childDesc; childDesc.m_passData = AZStd::make_shared(); RPI::ComputePassData* passData = static_cast(childDesc.m_passData.get()); passData->m_shaderReference = m_passData.m_shaderReference; childDesc.m_passName = Name{ AZStd::string::format("BloomComposite%d", static_cast(childIndex)) }; RPI::Ptr childPass = passSystem->CreatePass(childDesc); CreateBinding(childPass.get(), static_cast(mipLevel)); AddChild(childPass); } } } void BloomCompositePass::UpdateChildren() { uint32_t sourceWidth, sourceHeight, targetWidth, targetHeight; targetWidth = m_inputWidth; targetHeight = m_inputHeight; sourceWidth = targetWidth / 2; sourceHeight = targetHeight / 2; BloomCompositeChildPass* compositeChild = static_cast(m_children[m_children.size() - 1].get()); compositeChild->UpdateParameters(0, m_inputWidth, m_inputHeight, m_outputWidth, m_outputHeight, m_enableBicubic, m_tintData[0], m_intensity); for (int childIndex = static_cast(m_children.size() - 2), mipLevel = 1; childIndex >= 0; --childIndex, ++mipLevel) { BloomCompositeChildPass* blurChild = static_cast(m_children[childIndex].get()); blurChild->UpdateParameters(static_cast(mipLevel), sourceWidth, sourceHeight, targetWidth, targetHeight, m_enableBicubic, m_tintData[m_children.size() - childIndex - 1], m_intensity); targetWidth = sourceWidth; targetHeight = sourceHeight; sourceWidth = targetWidth / 2; sourceHeight = targetHeight / 2; } } RPI::Ptr BloomCompositeChildPass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew BloomCompositeChildPass(descriptor); return AZStd::move(pass); } BloomCompositeChildPass::BloomCompositeChildPass(const RPI::PassDescriptor& descriptor) : ComputePass(descriptor) { } void BloomCompositeChildPass::UpdateParameters(uint32_t sourceMip, uint32_t sourceImageWidth, uint32_t sourceImageHeight, uint32_t targetImageWidth, uint32_t targetImageHeight, bool enableBicubic, Vector3 tint, float intensity) { m_shaderResourceGroup->SetConstant(m_intensityInputIndex, intensity); float sourceWidth = static_cast(sourceImageWidth); float sourceHeight = static_cast(sourceImageHeight); m_shaderResourceGroup->SetConstant(m_sourceImageSizeInputIndex, AZ::Vector2(sourceWidth, sourceHeight)); m_shaderResourceGroup->SetConstant(m_sourceImageTexelSizeInputIndex, AZ::Vector2(1.0f / sourceWidth, 1.0f / sourceHeight)); m_shaderResourceGroup->SetConstant(m_sourceMipLevelInputIndex, sourceMip); m_shaderResourceGroup->SetConstant(m_enableBicubicInputIndex, enableBicubic); m_shaderResourceGroup->SetConstant(m_tintInputIndex, tint); m_targetImageWidth = targetImageWidth; m_targetImageHeight = targetImageHeight; } void BloomCompositeChildPass::FrameBeginInternal(FramePrepareParams params) { m_shaderResourceGroup->SetConstant(m_targetImageSizeInputIndex, AZ::Vector2(static_cast(m_targetImageWidth), static_cast(m_targetImageHeight))); SetTargetThreadCounts(m_targetImageWidth, m_targetImageHeight, 1); ComputePass::FrameBeginInternal(params); } } // namespace RPI } // namespace AZ