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.
287 lines
10 KiB
C++
287 lines
10 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include "RenderDll_precompiled.h"
|
|
#include "RTTSwappableRenderTarget.h"
|
|
#include <AzFramework/StringFunc/StringFunc.h>
|
|
#include <AzCore/std/string/conversions.h>
|
|
|
|
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
|
|
|
|
namespace AzRTT
|
|
{
|
|
const int INVALID_RENDER_TARGET = -1;
|
|
|
|
SwappableRenderTarget::SwappableRenderTarget()
|
|
: m_originalTexture(nullptr)
|
|
, m_rtt(nullptr)
|
|
, m_scale(1)
|
|
, m_rttId(INVALID_RENDER_TARGET)
|
|
, m_originalTextureId(INVALID_RENDER_TARGET)
|
|
, m_renderContextId(AzRTT::RenderContextId::CreateNull())
|
|
, m_swapped(false)
|
|
, m_width(0)
|
|
, m_height(0)
|
|
{
|
|
}
|
|
|
|
SwappableRenderTarget::SwappableRenderTarget(CTexture** texture)
|
|
: m_originalTexture(texture)
|
|
, m_rtt(nullptr)
|
|
, m_scale(1)
|
|
, m_rttId(INVALID_RENDER_TARGET)
|
|
, m_originalTextureId(INVALID_RENDER_TARGET)
|
|
, m_renderContextId(AzRTT::RenderContextId::CreateNull())
|
|
, m_swapped(false)
|
|
, m_width(0)
|
|
, m_height(0)
|
|
{
|
|
}
|
|
|
|
SwappableRenderTarget::SwappableRenderTarget(const SwappableRenderTarget& a)
|
|
: m_originalTexture(a.m_originalTexture)
|
|
, m_rtt(a.m_rtt)
|
|
, m_scale(a.m_scale)
|
|
, m_rttId(a.m_rttId)
|
|
, m_originalTextureId(a.m_originalTextureId)
|
|
, m_renderContextId(a.m_renderContextId)
|
|
, m_swapped(a.m_swapped)
|
|
, m_width(a.m_width)
|
|
, m_height(a.m_height)
|
|
{
|
|
}
|
|
|
|
SwappableRenderTarget::~SwappableRenderTarget()
|
|
{
|
|
Revert();
|
|
Release();
|
|
}
|
|
|
|
void SwappableRenderTarget::Release()
|
|
{
|
|
if (m_rttId != INVALID_RENDER_TARGET)
|
|
{
|
|
ITexture* renderTarget = GetTextureById(m_rttId);
|
|
if (renderTarget)
|
|
{
|
|
if (renderTarget->GetFlags() & FT_DONT_RELEASE)
|
|
{
|
|
SAFE_RELEASE_FORCE(renderTarget);
|
|
}
|
|
else
|
|
{
|
|
SAFE_RELEASE(renderTarget);
|
|
}
|
|
}
|
|
}
|
|
m_rtt = nullptr;
|
|
}
|
|
|
|
bool SwappableRenderTarget::IsSwapped() const
|
|
{
|
|
return m_swapped;
|
|
}
|
|
|
|
bool SwappableRenderTarget::IsValid() const
|
|
{
|
|
ITexture* rtt = GetTextureById(m_rttId);
|
|
ITexture* originalTexture = GetTextureById(m_originalTextureId);
|
|
|
|
// in order to be valid the original texture and swap texture must exist
|
|
// in a swapped, or not swapped state.
|
|
return m_originalTexture && *m_originalTexture && m_rtt && rtt && originalTexture &&
|
|
originalTexture->GetDevTexture() && rtt->GetDevTexture() &&
|
|
((*m_originalTexture == originalTexture && m_rtt == rtt) || (*m_originalTexture == rtt && m_rtt == originalTexture));
|
|
}
|
|
|
|
void SwappableRenderTarget::Revert()
|
|
{
|
|
if (IsSwapped())
|
|
{
|
|
Swap();
|
|
}
|
|
}
|
|
|
|
CTexture* SwappableRenderTarget::GetTextureById(int id) const
|
|
{
|
|
// can't use CTexture::GetById because it will return the default
|
|
// texture if this id is invalid, so manually search for the resource
|
|
if (id == INVALID_RENDER_TARGET)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
bool addReference = false;
|
|
return static_cast<CTexture*>(CBaseResource::GetResource(CTexture::mfGetClassName(), id, addReference));
|
|
}
|
|
|
|
void SwappableRenderTarget::Swap()
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
// Try to recover from a state where the renderer changed our texture
|
|
if (m_originalTexture && *m_originalTexture &&
|
|
m_originalTextureId != INVALID_RENDER_TARGET &&
|
|
m_rtt && *m_originalTexture != m_rtt)
|
|
{
|
|
if (m_rtt == *m_originalTexture)
|
|
{
|
|
// We cannot recover from this state because the renderer
|
|
// changed a static texture, usually be referencing or
|
|
// re-creating a texture by name (string) while a texture was
|
|
// swapped. Usually this means the renderer will re-create
|
|
// the texture every pass.
|
|
AZ_Warning("SwappableRenderTarget", false, "SwappableRenderTarget %s has been re-created", m_rtt->GetName());
|
|
}
|
|
else
|
|
{
|
|
// The renderer may have changed a texture size ($StereoR)
|
|
// or format. This can happen if the main camera is
|
|
// not using the same HDR settings as the RT or when a
|
|
// viewport is resized in the editor.
|
|
#ifdef _DEBUG
|
|
AZ_Warning("SwappableRenderTarget", false, "SwappableRenderTarget no longer valid, re-creating.");
|
|
#endif //ifdef _DEBUG
|
|
|
|
const ETEX_Format desiredFormat = (*m_originalTexture)->GetDstFormat();
|
|
|
|
// revert the swapped state
|
|
bool wasSwapped = m_swapped;
|
|
if (m_swapped)
|
|
{
|
|
AZStd::swap(*m_originalTexture, m_rtt);
|
|
}
|
|
|
|
// restore the original texture based on the ID
|
|
*m_originalTexture = GetTextureById(m_originalTextureId);
|
|
|
|
// release the render texture copy
|
|
Release();
|
|
|
|
// re-create the render target copy
|
|
CreateRenderTargetCopy(m_width, m_height, m_scale, m_renderContextId, desiredFormat);
|
|
|
|
// restore the swapped state
|
|
if (wasSwapped == m_swapped)
|
|
{
|
|
AZStd::swap(*m_originalTexture, m_rtt);
|
|
m_swapped = !m_swapped;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AZStd::swap(*m_originalTexture, m_rtt);
|
|
m_swapped = !m_swapped;
|
|
}
|
|
}
|
|
|
|
void SwappableRenderTarget::CreateRenderTargetCopy(RenderContextId id)
|
|
{
|
|
if (m_originalTexture && *m_originalTexture)
|
|
{
|
|
const uint32_t scale = 1;
|
|
CreateRenderTargetCopy((*m_originalTexture)->GetWidth(), (*m_originalTexture)->GetHeight(), scale, id);
|
|
}
|
|
}
|
|
|
|
void SwappableRenderTarget::CreateRenderTargetCopy(uint32_t width, uint32_t height, uint32_t scale, RenderContextId id, ETEX_Format formatOverride)
|
|
{
|
|
m_scale = scale;
|
|
m_width = width;
|
|
m_height = height;
|
|
m_renderContextId = id;
|
|
|
|
if (m_originalTexture && *m_originalTexture)
|
|
{
|
|
const CTexture* originalTexture = *m_originalTexture;
|
|
m_originalTextureId = originalTexture->GetID();
|
|
m_swapped = false;
|
|
|
|
// allow overriding the format which can change based on the render pass settings
|
|
const ETEX_Format format = formatOverride == eTF_Unknown ? originalTexture->GetDstFormat() : formatOverride;
|
|
const ETEX_Type texType = originalTexture->GetTexType();
|
|
const uint32_t flags = originalTexture->GetFlags() | FT_DONT_STREAM | FT_USAGE_RENDERTARGET;
|
|
const ColorF clearColor = originalTexture->GetClearColor();
|
|
const int customId = originalTexture->GetCustomID();
|
|
const int mips = originalTexture->GetNumMips();
|
|
|
|
// slice support check necessary to avoid __debugbreak() in StreamGetNumSlices
|
|
bool supportsSlices = texType == eTT_2D || texType == eTT_2DArray || texType == eTT_Cube;
|
|
const int numSlices = supportsSlices ? originalTexture->StreamGetNumSlices() : 1;
|
|
|
|
const uint32_t scaledWidth = width / m_scale;
|
|
const uint32_t scaledHeight = height / m_scale;
|
|
|
|
AZ_Assert(scaledWidth != 0 && scaledHeight != 0, "Invalid scaled width/height for render target copy.");
|
|
|
|
const AZStd::string name = GetRenderTargetCopyName(originalTexture->GetName());
|
|
|
|
if (originalTexture->GetDevTexture())
|
|
{
|
|
if (numSlices > 1)
|
|
{
|
|
m_rtt = CTexture::CreateTextureArray(name.c_str(), eTT_2D, scaledWidth, scaledHeight, numSlices, mips, flags, format, customId);
|
|
}
|
|
else
|
|
{
|
|
m_rtt = CTexture::CreateRenderTarget(name.c_str(), scaledWidth, scaledHeight, clearColor, eTT_2D, flags, format, customId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_rtt = CTexture::CreateTextureObject(name.c_str(), scaledWidth, scaledHeight, numSlices, eTT_2D, flags, format, customId);
|
|
}
|
|
|
|
if (m_rtt)
|
|
{
|
|
m_rtt->SetClearColor(clearColor);
|
|
m_rttId = m_rtt->GetID();
|
|
}
|
|
else
|
|
{
|
|
m_rttId = INVALID_RENDER_TARGET;
|
|
}
|
|
}
|
|
}
|
|
|
|
AZStd::string SwappableRenderTarget::GetRenderTargetCopyName(const char* textureName)
|
|
{
|
|
AZ_Assert(textureName, "Invalid texture name for render target copy");
|
|
|
|
// prepend $RTT so we can match these easily when swapping shader samplers and to identify when debugging
|
|
const AZStd::string prefix("$RTT");
|
|
|
|
AZStd::string name(textureName);
|
|
AzFramework::StringFunc::Replace(name, "$", prefix.c_str(), true, false);
|
|
|
|
// append the RenderContextId after a slash because some textures end in numbers so this
|
|
// makes the name more readable when debugging
|
|
name += AZStd::string("_");
|
|
name += m_renderContextId.ToString<AZStd::string>();
|
|
|
|
return name;
|
|
}
|
|
|
|
void SwappableRenderTarget::Resize(uint32_t width, uint32_t height)
|
|
{
|
|
if (m_rttId != INVALID_RENDER_TARGET)
|
|
{
|
|
m_width = width;
|
|
m_height = height;
|
|
gEnv->pRenderer->ResizeRenderTarget(m_rttId, width / m_scale, height / m_scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
|