diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/cmatqueuedrendercontext.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'materialsystem/cmatqueuedrendercontext.cpp')
| -rw-r--r-- | materialsystem/cmatqueuedrendercontext.cpp | 1753 |
1 files changed, 1753 insertions, 0 deletions
diff --git a/materialsystem/cmatqueuedrendercontext.cpp b/materialsystem/cmatqueuedrendercontext.cpp new file mode 100644 index 0000000..47b10d5 --- /dev/null +++ b/materialsystem/cmatqueuedrendercontext.cpp @@ -0,0 +1,1753 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pch_materialsystem.h" + +#include "tier1/functors.h" +#include "itextureinternal.h" + +#define MATSYS_INTERNAL + +#include "cmatqueuedrendercontext.h" +#include "cmaterialsystem.h" // @HACKHACK + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +ConVar mat_report_queue_status( "mat_report_queue_status", "0", FCVAR_MATERIAL_SYSTEM_THREAD ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#if defined( _WIN32 ) +void FastCopy( byte *pDest, const byte *pSrc, size_t nBytes ) +{ + if ( !nBytes ) + { + return; + } + +#if !defined( _X360 ) + if ( (size_t)pDest % 16 == 0 && (size_t)pSrc % 16 == 0 ) + { + const int BYTES_PER_FULL = 128; + int nBytesFull = nBytes - ( nBytes % BYTES_PER_FULL ); + for ( byte *pLimit = pDest + nBytesFull; pDest < pLimit; pDest += BYTES_PER_FULL, pSrc += BYTES_PER_FULL ) + { + // memcpy( pDest, pSrc, BYTES_PER_FULL); + __asm + { + mov esi, pSrc + mov edi, pDest + + movaps xmm0, [esi + 0] + movaps xmm1, [esi + 16] + movaps xmm2, [esi + 32] + movaps xmm3, [esi + 48] + movaps xmm4, [esi + 64] + movaps xmm5, [esi + 80] + movaps xmm6, [esi + 96] + movaps xmm7, [esi + 112] + + movntps [edi + 0], xmm0 + movntps [edi + 16], xmm1 + movntps [edi + 32], xmm2 + movntps [edi + 48], xmm3 + movntps [edi + 64], xmm4 + movntps [edi + 80], xmm5 + movntps [edi + 96], xmm6 + movntps [edi + 112], xmm7 + } + } + nBytes -= nBytesFull; + } + + if ( nBytes ) + { + memcpy( pDest, pSrc, nBytes ); + } +#else + if ( (size_t)pDest % 4 == 0 && nBytes % 4 == 0 ) + { + XMemCpyStreaming_WriteCombined( pDest, pSrc, nBytes ); + } + else + { + // work around a bug in memcpy + if ((size_t)pDest % 2 == 0 && nBytes == 4) + { + *(reinterpret_cast<short *>(pDest)) = *(reinterpret_cast<const short *>(pSrc)); + *(reinterpret_cast<short *>(pDest)+1) = *(reinterpret_cast<const short *>(pSrc)+1); + } + else + { + memcpy( pDest, pSrc, nBytes ); + } + } +#endif +} +#else +#define FastCopy memcpy +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum MatQueuedMeshFlags_t +{ + MQM_BUFFERED = ( 1 << 0 ), + MQM_FLEX = ( 1 << 1 ), +}; + +class CMatQueuedMesh : public IMesh +{ +public: + CMatQueuedMesh( CMatQueuedRenderContext *pOwner, IMatRenderContextInternal *pHardwareContext ) + : m_pLateBoundMesh( &m_pActualMesh ), + m_pOwner( pOwner ), + m_pCallQueue( pOwner->GetCallQueueInternal() ), + m_pHardwareContext( pHardwareContext ), + m_pVertexData( NULL ), + m_pIndexData( NULL ), + m_nVerts( 0 ), + m_nIndices( 0 ), + m_VertexSize( 0 ), + m_Type(MATERIAL_TRIANGLES), + m_pVertexOverride( NULL ), + m_pIndexOverride ( NULL ), + m_pActualMesh( NULL ), + m_nActualVertexOffsetInBytes( 0 ), + m_VertexFormat( 0 ), + m_MorphFormat( 0 ) + { + } + + CLateBoundPtr<IMesh> &AccessLateBoundMesh() + { + return m_pLateBoundMesh; + } + + byte *GetVertexData() { return m_pVertexData; } + uint16 *GetIndexData() { return m_pIndexData; } + IMesh *DetachActualMesh() { IMesh *p = m_pActualMesh; m_pActualMesh = NULL; return p; } + IMesh *GetActualMesh() { return m_pActualMesh; } + int GetActualVertexOffsetInBytes() { return m_nActualVertexOffsetInBytes; } + + void DeferredGetDynamicMesh( VertexFormat_t vertexFormat, unsigned flags, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterialInternal *pMaterial ) + { + if ( !( flags & MQM_FLEX )) + { + if ( vertexFormat == 0 ) + { + m_pActualMesh = m_pHardwareContext->GetDynamicMesh( ( ( flags & MQM_BUFFERED ) != 0 ), pVertexOverride, pIndexOverride, pMaterial ); + } + else + { + m_pActualMesh = m_pHardwareContext->GetDynamicMeshEx( vertexFormat, ( ( flags & MQM_BUFFERED ) != 0 ), pVertexOverride, pIndexOverride, pMaterial ); + } + } + else + { + m_pActualMesh = m_pHardwareContext->GetFlexMesh(); + } + } + + bool OnGetDynamicMesh( VertexFormat_t vertexFormat, unsigned flags, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterialInternal *pMaterial, int nHWSkinBoneCount ) + { + if ( !m_pVertexOverride && ( m_pVertexData || m_pIndexData ) ) + { + CannotSupport(); + if ( IsDebug() ) + { + Assert( !"Getting a dynamic mesh without resolving the previous one" ); + } + else + { + Error( "Getting a dynamic mesh without resolving the previous one" ); + } + } + FreeBuffers(); + + m_pVertexOverride = pVertexOverride; + m_pIndexOverride = pIndexOverride; + + if ( !( flags & MQM_FLEX ) ) + { + if ( pVertexOverride ) + { + m_VertexFormat = pVertexOverride->GetVertexFormat(); + } + else + { + // Remove VERTEX_FORMAT_COMPRESSED from the material's format (dynamic meshes don't + // support compression, and all materials should support uncompressed verts too) + m_VertexFormat = ( vertexFormat == 0 ) ? ( pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED ) : vertexFormat; + + if ( vertexFormat != 0 ) + { + int nVertexFormatBoneWeights = NumBoneWeights( vertexFormat ); + if ( nHWSkinBoneCount < nVertexFormatBoneWeights ) + { + nHWSkinBoneCount = nVertexFormatBoneWeights; + } + } + // Force the requested number of bone weights + m_VertexFormat &= ~VERTEX_BONE_WEIGHT_MASK; + m_VertexFormat |= VERTEX_BONEWEIGHT( nHWSkinBoneCount ); + if ( nHWSkinBoneCount > 0 ) + { + m_VertexFormat |= VERTEX_BONE_INDEX; + } + } + } + else + { + m_VertexFormat = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT; + if ( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ) + { + m_VertexFormat |= VERTEX_WRINKLE; + } + } + + MeshDesc_t temp; + g_pShaderAPI->ComputeVertexDescription( 0, m_VertexFormat, temp ); + m_VertexSize = temp.m_ActualVertexSize; + + // queue up get of real dynamic mesh, allocate space for verts & indices + m_pCallQueue->QueueCall( this, &CMatQueuedMesh::DeferredGetDynamicMesh, vertexFormat, flags, pVertexOverride, pIndexOverride, pMaterial ); + return true; + } + + void ModifyBegin( int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ) + { + CannotSupport(); + } + + void ModifyBeginEx( bool bReadOnly, int firstVertex, int numVerts, int firstIndex, int numIndices, MeshDesc_t& desc ) + { + CannotSupport(); + } + + void ModifyEnd( MeshDesc_t& desc ) + { + CannotSupport(); + } + + void GenerateSequentialIndexBuffer( unsigned short* pIndexMemory, int numIndices, int firstVertex ) + { + Assert( pIndexMemory == m_pIndexData ); + m_pCallQueue->QueueCall( &::GenerateSequentialIndexBuffer, pIndexMemory, numIndices, firstVertex ); + } + + void GenerateQuadIndexBuffer( unsigned short* pIndexMemory, int numIndices, int firstVertex ) + { + Assert( pIndexMemory == m_pIndexData ); + m_pCallQueue->QueueCall( &::GenerateQuadIndexBuffer, pIndexMemory, numIndices, firstVertex ); + } + + void GeneratePolygonIndexBuffer( unsigned short* pIndexMemory, int numIndices, int firstVertex ) + { + Assert( pIndexMemory == m_pIndexData ); + m_pCallQueue->QueueCall( &::GeneratePolygonIndexBuffer, pIndexMemory, numIndices, firstVertex ); + } + + void GenerateLineStripIndexBuffer( unsigned short* pIndexMemory, int numIndices, int firstVertex ) + { + Assert( pIndexMemory == m_pIndexData ); + m_pCallQueue->QueueCall( &::GenerateLineStripIndexBuffer, pIndexMemory, numIndices, firstVertex ); + } + + void GenerateLineLoopIndexBuffer( unsigned short* pIndexMemory, int numIndices, int firstVertex ) + { + Assert( pIndexMemory == m_pIndexData ); + m_pCallQueue->QueueCall( &::GenerateLineLoopIndexBuffer, pIndexMemory, numIndices, firstVertex ); + } + + int VertexCount() const + { + return m_VertexSize ? m_nVerts : 0; + } + + int IndexCount() const + { + return m_nIndices; + } + + int GetVertexSize() + { + return m_VertexSize; + } + + void SetPrimitiveType( MaterialPrimitiveType_t type ) + { + m_Type = type; + m_pCallQueue->QueueCall( m_pLateBoundMesh, &IMesh::SetPrimitiveType, type ); + } + + void SetColorMesh( IMesh *pColorMesh, int nVertexOffset ) + { + m_pCallQueue->QueueCall( m_pLateBoundMesh, &IMesh::SetColorMesh, pColorMesh, nVertexOffset ); + } + + void Draw( CPrimList *pLists, int nLists ) + { + CannotSupport(); + } + + void CopyToMeshBuilder( int iStartVert, int nVerts, int iStartIndex, int nIndices, int indexOffset, CMeshBuilder &builder ) + { + CannotSupport(); + } + + void Spew( int numVerts, int numIndices, const MeshDesc_t & desc ) + { + } + + void ValidateData( int numVerts, int numIndices, const MeshDesc_t & desc ) + { + } + + void LockMesh( int numVerts, int numIndices, MeshDesc_t& desc ) + { + if ( !m_pVertexOverride ) + { + m_nVerts = numVerts; + } + else + { + m_nVerts = 0; + } + + if ( !m_pIndexOverride ) + { + m_nIndices = numIndices; + } + else + { + m_nIndices = 0; + } + + if( numVerts > 0 ) + { + Assert( m_VertexSize ); + Assert( !m_pVertexData ); + m_pVertexData = (byte *)m_pOwner->AllocVertices( numVerts, m_VertexSize ); + Assert( (unsigned)m_pVertexData % 16 == 0 ); + + // Compute the vertex index.. + desc.m_nFirstVertex = 0; + static_cast< VertexDesc_t* >( &desc )->m_nOffset = 0; + // Set up the mesh descriptor + g_pShaderAPI->ComputeVertexDescription( m_pVertexData, m_VertexFormat, desc ); + } + else + { + desc.m_nFirstVertex = 0; + static_cast< VertexDesc_t* >( &desc )->m_nOffset = 0; + // Set up the mesh descriptor + g_pShaderAPI->ComputeVertexDescription( 0, 0, desc ); + } + + if ( m_Type != MATERIAL_POINTS && numIndices > 0 ) + { + Assert( !m_pIndexData ); + m_pIndexData = m_pOwner->AllocIndices( numIndices ); + desc.m_pIndices = m_pIndexData; + desc.m_nIndexSize = 1; + desc.m_nFirstIndex = 0; + static_cast< IndexDesc_t* >( &desc )->m_nOffset = 0; + } + else + { + desc.m_pIndices = &gm_ScratchIndexBuffer[0]; + desc.m_nIndexSize = 0; + desc.m_nFirstIndex = 0; + static_cast< IndexDesc_t* >( &desc )->m_nOffset = 0; + } + } + + void UnlockMesh( int numVerts, int numIndices, MeshDesc_t& desc ) + { + if ( m_pVertexData && numVerts < m_nVerts ) + { + m_pVertexData = m_pOwner->ReallocVertices( m_pVertexData, m_nVerts, numVerts, m_VertexSize ); + } + m_nVerts = numVerts; + + if ( m_pIndexData && numIndices < m_nIndices ) + { + m_pIndexData = m_pOwner->ReallocIndices( m_pIndexData, m_nIndices, numIndices ); + } + m_nIndices = numIndices; + } + + void SetFlexMesh( IMesh *pMesh, int nVertexOffset ) + { + m_pCallQueue->QueueCall( m_pLateBoundMesh, &IMesh::SetFlexMesh, pMesh, nVertexOffset ); + } + + void DisableFlexMesh() + { + m_pCallQueue->QueueCall( m_pLateBoundMesh, &IMesh::DisableFlexMesh ); + } + + void ExecuteDefferredBuild( byte *pVertexData, int nVerts, int nBytesVerts, uint16 *pIndexData, int nIndices ) + { + Assert( m_pActualMesh ); + MeshDesc_t desc; + m_pActualMesh->LockMesh( nVerts, nIndices, desc ); + m_nActualVertexOffsetInBytes = desc.m_nFirstVertex * desc.m_ActualVertexSize; + if ( pVertexData && desc.m_ActualVertexSize ) // if !desc.m_ActualVertexSize, device lost + { + void *pDest; + if ( desc.m_VertexSize_Position != 0 ) + { + pDest = desc.m_pPosition; + } + else + { + #define FindMin( desc, pCurrent, tag ) ( ( desc.m_VertexSize_##tag != 0 ) ? min( pCurrent, (void *)desc.m_p##tag ) : pCurrent ) + + pDest = (void *)(((byte *)0) - 1); + + pDest = FindMin( desc, pDest, BoneWeight ); + pDest = FindMin( desc, pDest, BoneMatrixIndex ); + pDest = FindMin( desc, pDest, Normal ); + pDest = FindMin( desc, pDest, Color ); + pDest = FindMin( desc, pDest, Specular ); + pDest = FindMin( desc, pDest, TangentS ); + pDest = FindMin( desc, pDest, TangentT ); + pDest = FindMin( desc, pDest, Wrinkle ); + + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) + { + if ( desc.m_VertexSize_TexCoord[i] && desc.m_pTexCoord < pDest ) + { + pDest = desc.m_pTexCoord; + } + } + + #undef FindMin + } + + Assert( pDest ); + if ( pDest ) + { + FastCopy( (byte *)pDest, pVertexData, nBytesVerts ); + } + } + + if ( pIndexData && pIndexData != &gm_ScratchIndexBuffer[0] && desc.m_nIndexSize ) + { + if ( !desc.m_nFirstVertex ) + { + // AssertMsg(desc.m_pIndices & 0x03 == 0,"desc.m_pIndices is misaligned in CMatQueuedMesh::ExecuteDefferedBuild\n"); + FastCopy( (byte *)desc.m_pIndices, (byte *)pIndexData, nIndices * sizeof(*pIndexData) ); + } + else + { + ALIGN16 uint16 tempIndices[16]; + + int i = 0; + if ( (size_t)desc.m_pIndices % 4 == 2 ) + { + desc.m_pIndices[i] = pIndexData[i] + desc.m_nFirstVertex; + i++; + } + while ( i < nIndices ) + { + int nToCopy = min( (int)ARRAYSIZE(tempIndices), nIndices - i ); + for ( int j = 0; j < nToCopy; j++ ) + { + tempIndices[j] = pIndexData[i+j] + desc.m_nFirstVertex; + } + FastCopy( (byte *)(desc.m_pIndices + i), (byte *)tempIndices, nToCopy * sizeof(uint16) ); + i += nToCopy; + } + } + } + + m_pActualMesh->UnlockMesh( nVerts, nIndices, desc ); + + if ( pIndexData && pIndexData != &gm_ScratchIndexBuffer[0]) + { + m_pOwner->FreeIndices( pIndexData, nIndices ); + } + if ( pVertexData ) + { + m_pOwner->FreeVertices( pVertexData, nVerts, desc.m_ActualVertexSize ); + } + } + + void QueueBuild( bool bDetachBuffers = true ) + { + m_pCallQueue->QueueCall( this, &CMatQueuedMesh::ExecuteDefferredBuild, m_pVertexData, m_nVerts, m_nVerts * m_VertexSize, m_pIndexData, m_nIndices ); + if ( bDetachBuffers ) + { + DetachBuffers(); + m_Type = MATERIAL_TRIANGLES; + } + } + + void Draw( int firstIndex = -1, int numIndices = 0 ) + { + if ( !m_nVerts && !m_nIndices ) + { + MarkAsDrawn(); + return; + } + + void (IMesh::*pfnDraw)( int, int) = &IMesh::Draw; // need assignment to disambiguate overloaded function + bool bDetachBuffers; + if ( firstIndex == -1 || numIndices == 0 ) + { + bDetachBuffers = true; + } + else if ( m_pIndexOverride ) + { + bDetachBuffers = ( firstIndex + numIndices == m_pIndexOverride->IndexCount() ); + } + else if ( !m_nIndices || firstIndex + numIndices == m_nIndices ) + { + bDetachBuffers = true; + } + else + { + bDetachBuffers = false; + } + + QueueBuild( bDetachBuffers ); + m_pCallQueue->QueueCall( m_pLateBoundMesh, pfnDraw, firstIndex, numIndices ); + } + + void MarkAsDrawn() + { + FreeBuffers(); + m_pCallQueue->QueueCall( m_pLateBoundMesh, &IMesh::MarkAsDrawn ); + } + + void FreeBuffers() + { + if ( m_pIndexData && m_pIndexData != &gm_ScratchIndexBuffer[0]) + { + m_pOwner->FreeIndices( m_pIndexData, m_nIndices ); + m_pIndexData = NULL; + } + if ( m_pVertexData ) + { + m_pOwner->FreeVertices( m_pVertexData, m_nVerts, m_VertexSize ); + m_pVertexData = NULL; + } + } + + void DetachBuffers() + { + m_pVertexData = NULL; + m_pIndexData = NULL; + } + + unsigned ComputeMemoryUsed() + { + return 0; + } + + virtual VertexFormat_t GetVertexFormat() const + { + return m_VertexFormat; + } + + virtual IMesh *GetMesh() + { + return this; + } + + // FIXME: Implement! + virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) + { + Assert( 0 ); + return false; + } + virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) + { + Assert( 0 ); + } + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) + { + CannotSupport(); + } + void ModifyEnd( IndexDesc_t& desc ) + { + CannotSupport(); + } + virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) + { + Assert( 0 ); + } + virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) + { + Assert( 0 ); + } + virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) + { + Assert( 0 ); + return false; + } + virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) + { + Assert( 0 ); + } + virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) + { + Assert( 0 ); + } + virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) + { + Assert( 0 ); + } + virtual bool IsDynamic() const + { + Assert( 0 ); + return false; + } + + virtual MaterialIndexFormat_t IndexFormat() const + { + Assert( 0 ); + return MATERIAL_INDEX_FORMAT_UNKNOWN; + } + + virtual void BeginCastBuffer( VertexFormat_t format ) + { + Assert( 0 ); + } + + virtual void BeginCastBuffer( MaterialIndexFormat_t format ) + { + Assert( 0 ); + } + + virtual void EndCastBuffer( ) + { + Assert( 0 ); + } + + // Returns the number of vertices that can still be written into the buffer + virtual int GetRoomRemaining() const + { + Assert( 0 ); + return 0; + } + + + //---------------------------------------------------------------------------- + + static void DoDraw( int firstIndex = -1, int numIndices = 0 ) + { + + } +private: + + IMesh *m_pActualMesh; + int m_nActualVertexOffsetInBytes; + + CLateBoundPtr<IMesh> m_pLateBoundMesh; + + CMatQueuedRenderContext *m_pOwner; + CMatCallQueue *m_pCallQueue; + IMatRenderContextInternal *m_pHardwareContext; + + //----------------------------------------------------- + + // The vertex format we're using... + VertexFormat_t m_VertexFormat; + + // The morph format we're using + MorphFormat_t m_MorphFormat; + + byte *m_pVertexData; + uint16 *m_pIndexData; + + int m_nVerts; + int m_nIndices; + + unsigned short m_VertexSize; + MaterialPrimitiveType_t m_Type; + + // Used in rendering sub-parts of the mesh + //static unsigned int s_NumIndices; + //static unsigned int s_FirstIndex; + + IMesh *m_pVertexOverride; + IMesh *m_pIndexOverride; + + static unsigned short gm_ScratchIndexBuffer[6]; +}; + +unsigned short CMatQueuedMesh::gm_ScratchIndexBuffer[6]; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::Init( CMaterialSystem *pMaterialSystem, CMatRenderContextBase *pHardwareContext ) +{ + BaseClass::Init(); + + m_pMaterialSystem = pMaterialSystem; + m_pHardwareContext = pHardwareContext; + + m_pQueuedMesh = new CMatQueuedMesh( this, pHardwareContext ); + + MEM_ALLOC_CREDIT(); + + int nSize = 16 * 1024 * 1024; + int nCommitSize = 128 * 1024; +#if defined(DEDICATED) + Assert( !"CMatQueuedRenderContext shouldn't be initialized on dedicated servers..." ); + nSize = nCommitSize = 1024; +#endif + + bool bVerticesInit = m_Vertices.Init( nSize, nCommitSize ); + bool bIndicesInit = m_Indices.Init( nSize, nCommitSize ); + + if ( !bVerticesInit || !bIndicesInit ) + { + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::Shutdown() +{ + if ( !m_pHardwareContext ) + { + return; + } + + Assert( !m_pCurrentMaterial ); + + delete m_pQueuedMesh; + m_pMaterialSystem = NULL; + m_pHardwareContext = NULL; + m_pQueuedMesh = NULL; + + m_Vertices.Term(); + m_Indices.Term(); + + BaseClass::Shutdown(); + Assert(m_queue.Count() == 0); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::Free() +{ + m_Vertices.FreeAll(); + m_Indices.FreeAll(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::CompactMemory() +{ + BaseClass::CompactMemory(); + m_Vertices.FreeAll(); + m_Indices.FreeAll(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::BeginQueue( CMatRenderContextBase *pInitialState ) +{ + if ( !pInitialState ) + { + pInitialState = m_pHardwareContext; + } + CMatRenderContextBase::InitializeFrom( pInitialState ); + g_pShaderAPI->GetBackBufferDimensions( m_WidthBackBuffer, m_HeightBackBuffer ); + m_FogMode = pInitialState->GetFogMode(); + m_nBoneCount = pInitialState->GetCurrentNumBones(); + pInitialState->GetFogDistances( &m_flFogStart, &m_flFogEnd, &m_flFogZ ); + +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::EndQueue( bool bCallQueued ) +{ + if ( bCallQueued ) + { + CallQueued(); + } + int i; + + if ( m_pCurrentMaterial ) + { + m_pCurrentMaterial = NULL; + } + + if ( m_pUserDefinedLightmap ) + { + m_pUserDefinedLightmap = NULL; + } + + if ( m_pLocalCubemapTexture ) + { + m_pLocalCubemapTexture = NULL; + } + + for ( i = 0; i < MAX_FB_TEXTURES; i++ ) + { + if ( m_pCurrentFrameBufferCopyTexture[i] ) + { + m_pCurrentFrameBufferCopyTexture[i] = NULL; + } + } + + for ( i = 0; i < m_RenderTargetStack.Count(); i++ ) + { + for ( int j = 0; j < MAX_RENDER_TARGETS; j++ ) + { + if ( m_RenderTargetStack[i].m_pRenderTargets[j] ) + { + m_RenderTargetStack[i].m_pRenderTargets[j] = NULL; + } + } + } + + m_RenderTargetStack.Clear(); +} + + +void CMatQueuedRenderContext::Bind( IMaterial *iMaterial, void *proxyData ) +{ + if ( !iMaterial ) + { + if( !g_pErrorMaterial ) + return; + } + else + { + iMaterial = ((IMaterialInternal *)iMaterial)->GetRealTimeVersion(); //always work with the real time versions of materials internally + } + + CMatRenderContextBase::Bind( iMaterial, proxyData ); + + // We've always gotta call the bind proxy (assuming there is one) + // so we can copy off the material vars at this point. + IMaterialInternal* pIMaterial = GetCurrentMaterialInternal(); + pIMaterial->CallBindProxy( proxyData ); + + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::Bind, iMaterial, proxyData ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::BeginRender() +{ + if ( ++m_iRenderDepth == 1 ) + { + VPROF_INCREMENT_GROUP_COUNTER( "render/CMatQBeginRender", COUNTER_GROUP_TELEMETRY, 1 ); + + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::BeginRender ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::EndRender() +{ + if ( --m_iRenderDepth == 0 ) + { + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::EndRender ); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::CallQueued( bool bTermAfterCall ) +{ + if ( mat_report_queue_status.GetBool() ) + { + Msg( "%d calls queued for %llu bytes in parameters and overhead, %d bytes verts, %d bytes indices, %d bytes other\n", m_queue.Count(), (uint64)(m_queue.GetMemoryUsed()), m_Vertices.GetUsed(), m_Indices.GetUsed(), RenderDataSizeUsed() ); + } + + m_queue.CallQueued(); + + m_Vertices.FreeAll( false ); + m_Indices.FreeAll( false ); + + if ( bTermAfterCall ) + { + Shutdown(); + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::FlushQueued() +{ + m_queue.Flush(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +ICallQueue *CMatQueuedRenderContext::GetCallQueue() +{ + return &m_CallQueueExternal; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetRenderTargetEx( int nRenderTargetID, ITexture *pNewTarget ) +{ + CMatRenderContextBase::SetRenderTargetEx( nRenderTargetID, pNewTarget ); + + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetRenderTargetEx, nRenderTargetID, pNewTarget ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::GetRenderTargetDimensions( int &width, int &height) const +{ + // Target at top of stack + ITexture *pTOS = NULL; + + if ( m_RenderTargetStack.Count() ) + { + pTOS = m_RenderTargetStack.Top().m_pRenderTargets[ 0 ]; + } + + // If top of stack isn't the back buffer, get dimensions from the texture + if ( pTOS != NULL ) + { + width = pTOS->GetActualWidth(); + height = pTOS->GetActualHeight(); + } + else // otherwise, get them from the shader API + { + width = m_WidthBackBuffer; + height = m_HeightBackBuffer; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::Viewport( int x, int y, int width, int height ) +{ + CMatRenderContextBase::Viewport( x, y, width, height ); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::Viewport, x, y, width, height ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetLight( int i, const LightDesc_t &desc ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetLight, i, RefToVal( desc ) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetLightingOrigin( Vector vLightingOrigin ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetLightingOrigin, vLightingOrigin ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetAmbientLightCube( LightCube_t cube ) +{ + // FIXME: does compiler do the right thing, is envelope needed? + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetAmbientLightCube, CUtlEnvelope<Vector4D>( &cube[0], 6 ) ); +} + +//----------------------------------------------------------------------------- +// Bone count +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetNumBoneWeights( int nBoneCount ) +{ + m_nBoneCount = nBoneCount; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetNumBoneWeights, nBoneCount ); +} + +int CMatQueuedRenderContext::GetCurrentNumBones( ) const +{ + return m_nBoneCount; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::FogMode( MaterialFogMode_t fogMode ) +{ + m_FogMode = fogMode; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::FogMode, fogMode ); +} + +void CMatQueuedRenderContext::FogStart( float fStart ) +{ + m_flFogStart = fStart; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::FogStart, fStart ); +} + +void CMatQueuedRenderContext::FogEnd( float fEnd ) +{ + m_flFogEnd = fEnd; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::FogEnd, fEnd ); +} + +void CMatQueuedRenderContext::FogMaxDensity( float flMaxDensity ) +{ + m_flFogMaxDensity = flMaxDensity; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::FogMaxDensity, flMaxDensity ); +} + +void CMatQueuedRenderContext::SetFogZ( float fogZ ) +{ + m_flFogZ = fogZ; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetFogZ, fogZ ); +} + +MaterialFogMode_t CMatQueuedRenderContext::GetFogMode( void ) +{ + return m_FogMode; +} + +void CMatQueuedRenderContext::FogColor3f( float r, float g, float b ) +{ + FogColor3ub( clamp( (int)(r * 255.0f), 0, 255 ), clamp( (int)(g * 255.0f), 0, 255 ), clamp( (int)(b * 255.0f), 0, 255 ) ); +} + +void CMatQueuedRenderContext::FogColor3fv( float const* rgb ) +{ + FogColor3ub( clamp( (int)(rgb[0] * 255.0f), 0, 255 ), clamp( (int)(rgb[1] * 255.0f), 0, 255 ), clamp( (int)(rgb[2] * 255.0f), 0, 255 ) ); +} + +void CMatQueuedRenderContext::FogColor3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + m_FogColor.r = r; + m_FogColor.g = g; + m_FogColor.b = b; + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::FogColor3ub, r, g, b ); +} + +void CMatQueuedRenderContext::FogColor3ubv( unsigned char const* rgb ) +{ + FogColor3ub( rgb[0], rgb[1], rgb[2] ); +} + +void CMatQueuedRenderContext::GetFogColor( unsigned char *rgb ) +{ + rgb[0] = m_FogColor.r; + rgb[1] = m_FogColor.g; + rgb[2] = m_FogColor.b; +} + +void CMatQueuedRenderContext::GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) +{ + if( fStart ) + *fStart = m_flFogStart; + + if( fEnd ) + *fEnd = m_flFogEnd; + + if( fFogZ ) + *fFogZ = m_flFogZ; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::GetViewport( int& x, int& y, int& width, int& height ) const +{ + // Verify valid top of RT stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Grab the top of stack + const RenderTargetStackElement_t& element = m_RenderTargetStack.Top(); + + // If either dimension is negative, set to full bounds of current target + if ( (element.m_nViewW < 0) || (element.m_nViewH < 0) ) + { + // Viewport origin at target origin + x = y = 0; + + // If target is back buffer + if ( element.m_pRenderTargets[0] == NULL ) + { + width = m_WidthBackBuffer; + height = m_HeightBackBuffer; + } + else // if target is texture + { + width = element.m_pRenderTargets[0]->GetActualWidth(); + height = element.m_pRenderTargets[0]->GetActualHeight(); + } + } + else // use the bounds from the stack directly + { + x = element.m_nViewX; + y = element.m_nViewY; + width = element.m_nViewW; + height = element.m_nViewH; + } +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SyncToken( const char *p ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SyncToken, CUtlEnvelope<const char *>( p ) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +IMesh* CMatQueuedRenderContext::GetDynamicMesh( bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterial *pAutoBind ) +{ + if( pAutoBind ) + Bind( pAutoBind, NULL ); + + if ( pVertexOverride && pIndexOverride ) + { + // Use the new batch API + DebuggerBreak(); + return NULL; + } + + if ( pVertexOverride ) + { + if ( CompressionType( pVertexOverride->GetVertexFormat() ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + DebuggerBreak(); + return NULL; + } + } + + // For anything more than 1 bone, imply the last weight from the 1 - the sum of the others. + int nCurrentBoneCount = GetCurrentNumBones(); + Assert( nCurrentBoneCount <= 4 ); + if ( nCurrentBoneCount > 1 ) + { + --nCurrentBoneCount; + } + + m_pQueuedMesh->OnGetDynamicMesh( 0, ( buffered ) ? MQM_BUFFERED : 0, pVertexOverride, pIndexOverride, GetCurrentMaterialInternal(), nCurrentBoneCount ); + return m_pQueuedMesh; +} + +IMesh* CMatQueuedRenderContext::GetDynamicMeshEx( VertexFormat_t vertexFormat, bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterial *pAutoBind ) +{ + if( pAutoBind ) + { + Bind( pAutoBind, NULL ); + } + + if ( pVertexOverride && pIndexOverride ) + { + // Use the new batch API + DebuggerBreak(); + return NULL; + } + + if ( pVertexOverride ) + { + if ( CompressionType( pVertexOverride->GetVertexFormat() ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + DebuggerBreak(); + return NULL; + } + } + + // For anything more than 1 bone, imply the last weight from the 1 - the sum of the others. + int nCurrentBoneCount = GetCurrentNumBones(); + Assert( nCurrentBoneCount <= 4 ); + if ( nCurrentBoneCount > 1 ) + { + --nCurrentBoneCount; + } + + m_pQueuedMesh->OnGetDynamicMesh( vertexFormat, ( bBuffered ) ? MQM_BUFFERED : 0, pVertexOverride, pIndexOverride, GetCurrentMaterialInternal(), nCurrentBoneCount ); + return m_pQueuedMesh; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CMatQueuedRenderContext::GetMaxVerticesToRender( IMaterial *pMaterial ) +{ + pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); //always work with the real time version of materials internally. + + MeshDesc_t temp; + + // Be conservative, assume no compression (in here, we don't know if the caller will used a compressed VB or not) + // FIXME: allow the caller to specify which compression type should be used to compute size from the vertex format + // (this can vary between multiple VBs/Meshes using the same material) + VertexFormat_t materialFormat = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED; + g_pShaderAPI->ComputeVertexDescription( 0, materialFormat, temp ); + + int maxVerts = g_pShaderAPI->GetCurrentDynamicVBSize() / temp.m_ActualVertexSize; + if ( maxVerts > 65535 ) + { + maxVerts = 65535; + } + return maxVerts; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) +{ + Assert( !bMaxUntilFlush ); + *pMaxVerts = g_pShaderAPI->GetCurrentDynamicVBSize() / m_pQueuedMesh->GetVertexSize(); + if ( *pMaxVerts > 65535 ) + { + *pMaxVerts = 65535; + } + *pMaxIndices = INDEX_BUFFER_SIZE; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +IMesh *CMatQueuedRenderContext::GetFlexMesh() +{ + m_pQueuedMesh->OnGetDynamicMesh( 0, MQM_FLEX, NULL, NULL, NULL, 0 ); + return m_pQueuedMesh; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +OcclusionQueryObjectHandle_t CMatQueuedRenderContext::CreateOcclusionQueryObject() +{ + OcclusionQueryObjectHandle_t h = g_pOcclusionQueryMgr->CreateOcclusionQueryObject(); + m_queue.QueueCall( g_pOcclusionQueryMgr, &COcclusionQueryMgr::OnCreateOcclusionQueryObject, h ); + return h; +} + +int CMatQueuedRenderContext::OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t h ) +{ + m_queue.QueueCall( g_pOcclusionQueryMgr, &COcclusionQueryMgr::OcclusionQuery_IssueNumPixelsRenderedQuery, h ); + return g_pOcclusionQueryMgr->OcclusionQuery_GetNumPixelsRendered( h, false ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::SetFlashlightState( const FlashlightState_t &s, const VMatrix &m ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::SetFlashlightState, RefToVal( s ), RefToVal( m ) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::EnableClipping( bool bEnable ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::EnableClipping, bEnable ); + return BaseClass::EnableClipping( bEnable ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::UserClipTransform( const VMatrix &m ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::UserClipTransform, RefToVal( m ) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::GetWindowSize( int &width, int &height ) const +{ + width = m_WidthBackBuffer; + height = m_HeightBackBuffer; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::DrawScreenSpaceRectangle( + IMaterial *pMaterial, + int destx, int desty, + int width, int height, + float src_texture_x0, float src_texture_y0, // which texel you want to appear at + // destx/y + float src_texture_x1, float src_texture_y1, // which texel you want to appear at + // destx+width-1, desty+height-1 + int src_texture_width, int src_texture_height, // needed for fixup + void *pClientRenderable, + int nXDice, int nYDice ) // Amount to tessellate the quad +{ + IMaterial *pRealTimeVersionMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); + pRealTimeVersionMaterial->CallBindProxy( pClientRenderable ); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::DrawScreenSpaceRectangle, pMaterial, destx, desty, width, height, src_texture_x0, src_texture_y0, src_texture_x1, src_texture_y1, src_texture_width, src_texture_height, pClientRenderable, nXDice, nYDice ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::LoadBoneMatrix( int i, const matrix3x4_t &m ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::LoadBoneMatrix, i, RefToVal( m ) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::CopyRenderTargetToTextureEx( ITexture *pTexture, int i, Rect_t *pSrc, Rect_t *pDst ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::CopyRenderTargetToTextureEx, pTexture, i, CUtlEnvelope<Rect_t>(pSrc), CUtlEnvelope<Rect_t>(pDst) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::CopyTextureToRenderTargetEx( int i, ITexture *pTexture, Rect_t *pSrc, Rect_t *pDst ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::CopyTextureToRenderTargetEx, i, pTexture, CUtlEnvelope<Rect_t>(pSrc), CUtlEnvelope<Rect_t>(pDst) ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices ) +{ + void (IMesh::*pfnDraw)( int, int) = &IMesh::Draw; // need assignment to disambiguate overloaded function + m_queue.QueueCall( pMesh, pfnDraw, firstIndex, numIndices ); + return false; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists ) +{ + CMatRenderData< CPrimList > rdPrimList( this, nLists, pLists ); + m_queue.QueueCall( this, &CMatQueuedRenderContext::DeferredDrawPrimList, pMesh, rdPrimList.Base(), nLists ); + return false; +} + +void CMatQueuedRenderContext::DeferredDrawPrimList( IMesh *pMesh, CPrimList *pLists, int nLists ) +{ + pMesh->Draw( pLists, nLists ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CMatQueuedRenderContext::DeferredSetFlexMesh( IMesh *pStaticMesh, int nVertexOffsetInBytes ) +{ + pStaticMesh->SetFlexMesh( m_pQueuedMesh->GetActualMesh(), m_pQueuedMesh->GetActualVertexOffsetInBytes() ); +} + +bool CMatQueuedRenderContext::OnSetFlexMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) +{ + Assert( pMesh == m_pQueuedMesh || !pMesh ); + if ( pMesh ) + { + m_pQueuedMesh->QueueBuild(); + m_queue.QueueCall( this, &CMatQueuedRenderContext::DeferredSetFlexMesh, pStaticMesh, nVertexOffsetInBytes ); + } + else + { + m_queue.QueueCall( pStaticMesh, &IMesh::SetFlexMesh, (IMesh *)NULL, 0 ); + } + return false; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::OnSetColorMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) +{ + m_queue.QueueCall( pStaticMesh, &IMesh::SetColorMesh, pMesh, nVertexOffsetInBytes ); + return false; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::OnSetPrimitiveType( IMesh *pMesh, MaterialPrimitiveType_t type ) +{ + m_queue.QueueCall( pMesh, &IMesh::SetPrimitiveType, type ); + return false; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CMatQueuedRenderContext::OnFlushBufferedPrimitives() +{ + m_queue.QueueCall( g_pShaderAPI, &IShaderAPI::FlushBufferedPrimitives ); + return false; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline void CMatQueuedRenderContext::QueueMatrixSync() +{ + void (IMatRenderContext::*pfnLoadMatrix)( const VMatrix & ) = &IMatRenderContext::LoadMatrix; // need assignment to disambiguate overloaded function + m_queue.QueueCall( m_pHardwareContext, pfnLoadMatrix, RefToVal( AccessCurrentMatrix() ) ); +} + +void CMatQueuedRenderContext::MatrixMode( MaterialMatrixMode_t mode ) +{ + CMatRenderContextBase::MatrixMode( mode ); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::MatrixMode, mode ); +} + +void CMatQueuedRenderContext::PushMatrix() +{ + CMatRenderContextBase::PushMatrix(); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::PushMatrix ); +} + +void CMatQueuedRenderContext::PopMatrix() +{ + CMatRenderContextBase::PopMatrix(); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::PopMatrix ); +} + +void CMatQueuedRenderContext::LoadMatrix( const VMatrix& matrix ) +{ + CMatRenderContextBase::LoadMatrix( matrix ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::LoadMatrix( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::LoadMatrix( matrix ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::MultMatrix( const VMatrix& matrix ) +{ + CMatRenderContextBase::MultMatrix( matrix ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::MultMatrix( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrix( VMatrix( matrix ) ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::MultMatrixLocal( const VMatrix& matrix ) +{ + CMatRenderContextBase::MultMatrixLocal( matrix ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::MultMatrixLocal( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrixLocal( VMatrix( matrix ) ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::LoadIdentity() +{ + CMatRenderContextBase::LoadIdentity(); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::LoadIdentity ); +} + +void CMatQueuedRenderContext::Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) +{ + CMatRenderContextBase::Ortho( left, top, right, bottom, zNear, zFar ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::PerspectiveX( double flFovX, double flAspect, double flZNear, double flZFar ) +{ + CMatRenderContextBase::PerspectiveX( flFovX, flAspect, flZNear, flZFar ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::PerspectiveOffCenterX( double flFovX, double flAspect, double flZNear, double flZFar, double bottom, double top, double left, double right ) +{ + CMatRenderContextBase::PerspectiveOffCenterX( flFovX, flAspect, flZNear, flZFar, bottom, top, left, right ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::PickMatrix( int x, int y, int nWidth, int nHeight ) +{ + CMatRenderContextBase::PickMatrix( x, y, nWidth, nHeight ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::Rotate( float flAngle, float x, float y, float z ) +{ + CMatRenderContextBase::Rotate( flAngle, x, y, z ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::Translate( float x, float y, float z ) +{ + CMatRenderContextBase::Translate( x, y, z ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::Scale( float x, float y, float z ) +{ + CMatRenderContextBase::Scale( x, y, z ); + QueueMatrixSync(); +} + +void CMatQueuedRenderContext::BeginBatch( IMesh* pIndices ) +{ + Assert( pIndices == (IMesh *)m_pQueuedMesh ); + m_queue.QueueCall( this, &CMatQueuedRenderContext::DeferredBeginBatch, m_pQueuedMesh->GetIndexData(), m_pQueuedMesh->IndexCount() ); + m_pQueuedMesh->DetachBuffers(); +} + +void CMatQueuedRenderContext::BindBatch( IMesh* pVertices, IMaterial *pAutoBind ) +{ + Assert( pVertices != (IMesh *)m_pQueuedMesh ); + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::BindBatch, pVertices, pAutoBind ); +} + +void CMatQueuedRenderContext::DrawBatch(int firstIndex, int numIndices ) +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::DrawBatch, firstIndex, numIndices ); +} + +void CMatQueuedRenderContext::EndBatch() +{ + m_queue.QueueCall( m_pHardwareContext, &IMatRenderContext::EndBatch ); +} + +void CMatQueuedRenderContext::DeferredBeginBatch( uint16 *pIndexData, int nIndices ) +{ + m_pQueuedMesh->DeferredGetDynamicMesh( 0, false, NULL, NULL, NULL ); + m_pQueuedMesh->ExecuteDefferredBuild( NULL, 0, 0, pIndexData, nIndices ); + m_pHardwareContext->BeginBatch( m_pQueuedMesh->DetachActualMesh() ); +} + +//----------------------------------------------------------------------------- +// Memory allocation calls for queued mesh, et. al. +//----------------------------------------------------------------------------- +byte *CMatQueuedRenderContext::AllocVertices( int nVerts, int nVertexSize ) +{ + MEM_ALLOC_CREDIT(); + +#if defined(_WIN32) && !defined(_X360) + const byte *pNextAlloc = (const byte *)(m_Vertices.GetBase()) + m_Vertices.GetUsed() + AlignValue( nVerts * nVertexSize, 16 ); + const byte *pCommitLimit = (const byte *)(m_Vertices.GetBase()) + m_Vertices.GetSize(); +#endif + + void *pResult = m_Vertices.Alloc( nVerts * nVertexSize, false ); +#if defined(_WIN32) && !defined(_X360) + if ( !pResult ) + { + // Force a crash with useful minidump info in the registers. + uint64 status = 0x31415926; + + // Print some information to the console so that it's picked up in the minidump comment. + Msg( "AllocVertices( %d, %d ) on %p failed. m_Vertices is based at %p with a size of 0x%x.\n", nVerts, nVertexSize, this, m_Vertices.GetBase(), m_Vertices.GetSize() ); + Msg( "%d vertices used.\n", m_Vertices.GetUsed() ); + if ( pNextAlloc > pCommitLimit ) + { + Msg( "VirtualAlloc would have been called. %p > %p.\n", pNextAlloc, pCommitLimit ); + + const byte *pNewCommitLimit = AlignValue( pNextAlloc, 128 * 1024 ); + const uint32 commitSize = pNewCommitLimit - pCommitLimit; + const void *pRet = VirtualAlloc( (void *)pCommitLimit, commitSize, MEM_COMMIT, PAGE_READWRITE ); + if ( !pRet ) + status = GetLastError(); + + Msg( "VirtualAlloc( %p, %d ) returned %p on repeat. VirtualAlloc %s with code %x.\n", pCommitLimit, commitSize, pRet, (pRet != NULL) ? "succeeded" : "failed", (uint32) status ); + } + else + { + Msg( "VirtualAlloc would not have been called. %p <= %p.\n", pNextAlloc, pCommitLimit ); + } + + // Now crash. + *(volatile uint64 *)0 = status << 32 | m_Vertices.GetUsed(); + } +#endif + return (byte *) pResult; +} + +uint16 *CMatQueuedRenderContext::AllocIndices( int nIndices ) +{ + MEM_ALLOC_CREDIT(); + +#if defined(_WIN32) && !defined(_X360) + const byte *pNextAlloc = (const byte *)(m_Indices.GetBase()) + m_Indices.GetUsed() + AlignValue( nIndices * sizeof(uint16), 16 ); + const byte *pCommitLimit = (const byte *)(m_Indices.GetBase()) + m_Indices.GetSize(); +#endif + + void *pResult = m_Indices.Alloc( nIndices * sizeof(uint16), false ); +#if defined(_WIN32) && !defined(_X360) + if ( !pResult ) + { + // Force a crash with useful minidump info in the registers. + uint64 status = 0x31415926; + + // Print some information to the console so that it's picked up in the minidump comment. + Msg( "AllocIndices( %d ) on %p failed. m_Indices is based at %p with a size of 0x%x.\n", nIndices, this, m_Indices.GetBase(), m_Indices.GetSize() ); + Msg( "%d indices used.\n", m_Indices.GetUsed() ); + if ( pNextAlloc > pCommitLimit ) + { + Msg( "VirtualAlloc would have been called. %p > %p.\n", pNextAlloc, pCommitLimit ); + + const byte *pNewCommitLimit = AlignValue( pNextAlloc, 128 * 1024 ); + const uint32 commitSize = pNewCommitLimit - pCommitLimit; + const void *pRet = VirtualAlloc( (void *)pCommitLimit, commitSize, MEM_COMMIT, PAGE_READWRITE ); + if ( !pRet ) + status = GetLastError(); + + Msg( "VirtualAlloc( %p, %d ) returned %p on repeat. VirtualAlloc %s with code %x.\n", pCommitLimit, commitSize, pRet, (pRet != NULL) ? "succeeded" : "failed", (uint32) status ); + } + else + { + Msg( "VirtualAlloc would not have been called. %p <= %p.\n", pNextAlloc, pCommitLimit ); + } + + // Now crash. + *(volatile uint64 *)0 = status << 32 | m_Indices.GetUsed(); + } +#endif + return (uint16 *) pResult; +} + +byte *CMatQueuedRenderContext::ReallocVertices( byte *pVerts, int nVertsOld, int nVertsNew, int nVertexSize ) +{ + Assert( nVertsNew <= nVertsOld ); + + if ( nVertsNew < nVertsOld ) + { + unsigned nBytes = ( ( nVertsOld - nVertsNew ) * nVertexSize ); + m_Vertices.FreeToAllocPoint( AlignValue( m_Vertices.GetCurrentAllocPoint() - nBytes, 16), false ); // memstacks 128 bit aligned + } + return pVerts; +} + +uint16 *CMatQueuedRenderContext::ReallocIndices( uint16 *pIndices, int nIndicesOld, int nIndicesNew ) +{ + Assert( nIndicesNew <= nIndicesOld ); + if ( nIndicesNew < nIndicesOld ) + { + unsigned nBytes = ( ( nIndicesOld - nIndicesNew ) * sizeof(uint16) ); + m_Indices.FreeToAllocPoint( AlignValue( m_Indices.GetCurrentAllocPoint() - nBytes, 16 ), false ); // memstacks 128 bit aligned + } + return pIndices; +} + +void CMatQueuedRenderContext::FreeVertices( byte *pVerts, int nVerts, int nVertexSize ) +{ + // free at end of call dispatch +} + +void CMatQueuedRenderContext::FreeIndices( uint16 *pIndices, int nIndices ) +{ + // free at end of call dispatch +} + + + +//------------------------------------------------------------------------------ +// Color correction related methods +//------------------------------------------------------------------------------ +ColorCorrectionHandle_t CMatQueuedRenderContext::AddLookup( const char *pName ) +{ + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + ColorCorrectionHandle_t hCC = ColorCorrectionSystem()->AddLookup( pName ); + m_pMaterialSystem->Unlock( hLock ); + return hCC; +} + +bool CMatQueuedRenderContext::RemoveLookup( ColorCorrectionHandle_t handle ) +{ + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + bool bRemoved = ColorCorrectionSystem()->RemoveLookup( handle ); + m_pMaterialSystem->Unlock( hLock ); + return bRemoved; +} + +void CMatQueuedRenderContext::LockLookup( ColorCorrectionHandle_t handle ) +{ + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + ColorCorrectionSystem()->LockLookup( handle ); + m_pMaterialSystem->Unlock( hLock ); +} + +void CMatQueuedRenderContext::LoadLookup( ColorCorrectionHandle_t handle, const char *pLookupName ) +{ + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + ColorCorrectionSystem()->LoadLookup( handle, pLookupName ); + m_pMaterialSystem->Unlock( hLock ); +} + +void CMatQueuedRenderContext::UnlockLookup( ColorCorrectionHandle_t handle ) +{ + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + ColorCorrectionSystem()->UnlockLookup( handle ); + m_pMaterialSystem->Unlock( hLock ); +} + +// NOTE: These are synchronous calls! The rendering thread is stopped, the current queue is drained and the pixels are read +// NOTE: We should also have a queued read pixels in the API for doing mid frame reads (as opposed to screenshots) +void CMatQueuedRenderContext::ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ) +{ + EndRender(); + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + this->CallQueued(false); + g_pShaderAPI->ReadPixels( x, y, width, height, data, dstFormat ); + m_pMaterialSystem->Unlock( hLock ); + BeginRender(); +} + +void CMatQueuedRenderContext::ReadPixelsAndStretch( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pBuffer, ImageFormat dstFormat, int nDstStride ) +{ + EndRender(); + MaterialLock_t hLock = m_pMaterialSystem->Lock(); + this->CallQueued(false); + g_pShaderAPI->ReadPixels( pSrcRect, pDstRect, pBuffer, dstFormat, nDstStride ); + m_pMaterialSystem->Unlock( hLock ); + BeginRender(); +} + |