/* * 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 namespace AZ { namespace RHI { RenderAttachmentLayoutBuilder::RenderAttachmentLayoutBuilder() { Reset(); } ResultCode RenderAttachmentLayoutBuilder::End(RenderAttachmentLayout& builtRenderAttachmentLayout) { auto& renderAttachmentFormats = builtRenderAttachmentLayout.m_attachmentFormats; Format depthStencilFormat = Format::Unknown; AZStd::unordered_map renderAttachmentsMap; // This lambda will handle if the render attachment needs to resolve and will add a resolve attachment if needed. auto handleResolveAttachment = [&](const SubpassAttachmentLayoutBuilder::RenderAttachmentEntry& attachment, uint32_t& resolveAttachmentIndex) { resolveAttachmentIndex = InvalidRenderAttachmentIndex; if (!attachment.m_resolveName.IsEmpty()) { AttachmentLoadStoreAction loadStoreAction; loadStoreAction.m_loadAction = AttachmentLoadAction::DontCare; loadStoreAction.m_storeAction = AttachmentStoreAction::Store; auto findResolveIter = renderAttachmentsMap.find(attachment.m_resolveName); if (findResolveIter == renderAttachmentsMap.end()) { if (attachment.m_format == Format::Unknown) { AZ_Assert(false, "Invalid format for resolve attachment %s", attachment.m_name.GetCStr()); return ResultCode::InvalidArgument; } resolveAttachmentIndex = builtRenderAttachmentLayout.m_attachmentCount++; renderAttachmentsMap[attachment.m_resolveName] = resolveAttachmentIndex; renderAttachmentFormats[resolveAttachmentIndex] = attachment.m_format; } else { resolveAttachmentIndex = findResolveIter->second; } if (attachment.m_format != Format::Unknown && renderAttachmentFormats[resolveAttachmentIndex] != attachment.m_format) { AZ_Assert(false, "Incompatible format for resolve attachment %s. Expected %d. Actual %d", attachment.m_name.GetCStr(), renderAttachmentFormats[resolveAttachmentIndex], attachment.m_format); return ResultCode::InvalidArgument; } } return ResultCode::Success; }; for (const auto& builder : m_subpassLayoutBuilders) { SubpassRenderAttachmentLayout& subpassLayout = builtRenderAttachmentLayout.m_subpassLayouts[builtRenderAttachmentLayout.m_subpassCount++]; subpassLayout.m_rendertargetCount = static_cast(builder.m_renderTargetAttachments.size()); subpassLayout.m_subpassInputCount = static_cast(builder.m_subpassInputAttachments.size()); // First add the resolve attachments, so they can be found when adding the MS attachments. for (uint32_t i = 0; i < builder.m_renderTargetAttachments.size(); ++i) { const SubpassAttachmentLayoutBuilder::RenderAttachmentEntry& renderTargetAttachment = builder.m_renderTargetAttachments[i]; uint32_t resolveAttachmentIndex; handleResolveAttachment(renderTargetAttachment, resolveAttachmentIndex); } uint32_t resolveAttachmentIndex; ResultCode result = handleResolveAttachment(builder.m_depthStencilAttachment, resolveAttachmentIndex); for (uint32_t i = 0; i < builder.m_renderTargetAttachments.size(); ++i) { const SubpassAttachmentLayoutBuilder::RenderAttachmentEntry& renderTargetAttachment = builder.m_renderTargetAttachments[i]; uint32_t attachmentIndex = 0; // First look if the render target has already been added to the list of attachments. auto findIter = renderAttachmentsMap.find(renderTargetAttachment.m_name); if (findIter == renderAttachmentsMap.end()) { if (renderTargetAttachment.m_format == Format::Unknown) { AZ_Assert(false, "Invalid format for rendertarget %s", renderTargetAttachment.m_name.GetCStr()); return ResultCode::InvalidArgument; } attachmentIndex = builtRenderAttachmentLayout.m_attachmentCount++; renderAttachmentsMap[renderTargetAttachment.m_name] = attachmentIndex; renderAttachmentFormats[attachmentIndex] = renderTargetAttachment.m_format; } else { attachmentIndex = findIter->second; } // Add the use of the attachment to the subpass. result = handleResolveAttachment(renderTargetAttachment, resolveAttachmentIndex); if (result != ResultCode::Success) { return result; } if (renderTargetAttachment.m_format != Format::Unknown && renderAttachmentFormats[attachmentIndex] != renderTargetAttachment.m_format) { AZ_Assert(false, "Incompatible format for attachment %s. Expected %d. Actual %d", renderTargetAttachment.m_name.GetCStr(), renderAttachmentFormats[attachmentIndex], renderTargetAttachment.m_format); return ResultCode::InvalidArgument; } subpassLayout.m_rendertargetDescriptors[i] = RenderAttachmentDescriptor{ attachmentIndex, resolveAttachmentIndex, renderTargetAttachment.m_loadStoreAction }; } if (!builder.m_depthStencilAttachment.m_name.IsEmpty()) { // Check if the depth/stencil has already been added and if it has, check if the format is the same. if (depthStencilFormat != Format::Unknown && builder.m_depthStencilAttachment.m_format != Format::Unknown && depthStencilFormat != builder.m_depthStencilAttachment.m_format) { AZ_Assert(false, "Invalid depth stencil format. Expected %s. Current %s", ToString(depthStencilFormat), ToString(builder.m_depthStencilAttachment.m_format)); return ResultCode::InvalidArgument; } // Search for the depth/stencil attachment in the list of added attachments. uint32_t attachmentIndex = 0; auto findIter = renderAttachmentsMap.find(builder.m_depthStencilAttachment.m_name); if (findIter == renderAttachmentsMap.end()) { if (builder.m_depthStencilAttachment.m_format == Format::Unknown) { AZ_Assert(false, "Invalid depth stencil format %s", ToString(builder.m_depthStencilAttachment.m_format)); return ResultCode::InvalidArgument; } depthStencilFormat = builder.m_depthStencilAttachment.m_format; attachmentIndex = builtRenderAttachmentLayout.m_attachmentCount++; renderAttachmentsMap[builder.m_depthStencilAttachment.m_name] = attachmentIndex; renderAttachmentFormats[attachmentIndex] = depthStencilFormat; } else { attachmentIndex = findIter->second; } result = handleResolveAttachment(builder.m_depthStencilAttachment, resolveAttachmentIndex); if (result != ResultCode::Success) { return result; } subpassLayout.m_depthStencilDescriptor = RenderAttachmentDescriptor{ attachmentIndex, resolveAttachmentIndex, builder.m_depthStencilAttachment.m_loadStoreAction }; } // Add the subpass inputs. for (uint32_t i = 0; i < builder.m_subpassInputAttachments.size(); ++i) { auto& subpassInputAttachmentName = builder.m_subpassInputAttachments[i]; uint32_t attachmentIndex = 0; auto findIter = renderAttachmentsMap.find(subpassInputAttachmentName); if (findIter == renderAttachmentsMap.end()) { AZ_Assert(false, "Could not find subpassInput %d", subpassInputAttachmentName.GetCStr()); return ResultCode::InvalidArgument; } attachmentIndex += findIter->second; subpassLayout.m_subpassInputIndices[i] = attachmentIndex; } } return ResultCode::Success; } void RenderAttachmentLayoutBuilder::Reset() { m_subpassLayoutBuilders.clear(); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::AddSubpass() { m_subpassLayoutBuilders.push_back(SubpassAttachmentLayoutBuilder(static_cast(m_subpassLayoutBuilders.size()))); return &m_subpassLayoutBuilders.back(); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder(uint32_t subpassIndex) : m_subpassIndex(subpassIndex) {} RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::RenderTargetAttachment( Format format, const AZ::Name& name /*= {}*/, const AttachmentLoadStoreAction& loadStoreAction /*= AttachmentLoadStoreAction()*/, bool resolve /*= false*/) { AZ::Name attachmentName = name; if (attachmentName.IsEmpty()) { // Assign a temp name if it's empty. attachmentName = AZStd::string::format("Color%zu_Subpass%d", m_renderTargetAttachments.size(), m_subpassIndex); } m_renderTargetAttachments.push_back({ attachmentName, format, loadStoreAction }); if (resolve) { return ResolveAttachment(attachmentName); } return this; } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::RenderTargetAttachment( Format format, bool resolve) { return RenderTargetAttachment(format, {}, AttachmentLoadStoreAction(), resolve); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::RenderTargetAttachment( const AZ::Name& name, bool resolve) { return RenderTargetAttachment(Format::Unknown, name, AttachmentLoadStoreAction(), resolve); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::RenderTargetAttachment( const AZ::Name& name, const AttachmentLoadStoreAction& loadStoreAction /*= AttachmentLoadStoreAction()*/, bool resolve /*= false*/) { return RenderTargetAttachment(Format::Unknown, name, loadStoreAction, resolve); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::ResolveAttachment( const AZ::Name& sourceName, const AZ::Name& resolveName /*= {}*/) { AZ::Name attachmentName = resolveName; if (attachmentName.IsEmpty()) { // Assign a temp name if it's empty. attachmentName = AZStd::string::format("Resolve%zu_Subpass%d", m_renderTargetAttachments.size(), m_subpassIndex); } auto findIter = AZStd::find_if(m_renderTargetAttachments.begin(), m_renderTargetAttachments.end(), [sourceName](const RenderAttachmentEntry& entry) { return entry.m_name == sourceName; }); if (findIter == m_renderTargetAttachments.end()) { AZ_Assert(false, "Failed to find render target %d to resolve", sourceName.GetCStr()); return this; } (*findIter).m_resolveName = attachmentName; return this; } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::DepthStencilAttachment( Format format, const AZ::Name& name /*= {}*/, const AttachmentLoadStoreAction& loadStoreAction /*= AttachmentLoadStoreAction()*/) { AZ_Assert(m_depthStencilAttachment.m_format == Format::Unknown || format == m_depthStencilAttachment.m_format, "DepthStencil format has already been set"); // Assign a temp name if it's empty. m_depthStencilAttachment = RenderAttachmentEntry{ name.IsEmpty() ? AZ::Name("DepthStencil") : name, format, loadStoreAction, {} }; return this; } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::DepthStencilAttachment( const AZ::Name name /*= {}*/, const AttachmentLoadStoreAction& loadStoreAction /*= AttachmentLoadStoreAction()*/) { return DepthStencilAttachment(m_depthStencilAttachment.m_format, name, loadStoreAction); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::DepthStencilAttachment( const AttachmentLoadStoreAction& loadStoreAction) { return DepthStencilAttachment(m_depthStencilAttachment.m_format, {}, loadStoreAction); } RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder* RenderAttachmentLayoutBuilder::SubpassAttachmentLayoutBuilder::SubpassInputAttachment( const AZ::Name& name) { m_subpassInputAttachments.push_back(name); return this; } } // namespace RHI } // namespace AZ