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/Code/CryEngine/RenderDll/Common/Shaders/ShaderResources.cpp

861 lines
28 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "RenderDll_precompiled.h"
#include "ShaderResources.h"
#include "GraphicsPipeline/Common/GraphicsPipelineStateSet.h"
#include <AzCore/std/sort.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#ifndef NULL_RENDERER
#include "DriverD3D.h"
#endif // ! NULL_RENDERER
enum MaterialRegister
{
MaterialRegister_DiffuseColor = 0, // float4
MaterialRegister_SpecularColor = 1, // float4
MaterialRegister_EmissiveColor = 2, // float4
MaterialRegister_DeformWave = 3, // float2x4
MaterialRegister_DetailTiling = 5, // float4
MaterialRegister_TexelDensity = 6, // float4
MaterialRegister_UVMatrixDiffuse = 7, // float4x4
MaterialRegister_UVMatrixCustom = 11,// float4x4
MaterialRegister_UVMatrixEmissiveMultiplier = 15,// float4x4
MaterialRegister_UVMatrixEmittance = 19,// float4x4
MaterialRegister_UVMatrixDetail = 23,// float4x4
// Reflected constants are appended after the fixed ones.
MaterialRegister_MaxFixed = 27
};
namespace UVTransform
{
struct TextureSlot
{
EEfResTextures m_Slot;
MaterialRegister m_RegisterOffset;
};
TextureSlot g_SupportedSlots[] =
{
{ EFTT_DIFFUSE, MaterialRegister_UVMatrixDiffuse },
{ EFTT_CUSTOM, MaterialRegister_UVMatrixCustom },
{ EFTT_DECAL_OVERLAY, MaterialRegister_UVMatrixEmissiveMultiplier },
{ EFTT_EMITTANCE, MaterialRegister_UVMatrixEmittance },
{ EFTT_DETAIL_OVERLAY, MaterialRegister_UVMatrixDetail }
};
inline TextureSlot GetSupportedSlot(AZ::u32 index)
{
return g_SupportedSlots[index];
}
inline AZ::u32 GetSupportedSlotCount()
{
return AZ_ARRAY_SIZE(g_SupportedSlots);
}
}
void CShaderResources::Reset()
{
m_TexturesResourcesMap.clear();
m_Id = 0;
m_IdGroup = 0;
m_pDeformInfo = NULL;
m_pSky = NULL;
m_ConstantBuffer = NULL;
m_nMtlLayerNoDrawFlags = 0;
m_Constants.resize(MaterialRegister_MaxFixed);
}
void CShaderResources::ConvertToInputResource(SInputShaderResources* pDst)
{
pDst->m_ResFlags = m_ResFlags;
pDst->m_AlphaRef = m_AlphaRef;
pDst->m_VoxelCoverage = m_VoxelCoverage;
pDst->m_SortPrio = m_SortPrio;
if (m_pDeformInfo)
{
pDst->m_DeformInfo = *m_pDeformInfo;
}
else
{
pDst->m_DeformInfo.m_eType = eDT_Unknown;
}
pDst->m_TexturePath = m_TexturePath;
if (m_pDeformInfo)
{
pDst->m_DeformInfo = *m_pDeformInfo;
}
// Copy all used textures data
pDst->m_TexturesResourcesMap = m_TexturesResourcesMap;
ToInputLM(pDst->m_LMaterial);
}
size_t CShaderResources::GetResourceMemoryUsage(ICrySizer* pSizer)
{
size_t nCurrentElement(0);
size_t nCurrentElementSize(0);
size_t nTotalSize(0);
SIZER_COMPONENT_NAME(pSizer, "CShaderResources");
for (auto &iter : m_TexturesResourcesMap )
{
SEfResTexture* pTexture = &(iter.second);
if (pTexture->m_Sampler.m_pITex)
{
nCurrentElementSize = pTexture->m_Sampler.m_pITex->GetDataSize();
pSizer->AddObject(pTexture->m_Sampler.m_pITex, nCurrentElementSize);
nTotalSize += nCurrentElementSize;
IResourceCollector* pColl = pSizer->GetResourceCollector();
if (pColl)
{
pColl->AddResource(pTexture->m_Sampler.m_pITex->GetName(), nCurrentElementSize);
}
}
}
return nTotalSize;
}
void CShaderResources::Release()
{
#ifndef NULL_RENDERER
AZ_Assert(gRenDev && gRenDev->m_pRT, "renderer not initialized");
gRenDev->m_pRT->EnqueueRenderCommand([this]()
{
if (!CryInterlockedDecrement(&m_nRefCounter))
{
delete this;
}
});
#endif
}
void CShaderResources::Cleanup()
{
m_TexturesResourcesMap.clear();
SAFE_DELETE(m_pDeformInfo);
if (m_pSky)
{
for (size_t i = 0; i < sizeof(m_pSky->m_SkyBox) / sizeof(m_pSky->m_SkyBox[0]); ++i)
{
SAFE_RELEASE(m_pSky->m_SkyBox[i]);
}
SAFE_DELETE(m_pSky);
}
ReleaseConstants();
// Not thread safe main thread can potentially access this destroyed entry in mfCreateShaderResources()
// (if flushing of unloaded textures (UnloadLevel) is not complete before pre-loading of new materials)
// [Shader System TO DO] - test and fix
if (CShader::s_ShaderResources_known.Num() > m_Id && CShader::s_ShaderResources_known[m_Id] == this)
{
CShader::s_ShaderResources_known[m_Id] = NULL;
}
}
CShaderResources::~CShaderResources()
{
Cleanup();
if (gRenDev->m_RP.m_pShaderResources == this)
{
gRenDev->m_RP.m_pShaderResources = NULL;
}
}
CShaderResources::CShaderResources()
{
// Only do expensive DX12 resource set building for PC DX12
#if defined(CRY_USE_DX12)
m_pipelineStateCache = AZStd::make_shared<CGraphicsPipelineStateLocalCache>();
#endif
Reset();
}
CShaderResources::CShaderResources(SInputShaderResources* pSrc)
{
assert(pSrc);
PREFAST_ASSUME(pSrc);
// Only do expensive DX12 resource set building for PC DX12
#if defined(CRY_USE_DX12)
m_pipelineStateCache = AZStd::make_shared<CGraphicsPipelineStateLocalCache>();
#endif
Reset();
if (!pSrc)
return;
m_szMaterialName = pSrc->m_szMaterialName;
m_TexturePath = pSrc->m_TexturePath;
m_ResFlags = pSrc->m_ResFlags;
m_AlphaRef = pSrc->m_AlphaRef;
m_VoxelCoverage = pSrc->m_VoxelCoverage;
m_SortPrio = pSrc->m_SortPrio;
m_ShaderParams = pSrc->m_ShaderParams;
if (pSrc->m_DeformInfo.m_eType)
{
m_pDeformInfo = new SDeformInfo;
*m_pDeformInfo = pSrc->m_DeformInfo;
}
for (auto it = pSrc->m_TexturesResourcesMap.begin(), end = pSrc->m_TexturesResourcesMap.end(); it != end; ++it)
{
const SEfResTexture& texture = it->second;
// Omit any resources with no texture present
if (!texture.m_Name.empty() || texture.m_Sampler.m_pTex)
{
m_TexturesResourcesMap[it->first] = texture;
}
}
SetInputLM(pSrc->m_LMaterial);
}
CShaderResources& CShaderResources::operator=(const CShaderResources& src)
{
Cleanup();
SBaseShaderResources::operator = (src);
m_TexturesResourcesMap = src.m_TexturesResourcesMap;
m_Constants = src.m_Constants;
m_IdGroup = src.m_IdGroup;
return *this;
}
CShaderResources* CShaderResources::Clone() const
{
CShaderResources* pSR = new CShaderResources();
*pSR = *this;
pSR->m_nRefCounter = 1;
for (uint32 i = 0; i < CShader::s_ShaderResources_known.Num(); i++)
{
if (!CShader::s_ShaderResources_known[i])
{
pSR->m_Id = i;
CShader::s_ShaderResources_known[i] = pSR;
return pSR;
}
}
if (CShader::s_ShaderResources_known.Num() >= MAX_REND_SHADER_RES)
{
Warning("ERROR: CShaderMan::mfCreateShaderResources: MAX_REND_SHADER_RESOURCES hit");
pSR->Release();
return CShader::s_ShaderResources_known[1];
}
pSR->m_Id = CShader::s_ShaderResources_known.Num();
CShader::s_ShaderResources_known.AddElem(pSR);
return pSR;
}
void CShaderResources::SetInputLM(const CInputLightMaterial& lm)
{
ColorF* pDst = (ColorF*)&m_Constants[0];
memcpy(pDst, lm.m_Channels, min(int(EFTT_MAX), MaterialRegister_DiffuseColor / 2) * sizeof(lm.m_Channels[0]));
const float fMinStepSignedFmt = (1.0f / 127.0f) * 255.0f;
const float fSmoothness = max(fMinStepSignedFmt, lm.m_Smoothness) / 255.0f;
const float fAlpha = lm.m_Opacity;
pDst[MaterialRegister_DiffuseColor] = lm.m_Diffuse;
pDst[MaterialRegister_SpecularColor] = lm.m_Specular;
pDst[MaterialRegister_EmissiveColor] = lm.m_Emittance;
pDst[MaterialRegister_DiffuseColor][3] = fAlpha;
pDst[MaterialRegister_SpecularColor][3] = fSmoothness;
}
void CShaderResources::ToInputLM(CInputLightMaterial& lm)
{
if (!m_Constants.size())
{
return;
}
ColorF* pDst = (ColorF*)&m_Constants[0];
lm.m_Diffuse = pDst[MaterialRegister_DiffuseColor];
lm.m_Specular = pDst[MaterialRegister_SpecularColor];
lm.m_Emittance = pDst[MaterialRegister_EmissiveColor];
lm.m_Opacity = pDst[MaterialRegister_DiffuseColor][3];
lm.m_Smoothness = pDst[MaterialRegister_SpecularColor][3] * 255.0f;
}
ColorF CShaderResources::GetColorValue(EEfResTextures slot) const
{
if (!m_Constants.size())
{
return Col_Black;
}
int majoroffs;
switch (slot)
{
case EFTT_DIFFUSE:
majoroffs = MaterialRegister_DiffuseColor;
break;
case EFTT_SPECULAR:
majoroffs = MaterialRegister_SpecularColor;
break;
case EFTT_OPACITY:
return Col_White;
case EFTT_SMOOTHNESS:
return Col_White;
case EFTT_EMITTANCE:
majoroffs = MaterialRegister_EmissiveColor;
break;
default:
return Col_White;
}
return reinterpret_cast<const ColorF&>(m_Constants[majoroffs]);
}
float CShaderResources::GetStrengthValue(EEfResTextures slot) const
{
if (!m_Constants.size())
{
return Col_Black.a;
}
int majoroffs, minoroffs;
switch (slot)
{
case EFTT_DIFFUSE:
return 1.0f;
case EFTT_SPECULAR:
return 1.0f;
case EFTT_OPACITY:
majoroffs = MaterialRegister_DiffuseColor;
minoroffs = 3;
break;
case EFTT_SMOOTHNESS:
majoroffs = MaterialRegister_SpecularColor;
minoroffs = 3;
break;
case EFTT_EMITTANCE:
majoroffs = MaterialRegister_EmissiveColor;
minoroffs = 3;
break;
default:
return 1.0f;
}
ColorF* pDst = (ColorF*)&m_Constants[0];
return pDst[majoroffs][minoroffs];
}
void CShaderResources::SetColorValue(EEfResTextures slot, const ColorF& color)
{
if (!m_Constants.size())
{
return;
}
// NOTE: ideally the switch goes away and values are indexed directly
int majoroffs;
switch (slot)
{
case EFTT_DIFFUSE:
majoroffs = MaterialRegister_DiffuseColor;
break;
case EFTT_SPECULAR:
majoroffs = MaterialRegister_SpecularColor;
break;
case EFTT_OPACITY:
return;
case EFTT_SMOOTHNESS:
return;
case EFTT_EMITTANCE:
majoroffs = MaterialRegister_EmissiveColor;
break;
default:
return;
}
ColorF* pDst = (ColorF*)&m_Constants[0];
pDst[majoroffs] = ColorF(color.toVec3(), pDst[majoroffs][3]);
}
void CShaderResources::SetStrengthValue(EEfResTextures slot, float value)
{
if (!m_Constants.size())
{
return;
}
// NOTE: ideally the switch goes away and values are indexed directly
int majoroffs, minoroffs;
switch (slot)
{
case EFTT_DIFFUSE:
return;
case EFTT_SPECULAR:
return;
case EFTT_OPACITY:
majoroffs = MaterialRegister_DiffuseColor;
minoroffs = 3;
break;
case EFTT_SMOOTHNESS:
majoroffs = MaterialRegister_SpecularColor;
minoroffs = 3;
break;
case EFTT_EMITTANCE:
majoroffs = MaterialRegister_EmissiveColor;
minoroffs = 3;
break;
default:
return;
}
ColorF* pDst = (ColorF*)&m_Constants[0];
pDst[majoroffs][minoroffs] = value;
}
#ifndef NULL_RENDERER
void CShaderResources::UpdateConstants(IShader* pISH)
{
if (gRenDev && gRenDev->m_pRT)
{
pISH->AddRef();
this->AddRef();
gRenDev->m_pRT->EnqueueRenderCommand([this, pISH]()
{
#if defined(CRY_USE_METAL)
//On metal the dynamic constant buffer usage assumes it will be updated every frame
//Since that is not the case with material properties use static option.
AzRHI::ConstantBufferUsage usage = AzRHI::ConstantBufferUsage::Static;
#else
AzRHI::ConstantBufferUsage usage = AzRHI::ConstantBufferUsage::Dynamic;
#endif
Rebuild(pISH, usage);
pISH->Release();
this->Release();
});
}
}
namespace
{
void WriteConstants(SFXParam* requestedParameter, AZStd::vector<SShaderParam>& parameters, AZStd::vector<Vec4>& outConstants)
{
const AZ::u32 parameterFlags = requestedParameter->GetFlags();
const AZ::u8 paramStageSetter = requestedParameter->m_OffsetStageSetter;
const AZ::u32 registerOffset = requestedParameter->m_Register[paramStageSetter];
AZ_Assert(registerOffset < outConstants.size(), "Requested parameter beyond the bounds of the constant buffer.");
float* outputData = &outConstants[registerOffset][0];
for (AZ::u32 componentIdx = 0; componentIdx < 4; componentIdx++)
{
if (parameterFlags & PF_AUTOMERGED)
{
CryFixedStringT<128> name;
requestedParameter->GetCompName(componentIdx, name);
SShaderParam::GetValue(name.c_str(), &parameters, outputData, componentIdx);
}
else
{
SShaderParam::GetValue(requestedParameter->m_Name.c_str(), &parameters, outputData, componentIdx);
}
}
}
// Creates a parameters list for populating the constants in the Constant Buffer and returns the minimum and maximum slot offset
// of the newly added parameters taking their size into account for the maximum offset.
// NOTE: the minimum and maximum slot offsets MUST be initialized outside (min=10000, max=0) for the gathering to be valid.
void AddShaderParamToArray(
SShaderFXParams& inParameters, FixedDynArray<SFXParam*>& outParameters,
EHWShaderClass shaderClass, AZ::s32 &minSlotOffset, AZ::s32 &maxSlotOffset )
{
for (int n = 0; n < inParameters.m_FXParams.size(); n++)
{
SFXParam* parameter = &inParameters.m_FXParams[n];
if (parameter->m_nFlags & PF_MERGE)
{
continue;
}
if (parameter->m_BindingSlot == eConstantBufferShaderSlot_PerMaterial)
{
if (parameter->m_Register[shaderClass] < 0 || parameter->m_Register[shaderClass] >= 10000)
{
continue;
}
// Run over all existing parameters and look for the name entry
size_t findIdx = 0;
for (; findIdx < outParameters.size(); findIdx++)
{
// The name entry was found - break with its index to prevent double insertion
if (outParameters[findIdx]->m_Name == parameter->m_Name)
{
// Add the current usage to the marked usage
outParameters[findIdx]->m_StagesUsage |= ((0x1 << shaderClass) & 0xff);
break;
}
}
// No existing entry for that name was found - add it. (otherwise ignore to avoid adding twice)
// Taking the first occurrence is not the optimal solution as it might leave gaps in constants offsets.
// A better solution would be to eliminate duplicates first with close grouping heuristics.
if (findIdx == outParameters.size())
{
outParameters.push_back(parameter);
parameter->m_OffsetStageSetter = shaderClass;
parameter->m_StagesUsage = ((0x1 << shaderClass) & 0xff);
minSlotOffset = AZStd::GetMin(minSlotOffset, static_cast<AZ::s32>(parameter->m_Register[shaderClass]));
maxSlotOffset = AZStd::GetMax(maxSlotOffset, static_cast<AZ::s32>(parameter->m_Register[shaderClass] + parameter->m_RegisterCount));
}
}
}
}
}
void CShaderResources::Rebuild(IShader* abstractShader, AzRHI::ConstantBufferUsage usage)
{
AZ_TRACE_METHOD();
CShader* shader = (CShader*)abstractShader;
// Do not attempt to update constant buffers for shaders that are not compiled or parsed.
// We can hit this case easily when r_shadersImport >= 2 under two primary scenarios:
// 1) We want to render an object with a shader that was never compiled because it was never added to the shader list from the Remote Shader Compiler.
// This is resolved by running the game in Debug or Profile and properly building your shader permutation list and rebuilding your shader paks.
// 2) The shader permutation was never compiled because it was never intended to render, but it is still loaded into memory and active.
// This occurs when a material's submaterial is unused, for example when a nodraw shader is attached as a submaterial.
// The material system naively attempts to update shader constants for all submaterial's shader techniques/passes
// via CMatInfo::RefreshShaderResourceConstants(), so we do not want to attempt to upload shader constants for shader permutations that will never be used.
if (!(shader->m_Flags & EF_LOADED))
{
return;
}
// Build list of used parameters and fill constant buffer scratchpad
SShaderFXParams& parameterRegistry = gRenDev->m_cEF.m_Bin.mfGetFXParams(shader);
const size_t parameterCount = parameterRegistry.m_FXParams.size();
const size_t parameterByteCount = parameterCount * sizeof(SFXParam*);
// Added this as a precaution
AZ_Assert((eHWSC_Num < 8), "More than 8 shader stages - m_StagesUsage can only represent 8, please adjust it to AZ::u16" );
FixedDynArray<SFXParam*> usedParameters;
PREFAST_SUPPRESS_WARNING(6255) usedParameters.set(ArrayT((SFXParam**)alloca((int)parameterByteCount), (int)parameterCount));
AZ::s32 registerStart = 10000;
AZ::s32 registerCountMax = 0;
for (AZ::u32 techniqueIdx = 0; techniqueIdx < (int)shader->m_HWTechniques.Num(); techniqueIdx++)
{
SShaderTechnique* technique = shader->m_HWTechniques[techniqueIdx];
for (AZ::u32 passIdx = 0; passIdx < technique->m_Passes.Num(); passIdx++)
{
const SShaderPass* pass = &technique->m_Passes[passIdx];
const CHWShader* shaders[] = { pass->m_VShader, pass->m_PShader, pass->m_GShader, pass->m_HShader, pass->m_DShader, pass->m_CShader };
for (EHWShaderClass shaderClass = EHWShaderClass(0); shaderClass < eHWSC_Num; shaderClass = EHWShaderClass(shaderClass + 1))
{
if (shaders[shaderClass])
{
AddShaderParamToArray(parameterRegistry, usedParameters, shaderClass, registerStart, registerCountMax);
}
}
}
}
// Ordering the slots according to the Vertex Shader's slots offsets. The order is valid in most cases with
// the exception when the different stages have different slots offsets, however the slots' offsets range
// is always valid since it's covered by the minimum and maximum gathering that happens during the
// slots go over.
AZStd::sort(usedParameters.begin(), usedParameters.end(), [] (const SFXParam* lhs, const SFXParam* rhs)
{
return lhs->m_Register[0] < rhs->m_Register[0];
});
if (usedParameters.size())
{
// Validate and resize constant buffer scratchpad to match our reflection data.
{
AZ_Assert(registerStart < registerCountMax, "invalid constant buffer register interval");
if(registerCountMax > m_Constants.size())
{
m_Constants.resize(registerCountMax);
}
}
// Copies local shader tweakable values to the shaders local scratchpad. Then for each used parameter
// copies that data into the constant buffer.
{
AZStd::vector<SShaderParam> publicParameters = shader->GetPublicParams();
if (publicParameters.size())
{
for (AZ::u32 techniqueIdx = 0; techniqueIdx < m_ShaderParams.size(); techniqueIdx++)
{
SShaderParam& tweakable = m_ShaderParams[techniqueIdx];
for (AZ::u32 j = 0; j < publicParameters.size(); j++)
{
SShaderParam& outParameter = publicParameters[j];
if (outParameter.m_Name == tweakable.m_Name)
{
tweakable.CopyType(outParameter);
outParameter.CopyValueNoString(tweakable); // there should not be 'string' values set to shader
break;
}
}
}
for (AZ::u32 i = 0; i < usedParameters.size(); i++)
{
WriteConstants(usedParameters[i], publicParameters, m_Constants);
}
}
}
}
// Update common parameters
{
// Updating the texture modifiers
SEfResTexture* pTextureRes = nullptr;
for (AZ::u32 i = 0; i < UVTransform::GetSupportedSlotCount(); ++i)
{
const EEfResTextures slot = UVTransform::GetSupportedSlot(i).m_Slot;
const AZ::u32 registerOffset = UVTransform::GetSupportedSlot(i).m_RegisterOffset;
Matrix44 matrix(IDENTITY);
pTextureRes = GetTextureResource (slot);
if (pTextureRes && pTextureRes->m_Ext.m_pTexModifier)
{
pTextureRes->Update(slot);
matrix = pTextureRes->m_Ext.m_pTexModifier->m_TexMatrix;
}
*reinterpret_cast<Matrix44*>(&m_Constants[registerOffset]) = matrix;
}
Vec4 texelDensity(0.0f, 0.0f, 1.0f, 1.0f);
Vec4 detailTiling(1.0f);
// [Shader System TO DO] - move that to be data driven
pTextureRes = GetTextureResource(EFTT_NORMALS);
if (pTextureRes && pTextureRes->m_Sampler.m_pTex)
{
texelDensity.x = (float)pTextureRes->m_Sampler.m_pTex->GetWidth();
texelDensity.y = (float)pTextureRes->m_Sampler.m_pTex->GetHeight();
texelDensity.z = 1.0f / std::max(1.0f, texelDensity.x);
texelDensity.w = 1.0f / std::max(1.0f, texelDensity.y);
}
pTextureRes = GetTextureResource(EFTT_DETAIL_OVERLAY);
if (pTextureRes && pTextureRes->m_Ext.m_pTexModifier)
{
pTextureRes->Update(EFTT_DETAIL_OVERLAY);
detailTiling.x = pTextureRes->m_Ext.m_pTexModifier->m_Tiling[0];
detailTiling.y = pTextureRes->m_Ext.m_pTexModifier->m_Tiling[1];
detailTiling.z = 1.0f / detailTiling.x;
detailTiling.w = 1.0f / detailTiling.y;
}
Vec4 deformWave0(0);
Vec4 deformWave1(0);
if (IsDeforming())
{
deformWave0.x = m_pDeformInfo->m_WaveX.m_Freq;
deformWave0.y = m_pDeformInfo->m_WaveX.m_Phase;
deformWave0.z = m_pDeformInfo->m_WaveX.m_Amp;
deformWave0.w = m_pDeformInfo->m_WaveX.m_Level;
deformWave1.x = 1.0f / m_pDeformInfo->m_fDividerX;
}
// We store the alpha test value into the last channel of deform wave (see GetMaterial_AlphaTest()).
deformWave1.w = m_AlphaRef;
m_Constants[MaterialRegister_TexelDensity] = texelDensity;
m_Constants[MaterialRegister_DetailTiling] = detailTiling;
m_Constants[MaterialRegister_DeformWave + 0] = deformWave0;
m_Constants[MaterialRegister_DeformWave + 1] = deformWave1;
}
SAFE_RELEASE(m_ConstantBuffer);
if (m_Constants.size())
{
m_ConstantBuffer = gcpRendD3D->m_DevBufMan.CreateConstantBuffer(
"PerMaterial",
m_Constants.size() * sizeof(Vec4),
usage,
AzRHI::ConstantBufferFlags::None);
m_ConstantBuffer->UpdateBuffer(&m_Constants[0], m_Constants.size() * sizeof(Vec4));
// Only do expensive DX12 resource set building for PC DX12
#if defined(CRY_USE_DX12)
if (!m_pCompiledResourceSet)
{
m_pCompiledResourceSet = CDeviceObjectFactory::GetInstance().CreateResourceSet();
}
m_pCompiledResourceSet->Clear();
m_pCompiledResourceSet->Fill(shader, this, EShaderStage_AllWithoutCompute);
m_pCompiledResourceSet->Build();
#endif
}
}
void CShaderResources::CloneConstants(const IRenderShaderResources* pISrc)
{
CShaderResources* pSrc = (CShaderResources*)pISrc;
if (!pSrc)
{
m_Constants.clear();
SAFE_RELEASE(m_ConstantBuffer);
return;
}
else
{
m_Constants = pSrc->m_Constants;
{
AzRHI::ConstantBuffer*& pCB0Dst = m_ConstantBuffer;
AzRHI::ConstantBuffer*& pCB0Src = pSrc->m_ConstantBuffer;
if (pCB0Src)
{
pCB0Src->AddRef();
}
if (pCB0Dst)
{
pCB0Dst->Release();
}
pCB0Dst = pCB0Src;
}
}
}
void CShaderResources::ReleaseConstants()
{
m_Constants.clear();
if (m_ConstantBuffer)
{
AzRHI::ConstantBuffer* constantBuffer = m_ConstantBuffer;
m_ConstantBuffer = nullptr;
gRenDev->m_pRT->EnqueueRenderCommand([constantBuffer]()
{
constantBuffer->Release();
});
}
}
static void AdjustSamplerState(SEfResTexture* pTex, bool bUseGlobalMipBias)
{
int nTS = pTex->m_Sampler.m_nTexState;
if (nTS < 0 || nTS >= (int)CTexture::s_TexStates.size())
{
return;
}
int8 nAniso = min(CRenderer::CV_r_texminanisotropy, CRenderer::CV_r_texmaxanisotropy);
if (nAniso < 1)
{
return;
}
STexState* pTS = &CTexture::s_TexStates[nTS];
STexState ST = *pTS;
float mipBias = 0.0f;
if (bUseGlobalMipBias)
{
mipBias = gRenDev->GetTemporalJitterMipBias();
}
if (ST.m_nAnisotropy == nAniso && ST.m_MipBias == mipBias)
{
return;
}
ST.m_pDeviceState = NULL; //otherwise state change is not applied
ST.m_MipBias = mipBias;
if (nAniso >= 16)
{
ST.m_nMipFilter =
ST.m_nMinFilter =
ST.m_nMagFilter = FILTER_ANISO16X;
}
else if (nAniso >= 8)
{
ST.m_nMipFilter =
ST.m_nMinFilter =
ST.m_nMagFilter = FILTER_ANISO8X;
}
else if (nAniso >= 4)
{
ST.m_nMipFilter =
ST.m_nMinFilter =
ST.m_nMagFilter = FILTER_ANISO4X;
}
else if (nAniso >= 2)
{
ST.m_nMipFilter =
ST.m_nMinFilter =
ST.m_nMagFilter = FILTER_ANISO2X;
}
else
{
ST.m_nMipFilter =
ST.m_nMinFilter =
ST.m_nMagFilter = FILTER_TRILINEAR;
}
ST.m_nAnisotropy = nAniso;
pTex->m_Sampler.m_nTexState = CTexture::GetTexState(ST);
}
// [Shader System TO DO] - delete this hard coded method. Move to data driven
void CShaderResources::AdjustForSpec()
{
// Note: Anisotropic filtering for smoothness maps is deliberately disabled, otherwise
// mip transitions become too obvious when using maps pre-filtered with normal variance
uint16 SamplersModulatorsIdx[] = {
EFTT_DIFFUSE,
EFTT_NORMALS,
EFTT_SPECULAR,
EFTT_CUSTOM,
EFTT_CUSTOM_SECONDARY,
EFTT_EMITTANCE,
0xffff,
};
for (uint32 pos = 0; SamplersModulatorsIdx[pos] != 0xffff ; pos++)
{
SEfResTexture* pTextureRes = GetTextureResource(SamplersModulatorsIdx[pos]);
if (pTextureRes)
{
AdjustSamplerState(pTextureRes, true);
}
}
}
#endif