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/RenderThread.cpp

3863 lines
111 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 <I3DEngine.h>
#include <IFont.h>
#include "RenderAuxGeom.h"
#include "IColorGradingControllerInt.h"
#include "PostProcess/PostEffects.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define RENDERTHREAD_CPP_SECTION_1 1
#define RENDERTHREAD_CPP_SECTION_2 2
#define RENDERTHREAD_CPP_SECTION_3 3
#define RENDERTHREAD_CPP_SECTION_4 4
#define RENDERTHREAD_CPP_SECTION_5 5
#define RENDERTHREAD_CPP_SECTION_6 6
#define RENDERTHREAD_CPP_SECTION_7 7
#define RENDERTHREAD_CPP_SECTION_8 8
#endif
#if !defined(NULL_RENDERER)
#include <DriverD3D.h>
#endif
#include "MainThreadRenderRequestBus.h"
#include "Common/RenderView.h"
#include "Common/Textures/TextureManager.h"
#include "GraphicsPipeline/FurBendData.h"
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Archive/IArchive.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include <IVideoRenderer.h>
#ifdef STRIP_RENDER_THREAD
#define m_nCurThreadFill 0
#define m_nCurThreadProcess 0
#endif
#define MULTITHREADED_RESOURCE_CREATION
#ifdef WIN32
HWND SRenderThread::GetRenderWindowHandle()
{
return (HWND)gRenDev->GetHWND();
}
#endif
CryCriticalSection SRenderThread::s_rcLock;
# define LOADINGLOCK_COMMANDQUEUE (void)0;
void CRenderThread::Run()
{
//f_Log = fopen("Mouse.txt", "w");
//fclose(f_Log);
CryThreadSetName(threadID(THREADID_NULL), RENDER_THREAD_NAME);
gEnv->pSystem->GetIThreadTaskManager()->MarkThisThreadForDebugging(RENDER_THREAD_NAME, true);
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
threadID renderThreadId = ::GetCurrentThreadId();
gRenDev->m_pRT->m_nRenderThread = renderThreadId;
CNameTableR::m_nRenderThread = renderThreadId;
gEnv->pCryPak->SetRenderThreadId(AZStd::this_thread::get_id());
//SetThreadAffinityMask(GetCurrentThread(), 2);
m_started.Set();
gRenDev->m_pRT->Process();
// Check pointers, it's not guaranteed that system is still valid.
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetIThreadTaskManager())
{
gEnv->pSystem->GetIThreadTaskManager()->MarkThisThreadForDebugging(RENDER_THREAD_NAME, false);
}
}
void CRenderThreadLoading::Run()
{
CryThreadSetName(threadID(THREADID_NULL), RENDER_LOADING_THREAD_NAME);
gEnv->pSystem->GetIThreadTaskManager()->MarkThisThreadForDebugging(RENDER_LOADING_THREAD_NAME, true);
threadID renderThreadId = ::GetCurrentThreadId();
gRenDev->m_pRT->m_nRenderThreadLoading = renderThreadId;
CNameTableR::m_nRenderThread = renderThreadId;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
// We aren't interested in file access from the render loading thread, and this
// would overwrite the real render thread id
//gEnv->pCryPak->SetRenderThreadId( renderThreadId );
m_started.Set();
gRenDev->m_pRT->ProcessLoading();
// Check pointers, it's not guaranteed that system is still valid.
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetIThreadTaskManager())
{
gEnv->pSystem->GetIThreadTaskManager()->MarkThisThreadForDebugging(RENDER_LOADING_THREAD_NAME, false);
}
}
void SRenderThread::SwitchMode(bool bEnableVideo)
{
if (bEnableVideo)
{
assert(IsRenderThread());
if (m_pThreadLoading)
{
return;
}
#if !defined(STRIP_RENDER_THREAD)
SSystemGlobalEnvironment* pEnv = iSystem->GetGlobalEnvironment();
if (pEnv && !pEnv->bTesting && !pEnv->IsEditor() && pEnv->pi.numCoresAvailableToProcess > 1 && CRenderer::CV_r_multithreaded > 0)
{
m_pThreadLoading = new CRenderThreadLoading(1);
}
m_eVideoThreadMode = eVTM_Active;
m_bQuitLoading = false;
StartRenderLoadingThread();
#endif
}
else
{
m_eVideoThreadMode = eVTM_ProcessingStop;
}
}
SRenderThread::SRenderThread()
{
m_eVideoThreadMode = eVTM_Disabled;
m_nRenderThreadLoading = 0;
m_pThreadLoading = 0;
m_pLoadtimeCallback = 0;
m_bEndFrameCalled = false;
m_bBeginFrameCalled = false;
m_bQuitLoading = false;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
#if defined(USE_HANDLE_FOR_FINAL_FLUSH_SYNC)
m_FlushFinishedCondition = CreateEvent(NULL, FALSE, FALSE, "FlushFinishedCondition");
#endif
Init(2);
}
threadID CNameTableR::m_nRenderThread = 0;
void SRenderThread::Init(int nCPU)
{
m_bQuit = false;
#ifndef STRIP_RENDER_THREAD
m_nCurThreadFill = 0;
m_nCurThreadProcess = 0;
#endif
InitFlushCond();
m_nRenderThread = ::GetCurrentThreadId();
CNameTableR::m_nRenderThread = m_nRenderThread;
m_nMainThread = m_nRenderThread;
m_bSuccessful = true;
m_pThread = NULL;
m_fTimeIdleDuringLoading = 0;
m_fTimeBusyDuringLoading = 0;
#if !defined(STRIP_RENDER_THREAD)
SSystemGlobalEnvironment* pEnv = iSystem->GetGlobalEnvironment();
if (pEnv && !pEnv->bTesting && !pEnv->IsDedicated() && !pEnv->IsEditor() && pEnv->pi.numCoresAvailableToProcess > 1 && CRenderer::CV_r_multithreaded > 0)
{
m_nCurThreadProcess = 1;
m_pThread = new CRenderThread(nCPU);
}
#ifndef CONSOLE_CONST_CVAR_MODE
else
{
CRenderer::CV_r_multithreaded = 0;
}
#endif
#else//STRIP_RENDER_THREAD
#ifndef CONSOLE_CONST_CVAR_MODE
CRenderer::CV_r_multithreaded = 0;
#endif
#endif//STRIP_RENDER_THREAD
gRenDev->m_RP.m_nProcessThreadID = threadID(m_nCurThreadProcess);
gRenDev->m_RP.m_nFillThreadID = threadID(m_nCurThreadFill);
for (uint32 i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
{
m_Commands[i].Free();
m_Commands[i].Create(300 * 1024); // 300 to stop growing in MP levels
m_Commands[i].SetUse(0);
gRenDev->m_fTimeWaitForMain[i] = 0;
gRenDev->m_fTimeWaitForRender[i] = 0;
gRenDev->m_fTimeProcessedRT[i] = 0;
gRenDev->m_fTimeProcessedGPU[i] = 0;
}
m_eVideoThreadMode = eVTM_Disabled;
}
SRenderThread::~SRenderThread()
{
QuitRenderLoadingThread();
QuitRenderThread();
#if defined(USE_HANDLE_FOR_FINAL_FLUSH_SYNC)
CloseHandle(m_FlushFinishedCondition);
#endif
}
void SRenderThread::ValidateThreadAccess(ERenderCommand eRC)
{
if (!IsMainThread() && !gRenDev->m_bStartLevelLoading)
{
CryFatalError("Trying to add a render command from a non-main thread, eRC = %d", (int)eRC);
}
}
//==============================================================================================
// NOTE: Render commands can be added from main thread only
bool SRenderThread::RC_CreateDevice()
{
LOADING_TIME_PROFILE_SECTION;
AZ_TRACE_METHOD();
#if defined(WIN32) || defined(WIN64) || defined(APPLE) || defined(LINUX) || defined(CREATE_DEVICE_ON_MAIN_THREAD)
return gRenDev->RT_CreateDevice();
#else
if (IsRenderThread())
{
return gRenDev->RT_CreateDevice();
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateDevice, 0);
EndCommand(p);
FlushAndWait();
return !IsFailed();
#endif
}
void SRenderThread::RC_ResetDevice()
{
AZ_TRACE_METHOD();
#if defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(APPLE) || defined(CREATE_DEVICE_ON_MAIN_THREAD)
gRenDev->RT_Reset();
#else
if (IsRenderThread())
{
gRenDev->RT_Reset();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ResetDevice, 0);
EndCommand(p);
FlushAndWait();
#endif
}
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_4
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
void SRenderThread::RC_PreloadTextures()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return CTexture::RT_Precache();
}
LOADINGLOCK_COMMANDQUEUE
{
byte* p = AddCommand(eRC_PreloadTextures, 0);
EndCommand(p);
FlushAndWait();
}
}
void SRenderThread::RC_Init()
{
LOADING_TIME_PROFILE_SECTION;
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return gRenDev->RT_Init();
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_Init, 0);
EndCommand(p);
FlushAndWait();
}
void SRenderThread::RC_ShutDown(uint32 nFlags)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return gRenDev->RT_ShutDown(nFlags);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ShutDown, 4);
AddDWORD(p, nFlags);
FlushAndWait();
}
void SRenderThread::RC_ResetGlass()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ResetGlass();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ResetGlass, 0);
EndCommand(p);
}
void SRenderThread::RC_ResetToDefault()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->ResetToDefault();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ResetToDefault, 0);
EndCommand(p);
}
void SRenderThread::RC_ParseShader (CShader* pSH, uint64 nMaskGen, uint32 flags, CShaderResources* pRes)
{
AZ_TRACE_METHOD();
if (IsRenderThread(true))
{
return gRenDev->m_cEF.RT_ParseShader(pSH, nMaskGen, flags, pRes);
}
if (!IsMainThread(true))
{
pSH->AddRef();
if (pRes)
{
pRes->AddRef();
}
AZStd::function<void()> runOnMainThread = [this, pSH, nMaskGen, flags, pRes]()
{
RC_ParseShader(pSH, nMaskGen, flags, pRes);
pSH->Release();
if (pRes)
{
pRes->Release();
}
// Make sure any materials using this shader get updated appropriately.
if (gEnv && gEnv->p3DEngine)
{
gEnv->p3DEngine->UpdateShaderItems();
}
};
EBUS_QUEUE_FUNCTION(AZ::MainThreadRenderRequestBus, runOnMainThread);
return;
}
LOADINGLOCK_COMMANDQUEUE
pSH->AddRef();
if (pRes)
{
pRes->AddRef();
}
byte* p = AddCommand(eRC_ParseShader, 12 + 2 * sizeof(void*));
AddPointer(p, pSH);
AddPointer(p, pRes);
AddDWORD64(p, nMaskGen);
AddDWORD(p, flags);
EndCommand(p);
}
void SRenderThread::RC_UpdateShaderItem (SShaderItem* pShaderItem, _smart_ptr<IMaterial> pMaterial)
{
AZ_TRACE_METHOD();
if (IsRenderThread(true))
{
return gRenDev->RT_UpdateShaderItem(pShaderItem, pMaterial.get());
}
if (!IsMainThread(true))
{
AZStd::function<void()> runOnMainThread = [this, pShaderItem, pMaterial]()
{
RC_UpdateShaderItem(pShaderItem, pMaterial);
};
EBUS_QUEUE_FUNCTION(AZ::MainThreadRenderRequestBus, runOnMainThread);
return;
}
LOADINGLOCK_COMMANDQUEUE
// We pass the raw pointer instead of the smart_ptr because writing/reading smart pointers from
// the render thread queue causes the ref count to be increased incorrectly in some platforms (e.g. 32 bit architectures).
// Because of this we manually increment the reference count before adding it to the queue and decrement it when we finish using it in the RenderThread.
IMaterial* materialRawPointer = pMaterial.get();
if (materialRawPointer)
{
// Add a reference to prevent it from getting deleted before the RenderThread process the message.
materialRawPointer->AddRef();
}
if (m_eVideoThreadMode == eVTM_Disabled)
{
byte* p = AddCommand(eRC_UpdateShaderItem, sizeof(pShaderItem) + sizeof(materialRawPointer));
AddPointer(p, pShaderItem);
AddPointer(p, materialRawPointer);
EndCommand(p);
}
else
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_UpdateShaderItem, sizeof(pShaderItem) + sizeof(materialRawPointer), m_CommandsLoading);
AddPointer(p, pShaderItem);
AddPointer(p, pMaterial);
EndCommandTo(p, m_CommandsLoading);
}
}
void SRenderThread::RC_RefreshShaderResourceConstants(SShaderItem* shaderItem, IMaterial* material)
{
if (IsRenderThread())
{
return gRenDev->RT_RefreshShaderResourceConstants(shaderItem);
}
LOADINGLOCK_COMMANDQUEUE;
if (material)
{
// Add a reference to prevent it from getting deleted before the RenderThread process the message.
material->AddRef();
}
if (m_eVideoThreadMode == eVTM_Disabled)
{
byte* p = AddCommand(eRC_RefreshShaderResourceConstants, sizeof(SShaderItem*) + sizeof(IMaterial*));
AddPointer(p, shaderItem);
AddPointer(p, material);
EndCommand(p);
}
else
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_RefreshShaderResourceConstants, sizeof(SShaderItem*) + sizeof(IMaterial*), m_CommandsLoading);
AddPointer(p, shaderItem);
AddPointer(p, material);
EndCommandTo(p, m_CommandsLoading);
}
}
void SRenderThread::RC_SetShaderQuality(EShaderType eST, EShaderQuality eSQ)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return gRenDev->m_cEF.RT_SetShaderQuality(eST, eSQ);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetShaderQuality, 8);
AddDWORD(p, eST);
AddDWORD(p, eSQ);
EndCommand(p);
}
void SRenderThread::RC_ReleaseVBStream(void* pVB, int nStream)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ReleaseVBStream(pVB, nStream);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseVBStream, 4 + sizeof(void*));
AddPointer(p, pVB);
AddDWORD(p, nStream);
EndCommand(p);
}
void SRenderThread::RC_ForceMeshGC(bool instant, bool wait)
{
AZ_TRACE_METHOD();
LOADING_TIME_PROFILE_SECTION;
if (IsRenderThread())
{
CRenderMesh::Tick();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ForceMeshGC, 0);
EndCommand(p);
if (instant)
{
if (wait)
{
FlushAndWait();
}
else
{
SyncMainWithRender();
}
}
}
void SRenderThread::RC_DevBufferSync()
{
AZ_TRACE_METHOD();
LOADING_TIME_PROFILE_SECTION;
if (IsRenderThread())
{
gRenDev->m_DevBufMan.Sync(gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameUpdateID);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DevBufferSync, 0);
EndCommand(p);
}
void SRenderThread::RC_ReleasePostEffects()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
if (gRenDev->m_pPostProcessMgr)
{
gRenDev->m_pPostProcessMgr->ReleaseResources();
}
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleasePostEffects, 0);
EndCommand(p);
}
void SRenderThread::RC_ResetPostEffects(bool bOnSpecChange)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
if (gRenDev->m_RP.m_pREPostProcess)
{
gRenDev->m_RP.m_pREPostProcess->Reset(bOnSpecChange);
}
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(bOnSpecChange ? eRC_ResetPostEffectsOnSpecChange : eRC_ResetPostEffects, 0);
EndCommand(p);
FlushAndWait();
}
void SRenderThread::RC_DisableTemporalEffects()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return gRenDev->RT_DisableTemporalEffects();
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DisableTemporalEffects, 0);
EndCommand(p);
}
void SRenderThread::RC_UpdateTextureRegion(CTexture* pTex, const byte* data, int nX, int nY, int nZ, int USize, int VSize, int ZSize, ETEX_Format eTFSrc)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return pTex->RT_UpdateTextureRegion(data, nX, nY, nZ, USize, VSize, ZSize, eTFSrc);
}
LOADINGLOCK_COMMANDQUEUE
int nSize = CTexture::TextureDataSize(USize, VSize, ZSize, pTex->GetNumMips(), 1, eTFSrc);
byte* pData = new byte[nSize];
cryMemcpy(pData, data, nSize);
pTex->AddRef();
if (m_eVideoThreadMode == eVTM_Disabled)
{
byte* p = AddCommand(eRC_UpdateTexture, 28 + 2 * sizeof(void*));
AddPointer(p, pTex);
AddPointer(p, pData);
AddDWORD(p, nX);
AddDWORD(p, nY);
AddDWORD(p, nZ);
AddDWORD(p, USize);
AddDWORD(p, VSize);
AddDWORD(p, ZSize);
AddDWORD(p, eTFSrc);
EndCommand(p);
}
else
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_UpdateTexture, 28 + 2 * sizeof(void*), m_CommandsLoading);
AddPointer(p, pTex);
AddPointer(p, pData);
AddDWORD(p, nX);
AddDWORD(p, nY);
AddDWORD(p, nZ);
AddDWORD(p, USize);
AddDWORD(p, VSize);
AddDWORD(p, ZSize);
AddDWORD(p, eTFSrc);
EndCommandTo(p, m_CommandsLoading);
}
}
bool SRenderThread::RC_DynTexUpdate(SDynTexture* pTex, int nNewWidth, int nNewHeight)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return pTex->RT_Update(nNewWidth, nNewHeight);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DynTexUpdate, 8 + sizeof(void*));
AddPointer(p, pTex);
AddDWORD(p, nNewWidth);
AddDWORD(p, nNewHeight);
EndCommand(p);
return true;
}
void SRenderThread::RC_EntityDelete(IRenderNode* pRenderNode)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return SDynTexture_Shadow::RT_EntityDelete(pRenderNode);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_EntityDelete, sizeof(void*));
AddPointer(p, pRenderNode);
EndCommand(p);
}
void TexBlurAnisotropicVertical(CTexture* pTex, int nAmount, float fScale, float fDistribution, bool bAlphaOnly);
void SRenderThread::RC_TexBlurAnisotropicVertical(CTexture* Tex, float fAnisoScale)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
TexBlurAnisotropicVertical(Tex, 1, 8 * max(1.0f - min(fAnisoScale / 100.0f, 1.0f), 0.2f), 1, false);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_TexBlurAnisotropicVertical, 4 + sizeof(void*));
AddPointer(p, Tex);
AddFloat(p, fAnisoScale);
EndCommand(p);
}
bool SRenderThread::RC_CreateDeviceTexture(CTexture* pTex, const byte* pData[6])
{
AZ_TRACE_METHOD();
#if !defined(MULTITHREADED_RESOURCE_CREATION)
if (IsRenderThread())
#endif
{
return pTex->RT_CreateDeviceTexture(pData);
}
#if !defined(MULTITHREADED_RESOURCE_CREATION)
if (pTex->IsAsyncDevTexCreation())
{
return !IsFailed();
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateDeviceTexture, 7 * sizeof(void*));
AddPointer(p, pTex);
for (int i = 0; i < 6; i++)
{
AddPointer(p, pData[i]);
}
EndCommand(p);
FlushAndWait();
return !IsFailed();
#endif
}
void SRenderThread::RC_CopyDataToTexture(
void* pkVoid, unsigned int uiStartMip, unsigned int uiEndMip)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
CTexture* pkTexture = (CTexture*) pkVoid;
pkTexture->StreamCopyMipsTexToMem(uiStartMip, uiEndMip, true, NULL);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CopyDataToTexture, 8 + sizeof(void*));
AddPointer(p, pkVoid);
AddDWORD(p, uiStartMip);
AddDWORD(p, uiEndMip);
EndCommand(p);
// -- kenzo: removing this causes crashes because the texture
// might have already been destroyed. This needs to be fixed
// somehow that the createtexture doesn't require the renderthread
// (PC only issue)
FlushAndWait();
}
void SRenderThread::RC_ClearTarget(void* pkVoid, const ColorF& kColor)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
CTexture* pkTexture = (CTexture*) pkVoid;
gRenDev->RT_ClearTarget(pkTexture, kColor);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ClearTarget, sizeof(void*) + sizeof(ColorF));
AddPointer(p, pkVoid);
AddColor(p, kColor);
EndCommand(p);
FlushAndWait();
}
void SRenderThread::RC_CreateResource(SResourceAsync* pRes)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_CreateResource(pRes);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateResource, sizeof(void*));
AddPointer(p, pRes);
EndCommand(p);
}
void SRenderThread::RC_StartVideoThread()
{
AZ_TRACE_METHOD();
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_StartVideoThread, 0);
EndCommand(p);
}
void SRenderThread::RC_StopVideoThread()
{
AZ_TRACE_METHOD();
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_StopVideoThread, 0);
EndCommand(p);
}
void SRenderThread::RC_PreactivateShaders()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
CHWShader::RT_PreactivateShaders();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PreactivateShaders, 0);
EndCommand(p);
}
void SRenderThread::RC_PrecacheShader(CShader* pShader, SShaderCombination& cmb, bool bForce, bool bCompressedOnly, CShaderResources* pRes)
{
AZ_TRACE_METHOD();
if (IsRenderLoadingThread())
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_PrecacheShader, sizeof(void*) * 2 + 8 + sizeof(SShaderCombination), m_CommandsLoading);
pShader->AddRef();
if (pRes)
{
pRes->AddRef();
}
AddPointer(p, pShader);
memcpy(p, &cmb, sizeof(cmb));
p += sizeof(SShaderCombination);
AddDWORD(p, bForce);
AddDWORD(p, bCompressedOnly);
AddPointer(p, pRes);
EndCommandTo(p, m_CommandsLoading);
}
else if (IsRenderThread())
{
pShader->mfPrecache(cmb, bForce, bCompressedOnly, pRes);
return;
}
else
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PrecacheShader, sizeof(void*) * 2 + 8 + sizeof(SShaderCombination));
pShader->AddRef();
if (pRes)
{
pRes->AddRef();
}
AddPointer(p, pShader);
memcpy(p, &cmb, sizeof(cmb));
p += sizeof(SShaderCombination);
AddDWORD(p, bForce);
AddDWORD(p, bCompressedOnly);
AddPointer(p, pRes);
EndCommand(p);
}
}
void SRenderThread::RC_ReleaseBaseResource(CBaseResource* pRes)
{
AZ_TRACE_METHOD();
if (IsRenderLoadingThread())
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_ReleaseBaseResource, sizeof(void*), m_CommandsLoading);
AddPointer(p, pRes);
EndCommandTo(p, m_CommandsLoading);
}
else if (IsRenderThread())
{
SAFE_DELETE(pRes);
return;
}
else
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseBaseResource, sizeof(void*));
AddPointer(p, pRes);
EndCommand(p);
}
}
void SRenderThread::RC_ReleaseFont(IFFont* font)
{
AZ_TRACE_METHOD();
if (IsRenderLoadingThread())
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_ReleaseFont, sizeof(void*), m_CommandsLoading);
AddPointer(p, font);
EndCommandTo(p, m_CommandsLoading);
}
else if (IsRenderThread())
{
SAFE_DELETE(font);
return;
}
else
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseFont, sizeof(void*));
AddPointer(p, font);
EndCommand(p);
}
}
void SRenderThread::RC_ReleaseSurfaceResource(SDepthTexture* pRes)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
if (pRes)
{
pRes->Release(true);
}
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseSurfaceResource, sizeof(void*));
AddPointer(p, pRes);
EndCommand(p);
}
void SRenderThread::RC_ReleaseResource(SResourceAsync* pRes)
{
RC_ReleaseResource(AZStd::unique_ptr<SResourceAsync>(pRes));
}
void SRenderThread::RC_ReleaseResource(AZStd::unique_ptr<SResourceAsync> pRes)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ReleaseResource(pRes.release());
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseResource, sizeof(void*));
// Move owner over the SResourceAsync over to the renderer command queue
AddPointer(p, pRes.release());
EndCommand(p);
}
void SRenderThread::RC_UnbindTMUs()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_UnbindTMUs();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_UnbindTMUs, 0);
EndCommand(p);
}
void SRenderThread::RC_UnbindResources()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_UnbindResources();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_UnbindResources, 0);
EndCommand(p);
}
void SRenderThread::RC_ReleaseRenderResources()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ReleaseRenderResources();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseRenderResources, 0);
EndCommand(p);
}
void SRenderThread::RC_CreateRenderResources()
{
LOADING_TIME_PROFILE_SECTION;
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_CreateRenderResources();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateRenderResources, 0);
EndCommand(p);
}
void SRenderThread::RC_CreateSystemTargets()
{
LOADING_TIME_PROFILE_SECTION;
AZ_TRACE_METHOD();
if (IsRenderThread())
{
CTexture::CreateSystemTargets();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateSystemTargets, 0);
EndCommand(p);
}
void SRenderThread::RC_PrecacheDefaultShaders()
{
LOADING_TIME_PROFILE_SECTION;
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_PrecacheDefaultShaders();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PrecacheDefaultShaders, 0);
EndCommand(p);
}
void SRenderThread::RC_RelinkTexture(CTexture* pTex)
{
if (IsRenderThread(true))
{
pTex->RT_Relink();
return;
}
if (!IsMainThread(true))
{
AZStd::function<void()> runOnMainThread = [this, pTex]()
{
RC_RelinkTexture(pTex);
};
EBUS_QUEUE_FUNCTION(AZ::MainThreadRenderRequestBus, runOnMainThread);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_RelinkTexture, sizeof(void*));
AddPointer(p, pTex);
EndCommand(p);
}
void SRenderThread::RC_UnlinkTexture(CTexture* pTex)
{
if (IsRenderThread())
{
pTex->RT_Unlink();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_UnlinkTexture, sizeof(void*));
AddPointer(p, pTex);
EndCommand(p);
}
void SRenderThread::RC_CreateREPostProcess(CRendElementBase** re)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return gRenDev->RT_CreateREPostProcess(re);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CreateREPostProcess, sizeof(void*));
AddPointer(p, re);
EndCommand(p);
FlushAndWait();
}
bool SRenderThread::RC_CheckUpdate2(CRenderMesh* pMesh, CRenderMesh* pVContainer, uint32 nStreamMask)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return pMesh->RT_CheckUpdate(pVContainer, nStreamMask);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_UpdateMesh2, 8 + 2 * sizeof(void*));
AddPointer(p, pMesh);
AddPointer(p, pVContainer);
AddDWORD(p, nStreamMask);
EndCommand(p);
FlushAndWait();
return !IsFailed();
}
void SRenderThread::RC_ReleaseVB(buffer_handle_t nID)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->m_DevBufMan.Destroy(nID);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseVB, sizeof(buffer_handle_t));
AddDWORD64(p, nID);
EndCommand(p);
}
void SRenderThread::RC_ReleaseIB(buffer_handle_t nID)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->m_DevBufMan.Destroy(nID);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseIB, sizeof(buffer_handle_t));
AddDWORD64(p, nID);
EndCommand(p);
}
void SRenderThread::RC_DrawDynVB(SVF_P3F_C4B_T2F* pBuf, uint16* pInds, int nVerts, int nInds, const PublicRenderPrimitiveType nPrimType)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_DrawDynVB(pBuf, pInds, nVerts, nInds, nPrimType);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DrawDynVB, Align4(20 + sizeof(SVF_P3F_C4B_T2F) * nVerts + sizeof(uint16) * nInds));
AddData(p, pBuf, sizeof(SVF_P3F_C4B_T2F) * nVerts);
AddData(p, pInds, sizeof(uint16) * nInds);
AddDWORD(p, nVerts);
AddDWORD(p, nInds);
AddDWORD(p, (int)nPrimType);
EndCommand(p);
}
void SRenderThread::RC_DrawDynUiPrimitiveList(IRenderer::DynUiPrimitiveList& primitives, int totalNumVertices, int totalNumIndices)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
// When this is called on the render thread we do not currently combine the draw calls since we would
// have to allocate a new buffer to do so using RT_DrawDynVBUI.
// We could avoid the allocate by having a RT_DrawDynUiPrimitiveList which was only used
// when RC_DrawDynUiPrimitiveList is called on the render thread. It would have to do some
// fancy stuff with TempDynVB. Currently we are optimizing the case where there is a separate
// render thread so this is not a priority.
for (const IRenderer::DynUiPrimitive& primitive : primitives)
{
gRenDev->RT_DrawDynVBUI(primitive.m_vertices, primitive.m_indices, primitive.m_numVertices, primitive.m_numIndices, prtTriangleList);
}
return;
}
size_t vertsSizeInBytes = Align4(sizeof(SVF_P2F_C4B_T2F_F4B) * totalNumVertices);
size_t indsSizeInBytes = Align4(sizeof(uint16) * totalNumIndices);
LOADINGLOCK_COMMANDQUEUE
const size_t fixedCommandSize = 5 * sizeof(uint32); // accounts for the 5 calls to AddDWORD below
byte* p = AddCommand(eRC_DrawDynVBUI, fixedCommandSize + vertsSizeInBytes + indsSizeInBytes);
// we can't use AddPtr for each primitive since that adds a length then memcpy's the pointer
// we want all the vertices added to the queue as one length plus one data chunk.
// Same for indices.
// We know SVF_P2F_C4B_T2F_F4B is a multiple of 4 bytes so no padding needed
AddDWORD(p, vertsSizeInBytes);
for (const IRenderer::DynUiPrimitive& primitive : primitives)
{
memcpy(p, primitive.m_vertices, sizeof(SVF_P2F_C4B_T2F_F4B) * primitive.m_numVertices);
p += sizeof(SVF_P2F_C4B_T2F_F4B) * primitive.m_numVertices;
}
AddDWORD(p, indsSizeInBytes);
// when copying the indicies we have to adjust them to be the correct index in the combined vertex buffer
uint16 vbOffset = 0;
for (const IRenderer::DynUiPrimitive& primitive : primitives)
{
uint16* pIndex = reinterpret_cast<uint16*>(p);
for (int i = 0; i < primitive.m_numIndices; ++i)
{
pIndex[i] = primitive.m_indices[i] + vbOffset;
}
p += sizeof(uint16) * primitive.m_numIndices;
vbOffset += primitive.m_numVertices;
}
// uint16 is not a multiple of 4 bytes so if there is an odd number of indices we need to pad
unsigned pad = indsSizeInBytes - sizeof(uint16) * totalNumIndices;
p += pad;
AddDWORD(p, totalNumVertices);
AddDWORD(p, totalNumIndices);
AddDWORD(p, (int)prtTriangleList);
EndCommand(p);
}
void SRenderThread::RC_Draw2dImageStretchMode(bool bStretch)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_Draw2dImageStretchMode(bStretch);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_Draw2dImageStretchMode, sizeof(DWORD));
AddDWORD(p, (DWORD)bStretch);
EndCommand(p);
}
void SRenderThread::RC_Draw2dImage(float xpos, float ypos, float w, float h, CTexture* pTexture, float s0, float t0, float s1, float t1, float angle, float r, float g, float b, float a, float z)
{
AZ_TRACE_METHOD();
DWORD col = D3DRGBA(r, g, b, a);
if (IsRenderThread())
{
// don't render using fixed function pipeline when video mode is active
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_Draw2dImage(xpos, ypos, w, h, pTexture, s0, t0, s1, t1, angle, col, z);
}
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_Draw2dImage, 44 + sizeof(void*));
AddFloat(p, xpos);
AddFloat(p, ypos);
AddFloat(p, w);
AddFloat(p, h);
AddPointer(p, pTexture);
AddFloat(p, s0);
AddFloat(p, t0);
AddFloat(p, s1);
AddFloat(p, t1);
AddFloat(p, angle);
AddDWORD(p, col);
AddFloat(p, z);
EndCommand(p);
}
void SRenderThread::RC_Push2dImage(float xpos, float ypos, float w, float h, CTexture* pTexture, float s0, float t0, float s1, float t1, float angle, float r, float g, float b, float a, float z, float stereoDepth)
{
AZ_TRACE_METHOD();
DWORD col = D3DRGBA(r, g, b, a);
if (IsRenderThread())
{
gRenDev->RT_Push2dImage(xpos, ypos, w, h, pTexture, s0, t0, s1, t1, angle, col, z, stereoDepth);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_Push2dImage, 48 + sizeof(void*));
AddFloat(p, xpos);
AddFloat(p, ypos);
AddFloat(p, w);
AddFloat(p, h);
AddPointer(p, pTexture);
AddFloat(p, s0);
AddFloat(p, t0);
AddFloat(p, s1);
AddFloat(p, t1);
AddFloat(p, angle);
AddDWORD(p, col);
AddFloat(p, z);
AddFloat(p, stereoDepth);
EndCommand(p);
}
void SRenderThread::RC_Draw2dImageList()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_Draw2dImageList();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_Draw2dImageList, 0);
EndCommand(p);
}
void SRenderThread::RC_DrawImageWithUV(float xpos, float ypos, float z, float w, float h, int textureid, float* s, float* t, float r, float g, float b, float a, bool filtered)
{
AZ_TRACE_METHOD();
DWORD col = D3DRGBA(r, g, b, a);
if (IsRenderThread())
{
gRenDev->RT_DrawImageWithUV(xpos, ypos, z, w, h, textureid, s, t, col, filtered);
return;
}
int i;
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DrawImageWithUV, 32 + 8 * 4);
AddFloat(p, xpos);
AddFloat(p, ypos);
AddFloat(p, z);
AddFloat(p, w);
AddFloat(p, h);
AddDWORD(p, textureid);
for (i = 0; i < 4; i++)
{
AddFloat(p, s[i]);
}
for (i = 0; i < 4; i++)
{
AddFloat(p, t[i]);
}
AddDWORD(p, col);
AddDWORD(p, filtered);
EndCommand(p);
}
void SRenderThread::RC_SetState(int State, int AlphaRef)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->FX_SetState(State, AlphaRef);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetState, 8);
AddDWORD(p, State);
AddDWORD(p, AlphaRef);
EndCommand(p);
}
void SRenderThread::RC_SetStencilState(int st, uint32 nStencRef, uint32 nStencMask, uint32 nStencWriteMask, bool bForceFullReadMask)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->FX_SetStencilState(st, nStencRef, nStencMask, nStencWriteMask, bForceFullReadMask);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetStencilState, 20);
AddDWORD(p, st);
AddDWORD(p, nStencRef);
AddDWORD(p, nStencMask);
AddDWORD(p, nStencWriteMask);
AddDWORD(p, bForceFullReadMask);
EndCommand(p);
}
void SRenderThread::RC_SetColorOp(byte eCo, byte eAo, byte eCa, byte eAa)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->EF_SetColorOp(eCo, eAo, eCa, eAa);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetColorOp, 16);
AddDWORD(p, eCo);
AddDWORD(p, eAo);
AddDWORD(p, eCa);
AddDWORD(p, eAa);
EndCommand(p);
}
void SRenderThread::RC_SetSrgbWrite(bool srgbWrite)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->EF_SetSrgbWrite(srgbWrite);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetSrgbWrite, 4);
AddDWORD(p, srgbWrite);
EndCommand(p);
}
void SRenderThread::RC_PushWireframeMode(int nMode)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->FX_PushWireframeMode(nMode);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushWireframeMode, 4);
AddDWORD(p, nMode);
EndCommand(p);
}
void SRenderThread::RC_PopWireframeMode()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->FX_PopWireframeMode();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PopWireframeMode, 0);
EndCommand(p);
}
void SRenderThread::RC_SetCull(int nMode)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_SetCull(nMode);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetCull, 4);
AddDWORD(p, nMode);
EndCommand(p);
}
void SRenderThread::RC_SetScissor(bool bEnable, int sX, int sY, int sWdt, int sHgt)
{
if (IsRenderThread())
{
gRenDev->RT_SetScissor(bEnable, sX, sY, sWdt, sHgt);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetScissor, sizeof(DWORD) * 5);
AddDWORD(p, bEnable);
AddDWORD(p, sX);
AddDWORD(p, sY);
AddDWORD(p, sWdt);
AddDWORD(p, sHgt);
EndCommand(p);
}
void SRenderThread::RC_PushProfileMarker(const char* label)
{
AZ_TRACE_METHOD();
if (IsRenderLoadingThread())
{
return;
}
if (IsRenderThread())
{
gRenDev->SetProfileMarker(label, CRenderer::ESPM_PUSH);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushProfileMarker, sizeof(void*));
AddPointer(p, label);
EndCommand(p);
}
void SRenderThread::RC_PopProfileMarker(const char* label)
{
AZ_TRACE_METHOD();
if (IsRenderLoadingThread())
{
return;
}
if (IsRenderThread())
{
gRenDev->SetProfileMarker(label, CRenderer::ESPM_POP);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PopProfileMarker, sizeof(void*));
AddPointer(p, label);
EndCommand(p);
}
void SRenderThread::RC_ReadFrameBuffer(unsigned char* pRGB, int nImageX, int nSizeX, int nSizeY, ERB_Type eRBType, bool bRGBA, int nScaledX, int nScaledY)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ReadFrameBuffer(pRGB, nImageX, nSizeX, nSizeY, eRBType, bRGBA, nScaledX, nScaledY);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReadFrameBuffer, 28 + sizeof(void*));
AddPointer(p, pRGB);
AddDWORD(p, nImageX);
AddDWORD(p, nSizeX);
AddDWORD(p, nSizeY);
AddDWORD(p, eRBType);
AddDWORD(p, bRGBA);
AddDWORD(p, nScaledX);
AddDWORD(p, nScaledY);
EndCommand(p);
FlushAndWait();
}
void SRenderThread::RC_SetCamera()
{
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
size_t commandSize = sizeof(Matrix44) * 3 + sizeof(CameraViewParameters);
byte* pData = AddCommand(eRC_SetCamera, Align4(commandSize));
gRenDev->GetProjectionMatrix((float*)&pData[0]);
gRenDev->GetModelViewMatrix((float*)&pData[sizeof(Matrix44)]);
*(Matrix44*)((float*)&pData[sizeof(Matrix44) * 2]) = gRenDev->m_CameraZeroMatrix[m_nCurThreadFill];
*(CameraViewParameters*)(&pData[sizeof(Matrix44) * 3]) = gRenDev->GetViewParameters();
if (gRenDev->m_RP.m_TI[m_nCurThreadFill].m_PersFlags & RBPF_OBLIQUE_FRUSTUM_CLIPPING)
{
Matrix44A mObliqueProjMatrix;
mObliqueProjMatrix.SetIdentity();
Plane pPlane = gRenDev->m_RP.m_TI[m_nCurThreadFill].m_pObliqueClipPlane;
mObliqueProjMatrix.m02 = pPlane.n[0];
mObliqueProjMatrix.m12 = pPlane.n[1];
mObliqueProjMatrix.m22 = pPlane.n[2];
mObliqueProjMatrix.m32 = pPlane.d;
Matrix44* mProj = (Matrix44*)&pData[0];
*mProj = (*mProj) * mObliqueProjMatrix;
gRenDev->m_RP.m_TI[m_nCurThreadFill].m_PersFlags &= ~RBPF_OBLIQUE_FRUSTUM_CLIPPING;
}
pData += Align4(commandSize);
EndCommand(pData);
}
else
{
gRenDev->RT_SetCameraInfo();
}
}
void SRenderThread::RC_PostLoadLevel()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_PostLevelLoading();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PostLevelLoading, 0);
EndCommand(p);
}
void SRenderThread::RC_PushFog()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushFog, 0);
EndCommand(p);
}
else
{
gRenDev->EF_PushFog();
}
}
void SRenderThread::RC_PopFog()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PopFog, 0);
EndCommand(p);
}
else
{
gRenDev->EF_PopFog();
}
}
void SRenderThread::RC_PushVP()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushVP, 0);
EndCommand(p);
}
else
{
gRenDev->FX_PushVP();
}
}
void SRenderThread::RC_PopVP()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PopVP, 0);
EndCommand(p);
}
else
{
gRenDev->FX_PopVP();
}
}
void SRenderThread::RC_RenderTextMessages()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_RenderTextMessages, 0);
EndCommand(p);
}
else
{
gRenDev->RT_RenderTextMessages();
}
}
void SRenderThread::RC_FlushTextureStreaming(bool bAbort)
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_FlushTextureStreaming, sizeof(DWORD));
AddDWORD(p, bAbort);
EndCommand(p);
}
else
{
CTexture::RT_FlushStreaming(bAbort);
}
}
void SRenderThread::RC_ReleaseSystemTextures()
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseSystemTextures, 0);
EndCommand(p);
}
else
{
CTextureManager::Instance()->Release();
CTexture::ReleaseSystemTextures();
}
}
void SRenderThread::RC_SetEnvTexRT(SEnvTexture* pEnvTex, int nWidth, int nHeight, bool bPush)
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetEnvTexRT, 12 + sizeof(void*));
AddPointer(p, pEnvTex);
AddDWORD(p, nWidth);
AddDWORD(p, nHeight);
AddDWORD(p, bPush);
EndCommand(p);
}
else
{
pEnvTex->m_pTex->RT_SetRT(0, nWidth, nHeight, bPush);
}
}
void SRenderThread::RC_SetEnvTexMatrix(SEnvTexture* pEnvTex)
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetEnvTexMatrix, sizeof(void*));
AddPointer(p, pEnvTex);
EndCommand(p);
}
else
{
pEnvTex->RT_SetMatrix();
}
}
void SRenderThread::RC_PushRT(int nTarget, CTexture* pTex, SDepthTexture* pDS, int nS)
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushRT, 8 + 2 * sizeof(void*));
AddDWORD(p, nTarget);
AddPointer(p, pTex);
AddPointer(p, pDS);
AddDWORD(p, nS);
EndCommand(p);
}
else
{
gRenDev->RT_PushRenderTarget(nTarget, pTex, pDS, nS);
}
}
void SRenderThread::RC_PopRT(int nTarget)
{
AZ_TRACE_METHOD();
if (!IsRenderThread())
{
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PopRT, 4);
AddDWORD(p, nTarget);
EndCommand(p);
}
else
{
gRenDev->RT_PopRenderTarget(nTarget);
}
}
void SRenderThread::RC_ForceSwapBuffers()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_ForceSwapBuffers();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ForceSwapBuffers, 0);
EndCommand(p);
}
void SRenderThread::RC_SwitchToNativeResolutionBackbuffer()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_SwitchToNativeResolutionBackbuffer(true);
return;
}
else
{
gRenDev->SetViewport(0, 0, gRenDev->GetOverlayWidth(), gRenDev->GetOverlayHeight());
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SwitchToNativeResolutionBackbuffer, 0);
EndCommand(p);
}
void SRenderThread::RC_BeginFrame()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_BeginFrame();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_BeginFrame, 0);
EndCommand(p);
}
void SRenderThread::RC_EndFrame(bool bWait)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_EndFrame();
SyncMainWithRender();
return;
}
if (!bWait && CheckFlushCond())
{
return;
}
LOADINGLOCK_COMMANDQUEUE
gRenDev->GetIRenderAuxGeom()->Commit(); // need to issue flush of main thread's aux cb before EndFrame (otherwise it is processed after p3dDev->EndScene())
byte* p = AddCommand(eRC_EndFrame, 0);
EndCommand(p);
SyncMainWithRender();
}
void SRenderThread::RC_PrecacheResource(ITexture* pTP, float fMipFactor, float fTimeToReady, int Flags, int nUpdateId, int nCounter)
{
if (IsRenderThread())
{
gRenDev->PrecacheTexture(pTP, fMipFactor, fTimeToReady, Flags, nUpdateId, nCounter);
return;
}
if (pTP == NULL)
{
return;
}
pTP->AddRef();
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PrecacheTexture, 20 + sizeof(void*));
AddPointer(p, pTP);
AddFloat(p, fMipFactor);
AddFloat(p, fTimeToReady);
AddDWORD(p, Flags);
AddDWORD(p, nUpdateId);
AddDWORD(p, nCounter);
EndCommand(p);
}
void SRenderThread::RC_ReleaseDeviceTexture(CTexture* pTexture)
{
AZ_TRACE_METHOD();
if (!strcmp(pTexture->GetName(), "EngineAssets/TextureMsg/ReplaceMe.tif"))
{
pTexture = pTexture;
}
if (IsRenderThread())
{
CryOptionalAutoLock<CryMutex> lock(m_lockRenderLoading, m_eVideoThreadMode != eVTM_Disabled);
pTexture->RT_ReleaseDevice();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ReleaseDeviceTexture, sizeof(void*));
AddPointer(p, pTexture);
EndCommand(p);
FlushAndWait();
}
void SRenderThread::RC_TryFlush()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return;
}
// do nothing if the render thread is still busy
if (CheckFlushCond())
{
return;
}
LOADINGLOCK_COMMANDQUEUE
gRenDev->GetIRenderAuxGeom()->Flush(); // need to issue flush of main thread's aux cb before EndFrame (otherwise it is processed after p3dDev->EndScene())
SyncMainWithRender();
}
void SRenderThread::RC_DrawLines(Vec3 v[], int nump, ColorF& col, int flags, float fGround)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_DrawLines(v, nump, col, flags, fGround);
}
else
{
LOADINGLOCK_COMMANDQUEUE
// since we use AddData(...) - we need to allocate 4 bytes (DWORD) more because AddData(...) adds hidden data size into command buffer
byte* p = AddCommand(eRC_DrawLines, Align4(sizeof(int) + 2 * sizeof(int) + sizeof(float) + nump * sizeof(Vec3) + sizeof(ColorF)));
AddDWORD(p, nump);
AddColor(p, col);
AddDWORD(p, flags);
AddFloat(p, fGround);
AddData (p, v, nump * sizeof(Vec3));
EndCommand(p);
}
}
void SRenderThread::RC_DrawStringU(IFFont_RenderProxy* pFont, float x, float y, float z, const char* pStr, const bool asciiMultiLine, const STextDrawContext& ctx)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_DrawStringU(pFont, x, y, z, pStr, asciiMultiLine, ctx);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_DrawStringU, Align4(16 + sizeof(void*) + sizeof(STextDrawContext) + TextCommandSize(pStr)));
AddPointer(p, pFont);
AddFloat(p, x);
AddFloat(p, y);
AddFloat(p, z);
AddDWORD(p, asciiMultiLine ? 1 : 0);
new(p) STextDrawContext(ctx);
p += sizeof(STextDrawContext);
AddText(p, pStr);
EndCommand(p);
}
void SRenderThread::RC_ClearTargetsImmediately(int8 nType, uint32 nFlags, const ColorF& vColor, float depth)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
switch (nType)
{
case 0:
gRenDev->EF_ClearTargetsImmediately(nFlags);
return;
case 1:
gRenDev->EF_ClearTargetsImmediately(nFlags, vColor, depth, 0);
return;
case 2:
gRenDev->EF_ClearTargetsImmediately(nFlags, vColor);
return;
case 3:
gRenDev->EF_ClearTargetsImmediately(nFlags, depth, 0);
return;
}
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_ClearTargetsImmediately, Align4(12 + sizeof(ColorF)));
AddDWORD(p, nType);
AddDWORD(p, nFlags);
AddColor(p, vColor);
AddFloat(p, depth);
EndCommand(p);
}
void SRenderThread::RC_SetViewport(int x, int y, int width, int height, int id)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_SetViewport(x, y, width, height, id);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetViewport, 20);
AddDWORD(p, x);
AddDWORD(p, y);
AddDWORD(p, width);
AddDWORD(p, height);
AddDWORD(p, id);
EndCommand(p);
}
void SRenderThread::RC_RenderScene(int nFlags, RenderFunc pRenderFunc)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_RenderScene(nFlags, gRenDev->m_RP.m_TI[m_nCurThreadProcess], pRenderFunc);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_RenderScene, Align4(8 + sizeof(void*) + sizeof(SThreadInfo)));
AddDWORD(p, nFlags);
AddTI(p, gRenDev->m_RP.m_TI[m_nCurThreadFill]);
AddPointer(p, (void*)pRenderFunc);
AddDWORD(p, SRendItem::m_RecurseLevel[m_nCurThreadFill]);
EndCommand(p);
}
void SRenderThread::RC_PrepareStereo(int mode, int output)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_PrepareStereo(mode, output);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PrepareStereo, 8);
AddDWORD(p, mode);
AddDWORD(p, output);
EndCommand(p);
}
void SRenderThread::RC_CopyToStereoTex(int channel)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_CopyToStereoTex(channel);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CopyToStereoTex, 4);
AddDWORD(p, channel);
EndCommand(p);
}
void SRenderThread::RC_SetStereoEye(int eye)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->m_CurRenderEye = eye;
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetStereoEye, 4);
AddDWORD(p, eye);
EndCommand(p);
}
void SRenderThread::RC_AuxFlush([[maybe_unused]] IRenderAuxGeomImpl* pAux, [[maybe_unused]] SAuxGeomCBRawDataPackaged& data, [[maybe_unused]] size_t begin, [[maybe_unused]] size_t end, [[maybe_unused]] bool reset)
{
AZ_TRACE_METHOD();
#if defined(ENABLE_RENDER_AUX_GEOM)
if (IsRenderThread())
{
pAux->RT_Flush(data, begin, end);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_AuxFlush, 4 * sizeof(void*) + sizeof(uint32));
AddPointer(p, pAux);
AddPointer(p, data.m_pData);
AddPointer(p, (void*) begin);
AddPointer(p, (void*) end);
AddDWORD (p, (uint32) reset);
EndCommand(p);
#endif
}
void SRenderThread::RC_SetTexture(int nTex, int nUnit)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
CTexture::ApplyForID(nUnit, nTex, -1, -1);
return;
}
LOADINGLOCK_COMMANDQUEUE
int nState = CTexture::GetByID(nTex)->GetDefState();
byte* p = AddCommand(eRC_SetTexture, 12);
AddDWORD(p, nTex);
AddDWORD(p, nUnit);
AddDWORD(p, nState);
EndCommand(p);
}
bool SRenderThread::RC_OC_ReadResult_Try(uint32 nDefaultNumSamples, CREOcclusionQuery* pRE)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return pRE->RT_ReadResult_Try(nDefaultNumSamples);
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_OC_ReadResult_Try, 4 + sizeof(void*));
AddDWORD(p, (uint32)nDefaultNumSamples);
AddPointer(p, pRE);
EndCommand(p);
return true;
}
void SRenderThread::RC_CGCSetLayers(IColorGradingControllerInt* pController, const SColorChartLayer* pLayers, uint32 numLayers)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
pController->RT_SetLayers(pLayers, numLayers);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_CGCSetLayers, 4 + sizeof(void*));
AddPointer(p, pController);
AddDWORD(p, (uint32)numLayers);
EndCommand(p);
if (numLayers)
{
const size_t copySize = sizeof(SColorChartLayer) * numLayers;
SColorChartLayer* pLayersDst = (SColorChartLayer*) m_Commands[m_nCurThreadFill].Grow(copySize);
memcpy(pLayersDst, pLayers, copySize);
}
}
void SRenderThread::RC_GenerateSkyDomeTextures(CREHDRSky* pSky, int32 width, int32 height)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
pSky->GenerateSkyDomeTextures(width, height);
return;
}
LOADINGLOCK_COMMANDQUEUE
if (m_eVideoThreadMode == eVTM_Disabled)
{
byte* p = AddCommand(eRC_GenerateSkyDomeTextures, sizeof(void*) + sizeof(int32) * 2);
AddPointer(p, pSky);
AddDWORD(p, width);
AddDWORD(p, height);
EndCommand(p);
}
else
{
// Move command into loading queue, which will be executed in first render frame after loading is done
byte* p = AddCommandTo(eRC_GenerateSkyDomeTextures, sizeof(void*) + sizeof(int32) * 2, m_CommandsLoading);
AddPointer(p, pSky);
AddDWORD(p, width);
AddDWORD(p, height);
EndCommandTo(p, m_CommandsLoading);
}
}
void SRenderThread::RC_SetRendererCVar(ICVar* pCVar, const char* pArgText, const bool bSilentMode)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_SetRendererCVar(pCVar, pArgText, bSilentMode);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_SetRendererCVar, sizeof(void*) + TextCommandSize(pArgText) + 4);
AddPointer(p, pCVar);
AddText(p, pArgText);
AddDWORD(p, bSilentMode ? 1 : 0);
EndCommand(p);
}
void SRenderThread::RC_RenderDebug(bool bRenderStats)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_RenderDebug(bRenderStats);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_RenderDebug, 0);
EndCommand(p);
}
void SRenderThread::RC_PushSkinningPoolId(uint32 poolId)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_SetSkinningPoolId(poolId);
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_PushSkinningPoolId, 4);
AddDWORD(p, poolId);
EndCommand(p);
}
void SRenderThread::RC_ReleaseRemappedBoneIndices(IRenderMesh* pRenderMesh, uint32 guid)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
pRenderMesh->ReleaseRemappedBoneIndicesPair(guid);
return;
}
LOADINGLOCK_COMMANDQUEUE
pRenderMesh->AddRef(); // don't allow mesh deletion while this command is pending
byte* p = AddCommand(eRC_ReleaseRemappedBoneIndices, sizeof(void*) + 4);
AddPointer(p, pRenderMesh);
AddDWORD(p, guid);
EndCommand(p);
}
void SRenderThread::RC_InitializeVideoRenderer(AZ::VideoRenderer::IVideoRenderer* pVideoRenderer)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_InitializeVideoRenderer(pVideoRenderer);
return;
}
LOADINGLOCK_COMMANDQUEUE;
byte* p = AddCommand(eRC_InitializeVideoRenderer, sizeof(AZ::VideoRenderer::IVideoRenderer*));
AddPointer(p, pVideoRenderer);
EndCommand(p);
// We want to block until the resources have been created.
SyncMainWithRender();
}
void SRenderThread::RC_CleanupVideoRenderer(AZ::VideoRenderer::IVideoRenderer* pVideoRenderer)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_CleanupVideoRenderer(pVideoRenderer);
return;
}
LOADINGLOCK_COMMANDQUEUE;
byte* p = AddCommand(eRC_CleanupVideoRenderer, sizeof(AZ::VideoRenderer::IVideoRenderer*));
AddPointer(p, pVideoRenderer);
EndCommand(p);
// We want to block until the cleanup is complete.
SyncMainWithRender();
}
void SRenderThread::RC_DrawVideoRenderer(AZ::VideoRenderer::IVideoRenderer* pVideoRenderer, const AZ::VideoRenderer::DrawArguments& drawArguments)
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
gRenDev->RT_DrawVideoRenderer(pVideoRenderer, drawArguments);
return;
}
LOADINGLOCK_COMMANDQUEUE;
byte* p = AddCommand(eRC_DrawVideoRenderer, sizeof(AZ::VideoRenderer::IVideoRenderer*) + sizeof(AZ::VideoRenderer::DrawArguments));
AddPointer(p, pVideoRenderer);
memcpy(p, &drawArguments, sizeof(AZ::VideoRenderer::DrawArguments));
p += sizeof(AZ::VideoRenderer::DrawArguments);
EndCommand(p);
}
void SRenderThread::EnqueueRenderCommand(RenderCommandCB command)
{
if (IsRenderThread())
{
command();
return;
}
LOADINGLOCK_COMMANDQUEUE
byte* p = AddCommand(eRC_AzFunction, sizeof(RenderCommandCB));
new (p) RenderCommandCB(AZStd::move(command));
p += sizeof(RenderCommandCB);
EndCommand(p);
}
//===========================================================================================
#ifdef DO_RENDERSTATS
#define START_PROFILE_RT Time = iTimer->GetAsyncTime();
#define END_PROFILE_PLUS_RT(Dst) Dst += iTimer->GetAsyncTime().GetDifferenceInSeconds(Time);
#define END_PROFILE_RT(Dst) Dst = iTimer->GetAsyncTime().GetDifferenceInSeconds(Time);
#else
#define START_PROFILE_RT
#define END_PROFILE_PLUS_RT(Dst)
#define END_PROFILE_RT(Dst)
#endif
#if defined(AZ_PROFILE_TELEMETRY)
static const char* GetRenderCommandName(ERenderCommand renderCommand)
{
switch (renderCommand)
{
// Please add to this list if you have a case that needs watching
case eRC_PreloadTextures:
return "PreloadTextures";
case eRC_ParseShader:
return "ParseShader";
case eRC_RenderScene:
return "RenderScene";
case eRC_AzFunction:
return "AzFunction";
}
return "<unknown>";
}
#endif
void SRenderThread::ProcessCommands(bool loadTimeProcessing)
{
AZ_TRACE_METHOD();
#ifndef STRIP_RENDER_THREAD
assert (IsRenderThread());
if (!CheckFlushCond())
{
return;
}
#if !defined (NULL_RENDERER)
DWORD nDeviceOwningThreadID = gcpRendD3D->GetBoundThreadID();
if (m_eVideoThreadMode == eVTM_Disabled)
{
gcpRendD3D->BindContextToThread(CryGetCurrentThreadId());
}
# endif
#if defined(OPENGL) && !DXGL_FULL_EMULATION && !defined(CRY_USE_METAL)
if (CRenderer::CV_r_multithreaded)
{
m_kDXGLContextHandle.Set(&gcpRendD3D->GetDevice());
}
if (m_eVideoThreadMode == eVTM_Disabled)
{
m_kDXGLDeviceContextHandle.Set(&gcpRendD3D->GetDeviceContext(), !CRenderer::CV_r_multithreaded);
}
#endif //defined(OPENGL) && !DXGL_FULL_EMULATION
#ifdef DO_RENDERSTATS
CTimeValue Time;
#endif
int threadId = m_nCurThreadProcess;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_5
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
int n = 0;
m_bSuccessful = true;
m_hResult = S_OK;
byte* pP;
while (n < (int)m_Commands[threadId].Num())
{
pP = &m_Commands[threadId][n];
n += sizeof(int);
byte nC = (byte) * ((int*)pP);
#if !defined(_RELEASE)
// Ensure that the command hasn't been processed already
int* pProcessed = (int*)(pP + sizeof(int));
IF_UNLIKELY (*pProcessed)
{
__debugbreak();
}
*pProcessed = 1;
n += sizeof(int);
#endif
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::RendererDetailed, "SRenderThread::ProcessCommands::Command: %s (%d)", GetRenderCommandName(static_cast<ERenderCommand>(nC)), nC);
switch (nC)
{
case eRC_CreateDevice:
m_bSuccessful &= gRenDev->RT_CreateDevice();
break;
case eRC_ResetDevice:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_Reset();
}
break;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_6
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
case eRC_ReleasePostEffects:
if (gRenDev->m_pPostProcessMgr)
{
gRenDev->m_pPostProcessMgr->ReleaseResources();
}
break;
case eRC_ResetPostEffects:
if (gRenDev->m_RP.m_pREPostProcess)
{
gRenDev->m_RP.m_pREPostProcess->mfReset();
}
break;
case eRC_ResetPostEffectsOnSpecChange:
if (gRenDev->m_RP.m_pREPostProcess)
{
gRenDev->m_RP.m_pREPostProcess->Reset(true);
}
break;
case eRC_DisableTemporalEffects:
gRenDev->RT_DisableTemporalEffects();
break;
case eRC_ResetGlass:
gRenDev->RT_ResetGlass();
break;
case eRC_ResetToDefault:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->ResetToDefault();
}
break;
case eRC_Init:
gRenDev->RT_Init();
break;
case eRC_ShutDown:
{
uint32 nFlags = ReadCommand<uint32>(n);
gRenDev->RT_ShutDown(nFlags);
}
break;
case eRC_ForceSwapBuffers:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_ForceSwapBuffers();
}
break;
case eRC_SwitchToNativeResolutionBackbuffer:
{
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_SwitchToNativeResolutionBackbuffer(true);
}
}
break;
case eRC_BeginFrame:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_BeginFrame();
}
else
{
m_bBeginFrameCalled = true;
}
break;
case eRC_EndFrame:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_EndFrame();
}
else
{
// RLT handles precache commands - so all texture streaming prioritisation
// needs to happen here. Scheduling and device texture management will happen
// on the RT later.
CTexture::RLT_LoadingUpdate();
m_bEndFrameCalled = true;
gRenDev->m_nFrameSwapID++;
}
break;
case eRC_PreloadTextures:
CTexture::RT_Precache();
break;
case eRC_PrecacheTexture:
{
ITexture* pTP = ReadCommand<ITexture*>(n);
float fMipFactor = ReadCommand<float>(n);
float fTimeToReady = ReadCommand<float>(n);
int Flags = ReadCommand<int>(n);
int nUpdateId = ReadCommand<int>(n);
int nCounter = ReadCommand<int>(n);
gRenDev->PrecacheTexture(pTP, fMipFactor, fTimeToReady, Flags, nUpdateId, nCounter);
pTP->Release();
}
break;
case eRC_ClearTargetsImmediately:
{
uint32 nType = ReadCommand<uint32>(n);
uint32 nFlags = ReadCommand<uint32>(n);
ColorF vColor = ReadCommand<ColorF>(n);
float fDepth = ReadCommand<float>(n);
if (m_eVideoThreadMode != eVTM_Disabled)
{
nFlags &= ~FRT_CLEAR_IMMEDIATE;
switch (nType)
{
case 0:
gRenDev->EF_ClearTargetsLater(nFlags);
break;
case 1:
gRenDev->EF_ClearTargetsLater(nFlags, vColor, fDepth, 0);
break;
case 2:
gRenDev->EF_ClearTargetsLater(nFlags, vColor);
break;
case 3:
gRenDev->EF_ClearTargetsLater(nFlags, fDepth, 0);
break;
}
}
switch (nType)
{
case 0:
gRenDev->EF_ClearTargetsImmediately(nFlags);
break;
case 1:
gRenDev->EF_ClearTargetsImmediately(nFlags, vColor, fDepth, 0);
break;
case 2:
gRenDev->EF_ClearTargetsImmediately(nFlags, vColor);
break;
case 3:
gRenDev->EF_ClearTargetsImmediately(nFlags, fDepth, 0);
break;
}
}
break;
case eRC_ReadFrameBuffer:
{
byte* pRGB = ReadCommand<byte*>(n);
int nImageX = ReadCommand<int>(n);
int nSizeX = ReadCommand<int>(n);
int nSizeY = ReadCommand<int>(n);
ERB_Type RBType = ReadCommand<ERB_Type>(n);
int bRGBA = ReadCommand<int>(n);
int nScaledX = ReadCommand<int>(n);
int nScaledY = ReadCommand<int>(n);
gRenDev->RT_ReadFrameBuffer(pRGB, nImageX, nSizeX, nSizeY, RBType, bRGBA, nScaledX, nScaledY);
}
break;
case eRC_UpdateShaderItem:
{
SShaderItem* pShaderItem = ReadCommand<SShaderItem*>(n);
// The material is necessary at this point because an UpdateShaderItem may have been queued
// for a material that was subsequently released and would have been deleted, thus resulting in a
// dangling pointer and a crash; this keeps it alive until this render command can complete
IMaterial* pMaterial = ReadCommand<IMaterial*>(n);
gRenDev->RT_UpdateShaderItem(pShaderItem, pMaterial);
if (pMaterial)
{
// Release the reference we added when we submitted the command.
pMaterial->Release();
}
}
break;
case eRC_RefreshShaderResourceConstants:
{
SShaderItem* shaderItem = ReadCommand<SShaderItem*>(n);
// The material is necessary at this point because an RefreshShaderResourceConstants may have been queued
// for a material that was subsequently released and would have been deleted, thus resulting in a
// dangling pointer and a crash; this keeps it alive until this render command can complete
IMaterial* material = ReadCommand<IMaterial*>(n);
gRenDev->RT_RefreshShaderResourceConstants(shaderItem);
if (material)
{
// Release the reference we added when we submitted the command.
material->Release();
}
}
break;
case eRC_ReleaseDeviceTexture:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
CTexture* pTexture = ReadCommand<CTexture*>(n);
pTexture->RT_ReleaseDevice();
}
break;
case eRC_AuxFlush:
{
#if defined(ENABLE_RENDER_AUX_GEOM)
IRenderAuxGeomImpl* pAux = ReadCommand<IRenderAuxGeomImpl*>(n);
CAuxGeomCB::SAuxGeomCBRawData* pData = ReadCommand<CAuxGeomCB::SAuxGeomCBRawData*>(n);
size_t begin = ReadCommand<size_t>(n);
size_t end = ReadCommand<size_t>(n);
bool reset = ReadCommand<uint32>(n) != 0;
if (m_eVideoThreadMode == eVTM_Disabled)
{
SAuxGeomCBRawDataPackaged data = SAuxGeomCBRawDataPackaged(pData);
pAux->RT_Flush(data, begin, end, reset);
}
#endif
}
break;
case eRC_SetTexture:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
int nTex = ReadCommand<int>(n);
int nUnit = ReadCommand<int>(n);
int nState = ReadCommand<int>(n);
CTexture::ApplyForID(nUnit, nTex, nState, -1);
}
break;
case eRC_DrawLines:
{
int nump = ReadCommand<int>(n);
ColorF col = ReadCommand<ColorF>(n);
int flags = ReadCommand<int>(n);
float fGround = ReadCommand<float>(n);
Vec3* pv = (Vec3*)&m_Commands[threadId][n += 4];
n += nump * sizeof(Vec3);
gRenDev->RT_DrawLines(pv, nump, col, flags, fGround);
}
break;
case eRC_DrawStringU:
{
IFFont_RenderProxy* pFont = ReadCommand<IFFont_RenderProxy*>(n);
float x = ReadCommand<float>(n);
float y = ReadCommand<float>(n);
float z = ReadCommand<float>(n);
bool asciiMultiLine = ReadCommand<int>(n) != 0;
const STextDrawContext* pCtx = (const STextDrawContext*) &m_Commands[threadId][n];
n += sizeof(STextDrawContext);
const char* pStr = ReadTextCommand<const char*>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_DrawStringU(pFont, x, y, z, pStr, asciiMultiLine, *pCtx);
}
}
break;
case eRC_SetState:
{
int nState = ReadCommand<int>(n);
int nAlphaRef = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->FX_SetState(nState, nAlphaRef);
}
}
break;
case eRC_PushWireframeMode:
{
int nMode = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->FX_PushWireframeMode(nMode);
}
}
break;
case eRC_PopWireframeMode:
{
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->FX_PopWireframeMode();
}
}
break;
case eRC_SetCull:
{
int nMode = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_SetCull(nMode); // nMode
}
}
break;
case eRC_SetScissor:
{
bool bEnable = ReadCommand<DWORD>(n) != 0;
int sX = (int)ReadCommand<DWORD>(n);
int sY = (int)ReadCommand<DWORD>(n);
int sWdt = (int)ReadCommand<DWORD>(n);
int sHgt = (int)ReadCommand<DWORD>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_SetScissor(bEnable, sX, sY, sWdt, sHgt);
}
}
break;
case eRC_SetStencilState:
{
int st = ReadCommand<DWORD>(n);
uint32 nStencRef = ReadCommand<DWORD>(n);
uint32 nStencMask = ReadCommand<DWORD>(n);
uint32 nStencWriteMask = ReadCommand<DWORD>(n);
bool bForceFullReadMask = ReadCommand<DWORD>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->FX_SetStencilState(st, nStencRef, nStencMask, nStencWriteMask, bForceFullReadMask);
}
}
break;
case eRC_UpdateTexture:
{
CTexture* pTexture = ReadCommand<CTexture*>(n);
byte* pData = ReadCommand<byte*>(n);
int nX = ReadCommand<int>(n);
int nY = ReadCommand<int>(n);
int nZ = ReadCommand<int>(n);
int nUSize = ReadCommand<int>(n);
int nVSize = ReadCommand<int>(n);
int nZSize = ReadCommand<int>(n);
ETEX_Format eTFSrc = (ETEX_Format)ReadCommand<uint32>(n);
pTexture->RT_UpdateTextureRegion(pData, nX, nY, nZ, nUSize, nVSize, nZSize, eTFSrc);
delete [] pData;
pTexture->Release();
}
break;
case eRC_CreateResource:
{
SResourceAsync* pRA = ReadCommand<SResourceAsync*>(n);
gRenDev->RT_CreateResource(pRA);
}
break;
case eRC_ReleaseResource:
{
SResourceAsync* pRes = ReadCommand<SResourceAsync*>(n);
gRenDev->RT_ReleaseResource(pRes);
}
break;
case eRC_ReleaseRenderResources:
gRenDev->RT_ReleaseRenderResources();
break;
case eRC_UnbindTMUs:
gRenDev->RT_UnbindTMUs();
break;
case eRC_UnbindResources:
gRenDev->RT_UnbindResources();
break;
case eRC_CreateRenderResources:
gRenDev->RT_CreateRenderResources();
break;
case eRC_CreateSystemTargets:
CTexture::CreateSystemTargets();
break;
case eRC_PrecacheDefaultShaders:
gRenDev->RT_PrecacheDefaultShaders();
break;
case eRC_ReleaseSurfaceResource:
{
SDepthTexture* pRes = ReadCommand<SDepthTexture*>(n);
if (pRes)
{
pRes->Release(true);
}
}
break;
case eRC_ReleaseBaseResource:
{
CBaseResource* pRes = ReadCommand<CBaseResource*>(n);
RC_ReleaseBaseResource(pRes);
}
break;
case eRC_ReleaseFont:
{
IFFont* font = ReadCommand<IFFont*>(n);
RC_ReleaseFont(font);
}
break;
case eRC_UpdateMesh2:
assert(m_eVideoThreadMode == eVTM_Disabled);
{
CRenderMesh* pMesh = ReadCommand<CRenderMesh*>(n);
CRenderMesh* pVContainer = ReadCommand<CRenderMesh*>(n);
uint32 nStreamMask = ReadCommand<uint32>(n);
pMesh->RT_CheckUpdate(pVContainer, nStreamMask);
}
break;
case eRC_CreateDeviceTexture:
{
CryOptionalAutoLock<CryMutex> lock(m_lockRenderLoading, loadTimeProcessing);
CTexture* pTex = ReadCommand<CTexture*>(n);
const byte* pData[6];
for (int i = 0; i < 6; i++)
{
pData[i] = ReadCommand<byte*>(n);
}
m_bSuccessful = pTex->RT_CreateDeviceTexture(pData);
}
break;
case eRC_CopyDataToTexture:
{
void* pkTexture = ReadCommand<void*>(n);
unsigned int uiStartMip = ReadCommand<unsigned int>(n);
unsigned int uiEndMip = ReadCommand<unsigned int>(n);
RC_CopyDataToTexture(pkTexture, uiStartMip, uiEndMip);
}
break;
case eRC_ClearTarget:
{
void* pkTexture = ReadCommand<void*>(n);
ColorF kColor = ReadCommand<ColorF>(n);
RC_ClearTarget(pkTexture, kColor);
}
break;
case eRC_CreateREPostProcess:
{
CRendElementBase** pRE = ReadCommand<CRendElementBase**>(n);
gRenDev->RT_CreateREPostProcess(pRE);
}
break;
case eRC_DrawDynVB:
{
pP = &m_Commands[threadId][0];
uint32 nSize = *(uint32*)&pP[n];
SVF_P3F_C4B_T2F* pBuf = (SVF_P3F_C4B_T2F*)&pP[n + 4];
n += nSize + 4;
nSize = *(uint32*)&pP[n];
uint16* pInds = (nSize > 0) ? (uint16*)&pP[n + 4] : nullptr;
n += nSize + 4;
int nVerts = ReadCommand<int>(n);
int nInds = ReadCommand<int>(n);
const PublicRenderPrimitiveType nPrimType = (PublicRenderPrimitiveType)ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_DrawDynVB(pBuf, pInds, nVerts, nInds, nPrimType);
}
}
break;
case eRC_Draw2dImage:
{
START_PROFILE_RT;
float xpos = ReadCommand<float>(n);
float ypos = ReadCommand<float>(n);
float w = ReadCommand<float>(n);
float h = ReadCommand<float>(n);
CTexture* pTexture = ReadCommand<CTexture*>(n);
float s0 = ReadCommand<float>(n);
float t0 = ReadCommand<float>(n);
float s1 = ReadCommand<float>(n);
float t1 = ReadCommand<float>(n);
float angle = ReadCommand<float>(n);
int col = ReadCommand<int>(n);
float z = ReadCommand<float>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_Draw2dImage(xpos, ypos, w, h, pTexture, s0, t0, s1, t1, angle, col, z);
}
END_PROFILE_PLUS_RT(gRenDev->m_fRTTimeMiscRender);
}
break;
case eRC_DrawDynVBUI:
{
pP = &m_Commands[threadId][0];
uint32 nSize = *(uint32*)&pP[n];
SVF_P2F_C4B_T2F_F4B* pBuf = (SVF_P2F_C4B_T2F_F4B*)&pP[n + 4];
n += nSize + 4;
nSize = *(uint32*)&pP[n];
uint16* pInds = (nSize > 0) ? (uint16*)&pP[n + 4] : nullptr;
n += nSize + 4;
int nVerts = ReadCommand<int>(n);
int nInds = ReadCommand<int>(n);
const PublicRenderPrimitiveType nPrimType = (PublicRenderPrimitiveType)ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_DrawDynVBUI(pBuf, pInds, nVerts, nInds, nPrimType);
}
}
break;
case eRC_Draw2dImageStretchMode:
{
START_PROFILE_RT;
int mode = ReadCommand<int>(n);
gRenDev->RT_Draw2dImageStretchMode(mode);
END_PROFILE_PLUS_RT(gRenDev->m_fRTTimeMiscRender);
}
break;
case eRC_Push2dImage:
{
float xpos = ReadCommand<float>(n);
float ypos = ReadCommand<float>(n);
float w = ReadCommand<float>(n);
float h = ReadCommand<float>(n);
CTexture* pTexture = ReadCommand<CTexture*>(n);
float s0 = ReadCommand<float>(n);
float t0 = ReadCommand<float>(n);
float s1 = ReadCommand<float>(n);
float t1 = ReadCommand<float>(n);
float angle = ReadCommand<float>(n);
int col = ReadCommand<int>(n);
float z = ReadCommand<float>(n);
float stereoDepth = ReadCommand<float>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_Push2dImage(xpos, ypos, w, h, pTexture, s0, t0, s1, t1, angle, col, z, stereoDepth);
}
}
break;
case eRC_Draw2dImageList:
{
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_Draw2dImageList();
}
}
break;
case eRC_DrawImageWithUV:
if (m_eVideoThreadMode == eVTM_Disabled)
{
pP = &m_Commands[threadId][n];
gRenDev->RT_DrawImageWithUV(*(float*)pP, // xpos
*(float*)&pP[4], // ypos
*(float*)&pP[8], // z
*(float*)&pP[12], // w
*(float*)&pP[16], // h
*(int*)&pP[20], // textureid
(float*)&pP[24], // s
(float*)&pP[40], // t
*(int*)&pP[56], // col
*(int*)&pP[60] != 0); // filtered
}
n += 64;
break;
case eRC_PushProfileMarker:
{
char* label = ReadCommand<char*>(n);
gRenDev->PushProfileMarker(label);
}
break;
case eRC_PopProfileMarker:
{
char* label = ReadCommand<char*>(n);
gRenDev->PopProfileMarker(label);
}
break;
case eRC_SetCamera:
{
START_PROFILE_RT;
Matrix44A ProjMat = ReadCommand<Matrix44>(n);
Matrix44A ViewMat = ReadCommand<Matrix44>(n);
Matrix44A CameraZeroMat = ReadCommand<Matrix44>(n);
CameraViewParameters viewParameters = ReadCommand<CameraViewParameters>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->SetMatrices(ProjMat.GetData(), ViewMat.GetData());
gRenDev->m_CameraZeroMatrix[threadId] = CameraZeroMat;
gRenDev->SetViewParameters(viewParameters);
gRenDev->RT_SetCameraInfo();
}
END_PROFILE_PLUS_RT(gRenDev->m_fRTTimeMiscRender);
}
break;
case eRC_AzFunction:
{
// Lock only when processing on the RenderLoadThread - multiple AzFunctions make calls that cause crashes if invoked concurrently with render
CryOptionalAutoLock<CryMutex> lock(m_lockRenderLoading, loadTimeProcessing);
// We "build" the command from the buffer memory (instead of copying it)
RenderCommandCB* command = alias_cast<RenderCommandCB*>(reinterpret_cast<uint32*>(&m_Commands[threadId][n]));
(*command)();
// We need to destroy the object that we created using placement new.
command->~RenderCommandCB();
n += Align4(sizeof(RenderCommandCB));
}
break;
case eRC_ReleaseVBStream:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
void* pVB = ReadCommand<void*>(n);
int nStream = ReadCommand<int>(n);
gRenDev->RT_ReleaseVBStream(pVB, nStream);
}
break;
case eRC_ReleaseVB:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
buffer_handle_t nID = ReadCommand<buffer_handle_t>(n);
gRenDev->m_DevBufMan.Destroy(nID);
}
break;
case eRC_ReleaseIB:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
buffer_handle_t nID = ReadCommand<buffer_handle_t>(n);
gRenDev->m_DevBufMan.Destroy(nID);
}
break;
case eRC_RenderScene:
{
START_PROFILE_RT;
int nFlags = ReadCommand<int>(n);
SThreadInfo TI;
LoadUnaligned((uint32*)&m_Commands[threadId][n], TI);
n += sizeof(SThreadInfo);
RenderFunc pRenderFunc = ReadCommand<RenderFunc>(n);
int nR = ReadCommand<int>(n);
int nROld = SRendItem::m_RecurseLevel[threadId];
SRendItem::m_RecurseLevel[threadId] = nR;
// when we are in video mode, don't execute the command
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_RenderScene(nFlags, TI, pRenderFunc);
}
else
{
// cleanup when showing loading render screen
if (nR == 1)
{
////////////////////////////////////////////////
// wait till all SRendItems for this frame have finished preparing
gRenDev->GetFinalizeRendItemJobExecutor(gRenDev->m_RP.m_nProcessThreadID)->WaitForCompletion();
gRenDev->GetFinalizeShadowRendItemJobExecutor(gRenDev->m_RP.m_nProcessThreadID)->WaitForCompletion();
////////////////////////////////////////////////
// to non-thread safe remaing work for *::Render functions
CRenderMesh::FinalizeRendItems(gRenDev->m_RP.m_nProcessThreadID);
CMotionBlur::InsertNewElements();
FurBendData::Get().InsertNewElements();
}
}
SRendItem::m_RecurseLevel[threadId] = nROld;
END_PROFILE_PLUS_RT(gRenDev->m_fRTTimeSceneRender);
}
break;
case eRC_PrepareStereo:
{
int mode = ReadCommand<int>(n);
int output = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_PrepareStereo(mode, output);
}
}
break;
case eRC_CopyToStereoTex:
{
int channel = ReadCommand<int>(n);
gRenDev->RT_CopyToStereoTex(channel);
}
break;
case eRC_SetStereoEye:
{
int eye = ReadCommand<int>(n);
gRenDev->m_CurRenderEye = eye;
}
break;
case eRC_DynTexUpdate:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
SDynTexture* pTex = ReadCommand<SDynTexture*>(n);
int nNewWidth = ReadCommand<int>(n);
int nNewHeight = ReadCommand<int>(n);
pTex->RT_Update(nNewWidth, nNewHeight);
}
break;
case eRC_ParseShader:
{
CShader* pSH = ReadCommand<CShader*>(n);
CShaderResources* pRes = ReadCommand<CShaderResources*>(n);
uint64 nMaskGen = ReadCommand<uint64>(n);
uint32 nFlags = ReadCommand<uint32>(n);
gRenDev->m_cEF.RT_ParseShader(pSH, nMaskGen, nFlags, pRes);
pSH->Release();
if (pRes)
{
pRes->Release();
}
}
break;
case eRC_SetShaderQuality:
{
EShaderType eST = (EShaderType) ReadCommand<uint32>(n);
EShaderQuality eSQ = (EShaderQuality) ReadCommand<uint32>(n);
gRenDev->m_cEF.RT_SetShaderQuality(eST, eSQ);
}
break;
case eRC_PushFog:
gRenDev->EF_PushFog();
break;
case eRC_PopFog:
gRenDev->EF_PopFog();
break;
case eRC_PushVP:
gRenDev->FX_PushVP();
break;
case eRC_PopVP:
gRenDev->FX_PopVP();
break;
case eRC_RenderTextMessages:
gRenDev->RT_RenderTextMessages();
break;
case eRC_FlushTextureStreaming:
{
bool bAbort = ReadCommand<DWORD>(n) != 0;
CTexture::RT_FlushStreaming(bAbort);
}
break;
case eRC_ReleaseSystemTextures:
CTextureManager::Instance()->Release();
CTexture::ReleaseSystemTextures();
break;
case eRC_SetEnvTexRT:
{
SEnvTexture* pTex = ReadCommand<SEnvTexture*>(n);
int nWidth = ReadCommand<int>(n);
int nHeight = ReadCommand<int>(n);
int bPush = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
pTex->m_pTex->RT_SetRT(0, nWidth, nHeight, bPush);
}
}
break;
case eRC_SetEnvTexMatrix:
{
SEnvTexture* pTex = ReadCommand<SEnvTexture*>(n);
pTex->RT_SetMatrix();
}
break;
case eRC_PushRT:
assert (m_eVideoThreadMode == eVTM_Disabled);
{
int nTarget = ReadCommand<int>(n);
CTexture* pTex = ReadCommand<CTexture*>(n);
SDepthTexture* pDS = ReadCommand<SDepthTexture*>(n);
int nS = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_PushRenderTarget(nTarget, pTex, pDS, nS);
}
}
break;
case eRC_PopRT:
{
int nTarget = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_PopRenderTarget(nTarget);
}
}
break;
case eRC_EntityDelete:
{
IRenderNode* pRN = ReadCommand<IRenderNode*>(n);
SDynTexture_Shadow::RT_EntityDelete(pRN);
}
break;
case eRC_PreactivateShaders:
{
CHWShader::RT_PreactivateShaders();
}
break;
case eRC_PrecacheShader:
{
CShader* pShader = ReadCommand<CShader*>(n);
SShaderCombination cmb = ReadCommand<SShaderCombination>(n);
bool bForce = ReadCommand<DWORD>(n) != 0;
bool bCompressedOnly = ReadCommand<DWORD>(n) != 0;
CShaderResources* pRes = ReadCommand<CShaderResources*>(n);
pShader->mfPrecache(cmb, bForce, bCompressedOnly, pRes);
SAFE_RELEASE(pRes);
pShader->Release();
}
break;
case eRC_SetViewport:
{
int nX = ReadCommand<int>(n);
int nY = ReadCommand<int>(n);
int nWidth = ReadCommand<int>(n);
int nHeight = ReadCommand<int>(n);
int nID = ReadCommand<int>(n);
// when we are in video mode, don't execute the command
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_SetViewport(nX, nY, nWidth, nHeight, nID);
}
}
break;
case eRC_TexBlurAnisotropicVertical:
{
CTexture* pTex = ReadCommand<CTexture*>(n);
float fAnisoScale = ReadCommand<float>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
TexBlurAnisotropicVertical(pTex, 1, 8 * max(1.0f - min(fAnisoScale / 100.0f, 1.0f), 0.2f), 1, false);
}
}
break;
case eRC_OC_ReadResult_Try:
{
uint32 nDefaultNumSamples = ReadCommand<uint32>(n);
CREOcclusionQuery* pRE = ReadCommand<CREOcclusionQuery*>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
pRE->RT_ReadResult_Try(nDefaultNumSamples);
}
}
break;
case eRC_PostLevelLoading:
{
gRenDev->RT_PostLevelLoading();
}
break;
case eRC_StartVideoThread:
m_eVideoThreadMode = eVTM_RequestStart;
break;
case eRC_StopVideoThread:
m_eVideoThreadMode = eVTM_RequestStop;
break;
case eRC_CGCSetLayers:
{
IColorGradingControllerInt* pController = ReadCommand<IColorGradingControllerInt*>(n);
uint32 numLayers = ReadCommand<uint32>(n);
const SColorChartLayer* pLayers = numLayers ? (const SColorChartLayer*) &m_Commands[threadId][n] : 0;
n += sizeof(SColorChartLayer) * numLayers;
if (m_eVideoThreadMode == eVTM_Disabled)
{
pController->RT_SetLayers(pLayers, numLayers);
}
}
break;
case eRC_GenerateSkyDomeTextures:
{
CREHDRSky* pSky = ReadCommand<CREHDRSky*>(n);
int32 width = ReadCommand<int32>(n);
int32 height = ReadCommand<int32>(n);
pSky->GenerateSkyDomeTextures(width, height);
}
break;
case eRC_SetRendererCVar:
{
ICVar* pCVar = ReadCommand<ICVar*>(n);
const char* pArgText = ReadTextCommand<const char*>(n);
const bool bSilentMode = ReadCommand<int>(n) != 0;
gRenDev->RT_SetRendererCVar(pCVar, pArgText, bSilentMode);
}
break;
case eRC_RenderDebug:
{
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->RT_RenderDebug();
}
else
{
gRenDev->RT_RenderTextMessages();
}
}
break;
case eRC_ForceMeshGC:
if (m_eVideoThreadMode == eVTM_Disabled)
{
CRenderMesh::Tick();
}
break;
case eRC_DevBufferSync:
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->m_DevBufMan.Sync(gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameUpdateID);
}
break;
case eRC_UnlinkTexture:
{
CTexture* tex = ReadCommand<CTexture*>(n);
tex->RT_Unlink();
}
break;
case eRC_RelinkTexture:
{
CTexture* tex = ReadCommand<CTexture*>(n);
tex->RT_Relink();
}
break;
case eRC_PushSkinningPoolId:
gRenDev->RT_SetSkinningPoolId(ReadCommand< uint32 >(n));
break;
case eRC_ReleaseRemappedBoneIndices:
{
IRenderMesh* pRenderMesh = ReadCommand<IRenderMesh*>(n);
uint32 guid = ReadCommand<uint32>(n);
pRenderMesh->ReleaseRemappedBoneIndicesPair(guid);
pRenderMesh->Release();
}
break;
case eRC_SetColorOp:
{
int eCo = ReadCommand<int>(n);
int eAo = ReadCommand<int>(n);
int eCa = ReadCommand<int>(n);
int eAa = ReadCommand<int>(n);
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->EF_SetColorOp(eCo, eAo, eCa, eAa);
}
}
break;
case eRC_SetSrgbWrite:
{
bool srgbWrite = ReadCommand<int>(n) != 0;
if (m_eVideoThreadMode == eVTM_Disabled)
{
gRenDev->EF_SetSrgbWrite(srgbWrite);
}
}
break;
case eRC_InitializeVideoRenderer:
{
AZ::VideoRenderer::IVideoRenderer* pVideoRenderer = ReadCommand<AZ::VideoRenderer::IVideoRenderer*>(n);
gRenDev->RT_InitializeVideoRenderer(pVideoRenderer);
}
break;
case eRC_CleanupVideoRenderer:
{
AZ::VideoRenderer::IVideoRenderer* pVideoRenderer = ReadCommand<AZ::VideoRenderer::IVideoRenderer*>(n);
gRenDev->RT_CleanupVideoRenderer(pVideoRenderer);
}
break;
case eRC_DrawVideoRenderer:
{
AZ::VideoRenderer::IVideoRenderer* pVideoRenderer = ReadCommand<AZ::VideoRenderer::IVideoRenderer*>(n);
const AZ::VideoRenderer::DrawArguments drawArguments = ReadCommand<AZ::VideoRenderer::DrawArguments>(n);
gRenDev->RT_DrawVideoRenderer(pVideoRenderer, drawArguments);
}
break;
default:
{
assert(0);
}
break;
}
}
#if !defined (NULL_RENDERER)
if (m_eVideoThreadMode == eVTM_Disabled)
{
gcpRendD3D->BindContextToThread(nDeviceOwningThreadID);
}
# endif
#endif//STRIP_RENDER_THREAD
}
void SRenderThread::Process()
{
AZ_TRACE_METHOD();
while (true)
{
CTimeValue Time = iTimer->GetAsyncTime();
if (m_bQuit)
{
SignalFlushFinishedCond();
break;//put it here to safely shut down
}
WaitFlushCond();
const uint64 start = CryGetTicks();
CTimeValue TimeAfterWait = iTimer->GetAsyncTime();
gRenDev->m_fTimeWaitForMain[m_nCurThreadProcess] += TimeAfterWait.GetDifferenceInSeconds(Time);
if (gRenDev->m_bStartLevelLoading)
{
m_fTimeIdleDuringLoading += TimeAfterWait.GetDifferenceInSeconds(Time);
}
float fT = 0.f;
if (m_eVideoThreadMode == eVTM_Disabled)
{
//gRenDev->m_fRTTimeBeginFrame = 0;
//gRenDev->m_fRTTimeEndFrame = 0;
gRenDev->m_fRTTimeSceneRender = 0;
gRenDev->m_fRTTimeMiscRender = 0;
ProcessCommands(false /*loadTimeProcessing*/);
CTimeValue TimeAfterProcess = iTimer->GetAsyncTime();
fT = TimeAfterProcess.GetDifferenceInSeconds(TimeAfterWait);
gRenDev->m_fTimeProcessedRT[m_nCurThreadProcess] += fT;
if (m_eVideoThreadMode == eVTM_RequestStart)
{
}
SignalFlushFinishedCond();
}
if (gRenDev->m_bStartLevelLoading)
{
m_fTimeBusyDuringLoading += fT;
}
#if !defined (NULL_RENDERER)
if (m_eVideoThreadMode == eVTM_RequestStart)
{
uint32 frameId = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameUpdateID;
DWORD nDeviceOwningThreadID = gcpRendD3D->GetBoundThreadID();
gcpRendD3D->BindContextToThread(CryGetCurrentThreadId());
gRenDev->m_DevBufMan.Sync(frameId); // make sure no request are flying when switching to render loading thread
// Guarantee default resources
gRenDev->InitSystemResources(0);
// Create another render thread;
SwitchMode(true);
{
CTimeValue lastTime = gEnv->pTimer->GetAsyncTime();
CTimeValue workingStart = lastTime;
CTimeValue workingEnd = lastTime;
while (m_eVideoThreadMode != eVTM_ProcessingStop)
{
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Renderer, "Loading Frame");
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION RENDERTHREAD_CPP_SECTION_7
#include AZ_RESTRICTED_FILE(RenderThread_cpp)
#endif
frameId += 1;
const CTimeValue curTime = gEnv->pTimer->GetAsyncTime();
const CTimeValue deltaTime = curTime - lastTime;
const CTimeValue workingTime = workingEnd - workingStart;
const float deltaTimeInSeconds = AZStd::max<float>(deltaTime.GetSeconds(), 0.0f);
lastTime = curTime;
// If we spent less than half of the last frame doing anything, try to spend most of that time sleeping this frame.
// This will help us spend less time in the lock while presenting when vsync is enabled.
if (workingTime.GetValue() < (deltaTime.GetValue() / 2))
{
const uint32_t sleepTime = AZStd::min<uint32_t>(aznumeric_cast<uint32_t>(deltaTime.GetMilliSeconds()) / 2, 16);
CrySleep(sleepTime);
}
workingStart = gEnv->pTimer->GetAsyncTime();
{
CryAutoLock<CryMutex> lock(m_lockRenderLoading);
gRenDev->m_DevBufMan.Update(frameId, true);
}
if (m_pLoadtimeCallback)
{
CryAutoLock<CryMutex> lock(m_lockRenderLoading);
m_pLoadtimeCallback->LoadtimeUpdate(deltaTimeInSeconds);
}
{
////////////////////////////////////////////////
// wait till all SRendItems for this frame have finished preparing
const threadID processThreadID = gRenDev->m_RP.m_nProcessThreadID;
gRenDev->GetFinalizeRendItemJobExecutor(processThreadID)->WaitForCompletion();
gRenDev->GetFinalizeShadowRendItemJobExecutor(processThreadID)->WaitForCompletion();
{
CryAutoLock<CryMutex> lock(m_lockRenderLoading);
gRenDev->SetViewport(0, 0, gRenDev->GetOverlayWidth(), gRenDev->GetOverlayHeight());
SPostEffectsUtils::AcquireFinalCompositeTarget(false);
if (m_pLoadtimeCallback)
{
m_pLoadtimeCallback->LoadtimeRender();
}
gRenDev->m_DevBufMan.ReleaseEmptyBanks(frameId);
workingEnd = gEnv->pTimer->GetAsyncTime();
gRenDev->RT_PresentFast();
CRenderMesh::Tick();
CTexture::RT_LoadingUpdate();
}
}
// Make sure we aren't running with thousands of FPS with VSync disabled
gRenDev->LimitFramerate(120, true);
#if defined(SUPPORT_DEVICE_INFO_MSG_PROCESSING)
gcpRendD3D->DevInfo().ProcessSystemEventQueue();
#endif
}
}
if (m_pThreadLoading)
{
QuitRenderLoadingThread();
}
m_eVideoThreadMode = eVTM_Disabled;
if (m_bBeginFrameCalled)
{
m_bBeginFrameCalled = false;
gRenDev->RT_BeginFrame();
}
if (m_bEndFrameCalled)
{
m_bEndFrameCalled = false;
gRenDev->RT_EndFrame();
}
gcpRendD3D->BindContextToThread(nDeviceOwningThreadID);
}
#endif
const uint64 elapsed = CryGetTicks() - start;
gEnv->pSystem->GetCurrentUpdateTimeStats().RenderTime = elapsed;
}
#if defined(OPENGL) && !DXGL_FULL_EMULATION && !defined(CRY_USE_METAL)
m_kDXGLDeviceContextHandle.Set(NULL, !CRenderer::CV_r_multithreaded);
m_kDXGLContextHandle.Set(NULL);
#endif //defined(OPENGL) && !DXGL_FULL_EMULATION
}
void SRenderThread::ProcessLoading()
{
AZ_TRACE_METHOD();
while (true)
{
float fTime = iTimer->GetAsyncCurTime();
WaitFlushCond();
if (m_bQuitLoading)
{
SignalFlushFinishedCond();
break;//put it here to safely shut down
}
float fTimeAfterWait = iTimer->GetAsyncCurTime();
gRenDev->m_fTimeWaitForMain[m_nCurThreadProcess] += fTimeAfterWait - fTime;
if (gRenDev->m_bStartLevelLoading)
{
m_fTimeIdleDuringLoading += fTimeAfterWait - fTime;
}
ProcessCommands(true /*loadTimeProcessing*/);
SignalFlushFinishedCond();
float fTimeAfterProcess = iTimer->GetAsyncCurTime();
gRenDev->m_fTimeProcessedRT[m_nCurThreadProcess] += fTimeAfterProcess - fTimeAfterWait;
if (gRenDev->m_bStartLevelLoading)
{
m_fTimeBusyDuringLoading += fTimeAfterProcess - fTimeAfterWait;
}
if (m_eVideoThreadMode == eVTM_RequestStop)
{
// Switch to general render thread
SwitchMode(false);
}
}
#if defined(OPENGL) && !DXGL_FULL_EMULATION && !defined(CRY_USE_METAL)
m_kDXGLDeviceContextHandle.Set(NULL, !CRenderer::CV_r_multithreaded);
m_kDXGLContextHandle.Set(NULL);
#endif //defined(OPENGL) && !DXGL_FULL_EMULATION
}
#ifndef STRIP_RENDER_THREAD
// Flush current frame and wait for result (main thread only)
void SRenderThread::FlushAndWait()
{
AZ_TRACE_METHOD();
if (IsRenderThread())
{
return;
}
FUNCTION_PROFILER_LEGACYONLY(GetISystem(), PROFILE_RENDERER);
if (!m_pThread)
{
return;
}
SyncMainWithRender();
SyncMainWithRender();
}
#endif//STRIP_RENDER_THREAD
// Flush current frame without waiting (should be called from main thread)
void SRenderThread::SyncMainWithRender()
{
AZ_PROFILE_FUNCTION_STALL(AZ::Debug::ProfileCategory::Renderer);
FUNCTION_PROFILER_LEGACYONLY(GetISystem(), PROFILE_RENDERER);
#if defined(USE_HANDLE_FOR_FINAL_FLUSH_SYNC)
if (m_bQuit && m_pThread && !m_pThread->IsRunning())
{
// We're in shutdown and the render thread is not running.
// We should not attempt to wait for the render thread to signal us.
return;
}
#endif // defined(USE_HANDLE_FOR_FINAL_FLUSH_SYNC)
if (!IsMultithreaded())
{
gRenDev->SyncMainWithRender();
gRenDev->m_fTimeProcessedRT[m_nCurThreadProcess] = 0;
gRenDev->m_fTimeWaitForMain[m_nCurThreadProcess] = 0;
gRenDev->m_fTimeWaitForGPU[m_nCurThreadProcess] = 0;
return;
}
#ifndef STRIP_RENDER_THREAD
CTimeValue time = iTimer->GetAsyncTime();
WaitFlushFinishedCond();
CPostEffectsMgr* pPostEffectMgr = PostEffectMgr();
if (pPostEffectMgr)
{
// Must be called before the thread ID's get swapped
pPostEffectMgr->SyncMainWithRender();
}
gRenDev->SyncMainWithRender();
gRenDev->m_fTimeWaitForRender[m_nCurThreadFill] = iTimer->GetAsyncTime().GetDifferenceInSeconds(time);
// gRenDev->ToggleMainThreadAuxGeomCB();
gRenDev->m_RP.m_TI[m_nCurThreadProcess].m_nFrameUpdateID = gRenDev->m_RP.m_TI[m_nCurThreadFill].m_nFrameUpdateID;
gRenDev->m_RP.m_TI[m_nCurThreadProcess].m_nFrameID = gRenDev->m_RP.m_TI[m_nCurThreadFill].m_nFrameID;
m_nCurThreadProcess = m_nCurThreadFill;
m_nCurThreadFill = (m_nCurThreadProcess + 1) & 1;
gRenDev->m_RP.m_nProcessThreadID = threadID(m_nCurThreadProcess);
gRenDev->m_RP.m_nFillThreadID = threadID(m_nCurThreadFill);
m_Commands[m_nCurThreadFill].SetUse(0);
gRenDev->m_fTimeProcessedRT[m_nCurThreadProcess] = 0;
gRenDev->m_fTimeWaitForMain[m_nCurThreadProcess] = 0;
gRenDev->m_fTimeWaitForGPU[m_nCurThreadProcess] = 0;
gRenDev->m_RP.m_pCurrentRenderView = gRenDev->m_RP.m_pRenderViews[gRenDev->m_RP.m_nProcessThreadID].get();
gRenDev->m_RP.m_pCurrentFillView = gRenDev->m_RP.m_pRenderViews[gRenDev->m_RP.m_nFillThreadID].get();
gRenDev->m_RP.m_pCurrentRenderView->PrepareForRendering();
SignalFlushCond();
#endif
}
void SRenderThread::QuitRenderThread()
{
AZ_TRACE_METHOD();
if (IsMultithreaded() && m_pThread)
{
SignalQuitCond();
#if defined(USE_LOCKS_FOR_FLUSH_SYNC)
FlushAndWait();
#endif
m_pThread->WaitForThread();
SAFE_DELETE(m_pThread);
#if !defined(STRIP_RENDER_THREAD)
m_nCurThreadProcess = m_nCurThreadFill;
#endif
}
m_bQuit = 1;
}
void SRenderThread::QuitRenderLoadingThread()
{
AZ_TRACE_METHOD();
if (IsMultithreaded() && m_pThreadLoading)
{
FlushAndWait();
m_bQuitLoading = true;
m_pThreadLoading->WaitForThread();
SAFE_DELETE(m_pThreadLoading);
m_nRenderThreadLoading = 0;
CNameTableR::m_nRenderThread = m_nRenderThread;
}
}
bool SRenderThread::IsFailed()
{
AZ_TRACE_METHOD();
return !m_bSuccessful;
}
bool CRenderer::FlushRTCommands(bool bWait, bool bImmediatelly, bool bForce)
{
AZ_TRACE_METHOD();
SRenderThread* pRT = m_pRT;
IF (!pRT, 0)
{
return true;
}
if (pRT->IsRenderThread(true))
{
SSystemGlobalEnvironment* pEnv = iSystem->GetGlobalEnvironment();
if (pEnv && pEnv->IsEditor())
{
CPostEffectsMgr* pPostEffectMgr = PostEffectMgr();
if (pPostEffectMgr)
{
pPostEffectMgr->SyncMainWithRender();
}
}
return true;
}
if (!bForce && (!m_bStartLevelLoading || !pRT->IsMultithreaded()))
{
return false;
}
if (!bImmediatelly && pRT->CheckFlushCond())
{
return false;
}
if (bWait)
{
pRT->FlushAndWait();
}
return true;
}
bool CRenderer::ForceFlushRTCommands()
{
AZ_TRACE_METHOD();
LOADING_TIME_PROFILE_SECTION;
return FlushRTCommands(true, true, true);
}
// Must be executed from main thread
void SRenderThread::WaitFlushFinishedCond()
{
AZ_TRACE_METHOD();
CTimeValue time = iTimer->GetAsyncTime();
#ifdef USE_LOCKS_FOR_FLUSH_SYNC
m_LockFlushNotify.Lock();
while (*(volatile int*)&m_nFlush)
{
#if defined(USE_HANDLE_FOR_FINAL_FLUSH_SYNC)
m_LockFlushNotify.Unlock();
MsgWaitForMultipleObjects(1, &m_FlushFinishedCondition, FALSE, 1, QS_ALLINPUT);
m_LockFlushNotify.Lock();
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
if (m_bQuit && m_pThread && !m_pThread->IsRunning())
{
// We're in shutdown and the render thread is not running.
// We should not attempt to wait for the render thread to signal us -
// we force signal the flush condition to exit out of this wait loop.
m_nFlush = 0;
}
#else
const int OneHunderdMilliseconds = 100;
bool timedOut = !(m_FlushFinishedCondition.TimedWait(m_LockFlushNotify, OneHunderdMilliseconds));
#if defined(AZ_PLATFORM_IOS) && !defined(_RELEASE)
// When we trigger asserts or warnings from a thread other than the main thread, the dialog box has to be
// presented from the main thread. So, we need to pump the system event loop while the main thread is waiting.
// We're using locks for waiting on iOS. This means that once the main thread goes into the wait, it's not
// going to be able to pump system events. To handle this, we use a timed wait with 100ms. In most cases,
// the render thread will complete within 100ms. But, when we need to display a dialog from the render thread,
// it times out and pumps the system event loop so we can display the dialog. After that, since m_nFlush is still
// true, we will go back into the wait and let the render thread complete.
if (timedOut)
{
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
}
#endif
#endif
}
m_LockFlushNotify.Unlock();
#else
READ_WRITE_BARRIER
while (*(volatile int*)&m_nFlush)
{
#ifdef WIN32
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
Sleep(0);
#elif defined(AZ_PLATFORM_MAC) && !defined(_RELEASE)
// On MacOS, we display blocking alerts(dialogs) to provide notifications to users(eg: assert failed).
// These alerts(NSAlert) can be triggered only from the main thread. If we run into an assert on the render thread,
// this block of code ensures that the alert is displayed on the main thread and we're not deadlocked with render thread.
if (!gEnv->IsEditor())
{
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
}
#endif
READ_WRITE_BARRIER
}
#endif
}
// Must be executed from render thread
void SRenderThread::WaitFlushCond()
{
AZ_TRACE_METHOD();
AZ_PROFILE_FUNCTION_STALL(AZ::Debug::ProfileCategory::Renderer);
FUNCTION_PROFILER_LEGACYONLY(GetISystem(), PROFILE_RENDERER);
CTimeValue time = iTimer->GetAsyncTime();
#ifdef USE_LOCKS_FOR_FLUSH_SYNC
m_LockFlushNotify.Lock();
while (!*(volatile int*)&m_nFlush)
{
m_FlushCondition.Wait(m_LockFlushNotify);
}
m_LockFlushNotify.Unlock();
#else
READ_WRITE_BARRIER
while (!*(volatile int*)&m_nFlush)
{
if (m_bQuit)
{
break;
}
::Sleep(0);
READ_WRITE_BARRIER
}
#endif
}
#undef m_nCurThreadFill
#undef m_nCurThreadProcess