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/RendElements/FlareSoftOcclusionQuery.cpp

445 lines
14 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 "FlareSoftOcclusionQuery.h"
#include "../CryNameR.h"
#include "../../XRenderD3D9/DriverD3D.h"
#include "../Textures/Texture.h"
#include "I3DEngine.h"
unsigned char CFlareSoftOcclusionQuery::s_paletteRawCache[s_nIDMax * 4];
char CFlareSoftOcclusionQuery::s_idHashTable[s_nIDMax];
int CFlareSoftOcclusionQuery::s_idCount = 0;
int CFlareSoftOcclusionQuery::s_ringReadIdx = 1;
int CFlareSoftOcclusionQuery::s_ringWriteIdx = 0;
int CFlareSoftOcclusionQuery::s_ringSize = MAX_OCCLUSION_READBACK_TEXTURES;
static bool g_bCreatedGlobalResources = false;
const float CFlareSoftOcclusionQuery::s_fSectorWidth = (float)s_nGatherEachSectorWidth / (float)s_nGatherTextureWidth;
const float CFlareSoftOcclusionQuery::s_fSectorHeight = (float)s_nGatherEachSectorHeight / (float)s_nGatherTextureHeight;
float CSoftOcclusionVisiblityFader::UpdateVisibility(const float newTargetVisibility, const float duration)
{
m_TimeLine.duration = duration;
if (fabs(newTargetVisibility - m_fTargetVisibility) > 0.1f)
{
m_TimeLine.startValue = m_TimeLine.GetPrevYValue();
m_fTargetVisibility = newTargetVisibility;
m_TimeLine.rewind();
}
else if (newTargetVisibility < 0.05f)
{
m_fTargetVisibility = newTargetVisibility;
m_TimeLine.endValue = newTargetVisibility;
}
else
{
m_TimeLine.endValue = m_fTargetVisibility;
}
// Fade in fixed time (in ms), fade out user controlled
const float fadeInTime = m_TimeLine.duration * (1.0f / 0.15f);
const float fadeOutTime = 1e3f;
float dt = (newTargetVisibility > m_fVisibilityFactor) ? fadeInTime : fadeOutTime;
m_fVisibilityFactor = m_TimeLine.step(gEnv->pTimer->GetFrameTime() * dt); // interpolate
return m_fVisibilityFactor;
}
void ReportIDPoolOverflow(int idCount, int idMax)
{
iLog->Log("Number of soft occlusion queries [%d] exceeds current allowed range %d", idCount, idMax);
}
int CFlareSoftOcclusionQuery::GenID()
{
int hashCode = s_idCount % s_nIDMax;
while (hashCode < s_nIDMax && s_idHashTable[hashCode])
{
hashCode++;
}
if (hashCode >= s_nIDMax)
{
ReportIDPoolOverflow(s_idCount + 1, s_nIDMax);
hashCode = 0;
}
s_idHashTable[hashCode] = (char)1;
s_idCount++;
return hashCode;
}
void CFlareSoftOcclusionQuery::ReleaseID(int id)
{
if (id < s_nIDMax)
{
s_idHashTable[id] = (char)0;
}
}
void CFlareSoftOcclusionQuery::InitGlobalResources()
{
if (g_bCreatedGlobalResources)
{
return;
}
const uint32 numGPUs = (gRenDev->GetActiveGPUCount() <= MAX_OCCLUSION_READBACK_TEXTURES / 2) ? gRenDev->GetActiveGPUCount() : 1;
s_ringWriteIdx = 0;
s_ringReadIdx = numGPUs;
s_ringSize = MAX_OCCLUSION_READBACK_TEXTURES;
memset(s_idHashTable, 0, sizeof(s_idHashTable));
memset(s_paletteRawCache, 0, sizeof(s_paletteRawCache));
g_bCreatedGlobalResources = true;
}
void CFlareSoftOcclusionQuery::GetDomainInTexture(float& out_x0, float& out_y0, float& out_x1, float& out_y1)
{
out_x0 = (m_nID % s_nIDColMax) * s_fSectorWidth;
out_y0 = (m_nID / s_nIDColMax) * s_fSectorHeight;
out_x1 = out_x0 + s_fSectorWidth;
out_y1 = out_y0 + s_fSectorHeight;
}
bool CFlareSoftOcclusionQuery::ComputeProjPos(const Vec3& vWorldPos, const Matrix44A& viewMat, const Matrix44A& projMat, Vec3& outProjPos)
{
static int vp[] = {0, 0, 1, 1};
if (mathVec3Project(&outProjPos, &vWorldPos, vp, &projMat, &viewMat, &gRenDev->m_IdentityMatrix) == 0.0f)
{
return false;
}
return true;
}
void CFlareSoftOcclusionQuery::GetSectorSize(float& width, float& height)
{
width = s_fSectorWidth;
height = s_fSectorWidth;
}
void CFlareSoftOcclusionQuery::GetOcclusionSectorInfo(SOcclusionSectorInfo& out_Info)
{
Vec3 vProjectedPos;
if (ComputeProjPos(m_PosToBeChecked, gRenDev->m_ViewMatrix, gRenDev->m_ProjMatrix, vProjectedPos) == false)
{
return;
}
out_Info.lineardepth = clamp_tpl(ComputeLinearDepth(m_PosToBeChecked, gRenDev->m_CameraMatrix, gRenDev->GetViewParameters().fNear, gRenDev->GetViewParameters().fFar), -1.0f, 0.99f);
out_Info.u0 = vProjectedPos.x - m_fOccPlaneWidth / 2;
out_Info.v0 = vProjectedPos.y - m_fOccPlaneHeight / 2;
out_Info.u1 = vProjectedPos.x + m_fOccPlaneWidth / 2;
out_Info.v1 = vProjectedPos.y + m_fOccPlaneHeight / 2;
if (out_Info.u0 < 0)
{
out_Info.u0 = 0;
}
if (out_Info.v0 < 0)
{
out_Info.v0 = 0;
}
if (out_Info.u1 > 1)
{
out_Info.u1 = 1;
}
if (out_Info.v1 > 1)
{
out_Info.v1 = 1;
}
GetDomainInTexture(out_Info.x0, out_Info.y0, out_Info.x1, out_Info.y1);
out_Info.x0 = out_Info.x0 * 2.0f - 1;
out_Info.y0 = -(out_Info.y0 * 2.0f - 1.0f);
out_Info.x1 = out_Info.x1 * 2.0f - 1;
out_Info.y1 = -(out_Info.y1 * 2.0f - 1.0f);
}
float CFlareSoftOcclusionQuery::ComputeLinearDepth(const Vec3& worldPos, const Matrix44A& cameraMat, float nearDist, float farDist)
{
Vec4 out, wPos4;
wPos4.x = worldPos.x;
wPos4.y = worldPos.y;
wPos4.z = worldPos.z;
wPos4.w = 1;
mathVec4Transform((f32*)&out, (f32*)(&cameraMat), (f32*)&wPos4);
if (out.w == 0.0f)
{
return 0;
}
const CameraViewParameters& rc = gRenDev->GetViewParameters();
float linearDepth = (-out.z - rc.fNear) / (farDist - nearDist);
return linearDepth;
}
void CFlareSoftOcclusionQuery::UpdateCachedResults()
{
int cacheIdx = 4 * m_nID;
m_fOccResultCache = s_paletteRawCache[cacheIdx + 0] / 255.0f;
m_fDirResultCache = (s_paletteRawCache[cacheIdx + 3] / 255.0f) * 2.0f * PI;
sincos_tpl(m_fDirResultCache, &m_DirVecResultCache.y, &m_DirVecResultCache.x);
}
CTexture* CFlareSoftOcclusionQuery::GetGatherTexture() const
{
return CTexture::s_ptexFlaresGather;
}
void CFlareSoftOcclusionQuery::BatchReadResults()
{
if (!g_bCreatedGlobalResources)
{
return;
}
CTexture::s_ptexFlaresOcclusionRing[s_ringWriteIdx]->GetDevTexture()->AccessCurrStagingResource(0, false, [=](void* pData, uint32 rowPitch, [[maybe_unused]] uint32 slicePitch)
{
unsigned char* pTexBuf = reinterpret_cast<unsigned char*>(pData);
int validLineStrideBytes = s_nIDColMax * 4;
for (int i = 0; i < s_nIDRowMax; i++)
{
memcpy(s_paletteRawCache + i * validLineStrideBytes, pTexBuf + i * rowPitch, validLineStrideBytes);
}
return true;
});
}
void CFlareSoftOcclusionQuery::ReadbackSoftOcclQuery()
{
CTexture::s_ptexFlaresOcclusionRing[s_ringWriteIdx]->GetDevTexture()->DownloadToStagingResource(0);
// sync point. Move to next texture to read and write
s_ringReadIdx = (s_ringReadIdx + 1) % s_ringSize;
s_ringWriteIdx = (s_ringWriteIdx + 1) % s_ringSize;
}
CTexture* CFlareSoftOcclusionQuery::GetOcclusionTex()
{
return CTexture::s_ptexFlaresOcclusionRing[s_ringWriteIdx];
}
void CSoftOcclusionManager::ComputeVisibility()
{
static STexState ShadowTexState(FILTER_POINT, TADDR_BORDER, TADDR_BORDER, TADDR_BORDER, 0);
CShader* pShader = CShaderMan::s_ShaderSoftOcclusionQuery;
gcpRendD3D->FX_ClearTarget(CTexture::s_ptexFlaresGather, Clr_Transparent);
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexFlaresGather, NULL);
pShader->FXBeginPass(0);
const uint32 vertexCount = GetSize() * 4;
TempDynVB<SVF_P3F_C4B_T2F> vb(gcpRendD3D);
vb.Allocate(vertexCount);
SVF_P3F_C4B_T2F* pDeviceVBAddr = vb.Lock();
for (int i(0), iSoftOcclusionListSize(GetSize()); i < iSoftOcclusionListSize; ++i)
{
CFlareSoftOcclusionQuery* pSoftOcclusion = GetSoftOcclusionQuery(i);
if (pSoftOcclusion == NULL)
{
continue;
}
int offset = i * 4;
CFlareSoftOcclusionQuery::SOcclusionSectorInfo sInfo;
pSoftOcclusion->GetOcclusionSectorInfo(sInfo);
for (int k = 0; k < 4; ++k)
{
pDeviceVBAddr[offset + k].color.dcolor = 0xFFFFFFFF;
}
pDeviceVBAddr[offset + 0].st = Vec2(sInfo.u0 * gcpRendD3D->m_CurViewportScale.x, sInfo.v0 * gcpRendD3D->m_CurViewportScale.y);
pDeviceVBAddr[offset + 1].st = Vec2(sInfo.u1 * gcpRendD3D->m_CurViewportScale.x, sInfo.v0 * gcpRendD3D->m_CurViewportScale.y);
pDeviceVBAddr[offset + 2].st = Vec2(sInfo.u1 * gcpRendD3D->m_CurViewportScale.x, sInfo.v1 * gcpRendD3D->m_CurViewportScale.y);
pDeviceVBAddr[offset + 3].st = Vec2(sInfo.u0 * gcpRendD3D->m_CurViewportScale.x, sInfo.v1 * gcpRendD3D->m_CurViewportScale.y);
pDeviceVBAddr[offset + 0].xyz = Vec3(sInfo.x0, sInfo.y1, sInfo.lineardepth);
pDeviceVBAddr[offset + 1].xyz = Vec3(sInfo.x1, sInfo.y1, sInfo.lineardepth);
pDeviceVBAddr[offset + 2].xyz = Vec3(sInfo.x1, sInfo.y0, sInfo.lineardepth);
pDeviceVBAddr[offset + 3].xyz = Vec3(sInfo.x0, sInfo.y0, sInfo.lineardepth);
}
vb.Unlock();
vb.Bind(0);
vb.Release();
if (pDeviceVBAddr && m_bSuccessGenerateIB)
{
CTexture::s_ptexZTargetScaled->Apply(0, CTexture::GetTexState(ShadowTexState));
gcpRendD3D->FX_Commit();
if (SUCCEEDED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3F_C4B_T2F)))
{
gcpRendD3D->FX_DrawIndexedPrimitive(eptTriangleList, 0, 0, vertexCount, 0, m_IndexBufferCount);
}
}
pShader->FXEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
bool CSoftOcclusionManager::GenerateIndexBuffer()
{
m_IndexBufferCount = GetSize() * 2 * 3;
if (m_IndexBufferCount <= 0)
{
return false;
}
TempDynIB16 ib(gcpRendD3D);
ib.Allocate(m_IndexBufferCount);
uint16* pDeviceIBAddr = ib.Lock();
for (int i(0), iSoftOcclusionListSize(GetSize()); i < iSoftOcclusionListSize; ++i)
{
CFlareSoftOcclusionQuery* pSoftOcclusion = GetSoftOcclusionQuery(i);
if (pSoftOcclusion == NULL)
{
continue;
}
int offset0 = i * 6;
int offset1 = i * 4;
pDeviceIBAddr[offset0 + 0] = offset1 + 0;
pDeviceIBAddr[offset0 + 1] = offset1 + 1;
pDeviceIBAddr[offset0 + 2] = offset1 + 2;
pDeviceIBAddr[offset0 + 3] = offset1 + 2;
pDeviceIBAddr[offset0 + 4] = offset1 + 3;
pDeviceIBAddr[offset0 + 5] = offset1 + 0;
}
ib.Unlock();
m_IndexBufferOffset = 0;
ib.Bind();
ib.Release();
return true;
}
void CSoftOcclusionManager::GatherOcclusions()
{
CShader* pShader = CShaderMan::s_ShaderSoftOcclusionQuery;
static STexState GatherTexState(FILTER_POINT, true);
gcpRendD3D->FX_ClearTarget(CFlareSoftOcclusionQuery::GetOcclusionTex(), Clr_Transparent);
gcpRendD3D->FX_PushRenderTarget(0, CFlareSoftOcclusionQuery::GetOcclusionTex(), NULL);
pShader->FXBeginPass(1);
float x0 = 0, y0 = 0, x1 = 0, y1 = 0;
const uint32 vertexCount = GetSize() * 4;
TempDynVB<SVF_P3F_C4B_T2F> vb(gcpRendD3D);
vb.Allocate(vertexCount);
SVF_P3F_C4B_T2F* pDeviceVBAddr = vb.Lock();
for (int i = 0, iSoftOcclusionListSize(GetSize()); i < iSoftOcclusionListSize; ++i)
{
CFlareSoftOcclusionQuery* pSoftOcclusion = GetSoftOcclusionQuery(i);
if (pSoftOcclusion == NULL)
{
continue;
}
int offset = i * 4;
pSoftOcclusion->GetDomainInTexture(x0, y0, x1, y1);
for (int k = 0; k < 4; ++k)
{
pDeviceVBAddr[offset + k].st = Vec2((x0 + x1) * 0.5f * gcpRendD3D->m_CurViewportScale.x, (y0 + y1) * 0.5f * gcpRendD3D->m_CurViewportScale.y);
pDeviceVBAddr[offset + k].color.dcolor = 0xFFFFFFFF;
}
x0 = x0 * 2.0f - 1.0f;
y0 = -(y0 * 2.0f - 1.0f);
x1 = x1 * 2.0f - 1.0f;
y1 = -(y1 * 2.0f - 1.0f);
pDeviceVBAddr[offset + 0].xyz = Vec3(x0, y1, 1);
pDeviceVBAddr[offset + 1].xyz = Vec3(x1, y1, 1);
pDeviceVBAddr[offset + 2].xyz = Vec3(x1, y0, 1);
pDeviceVBAddr[offset + 3].xyz = Vec3(x0, y0, 1);
}
vb.Unlock();
vb.Bind(0);
vb.Release();
if (pDeviceVBAddr && m_bSuccessGenerateIB)
{
static CCryNameR occlusionNormalizedSizeName("occlusionNormalizedSize");
const Vec4 occlusionSizeParam(CFlareSoftOcclusionQuery::s_fSectorWidth, CFlareSoftOcclusionQuery::s_fSectorHeight, 0, 0);
pShader->FXSetPSFloat(occlusionNormalizedSizeName, &occlusionSizeParam, 1);
CTexture::s_ptexFlaresGather->Apply(0, CTexture::GetTexState(GatherTexState));
gcpRendD3D->FX_Commit();
if (SUCCEEDED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3F_C4B_T2F)))
{
gcpRendD3D->FX_DrawIndexedPrimitive(eptTriangleList, 0, 0, vertexCount, 0, m_IndexBufferCount);
}
}
pShader->FXEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
CFlareSoftOcclusionQuery* CSoftOcclusionManager::GetSoftOcclusionQuery(int nIndex) const
{
if (nIndex >= CFlareSoftOcclusionQuery::s_nIDMax)
{
return NULL;
}
return m_SoftOcclusionQueries[nIndex];
}
void CSoftOcclusionManager::AddSoftOcclusionQuery(CFlareSoftOcclusionQuery* pQuery, const Vec3& vPos)
{
if (m_nPos < CFlareSoftOcclusionQuery::s_nIDMax)
{
pQuery->SetPosToBeChecked(vPos);
m_SoftOcclusionQueries[m_nPos++] = pQuery;
}
}
bool CSoftOcclusionManager::Begin()
{
if (GetSize() > 0)
{
m_bSuccessGenerateIB = GenerateIndexBuffer();
return true;
}
return false;
}
void CSoftOcclusionManager::End()
{
m_nPos = 0;
}
void CSoftOcclusionManager::ClearResources()
{
for (int i = 0; i < CFlareSoftOcclusionQuery::s_nIDMax; ++i)
{
m_SoftOcclusionQueries[i] = NULL;
}
}