You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp

215 lines
9.2 KiB
C++

/*
* 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 <AzCore/Math/MatrixUtils.h>
#include <Atom/RHI/FrameScheduler.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI.Reflect/Format.h>
#include <Atom/RPI.Public/Image/AttachmentImage.h>
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/View.h>
namespace AZ
{
namespace RPI
{
Ptr<EnvironmentCubeMapPass> EnvironmentCubeMapPass::Create(const PassDescriptor& passDescriptor)
{
Ptr<EnvironmentCubeMapPass> pass = aznew EnvironmentCubeMapPass(passDescriptor);
return pass;
}
EnvironmentCubeMapPass::EnvironmentCubeMapPass(const PassDescriptor& passDescriptor)
: ParentPass(passDescriptor)
{
// load pass data
const EnvironmentCubeMapPassData* passData = PassUtils::GetPassData<EnvironmentCubeMapPassData>(passDescriptor);
if (passData == nullptr)
{
AZ_Error("PassSystem", false, "[EnvironmentCubeMapPass '%s']: Trying to construct without valid EnvironmentCubeMapPassData!", GetPathName().GetCStr());
return;
}
m_position = passData->m_position;
// create the cubemap pipeline as a child of this pass
PassRequest childRequest;
childRequest.m_templateName = "EnvironmentCubeMapPipeline";
childRequest.m_passName = "Child";
PassConnection childInputConnection;
childInputConnection.m_localSlot = "Output";
childInputConnection.m_attachmentRef.m_pass = "Parent";
childInputConnection.m_attachmentRef.m_attachment = "Output";
childRequest.m_connections.emplace_back(childInputConnection);
PassSystemInterface* passSystem = PassSystemInterface::Get();
m_childPass = passSystem->CreatePassFromRequest(&childRequest);
AZ_Assert(m_childPass, "EnvironmentCubeMap child pass is invalid");
// setup viewport
m_viewportState.m_minX = 0;
m_viewportState.m_minY = 0;
m_viewportState.m_maxX = static_cast<float>(CubeMapFaceSize);
m_viewportState.m_maxY = static_cast<float>(CubeMapFaceSize);
// setup scissor
m_scissorState.m_minX = 0;
m_scissorState.m_minY = 0;
m_scissorState.m_maxX = static_cast<int16_t>(CubeMapFaceSize);
m_scissorState.m_maxY = static_cast<int16_t>(CubeMapFaceSize);
// create view
AZ::Name viewName(AZStd::string::format("%s_%s", childRequest.m_templateName.GetCStr(), childRequest.m_passName.GetCStr()));
m_view = RPI::View::CreateView(viewName, RPI::View::UsageReflectiveCubeMap);
AZ::Matrix3x4 viewTransform;
const Vector3* basis = &CameraBasis[0][0];
viewTransform.SetBasisAndTranslation(basis[0], basis[1], basis[2], m_position);
m_view->SetCameraTransform(viewTransform);
AZ::Matrix4x4 viewToClipMatrix;
MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, 1.0f, 0.1f, 100.0f, true);
m_view->SetViewToClipMatrix(viewToClipMatrix);
}
EnvironmentCubeMapPass::~EnvironmentCubeMapPass()
{
for (uint32_t i = 0; i < NumCubeMapFaces; ++i)
{
delete [] m_textureData[i];
}
}
void EnvironmentCubeMapPass::SetDefaultView()
{
if (m_pipeline)
{
m_pipeline->SetDefaultView(m_view);
}
}
void EnvironmentCubeMapPass::CreateChildPassesInternal()
{
AddChild(m_childPass);
}
void EnvironmentCubeMapPass::BuildInternal()
{
// create output image descriptor
m_outputImageDesc = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::Color | RHI::ImageBindFlags::CopyRead, CubeMapFaceSize, CubeMapFaceSize, RHI::Format::R16G16B16A16_FLOAT);
// create output PassAttachment
m_passAttachment = aznew PassAttachment();
m_passAttachment->m_name = "Output";
AZ::Name attachmentPath(AZStd::string::format("%s.%s", GetPathName().GetCStr(), m_passAttachment->m_name.GetCStr()));
m_passAttachment->m_path = attachmentPath;
m_passAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;
m_passAttachment->m_descriptor = m_outputImageDesc;
// create pass attachment binding
PassAttachmentBinding outputAttachment;
outputAttachment.m_name = "Output";
outputAttachment.m_slotType = PassSlotType::InputOutput;
outputAttachment.m_attachment = m_passAttachment;
outputAttachment.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::RenderTarget;
m_attachmentBindings.push_back(outputAttachment);
ParentPass::BuildInternal();
}
void EnvironmentCubeMapPass::FrameBeginInternal(FramePrepareParams params)
{
params.m_scissorState = m_scissorState;
params.m_viewportState = m_viewportState;
RHI::FrameGraphAttachmentInterface attachmentDatabase = params.m_frameGraphBuilder->GetAttachmentDatabase();
attachmentDatabase.CreateTransientImage(RHI::TransientImageDescriptor(m_passAttachment->GetAttachmentId(), m_outputImageDesc));
m_readBackLock.lock();
if (!m_attachmentReadback || !m_readBackRequested)
{
// create the AttachmentReadback if this is the first time in FramePrepare, or we finished with the last readback
// in which case we will free the previous one and allocate a new one
m_attachmentReadback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "EnvironmentCubeMapReadBack" });
m_attachmentReadback->SetCallback(AZStd::bind(&EnvironmentCubeMapPass::AttachmentReadbackCallback, this, AZStd::placeholders::_1));
}
m_readBackLock.unlock();
ParentPass::FrameBeginInternal(params);
// Note: this needs to be after the call to ParentPass::FrameBeginInternal in order to setup the scopes correctly for readback
m_attachmentReadback->FrameBegin(params);
}
void EnvironmentCubeMapPass::FrameEndInternal()
{
m_readBackLock.lock();
if (m_renderFace < NumCubeMapFaces)
{
if (!m_readBackRequested)
{
// delay a number of frames before requesting the readback
if (m_readBackDelayFrames < NumReadBackDelayFrames)
{
m_readBackDelayFrames++;
}
else
{
m_readBackRequested = true;
AZStd::string readbackName = AZStd::string::format("%s_%s", m_passAttachment->GetAttachmentId().GetCStr(), GetName().GetCStr());
m_attachmentReadback->ReadPassAttachment(m_passAttachment.get(), AZ::Name(readbackName));
}
}
// set the appropriate render camera transform for the next frame
AZ::Matrix3x4 viewTransform;
const Vector3* basis = &CameraBasis[m_renderFace][0];
viewTransform.SetBasisAndTranslation(basis[0], basis[1], basis[2], m_position);
m_view->SetCameraTransform(viewTransform);
m_pipeline->SetDefaultView(m_view);
}
m_readBackLock.unlock();
ParentPass::FrameEndInternal();
}
void EnvironmentCubeMapPass::AttachmentReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
RHI::ImageSubresourceLayout imageLayout = RHI::GetImageSubresourceLayout(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format);
delete [] m_textureData[m_renderFace];
// copy face texture data
m_textureData[m_renderFace] = new uint8_t[imageLayout.m_bytesPerImage];
uint32_t bytesRead = (uint32_t)readbackResult.m_dataBuffer->size();
memcpy(m_textureData[m_renderFace], readbackResult.m_dataBuffer->data(), bytesRead);
m_textureFormat = readbackResult.m_imageDescriptor.m_format;
m_readBackLock.lock();
{
// move to the next face
m_renderFace++;
m_readBackRequested = false;
m_readBackDelayFrames = 0;
}
m_readBackLock.unlock();
}
} // namespace RPI
} // namespace AZ