/* * 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 namespace AZ { namespace RPI { // --- PassAttachment --- PassAttachment::PassAttachment(const PassImageAttachmentDesc& attachmentDesc) { m_name = attachmentDesc.m_name; m_lifetime = attachmentDesc.m_lifetime; m_generateFullMipChain = attachmentDesc.m_generateFullMipChain; m_descriptor = RHI::UnifiedAttachmentDescriptor(attachmentDesc.m_imageDescriptor); ValidateDeviceFormats(attachmentDesc.m_formatFallbacks); } PassAttachment::PassAttachment(const PassBufferAttachmentDesc& attachmentDesc) { m_descriptor = RHI::UnifiedAttachmentDescriptor(attachmentDesc.m_bufferDescriptor); m_name = attachmentDesc.m_name; m_lifetime = attachmentDesc.m_lifetime; } Ptr PassAttachment::Clone() const { Ptr clone = aznew PassAttachment(); clone->m_name = this->m_name; clone->m_descriptor = this->m_descriptor; clone->m_lifetime = this->m_lifetime; clone->m_formatSource = this->m_formatSource; clone->m_multisampleSource = this->m_multisampleSource; clone->m_sizeSource = this->m_sizeSource; clone->m_sizeMultipliers = this->m_sizeMultipliers; clone->m_arraySizeSource = this->m_arraySizeSource; clone->m_generateFullMipChain = this->m_generateFullMipChain; return clone; } void PassAttachment::ValidateDeviceFormats(const AZStd::vector& formatFallbacks, RHI::FormatCapabilities capabilities) { if (m_descriptor.m_type == RHI::AttachmentType::Image) { RHI::Format format = m_descriptor.m_image.m_format; capabilities |= RHI::FormatCapabilities::Sample; AZStd::string formatLocation = AZStd::string::format("PassAttachment [%s]", m_name.GetCStr()); m_descriptor.m_image.m_format = RHI::ValidateFormat(format, formatLocation.c_str(), formatFallbacks); } } RHI::AttachmentId PassAttachment::GetAttachmentId() const { AZ_Warning("PassSystem", !m_path.IsEmpty(), "PassAttachment::GetAttachmentId(): Trying to get AttachmentId without a valid path. Make sure you call ComputePathName."); return m_path; } RHI::AttachmentType PassAttachment::GetAttachmentType() const { return m_descriptor.m_type; } void PassAttachment::ComputePathName(const Name& passPath) { m_path = RHI::AttachmentId(ConcatPassString(passPath, m_name)); } const RHI::TransientImageDescriptor PassAttachment::GetTransientImageDescriptor() const { AZ_Assert(m_lifetime == RHI::AttachmentLifetimeType::Transient, "Error, building a transient image descriptor from non-transient pass attachment with path: %s", m_path.GetCStr()); AZ_Assert(m_descriptor.m_type == RHI::AttachmentType::Image, "Error, building a transient image descriptor for an attachment that is not an image: %s", m_path.GetCStr()); return RHI::TransientImageDescriptor(GetAttachmentId(), m_descriptor.m_image); } const RHI::TransientBufferDescriptor PassAttachment::GetTransientBufferDescriptor() const { AZ_Assert(m_lifetime == RHI::AttachmentLifetimeType::Transient, "Error, building a transient image descriptor from non-transient pass attachment with path: %s", m_path.GetCStr()); AZ_Assert(m_descriptor.m_type == RHI::AttachmentType::Buffer, "Error, building a transient buffer descriptor for an attachment that is not a buffer: %s", m_path.GetCStr()); return RHI::TransientBufferDescriptor(GetAttachmentId(), m_descriptor.m_buffer); } void PassAttachment::Update(bool updateImportedAttachments) { if (m_descriptor.m_type == RHI::AttachmentType::Image && (m_lifetime == RHI::AttachmentLifetimeType::Transient || updateImportedAttachments == true)) { if (m_settingFlags.m_getFormatFromPipeline && m_renderPipelineSource) { m_descriptor.m_image.m_format = m_renderPipelineSource->GetRenderSettings().m_format; } else if (m_formatSource && m_formatSource->m_attachment) { m_descriptor.m_image.m_format = m_formatSource->m_attachment->m_descriptor.m_image.m_format; } if (m_settingFlags.m_getMultisampleStateFromPipeline && m_renderPipelineSource) { m_descriptor.m_image.m_multisampleState = m_renderPipelineSource->GetRenderSettings().m_multisampleState; } else if (m_multisampleSource && m_multisampleSource->m_attachment) { m_descriptor.m_image.m_multisampleState = m_multisampleSource->m_attachment->m_descriptor.m_image.m_multisampleState; } if (m_settingFlags.m_getSizeFromPipeline && m_renderPipelineSource) { RHI::Size sourceSize = m_renderPipelineSource->GetRenderSettings().m_size; m_descriptor.m_image.m_size = m_sizeMultipliers.ApplyModifiers(sourceSize); } else if(m_sizeSource && m_sizeSource->m_attachment) { RHI::Size sourceSize = m_sizeSource->m_attachment->m_descriptor.m_image.m_size; m_descriptor.m_image.m_size = m_sizeMultipliers.ApplyModifiers(sourceSize); } if (m_arraySizeSource && m_arraySizeSource->m_attachment) { uint16_t arraySize = m_arraySizeSource->m_attachment->m_descriptor.m_image.m_arraySize; m_descriptor.m_image.m_arraySize = arraySize; } if (m_generateFullMipChain) { uint32_t width = m_descriptor.m_image.m_size.m_width; uint32_t height = m_descriptor.m_image.m_size.m_height; double maxDimension = static_cast(AZStd::max(width, height)); double mipMapLevels = floor(log2(maxDimension)) + 1; m_descriptor.m_image.m_mipLevels = static_cast(mipMapLevels); } } } void PassAttachment::OnAttached(const PassAttachmentBinding& binding) { // Auto-infer image and buffer bind flags... if (GetAttachmentType() == RHI::AttachmentType::Image) { m_descriptor.m_image.m_bindFlags |= RHI::GetImageBindFlags(binding.m_scopeAttachmentUsage, binding.GetAttachmentAccess()); } else if (GetAttachmentType() == RHI::AttachmentType::Buffer) { bool isInputAssembly = RHI::CheckBitsAny(m_descriptor.m_buffer.m_bindFlags, RHI::BufferBindFlags::InputAssembly | RHI::BufferBindFlags::DynamicInputAssembly); bool isConstant = RHI::CheckBitsAny(m_descriptor.m_buffer.m_bindFlags, RHI::BufferBindFlags::Constant); // Since InputAssembly and Constant cannot be inferred they are set manually. If those flags are set we don't want to add inferred flags on top as it may have a performance penalty if (!isInputAssembly && !isConstant) { m_descriptor.m_buffer.m_bindFlags |= RHI::GetBufferBindFlags(binding.m_scopeAttachmentUsage, binding.GetAttachmentAccess()); } } } // --- PassBinding --- PassAttachmentBinding::PassAttachmentBinding(const PassSlot& slot) { m_name = slot.m_name; m_shaderInputName = slot.m_shaderInputName; m_shaderInputArrayIndex = slot.m_shaderInputArrayIndex; m_slotType = slot.m_slotType; m_scopeAttachmentUsage = slot.m_scopeAttachmentUsage; m_unifiedScopeDesc.m_loadStoreAction = slot.m_loadStoreAction; if (slot.m_imageViewDesc != nullptr) { m_unifiedScopeDesc.SetAsImage(*slot.m_imageViewDesc); } else if (slot.m_bufferViewDesc != nullptr) { m_unifiedScopeDesc.SetAsBuffer(*slot.m_bufferViewDesc); } ValidateDeviceFormats(slot.m_formatFallbacks); } void PassAttachmentBinding::ValidateDeviceFormats(const AZStd::vector& formatFallbacks) { RHI::FormatCapabilities capabilities = RHI::GetCapabilities(m_scopeAttachmentUsage, GetAttachmentAccess(), m_unifiedScopeDesc.GetType()); if (m_unifiedScopeDesc.GetType() == RHI::AttachmentType::Buffer) { RHI::BufferViewDescriptor& bufferViewDesc = m_unifiedScopeDesc.GetBufferViewDescriptor(); RHI::Format format = bufferViewDesc.m_elementFormat; AZStd::string formatLocation = AZStd::string::format("BufferViewDescriptor on PassAttachmentBinding [%s]", m_name.GetCStr()); bufferViewDesc.m_elementFormat = RHI::ValidateFormat(format, formatLocation.c_str(), formatFallbacks, capabilities); } else if (m_unifiedScopeDesc.GetType() == RHI::AttachmentType::Image) { RHI::ImageViewDescriptor& imageViewDesc = m_unifiedScopeDesc.GetImageViewDescriptor(); RHI::Format format = imageViewDesc.m_overrideFormat; AZStd::string formatLocation = AZStd::string::format("ImageViewDescriptor on PassAttachmentBinding [%s]", m_name.GetCStr()); imageViewDesc.m_overrideFormat = RHI::ValidateFormat(format, formatLocation.c_str(), formatFallbacks, capabilities); } } RHI::ScopeAttachmentAccess PassAttachmentBinding::GetAttachmentAccess() const { RHI::ScopeAttachmentAccess access = RPI::GetAttachmentAccess(m_slotType); access = AdjustAccessBasedOnUsage(access, m_scopeAttachmentUsage); return access; } void PassAttachmentBinding::SetAttachment(const Ptr& attachment) { m_attachment = attachment; m_unifiedScopeDesc.m_attachmentId = attachment->GetAttachmentId(); // setup scope descriptors for transient attachments if they weren't set in slot if (m_unifiedScopeDesc.GetType() == RHI::AttachmentType::Uninitialized) { if (attachment->m_lifetime == RHI::AttachmentLifetimeType::Transient) { if (attachment->GetAttachmentType() == RHI::AttachmentType::Buffer) { //AZ_Assert(false, "Transient buffer's buffer view need to be set in slot"); m_unifiedScopeDesc.SetAsBuffer(RHI::BufferViewDescriptor()); } else if (attachment->GetAttachmentType() == RHI::AttachmentType::Image) { m_unifiedScopeDesc.SetAsImage(RHI::ImageViewDescriptor()); } } else if (attachment->m_lifetime == RHI::AttachmentLifetimeType::Imported) { if (attachment->m_importedResource) { if (attachment->GetAttachmentType() == RHI::AttachmentType::Buffer) { Buffer* buffer = static_cast(attachment->m_importedResource.get()); m_unifiedScopeDesc.SetAsBuffer(buffer->GetBufferViewDescriptor()); } else if (attachment->GetAttachmentType() == RHI::AttachmentType::Image) { AttachmentImage* image = static_cast(attachment->m_importedResource.get()); m_unifiedScopeDesc.SetAsImage(image->GetImageView()->GetDescriptor()); } } else { AZ_Assert(false, "Imported pass attachment should have the m_importedResource set"); } } } RHI::FormatCapabilities capabilities = RHI::GetCapabilities(m_scopeAttachmentUsage, GetAttachmentAccess(), m_unifiedScopeDesc.GetType()); m_attachment->ValidateDeviceFormats(AZStd::vector(), capabilities); m_attachment->OnAttached(*this); AZ_Error("PassSystem", m_unifiedScopeDesc.GetType() == attachment->GetAttachmentType(), "Attachment must have same type as unified scope descriptor"); } } // namespace RPI } // namespace AZ