diff options
Diffstat (limited to 'togl')
| -rw-r--r-- | togl/linuxwin/cglmbuffer.cpp | 1215 | ||||
| -rw-r--r-- | togl/linuxwin/cglmfbo.cpp | 355 | ||||
| -rw-r--r-- | togl/linuxwin/cglmprogram.cpp | 1570 | ||||
| -rw-r--r-- | togl/linuxwin/cglmquery.cpp | 363 | ||||
| -rw-r--r-- | togl/linuxwin/cglmtex.cpp | 1990 | ||||
| -rw-r--r-- | togl/linuxwin/dx9asmtogl2.cpp | 3838 | ||||
| -rw-r--r-- | togl/linuxwin/dx9asmtogl2.h | 261 | ||||
| -rw-r--r-- | togl/linuxwin/dxabstract.cpp | 6845 | ||||
| -rw-r--r-- | togl/linuxwin/glentrypoints.cpp | 510 | ||||
| -rw-r--r-- | togl/linuxwin/glmgr.cpp | 6092 | ||||
| -rw-r--r-- | togl/linuxwin/glmgr_flush.inl | 622 | ||||
| -rw-r--r-- | togl/linuxwin/glmgrbasics.cpp | 4684 | ||||
| -rw-r--r-- | togl/linuxwin/glmgrcocoa.mm | 34 | ||||
| -rw-r--r-- | togl/linuxwin/glmtexinlines.h | 29 | ||||
| -rw-r--r-- | togl/linuxwin/intelglmallocworkaround.cpp | 71 | ||||
| -rw-r--r-- | togl/linuxwin/intelglmallocworkaround.h | 61 | ||||
| -rw-r--r-- | togl/linuxwin/mach_override.c | 765 | ||||
| -rw-r--r-- | togl/linuxwin/mach_override.h | 76 | ||||
| -rw-r--r-- | togl/togl.vpc | 115 |
19 files changed, 29496 insertions, 0 deletions
diff --git a/togl/linuxwin/cglmbuffer.cpp b/togl/linuxwin/cglmbuffer.cpp new file mode 100644 index 0000000..ecba047 --- /dev/null +++ b/togl/linuxwin/cglmbuffer.cpp @@ -0,0 +1,1215 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmbuffer.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +// 7LS TODO : took out cmdline here +bool g_bUsePseudoBufs = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_enable_pseudobufs" ) != NULL ) : false; +#ifdef OSX +// Significant perf degradation on some OSX parts if static buffers not disabled +bool g_bDisableStaticBuffer = true; +#else +bool g_bDisableStaticBuffer = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_disable_static_buffer" ) != NULL ) : false; +#endif + +// http://www.opengl.org/registry/specs/ARB/vertex_buffer_object.txt +// http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt + +// gl_bufmode: zero means we mark all vertex/index buffers static + +// non zero means buffers are initially marked static.. +// ->but can shift to dynamic upon first 'discard' (orphaning) + +// #define REPORT_LOCK_TIME 0 + +ConVar gl_bufmode( "gl_bufmode", "1" ); + +char ALIGN16 CGLMBuffer::m_StaticBuffers[ GL_MAX_STATIC_BUFFERS ][ GL_STATIC_BUFFER_SIZE ] ALIGN16_POST; +bool CGLMBuffer::m_bStaticBufferUsed[ GL_MAX_STATIC_BUFFERS ]; + +extern bool g_bNullD3DDevice; + +//===========================================================================// + +static uint gMaxPersistentOffset[kGLMNumBufferTypes] = +{ + 0, + 0, + 0, + 0 +}; +CON_COMMAND( gl_persistent_buffer_max_offset, "" ) +{ + ConMsg( "OpenGL Persistent buffer max offset :\n" ); + ConMsg( " Vertex buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMVertexBuffer], gMaxPersistentOffset[kGLMVertexBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Index buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMIndexBuffer], gMaxPersistentOffset[kGLMIndexBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Uniform buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMUniformBuffer], gMaxPersistentOffset[kGLMUniformBuffer] / (1024.0f*1024.0f) ); + ConMsg( " Pixel buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMPixelBuffer], gMaxPersistentOffset[kGLMPixelBuffer] / (1024.0f*1024.0f) ); +} + +CPersistentBuffer::CPersistentBuffer() +: + m_nSize( 0 ) + , m_nHandle( 0 ) + , m_pImmutablePersistentBuf( NULL ) + , m_nOffset( 0 ) +#ifdef HAVE_GL_ARB_SYNC + , m_nSyncObj( 0 ) +#endif +{} + +CPersistentBuffer::~CPersistentBuffer() +{ + Deinit(); +} + +void CPersistentBuffer::Init( EGLMBufferType type,uint nSize ) +{ + Assert( gGL->m_bHave_GL_ARB_buffer_storage ); + Assert( gGL->m_bHave_GL_ARB_map_buffer_range ); + + m_nSize = nSize; + m_nOffset = 0; + m_type = type; + + switch ( type ) + { + case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + + default: Assert( nSize == 0 ); + } + + if ( m_nSize > 0 ) + { + gGL->glGenBuffersARB( 1, &m_nHandle ); + gGL->glBindBufferARB( m_buffGLTarget, m_nHandle ); + + // Create persistent immutable buffer that we will permanently map. This buffer can be written from any thread (not just + // the renderthread) + gGL->glBufferStorage( m_buffGLTarget, m_nSize, (const GLvoid *)NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_buffer_storage, GL_ARB_map_buffer_range, GL_VERSION_4_4 + + // Map the buffer for all of eternity. Pointer can be used from multiple threads. + m_pImmutablePersistentBuf = gGL->glMapBufferRange( m_buffGLTarget, 0, m_nSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_map_buffer_range, GL_ARB_buffer_storage, GL_VERSION_4_4 + Assert( m_pImmutablePersistentBuf != NULL ); + } +} + +void CPersistentBuffer::Deinit() +{ + if ( !m_pImmutablePersistentBuf ) + { + return; + } + + BlockUntilNotBusy(); + + gGL->glBindBufferARB( m_buffGLTarget, m_nHandle ); + gGL->glUnmapBuffer( m_buffGLTarget ); + gGL->glBindBufferARB( m_buffGLTarget, 0 ); + + gGL->glDeleteBuffersARB( 1, &m_nHandle ); + + m_nSize = 0; + m_nHandle = 0; + m_nOffset = 0; + m_pImmutablePersistentBuf = NULL; +} + +void CPersistentBuffer::InsertFence() +{ +#ifdef HAVE_GL_ARB_SYNC + if (m_nSyncObj) + { + gGL->glDeleteSync( m_nSyncObj ); + } + + m_nSyncObj = gGL->glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); +#endif +} + +void CPersistentBuffer::BlockUntilNotBusy() +{ +#ifdef HAVE_GL_ARB_SYNC + if (m_nSyncObj) + { + gGL->glClientWaitSync( m_nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 3000000000000ULL ); + + gGL->glDeleteSync( m_nSyncObj ); + + m_nSyncObj = 0; + } +#endif + m_nOffset = 0; +} + +void CPersistentBuffer::Append( uint nSize ) +{ + m_nOffset += nSize; + Assert( m_nOffset <= m_nSize ); + + gMaxPersistentOffset[m_type] = Max( m_nOffset, gMaxPersistentOffset[m_type] ); +} + +//===========================================================================// + +#if GL_ENABLE_INDEX_VERIFICATION + +CGLMBufferSpanManager::CGLMBufferSpanManager() : + m_pCtx( NULL ), + m_nBufType( kGLMVertexBuffer ), + m_nBufSize( 0 ), + m_bDynamic( false ), + m_nSpanEndMax( -1 ), + m_nNumAllocatedBufs( 0 ), + m_nTotalBytesAllocated( 0 ) +{ +} + +CGLMBufferSpanManager::~CGLMBufferSpanManager() +{ + Deinit(); +} + +void CGLMBufferSpanManager::Init( GLMContext *pContext, EGLMBufferType nBufType, uint nInitialCapacity, uint nBufSize, bool bDynamic ) +{ + Assert( ( nBufType == kGLMIndexBuffer ) || ( nBufType == kGLMVertexBuffer ) ); + + m_pCtx = pContext; + m_nBufType = nBufType; + + m_nBufSize = nBufSize; + m_bDynamic = bDynamic; + + m_ActiveSpans.EnsureCapacity( nInitialCapacity ); + m_DeletedSpans.EnsureCapacity( nInitialCapacity ); + m_nSpanEndMax = -1; + + m_nNumAllocatedBufs = 0; + m_nTotalBytesAllocated = 0; +} + +bool CGLMBufferSpanManager::AllocDynamicBuf( uint nSize, GLDynamicBuf_t &buf ) +{ + buf.m_nGLType = GetGLBufType(); + buf.m_nActualBufSize = nSize; + buf.m_nHandle = 0; + buf.m_nSize = nSize; + + m_nNumAllocatedBufs++; + m_nTotalBytesAllocated += buf.m_nActualBufSize; + + return true; +} + +void CGLMBufferSpanManager::ReleaseDynamicBuf( GLDynamicBuf_t &buf ) +{ + Assert( m_nNumAllocatedBufs > 0 ); + m_nNumAllocatedBufs--; + + Assert( m_nTotalBytesAllocated >= (int)buf.m_nActualBufSize ); + m_nTotalBytesAllocated -= buf.m_nActualBufSize; +} + +void CGLMBufferSpanManager::Deinit() +{ + if ( !m_pCtx ) + return; + + for ( int i = 0; i < m_ActiveSpans.Count(); i++ ) + { + if ( m_ActiveSpans[i].m_bOriginalAlloc ) + ReleaseDynamicBuf( m_ActiveSpans[i].m_buf ); + } + m_ActiveSpans.SetCountNonDestructively( 0 ); + + for ( int i = 0; i < m_DeletedSpans.Count(); i++ ) + ReleaseDynamicBuf( m_DeletedSpans[i].m_buf ); + + m_DeletedSpans.SetCountNonDestructively( 0 ); + + m_pCtx->BindGLBufferToCtx( GetGLBufType(), NULL, true ); + + m_nSpanEndMax = -1; + m_pCtx = NULL; + + Assert( !m_nNumAllocatedBufs ); + Assert( !m_nTotalBytesAllocated ); +} + +void CGLMBufferSpanManager::DiscardAllSpans() +{ + for ( int i = 0; i < m_ActiveSpans.Count(); i++ ) + { + if ( m_ActiveSpans[i].m_bOriginalAlloc ) + ReleaseDynamicBuf( m_ActiveSpans[i].m_buf ); + } + m_ActiveSpans.SetCountNonDestructively( 0 ); + + for ( int i = 0; i < m_DeletedSpans.Count(); i++ ) + ReleaseDynamicBuf( m_DeletedSpans[i].m_buf ); + + m_DeletedSpans.SetCountNonDestructively( 0 ); + + m_nSpanEndMax = -1; + + Assert( !m_nNumAllocatedBufs ); + Assert( !m_nTotalBytesAllocated ); +} + +// TODO: Add logic to detect incorrect usage of bNoOverwrite. +CGLMBufferSpanManager::ActiveSpan_t *CGLMBufferSpanManager::AddSpan( uint nOffset, uint nMaxSize, uint nActualSize, bool bDiscard, bool bNoOverwrite ) +{ + (void)bDiscard; + (void)bNoOverwrite; + + const uint nStart = nOffset; + const uint nSize = nActualSize; + const uint nEnd = nStart + nSize; + + GLDynamicBuf_t newDynamicBuf; + if ( !AllocDynamicBuf( nSize, newDynamicBuf ) ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; + } + + if ( (int)nStart < m_nSpanEndMax ) + { + // Lock region potentially overlaps another previously locked region (since the last discard) - this is a very rarely (if ever) taken path in Source1 games. + int i = 0; + while ( i < m_ActiveSpans.Count() ) + { + ActiveSpan_t &existingSpan = m_ActiveSpans[i]; + if ( ( nEnd <= existingSpan.m_nStart ) || ( nStart >= existingSpan.m_nEnd ) ) + { + i++; + continue; + } + + Warning( "GL performance warning: AddSpan() at offset %u max size %u actual size %u, on a %s %s buffer of total size %u, overwrites an existing active lock span at offset %u size %u!\n", + nOffset, nMaxSize, nActualSize, + m_bDynamic ? "dynamic" : "static", ( m_nBufType == kGLMVertexBuffer ) ? "vertex" : "index", m_nBufSize, + existingSpan.m_nStart, existingSpan.m_nEnd - existingSpan.m_nStart ); + + if ( ( nStart <= existingSpan.m_nStart ) && ( nEnd >= existingSpan.m_nEnd ) ) + { + if ( existingSpan.m_bOriginalAlloc ) + { + // New span totally covers existing span + // Can't immediately delete the span's buffer because it could be referred to by another (child) span. + m_DeletedSpans.AddToTail( existingSpan ); + } + + // Delete span + m_ActiveSpans[i] = m_ActiveSpans[ m_ActiveSpans.Count() - 1 ]; + m_ActiveSpans.SetCountNonDestructively( m_ActiveSpans.Count() - 1 ); + continue; + } + + // New span does NOT fully cover the existing span (partial overlap) + if ( nStart < existingSpan.m_nStart ) + { + // New span starts before existing span, but ends somewhere inside, so shrink it (start moves "right") + existingSpan.m_nStart = nEnd; + } + else if ( nEnd > existingSpan.m_nEnd ) + { + // New span ends after existing span, but starts somewhere inside (end moves "left") + existingSpan.m_nEnd = nStart; + } + else //if ( ( nStart >= existingSpan.m_nStart ) && ( nEnd <= existingSpan.m_nEnd ) ) + { + // New span lies inside of existing span + if ( nStart == existingSpan.m_nStart ) + { + // New span begins inside the existing span (start moves "right") + existingSpan.m_nStart = nEnd; + } + else + { + if ( nEnd < existingSpan.m_nEnd ) + { + // New span is completely inside existing span + m_ActiveSpans.AddToTail( ActiveSpan_t( nEnd, existingSpan.m_nEnd, existingSpan.m_buf, false ) ); + } + + existingSpan.m_nEnd = nStart; + } + } + + Assert( existingSpan.m_nStart < existingSpan.m_nEnd ); + i++; + } + } + + newDynamicBuf.m_nLockOffset = nStart; + newDynamicBuf.m_nLockSize = nSize; + + m_ActiveSpans.AddToTail( ActiveSpan_t( nStart, nEnd, newDynamicBuf, true ) ); + m_nSpanEndMax = MAX( m_nSpanEndMax, (int)nEnd ); + + return &m_ActiveSpans.Tail(); +} + +bool CGLMBufferSpanManager::IsValid( uint nOffset, uint nSize ) const +{ + const uint nEnd = nOffset + nSize; + + int nTotalBytesRemaining = nSize; + + for ( int i = m_ActiveSpans.Count() - 1; i >= 0; --i ) + { + const ActiveSpan_t &span = m_ActiveSpans[i]; + + if ( span.m_nEnd <= nOffset ) + continue; + if ( span.m_nStart >= nEnd ) + continue; + + uint nIntersectStart = MAX( span.m_nStart, nOffset ); + uint nIntersectEnd = MIN( span.m_nEnd, nEnd ); + Assert( nIntersectStart <= nIntersectEnd ); + + nTotalBytesRemaining -= ( nIntersectEnd - nIntersectStart ); + Assert( nTotalBytesRemaining >= 0 ); + if ( nTotalBytesRemaining <= 0 ) + break; + } + + return nTotalBytesRemaining == 0; +} +#endif // GL_ENABLE_INDEX_VERIFICATION + +// glBufferSubData() with a max size limit, to work around NVidia's threaded driver limits (anything > than roughly 256KB triggers a sync with the server thread). +void glBufferSubDataMaxSize( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data, uint nMaxSizePerCall ) +{ +#if TOGL_SUPPORT_NULL_DEVICE + if ( g_bNullD3DDevice ) return; +#endif + + uint nBytesLeft = size; + uint nOfs = 0; + while ( nBytesLeft ) + { + uint nBytesToCopy = MIN( nMaxSizePerCall, nBytesLeft ); + + gGL->glBufferSubData( target, offset + nOfs, nBytesToCopy, static_cast<const unsigned char *>( data ) + nOfs ); + + nBytesLeft -= nBytesToCopy; + nOfs += nBytesToCopy; + } +} + +CGLMBuffer::CGLMBuffer( GLMContext *pCtx, EGLMBufferType type, uint size, uint options ) +{ + m_pCtx = pCtx; + m_type = type; + + m_bDynamic = ( options & GLMBufferOptionDynamic ) != 0; + + switch ( m_type ) + { + case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break; + case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break; + + default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR(); + } + + m_nSize = size; + m_nActualSize = size; + m_bMapped = false; + m_pLastMappedAddress = NULL; + + m_pStaticBuffer = NULL; + m_nPinnedMemoryOfs = -1; + m_nPersistentBufferStartOffset = 0; + m_bUsingPersistentBuffer = false; + + m_bEnableAsyncMap = false; + m_bEnableExplicitFlush = false; + m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock + + m_pCtx->CheckCurrent(); + m_nRevision = rand(); + + m_pPseudoBuf = NULL; + m_pActualPseudoBuf = NULL; + + m_bPseudo = false; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + m_bPseudo = true; +#endif + +#if GL_ENABLE_INDEX_VERIFICATION + m_BufferSpanManager.Init( m_pCtx, m_type, 512, m_nSize, m_bDynamic ); + + if ( m_type == kGLMIndexBuffer ) + m_bPseudo = true; +#endif + + if ( g_bUsePseudoBufs && m_bDynamic ) + { + m_bPseudo = true; + } + + if ( m_bPseudo ) + { + m_nHandle = 0; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + m_nDirtyRangeStart = 0xFFFFFFFF; + m_nDirtyRangeEnd = 0; + + m_nActualSize = ALIGN_VALUE( ( m_nSize + sizeof( uint32 ) ), 4096 ); + m_pPseudoBuf = m_pActualPseudoBuf = (char *)VirtualAlloc( NULL, m_nActualSize, MEM_COMMIT, PAGE_READWRITE ); + if ( !m_pPseudoBuf ) + { + Error( "VirtualAlloc() failed!\n" ); + } + + for ( uint i = 0; i < m_nActualSize / sizeof( uint32 ); i++ ) + { + reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF; + } + + DWORD nOldProtect; + BOOL bResult = VirtualProtect( m_pActualPseudoBuf, m_nActualSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#else + m_nActualSize = size + 15; + m_pActualPseudoBuf = (char*)malloc( m_nActualSize ); + m_pPseudoBuf = (char*)(((intp)m_pActualPseudoBuf + 15) & ~15); +#endif + + m_pCtx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound + } + else + { + gGL->glGenBuffersARB( 1, &m_nHandle ); + + m_pCtx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB + + // buffers start out static, but if they get orphaned and gl_bufmode is non zero, + // then they will get flipped to dynamic. + + GLenum hint = GL_STATIC_DRAW_ARB; + switch (m_type) + { + case kGLMVertexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + case kGLMIndexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break; + case kGLMPixelBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break; + + default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR(); + } + + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming) + + SetModes( false, true, true ); + + m_pCtx->BindBufferToCtx( m_type, NULL ); // unbind me + } +} + +CGLMBuffer::~CGLMBuffer( ) +{ + m_pCtx->CheckCurrent(); + + if ( m_bPseudo ) + { +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + BOOL bResult = VirtualFree( m_pActualPseudoBuf, 0, MEM_RELEASE ); + if ( !bResult ) + { + Error( "VirtualFree() failed!\n" ); + } +#else + free( m_pActualPseudoBuf ); +#endif + m_pActualPseudoBuf = NULL; + m_pPseudoBuf = NULL; + } + else + { + gGL->glDeleteBuffersARB( 1, &m_nHandle ); + } + + m_pCtx = NULL; + m_nHandle = 0; + + m_pLastMappedAddress = NULL; + +#if GL_ENABLE_INDEX_VERIFICATION + m_BufferSpanManager.Deinit(); +#endif +} + +void CGLMBuffer::SetModes( bool bAsyncMap, bool bExplicitFlush, bool bForce ) +{ + // assumes buffer is bound. called by constructor and by Lock. + + if ( m_bPseudo ) + { + // ignore it... + } + else + { + if ( bForce || ( m_bEnableAsyncMap != bAsyncMap ) ) + { + // note the sense of the parameter, it's TRUE if you *want* serialization, so for async you turn it to false. + if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) ) + { + gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_SERIALIZED_MODIFY_APPLE, bAsyncMap == false ); + } + m_bEnableAsyncMap = bAsyncMap; + } + + if ( bForce || ( m_bEnableExplicitFlush != bExplicitFlush ) ) + { + // Note that the GL_ARB_map_buffer_range path handles this in the glMapBufferRange() call in Lock(). + // note the sense of the parameter, it's TRUE if you *want* auto-flush-on-unmap, so for explicit-flush, you turn it to false. + if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) ) + { + gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_FLUSHING_UNMAP_APPLE, bExplicitFlush == false ); + } + m_bEnableExplicitFlush = bExplicitFlush; + } + } +} + +#if GL_ENABLE_INDEX_VERIFICATION +bool CGLMBuffer::IsSpanValid( uint nOffset, uint nSize ) const +{ + return m_BufferSpanManager.IsValid( nOffset, nSize ); +} +#endif + +void CGLMBuffer::FlushRange( uint offset, uint size ) +{ + if ( m_pStaticBuffer ) + { + } + else if ( m_bPseudo ) + { + // nothing to do + } + else + { +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + // assumes buffer is bound. + if ( gGL->m_bHave_GL_ARB_map_buffer_range ) + { + gGL->glFlushMappedBufferRange( m_buffGLTarget, (GLintptr)( offset - m_dirtyMinOffset ), (GLsizeiptr)size ); + } + else if ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) + { + gGL->glFlushMappedBufferRangeAPPLE( m_buffGLTarget, (GLintptr)offset, (GLsizeiptr)size ); + } + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glFlushMappedBufferRange Time %d: ( Name=%d BufSize=%d ) Target=%p Offset=%d FlushSize=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, offset - m_dirtyMinOffset, size ); + } +#endif + + // If you don't have any extension support here, you'll flush the whole buffer on unmap. Performance loss, but it's still safe and correct. + } +} + +void CGLMBuffer::Lock( GLMBuffLockParams *pParams, char **pAddressOut ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Lock" ); + g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++; +#endif + + char *resultPtr = NULL; + + if ( m_bMapped ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + m_pCtx->CheckCurrent(); + + Assert( pParams->m_nSize ); + + m_LockParams = *pParams; + + if ( pParams->m_nOffset >= m_nSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + if ( ( pParams->m_nOffset + pParams->m_nSize ) > m_nSize) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + +#if GL_ENABLE_INDEX_VERIFICATION + if ( pParams->m_bDiscard ) + { + m_BufferSpanManager.DiscardAllSpans(); + } +#endif + + m_pStaticBuffer = NULL; + bool bUsingPersistentBuffer = false; + + uint padding = 0; + if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage ) + { + // Compute padding to add to make sure the start offset is valid + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + uint persistentBufferOffset = pTempBuffer->GetOffset(); + + if (pParams->m_nOffset > persistentBufferOffset) + { + // Make sure the start offset if valid (adding padding to the persistent buffer) + padding = pParams->m_nOffset - persistentBufferOffset; + } + } + + if ( m_bPseudo ) + { + if ( pParams->m_bDiscard ) + { + m_nRevision++; + } + + // async map modes are a no-op + + // calc lock address + resultPtr = m_pPseudoBuf + pParams->m_nOffset; + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + BOOL bResult; + DWORD nOldProtect; + if ( pParams->m_bDiscard ) + { + bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READWRITE, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } + + m_nDirtyRangeStart = 0xFFFFFFFF; + m_nDirtyRangeEnd = 0; + + for ( uint i = 0; i < m_nSize / sizeof( uint32 ); i++ ) + { + reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF; + } + + bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } + } + uint nProtectOfs = m_LockParams.m_nOffset & 4095; + uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095; + uint nProtectSize = nProtectEnd - nProtectOfs; + bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READWRITE, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#endif + } + else if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage && ( m_pCtx->GetCurPersistentBuffer( m_type )->GetBytesRemaining() >= ( pParams->m_nSize + padding ) ) ) + { + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + + // Make sure the start offset if valid (adding padding to the persistent buffer) + pTempBuffer->Append( padding ); + + uint persistentBufferOffset = pTempBuffer->GetOffset(); + uint startOffset = persistentBufferOffset - pParams->m_nOffset; + + if ( pParams->m_bDiscard || ( startOffset != m_nPersistentBufferStartOffset ) ) + { + m_nRevision++; + // Offset to be added to the vertex and index buffer when setting the vertex and index buffer (before drawing) + // Since we are using a immutable buffer storage, the persistent buffer is actually bigger than + // buffer size requested upon creation. We keep appending to the end of the persistent buffer + // and therefore need to keep track of the start of the actual buffer (in the persistent one) + m_nPersistentBufferStartOffset = startOffset; + + //DevMsg( "Discard (%s): startOffset = %d\n", pParams->m_bDiscard ? "true" : "false", m_nPersistentBufferStartOffset ); + } + + resultPtr = static_cast<char*>(pTempBuffer->GetPtr()) + persistentBufferOffset; + bUsingPersistentBuffer = true; + + //DevMsg( " --> buff=%x, startOffset=%d, paramsOffset=%d, persistOffset = %d\n", this, m_nPersistentBufferStartOffset, pParams->m_nOffset, persistentBufferOffset ); + } +#ifndef OSX + else if ( m_bDynamic && gGL->m_bHave_GL_AMD_pinned_memory && ( m_pCtx->GetCurPinnedMemoryBuffer()->GetBytesRemaining() >= pParams->m_nSize ) ) + { + if ( pParams->m_bDiscard ) + { + m_nRevision++; + } + + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + + CPinnedMemoryBuffer *pTempBuffer = m_pCtx->GetCurPinnedMemoryBuffer(); + + m_nPinnedMemoryOfs = pTempBuffer->GetOfs(); + + resultPtr = static_cast<char*>( pTempBuffer->GetPtr() ) + m_nPinnedMemoryOfs; + + pTempBuffer->Append( pParams->m_nSize ); + } +#endif // OSX + else if ( !g_bDisableStaticBuffer && ( pParams->m_bDiscard || pParams->m_bNoOverwrite ) && ( pParams->m_nSize <= GL_STATIC_BUFFER_SIZE ) ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) +#endif + { + if ( pParams->m_bDiscard ) + { + m_pCtx->BindBufferToCtx( m_type, this ); + + // observe gl_bufmode on any orphan event. + // if orphaned and bufmode is nonzero, flip it to dynamic. + GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); + + m_nRevision++; // revision grows on orphan event + } + } + + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + + switch ( m_type ) + { + case kGLMVertexBuffer: + { + m_pStaticBuffer = m_StaticBuffers[ 0 ]; + break; + } + case kGLMIndexBuffer: + { + m_pStaticBuffer = m_StaticBuffers[ 1 ]; + break; + } + default: + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + } + + resultPtr = m_pStaticBuffer; + } + else + { + // bind (yes, even for pseudo - this binds name 0) + m_pCtx->BindBufferToCtx( m_type, this ); + + // perform discard if requested + if ( pParams->m_bDiscard ) + { + // observe gl_bufmode on any orphan event. + // if orphaned and bufmode is nonzero, flip it to dynamic. + + // We always want to call glBufferData( ..., NULL ) on discards, even though we're using the GL_MAP_INVALIDATE_BUFFER_BIT flag, because this flag is actually only a hint according to AMD. + GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; + gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); + + m_nRevision++; // revision grows on orphan event + } + + // adjust async map option appropriately, leave explicit flush unchanged + SetModes( pParams->m_bNoOverwrite, m_bEnableExplicitFlush ); + + // map + char *mapPtr; + if ( gGL->m_bHave_GL_ARB_map_buffer_range ) + { + // m_bEnableAsyncMap is actually pParams->m_bNoOverwrite + GLbitfield parms = GL_MAP_WRITE_BIT | ( m_bEnableAsyncMap ? GL_MAP_UNSYNCHRONIZED_BIT : 0 ) | ( pParams->m_bDiscard ? GL_MAP_INVALIDATE_BUFFER_BIT : 0 ) | ( m_bEnableExplicitFlush ? GL_MAP_FLUSH_EXPLICIT_BIT : 0 ); + +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + mapPtr = (char*)gGL->glMapBufferRange( m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize, parms); + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glMapBufferRange Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d LockSize=%d ", nDelta, m_nHandle, m_nSize, m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize ); + if ( parms & GL_MAP_WRITE_BIT ) + { + Msg( "GL_MAP_WRITE_BIT "); + } + if ( parms & GL_MAP_UNSYNCHRONIZED_BIT ) + { + Msg( "GL_MAP_UNSYNCHRONIZED_BIT "); + } + if ( parms & GL_MAP_INVALIDATE_BUFFER_BIT ) + { + Msg( "GL_MAP_INVALIDATE_BUFFER_BIT "); + } + if ( parms & GL_MAP_INVALIDATE_RANGE_BIT ) + { + Msg( "GL_MAP_INVALIDATE_RANGE_BIT "); + } + if ( parms & GL_MAP_FLUSH_EXPLICIT_BIT ) + { + Msg( "GL_MAP_FLUSH_EXPLICIT_BIT "); + } + Msg( "\n" ); + } +#endif + } + else + { + mapPtr = (char*)gGL->glMapBufferARB( m_buffGLTarget, GL_WRITE_ONLY_ARB ); + } + + Assert( mapPtr ); + + // calculate offset location + resultPtr = mapPtr; + if ( !gGL->m_bHave_GL_ARB_map_buffer_range ) + { + resultPtr += pParams->m_nOffset; + } + + // set range + m_dirtyMinOffset = pParams->m_nOffset; + m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize; + } + + if ( m_bUsingPersistentBuffer != bUsingPersistentBuffer ) + { + // Up the revision number when switching from a persistent to a non persistent buffer (or vice versa) + // Ensure the right GL buffer is bound before drawing (and vertex attribs properly set) + m_nRevision++; + m_bUsingPersistentBuffer = bUsingPersistentBuffer; + } + + m_bMapped = true; + + m_pLastMappedAddress = (float*)resultPtr; + + *pAddressOut = resultPtr; +} + +void CGLMBuffer::Unlock( int nActualSize, const void *pActualData ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Unlock" ); + g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++; +#endif + + m_pCtx->CheckCurrent(); + + if ( !m_bMapped ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + + if ( nActualSize < 0 ) + { + nActualSize = m_LockParams.m_nSize; + } + + if ( nActualSize > (int)m_LockParams.m_nSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + return; + } + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + if ( m_bPseudo ) + { + // Check guard DWORD to detect buffer overruns (but are still within the last 4KB page so they don't get caught via pagefaults) + if ( *reinterpret_cast< const uint32 * >( m_pPseudoBuf + m_nSize ) != 0xDEADBEEF ) + { + // If this fires the client app has overwritten the guard DWORD beyond the end of the buffer. + DXABSTRACT_BREAK_ON_ERROR(); + } + + static const uint s_nInitialValues[4] = { 0xEF, 0xBE, 0xAD, 0xDE }; + + int nActualModifiedStart, nActualModifiedEnd; + for ( nActualModifiedStart = 0; nActualModifiedStart < (int)m_LockParams.m_nSize; ++nActualModifiedStart ) + if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedStart] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedStart ) & 3 ] ) + break; + + for ( nActualModifiedEnd = m_LockParams.m_nSize - 1; nActualModifiedEnd > nActualModifiedStart; --nActualModifiedEnd ) + if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedEnd] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedEnd ) & 3 ] ) + break; + + int nNumActualBytesModified = 0; + + if ( nActualModifiedEnd >= nActualModifiedStart ) + { + // The modified check is conservative (i.e. it should always err on the side of detecting <= actual bytes than where actually modified, never more). + // We primarily care about the case where the user lies about the actual # of modified bytes, which can lead to difficult to debug/inconsistent problems with some drivers. + // Round up/down the modified range, because the user's data may alias with the initial buffer values (0xDEADBEEF) so we may miss some bytes that where written. + if ( m_type == kGLMIndexBuffer ) + { + nActualModifiedStart &= ~1; + nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 1 ) & ~1 ) - 1; + } + else + { + nActualModifiedStart &= ~3; + nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 3 ) & ~3 ) - 1; + } + + nNumActualBytesModified = nActualModifiedEnd + 1; + + if ( nActualSize < nNumActualBytesModified ) + { + // The caller may be lying about the # of actually modified bytes in this lock. + // Has this lock region been previously locked? If so, it may have been previously overwritten before. Otherwise, the region had to be the 0xDEADBEEF fill DWORD at lock time. + if ( ( m_nDirtyRangeStart > m_nDirtyRangeEnd ) || + ( m_LockParams.m_nOffset > m_nDirtyRangeEnd ) || ( ( m_LockParams.m_nOffset + m_LockParams.m_nSize ) <= m_nDirtyRangeStart ) ) + { + // If this fires the client has lied about the actual # of bytes they've modified in the buffer - this will cause unreliable rendering on AMD drivers (because AMD actually pays attention to the actual # of flushed bytes). + DXABSTRACT_BREAK_ON_ERROR(); + } + } + + m_nDirtyRangeStart = MIN( m_nDirtyRangeStart, m_LockParams.m_nOffset + nActualModifiedStart ); + m_nDirtyRangeEnd = MAX( m_nDirtyRangeEnd, m_LockParams.m_nOffset + nActualModifiedEnd ); + } + +#if GL_ENABLE_INDEX_VERIFICATION + if ( nActualModifiedEnd >= nActualModifiedStart ) + { + int n = nActualModifiedEnd + 1; + if ( n != nActualSize ) + { + // The actual detected modified size is < than the reported size, which is common because the last few DWORD's of the vertex format may not actually be used/written (or read by the vertex shader). So just fudge it so the batch consumption checks work. + if ( ( (int)nActualSize - n ) <= 32 ) + { + n = nActualSize; + } + } + + m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset + nActualModifiedStart, m_LockParams.m_nSize, n - nActualModifiedStart, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite ); + } +#endif + } +#elif GL_ENABLE_INDEX_VERIFICATION + if ( nActualSize > 0 ) + { + m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset, m_LockParams.m_nSize, nActualSize, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite ); + } +#endif + +#if GL_BATCH_PERF_ANALYSIS + if ( m_type == kGLMIndexBuffer ) + g_nTotalIBLockBytes += nActualSize; + else if ( m_type == kGLMVertexBuffer ) + g_nTotalVBLockBytes += nActualSize; +#endif + +#ifndef OSX + if ( m_nPinnedMemoryOfs >= 0 ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) + { +#endif + if ( nActualSize ) + { + m_pCtx->BindBufferToCtx( m_type, this ); + + gGL->glCopyBufferSubData( + GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, + m_buffGLTarget, + m_nPinnedMemoryOfs, + m_dirtyMinOffset, + nActualSize ); + } + +#if TOGL_SUPPORT_NULL_DEVICE + } +#endif + + m_nPinnedMemoryOfs = -1; + } + else +#endif // !OSX + if ( m_bUsingPersistentBuffer ) + { + if ( nActualSize ) + { + CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type ); + pTempBuffer->Append( nActualSize ); + + //DevMsg( " <-- actualSize=%d, persistOffset = %d\n", nActualSize, pTempBuffer->GetOffset() ); + } + } + else if ( m_pStaticBuffer ) + { +#if TOGL_SUPPORT_NULL_DEVICE + if ( !g_bNullD3DDevice ) +#endif + { + if ( nActualSize ) + { + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockSubData" ); + + #ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); + #endif + m_pCtx->BindBufferToCtx( m_type, this ); + + Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) ); + + glBufferSubDataMaxSize( m_buffGLTarget, m_dirtyMinOffset, nActualSize, pActualData ? pActualData : m_pStaticBuffer ); + + #ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + // Msg( "glBufferSubData Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d Size=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, m_dirtyMinOffset, m_dirtyMaxOffset - m_dirtyMinOffset ); + } + #endif + } + } + + m_pStaticBuffer = NULL; + } + else if ( m_bPseudo ) + { + if ( pActualData ) + { + memcpy( m_pLastMappedAddress, pActualData, nActualSize ); + } + +#if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION + uint nProtectOfs = m_LockParams.m_nOffset & 4095; + uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095; + uint nProtectSize = nProtectEnd - nProtectOfs; + + DWORD nOldProtect; + BOOL bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READONLY, &nOldProtect ); + if ( !bResult ) + { + Error( "VirtualProtect() failed!\n" ); + } +#endif + } + else + { + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockUnmap" ); + + if ( pActualData ) + { + memcpy( m_pLastMappedAddress, pActualData, nActualSize ); + } + + m_pCtx->BindBufferToCtx( m_type, this ); + + Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) ); + + // time to do explicit flush (currently m_bEnableExplicitFlush is always true) + if ( m_bEnableExplicitFlush ) + { + FlushRange( m_dirtyMinOffset, nActualSize ); + } + + // clear dirty range no matter what + m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock + +#ifdef REPORT_LOCK_TIME + double flStart = Plat_FloatTime(); +#endif + + gGL->glUnmapBuffer( m_buffGLTarget ); + +#ifdef REPORT_LOCK_TIME + double flEnd = Plat_FloatTime(); + if ( flEnd - flStart > 5.0 / 1000.0 ) + { + int nDelta = ( int )( ( flEnd - flStart ) * 1000 ); + if ( nDelta > 2 ) + { + Msg( "**** " ); + } + Msg( "glUnmapBuffer Time=%d: ( Name=%d BufSize=%d ) Target=%p\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget ); + } +#endif + } + + m_bMapped = false; +} + +GLuint CGLMBuffer::GetHandle() const +{ + return ( m_bUsingPersistentBuffer ? m_pCtx->GetCurPersistentBuffer( m_type )->GetHandle() : m_nHandle ); +} diff --git a/togl/linuxwin/cglmfbo.cpp b/togl/linuxwin/cglmfbo.cpp new file mode 100644 index 0000000..ec2418d --- /dev/null +++ b/togl/linuxwin/cglmfbo.cpp @@ -0,0 +1,355 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmfbo.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +CGLMFBO::CGLMFBO( GLMContext *ctx ) +{ + m_ctx = ctx; + m_ctx->CheckCurrent(); + + gGL->glGenFramebuffersEXT( 1, &m_name ); + + memset( m_attach, 0, sizeof( m_attach ) ); +} + + +CGLMFBO::~CGLMFBO( ) +{ + m_ctx->CheckCurrent(); + + // detach all known attached textures first... necessary ? + for( int index = 0; index < kAttCount; index++) + { + if (m_attach[ index ].m_tex) + { + TexDetach( (EGLMFBOAttachment)index ); + } + } + + gGL->glDeleteFramebuffersEXT( 1, &m_name ); + + m_name = 0; + m_ctx = NULL; +} + +// the tex attach path should also select a specific slice of the texture... +// and we need a way to make renderbuffers.. + +static GLenum EncodeAttachmentFBO( EGLMFBOAttachment index ) +{ + if (index < kAttDepth) + { + return GL_COLOR_ATTACHMENT0_EXT + (int) index; + } + else + { + switch( index ) + { + case kAttDepth: + return GL_DEPTH_ATTACHMENT_EXT; + break; + + case kAttStencil: + return GL_STENCIL_ATTACHMENT_EXT; + break; + + case kAttDepthStencil: + return GL_DEPTH_STENCIL_ATTACHMENT_EXT; + break; + + default: + GLMStop(); // bad news + break; + } + } + + GLMStop(); // bad news + // shouldn't get here + return GL_COLOR_ATTACHMENT0_EXT; +} + +void CGLMFBO::TexAttach( GLMFBOTexAttachParams *params, EGLMFBOAttachment attachIndex, GLenum fboBindPoint ) +{ + // force our parent context to be current + m_ctx->MakeCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this, fboBindPoint ); + + // it's either a plain 2D, a 2D face of a cube map, or a slice of a 3D. + CGLMTex *tex = params->m_tex; + + // always detach what is currently there, if anything + this->TexDetach( attachIndex, fboBindPoint ); + + if (!tex) + { + // andif they pass NULL to us, then we are done. + return; + } + + GLMTexLayout *layout = tex->m_layout; + + GLenum target = tex->m_layout->m_key.m_texGLTarget; + + GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex ); + + switch( target ) + { + case GL_TEXTURE_2D: + { + // we will attach the underlying RBO on a multisampled tex, iff the tex has one, **and** we're not being asked to attach it to the read buffer. + // if we get a req to attach an MSAA tex to the read buffer, chances are it's BlitTex calling, andit has already resolved the tex, so in those + // cases you really do want to attach the texture and not the RBO to the FBO in question. + + bool useRBO = false; // initial state + + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + // it is an MSAA tex + if (fboBindPoint == GL_READ_FRAMEBUFFER_EXT) + { + // I think you just want to read a resolved tex. + // But I will check that it is resolved first.. + Assert( tex->IsRBODirty() == false ); + } + else + { + // you want to draw into it. You get the RBO bound instead of the tex. + useRBO = true; + } + } + + if (useRBO) + { + // MSAA path - attach the RBO, not the texture, and mark the RBO dirty + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + // bind the RBO to the GL_RENDERBUFFER_EXT target + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + // attach the GL_RENDERBUFFER_EXT target to the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glFramebufferRenderbufferEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + // no need to leave the RBO hanging on + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + // color attachment (likely 0) + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + gGL->glFramebufferRenderbufferEXT( fboBindPoint, attachIndexGL, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + tex->ForceRBODirty(); + } + else + { + // regular path - attaching a texture2d + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip ); + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip ); + } + else + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip ); + } + } + } + break; + + case GL_TEXTURE_3D: + { + gGL->glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip, params->m_zslice ); + } + break; + + case GL_TEXTURE_CUBE_MAP: + { + // adjust target to steer to the proper face of the cube map + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + params->m_face; + + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip ); + } + break; + } + + // log the attached tex + m_attach[ attachIndex ] = *params; + + // indicate that the tex has been bound to an RT + tex->m_rtAttachCount++; +} + +void CGLMFBO::TexDetach( EGLMFBOAttachment attachIndex, GLenum fboBindPoint ) +{ + // force our parent context to be current + m_ctx->MakeCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this, fboBindPoint ); + + if (m_attach[ attachIndex ].m_tex) + { + CGLMTex *tex = m_attach[ attachIndex ].m_tex; + GLMTexLayout *layout = tex->m_layout; + GLenum target = tex->m_layout->m_key.m_texGLTarget; + + GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex ); + + switch( target ) + { + case GL_TEXTURE_2D: + { + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + // MSAA path - detach the RBO, not the texture + // (is this the right time to resolve? probably better to wait until someone tries to sample the texture) + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // detach the GL_RENDERBUFFER_EXT target at depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + } + else + { + // color attachment (likely 0) + gGL->glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, 0); + } + } + else + { + // plain tex detach + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to detach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, target, 0, 0 ); + gGL->glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, target, 0, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, 0, 0 ); + } + } + } + break; + + case GL_TEXTURE_3D: + { + gGL->glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, target, 0, 0, 0 ); + } + break; + + case GL_TEXTURE_CUBE_MAP: + { + gGL->glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, 0, 0 ); + } + break; + } + + // un-log the attached tex + memset( &m_attach[ attachIndex ], 0, sizeof( m_attach[0] ) ); + + // drop the RT attach count + tex->m_rtAttachCount--; + } + else + { + //Debugger(); // odd, but not harmful - typ comes from D3D code passing NULL into SetRenderTarget + } +} + +void CGLMFBO::TexScrub( CGLMTex *tex ) +{ + // see if it's attached anywhere + for( int attachIndex = 0; attachIndex < kAttCount; attachIndex++ ) + { + if (m_attach[ attachIndex ].m_tex == tex) + { + // blammo + TexDetach( (EGLMFBOAttachment)attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + } + } +} + + +bool CGLMFBO::IsReady( void ) +{ + bool result = false; + + // ensure our parent context is current + m_ctx->CheckCurrent(); + + // bind to context (will cause FBO object creation on first use) + m_ctx->BindFBOToCtx( this ); + + GLenum status; + status = gGL->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + result = true; + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + result = false; + DebuggerBreak(); + /* choose different formats */ + break; + + default: + result = false; + DebuggerBreak(); + /* programming error; will fail on all hardware */ + break; + } + return result; +} diff --git a/togl/linuxwin/cglmprogram.cpp b/togl/linuxwin/cglmprogram.cpp new file mode 100644 index 0000000..8c0103b --- /dev/null +++ b/togl/linuxwin/cglmprogram.cpp @@ -0,0 +1,1570 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmprogram.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "filesystem.h" +#include "tier1/fmtstr.h" +#include "tier1/KeyValues.h" +#include "tier0/fasttimer.h" + +#if GLMDEBUG && defined( _MSC_VER ) +#include <direct.h> +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +#if GLMDEBUG +#define GLM_FREE_SHADER_TEXT 0 +#else +#define GLM_FREE_SHADER_TEXT 1 +#endif + +//=============================================================================== + +ConVar gl_shaderpair_cacherows_lg2( "gl_paircache_rows_lg2", "10"); // 10 is minimum +ConVar gl_shaderpair_cacheways_lg2( "gl_paircache_ways_lg2", "5"); // 5 is minimum +ConVar gl_shaderpair_cachelog( "gl_shaderpair_cachelog", "0" ); + +static CCycleCount gShaderCompileTime; +static int gShaderCompileCount = 0; +static CCycleCount gShaderCompileQueryTime; +static CCycleCount gShaderLinkTime; +static int gShaderLinkCount = 0; +static CCycleCount gShaderLinkQueryTime; +CON_COMMAND( gl_shader_compile_time_dump, "Dump stats shader compile time." ) +{ + ConMsg( "Shader Compile Time: %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderCompileTime.GetMilliseconds(), gShaderCompileCount, (uint32)gShaderCompileQueryTime.GetMilliseconds() ); + ConMsg( "Shader Link Time : %u ms (Count: %d) / Query: %u ms \n", (uint32)gShaderLinkTime.GetMilliseconds(), gShaderLinkCount, (uint32)gShaderLinkQueryTime.GetMilliseconds() ); +} + +//=============================================================================== + + +GLenum GLMProgTypeToARBEnum( EGLMProgramType type ) +{ + GLenum result = 0; + switch(type) + { + case kGLMVertexProgram: result = GL_VERTEX_PROGRAM_ARB; break; + case kGLMFragmentProgram: result = GL_FRAGMENT_PROGRAM_ARB; break; + default: Assert( !"bad program type"); result = 0; break; + } + return result; +} + +GLenum GLMProgTypeToGLSLEnum( EGLMProgramType type ) +{ + GLenum result = 0; + switch(type) + { + case kGLMVertexProgram: result = GL_VERTEX_SHADER_ARB; break; + case kGLMFragmentProgram: result = GL_FRAGMENT_SHADER_ARB; break; + default: Assert( !"bad program type"); result = 0; break; + } + return result; +} + +CGLMProgram::CGLMProgram( GLMContext *ctx, EGLMProgramType type ) +{ + m_ctx = ctx; + m_ctx->CheckCurrent(); + + m_type = type; + m_nHashTag = rand() ^ ( rand() << 15 ); + m_text = NULL; // no text yet + +#if GLMDEBUG + m_editable = NULL; +#endif + + memset( &m_descs, 0, sizeof( m_descs ) ); + + m_samplerMask = 0; // dxabstract sets this field later + m_samplerTypes = 0; + m_fragDataMask = 0; + m_numDrawBuffers = 0; + memset( &m_drawBuffers, 0, sizeof( m_drawBuffers ) ); + + m_maxSamplers = GLM_SAMPLER_COUNT; + m_nNumUsedSamplers = GLM_SAMPLER_COUNT; + m_maxVertexAttrs = kGLMVertexAttributeIndexMax; + + // create an ARB vp/fp program object name. No need to bind it yet. + GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ]; + Assert(gGL); + gGL->glGenProgramsARB( 1, &arbDesc->m_object.arb ); + + // create a GLSL shader object. + GLMShaderDesc *glslDesc = &m_descs[ kGLMGLSL ]; + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); + + glslDesc->m_object.glsl = gGL->glCreateShaderObjectARB( glslStage );; + + m_shaderName[0] = '\0'; + + m_bTranslatedProgram = false; + + m_nCentroidMask = 0; + m_nShadowDepthSamplerMask = 0; + + m_labelName[0] = '\0'; + m_labelIndex = -1; + m_labelCombo = -1; + + // no text has arrived yet. That's done in SetProgramText. +} + +CGLMProgram::~CGLMProgram( ) +{ + m_ctx->CheckCurrent(); + + // if there is an arb program, delete it + GLMShaderDesc *arbDesc = &m_descs[ kGLMARB ]; + if (arbDesc->m_object.arb) + { + gGL->glDeleteProgramsARB( 1, &arbDesc->m_object.arb ); + arbDesc->m_object.arb = 0; + } + + // if there is a GLSL shader, delete it + GLMShaderDesc *glslDesc = &m_descs[kGLMGLSL]; + if (glslDesc->m_object.glsl) + { + gGL->glDeleteShader( (uint)glslDesc->m_object.glsl ); // why do I need a cast here again ? + glslDesc->m_object.glsl = 0; + } + +#if GLMDEBUG + if (m_editable) + { + delete m_editable; + m_editable = NULL; + } +#endif + + if (m_text) + { + free( m_text ); + m_text = NULL; + } + m_ctx = NULL; +} + +enum EShaderSection +{ + kGLMARBVertex, kGLMARBVertexDisabled, + kGLMARBFragment, kGLMARBFragmentDisabled, + kGLMGLSLVertex, kGLMGLSLVertexDisabled, + kGLMGLSLFragment, kGLMGLSLFragmentDisabled, + +}; + +const char *g_shaderSectionMarkers[] = // match ordering of enum +{ + "!!ARBvp", "-!!ARBvp", // enabled and disabled markers. so you can have multiple flavors in a blob and activate the one you want. + "!!ARBfp", "-!!ARBfp", + "//GLSLvp", "-//GLSLvp", + "//GLSLfp", "-//GLSLfp", + NULL +}; + +void CGLMProgram::SetShaderName( const char *name ) +{ + V_strncpy( m_shaderName, name, sizeof( m_shaderName ) ); +} + +void CGLMProgram::SetProgramText( char *text ) +{ + // free old text if any + // clone new text + // scan newtext to find sections + // walk sections, and mark descs to indicate where text is at + + if (m_text) + { + free( m_text ); + m_text = NULL; + } + + // scrub desc text references + for( int i=0; i<kGLMNumProgramTypes; i++) + { + GLMShaderDesc *desc = &m_descs[i]; + + desc->m_textPresent = false; + desc->m_textOffset = 0; + desc->m_textLength = 0; + } + + m_text = strdup( text ); + Assert( m_text != NULL ); + + #if GLMDEBUG + // create editable text item, if it does not already exist + if (!m_editable) + { + char *suffix = ""; + + switch(m_type) + { + case kGLMVertexProgram: suffix = ".vsh"; break; + case kGLMFragmentProgram: suffix = ".fsh"; break; + default: GLMDebugger(); + } + +#ifdef POSIX + CFmtStr debugShaderPath( "%s/debugshaders/", getenv( "HOME" ) ); +#else + CFmtStr debugShaderPath( "debugshaders/" ); +#endif + _mkdir( debugShaderPath.Access() ); + m_editable = new CGLMEditableTextItem( m_text, strlen(m_text), false, debugShaderPath.Access(), suffix ); + + // pull our string back from the editable (it has probably munged it) + if (m_editable->HasData()) + { + ReloadStringFromEditable(); + } + } + #endif + + + // scan the text and find sections + CGLMTextSectioner sections( m_text, strlen( m_text ), g_shaderSectionMarkers ); + + int sectionCount = sections.Count(); + for( int i=0; i < sectionCount; i++ ) + { + uint subtextOffset = 0; + uint subtextLength = 0; + int markerIndex = 0; + + sections.GetSection( i, &subtextOffset, &subtextLength, &markerIndex ); + + // act on the section + GLMShaderDesc *desc = NULL; + switch( m_type ) + { + case kGLMVertexProgram: + switch( markerIndex ) + { + case kGLMARBVertex: + case kGLMGLSLVertex: + desc = &m_descs[ (markerIndex==kGLMARBVertex) ? kGLMARB : kGLMGLSL]; + + // these steps are generic across both langs + desc->m_textPresent = true; + desc->m_textOffset = subtextOffset; + desc->m_textLength = subtextLength; + desc->m_compiled = false; + desc->m_valid = false; + break; + + case kGLMARBVertexDisabled: + case kGLMGLSLVertexDisabled: + // ignore quietly + break; + + default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break; + } + break; + + case kGLMFragmentProgram: + switch( markerIndex ) + { + case kGLMARBFragment: + case kGLMGLSLFragment: + desc = &m_descs[ (markerIndex==kGLMARBFragment) ? kGLMARB : kGLMGLSL]; + + // these steps are generic across both langs + desc->m_textPresent = true; + desc->m_textOffset = subtextOffset; + desc->m_textLength = subtextLength; + desc->m_compiled = false; + desc->m_valid = false; + break; + + case kGLMARBFragmentDisabled: + case kGLMGLSLFragmentDisabled: + // ignore quietly + break; + + default: Assert(!"Mismatched section marker seen in SetProgramText (VP)"); break; + } + break; + } + } + + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + + char *lineStr = strstr( m_text, "// trans#" ); + if (lineStr) + { + int scratch = -1; + + if (this->m_type == kGLMVertexProgram) + { + sscanf( lineStr, "// trans#%d label:vs-file %s vs-index %d vs-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo ); + } + else + { + sscanf( lineStr, "// trans#%d label:ps-file %s ps-index %d ps-combo %d", &scratch, m_labelName, &m_labelIndex, &m_labelCombo ); + } + } +} + +void CGLMProgram::CompileActiveSources ( void ) +{ + // compile everything we have text for + for( int i=0; i<kGLMNumProgramTypes; i++) + { + if (m_descs[i].m_textPresent) + { + Compile( (EGLMProgramLang)i ); + } + } +} + +void CGLMProgram::Compile( EGLMProgramLang lang ) +{ + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + bool noisy = false; noisy; + int loglevel = gl_shaderpair_cachelog.GetInt(); + + switch( lang ) + { + case kGLMARB: + { + GLMShaderDesc *arbDesc; + + arbDesc = &m_descs[ kGLMARB ]; + + // make sure no GLSL program is set up + gGL->glUseProgram(0); + // bind our program container to context + GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); + + glSetEnable( arbTarget, true ); // unclear if I need this to compile or just to draw... + + gGL->glBindProgramARB( arbTarget, arbDesc->m_object.arb ); // object created or just re-bound + + char *section = m_text + arbDesc->m_textOffset; + char *lastCharOfSection = section + arbDesc->m_textLength; // actually it's one past the last textual character + lastCharOfSection; + + #if GLMDEBUG + if(noisy) + { + GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for ARB %s program (name %d) ---------------------", + arbTarget == GL_FRAGMENT_PROGRAM_ARB ? "fragment" : "vertex", + arbDesc->m_object.arb )); + + // we don't have a "print this many chars" call yet + // just temporarily null terminate the text we want to print + + char saveChar = *lastCharOfSection; + + *lastCharOfSection= 0; + GLMPRINTTEXT(( section, eDebugDump )); + *lastCharOfSection= saveChar; + + GLMPRINTF(("<-D- CGLMProgram::Compile ARB EOT--" )); + } + #endif + + gGL->glProgramStringARB( arbTarget, GL_PROGRAM_FORMAT_ASCII_ARB, arbDesc->m_textLength, section ); + arbDesc->m_compiled = true; // compiled but not necessarily valid + + CheckValidity( lang ); + // leave it bound n enabled, don't care (draw will sort it all out) + } + break; + + case kGLMGLSL: + { + GLMShaderDesc *glslDesc; + + glslDesc = &m_descs[ kGLMGLSL ]; + + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); + glslStage; + + // there's no binding to do for GLSL. but make sure no ARB stuff is bound for tidiness. + glSetEnable( GL_VERTEX_PROGRAM_ARB, false ); + glSetEnable( GL_FRAGMENT_PROGRAM_ARB, false ); // add check errors on these + + gGL->glBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); + gGL->glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, 0 ); + + // no GLSL program either + gGL->glUseProgram(0); + + // pump text into GLSL shader object + + char *section = m_text + glslDesc->m_textOffset; + char *lastCharOfSection = section + glslDesc->m_textLength; // actually it's one past the last textual character + lastCharOfSection; + + #if GLMDEBUG + if(noisy) + { + GLMPRINTF((">-D- CGLMProgram::Compile submitting following text for GLSL %s program (name %d) ---------------------", + glslStage == GL_FRAGMENT_SHADER_ARB ? "fragment" : "vertex", + glslDesc->m_object.glsl )); + + // we don't have a "print this many chars" call yet + // just temporarily null terminate the text we want to print + + char saveChar = *lastCharOfSection; + + *lastCharOfSection= 0; + GLMPRINTTEXT(( section, eDebugDump )); + *lastCharOfSection= saveChar; + + GLMPRINTF(("<-D- CGLMProgram::Compile GLSL EOT--" )); + } + #endif + + gGL->glShaderSourceARB( glslDesc->m_object.glsl, 1, (const GLchar **)§ion, &glslDesc->m_textLength); + +#if GLM_FREE_SHADER_TEXT + // Free the shader program text - not needed anymore (GL has its own copy) + if ( m_text && !m_descs[kGLMARB].m_textPresent ) + { + free( m_text ); + m_text = NULL; + } +#endif + + // compile + gGL->glCompileShaderARB( glslDesc->m_object.glsl ); + glslDesc->m_compiled = true; // compiled but not necessarily valid + + // Check shader validity at creation time. This will cause the driver to not be able to + // multi-thread/defer shader compiles, but it is useful for getting error messages on the + // shader when it is compiled + bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0); + if (bValidateShaderEarly) + { + CheckValidity( lang ); + } + + if (loglevel>=2) + { + char tempname[128]; + //int tempindex = -1; + //int tempcombo = -1; + + //GetLabelIndexCombo( tempname, sizeof(tempname), &tempindex, &tempcombo ); + //printf("\ncompile: - [ %s/%d/%d ] on GL name %d ", tempname, tempindex, tempcombo, glslDesc->m_object.glsl ); + + + GetComboIndexNameString( tempname, sizeof(tempname) ); + printf("\ncompile: %s on GL name %d ", tempname, glslDesc->m_object.glsl ); + } + } + break; + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderCompileTime += shaderCompileTimer.GetDuration(); + gShaderCompileCount++; + } +} + +#if GLMDEBUG + + bool CGLMProgram::PollForChanges( void ) + { + bool result = false; + if (m_editable) + { + result = m_editable->PollForChanges(); + } + return result; + } + + void CGLMProgram::ReloadStringFromEditable( void ) + { + uint dataSize=0; + char *data=NULL; + + m_editable->GetCurrentText( &data, &dataSize ); + + char *buf = (char *)malloc( dataSize+1 ); // we will NULL terminate it, since the mirror copy might not be + memcpy( buf, data, dataSize ); + buf[dataSize] = 0; + + SetProgramText( buf ); + + free( buf ); + } + + bool CGLMProgram::SyncWithEditable( void ) + { + bool result = false; + + if (m_editable->PollForChanges()) + { + ReloadStringFromEditable(); + + CompileActiveSources(); + + // invalidate shader pair cache entries using this shader.. + m_ctx->m_pairCache->PurgePairsWithShader( this ); + + result = true; // result true means "it changed" + } + return result; + } + +#endif + + +// attributes which are general to both stages +// VP and FP: +// +// 0x88A0 PROGRAM_INSTRUCTIONS_ARB VP FP +// 0x88A1 MAX_PROGRAM_INSTRUCTIONS_ARB VP FP +// 0x88A2 PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP +// 0x88A3 MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB VP FP +// +// 0x88A4 PROGRAM_TEMPORARIES_ARB VP FP +// 0x88A5 MAX_PROGRAM_TEMPORARIES_ARB VP FP +// 0x88A6 PROGRAM_NATIVE_TEMPORARIES_ARB VP FP +// 0x88A7 MAX_PROGRAM_NATIVE_TEMPORARIES_ARB VP FP +// +// 0x88A8 PROGRAM_PARAMETERS_ARB VP FP +// 0x88A9 MAX_PROGRAM_PARAMETERS_ARB VP FP +// 0x88AA PROGRAM_NATIVE_PARAMETERS_ARB VP FP +// 0x88AB MAX_PROGRAM_NATIVE_PARAMETERS_ARB VP FP +// +// 0x88AC PROGRAM_ATTRIBS_ARB VP FP +// 0x88AD MAX_PROGRAM_ATTRIBS_ARB VP FP +// 0x88AE PROGRAM_NATIVE_ATTRIBS_ARB VP FP +// 0x88AF MAX_PROGRAM_NATIVE_ATTRIBS_ARB VP FP +// +// 0x88B4 MAX_PROGRAM_LOCAL_PARAMETERS_ARB VP FP +// 0x88B5 MAX_PROGRAM_ENV_PARAMETERS_ARB VP FP +// 0x88B6 PROGRAM_UNDER_NATIVE_LIMITS_ARB VP FP +// +// VP only: +// +// 0x88B0 PROGRAM_ADDRESS_REGISTERS_ARB VP +// 0x88B1 MAX_PROGRAM_ADDRESS_REGISTERS_ARB VP +// 0x88B2 PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP +// 0x88B3 MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB VP +// +// FP only: +// +// 0x8805 PROGRAM_ALU_INSTRUCTIONS_ARB FP +// 0x880B MAX_PROGRAM_ALU_INSTRUCTIONS_ARB FP +// 0x8808 PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP +// 0x880E MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB FP + +// 0x8806 PROGRAM_TEX_INSTRUCTIONS_ARB FP +// 0x880C MAX_PROGRAM_TEX_INSTRUCTIONS_ARB FP +// 0x8809 PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP +// 0x880F MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB FP + +// 0x8807 PROGRAM_TEX_INDIRECTIONS_ARB FP +// 0x880D MAX_PROGRAM_TEX_INDIRECTIONS_ARB FP +// 0x880A PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP +// 0x8810 MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB FP + +struct GLMShaderLimitDesc +{ + GLenum m_valueEnum; + GLenum m_limitEnum; + const char *m_debugName; + char m_flags; + // m_flags - 0x01 for VP, 0x02 for FP, or set both if applicable to both +}; + +// macro to help make the table of what to check +#ifndef LMD +#define LMD( val, flags ) { GL_PROGRAM_##val##_ARB, GL_MAX_PROGRAM_##val##_ARB, #val, flags } +#else +#error you need to use a different name for this macro. +#endif + +GLMShaderLimitDesc g_glmShaderLimitDescs[] = +{ + // VP and FP.. + LMD( INSTRUCTIONS, 3 ), + LMD( NATIVE_INSTRUCTIONS, 3 ), + LMD( NATIVE_TEMPORARIES, 3 ), + LMD( PARAMETERS, 3 ), + LMD( NATIVE_PARAMETERS, 3 ), + LMD( ATTRIBS, 3 ), + LMD( NATIVE_ATTRIBS, 3 ), + + // VP only.. + LMD( ADDRESS_REGISTERS, 1 ), + LMD( NATIVE_ADDRESS_REGISTERS, 1 ), + + // FP only.. + LMD( ALU_INSTRUCTIONS, 2 ), + LMD( NATIVE_ALU_INSTRUCTIONS, 2 ), + LMD( TEX_INSTRUCTIONS, 2 ), + LMD( NATIVE_TEX_INSTRUCTIONS, 2 ), + LMD( TEX_INDIRECTIONS, 2 ), + LMD( NATIVE_TEX_INDIRECTIONS, 2 ), + + { 0, 0, NULL, 0 } +}; + +#undef LMD + +bool CGLMProgram::CheckValidity( EGLMProgramLang lang ) +{ + static char *targnames[] = { "vertex", "fragment" }; + + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + bool bValid = false; + + switch(lang) + { + case kGLMARB: + { + GLMShaderDesc *arbDesc; + arbDesc = &m_descs[ kGLMARB ]; + + GLenum arbTarget = GLMProgTypeToARBEnum( m_type ); + + Assert( arbDesc->m_compiled ); + + arbDesc->m_valid = true; // assume success til we see otherwise + + // assume program is bound. is there anything wrong with it ? + + GLint isNative=0; + gGL->glGetProgramivARB( arbTarget, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative ); + + // If the program is over the hardware's limits, print out some information + if (isNative!=1) + { + arbDesc->m_valid = false; + + // check everything we can check + char checkmask = (1<<m_type); // 1 for VP, 2 for FP + + for( GLMShaderLimitDesc *desc = g_glmShaderLimitDescs; desc->m_valueEnum !=0; desc++ ) + { + if ( desc->m_flags & checkmask ) + { + // test it + GLint value = 0; + GLint limit = 0; + gGL->glGetProgramivARB(arbTarget, desc->m_valueEnum, &value); + + gGL->glGetProgramivARB(arbTarget, desc->m_limitEnum, &limit); + + if (value > limit) + { + GLMPRINTF(("-D- Invalid %s program: program has %d %s; limit is %d", targnames[ m_type ], value, desc->m_debugName, limit )); + } + } + } + } + + // syntax error check + GLint errorLine; + gGL->glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errorLine ); + + if ( errorLine!=-1 ) + { + const GLubyte* errorString = gGL->glGetString(GL_PROGRAM_ERROR_STRING_ARB); errorString; + GLMPRINTF(( "-D- Syntax error in ARB %s program: %s",targnames[ m_type ], errorString )); + arbDesc->m_valid = false; + } + if (!arbDesc->m_valid) + { + char *temp = strdup(m_text); + temp[ arbDesc->m_textOffset + arbDesc->m_textLength ] = 0; + GLMPRINTF(("-D- ----- ARB compile failed; bad source follows -----" )); + GLMPRINTTEXT(( temp + arbDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); + GLMPRINTF(("-D- -----end-----" )); + free( temp ); + } + + bValid = arbDesc->m_valid; + } + break; + + case kGLMGLSL: + { + GLMShaderDesc *glslDesc; + GLcharARB *logString = NULL; + glslDesc = &m_descs[ kGLMGLSL ]; + + GLenum glslStage = GLMProgTypeToGLSLEnum( m_type ); glslStage; + + Assert( glslDesc->m_compiled ); + + glslDesc->m_valid = true; // assume success til we see otherwise + + // GLSL error check + int compiled = 0, length = 0, laux = 0; + + gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); + gGL->glGetObjectParameterivARB( (GLhandleARB)glslDesc->m_object.glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); + if ( length > 0 ) + { + logString = (GLcharARB *)malloc(length * sizeof(GLcharARB)); + gGL->glGetInfoLogARB((GLhandleARB)glslDesc->m_object.glsl, length, &laux, logString); + } + // we may not be able to check "native limits" stuff until link time. meh + + if (!compiled) + { + glslDesc->m_valid = false; + } + + if (!glslDesc->m_valid) + { + GLMPRINTF(("-D- ----- GLSL compile failed: \n %s \n",logString )); +#if !GLM_FREE_SHADER_TEXT + char *temp = strdup(m_text); + temp[ glslDesc->m_textOffset + glslDesc->m_textLength ] = 0; + GLMPRINTTEXT(( temp + glslDesc->m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES )); + free( temp ); +#endif + GLMPRINTF(("-D- -----end-----" )); + } + + if ( logString ) + free( logString ); + + bValid = glslDesc->m_valid; + } + break; + } + + if ( !bValid ) + { + GLMDebugPrintf( "Compile of \"%s\" Failed:\n%s\n", m_shaderName, m_text ? m_text : "" ); + } + AssertOnce( bValid ); + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderCompileQueryTime += shaderCompileTimer.GetDuration(); + } + + return bValid; +} + +void CGLMProgram::LogSlow( EGLMProgramLang lang ) +{ + // find the desc, see if it's marked + GLMShaderDesc *desc = &m_descs[ lang ]; + + if (!desc->m_slowMark) + { +#if !GLM_FREE_SHADER_TEXT + // log it + printf( "\n-------------- Slow %s ( CGLMProgram @ %p, lang %s, name %d ) : \n%s \n", + m_type==kGLMVertexProgram ? "VS" : "FS", + this, + lang==kGLMGLSL ? "GLSL" : "ARB", + (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb), + m_text + ); +#endif + } + else // complain on a decreasing basis (powers of two) + { + if ( (desc->m_slowMark & (desc->m_slowMark-1)) == 0 ) + { + // short blurb + printf( "\n Slow %s ( CGLMProgram @ %p, lang %s, name %d ) (%d times)", + m_type==kGLMVertexProgram ? "VS" : "FS", + this, + lang==kGLMGLSL ? "GLSL" : "ARB", + (int)(lang==kGLMGLSL ? (int)desc->m_object.glsl : (int)desc->m_object.arb), + desc->m_slowMark+1 + ); + } + } + + // mark it + desc->m_slowMark++; + + +} + +void CGLMProgram::GetLabelIndexCombo ( char *labelOut, int labelOutMaxChars, int *indexOut, int *comboOut ) +{ + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + // Done in SetProgramText + + *labelOut = 0; + *indexOut = -1; + + if ((strlen( m_labelName ) != 0)) + { + Q_strncpy( labelOut, m_labelName, labelOutMaxChars ); + *indexOut = m_labelIndex; + *comboOut = m_labelCombo; + } +} + +void CGLMProgram::GetComboIndexNameString ( char *stringOut, int stringOutMaxChars ) // mmmmmmmm-nnnnnnnn-filename +{ + // find the label string + // example: + // trans#2871 label:vs-file vertexlit_and_unlit_generic_vs20 vs-index 294912 vs-combo 1234 + // Done in SetProgramText + + *stringOut = 0; + + int len = strlen( m_labelName ); + + if ( (len+20) < stringOutMaxChars ) + { + // output formatted version + sprintf( stringOut, "%08X-%08X-%s", m_labelCombo, m_labelIndex, m_labelName ); + } +} + +//=============================================================================== + + +CGLMShaderPair::CGLMShaderPair( GLMContext *ctx ) +{ + m_ctx = ctx; + m_vertexProg = m_fragmentProg = NULL; + + m_program = gGL->glCreateProgramObjectARB(); + + m_locVertexParams = -1; + m_locVertexBoneParams = -1; + m_locVertexScreenParams = -1; + m_nScreenWidthHeight = 0xFFFFFFFF; + m_locVertexInteger0 = -1; // "i0" + memset( m_locVertexBool, 0xFF, sizeof( m_locVertexBool ) ); + memset( m_locFragmentBool, 0xFF, sizeof( m_locFragmentBool ) ); + m_bHasBoolOrIntUniforms = false; + + m_locFragmentParams = -1; + + m_locFragmentFakeSRGBEnable = -1; + m_fakeSRGBEnableValue = -1.0f; + + memset( m_locSamplers, 0xFF, sizeof( m_locSamplers ) ); + + m_valid = false; + m_bCheckLinkStatus = false; + m_revision = 0; // bumps to 1 once linked +} + +CGLMShaderPair::~CGLMShaderPair( ) +{ + if (m_program) + { + gGL->glDeleteObjectARB( (GLhandleARB)m_program ); + m_program = 0; + } +} + +bool CGLMShaderPair::ValidateProgramPair() +{ + if ( m_vertexProg && m_vertexProg->m_descs[kGLMGLSL].m_textPresent && !m_vertexProg->m_descs[kGLMGLSL].m_valid ) + { + m_vertexProg->CheckValidity( kGLMGLSL ); + } + if (m_fragmentProg && m_fragmentProg->m_descs[kGLMGLSL].m_textPresent && !m_fragmentProg->m_descs[kGLMGLSL].m_valid) + { + m_fragmentProg->CheckValidity( kGLMGLSL ); + } + + if ( !m_valid && m_bCheckLinkStatus ) + { + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + // check for success + GLint result = 0; + gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_LINK_STATUS_ARB, &result ); // want GL_TRUE + m_bCheckLinkStatus = false; + + if (result == GL_TRUE) + { + // success + + m_valid = true; + m_revision++; + } + else + { + GLint length = 0; + GLint laux = 0; + + // do some digging + gGL->glGetObjectParameterivARB( m_program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length ); + + GLcharARB *logString = (GLcharARB *)malloc( length * sizeof(GLcharARB) ); + gGL->glGetInfoLogARB( m_program, length, &laux, logString ); + + GLMPRINTF( ("-D- ----- GLSL link failed: \n %s ", logString) ); +#if !GLM_FREE_SHADER_TEXT + char *vtemp = strdup( m_vertexProg->m_text ); + vtemp[m_vertexProg->m_descs[kGLMGLSL].m_textOffset + m_vertexProg->m_descs[kGLMGLSL].m_textLength] = 0; + + char *ftemp = strdup( m_fragmentProg->m_text ); + ftemp[m_fragmentProg->m_descs[kGLMGLSL].m_textOffset + m_fragmentProg->m_descs[kGLMGLSL].m_textLength] = 0; + + GLMPRINTF( ("-D- ----- GLSL vertex program selected: %08x (handle %08x)", m_vertexProg, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl) ); + GLMPRINTTEXT( (vtemp + m_vertexProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) ); + + GLMPRINTF( ("-D- ----- GLSL fragment program selected: %08x (handle %08x)", m_fragmentProg, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl) ); + GLMPRINTTEXT( (ftemp + m_fragmentProg->m_descs[kGLMGLSL].m_textOffset, eDebugDump, GLMPRINTTEXT_NUMBEREDLINES) ); + + free( ftemp ); + free( vtemp ); +#endif + free( logString ); + + GLMPRINTF( ("-D- -----end-----") ); + } + + if (m_valid) + { + gGL->glUseProgram( m_program ); + + m_ctx->NewLinkedProgram(); + + m_locVertexParams = gGL->glGetUniformLocationARB( m_program, "vc" ); + m_locVertexBoneParams = gGL->glGetUniformLocationARB( m_program, "vcbones" ); + m_locVertexScreenParams = gGL->glGetUniformLocationARB( m_program, "vcscreen" ); + m_nScreenWidthHeight = 0xFFFFFFFF; + + m_locVertexInteger0 = gGL->glGetUniformLocationARB( m_program, "i0" ); + + m_bHasBoolOrIntUniforms = false; + if (m_locVertexInteger0 >= 0) + m_bHasBoolOrIntUniforms = true; + + for (uint i = 0; i < cMaxVertexShaderBoolUniforms; i++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "b%d", i ); + m_locVertexBool[i] = gGL->glGetUniformLocationARB( m_program, buf ); + if (m_locVertexBool[i] != -1) + m_bHasBoolOrIntUniforms = true; + } + + for (uint i = 0; i < cMaxFragmentShaderBoolUniforms; i++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "fb%d", i ); + m_locFragmentBool[i] = gGL->glGetUniformLocationARB( m_program, buf ); + if (m_locFragmentBool[i] != -1) + m_bHasBoolOrIntUniforms = true; + } + + m_locFragmentParams = gGL->glGetUniformLocationARB( m_program, "pc" ); + + for (uint i = 0; i < kGLMNumProgramTypes; i++) + { + m_NumUniformBufferParams[i] = 0; + + if (i == kGLMVertexProgram) + { + if (m_locVertexParams < 0) + continue; + } + else if (m_locFragmentParams < 0) + continue; + + const uint nNum = (i == kGLMVertexProgram) ? m_vertexProg->m_descs[kGLMGLSL].m_highWater : m_fragmentProg->m_descs[kGLMGLSL].m_highWater; + + uint j; + for (j = 0; j < nNum; j++) + { + char buf[256]; + V_snprintf( buf, sizeof(buf), "%cc[%i]", "vp"[i], j ); + // Grab the handle of each array element, so we can more efficiently update array elements in the middle. + int l = m_UniformBufferParams[i][j] = gGL->glGetUniformLocationARB( m_program, buf ); + if (l < 0) + break; + } + + m_NumUniformBufferParams[i] = j; + } + + m_locFragmentFakeSRGBEnable = gGL->glGetUniformLocationARB( m_program, "flSRGBWrite" ); + m_fakeSRGBEnableValue = -1.0f; + + for (int sampler = 0; sampler < 16; sampler++) + { + char tmp[16]; + sprintf( tmp, "sampler%d", sampler ); // sampler0 .. sampler1.. etc + + GLint nLoc = gGL->glGetUniformLocationARB( m_program, tmp ); + m_locSamplers[sampler] = nLoc; + if (nLoc >= 0) + { + gGL->glUniform1iARB( nLoc, sampler ); + } + } + } + else + { + m_locVertexParams = -1; + m_locVertexBoneParams = -1; + m_locVertexScreenParams = -1; + m_nScreenWidthHeight = 0xFFFFFFFF; + + m_locVertexInteger0 = -1; + memset( m_locVertexBool, 0xFF, sizeof(m_locVertexBool) ); + memset( m_locFragmentBool, 0xFF, sizeof(m_locFragmentBool) ); + m_bHasBoolOrIntUniforms = false; + + m_locFragmentParams = -1; + m_locFragmentFakeSRGBEnable = -1; + m_fakeSRGBEnableValue = -999; + + memset( m_locSamplers, 0xFF, sizeof(m_locSamplers) ); + + m_revision = 0; + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderLinkQueryTime += shaderCompileTimer.GetDuration(); + } + } + + return m_valid; +} + +// glUseProgram() will be called as a side effect! +bool CGLMShaderPair::SetProgramPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + bool bTimeShaderCompiles = (CommandLine()->FindParm( "-gl_time_shader_compiles" ) != 0); + // If using "-gl_time_shader_compiles", keeps track of total cycle count spent on shader compiles. + CFastTimer shaderCompileTimer; + if (bTimeShaderCompiles) + { + shaderCompileTimer.Start(); + } + + m_valid = false; // assume failure + + // No need to check that vp and fp are valid at this point (ie shader compile succeed) + // It is permissible to attach a shader object to a program before source code has been loaded + // into the shader object or before the shader object has been compiled. The program won't + // link if one or more of the shader objects has not been successfully compiled. + // (Defer querying the compile and link status to take advantage of GLSL shaders + // building in parallels) + bool vpgood = (vp != NULL); + bool fpgood = (fp != NULL); + + if ( !fpgood ) + { + // fragment side allowed to be "null". + fp = m_ctx->m_pNullFragmentProgram; + } + + if ( vpgood && fpgood ) + { + if ( vp->m_nCentroidMask != fp->m_nCentroidMask ) + { + Warning( "CGLMShaderPair::SetProgramPair: Centroid masks differ at link time of vertex shader %s and pixel shader %s!\n", + vp->m_shaderName, fp->m_shaderName ); + } + + // attempt link. but first, detach any previously attached programs + if (m_vertexProg) + { + gGL->glDetachObjectARB(m_program, m_vertexProg->m_descs[kGLMGLSL].m_object.glsl); + m_vertexProg = NULL; + } + + if (m_fragmentProg) + { + gGL->glDetachObjectARB(m_program, m_fragmentProg->m_descs[kGLMGLSL].m_object.glsl); + m_fragmentProg = NULL; + } + + // now attach + + gGL->glAttachObjectARB( m_program, vp->m_descs[kGLMGLSL].m_object.glsl ); + m_vertexProg = vp; + + gGL->glAttachObjectARB( m_program, fp->m_descs[kGLMGLSL].m_object.glsl ); + m_fragmentProg = fp; + + // force the locations for input attributes v0-vN to be at locations 0-N + // use the vertex attrib map to know which slots are live or not... oy! we don't have that map yet... but it's OK. + // fallback - just force v0-v15 to land in locations 0-15 as a standard. + + for( int i = 0; i < 16; i++ ) + { + char tmp[16]; + sprintf(tmp, "v%d", i); // v0 v1 v2 ... et al + + gGL->glBindAttribLocationARB( m_program, i, tmp ); + } +#if !GLM_FREE_SHADER_TEXT + if (CommandLine()->CheckParm("-dumpallshaders")) + { + // Dump all shaders, for debugging. + FILE* pFile = fopen("shaderdump.txt", "a+"); + if (pFile) + { + fprintf(pFile, "--------------VP:%s\n%s\n", vp->m_shaderName, vp->m_text); + fprintf(pFile, "--------------FP:%s\n%s\n", fp->m_shaderName, fp->m_text); + fclose(pFile); + } + } +#endif + + // now link + gGL->glLinkProgramARB( m_program ); + m_bCheckLinkStatus = true; + } + else + { + // fail + Assert(!"Can't link these programs"); + } + + // Check shader validity at creation time. This will cause the driver to not be able to + // multi-thread/defer shader compiles, but it is useful for getting error messages on the + // shader when it is compiled + bool bValidateShaderEarly = (CommandLine()->FindParm( "-gl_validate_shader_early" ) != 0); + if (bValidateShaderEarly) + { + ValidateProgramPair(); + } + + if (bTimeShaderCompiles) + { + shaderCompileTimer.End(); + gShaderLinkTime += shaderCompileTimer.GetDuration(); + gShaderLinkCount++; + } + + return m_valid; +} + + +bool CGLMShaderPair::RefreshProgramPair ( void ) +{ + // re-link and re-query the uniforms. + + // since SetProgramPair knows how to detach previously attached shader objects, just pass the same ones in again. + CGLMProgram *vp = m_vertexProg; + CGLMProgram *fp = m_fragmentProg; + + bool vpgood = (vp!=NULL) && (vp->m_descs[ kGLMGLSL ].m_valid); + bool fpgood = (fp!=NULL) && (fp->m_descs[ kGLMGLSL ].m_valid); + + if (vpgood && fpgood) + { + SetProgramPair( vp, fp ); + } + else + { + DebuggerBreak(); + return false; + } + + return false; +} + + +//=============================================================================== + +CGLMShaderPairCache::CGLMShaderPairCache( GLMContext *ctx ) +{ + m_ctx = ctx; + + m_mark = 1; + + m_rowsLg2 = gl_shaderpair_cacherows_lg2.GetInt(); + if (m_rowsLg2 < 10) + m_rowsLg2 = 10; + m_rows = 1<<m_rowsLg2; + m_rowsMask = m_rows - 1; + + m_waysLg2 = gl_shaderpair_cacheways_lg2.GetInt(); + if (m_waysLg2 < 5) + m_waysLg2 = 5; + m_ways = 1<<m_waysLg2; + + m_entryCount = m_rows * m_ways; + + uint entryTableSize = m_rows * m_ways * sizeof(CGLMPairCacheEntry); + m_entries = (CGLMPairCacheEntry*)malloc( entryTableSize ); // array[ m_rows ][ m_ways ] + memset( m_entries, 0, entryTableSize ); + + uint evictTableSize = m_rows * sizeof(uint); + m_evictions = (uint*)malloc( evictTableSize ); + memset (m_evictions, 0, evictTableSize); + +#if GL_SHADER_PAIR_CACHE_STATS + // hit counter table is same size + m_hits = (uint*)malloc( evictTableSize ); + memset (m_hits, 0, evictTableSize); +#endif +} + +CGLMShaderPairCache::~CGLMShaderPairCache( ) +{ + if (gl_shaderpair_cachelog.GetInt()) + { + DumpStats(); + } + + // free all the built pairs + // free the entry table + bool purgeResult = this->Purge(); + (void)purgeResult; + Assert( !purgeResult ); + + if (m_entries) + { + free( m_entries ); + m_entries = NULL; + } + + if (m_evictions) + { + free( m_evictions ); + m_evictions = NULL; + } + +#if GL_SHADER_PAIR_CACHE_STATS + if (m_hits) + { + free( m_hits ); + m_hits = NULL; + } +#endif +} + +// Set this convar internally to build or add to the shader pair cache file (link hints) +// We really only expect this to work on POSIX +static ConVar glm_cacheprograms( "glm_cacheprograms", "0", FCVAR_DEVELOPMENTONLY ); + +#define PROGRAM_CACHE_FILE "program_cache.cfg" + +static void WriteToProgramCache( CGLMShaderPair *pair ) +{ + KeyValues *pProgramCache = new KeyValues( "programcache" ); + pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); + + if ( !pProgramCache ) + { + Warning( "Could not write to program cache file!\n" ); + return; + } + + // extract values of interest which represent a pair of shaders + + char vprogramName[128]; + int vprogramStaticIndex = -1; + int vprogramDynamicIndex = -1; + pair->m_vertexProg->GetLabelIndexCombo( vprogramName, sizeof(vprogramName), &vprogramStaticIndex, &vprogramDynamicIndex ); + + + char pprogramName[128]; + int pprogramStaticIndex = -1; + int pprogramDynamicIndex = -1; + pair->m_fragmentProg->GetLabelIndexCombo( pprogramName, sizeof(pprogramName), &pprogramStaticIndex, &pprogramDynamicIndex ); + + // make up a key - this thing is really a list of tuples, so need not be keyed by anything particular + KeyValues *pProgramKey = pProgramCache->CreateNewKey(); + Assert( pProgramKey ); + + pProgramKey->SetString ( "vs", vprogramName ); + pProgramKey->SetString ( "ps", pprogramName ); + + pProgramKey->SetInt ( "vs_static", vprogramStaticIndex ); + pProgramKey->SetInt ( "ps_static", pprogramStaticIndex ); + + pProgramKey->SetInt ( "vs_dynamic", vprogramDynamicIndex ); + pProgramKey->SetInt ( "ps_dynamic", pprogramDynamicIndex ); + + pProgramCache->SaveToFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" ); + pProgramCache->deleteThis(); +} + +// Calls glUseProgram() as a side effect +CGLMShaderPair *CGLMShaderPairCache::SelectShaderPairInternal( CGLMProgram *vp, CGLMProgram *fp, uint extraKeyBits, int rowIndex ) +{ + CGLMShaderPair *result = NULL; + +#if GLMDEBUG + int loglevel = gl_shaderpair_cachelog.GetInt(); +#else + const int loglevel = 0; +#endif + + char vtempname[128]; + int vtempindex = -1; vtempindex; + int vtempcombo = -1; vtempcombo; + + char ptempname[128]; + int ptempindex = -1; ptempindex; + int ptempcombo = -1; ptempcombo; + + CGLMPairCacheEntry *row = HashRowPtr( rowIndex ); + + // Re-probe to find the oldest and first unoccupied entry (this func should be very rarely called if the cache is properly configured so re-scanning shouldn't matter). + int hitway, emptyway, oldestway; + HashRowProbe( row, vp, fp, extraKeyBits, hitway, emptyway, oldestway ); + Assert( hitway == -1 ); + + // we missed. if there is no empty way, then somebody's getting evicted. + int destway = -1; + + if (emptyway>=0) + { + destway = emptyway; + + if (loglevel >= 2) // misses logged at level 3 and higher + { + printf("\nSSP: miss - row %05d - ", rowIndex ); + } + } + else + { + // evict the oldest way + Assert( oldestway >= 0); // better not come back negative + + CGLMPairCacheEntry *evict = row + oldestway; + + Assert( evict->m_pair != NULL ); + Assert( evict->m_pair != m_ctx->m_pBoundPair ); // just check + + ///////////////////////FIXME may need to do a shoot-down if the pair being evicted is currently active in the context + + m_evictions[ rowIndex ]++; + + // log eviction if desired + if (loglevel >= 2) // misses logged at level 3 and higher + { + //evict->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo ); + //evict->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo ); + //printf("\nSSP: miss - row %05d - [ %s/%d/%d %s/%d/%d ]'s %d'th eviction - ", rowIndex, vtempname, vtempindex, vtempcombo, ptempname, ptempindex, ptempcombo, m_evictions[ rowIndex ] ); + + evict->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) ); + evict->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) ); + printf("\nSSP: miss - row %05d - [ %s + %s ]'s %d'th eviction - ", rowIndex, vtempname, ptempname, m_evictions[ rowIndex ] ); + } + + delete evict->m_pair; evict->m_pair = NULL; + memset( evict, 0, sizeof(*evict) ); + + destway = oldestway; + } + + // make the new entry + CGLMPairCacheEntry *newentry = row + destway; + + newentry->m_lastMark = m_mark; + newentry->m_vertexProg = vp; + newentry->m_fragmentProg = fp; + newentry->m_extraKeyBits = extraKeyBits; + newentry->m_pair = new CGLMShaderPair( m_ctx ); + Assert( newentry->m_pair ); + newentry->m_pair->SetProgramPair( vp, fp ); + + if (loglevel >= 2) // say a little bit more + { + //newentry->m_vertexProg->GetLabelIndexCombo( vtempname, sizeof(vtempname), &vtempindex, &vtempcombo ); + //newentry->m_fragmentProg->GetLabelIndexCombo( ptempname, sizeof(ptempname), &ptempindex, &ptempcombo ); + //printf("new [ %s/%d/%d %s/%d/%d ]", vtempname, vtempindex, vtempcombo, ptempname, ptempindex, ptempcombo ); + + newentry->m_vertexProg->GetComboIndexNameString( vtempname, sizeof(vtempname) ); + newentry->m_fragmentProg->GetComboIndexNameString( ptempname, sizeof(ptempname) ); + printf("new [ %s + %s ]", vtempname, ptempname ); + } + + m_mark = m_mark+1; + if (!m_mark) // somewhat unlikely this will ever be reached.. but we need to avoid zero as a mark value + { + m_mark = 1; + } + + result = newentry->m_pair; + + if (glm_cacheprograms.GetInt()) + { + WriteToProgramCache( newentry->m_pair ); + } + + return result; +} + +void CGLMShaderPairCache::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + if ( (index<0) || ( static_cast<uint>(index) >= (m_rows*m_ways) ) ) + { + // no such location + memset( infoOut, 0, sizeof(*infoOut) ); + + infoOut->m_status = -1; + } + else + { + // locate the entry, and see if an active pair is present. + // if so, extract info and return with m_status=1. + // if not, exit with m_status = 0. + + CGLMPairCacheEntry *entry = &m_entries[index]; + + if (entry->m_pair) + { + // live + // extract values of interest for caller + + entry->m_pair->m_vertexProg->GetLabelIndexCombo ( infoOut->m_vsName, sizeof(infoOut->m_vsName), &infoOut->m_vsStaticIndex, &infoOut->m_vsDynamicIndex ); + entry->m_pair->m_fragmentProg->GetLabelIndexCombo ( infoOut->m_psName, sizeof(infoOut->m_psName), &infoOut->m_psStaticIndex, &infoOut->m_psDynamicIndex ); + + infoOut->m_status = 1; + } + else + { + // not + memset( infoOut, 0, sizeof(*infoOut) ); + infoOut->m_status = 0; + } + } +} + +bool CGLMShaderPairCache::PurgePairsWithShader( CGLMProgram *prog ) +{ + bool result = false; + + // walk all rows*ways + int limit = m_rows * m_ways; + for( int i=0; i < limit; i++) + { + CGLMPairCacheEntry *entry = &m_entries[i]; + + if (entry->m_pair) + { + //scrub it, if not currently bound, and if the supplied shader matches either stage + if ( (entry->m_vertexProg==prog) || (entry->m_fragmentProg==prog) ) + { + // found it, but does it conflict with bound pair ? + if (entry->m_pair == m_ctx->m_pBoundPair) + { + m_ctx->m_pBoundPair = NULL; + m_ctx->m_bDirtyPrograms = true; + } + delete entry->m_pair; + memset( entry, 0, sizeof(*entry) ); + } + } + } + return result; +} + +bool CGLMShaderPairCache::Purge( void ) +{ + bool result = false; + + // walk all rows*ways + int limit = m_rows * m_ways; + for( int i=0; i < limit; i++) + { + CGLMPairCacheEntry *entry = &m_entries[i]; + + if (entry->m_pair) + { + //scrub it, unless the pair is the currently bound pair in our parent glm context + if (entry->m_pair != m_ctx->m_pBoundPair) + { + delete entry->m_pair; + memset( entry, 0, sizeof(*entry) ); + } + else + { + result = true; + } + } + } + return result; +} + +void CGLMShaderPairCache::DumpStats ( void ) +{ +#if GL_SHADER_PAIR_CACHE_STATS + printf("\n------------------\npair cache stats"); + int total = 0; + for( uint row=0; row < m_rows; row++ ) + { + if ( (m_evictions[row] != 0) || (m_hits[row] != 0) ) + { + printf("\n row %d : %d evictions, %d hits",row,m_evictions[row], m_hits[row]); + total += m_evictions[row]; + } + } + printf("\n\npair cache evictions: %d\n-----------------------\n",total ); +#endif +} + + //=============================== + + diff --git a/togl/linuxwin/cglmquery.cpp b/togl/linuxwin/cglmquery.cpp new file mode 100644 index 0000000..199780a --- /dev/null +++ b/togl/linuxwin/cglmquery.cpp @@ -0,0 +1,363 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmquery.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#ifndef _WIN32 + #include <unistd.h> +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +//=============================================================================== + +// http://www.opengl.org/registry/specs/ARB/occlusion_query.txt +// Workaround for "Calling either GenQueriesARB or DeleteQueriesARB while any query of any target is active causes an INVALID_OPERATION error to be generated." +uint CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + +extern ConVar gl_errorcheckall; +extern ConVar gl_errorcheckqueries; +extern ConVar gl_errorchecknone; + +// how many microseconds to wait after a failed query-available test +// presently on MTGL this doesn't happen, but it could change, keep this handy +ConVar gl_nullqueries( "gl_nullqueries", "0" ); + + +//=============================================================================== + +CGLMQuery::CGLMQuery( GLMContext *ctx, GLMQueryParams *params ) +{ + // get the type of query requested + // generate name(s) needed + // set initial state appropriately + + m_ctx = ctx; + m_params = *params; + + m_name = 0; + m_syncobj = 0; + + m_started = m_stopped = m_done = false; + + m_nullQuery = false; + // assume value of convar at start time + // does not change during individual query lifetime + // started null = stays null + // started live = stays live + + switch(m_params.m_type) + { + case EOcclusion: + { + //make an occlusion query (and a fence to go with it) + gGL->glGenQueriesARB( 1, &m_name ); + s_nTotalOcclusionQueryCreatesOrDeletes++; + GLMPRINTF(("-A- CGLMQuery(OQ) created name %d", m_name)); + } + break; + + case EFence: + //make a fence - no aux fence needed + + m_syncobj = 0; + + if (gGL->m_bHave_GL_ARB_sync) + { /* GL_ARB_sync doesn't separate gen and set, so we do glFenceSync() later. */ } + else if (gGL->m_bHave_GL_NV_fence) + gGL->glGenFencesNV(1, &m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glGenFencesAPPLE(1, &m_name ); + + GLMPRINTF(("-A- CGLMQuery(fence) created name %d", m_name)); + break; + } + +} + +CGLMQuery::~CGLMQuery() +{ + GLMPRINTF(("-A-> ~CGLMQuery")); + + // make sure query has completed (might not be necessary) + // delete the name(s) + + switch(m_params.m_type) + { + case EOcclusion: + { + // do a finish occlusion query ? + GLMPRINTF(("-A- ~CGLMQuery(OQ) deleting name %d", m_name)); + gGL->glDeleteQueriesARB(1, &m_name ); + s_nTotalOcclusionQueryCreatesOrDeletes++; + } + break; + + case EFence: + { + // do a finish fence ? + GLMPRINTF(("-A- ~CGLMQuery(fence) deleting name %llu", gGL->m_bHave_GL_ARB_sync ? (unsigned long long) m_syncobj : (unsigned long long) m_name)); +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + gGL->glDeleteSync( m_syncobj ); + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glDeleteFencesNV(1, &m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glDeleteFencesAPPLE(1, &m_name ); + } + break; + } + + m_name = 0; + m_syncobj = 0; + + GLMPRINTF(("-A-< ~CGLMQuery")); +} + + + + +void CGLMQuery::Start( void ) // "start counting" +{ + m_nullQuery = (gl_nullqueries.GetInt() != 0); // latch value for remainder of query life + + m_started = true; + m_stopped = false; + m_done = false; + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + // do nothing.. + } + else + { + gGL->glBeginQueryARB( GL_SAMPLES_PASSED_ARB, m_name ); + } + } + break; + + case EFence: +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + { + if (m_syncobj != 0) gGL->glDeleteSync(m_syncobj); + m_syncobj = gGL->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glSetFenceNV( m_name, GL_ALL_COMPLETED_NV ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glSetFenceAPPLE( m_name ); + + m_stopped = true; // caller should not call Stop on a fence, it self-stops + break; + } +} + +void CGLMQuery::Stop( void ) // "stop counting" +{ + Assert(m_started); + + if ( m_stopped ) + return; + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + // do nothing.. + } + else + { + gGL->glEndQueryARB( GL_SAMPLES_PASSED_ARB ); // we are only putting the request-to-stop-counting into the cmd stream. + } + } + break; + + case EFence: + // nop - you don't "end" a fence, you just test it and/or finish it out in Complete + break; + } + + m_stopped = true; +} + +bool CGLMQuery::IsDone( void ) +{ + Assert(m_started); + Assert(m_stopped); + + if(!m_done) // you can ask more than once, but we only check until it comes back as done. + { + // on occlusion: glGetQueryObjectivARB - large cost on pre SLGU, cheap after + // on fence: glTestFence* on the fence + switch(m_params.m_type) + { + case EOcclusion: // just test the fence that was set after the query begin + { + if (m_nullQuery) + { + // do almost nothing.. but claim work is complete + m_done = true; + } + else + { + // prepare to pay a big price on drivers prior to 10.6.4+SLGU + + GLint available = 0; + gGL->glGetQueryObjectivARB(m_name, GL_QUERY_RESULT_AVAILABLE_ARB, &available ); + + m_done = (available != 0); + } + } + break; + + case EFence: + { +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + m_done = (gGL->glClientWaitSync( m_syncobj, 0, 0 ) == GL_ALREADY_SIGNALED); + else +#endif + if ( m_name == 0 ) + m_done = true; + else if (gGL->m_bHave_GL_NV_fence) + m_done = gGL->glTestFenceNV( m_name ) != 0; + else if (gGL->m_bHave_GL_APPLE_fence) + m_done = gGL->glTestFenceAPPLE( m_name ) != 0; + + if (m_done) + { + if (gGL->m_bHave_GL_ARB_sync) + { /* no-op; we already know it's set to GL_ALREADY_SIGNALED. */ } + else + { + if (gGL->m_bHave_GL_NV_fence) + gGL->glFinishFenceNV( m_name ); // no set fence goes un-finished + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glFinishFenceAPPLE( m_name ); // no set fence goes un-finished + } + } + } + break; + } + } + + return m_done; +} + +void CGLMQuery::Complete( uint *result ) +{ + uint resultval = 0; + //bool bogus_available = false; + + // blocking call if not done + Assert(m_started); + Assert(m_stopped); + + switch(m_params.m_type) + { + case EOcclusion: + { + if (m_nullQuery) + { + m_done = true; + resultval = 0; // we did say "null queries..." + } + else + { + gGL->glGetQueryObjectuivARB( m_name, GL_QUERY_RESULT_ARB, &resultval); + m_done = true; + } + } + break; + + case EFence: + { + if(!m_done) + { +#ifdef HAVE_GL_ARB_SYNC + if (gGL->m_bHave_GL_ARB_sync) + { + if (gGL->glClientWaitSync( m_syncobj, 0, 0 ) != GL_ALREADY_SIGNALED) + { + GLenum syncstate; + do { + const GLuint64 timeout = 10 * ((GLuint64)1000 * 1000 * 1000); // 10 seconds in nanoseconds. + (void)timeout; + syncstate = gGL->glClientWaitSync( m_syncobj, GL_SYNC_FLUSH_COMMANDS_BIT, 0 ); + } while (syncstate == GL_TIMEOUT_EXPIRED); // any errors or success break out of this loop. + } + } + else +#endif + if (gGL->m_bHave_GL_NV_fence) + gGL->glFinishFenceNV( m_name ); + else if (gGL->m_bHave_GL_APPLE_fence) + gGL->glFinishFenceAPPLE( m_name ); + + m_done = true; // for clarity or if they try to Complete twice + } + } + break; + } + + Assert( m_done ); + + // reset state for re-use - i.e. you have to call Complete if you want to re-use the object + m_started = m_stopped = m_done = false; + + if (result) // caller may pass NULL if not interested in result, for example to clear a fence + { + *result = resultval; + } +} + + + + // accessors for the started/stopped state +bool CGLMQuery::IsStarted ( void ) +{ + return m_started; +} + +bool CGLMQuery::IsStopped ( void ) +{ + return m_stopped; +} + diff --git a/togl/linuxwin/cglmtex.cpp b/togl/linuxwin/cglmtex.cpp new file mode 100644 index 0000000..30eb9e4 --- /dev/null +++ b/togl/linuxwin/cglmtex.cpp @@ -0,0 +1,1990 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// cglmtex.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" +#include "glmtexinlines.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +#if defined(OSX) +#include "appframework/ilaunchermgr.h" +extern ILauncherMgr *g_pLauncherMgr; +#endif + +//=============================================================================== + +#if GLMDEBUG +CGLMTex *g_pFirstCGMLTex; +#endif + +ConVar gl_pow2_tempmem( "gl_pow2_tempmem", "0", FCVAR_INTERNAL_USE, + "If set, use power-of-two allocations for temporary texture memory during uploads. " + "May help with fragmentation on certain systems caused by heavy churn of large allocations." ); + +#define TEXSPACE_LOGGING 0 + +// encoding layout to an index where the bits read +// 4 : 1 if compressed +// 2 : 1 if not power of two +// 1 : 1 if mipmapped + +bool pwroftwo (int val ) +{ + return (val & (val-1)) == 0; +} + +int sEncodeLayoutAsIndex( GLMTexLayoutKey *key ) +{ + int index = 0; + + if (key->m_texFlags & kGLMTexMipped) + { + index |= 1; + } + + if ( ! ( pwroftwo(key->m_xSize) && pwroftwo(key->m_ySize) && pwroftwo(key->m_zSize) ) ) + { + // if not all power of two + index |= 2; + } + + if (GetFormatDesc( key->m_texFormat )->m_chunkSize >1 ) + { + index |= 4; + } + + return index; +} + +static unsigned long g_texGlobalBytes[8]; + +//=============================================================================== + +const GLMTexFormatDesc g_formatDescTable[] = +{ + // not yet handled by this table: + // D3DFMT_INDEX16, D3DFMT_VERTEXDATA // D3DFMT_INDEX32, + // WTF { D3DFMT_R5G6R5 ???, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 1, 2 }, + // WTF { D3DFMT_A ???, GL_ALPHA8, GL_ALPHA, GL_UNSIGNED_BYTE, 1, 1 }, + // ??? D3DFMT_V8U8, + // ??? D3DFMT_Q8W8V8U8, + // ??? D3DFMT_X8L8V8U8, + // ??? D3DFMT_R32F, + // ??? D3DFMT_D24X4S4 unsure how to handle or if it is ever used.. + // ??? D3DFMT_D15S1 ever used ? + // ??? D3DFMT_D24X8 ever used? + + // summ-name d3d-format gl-int-format gl-int-format-srgb gl-data-format gl-data-type chunksize, bytes-per-sqchunk + { "_D16", D3DFMT_D16, GL_DEPTH_COMPONENT16, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 1, 2 }, + { "_D24X8", D3DFMT_D24X8, GL_DEPTH_COMPONENT24, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 1, 4 }, // ??? unsure on this one + { "_D24S8", D3DFMT_D24S8, GL_DEPTH24_STENCIL8_EXT, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 1, 4 }, + + { "_A8R8G8B8", D3DFMT_A8R8G8B8, GL_RGBA8, GL_SRGB8_ALPHA8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, + { "_A4R4G4B4", D3DFMT_A4R4G4B4, GL_RGBA4, 0, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, 1, 2 }, + { "_X8R8G8B8", D3DFMT_X8R8G8B8, GL_RGB8, GL_SRGB8_EXT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, + + { "_X1R5G5B5", D3DFMT_X1R5G5B5, GL_RGB5, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 2 }, + { "_A1R5G5B5", D3DFMT_A1R5G5B5, GL_RGB5_A1, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 1, 2 }, + + { "_L8", D3DFMT_L8, GL_LUMINANCE8, GL_SLUMINANCE8_EXT, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 1 }, + { "_A8L8", D3DFMT_A8L8, GL_LUMINANCE8_ALPHA8, GL_SLUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 1, 2 }, + + { "_DXT1", D3DFMT_DXT1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_BYTE, 4, 8 }, + { "_DXT3", D3DFMT_DXT3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 4, 16 }, + { "_DXT5", D3DFMT_DXT5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 4, 16 }, + + { "_A16B16G16R16F", D3DFMT_A16B16G16R16F, GL_RGBA16F_ARB, 0, GL_RGBA, GL_HALF_FLOAT_ARB, 1, 8 }, + { "_A16B16G16R16", D3DFMT_A16B16G16R16, GL_RGBA16, 0, GL_RGBA, GL_UNSIGNED_SHORT, 1, 8 }, // 16bpc integer tex + + { "_A32B32G32R32F", D3DFMT_A32B32G32R32F, GL_RGBA32F_ARB, 0, GL_RGBA, GL_FLOAT, 1, 16 }, + + { "_R8G8B8", D3DFMT_R8G8B8, GL_RGB8, GL_SRGB8_EXT, GL_BGR, GL_UNSIGNED_BYTE, 1, 3 }, + + { "_A8", D3DFMT_A8, GL_ALPHA8, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 1, 1 }, + { "_R5G6B5", D3DFMT_R5G6B5, GL_RGB, GL_SRGB_EXT, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 1, 2 }, + + // fakey tex formats: the stated GL format and the memory layout may not agree (U8V8 for example) + + // _Q8W8V8U8 we just pass through as RGBA bytes. Shader does scale/bias fix + { "_Q8W8V8U8", D3DFMT_Q8W8V8U8, GL_RGBA8, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 1, 4 }, // straight ripoff of D3DFMT_A8R8G8B8 + + // U8V8 is exposed to the client as 2-bytes per texel, but we download it as 3-byte RGB. + // WriteTexels needs to do that conversion from rg8 to rgb8 in order to be able to download it correctly + { "_V8U8", D3DFMT_V8U8, GL_RGB8, 0, GL_RG, GL_BYTE, 1, 2 }, + + { "_R32F", D3DFMT_R32F, GL_R32F, GL_R32F, GL_RED, GL_FLOAT, 1, 4 }, +//$ TODO: Need to merge bitmap changes over from Dota to get these formats. +#if 0 + { "_A2R10G10B10", D3DFMT_A2R10G10B10, GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_10_10_10_2, 1, 4 }, + { "_A2B10G10R10", D3DFMT_A2B10G10R10, GL_RGB10_A2, GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_10_10_10_2, 1, 4 }, +#endif + + /* + // NV shadow depth tex + D3DFMT_NV_INTZ = 0x5a544e49, // MAKEFOURCC('I','N','T','Z') + D3DFMT_NV_RAWZ = 0x5a574152, // MAKEFOURCC('R','A','W','Z') + + // NV null tex + D3DFMT_NV_NULL = 0x4c4c554e, // MAKEFOURCC('N','U','L','L') + + // ATI shadow depth tex + D3DFMT_ATI_D16 = 0x36314644, // MAKEFOURCC('D','F','1','6') + D3DFMT_ATI_D24S8 = 0x34324644, // MAKEFOURCC('D','F','2','4') + + // ATI 1N and 2N compressed tex + D3DFMT_ATI_2N = 0x32495441, // MAKEFOURCC('A', 'T', 'I', '2') + D3DFMT_ATI_1N = 0x31495441, // MAKEFOURCC('A', 'T', 'I', '1') + */ +}; + +int g_formatDescTableCount = sizeof(g_formatDescTable) / sizeof( g_formatDescTable[0] ); + +const GLMTexFormatDesc *GetFormatDesc( D3DFORMAT format ) +{ + for( int i=0; i<g_formatDescTableCount; i++) + { + if (g_formatDescTable[i].m_d3dFormat == format) + { + return &g_formatDescTable[i]; + } + } + return (const GLMTexFormatDesc *)NULL; // not found +} + +//=============================================================================== + + +void InsertTexelComponentFixed( float value, int width, unsigned long *valuebuf ) +{ + unsigned long range = (1<<width); + unsigned long scaled = (value * (float) range) * (range-1) / (range); + + if (scaled >= range) DebuggerBreak(); + + *valuebuf = (*valuebuf << width) | scaled; +} + +// return true if successful +bool GLMGenTexels( GLMGenTexelParams *params ) +{ + unsigned char chunkbuf[256]; // can't think of any chunk this big.. + + const GLMTexFormatDesc *format = GetFormatDesc( params->m_format ); + + if (!format) + { + return FALSE; // fail + } + + // this section just generates one square chunk in the desired format + unsigned long *temp32 = (unsigned long*)chunkbuf; + unsigned int chunksize = 0; // we can sanity check against the format table with this + + switch( params->m_format ) + { + // comment shows byte order in RAM + // lowercase is bit arrangement in a byte + + case D3DFMT_A8R8G8B8: // B G R A + InsertTexelComponentFixed( params->a, 8, temp32 ); // A is inserted first and winds up at most significant bits after insertions follow + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 4; + break; + + case D3DFMT_A4R4G4B4: // [ggggbbbb] [aaaarrrr] RA (nibbles) + InsertTexelComponentFixed( params->a, 4, temp32 ); + InsertTexelComponentFixed( params->r, 4, temp32 ); + InsertTexelComponentFixed( params->g, 4, temp32 ); + InsertTexelComponentFixed( params->b, 4, temp32 ); + chunksize = 2; + break; + + case D3DFMT_X8R8G8B8: // B G R X + InsertTexelComponentFixed( 0.0, 8, temp32 ); + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 4; + break; + + case D3DFMT_X1R5G5B5: // [gggbbbbb] [xrrrrrgg] + InsertTexelComponentFixed( 0.0, 1, temp32 ); + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 5, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_A1R5G5B5: // [gggbbbbb] [arrrrrgg] + InsertTexelComponentFixed( params->a, 1, temp32 ); + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 5, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_L8: // L // caller, use R for L + InsertTexelComponentFixed( params->r, 8, temp32 ); + chunksize = 1; + break; + + case D3DFMT_A8L8: // L A // caller, use R for L and A for A + InsertTexelComponentFixed( params->a, 8, temp32 ); + InsertTexelComponentFixed( params->r, 8, temp32 ); + chunksize = 2; + break; + + case D3DFMT_R8G8B8: // B G R + InsertTexelComponentFixed( params->r, 8, temp32 ); + InsertTexelComponentFixed( params->g, 8, temp32 ); + InsertTexelComponentFixed( params->b, 8, temp32 ); + chunksize = 3; + break; + + case D3DFMT_A8: // A + InsertTexelComponentFixed( params->a, 8, temp32 ); + chunksize = 1; + break; + + case D3DFMT_R5G6B5: // [gggbbbbb] [rrrrrggg] + InsertTexelComponentFixed( params->r, 5, temp32 ); + InsertTexelComponentFixed( params->g, 6, temp32 ); + InsertTexelComponentFixed( params->b, 5, temp32 ); + chunksize = 2; + break; + + case D3DFMT_DXT1: + { + memset( temp32, 0, 8 ); // zap 8 bytes + + // two 565 RGB words followed by 32 bits of 2-bit interp values for a 4x4 block + // we write the same color to both slots and all zeroes for the mask (one color total) + + unsigned long dxt1_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt1_color ); + InsertTexelComponentFixed( params->g, 6, &dxt1_color ); + InsertTexelComponentFixed( params->b, 5, &dxt1_color ); + + // dupe + dxt1_color = dxt1_color | (dxt1_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[0] = dxt1_color; + + // color mask bits after that are already set to all zeroes. chunk is done. + chunksize = 8; + } + break; + + case D3DFMT_DXT3: + { + memset( temp32, 0, 16 ); // zap 16 bytes + + // eight bytes of alpha (16 4-bit alpha nibbles) + // followed by a DXT1 block + + unsigned long dxt3_alpha = 0; + for( int i=0; i<8; i++) + { + // splat same alpha through block + InsertTexelComponentFixed( params->a, 4, &dxt3_alpha ); + } + + unsigned long dxt3_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt3_color ); + InsertTexelComponentFixed( params->g, 6, &dxt3_color ); + InsertTexelComponentFixed( params->b, 5, &dxt3_color ); + + // dupe + dxt3_color = dxt3_color | (dxt3_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[0] = dxt3_alpha; + *(unsigned long*)&chunkbuf[4] = dxt3_alpha; + *(unsigned long*)&chunkbuf[8] = dxt3_color; + *(unsigned long*)&chunkbuf[12] = dxt3_color; + + chunksize = 16; + } + break; + + case D3DFMT_DXT5: + { + memset( temp32, 0, 16 ); // zap 16 bytes + + // DXT5 has 8 bytes of compressed alpha, then 8 bytes of compressed RGB like DXT1. + + // the 8 alpha bytes are 2 bytes of endpoint alpha values, then 16x3 bits of interpolants. + // so to write a single alpha value, just figure out the value, store it in both the first two bytes then store zeroes. + + InsertTexelComponentFixed( params->a, 8, (unsigned long*)&chunkbuf[0] ); + InsertTexelComponentFixed( params->a, 8, (unsigned long*)&chunkbuf[0] ); + // rest of the alpha mask was already zeroed. + + // now do colors + unsigned long dxt5_color = 0; + + // generate one such word and clone it + InsertTexelComponentFixed( params->r, 5, &dxt5_color ); + InsertTexelComponentFixed( params->g, 6, &dxt5_color ); + InsertTexelComponentFixed( params->b, 5, &dxt5_color ); + + // dupe + dxt5_color = dxt5_color | (dxt5_color<<16); + + // write into chunkbuf + *(unsigned long*)&chunkbuf[8] = dxt5_color; + *(unsigned long*)&chunkbuf[12] = dxt5_color; + + chunksize = 16; + } + break; + + + case D3DFMT_A32B32G32R32F: + { + *(float*)&chunkbuf[0] = params->r; + *(float*)&chunkbuf[4] = params->g; + *(float*)&chunkbuf[8] = params->b; + *(float*)&chunkbuf[12] = params->a; + + chunksize = 16; + } + break; + + case D3DFMT_A16B16G16R16: + memset( chunkbuf, 0, 8 ); + // R and G wind up in the first 32 bits + // B and A wind up in the second 32 bits + + InsertTexelComponentFixed( params->a, 16, (unsigned long*)&chunkbuf[4] ); // winds up as MSW of second word (note [4]) - thus last in RAM + InsertTexelComponentFixed( params->b, 16, (unsigned long*)&chunkbuf[4] ); + + InsertTexelComponentFixed( params->g, 16, (unsigned long*)&chunkbuf[0] ); + InsertTexelComponentFixed( params->r, 16, (unsigned long*)&chunkbuf[0] ); // winds up as LSW of first word, thus first in RAM + + chunksize = 8; + break; + + // not done yet + + + //case D3DFMT_D16: + //case D3DFMT_D24X8: + //case D3DFMT_D24S8: + + //case D3DFMT_A16B16G16R16F: + + default: + return FALSE; // fail + break; + } + + // once the chunk buffer is filled.. + + // sanity check the reported chunk size. + if (static_cast<int>(chunksize) != format->m_bytesPerSquareChunk) + { + DebuggerBreak(); + return FALSE; + } + + // verify that the amount you want to write will not exceed the limit byte count + unsigned long destByteCount = chunksize * params->m_chunkCount; + + if (static_cast<int>(destByteCount) > params->m_byteCountLimit) + { + DebuggerBreak(); + return FALSE; + } + + // write the bytes. + unsigned char *destP = (unsigned char*)params->m_dest; + for( int chunk=0; chunk < params->m_chunkCount; chunk++) + { + for( uint byteindex = 0; byteindex < chunksize; byteindex++) + { + *destP++ = chunkbuf[byteindex]; + } + } + params->m_bytesWritten = destP - (unsigned char*)params->m_dest; + + return TRUE; +} + + +//=============================================================================== +bool LessFunc_GLMTexLayoutKey( const GLMTexLayoutKey &a, const GLMTexLayoutKey &b ) +{ + #define DO_LESS(fff) if (a.fff != b.fff) { return (a.fff< b.fff); } + + DO_LESS(m_texGLTarget); + DO_LESS(m_texFormat); + DO_LESS(m_texFlags); + DO_LESS(m_texSamples); + DO_LESS(m_xSize); + DO_LESS(m_ySize) + DO_LESS(m_zSize); + + #undef DO_LESS + + return false; // they are equal +} + +CGLMTexLayoutTable::CGLMTexLayoutTable() +{ + m_layoutMap.SetLessFunc( LessFunc_GLMTexLayoutKey ); +} + +GLMTexLayout *CGLMTexLayoutTable::NewLayoutRef( GLMTexLayoutKey *pDesiredKey ) +{ + GLMTexLayoutKey tempKey; + GLMTexLayoutKey *key = pDesiredKey; + + // look up 'key' in the map and see if it's a hit, if so, bump the refcount and return + // if not, generate a completed layout based on the key, add to map, set refcount to 1, return that + + const GLMTexFormatDesc *formatDesc = GetFormatDesc( key->m_texFormat ); + + //bool compression = (formatDesc->m_chunkSize > 1) != 0; + if (!formatDesc) + { + GLMStop(); // bad news + } + + if ( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) + { + if ( ( formatDesc->m_glIntFormatSRGB != 0 ) && ( ( key->m_texFlags & kGLMTexSRGB ) == 0 ) ) + { + tempKey = *pDesiredKey; + key = &tempKey; + + // Slam on SRGB texture flag, and we'll use GL_EXT_texture_sRGB_decode to selectively turn it off in the samplers + key->m_texFlags |= kGLMTexSRGB; + } + } + + unsigned short index = m_layoutMap.Find( *key ); + if (index != m_layoutMap.InvalidIndex()) + { + // found it + //printf(" -hit- "); + GLMTexLayout *layout = m_layoutMap[ index ]; + + // bump ref count + layout->m_refCount ++; + + return layout; + } + else + { + //printf(" -miss- "); + // need to make a new one + // to allocate it, we need to know how big to make it (slice count) + + // figure out how many mip levels are in play + int mipCount = 1; + if (key->m_texFlags & kGLMTexMipped) + { + int largestAxis = key->m_xSize; + + if (key->m_ySize > largestAxis) + largestAxis = key->m_ySize; + + if (key->m_zSize > largestAxis) + largestAxis = key->m_zSize; + + mipCount = 0; + while( largestAxis > 0 ) + { + mipCount ++; + largestAxis >>= 1; + } + } + + int faceCount = 1; + if (key->m_texGLTarget == GL_TEXTURE_CUBE_MAP) + { + faceCount = 6; + } + + int sliceCount = mipCount * faceCount; + + if (key->m_texFlags & kGLMTexMultisampled) + { + Assert( (key->m_texGLTarget == GL_TEXTURE_2D) ); + Assert( sliceCount == 1 ); + + // assume non mipped + Assert( (key->m_texFlags & kGLMTexMipped) == 0 ); + Assert( (key->m_texFlags & kGLMTexMippedAuto) == 0 ); + + // assume renderable and srgb + Assert( (key->m_texFlags & kGLMTexRenderable) !=0 ); + //Assert( (key->m_texFlags & kGLMTexSRGB) !=0 ); //FIXME don't assert on making depthstencil surfaces which are non srgb + + // double check sample count (FIXME need real limit check here against device/driver) + Assert( (key->m_texSamples==2) || (key->m_texSamples==4) || (key->m_texSamples==6) || (key->m_texSamples==8) ); + } + + // now we know enough to allocate and populate the new tex layout. + + // malloc the new layout + int layoutSize = sizeof( GLMTexLayout ) + (sliceCount * sizeof( GLMTexLayoutSlice )); + GLMTexLayout *layout = (GLMTexLayout *)malloc( layoutSize ); + memset( layout, 0, layoutSize ); + + // clone the key in there + memset( &layout->m_key, 0x00, sizeof(layout->m_key) ); + layout->m_key = *key; + + // set refcount + layout->m_refCount = 1; + + // save the format desc + layout->m_format = (GLMTexFormatDesc *)formatDesc; + + // we know the mipcount from before + layout->m_mipCount = mipCount; + + // we know the face count too + layout->m_faceCount = faceCount; + + // slice count is the product + layout->m_sliceCount = mipCount * faceCount; + + // we can now fill in the slices. + GLMTexLayoutSlice *slicePtr = &layout->m_slices[0]; + int storageOffset = 0; + + //bool compressed = (formatDesc->m_chunkSize > 1); // true if DXT + + for( int mip = 0; mip < mipCount; mip ++ ) + { + for( int face = 0; face < faceCount; face++ ) + { + // note application of chunk size which is 1 for uncompressed, and 4 for compressed tex (DXT) + // note also that the *dimensions* must scale down to 1 + // but that the *storage* cannot go below 4x4. + // we introduce the "storage sizes" which are clamped, to compute the storage footprint. + + int storage_x,storage_y,storage_z; + + slicePtr->m_xSize = layout->m_key.m_xSize >> mip; + slicePtr->m_xSize = MAX( slicePtr->m_xSize, 1 ); // dimension can't go to zero + storage_x = MAX( slicePtr->m_xSize, formatDesc->m_chunkSize ); // storage extent can't go below chunk size + + slicePtr->m_ySize = layout->m_key.m_ySize >> mip; + slicePtr->m_ySize = MAX( slicePtr->m_ySize, 1 ); // dimension can't go to zero + storage_y = MAX( slicePtr->m_ySize, formatDesc->m_chunkSize ); // storage extent can't go below chunk size + + slicePtr->m_zSize = layout->m_key.m_zSize >> mip; + slicePtr->m_zSize = MAX( slicePtr->m_zSize, 1 ); // dimension can't go to zero + storage_z = MAX( slicePtr->m_zSize, 1); // storage extent for Z cannot go below '1'. + + //if (compressed) NO NO NO do not lie about the dimensionality, just fudge the storage. + //{ + // // round up to multiple of 4 in X and Y axes + // slicePtr->m_xSize = (slicePtr->m_xSize+3) & (~3); + // slicePtr->m_ySize = (slicePtr->m_ySize+3) & (~3); + //} + + int xchunks = (storage_x / formatDesc->m_chunkSize ); + int ychunks = (storage_y / formatDesc->m_chunkSize ); + + slicePtr->m_storageSize = (xchunks * ychunks * formatDesc->m_bytesPerSquareChunk) * storage_z; + slicePtr->m_storageOffset = storageOffset; + + storageOffset += slicePtr->m_storageSize; + storageOffset = ( (storageOffset+0x0F) & (~0x0F)); // keep each MIP starting on a 16 byte boundary. + + slicePtr++; + } + } + + layout->m_storageTotalSize = storageOffset; + //printf("\n size %08x for key (x=%d y=%d z=%d, fmt=%08x, bpsc=%d)", layout->m_storageTotalSize, key->m_xSize, key->m_ySize, key->m_zSize, key->m_texFormat, formatDesc->m_bytesPerSquareChunk ); + + // generate summary + // "target, format, +/- mips, base size" + char scratch[1024]; + + char *targetname = "?"; + switch( key->m_texGLTarget ) + { + case GL_TEXTURE_2D: targetname = "2D "; break; + case GL_TEXTURE_3D: targetname = "3D "; break; + case GL_TEXTURE_CUBE_MAP: targetname = "CUBE"; break; + } + + sprintf( scratch, "[%s %s %dx%dx%d mips=%d slices=%d flags=%02lX%s]", + targetname, + formatDesc->m_formatSummary, + layout->m_key.m_xSize, layout->m_key.m_ySize, layout->m_key.m_zSize, + mipCount, + sliceCount, + layout->m_key.m_texFlags, + (layout->m_key.m_texFlags & kGLMTexSRGB) ? " SRGB" : "" + ); + layout->m_layoutSummary = strdup( scratch ); + //GLMPRINTF(("-D- new tex layout [ %s ]", scratch )); + + // then insert into map. disregard returned index. + m_layoutMap.Insert( layout->m_key, layout ); + + return layout; + } +} + +void CGLMTexLayoutTable::DelLayoutRef( GLMTexLayout *layout ) +{ + // locate layout in hash, drop refcount + // (some GC step later on will harvest expired layouts - not like it's any big challenge to re-generate them) + + unsigned short index = m_layoutMap.Find( layout->m_key ); + if (index != m_layoutMap.InvalidIndex()) + { + // found it + GLMTexLayout *layout = m_layoutMap[ index ]; + + // drop ref count + layout->m_refCount --; + + //assert( layout->m_refCount >= 0 ); + } + else + { + // that's bad + GLMStop(); + } +} + +void CGLMTexLayoutTable::DumpStats( ) +{ + for (uint i=0; i<m_layoutMap.Count(); i++ ) + { + GLMTexLayout *layout = m_layoutMap[ i ]; + + // print it out + printf("\n%05d instances %08d bytes %08d totbytes %s", layout->m_refCount, layout->m_storageTotalSize, (layout->m_refCount*layout->m_storageTotalSize), layout->m_layoutSummary ); + } +} + +ConVar gl_texmsaalog ( "gl_texmsaalog", "0"); + +ConVar gl_rt_forcergba ( "gl_rt_forcergba", "1" ); // on teximage of a renderable tex, pass GL_RGBA in place of GL_BGRA + +ConVar gl_minimize_rt_tex ( "gl_minimize_rt_tex", "0" ); // if 1, set the GL_TEXTURE_MINIMIZE_STORAGE_APPLE texture parameter to cut off mipmaps for RT's +ConVar gl_minimize_all_tex ( "gl_minimize_all_tex", "1" ); // if 1, set the GL_TEXTURE_MINIMIZE_STORAGE_APPLE texture parameter to cut off mipmaps for textures which are unmipped +ConVar gl_minimize_tex_log ( "gl_minimize_tex_log", "0" ); // if 1, printf the names of the tex that got minimized + +CGLMTex::CGLMTex( GLMContext *ctx, GLMTexLayout *layout, uint levels, const char *debugLabel ) +{ +#if GLMDEBUG + m_pPrevTex = NULL; + m_pNextTex = g_pFirstCGMLTex; + if ( m_pNextTex ) + { + Assert( m_pNextTex->m_pPrevTex == NULL ); + m_pNextTex->m_pPrevTex = this; + } + g_pFirstCGMLTex = this; +#endif + + // caller has responsibility to make 'ctx' current, but we check to be sure. + ctx->CheckCurrent(); + + m_nLastResolvedBatchCounter = ctx->m_nBatchCounter; + + // note layout requested + m_layout = layout; + m_texGLTarget = m_layout->m_key.m_texGLTarget; + + m_nSamplerType = SAMPLER_TYPE_UNUSED; + switch ( m_texGLTarget ) + { + case GL_TEXTURE_CUBE_MAP: m_nSamplerType = SAMPLER_TYPE_CUBE; break; + case GL_TEXTURE_2D: m_nSamplerType = SAMPLER_TYPE_2D; break; + case GL_TEXTURE_3D: m_nSamplerType = SAMPLER_TYPE_3D; break; + default: + Assert( 0 ); + break; + } + + m_maxActiveMip = -1; //index of highest mip that has been written - increase as each mip arrives + m_minActiveMip = 999; //index of lowest mip that has been written - lower it as each mip arrives + + // note context owner + m_ctx = ctx; + + // clear the bind point flags + //m_bindPoints.ClearAll(); + + // clear the RT attach count + m_rtAttachCount = 0; + + // come up with a GL name for this texture. + m_texName = ctx->CreateTex( m_texGLTarget, m_layout->m_format->m_glIntFormat ); + + m_pBlitSrcFBO = NULL; + m_pBlitDstFBO = NULL; + + // Sense whether to try and apply client storage upon teximage/subimage. + // This should only be true if we're running on OSX 10.6 or it was explicitly + // enabled with -gl_texclientstorage on the command line. + m_texClientStorage = ctx->m_bTexClientStorage; + + // flag that we have not yet been explicitly kicked into VRAM.. + m_texPreloaded = false; + + // clone the debug label if there is one. + m_debugLabel = debugLabel ? strdup(debugLabel) : NULL; + + // if tex is MSAA renderable, make an RBO, else zero the RBO name and dirty bit + if (layout->m_key.m_texFlags & kGLMTexMultisampled) + { + gGL->glGenRenderbuffersEXT( 1, &m_rboName ); + + // so we have enough info to go ahead and bind the RBO and put storage on it? + // try it. + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_rboName ); + + // quietly clamp if sample count exceeds known limit for the device + int sampleCount = layout->m_key.m_texSamples; + + if (sampleCount > ctx->Caps().m_maxSamples) + { + sampleCount = ctx->Caps().m_maxSamples; // clamp + } + + GLenum msaaFormat = (layout->m_key.m_texFlags & kGLMTexSRGB) ? layout->m_format->m_glIntFormatSRGB : layout->m_format->m_glIntFormat; + gGL->glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, + sampleCount, // not "layout->m_key.m_texSamples" + msaaFormat, + layout->m_key.m_xSize, + layout->m_key.m_ySize ); + + if (gl_texmsaalog.GetInt()) + { + printf( "\n == MSAA Tex %p %s : MSAA RBO is intformat %s (%x)", this, m_debugLabel?m_debugLabel:"", GLMDecode( eGL_ENUM, msaaFormat ), msaaFormat ); + } + + gGL->glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + m_rboName = 0; + } + + // at this point we have the complete description of the texture, and a name for it, but no data and no actual GL object. + // we know this name has bever seen duty before, so we're going to hard-bind it to TMU 0, displacing any other tex that might have been bound there. + // any previously bound tex will be unbound and appropriately marked as a result. + // the active TMU will be set as a side effect. + CGLMTex *pPrevTex = ctx->m_samplers[0].m_pBoundTex; + ctx->BindTexToTMU( this, 0 ); + + m_SamplingParams.SetToDefaults(); + m_SamplingParams.SetToTarget( m_texGLTarget ); + + // OK, our texture now exists and is bound on the active TMU. Not drawable yet though. + + // Create backing storage and fill it + if ( !(layout->m_key.m_texFlags & kGLMTexRenderable) && m_texClientStorage ) + { + m_backing = (char *)malloc( m_layout->m_storageTotalSize ); + memset( m_backing, 0, m_layout->m_storageTotalSize ); + + // track bytes allocated for non-RT's + int formindex = sEncodeLayoutAsIndex( &layout->m_key ); + + g_texGlobalBytes[ formindex ] += m_layout->m_storageTotalSize; + + #if TEXSPACE_LOGGING + printf( "\n Tex %s added %d bytes in form %d which is now %d bytes", m_debugLabel ? m_debugLabel : "-", m_layout->m_storageTotalSize, formindex, g_texGlobalBytes[ formindex ] ); + printf( "\n\t\t[ %d %d %d %d %d %d %d %d ]", + g_texGlobalBytes[ 0 ],g_texGlobalBytes[ 1 ],g_texGlobalBytes[ 2 ],g_texGlobalBytes[ 3 ], + g_texGlobalBytes[ 4 ],g_texGlobalBytes[ 5 ],g_texGlobalBytes[ 6 ],g_texGlobalBytes[ 7 ] + ); + #endif + } + else + { + m_backing = NULL; + + m_texClientStorage = false; + } + + // init lock count + // lock reqs are tracked by the owning context + m_lockCount = 0; + + m_sliceFlags.SetCount( m_layout->m_sliceCount ); + for( int i=0; i< m_layout->m_sliceCount; i++) + { + m_sliceFlags[i] = 0; + // kSliceValid = false (we have not teximaged each slice yet) + // kSliceStorageValid = false (the storage allocated does not reflect what is in the tex) + // kSliceLocked = false (the slices are not locked) + // kSliceFullyDirty = false (this does not come true til first lock) + } + + // texture minimize parameter keeps driver from allocing mips when it should not, by being explicit about the ones that have no mips. + + bool setMinimizeParameter = false; + bool minimize_rt = (gl_minimize_rt_tex.GetInt()!=0); + bool minimize_all = (gl_minimize_all_tex.GetInt()!=0); + + if (layout->m_key.m_texFlags & kGLMTexRenderable) + { + // it's an RT. if mips were not explicitly requested, and "gl_minimize_rt_tex" is true, set the minimize parameter. + if ( (minimize_rt || minimize_all) && ( !(layout->m_key.m_texFlags & kGLMTexMipped) ) ) + { + setMinimizeParameter = true; + } + } + else + { + // not an RT. if mips were not requested, and "gl_minimize_all_tex" is true, set the minimize parameter. + if ( minimize_all && ( !(layout->m_key.m_texFlags & kGLMTexMipped) ) ) + { + setMinimizeParameter = true; + } + } + + if (setMinimizeParameter) + { + if (gl_minimize_tex_log.GetInt()) + { + printf("\n minimizing storage for tex '%s' [%s] ", m_debugLabel?m_debugLabel:"-", m_layout->m_layoutSummary ); + } + + if (gGL->m_bHave_GL_APPLE_texture_range) + gGL->glTexParameteri( m_layout->m_key.m_texGLTarget, GL_TEXTURE_MINIMIZE_STORAGE_APPLE, 1 ); + } + + // after a lot of pain with texture completeness... + // always push black into all slices of all newly created textures. + + #if 0 + bool pushRenderableSlices = (m_layout->m_key.m_texFlags & kGLMTexRenderable) != 0; + bool pushTexSlices = true; // just do it everywhere (m_layout->m_mipCount>1) && (m_layout->m_format->m_chunkSize !=1) ; + if (pushTexSlices) + { + // fill storage with mostly-opaque purple + + GLMGenTexelParams genp; + memset( &genp, 0, sizeof(genp) ); + + genp.m_format = m_layout->m_format->m_d3dFormat; + const GLMTexFormatDesc *format = GetFormatDesc( genp.m_format ); + + genp.m_dest = m_backing; // dest addr + genp.m_chunkCount = m_layout->m_storageTotalSize / format->m_bytesPerSquareChunk; // fill the whole slab + genp.m_byteCountLimit = m_layout->m_storageTotalSize; // limit writes to this amount + + genp.r = 1.0; + genp.g = 0.0; + genp.b = 1.0; + genp.a = 0.75; + + GLMGenTexels( &genp ); + } + #endif + + //if (pushRenderableSlices || pushTexSlices) + if ( !( ( layout->m_key.m_texFlags & kGLMTexMipped ) && ( levels == ( unsigned ) m_layout->m_mipCount ) ) ) + { + for( int face=0; face <m_layout->m_faceCount; face++) + { + for( int mip=0; mip <m_layout->m_mipCount; mip++) + { + // we're not really going to lock, we're just going to write the blank data from the backing store we just made + GLMTexLockDesc desc; + + desc.m_req.m_tex = this; + desc.m_req.m_face = face; + desc.m_req.m_mip = mip; + + desc.m_sliceIndex = CalcSliceIndex( face, mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc.m_sliceIndex ]; + + desc.m_req.m_region.xmin = desc.m_req.m_region.ymin = desc.m_req.m_region.zmin = 0; + desc.m_req.m_region.xmax = slice->m_xSize; + desc.m_req.m_region.ymax = slice->m_ySize; + desc.m_req.m_region.zmax = slice->m_zSize; + + desc.m_sliceBaseOffset = slice->m_storageOffset; // doesn't really matter... we're just pushing zeroes.. + desc.m_sliceRegionOffset = 0; + + WriteTexels( &desc, true, (layout->m_key.m_texFlags & kGLMTexRenderable)!=0 ); // write whole slice - but disable data source if it's an RT, as there's no backing + } + } + } + GLMPRINTF(("-A- -**TEXNEW '%-60s' name=%06d size=%09d storage=%08x label=%s ", m_layout->m_layoutSummary, m_texName, m_layout->m_storageTotalSize, m_backing, m_debugLabel ? m_debugLabel : "-" )); + + ctx->BindTexToTMU( pPrevTex, 0 ); +} + +CGLMTex::~CGLMTex( ) +{ +#if GLMDEBUG + if ( m_pPrevTex ) + { + Assert( m_pPrevTex->m_pNextTex == this ); + m_pPrevTex->m_pNextTex = m_pNextTex; + } + else + { + Assert( g_pFirstCGMLTex == this ); + g_pFirstCGMLTex = m_pNextTex; + } + if ( m_pNextTex ) + { + Assert( m_pNextTex->m_pPrevTex == this ); + m_pNextTex->m_pPrevTex = m_pPrevTex; + } + m_pNextTex = m_pPrevTex = NULL; +#endif + + if ( !(m_layout->m_key.m_texFlags & kGLMTexRenderable) ) + { + int formindex = sEncodeLayoutAsIndex( &m_layout->m_key ); + + g_texGlobalBytes[ formindex ] -= m_layout->m_storageTotalSize; + + #if TEXSPACE_LOGGING + printf( "\n Tex %s freed %d bytes in form %d which is now %d bytes", m_debugLabel ? m_debugLabel : "-", m_layout->m_storageTotalSize, formindex, g_texGlobalBytes[ formindex ] ); + printf( "\n\t\t[ %d %d %d %d %d %d %d %d ]", + g_texGlobalBytes[ 0 ],g_texGlobalBytes[ 1 ],g_texGlobalBytes[ 2 ],g_texGlobalBytes[ 3 ], + g_texGlobalBytes[ 4 ],g_texGlobalBytes[ 5 ],g_texGlobalBytes[ 6 ],g_texGlobalBytes[ 7 ] + ); + #endif + } + + GLMPRINTF(("-A- -**TEXDEL '%-60s' name=%06d size=%09d storage=%08x label=%s ", m_layout->m_layoutSummary, m_texName, m_layout->m_storageTotalSize, m_backing, m_debugLabel ? m_debugLabel : "-" )); + // check first to see if we were still bound anywhere or locked... these should be failures. + + if ( m_pBlitSrcFBO ) + { + m_ctx->DelFBO( m_pBlitSrcFBO ); + m_pBlitSrcFBO = NULL; + } + + if ( m_pBlitDstFBO ) + { + m_ctx->DelFBO( m_pBlitDstFBO ); + m_pBlitDstFBO = NULL; + } + + if ( m_rboName ) + { + gGL->glDeleteRenderbuffersEXT( 1, &m_rboName ); + m_rboName = 0; + } + + // if all that is OK, then delete the underlying tex + if ( m_texName ) + { + m_ctx->DestroyTex( m_texGLTarget, m_layout, m_texName ); + m_texName = 0; + } + + // release our usage of the layout + m_ctx->m_texLayoutTable->DelLayoutRef( m_layout ); + m_layout = NULL; + + if (m_backing) + { + free( m_backing ); + m_backing = NULL; + } + + if (m_debugLabel) + { + free( m_debugLabel ); + m_debugLabel = NULL; + } + + m_ctx = NULL; +} + +int CGLMTex::CalcSliceIndex( int face, int mip ) +{ + // faces of the same mip level are adjacent. "face major" storage + int index = (mip * m_layout->m_faceCount) + face; + + return index; +} + +void CGLMTex::CalcTexelDataOffsetAndStrides( int sliceIndex, int x, int y, int z, int *offsetOut, int *yStrideOut, int *zStrideOut ) +{ + int offset = 0; + int yStride = 0; + int zStride = 0; + + GLMTexFormatDesc *format = m_layout->m_format; + if (format->m_chunkSize==1) + { + // figure out row stride and layer stride + yStride = format->m_bytesPerSquareChunk * m_layout->m_slices[sliceIndex].m_xSize; // bytes per texel row (y stride) + zStride = yStride * m_layout->m_slices[sliceIndex].m_ySize; // bytes per texel layer (if 3D tex) + + offset = x * format->m_bytesPerSquareChunk; // lateral offset + offset += (y * yStride); // scanline offset + offset += (z * zStride); // should be zero for 2D tex + } + else + { + yStride = format->m_bytesPerSquareChunk * (m_layout->m_slices[sliceIndex].m_xSize / format->m_chunkSize); + zStride = yStride * (m_layout->m_slices[sliceIndex].m_ySize / format->m_chunkSize); + + // compressed format. scale the x,y,z values into chunks. + // assert if any of them are not multiples of a chunk. + int chunkx = x / format->m_chunkSize; + int chunky = y / format->m_chunkSize; + int chunkz = z / format->m_chunkSize; + + if ( (chunkx * format->m_chunkSize) != x) + { + GLMStop(); + } + + if ( (chunky * format->m_chunkSize) != y) + { + GLMStop(); + } + + if ( (chunkz * format->m_chunkSize) != z) + { + GLMStop(); + } + + offset = chunkx * format->m_bytesPerSquareChunk; // lateral offset + offset += (chunky * yStride); // chunk row offset + offset += (chunkz * zStride); // should be zero for 2D tex + } + + *offsetOut = offset; + *yStrideOut = yStride; + *zStrideOut = zStride; +} + +void CGLMTex::ReadTexels( GLMTexLockDesc *desc, bool readWholeSlice ) +{ + GLMRegion readBox; + + if (readWholeSlice) + { + readBox.xmin = readBox.ymin = readBox.zmin = 0; + + readBox.xmax = m_layout->m_slices[ desc->m_sliceIndex ].m_xSize; + readBox.ymax = m_layout->m_slices[ desc->m_sliceIndex ].m_ySize; + readBox.zmax = m_layout->m_slices[ desc->m_sliceIndex ].m_zSize; + } + else + { + readBox = desc->m_req.m_region; + } + + CGLMTex *pPrevTex = m_ctx->m_samplers[0].m_pBoundTex; + m_ctx->BindTexToTMU( this, 0 ); // SelectTMU(n) is a side effect + + if (readWholeSlice) + { + // make this work first.... then write the partial path + // (Hmmmm, I don't think we will ever actually need a partial path - + // since we have no notion of a partially valid slice of storage + + GLMTexFormatDesc *format = m_layout->m_format; + GLenum target = m_layout->m_key.m_texGLTarget; + + void *sliceAddress = m_backing + m_layout->m_slices[ desc->m_sliceIndex ].m_storageOffset; // this would change for PBO + //int sliceSize = m_layout->m_slices[ desc->m_sliceIndex ].m_storageSize; + + // interestingly enough, we can use the same path for both 2D and 3D fetch + + switch( target ) + { + case GL_TEXTURE_CUBE_MAP: + + // adjust target to steer to the proper face, then fall through to the 2D texture path. + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + desc->m_req.m_face; + + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glGetCompressedTexImage.xml + + gGL->glGetCompressedTexImage( target, // target + desc->m_req.m_mip, // level + sliceAddress ); // destination + } + else + { + // uncompressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml + + gGL->glGetTexImage( target, // target + desc->m_req.m_mip, // level + format->m_glDataFormat, // dataformat + format->m_glDataType, // datatype + sliceAddress ); // destination + } + } + break; + } + } + else + { + GLMStop(); + } + + m_ctx->BindTexToTMU( pPrevTex, 0 ); +} + +// TexSubImage should work properly on every driver stack and GPU--enabling by default. +ConVar gl_enabletexsubimage( "gl_enabletexsubimage", "1" ); + +void CGLMTex::WriteTexels( GLMTexLockDesc *desc, bool writeWholeSlice, bool noDataWrite ) +{ + //if ( m_nBindlessHashNumEntries ) + // return; + + GLMRegion writeBox; + + bool needsExpand = false; + char *expandTemp = NULL; + + switch( m_layout->m_format->m_d3dFormat) + { + case D3DFMT_V8U8: + { + needsExpand = true; + writeWholeSlice = true; + + // shoot down client storage if we have to generate a new flavor of the data + m_texClientStorage = false; + } + break; + } + + if (writeWholeSlice) + { + writeBox.xmin = writeBox.ymin = writeBox.zmin = 0; + + writeBox.xmax = m_layout->m_slices[ desc->m_sliceIndex ].m_xSize; + writeBox.ymax = m_layout->m_slices[ desc->m_sliceIndex ].m_ySize; + writeBox.zmax = m_layout->m_slices[ desc->m_sliceIndex ].m_zSize; + } + else + { + writeBox = desc->m_req.m_region; + } + + // first thing is to get the GL texture bound to a TMU, or just select one if already bound + // to get this running we will just always slam TMU 0 and let the draw time code fix it back + // a later optimization would be to hoist the bind call to the caller, do it exactly once + + CGLMTex *pPrevTex = m_ctx->m_samplers[0].m_pBoundTex; + m_ctx->BindTexToTMU( this, 0 ); // SelectTMU(n) is a side effect + + GLMTexFormatDesc *format = m_layout->m_format; + + GLenum target = m_layout->m_key.m_texGLTarget; + GLenum glDataFormat = format->m_glDataFormat; // this could change if expansion kicks in + GLenum glDataType = format->m_glDataType; + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc->m_sliceIndex ]; + void *sliceAddress = m_backing ? (m_backing + slice->m_storageOffset) : NULL; // this would change for PBO + + // allow use of subimage if the target is texture2D and it has already been teximage'd + bool mayUseSubImage = false; + if ( (target==GL_TEXTURE_2D) && (m_sliceFlags[ desc->m_sliceIndex ] & kSliceValid) ) + { + mayUseSubImage = gl_enabletexsubimage.GetInt() != 0; + } + + // check flavor, 2D, 3D, or cube map + // we also have the choice to use subimage if this is a tex already created. (open question as to benefit) + + + // SRGB select. At this level (writetexels) we firmly obey the m_texFlags. + // (mechanism not policy) + + GLenum intformat = (m_layout->m_key.m_texFlags & kGLMTexSRGB) ? format->m_glIntFormatSRGB : format->m_glIntFormat; + if (CommandLine()->FindParm("-disable_srgbtex")) + { + // force non srgb flavor - experiment to make ATI r600 happy on 10.5.8 (maybe x1600 too!) + intformat = format->m_glIntFormat; + } + + Assert( intformat != 0 ); + + if (m_layout->m_key.m_texFlags & kGLMTexSRGB) + { + Assert( m_layout->m_format->m_glDataFormat != GL_DEPTH_COMPONENT ); + Assert( m_layout->m_format->m_glDataFormat != GL_DEPTH_STENCIL_EXT ); + Assert( m_layout->m_format->m_glDataFormat != GL_ALPHA ); + } + + // adjust min and max mip written + if (desc->m_req.m_mip > m_maxActiveMip) + { + m_maxActiveMip = desc->m_req.m_mip; + + gGL->glTexParameteri( target, GL_TEXTURE_MAX_LEVEL, desc->m_req.m_mip); + } + + if (desc->m_req.m_mip < m_minActiveMip) + { + m_minActiveMip = desc->m_req.m_mip; + + gGL->glTexParameteri( target, GL_TEXTURE_BASE_LEVEL, desc->m_req.m_mip); + } + + if (needsExpand) + { + int expandSize = 0; + + switch( m_layout->m_format->m_d3dFormat) + { + case D3DFMT_V8U8: + { + // figure out new size based on 3byte RGB format + // easy, just take the two byte size and grow it by 50% + expandSize = (slice->m_storageSize * 3) / 2; + expandTemp = (char*)malloc( expandSize ); + + char *src = (char*)sliceAddress; + char *dst = expandTemp; + + // transfer RG's to RGB's + while(expandSize>0) + { + *dst = *src++; // move first byte + *dst = *src++; // move second byte + *reinterpret_cast<uint8*>(dst) = 0xBB; // pad third byte + + expandSize -= 3; + } + + // move the slice pointer + sliceAddress = expandTemp; + + // change the data format we tell GL about + glDataFormat = GL_RGB; + } + break; + + default: Assert(!"Don't know how to expand that format.."); + } + + } + + // set up the client storage now, one way or another + // If this extension isn't supported, we just end up with two copies of the texture, one in the GL and one in app memory. + // So it's safe to just go on as if this extension existed and hold the possibly-unnecessary extra RAM. + if (gGL->m_bHave_GL_APPLE_client_storage) + { + gGL->glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, m_texClientStorage ); + } + + switch( target ) + { + case GL_TEXTURE_CUBE_MAP: + + // adjust target to steer to the proper face, then fall through to the 2D texture path. + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + desc->m_req.m_face; + + case GL_TEXTURE_2D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + Assert( writeWholeSlice ); //subimage not implemented in this path yet + + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage2D.xml + gGL->glCompressedTexImage2D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat - don't use format->m_glIntFormat because we have the SRGB select going on above + slice->m_xSize, // width + slice->m_ySize, // height + 0, // border + slice->m_storageSize, // imageSize + sliceAddress ); // data + + + } + else + { + if (mayUseSubImage) + { + // go subimage2D if it's a replacement, not a creation + + + gGL->glPixelStorei( GL_UNPACK_ROW_LENGTH, slice->m_xSize ); // in pixels + gGL->glPixelStorei( GL_UNPACK_SKIP_PIXELS, writeBox.xmin ); // in pixels + gGL->glPixelStorei( GL_UNPACK_SKIP_ROWS, writeBox.ymin ); // in pixels + + gGL->glTexSubImage2D( target, + desc->m_req.m_mip, // level + writeBox.xmin, // xoffset into dest + writeBox.ymin, // yoffset into dest + writeBox.xmax - writeBox.xmin, // width (was slice->m_xSize) + writeBox.ymax - writeBox.ymin, // height (was slice->m_ySize) + glDataFormat, // format + glDataType, // type + sliceAddress // data (will be offsetted by the SKIP_PIXELS and SKIP_ROWS - let GL do the math to find the first source texel) + ); + + gGL->glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + gGL->glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + gGL->glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + + /* + //http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage2D.xml + glTexSubImage2D( target, + desc->m_req.m_mip, // level + 0, // xoffset + 0, // yoffset + slice->m_xSize, // width + slice->m_ySize, // height + glDataFormat, // format + glDataType, // type + sliceAddress // data + ); + */ + } + else + { + if (m_layout->m_key.m_texFlags & kGLMTexRenderable) + { + if (gl_rt_forcergba.GetInt()) + { + if (glDataFormat == GL_BGRA) + { + // change it + glDataFormat = GL_RGBA; + } + } + } + + // uncompressed path + // http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/teximage2d.html + gGL->glTexImage2D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat - don't use format->m_glIntFormat because we have the SRGB select going on above + slice->m_xSize, // width + slice->m_ySize, // height + 0, // border + glDataFormat, // dataformat + glDataType, // datatype + noDataWrite ? NULL : sliceAddress ); // data (optionally suppressed in case ResetSRGB desires) + + if (m_layout->m_key.m_texFlags & kGLMTexMultisampled) + { + if (gl_texmsaalog.GetInt()) + { + printf( "\n == MSAA Tex %p %s : glTexImage2D for flat tex using intformat %s (%x)", this, m_debugLabel?m_debugLabel:"", GLMDecode( eGL_ENUM, intformat ), intformat ); + printf( "\n" ); + } + } + + m_sliceFlags[ desc->m_sliceIndex ] |= kSliceValid; // for next time, we can subimage.. + } + } + } + break; + + case GL_TEXTURE_3D: + { + // check compressed or not + if (format->m_chunkSize != 1) + { + // compressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage3D.xml + + gGL->glCompressedTexImage3D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat + slice->m_xSize, // width + slice->m_ySize, // height + slice->m_zSize, // depth + 0, // border + slice->m_storageSize, // imageSize + sliceAddress ); // data + } + else + { + // uncompressed path + // http://www.opengl.org/sdk/docs/man/xhtml/glTexImage3D.xml + gGL->glTexImage3D( target, // target + desc->m_req.m_mip, // level + intformat, // internalformat + slice->m_xSize, // width + slice->m_ySize, // height + slice->m_zSize, // depth + 0, // border + glDataFormat, // dataformat + glDataType, // datatype + noDataWrite ? NULL : sliceAddress ); // data (optionally suppressed in case ResetSRGB desires) + } + } + break; + } + + if (gGL->m_bHave_GL_APPLE_client_storage) + { + gGL->glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE ); + } + + if ( expandTemp ) + { + free( expandTemp ); + } + + m_ctx->BindTexToTMU( pPrevTex, 0 ); +} + + +void CGLMTex::Lock( GLMTexLockParams *params, char** addressOut, int* yStrideOut, int *zStrideOut ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMTex::Lock" ); + g_TelemetryGPUStats.m_nTotalTexLocksAndUnlocks++; +#endif + + // locate appropriate slice in layout record + int sliceIndex = CalcSliceIndex( params->m_face, params->m_mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[sliceIndex]; + + // obtain offset + //int sliceBaseOffset = slice->m_storageOffset; + + // cross check region req against slice bounds - figure out if it matches, exceeds, or is less than the whole slice. + char exceed = (params->m_region.xmin < 0) || (params->m_region.xmax > slice->m_xSize) || + (params->m_region.ymin < 0) || (params->m_region.ymax > slice->m_ySize) || + (params->m_region.zmin < 0) || (params->m_region.zmax > slice->m_zSize); + + char partial = (params->m_region.xmin > 0) || (params->m_region.xmax < slice->m_xSize) || + (params->m_region.ymin > 0) || (params->m_region.ymax < slice->m_ySize) || + (params->m_region.zmin > 0) || (params->m_region.zmax < slice->m_zSize); + + bool copyout = false; // set if a readback of the texture slice from GL is needed + + if (exceed) + { + // illegal rect, out of bounds + GLMStop(); + } + + // on return, these things need to be true + + // a - there needs to be storage allocated, which we will return an address within + // b - the region corresponding to the slice being locked, will have valid data there for the whole slice. + // c - the slice is marked as locked + // d - the params of the lock request have been saved in the lock table (in the context) + + // so step 1 is unambiguous. If there's no backing storage, make some. + if (!m_backing) + { + if ( gl_pow2_tempmem.GetBool() ) + { + uint32_t unStoragePow2 = m_layout->m_storageTotalSize; + // Round up to next power of 2 + unStoragePow2--; + unStoragePow2 |= unStoragePow2 >> 1; + unStoragePow2 |= unStoragePow2 >> 2; + unStoragePow2 |= unStoragePow2 >> 4; + unStoragePow2 |= unStoragePow2 >> 8; + unStoragePow2 |= unStoragePow2 >> 16; + unStoragePow2++; + m_backing = (char *)calloc( unStoragePow2, 1 ); + } + else + { + m_backing = (char *)calloc( m_layout->m_storageTotalSize, 1 ); + } + + // clear the kSliceStorageValid bit on all slices + for( int i=0; i<m_layout->m_sliceCount; i++) + { + m_sliceFlags[i] &= ~kSliceStorageValid; + } + } + + // work on this slice now + + // storage is known to exist at this point, but we need to check if its contents are valid for this slice. + // this is tracked per-slice so we don't hoist all the texels back out of GL across all slices if caller only + // wanted to lock some of them. + + // (i.e. if we just alloced it, it's blank) + // if storage is invalid, but the texture itself is valid, hoist the texels back to the storage and mark it valid. + // if storage is invalid, and texture itself is also invalid, go ahead and mark storage as valid and fully dirty... to force teximage. + + // ???????????? we need to go over this more carefully re "slice valid" (it has been teximaged) vs "storage valid" (it has been copied out). + + unsigned char *sliceFlags = &m_sliceFlags[ sliceIndex ]; + + if (params->m_readback) + { + // caller is letting us know that it wants to readback the real texels. + *sliceFlags |= kSliceStorageValid; + *sliceFlags |= kSliceValid; + *sliceFlags &= ~(kSliceFullyDirty); + copyout = true; + } + else + { + // caller is pushing texels. + if (! (*sliceFlags & kSliceStorageValid) ) + { + // storage is invalid. check texture state + if ( *sliceFlags & kSliceValid ) + { + // kSliceValid set: the texture itself has a valid slice, but we don't have it in our backing copy, so copy it out. + copyout = true; + } + else + { + // kSliceValid not set: the texture does not have a valid slice to copy out - it hasn't been teximage'd yet. + // set the "full dirty" bit to make sure we teximage the whole thing on unlock. + *sliceFlags |= kSliceFullyDirty; + + // assert if they did not ask to lock the full slice size on this go-round + if (partial) + { + // choice here - + // 1 - stop cold, we don't know how to subimage yet. + // 2 - grin and bear it, mark whole slice dirty (ah, we already did... so, do nothing). + // choice 2: // GLMStop(); + } + } + + // one way or another, upon reaching here the slice storage is valid for read. + *sliceFlags |= kSliceStorageValid; + } + } + + + // when we arrive here, there is storage, and the content of the storage for this slice is valid + // (or zeroes if it's the first lock) + + // log the lock request in the context. + int newdesc = m_ctx->m_texLocks.AddToTail(); + + GLMTexLockDesc *desc = &m_ctx->m_texLocks[newdesc]; + + desc->m_req = *params; + desc->m_active = true; + desc->m_sliceIndex = sliceIndex; + desc->m_sliceBaseOffset = m_layout->m_slices[sliceIndex].m_storageOffset; + + // to calculate the additional offset we need to look at the rect's min corner + // combined with the per-texel size and Y/Z stride + // also cross check it for 4x multiple if there is compression in play + + int offsetInSlice = 0; + int yStride = 0; + int zStride = 0; + + CalcTexelDataOffsetAndStrides( sliceIndex, params->m_region.xmin, params->m_region.ymin, params->m_region.zmin, &offsetInSlice, &yStride, &zStride ); + + // for compressed case... + // since there is presently no way to texsubimage a DXT when the rect does not cover the whole width, + // we will probably need to inflate the dirty rect in the recorded lock req so that the entire span is + // pushed across at unlock time. + + desc->m_sliceRegionOffset = offsetInSlice + desc->m_sliceBaseOffset; + + if (copyout) + { + // read the whole slice + // (odds are we'll never request anything but a whole slice to be read..) + ReadTexels( desc, true ); + } // this would be a good place to fill with scrub value if in debug... + + *addressOut = m_backing + desc->m_sliceRegionOffset; + *yStrideOut = yStride; + *zStrideOut = zStride; + + m_lockCount++; +} + +void CGLMTex::Unlock( GLMTexLockParams *params ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "CGLMTex::Unlock" ); + g_TelemetryGPUStats.m_nTotalTexLocksAndUnlocks++; +#endif + + // look for an active lock request on this face and mip (doesn't necessarily matter which one, if more than one) + // and mark it inactive. + // --> if you can't find one, fail. first line of defense against mismatched locks/unlocks.. + + int i=0; + bool found = false; + while( !found && (i<m_ctx->m_texLocks.Count()) ) + { + GLMTexLockDesc *desc = &m_ctx->m_texLocks[i]; + + // is lock at index 'i' targeted at the texture/face/mip in question? + if ( (desc->m_req.m_tex == this) && (desc->m_req.m_face == params->m_face) & (desc->m_req.m_mip == params->m_mip) && (desc->m_active) ) + { + // matched and active, so retire it + desc->m_active = false; + + // stop searching + found = true; + } + i++; + } + + if (!found) + { + GLMStop(); // bad news + } + + // found - so drop lock count + m_lockCount--; + + if (m_lockCount <0) + { + GLMStop(); // bad news + } + + if (m_lockCount==0) + { + // there should not be any active locks remaining on this texture. + + // motivation to defer all texel pushing til *all* open locks are closed out - + // if/when we back the texture with a PBO, we will need to unmap that PBO before teximaging from it; + // by waiting for all the locks to clear this gives us an unambiguous signal to act on. + + // scan through all the retired locks for this texture and push the texels for each one. + // after each one is dispatched, remove it from the pile. + + int j=0; + while( j<m_ctx->m_texLocks.Count() ) + { + GLMTexLockDesc *desc = &m_ctx->m_texLocks[j]; + + if ( desc->m_req.m_tex == this ) + { + // if it's active, something is wrong + if (desc->m_active) + { + GLMStop(); + } + + // write the texels + bool fullyDirty = false; + + fullyDirty |= ((m_sliceFlags[ desc->m_sliceIndex ] & kSliceFullyDirty) != 0); + + // this is not optimal and will result in full downloads on any dirty. + // we're papering over the fact that subimage isn't done yet. + // but this is safe if the slice of storage is all valid. + + // at some point we'll need to actually compare the lock box against the slice bounds. + + // fullyDirty |= (m_sliceFlags[ desc->m_sliceIndex ] & kSliceStorageValid); + + WriteTexels( desc, fullyDirty ); + + // logical place to trigger preloading + // only do it for an RT tex, if it is not yet attached to any FBO. + // also, only do it if the slice number is the last slice in the tex. + if ( desc->m_sliceIndex == (m_layout->m_sliceCount-1) ) + { + if ( !(m_layout->m_key.m_texFlags & kGLMTexRenderable) || (m_rtAttachCount==0) ) + { + m_ctx->PreloadTex( this ); + // printf("( slice %d of %d )", desc->m_sliceIndex, m_layout->m_sliceCount ); + } + } + + m_ctx->m_texLocks.FastRemove( j ); // remove from the pile, don't advance index + } + else + { + j++; // move on to next one + } + } + + // clear the locked and full-dirty flags for all slices + for( int slice=0; slice < m_layout->m_sliceCount; slice++) + { + m_sliceFlags[slice] &= ~( kSliceLocked | kSliceFullyDirty ); + } + + // The 3D texture upload code seems to rely on the host copy, probably + // because it reuploads the whole thing each slice; we only use 3D textures + // for the 32x32x32 colorpsace conversion lookups and debugging the problem + // would not save any more memory. + if ( !m_texClientStorage && ( m_texGLTarget == GL_TEXTURE_2D ) ) + { + free(m_backing); + m_backing = NULL; + } + } +} + +#if defined( OSX ) + +void CGLMTex::HandleSRGBMismatch( bool srgb, int &srgbFlipCount ) +{ + bool srgbCapableTex = false; // not yet known + bool renderableTex = false; // not yet known. + + srgbCapableTex = m_layout->m_format->m_glIntFormatSRGB != 0; + renderableTex = ( m_layout->m_key.m_texFlags & kGLMTexRenderable ) != 0; + // we can fix it if it's not a renderable, and an sRGB enabled format variation is available. + + if ( srgbCapableTex && !renderableTex ) + { + char *texname = m_debugLabel; + if (!texname) texname = "-"; + + m_srgbFlipCount++; + +#if GLMDEBUG + //policy: print the ones that have flipped 1 or N times + static bool print_allflips = CommandLine()->FindParm("-glmspewallsrgbflips"); + static bool print_firstflips = CommandLine()->FindParm("-glmspewfirstsrgbflips"); + static bool print_freqflips = CommandLine()->FindParm("-glmspewfreqsrgbflips"); + static bool print_crawls = CommandLine()->FindParm("-glmspewsrgbcrawls"); + static bool print_maxcrawls = CommandLine()->FindParm("-glmspewsrgbmaxcrawls"); + bool print_it = false; + + if (print_allflips) + { + print_it = true; + } + if (print_firstflips) // report on first flip + { + print_it |= m_srgbFlipCount==1; + } + if (print_freqflips) // report on 50th flip + { + print_it |= m_srgbFlipCount==50; + } + + if ( print_it ) + { + char *formatStr; + formatStr = "srgb change (samp=%d): tex '%-30s' %08x %s (srgb=%d, %d times)"; + + if (strlen(texname) >= 30) + { + formatStr = "srgb change (samp=%d): tex '%s' %08x %s (srgb=%d, %d times)"; + } + + printf( "\n" ); + printf( formatStr, index, texname, m_layout->m_layoutSummary, (int)srgb, m_srgbFlipCount ); + +#ifdef POSIX + if (print_crawls) + { + static char *interesting_crawl_substrs[] = { "CShader::OnDrawElements", NULL }; // add more as needed + + CStackCrawlParams cp; + memset( &cp, 0, sizeof(cp) ); + cp.m_frameLimit = 20; + + g_pLauncherMgr->GetStackCrawl(&cp); + + for( int i=0; i< cp.m_frameCount; i++) + { + // for each row of crawl, decide if name is interesting + bool hit = print_maxcrawls; + + for( char **match = interesting_crawl_substrs; (!hit) && (*match != NULL); match++) + { + if (strstr(cp.m_crawlNames[i], *match)) + { + hit = true; + } + } + + if (hit) + { + printf( "\n\t%s", cp.m_crawlNames[i] ); + } + } + printf( "\n"); + } +#endif + } +#endif // GLMDEBUG + +#if GLMDEBUG && 0 + //"toi" = texture of interest + static char s_toi[256] = "colorcorrection"; + if (strstr( texname, s_toi )) + { + // breakpoint on this if you like + GLMPRINTF(( "srgb change %d for %s", m_srgbFlipCount, texname )); + } +#endif + + // re-submit the tex unless we're stifling it + static bool s_nosrgbflips = CommandLine()->FindParm( "-glmnosrgbflips" ); + if ( !s_nosrgbflips ) + { + ResetSRGB( srgb, false ); + } + } + else + { + //GLMPRINTF(("-Z- srgb sampling conflict: NOT fixing tex %08x [%s] (srgb req: %d) because (tex-srgb-capable=%d tex-renderable=%d)", m_textures[index], m_textures[index]->m_tex->m_layout->m_layoutSummary, (int)glsamp->m_srgb, (int)srgbCapableTex, (int)renderableTex )); + // we just leave the sampler state where it is, and that's life + } +} + + +void CGLMTex::ResetSRGB( bool srgb, bool noDataWrite ) +{ + // see if requested SRGB state differs from the known one + bool wasSRGB = (m_layout->m_key.m_texFlags & kGLMTexSRGB); + GLMTexLayout *oldLayout = m_layout; // need to m_ctx->m_texLayoutTable->DelLayoutRef on this one if we flip + + if (srgb != wasSRGB) + { + // we're going to need a new layout (though the storage size should be the same - check it) + GLMTexLayoutKey newKey = m_layout->m_key; + + newKey.m_texFlags &= (~kGLMTexSRGB); // turn off that bit + newKey.m_texFlags |= srgb ? kGLMTexSRGB : 0; // turn on that bit if it should be so + + // get new layout + GLMTexLayout *newLayout = m_ctx->m_texLayoutTable->NewLayoutRef( &newKey ); + + // if SRGB requested, verify that the layout we just got can do it. + // if it can't, delete the new layout ref and bail. + if (srgb && (newLayout->m_format->m_glIntFormatSRGB == 0)) + { + Assert( !"Can't enable SRGB mode on this format" ); + m_ctx->m_texLayoutTable->DelLayoutRef( newLayout ); + return; + } + + // check sizes and fail if no match + if( newLayout->m_storageTotalSize != oldLayout->m_storageTotalSize ) + { + Assert( !"Bug: layout sizes don't match on SRGB change" ); + m_ctx->m_texLayoutTable->DelLayoutRef( newLayout ); + return; + } + + // commit to new layout + m_layout = newLayout; + m_texGLTarget = m_layout->m_key.m_texGLTarget; + + // check same size + Assert( m_layout->m_storageTotalSize == oldLayout->m_storageTotalSize ); + + Assert( newLayout != oldLayout ); + + // release old + m_ctx->m_texLayoutTable->DelLayoutRef( oldLayout ); + oldLayout = NULL; + + // force texel re-DL + + // note this messes with TMU 0 as side effect of WriteTexels + // so we save and restore the TMU 0 binding first + + // since we're likely to be called in dxabstract when it is syncing sampler state, we can't go trampling the bindings. + // a refinement would be to have each texture make a note of which TMU they're bound on, and just use that active TMU for DL instead of 0. + CGLMTex *tmu0save = m_ctx->m_samplers[0].m_pBoundTex; + + for( int face=0; face <m_layout->m_faceCount; face++) + { + for( int mip=0; mip <m_layout->m_mipCount; mip++) + { + // we're not really going to lock, we're just going to rewrite the orig data + GLMTexLockDesc desc; + + desc.m_req.m_tex = this; + desc.m_req.m_face = face; + desc.m_req.m_mip = mip; + + desc.m_sliceIndex = CalcSliceIndex( face, mip ); + + GLMTexLayoutSlice *slice = &m_layout->m_slices[ desc.m_sliceIndex ]; + + desc.m_req.m_region.xmin = desc.m_req.m_region.ymin = desc.m_req.m_region.zmin = 0; + desc.m_req.m_region.xmax = slice->m_xSize; + desc.m_req.m_region.ymax = slice->m_ySize; + desc.m_req.m_region.zmax = slice->m_zSize; + + desc.m_sliceBaseOffset = slice->m_storageOffset; // doesn't really matter... we're just pushing zeroes.. + desc.m_sliceRegionOffset = 0; + + WriteTexels( &desc, true, noDataWrite ); // write whole slice. and avoid pushing real bits if the caller requests (RT's) + } + } + + // put it back + m_ctx->BindTexToTMU( tmu0save, 0 ); + } +} +#endif + +bool CGLMTex::IsRBODirty() const +{ + return m_nLastResolvedBatchCounter != m_ctx->m_nBatchCounter; +} + +void CGLMTex::ForceRBONonDirty() +{ + m_nLastResolvedBatchCounter = m_ctx->m_nBatchCounter; +} + +void CGLMTex::ForceRBODirty() +{ + m_nLastResolvedBatchCounter = m_ctx->m_nBatchCounter - 1; +} diff --git a/togl/linuxwin/dx9asmtogl2.cpp b/togl/linuxwin/dx9asmtogl2.cpp new file mode 100644 index 0000000..22be8fc --- /dev/null +++ b/togl/linuxwin/dx9asmtogl2.cpp @@ -0,0 +1,3838 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------ +// DX9AsmToGL2.cpp +//------------------------------------------------------------------------------ +// Immediately include gl.h, etc. here to avoid compilation warnings. +#include <GL/gl.h> +#include <GL/glext.h> + +#include "togl/rendermechanism.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier1/utlbuffer.h" +#include "dx9asmtogl2.h" + +#include "materialsystem/IShader.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef POSIX +#define strcat_s( a, b, c) V_strcat( a, c, b ) +#endif + +#define DST_REGISTER 0 +#define SRC_REGISTER 1 + +// Flags to PrintUsageAndIndexToString. +#define SEMANTIC_OUTPUT 0x01 +#define SEMANTIC_INPUT 0x02 + +#define UNDECLARED_OUTPUT 0xFFFFFFFF +#define UNDECLARED_INPUT 0xFFFFFFFF + +#ifndef POSIX +#define Debugger() Assert(0) +#endif + +//#define Assert(n) if( !(n) ){ TranslationError(); } + + +static char *g_szVecZeros[] = { NULL, "0.0", "vec2( 0.0, 0.0 )", "vec3( 0.0, 0.0, 0.0 )", "vec4( 0.0, 0.0, 0.0, 0.0 )" }; +static char *g_szVecOnes[] = { NULL, "1.0", "vec2( 1.0, 1.0 )", "vec3( 1.0, 1.0, 1.0 )", "vec4( 1.0, 1.0, 1.0, 1.0 )" }; +static char *g_szDefaultSwizzle = "xyzw"; +static char *g_szDefaultSwizzleStrings[] = { "x", "y", "z", "w" }; +static char *g_szSamplerStrings[] = { "2D", "CUBE", "3D" }; + +static const char *g_pAtomicTempVarName = "atomic_temp_var"; +static const char *g_pTangentAttributeName = "g_tangent"; + +int __cdecl SortInts( const int *a, const int *b ) +{ + if ( *a < *b ) + return -1; + else if ( *a > *b ) + return 1; + else + return 0; +} + +void StripExtraTrailingZeros( char *pStr ) +{ + int len = (int)V_strlen( pStr ); + while ( len >= 2 && pStr[len-1] == '0' && pStr[len-2] != '.' ) + { + pStr[len-1] = 0; + --len; + } +} + +void D3DToGL::PrintToBufWithIndents( CUtlBuffer &buf, const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + + char szTemp[1024]; + V_vsnprintf( szTemp, sizeof( szTemp ), pFormat, marker ); + va_end( marker ); + + PrintIndentation( (char*)buf.Base(), buf.Size() ); + strcat_s( (char*)buf.Base(), buf.Size(), szTemp ); +} + +void PrintToBuf( CUtlBuffer &buf, const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + + char szTemp[1024]; + V_vsnprintf( szTemp, sizeof( szTemp ), pFormat, marker ); + va_end( marker ); + + strcat_s( (char*)buf.Base(), buf.Size(), szTemp ); +} + +void PrintToBuf( char *pOut, int nOutSize, const char *pFormat, ... ) +{ + int nStrlen = V_strlen( pOut ); + pOut += nStrlen; + nOutSize -= nStrlen; + + va_list marker; + va_start( marker, pFormat ); + V_vsnprintf( pOut, nOutSize, pFormat, marker ); + va_end( marker ); +} + +// Return the number of letters following the dot. +// Returns 4 if there is no dot. +// (So "r0.xy" returns 2 and "r0" returns 4). +int GetNumWriteMaskEntries( const char *pParam ) +{ + const char *pDot = strchr( pParam, '.' ); + if ( pDot ) + return V_strlen( pDot + 1 ); + else + return 4; +} + +const char* GetSwizzleDot( const char *pParam ) +{ + const char *pDot = strrchr( pParam, '.' ); + + const char *pSquareClose = strrchr( pParam, ']' ); + + if ( pSquareClose ) + { + // The test against ']' catches cases like, so we point to the last dot vc[int(va_r.x) + 29].x + if ( pDot && ( pSquareClose < pDot ) ) + return pDot; + else + return NULL; + } + + // Make sure the next character is a valid swizzle since we want to treat strings like vec4( gl_Normal, 0.0 ) as a whole param name. + if ( pDot && ( ( *(pDot+1) == 'x' ) || ( *(pDot+1) == 'y' ) || ( *(pDot+1) == 'z' ) || ( *(pDot+1) == 'w' ) || + ( *(pDot+1) == 'r' ) || ( *(pDot+1) == 'g' ) || ( *(pDot+1) == 'b' ) || ( *(pDot+1) == 'z' ) ) ) + { + return pDot; + } + + return NULL; +} + +int GetNumSwizzleComponents( const char *pParam ) +{ + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "gl_FogFragCoord" ) ) + return 1; + + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "gl_FragDepth" ) ) + return 1; + + // Special scalar output which won't accept a swizzle + if ( !V_stricmp( pParam, "a0" ) ) + return 1; + + const char *pDot = GetSwizzleDot( pParam ); + if ( pDot ) + { + pDot++; // Step over the dot + + int nNumSwizzleComponents = 0; + while ( ( *pDot == 'x' ) || ( *pDot == 'y' ) || ( *pDot == 'z' ) || ( *pDot == 'w' ) || + ( *pDot == 'r' ) || ( *pDot == 'g' ) || ( *pDot == 'b' ) || ( *pDot == 'z' ) ) + { + nNumSwizzleComponents++; + pDot++; + } + + return nNumSwizzleComponents; + } + + return 0; +} + +char GetSwizzleComponent( const char *pParam, int n ) +{ + Assert( n < 4 ); + + const char *pDot = GetSwizzleDot( pParam ); + if ( pDot ) + { + ++pDot; + int nComponents = (int)V_strlen( pDot ); + Assert( nComponents > 0 ); + + if ( n < nComponents ) + return pDot[n]; + else + return pDot[nComponents-1]; + } + + return g_szDefaultSwizzle[n]; +} + +// Replace the parameter name and leave the swizzle intact. +// So "somevar.xyz" becomes "othervar.xyz". +void ReplaceParamName( const char *pSrc, const char *pNewParamName, char *pOut, int nOutLen ) +{ + // Start with the new parameter name. + V_strncpy( pOut, pNewParamName, nOutLen ); + + // Now add the swizzle if necessary. + const char *pDot = GetSwizzleDot( pSrc ); + if ( pDot ) + { + V_strncat( pOut, pDot, nOutLen ); + } +} + +void GetParamNameWithoutSwizzle( const char *pParam, char *pOut, int nOutLen ) +{ + char *pParamStart = (char *) pParam; + const char *pDot = GetSwizzleDot( pParam ); // dot followed by valid swizzle characters + bool bAbsWrapper = false; + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pParam, "abs(", 4 ) || !V_strncmp( pParam, "-abs(", 5 ) ) + { + const char *pOpenParen = strchr( pParam, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pParam, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); + pClosingParen; // hush compiler + + pParamStart = (char *) pOpenParen; + pParamStart++; + bAbsWrapper = true; + } + + if ( pDot ) + { + int nToCopy = MIN( nOutLen-1, pDot - pParamStart ); + memcpy( pOut, pParamStart, nToCopy ); + pOut[nToCopy] = 0; + } + else + { + V_strncpy( pOut, pParamStart, bAbsWrapper ? nOutLen - 1 : nOutLen ); + } +} + +bool DoParamNamesMatch( const char *pParam1, const char *pParam2 ) +{ + char szTemp[2][256]; + GetParamNameWithoutSwizzle( pParam1, szTemp[0], sizeof( szTemp[0] ) ); + GetParamNameWithoutSwizzle( pParam2, szTemp[1], sizeof( szTemp[1] ) ); + return ( V_stricmp( szTemp[0], szTemp[1] ) == 0 ); +} + + + +// Extract the n'th component of the swizzle mask. +// If n would exceed the length of the swizzle mask, then it looks up into "xyzw". +void WriteParamWithSingleMaskEntry( const char *pParam, int n, char *pOut, int nOutLen ) +{ + bool bCloseParen = false; + if ( !V_strncmp( pParam, "-abs(", 5 ) ) + { + V_strcpy( pOut, "-abs(" ); + bCloseParen = true; + + pOut += 5; nOutLen -= 5; + } + else if ( !V_strncmp( pParam, "abs(", 4 ) ) + { + V_strcpy( pOut, "abs(" ); + bCloseParen = true; + + pOut += 4; nOutLen -= 4; + } + + GetParamNameWithoutSwizzle( pParam, pOut, nOutLen ); + PrintToBuf( pOut, nOutLen, "." ); + PrintToBuf( pOut, nOutLen, "%c", GetSwizzleComponent( pParam, n ) ); + + if ( bCloseParen ) + { + PrintToBuf( pOut, nOutLen, ")" ); + } +} + + +float uint32ToFloat( uint32 dw ) +{ + return *((float*)&dw); +} + +CUtlString EnsureNumSwizzleComponents( const char *pSrcRegisterName, int nComponents ) +{ + int nExisting = GetNumSwizzleComponents( pSrcRegisterName ); + if ( nExisting == nComponents ) + return pSrcRegisterName; + + bool bAbsWrapper = false; // Parameter wrapped in an abs() + bool bAbsNegative = false; // -abs() + char szSrcRegister[128]; + V_strncpy( szSrcRegister, pSrcRegisterName, sizeof(szSrcRegister) ); + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pSrcRegisterName, "abs(", 4 ) || !V_strncmp( pSrcRegisterName, "-abs(", 5 ) ) + { + bAbsWrapper = true; + bAbsNegative = pSrcRegisterName[0] == '-'; + + const char *pOpenParen = strchr( pSrcRegisterName, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pSrcRegisterName, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); // If we start with abs( and don't get both parens, something is very wrong + + // Copy out just the register name with no abs() + int nRegNameLength = pClosingParen - pOpenParen - 1; + V_strncpy( szSrcRegister, pOpenParen+1, nRegNameLength + 1 ); // Kind of a weird function...copy more than you need and slam the last char to NULL-terminate + } + + char szReg[256]; + GetParamNameWithoutSwizzle( szSrcRegister, szReg, sizeof( szReg ) ); + if ( nComponents == 0 ) + return szReg; + + PrintToBuf( szReg, sizeof( szReg ), "." ); + if ( nExisting > nComponents ) + { + // DX ASM will sometimes have statements like "NRM r0.xyz, r1.yzww", where it just doesn't use the last part of r1. So we won't either. + for ( int i=0; i < nComponents; i++ ) + { + PrintToBuf( szReg, sizeof( szReg ), "%c", GetSwizzleComponent( szSrcRegister, i ) ); + } + } + else + { + if ( nExisting == 0 ) + { + // We've got something like r0 and need N more components, so add as much of "xyzw" is needed. + for ( int i=0; i < nComponents; i++ ) + PrintToBuf( szReg, sizeof( szReg ), "%c", g_szDefaultSwizzle[i] ); + } + else + { + // We've got something like r0.x and need N more components, so replicate the X so it looks like r0.xxx + V_strncpy( szReg, szSrcRegister, sizeof( szReg ) ); + char cLast = szSrcRegister[ V_strlen( szSrcRegister ) - 1 ]; + for ( int i=nExisting; i < nComponents; i++ ) + { + PrintToBuf( szReg, sizeof( szReg ), "%c", cLast ); + } + } + } + + if ( bAbsWrapper ) + { + char szTemp[128]; + V_strncpy( szTemp, szReg, sizeof(szTemp) ); + V_snprintf( szReg, sizeof( szReg ), "%sabs(%s)", bAbsNegative ? "-" : "", szTemp ) ; + } + + return szReg; +} + +static void TranslationError() +{ + GLMDebugPrintf( "D3DToGL: GLSL translation error!\n" ); + DebuggerBreakIfDebugging(); + + Error( "D3DToGL: GLSL translation error!\n" ); +} + +D3DToGL::D3DToGL() +{ +} + +uint32 D3DToGL::GetNextToken( void ) +{ + uint32 dwToken = *m_pdwNextToken; + m_pdwNextToken++; + return dwToken; +} + +void D3DToGL::SkipTokens( uint32 numToSkip ) +{ + m_pdwNextToken += numToSkip; +} + +uint32 D3DToGL::Opcode( uint32 dwToken ) +{ + return ( dwToken & D3DSI_OPCODE_MASK ); +} + +uint32 D3DToGL::OpcodeSpecificData (uint32 dwToken) +{ + return ( ( dwToken & D3DSP_OPCODESPECIFICCONTROL_MASK ) >> D3DSP_OPCODESPECIFICCONTROL_SHIFT ); +} + +uint32 D3DToGL::TextureType ( uint32 dwToken ) +{ + return ( dwToken & D3DSP_TEXTURETYPE_MASK ); // Note this one doesn't shift due to weird D3DSAMPLER_TEXTURE_TYPE enum +} + + + +// Print GLSL intrinsic corresponding to particular instruction +bool D3DToGL::OpenIntrinsic( uint32 inst, char* buff, int nBufLen, uint32 destDimension, uint32 nArgumentDimension ) +{ + // Some GLSL intrinsics need type conversion, which we do in this routine + // As a result, the caller must sometimes close both parentheses, not just one + bool bDoubleClose = false; + + if ( nArgumentDimension == 0 ) + { + nArgumentDimension = 4; + } + + switch ( inst ) + { + case D3DSIO_RSQ: + V_snprintf( buff, nBufLen, "inversesqrt( " ); + break; + case D3DSIO_DP3: + case D3DSIO_DP4: + if ( destDimension == 1 ) + { + V_snprintf( buff, nBufLen, "dot( " ); + } + else + { + if ( !destDimension ) + destDimension = 4; + V_snprintf( buff, nBufLen, "vec%d( dot( ", destDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_MIN: + V_snprintf( buff, nBufLen, "min( " ); + break; + case D3DSIO_MAX: + V_snprintf( buff, nBufLen, "max( " ); + break; + case D3DSIO_SLT: + if ( nArgumentDimension == 1 ) + { + V_snprintf( buff, nBufLen, "float( " ); // lessThan doesn't have a scalar version + } + else + { + Assert( nArgumentDimension > 1 ); + V_snprintf( buff, nBufLen, "vec%d( lessThan( ", nArgumentDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_SGE: + if ( nArgumentDimension == 1 ) + { + V_snprintf( buff, nBufLen, "float( " ); // greaterThanEqual doesn't have a scalar version + } + else + { + Assert( nArgumentDimension > 1 ); + V_snprintf( buff, nBufLen, "vec%d( greaterThanEqual( ", nArgumentDimension ); + bDoubleClose = true; + } + break; + case D3DSIO_EXP: + V_snprintf( buff, nBufLen, "exp( " ); // exp2 ? + break; + case D3DSIO_LOG: + V_snprintf( buff, nBufLen, "log( " ); // log2 ? + break; + case D3DSIO_LIT: + TranslationError(); + V_snprintf( buff, nBufLen, "lit( " ); // gonna have to write this one + break; + case D3DSIO_DST: + V_snprintf( buff, nBufLen, "dst( " ); // gonna have to write this one + break; + case D3DSIO_LRP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "mix( " ); + break; + case D3DSIO_FRC: + V_snprintf( buff, nBufLen, "fract( " ); + break; + case D3DSIO_M4x4: + TranslationError(); + V_snprintf( buff, nBufLen, "m4x4" ); + break; + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALL: + case D3DSIO_CALLNZ: + case D3DSIO_LOOP: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_LABEL: + case D3DSIO_DCL: + TranslationError(); + break; + case D3DSIO_POW: + V_snprintf( buff, nBufLen, "pow( " ); + break; + case D3DSIO_CRS: + V_snprintf( buff, nBufLen, "cross( " ); + break; + case D3DSIO_SGN: + TranslationError(); + V_snprintf( buff, nBufLen, "sign( " ); + break; + case D3DSIO_ABS: + V_snprintf( buff, nBufLen, "abs( " ); + break; + case D3DSIO_NRM: + TranslationError(); + V_snprintf( buff, nBufLen, "normalize( " ); + break; + case D3DSIO_SINCOS: + TranslationError(); + V_snprintf( buff, nBufLen, "sincos( " ); // gonna have to write this one + break; + case D3DSIO_REP: + case D3DSIO_ENDREP: + case D3DSIO_IF: + case D3DSIO_IFC: + case D3DSIO_ELSE: + case D3DSIO_ENDIF: + case D3DSIO_BREAK: + case D3DSIO_BREAKC: // TODO: these are the reason we even need GLSL...gotta make these work + TranslationError(); + break; + case D3DSIO_DEFB: + case D3DSIO_DEFI: + TranslationError(); + break; + case D3DSIO_TEXCOORD: + V_snprintf( buff, nBufLen, "texcoord" ); + break; + case D3DSIO_TEXKILL: + V_snprintf( buff, nBufLen, "kill( " ); // wrap the discard instruction? + break; + case D3DSIO_TEX: + TranslationError(); + V_snprintf( buff, nBufLen, "TEX" ); // We shouldn't get here + break; + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXM3x2PAD: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_TEXM3x3VSPEC: + TranslationError(); + break; + case D3DSIO_EXPP: + V_snprintf( buff, nBufLen, "exp( " ); + break; + case D3DSIO_LOGP: + V_snprintf( buff, nBufLen, "log( " ); + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_DEF: + TranslationError(); + V_snprintf( buff, nBufLen, "DEF" ); + break; + case D3DSIO_TEXREG2RGB: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXDP3: + case D3DSIO_TEXM3x3: + TranslationError(); + break; + case D3DSIO_TEXDEPTH: + V_snprintf( buff, nBufLen, "texdepth" ); + break; + case D3DSIO_CMP: + TranslationError(); + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "CMP" ); + break; + case D3DSIO_BEM: + TranslationError(); + break; + case D3DSIO_DP2ADD: + TranslationError(); + break; + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + case D3DSIO_TEXLDD: + V_snprintf( buff, nBufLen, "texldd" ); + break; + case D3DSIO_SETP: + TranslationError(); + break; + case D3DSIO_TEXLDL: + V_snprintf( buff, nBufLen, "texldl" ); + break; + case D3DSIO_BREAKP: + case D3DSIO_PHASE: + TranslationError(); + break; + } + + return bDoubleClose; +} + + +const char* D3DToGL::GetGLSLOperatorString( uint32 inst ) +{ + if ( inst == D3DSIO_ADD ) + return "+"; + else if ( inst == D3DSIO_SUB ) + return "-"; + else if ( inst == D3DSIO_MUL ) + return "*"; + + Error( "GetGLSLOperatorString: unknown operator" ); + return "zzzz"; +} + + +// Print ASM opcode +void D3DToGL::PrintOpcode( uint32 inst, char* buff, int nBufLen ) +{ + switch ( inst ) + { + case D3DSIO_NOP: + V_snprintf( buff, nBufLen, "NOP" ); + TranslationError(); + break; + case D3DSIO_MOV: + V_snprintf( buff, nBufLen, "MOV" ); + break; + case D3DSIO_ADD: + V_snprintf( buff, nBufLen, "ADD" ); + break; + case D3DSIO_SUB: + V_snprintf( buff, nBufLen, "SUB" ); + break; + case D3DSIO_MAD: + V_snprintf( buff, nBufLen, "MAD" ); + break; + case D3DSIO_MUL: + V_snprintf( buff, nBufLen, "MUL" ); + break; + case D3DSIO_RCP: + V_snprintf( buff, nBufLen, "RCP" ); + break; + case D3DSIO_RSQ: + V_snprintf( buff, nBufLen, "RSQ" ); + break; + case D3DSIO_DP3: + V_snprintf( buff, nBufLen, "DP3" ); + break; + case D3DSIO_DP4: + V_snprintf( buff, nBufLen, "DP4" ); + break; + case D3DSIO_MIN: + V_snprintf( buff, nBufLen, "MIN" ); + break; + case D3DSIO_MAX: + V_snprintf( buff, nBufLen, "MAX" ); + break; + case D3DSIO_SLT: + V_snprintf( buff, nBufLen, "SLT" ); + break; + case D3DSIO_SGE: + V_snprintf( buff, nBufLen, "SGE" ); + break; + case D3DSIO_EXP: + V_snprintf( buff, nBufLen, "EX2" ); + break; + case D3DSIO_LOG: + V_snprintf( buff, nBufLen, "LG2" ); + break; + case D3DSIO_LIT: + V_snprintf( buff, nBufLen, "LIT" ); + break; + case D3DSIO_DST: + V_snprintf( buff, nBufLen, "DST" ); + break; + case D3DSIO_LRP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "LRP" ); + break; + case D3DSIO_FRC: + V_snprintf( buff, nBufLen, "FRC" ); + break; + case D3DSIO_M4x4: + V_snprintf( buff, nBufLen, "m4x4" ); + break; + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALL: + case D3DSIO_CALLNZ: + case D3DSIO_LOOP: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_LABEL: + TranslationError(); + break; + case D3DSIO_DCL: + V_snprintf( buff, nBufLen, "DCL" ); + break; + case D3DSIO_POW: + V_snprintf( buff, nBufLen, "POW" ); + break; + case D3DSIO_CRS: + V_snprintf( buff, nBufLen, "XPD" ); + break; + case D3DSIO_SGN: + TranslationError(); + V_snprintf( buff, nBufLen, "SGN" ); + break; + case D3DSIO_ABS: + V_snprintf( buff, nBufLen, "ABS" ); + break; + case D3DSIO_NRM: + TranslationError(); + V_snprintf( buff, nBufLen, "NRM" ); + break; + case D3DSIO_SINCOS: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "SCS" ); + break; + case D3DSIO_REP: + case D3DSIO_ENDREP: + case D3DSIO_IF: + case D3DSIO_IFC: + case D3DSIO_ELSE: + case D3DSIO_ENDIF: + case D3DSIO_BREAK: + case D3DSIO_BREAKC: + TranslationError(); + break; + case D3DSIO_MOVA: + Assert( m_bVertexShader ); + V_snprintf( buff, nBufLen, "MOV" ); // We're always moving into a temp instead, so this is MOV instead of ARL + break; + case D3DSIO_DEFB: + case D3DSIO_DEFI: + TranslationError(); + break; + case D3DSIO_TEXCOORD: + V_snprintf( buff, nBufLen, "texcoord" ); + break; + case D3DSIO_TEXKILL: + V_snprintf( buff, nBufLen, "KIL" ); + break; + case D3DSIO_TEX: + V_snprintf( buff, nBufLen, "TEX" ); + break; + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXM3x2PAD: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_TEXM3x3VSPEC: + TranslationError(); + break; + case D3DSIO_EXPP: + V_snprintf( buff, nBufLen, "EXP" ); + break; + case D3DSIO_LOGP: + V_snprintf( buff, nBufLen, "LOG" ); + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_DEF: + V_snprintf( buff, nBufLen, "DEF" ); + break; + case D3DSIO_TEXREG2RGB: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXDP3: + case D3DSIO_TEXM3x3: + TranslationError(); + break; + case D3DSIO_TEXDEPTH: + V_snprintf( buff, nBufLen, "texdepth" ); + break; + case D3DSIO_CMP: + Assert( !m_bVertexShader ); + V_snprintf( buff, nBufLen, "CMP" ); + break; + case D3DSIO_BEM: + TranslationError(); + break; + case D3DSIO_DP2ADD: + TranslationError(); + break; + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + case D3DSIO_TEXLDD: + V_snprintf( buff, nBufLen, "texldd" ); + break; + case D3DSIO_SETP: + TranslationError(); + break; + case D3DSIO_TEXLDL: + V_snprintf( buff, nBufLen, "texldl" ); + break; + case D3DSIO_BREAKP: + case D3DSIO_PHASE: + TranslationError(); + break; + } +} + +CUtlString D3DToGL::GetUsageAndIndexString( uint32 dwToken, int fSemanticFlags ) +{ + char szTemp[1024]; + PrintUsageAndIndexToString( dwToken, szTemp, sizeof( szTemp ), fSemanticFlags ); + return szTemp; +} + +//------------------------------------------------------------------------------ +// Helper function which prints ASCII representation of usage-usageindex pair to string +// +// Strictly used by vertex shaders +// not used any more now that we have attribmap metadata +//------------------------------------------------------------------------------ +void D3DToGL::PrintUsageAndIndexToString( uint32 dwToken, char* strUsageUsageIndexName, int nBufLen, int fSemanticFlags ) +{ + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + switch ( dwUsage ) + { + case D3DDECLUSAGE_POSITION: + if ( m_bVertexShader ) + { + if ( fSemanticFlags & SEMANTIC_OUTPUT ) + V_snprintf( strUsageUsageIndexName, nBufLen, "vTempPos" ); // effectively gl_Position + else + V_snprintf( strUsageUsageIndexName, nBufLen, "gl_Vertex" ); + } + else + { + // .xy = position in viewport coordinates + // .z = depth + V_snprintf( strUsageUsageIndexName, nBufLen, "gl_FragCoord" ); + } + + break; + case D3DDECLUSAGE_BLENDWEIGHT: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[1]" ); // "vertex.attrib[12]" ); // or [1] + break; + case D3DDECLUSAGE_BLENDINDICES: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[13]" ); // "vertex.attrib[13]" ); // or [ 7 ] + break; + case D3DDECLUSAGE_NORMAL: + V_snprintf( strUsageUsageIndexName, nBufLen, "vec4( gl_Normal, 0.0 )" ); + break; + case D3DDECLUSAGE_PSIZE: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_psize" ); // no analog + break; + case D3DDECLUSAGE_TEXCOORD: + V_snprintf( strUsageUsageIndexName, nBufLen, "oT%d", dwUsageIndex ); + break; + case D3DDECLUSAGE_TANGENT: + + NoteTangentInputUsed(); + V_strncpy( strUsageUsageIndexName, g_pTangentAttributeName, nBufLen ); + + break; + case D3DDECLUSAGE_BINORMAL: + V_snprintf( strUsageUsageIndexName, nBufLen, "vertex.attrib[14]" ); // aka texc[6] + break; +// case D3DDECLUSAGE_TESSFACTOR: +// TranslationError(); +// V_snprintf( strUsageUsageIndexName, nBufLen, "_position" ); // no analog +// break; +// case D3DDECLUSAGE_POSITIONT: +// TranslationError(); +// V_snprintf( strUsageUsageIndexName, nBufLen, "_positiont" ); // no analog +// break; + case D3DDECLUSAGE_COLOR: + + Assert( dwUsageIndex <= 1 ); +// if ( fSemanticFlags & SEMANTIC_OUTPUT ) +// V_snprintf( strUsageUsageIndexName, nBufLen, dwUsageIndex != 0 ? "gl_BackColor" : "gl_FrontColor" ); +// else + V_snprintf( strUsageUsageIndexName, nBufLen, dwUsageIndex != 0 ? "gl_SecondaryColor" : "gl_Color" ); + + break; + case D3DDECLUSAGE_FOG: + TranslationError(); + break; + case D3DDECLUSAGE_DEPTH: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_depth" ); // no analog + break; + case D3DDECLUSAGE_SAMPLE: + TranslationError(); + V_snprintf( strUsageUsageIndexName, nBufLen, "_sample" ); // no analog + break; + default: + Debugger(); + break; + } +} + +uint32 D3DToGL::GetRegType( uint32 dwRegToken ) +{ + return ( ( dwRegToken & D3DSP_REGTYPE_MASK2 ) >> D3DSP_REGTYPE_SHIFT2 ) | ( ( dwRegToken & D3DSP_REGTYPE_MASK ) >> D3DSP_REGTYPE_SHIFT ); +} + +void D3DToGL::PrintIndentation( char *pBuf, int nBufLen ) +{ + for( int i=0; i<m_NumIndentTabs; i++ ) + { + strcat_s( pBuf, nBufLen, "\t" ); + } +} + +CUtlString D3DToGL::GetParameterString( uint32 dwToken, uint32 dwSourceOrDest, bool bForceScalarSource, int *pARLDestReg ) +{ + char szTemp[1024]; + PrintParameterToString( dwToken, dwSourceOrDest, szTemp, sizeof( szTemp ), bForceScalarSource, pARLDestReg ); + return szTemp; +} + + +// If the register happens to end with ".xyzw", then this strips off the mask. +void SimplifyFourParamRegister( char *pRegister ) +{ + int nLen = V_strlen( pRegister ); + if ( nLen > 5 && V_strcmp( &pRegister[nLen-5], ".xyzw" ) == 0 ) + pRegister[nLen-5] = 0; +} + + +// This returns 0 for x, 1 for y, 2 for z, and 3 for w. +int GetSwizzleComponentVectorIndex( char chMask ) +{ + if ( chMask == 'x' ) + return 0; + else if ( chMask == 'y' ) + return 1; + else if ( chMask == 'z' ) + return 2; + else if ( chMask == 'w' ) + return 3; + + Error( "GetSwizzleComponentVectorIndex( '%c' ) - invalid parameter.\n", chMask ); + return 0; +} + + +// GLSL needs the # of src masks to match the dest write mask. +// +// So this: +// r0.xy = r1 + r2; +// becomes: +// r0.xy = r1.xy + r2.xy; +// +// +// Also, and this is the trickier one: GLSL reads the source registers from their first component on +// whereas D3D reads them as referenced in the dest register mask! +// +// So this code in D3D: +// r0.yz = c0.x + c1.wxyz +// Really means: +// r0.y = c0.x + c1.x +// r0.z = c0.x + c1.y +// So we translate it to this in GLSL: +// r0.yz = c0.xx + c1.wx +// r0.yz = c0.xx + c1.xy +// +CUtlString D3DToGL::FixGLSLSwizzle( const char *pDestRegisterName, const char *pSrcRegisterName ) +{ + bool bAbsWrapper = false; // Parameter wrapped in an abs() + bool bAbsNegative = false; // -abs() + char szSrcRegister[128]; + V_strncpy( szSrcRegister, pSrcRegisterName, sizeof(szSrcRegister) ); + + // Check for abs() or -abs() wrapper and strip it off during the fixup + if ( !V_strncmp( pSrcRegisterName, "abs(", 4 ) || !V_strncmp( pSrcRegisterName, "-abs(", 5 ) ) + { + bAbsWrapper = true; + bAbsNegative = pSrcRegisterName[0] == '-'; + + const char *pOpenParen = strchr( pSrcRegisterName, '(' ); // FIRST opening paren + const char *pClosingParen = strrchr( pSrcRegisterName, ')' ); // LAST closing paren + + Assert ( pOpenParen && pClosingParen ); // If we start with abs( and don't get both parens, something is very wrong + + // Copy out just the register name with no abs() + int nRegNameLength = pClosingParen - pOpenParen - 1; + V_strncpy( szSrcRegister, pOpenParen+1, nRegNameLength + 1 ); // Kind of a weird function...copy more than you need and slam the last char to NULL-terminate + + } + + int nSwizzlesInDest = GetNumSwizzleComponents( pDestRegisterName ); + if ( nSwizzlesInDest == 0 ) + nSwizzlesInDest = 4; + + char szFixedSrcRegister[128]; + GetParamNameWithoutSwizzle( szSrcRegister, szFixedSrcRegister, sizeof( szFixedSrcRegister ) ); + V_strncat( szFixedSrcRegister, ".", sizeof( szFixedSrcRegister ) ); + for ( int i=0; i < nSwizzlesInDest; i++ ) + { + char chDestWriteMask = GetSwizzleComponent( pDestRegisterName, i ); + int nVectorIndex = GetSwizzleComponentVectorIndex( chDestWriteMask ); + + char ch[2]; + ch[0] = GetSwizzleComponent( szSrcRegister, nVectorIndex ); + ch[1] = 0; + V_strncat( szFixedSrcRegister, ch, sizeof( szFixedSrcRegister ) ); + } + + SimplifyFourParamRegister( szFixedSrcRegister ); + + if ( bAbsWrapper ) + { + char szTempSrcRegister[128]; + V_strncpy( szTempSrcRegister, szFixedSrcRegister, sizeof(szTempSrcRegister) ); + V_snprintf( szFixedSrcRegister, sizeof( szFixedSrcRegister ), "%sabs(%s)", bAbsNegative ? "-" : "", szTempSrcRegister ) ; + } + + return szFixedSrcRegister; +} + +// Weird encoding...bits are split apart in the dwToken +inline uint32 GetRegTypeFromToken( uint32 dwToken ) +{ + return ( ( dwToken & D3DSP_REGTYPE_MASK2 ) >> D3DSP_REGTYPE_SHIFT2 ) | ( ( dwToken & D3DSP_REGTYPE_MASK ) >> D3DSP_REGTYPE_SHIFT ); +} + +void D3DToGL::FlagIndirectRegister( uint32 dwToken, int *pARLDestReg ) +{ + if ( !pARLDestReg ) + return; + + switch ( dwToken & D3DVS_SWIZZLE_MASK & D3DVS_X_W ) + { + case D3DVS_X_X: + *pARLDestReg = ARL_DEST_X; + break; + case D3DVS_X_Y: + *pARLDestReg = ARL_DEST_Y; + break; + case D3DVS_X_Z: + *pARLDestReg = ARL_DEST_Z; + break; + case D3DVS_X_W: + *pARLDestReg = ARL_DEST_W; + break; + } +} + + +//------------------------------------------------------------------------------ +// PrintParameterToString() +// +// Helper function which prints ASCII representation of passed Parameter dwToken +// to string. Token defines parameter details. The dwSourceOrDest parameter says +// whether or not this is a source or destination register +//------------------------------------------------------------------------------ +void D3DToGL::PrintParameterToString ( uint32 dwToken, uint32 dwSourceOrDest, char *pRegisterName, int nBufLen, bool bForceScalarSource, int *pARLDestReg ) +{ + char buff[32]; + bool bAllowWriteMask = true; + bool bAllowSwizzle = true; + + uint32 dwRegNum = dwToken & D3DSP_REGNUM_MASK; + + uint32 dwRegType, dwSwizzle; + uint32 dwSrcModifier = D3DSPSM_NONE; + + // Clear string to zero length + pRegisterName[ 0 ] = 0; + + dwRegType = GetRegTypeFromToken( dwToken ); + + // If this is a dest register + if ( dwSourceOrDest == DST_REGISTER ) + { + // Instruction modifiers + if ( dwToken & D3DSPDM_PARTIALPRECISION ) + { +// strcat_s( pRegisterName, nBufLen, "_pp" ); + } + + if ( dwToken & D3DSPDM_MSAMPCENTROID) + { +// strcat_s( pRegisterName, nBufLen, "_centroid" ); + } + } + + // If this is a source register + if ( dwSourceOrDest == SRC_REGISTER ) + { + dwSrcModifier = dwToken & D3DSP_SRCMOD_MASK; + + // If there are any source modifiers, check to see if they're at + // least partially "prefix" and prepend appropriately + if ( dwSrcModifier != D3DSPSM_NONE ) + { + switch ( dwSrcModifier ) + { + // These four start with just minus... (some may result in "postfix" notation as well later on) + case D3DSPSM_NEG: // negate + strcat_s( pRegisterName, nBufLen, "-" ); + break; + case D3DSPSM_BIASNEG: // bias and negate + case D3DSPSM_SIGNNEG: // sign and negate + case D3DSPSM_X2NEG: // *2 and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "-" ); + break; + case D3DSPSM_COMP: // complement + TranslationError(); + strcat_s( pRegisterName, nBufLen, "1-" ); + break; + case D3DSPSM_ABS: // abs() + strcat_s( pRegisterName, nBufLen, "abs(" ); + + break; + case D3DSPSM_ABSNEG: // -abs() + strcat_s( pRegisterName, nBufLen, "-abs(" ); + + break; + case D3DSPSM_NOT: // for predicate register: "!p0" + TranslationError(); + strcat_s( pRegisterName, nBufLen, "!" ); + break; + } + } + } + + // Register name (from type and number) + switch ( dwRegType ) + { + case D3DSPR_TEMP: + V_snprintf( buff, sizeof( buff ), "r%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_dwTempUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this temp + break; + case D3DSPR_INPUT: + if ( !m_bVertexShader && ( dwSourceOrDest == SRC_REGISTER ) ) + { + if ( m_dwMajorVersion == 3 ) + { + V_snprintf( buff, sizeof( buff ), "oTempT%d", dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), dwRegNum == 0 ? "gl_Color" : "gl_SecondaryColor" ); + } + strcat_s( pRegisterName, nBufLen, buff ); + } + else + { + V_snprintf( buff, sizeof( buff ), "v%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + break; + case D3DSPR_CONST: + if ( m_bConstantRegisterDefined[dwRegNum] ) + { + char szConstantRegName[3]; + if ( m_bVertexShader ) + { + V_snprintf( szConstantRegName, 3, "vd" ); + } + else + { + V_snprintf( szConstantRegName, 3, "pd" ); + } + + // Put defined constants into their own namespace "d" + V_snprintf( buff, sizeof( buff ), "%s%d", szConstantRegName, dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + else if ( dwToken & D3DSHADER_ADDRESSMODE_MASK ) // Indirect addressing (e.g. skinning in a vertex shader) + { + char szConstantRegName[16]; + if ( m_bVertexShader ) + { + V_snprintf( szConstantRegName, 3, "vc" ); + } + else // No indirect addressing in PS, this shouldn't happen + { + TranslationError(); + V_snprintf( szConstantRegName, 3, "pc" ); + } + + if ( ( m_bGenerateBoneUniformBuffer ) && ( dwRegNum >= DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + if( dwRegNum < DXABSTRACT_VS_LAST_BONE_SLOT ) + { + dwRegNum -= DXABSTRACT_VS_FIRST_BONE_SLOT; + V_strcpy( szConstantRegName, "vcbones" ); + + m_nHighestBoneRegister = ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + } + else + { + dwRegNum -= ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + m_nHighestRegister = m_bGenerateBoneUniformBuffer ? ( ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - ( ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT ) ): ( DXABSTRACT_VS_PARAM_SLOTS - 1 ); + } + } + else + { + m_nHighestRegister = m_bGenerateBoneUniformBuffer ? ( ( DXABSTRACT_VS_PARAM_SLOTS - 1 ) - ( ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT ) ): ( DXABSTRACT_VS_PARAM_SLOTS - 1 ); + } + + // Index into single pc/vc[] register array with relative addressing + int nDstReg = -1; + FlagIndirectRegister( GetNextToken(), &nDstReg ); + if ( pARLDestReg ) + *pARLDestReg = nDstReg; + + Assert( nDstReg != ARL_DEST_NONE ); + int nSrcSwizzle = 'x'; + if ( nDstReg == ARL_DEST_Y ) + nSrcSwizzle = 'y'; + else if ( nDstReg == ARL_DEST_Z ) + nSrcSwizzle = 'z'; + else if ( nDstReg == ARL_DEST_W ) + nSrcSwizzle = 'w'; + V_snprintf( buff, sizeof( buff ), "%s[int(va_r.%c) + %d]", szConstantRegName, nSrcSwizzle, dwRegNum ); + + strcat_s( pRegisterName, nBufLen, buff ); + + // Must allow swizzling, otherwise this example doesn't compile right: mad r3.xyz, c27[a0.w].w, r3, r7 + //bAllowSwizzle = false; + } + else // Direct addressing of constant array + { + char szConstantRegName[16]; + V_snprintf( szConstantRegName, 3, m_bVertexShader ? "vc" : "pc" ); + + if ( ( m_bGenerateBoneUniformBuffer ) && ( dwRegNum >= DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + if( dwRegNum < DXABSTRACT_VS_LAST_BONE_SLOT ) + { + dwRegNum -= DXABSTRACT_VS_FIRST_BONE_SLOT; + V_strcpy( szConstantRegName, "vcbones" ); + + m_nHighestBoneRegister = MAX( m_nHighestBoneRegister, (int)dwRegNum ); + } + else + { + // handles case where constants after the bones are used (c217 onwards), these are to be concatenated with those before the bones (c0-c57) + // keep track of regnum for concatenated array + dwRegNum -= ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; + m_nHighestRegister = MAX( m_nHighestRegister, dwRegNum ); + } + } + else + { + //// NOGO if (dwRegNum != 255) // have seen cases where dwRegNum is 0xFF... need to figure out where those opcodes are coming from + { + m_nHighestRegister = MAX( m_nHighestRegister, dwRegNum ); + } + + Assert( m_nHighestRegister < DXABSTRACT_VS_PARAM_SLOTS ); + } + + // Index into single pc/vc[] register array with absolute addressing, same for GLSL and ASM + V_snprintf( buff, sizeof( buff ), "%s[%d]", szConstantRegName, dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + } + break; + case D3DSPR_ADDR: // aliases to D3DSPR_TEXTURE + if ( m_bVertexShader ) + { + Assert( dwRegNum == 0 ); + + V_snprintf( buff, sizeof( buff ), "va_r" ); + } + else // D3DSPR_TEXTURE in the pixel shader + { + // If dest reg, this is an iterator/varying declaration + if ( dwSourceOrDest == DST_REGISTER ) + { + // Is this iterator centroid? + if ( m_nCentroidMask & ( 0x00000001 << dwRegNum ) ) + { + V_snprintf( buff, sizeof( buff ), "centroid varying vec4 oT%d", dwRegNum ); // centroid varying + } + else + { + V_snprintf( buff, sizeof( buff ), "varying vec4 oT%d", dwRegNum ); + } + + bAllowWriteMask = false; + } + else // source register + { + V_snprintf( buff, sizeof( buff ), "oT%d", dwRegNum ); + } + } + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_RASTOUT: // vertex shader oPos + Assert( m_bVertexShader ); + Assert( m_dwMajorVersion == 2 ); + switch( dwRegNum ) + { + case D3DSRO_POSITION: + strcat_s( pRegisterName, nBufLen, "vTempPos" ); // In GLSL, this ends up in gl_Position later on + m_bDeclareVSOPos = true; + break; + + case D3DSRO_FOG: + strcat_s( pRegisterName, nBufLen, "gl_FogFragCoord" ); + m_bDeclareVSOFog = true; + break; + + default: + printf( "\nD3DSPR_RASTOUT: dwRegNum is %08x and token is %08x", dwRegNum, dwToken ); + TranslationError(); + break; + } + break; + case D3DSPR_ATTROUT: + Assert( m_bVertexShader ); + Assert( m_dwMajorVersion == 2 ); + + if ( dwRegNum == 0 ) + { + V_snprintf( buff, sizeof( buff ), "gl_FrontColor" ); + } + else if ( dwRegNum == 1 ) + { + V_snprintf( buff, sizeof( buff ), "gl_FrontSecondaryColor" ); + } + else + { + Error( "Invalid D3DSPR_ATTROUT index" ); + } + + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_TEXCRDOUT: // aliases to D3DSPR_OUTPUT + if ( m_bVertexShader ) + { + if ( m_nVSPositionOutput == (int32) dwRegNum ) + { + V_snprintf( buff, sizeof( buff ), "vTempPos" ); // This output varying is the position + } + else if ( m_dwMajorVersion == 3 ) + { + V_snprintf( buff, sizeof( buff ), "oTempT%d", dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), "oT%d", dwRegNum ); + } + + m_dwTexCoordOutMask |= ( 0x00000001 << dwRegNum ); + } + else + { + V_snprintf( buff, sizeof( buff ), "oC%d", dwRegNum ); + } + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONSTINT: + V_snprintf( buff, sizeof( buff ), "i%d", dwRegNum ); // Loops use these + strcat_s( pRegisterName, nBufLen, buff ); + m_dwConstIntUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this integer constant + break; + case D3DSPR_COLOROUT: + V_snprintf( buff, sizeof( buff ), "gl_FragData[%d]", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_bOutputColorRegister[dwRegNum] = true; + break; + case D3DSPR_DEPTHOUT: + V_snprintf( buff, sizeof( buff ), "gl_FragDepth" ); + strcat_s( pRegisterName, nBufLen, buff ); + m_bOutputDepthRegister = true; + break; + case D3DSPR_SAMPLER: + V_snprintf( buff, sizeof( buff ), "sampler%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST2: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+2048); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST3: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+4096); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONST4: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "c%d", dwRegNum+6144); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_CONSTBOOL: + V_snprintf( buff, sizeof( buff ), m_bVertexShader ? "b%d" : "fb%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + m_dwConstBoolUsageMask |= 0x00000001 << dwRegNum; // Keep track of the use of this bool constant + break; + case D3DSPR_LOOP: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "aL%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_TEMPFLOAT16: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "temp_float16_xxx%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_MISCTYPE: + Assert( dwRegNum == 0 ); // So far, we know that MISC[0] is gl_FragCoord (aka vPos in DX ASM parlance), but we don't know about any other MISC registers + V_snprintf( buff, sizeof( buff ), "gl_FragCoord" ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_LABEL: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "label%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + case D3DSPR_PREDICATE: + TranslationError(); + V_snprintf( buff, sizeof( buff ), "p%d", dwRegNum ); + strcat_s( pRegisterName, nBufLen, buff ); + break; + } + + // If this is a dest register + if ( dwSourceOrDest == DST_REGISTER ) + { + // + // Write masks + // + // If some (not all, not none) of the write masks are set, we should include them + // + if ( bAllowWriteMask && ( !((dwToken & D3DSP_WRITEMASK_ALL) == D3DSP_WRITEMASK_ALL) || ((dwToken & D3DSP_WRITEMASK_ALL) == 0x00000000) ) ) + { + // Put the dot on there + strcat_s( pRegisterName, nBufLen, "." ); + + // Optionally put on the x, y, z or w + int nMasksWritten = 0; + if ( dwToken & D3DSP_WRITEMASK_0 ) + { + strcat_s( pRegisterName, nBufLen, "x" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_1 ) + { + strcat_s( pRegisterName, nBufLen, "y" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_2 ) + { + strcat_s( pRegisterName, nBufLen, "z" ); + ++nMasksWritten; + } + if ( dwToken & D3DSP_WRITEMASK_3 ) + { + strcat_s( pRegisterName, nBufLen, "w" ); + ++nMasksWritten; + } + } + } + else // must be a source register + { + if ( bAllowSwizzle ) // relative addressing hard-codes the swizzle on a0.x + { + uint32 dwXSwizzle, dwYSwizzle, dwZSwizzle, dwWSwizzle; + + // Mask out the swizzle modifier + dwSwizzle = dwToken & D3DVS_SWIZZLE_MASK; + + // If there are any swizzles at all, tack on the appropriate notation + if ( dwSwizzle != D3DVS_NOSWIZZLE ) + { + // Separate out the two-bit codes for each component swizzle + dwXSwizzle = dwSwizzle & D3DVS_X_W; + dwYSwizzle = dwSwizzle & D3DVS_Y_W; + dwZSwizzle = dwSwizzle & D3DVS_Z_W; + dwWSwizzle = dwSwizzle & D3DVS_W_W; + + // Put on the dot + strcat_s( pRegisterName, nBufLen, "." ); + + // See where X comes from + switch ( dwXSwizzle ) + { + case D3DVS_X_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_X_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_X_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_X_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + if ( !bForceScalarSource ) + { + // If the source of the remaining components are aren't + // identical to the source of x, continue with swizzle + if ( ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwYSwizzle >> (D3DVS_SWIZZLE_SHIFT + 2))) || // X and Y sources match? + ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwZSwizzle >> (D3DVS_SWIZZLE_SHIFT + 4))) || // X and Z sources match? + ((dwXSwizzle >> D3DVS_SWIZZLE_SHIFT) != (dwWSwizzle >> (D3DVS_SWIZZLE_SHIFT + 6)))) // X and W sources match? + { + + // OpenGL seems to want us to have either 1 or 4 components in a swizzle, so just plow on through the rest + switch ( dwYSwizzle ) + { + case D3DVS_Y_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_Y_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_Y_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_Y_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + switch ( dwZSwizzle ) + { + case D3DVS_Z_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_Z_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_Z_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_Z_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + switch ( dwWSwizzle ) + { + case D3DVS_W_X: + strcat_s( pRegisterName, nBufLen, "x" ); + break; + case D3DVS_W_Y: + strcat_s( pRegisterName, nBufLen, "y" ); + break; + case D3DVS_W_Z: + strcat_s( pRegisterName, nBufLen, "z" ); + break; + case D3DVS_W_W: + strcat_s( pRegisterName, nBufLen, "w" ); + break; + } + + } + + } // end !bForceScalarSource + } + else // dwSwizzle == D3DVS_NOSWIZZLE + { + // If this is a MOVA / ARL, GL on the Mac requires us to tack the .x onto the source register + if ( bForceScalarSource ) + { + strcat_s( pRegisterName, nBufLen, ".x" ); + } + } + } // bAllowSwizzle + + // If there are any source modifiers, check to see if they're at + // least partially "postfix" and tack them on as appropriate + if ( dwSrcModifier != D3DSPSM_NONE ) + { + switch ( dwSrcModifier ) + { + case D3DSPSM_BIAS: // bias + case D3DSPSM_BIASNEG: // bias and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_bx2" ); + break; + case D3DSPSM_SIGN: // sign + case D3DSPSM_SIGNNEG: // sign and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_sgn" ); + break; + case D3DSPSM_X2: // *2 + case D3DSPSM_X2NEG: // *2 and negate + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_x2" ); + break; + case D3DSPSM_ABS: // abs() + case D3DSPSM_ABSNEG: // -abs() + strcat_s( pRegisterName, nBufLen, ")" ); + break; + case D3DSPSM_DZ: // divide through by z component + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_dz" ); + break; + case D3DSPSM_DW: // divide through by w component + TranslationError(); + strcat_s( pRegisterName, nBufLen, "_dw" ); + break; + } + } // end postfix modifiers (really only ps.1.x) + } +} + +void D3DToGL::RecordInputAndOutputPositions() +{ + // Remember where we are in the token stream. + m_pRecordedInputTokenStart = m_pdwNextToken; + + // Remember where our outputs are. + m_nRecordedParamCodeStrlen = V_strlen( (char*)m_pBufParamCode->Base() ); + m_nRecordedALUCodeStrlen = V_strlen( (char*)m_pBufALUCode->Base() ); + m_nRecordedAttribCodeStrlen = V_strlen( (char*)m_pBufAttribCode->Base() ); +} +void D3DToGL::AddTokenHexCodeToBuffer( char *pBuffer, int nSize, int nLastStrlen ) +{ + int nCurStrlen = V_strlen( pBuffer ); + if ( nCurStrlen == nLastStrlen ) + return; + + // Build a string with all the hex codes of the tokens since last time. + char szHex[512]; + szHex[0] = '\n'; + V_snprintf( &szHex[1], sizeof( szHex )-1, HEXCODE_HEADER ); + int nTokens = MIN( 10, m_pdwNextToken - m_pRecordedInputTokenStart ); + for ( int i=0; i < nTokens; i++ ) + { + char szTemp[32]; + V_snprintf( szTemp, sizeof( szTemp ), "0x%x ", m_pRecordedInputTokenStart[i] ); + V_strncat( szHex, szTemp, sizeof( szHex ) ); + } + V_strncat( szHex, "\n", sizeof( szHex ) ); + + // Insert the hex codes into the string. + int nBytesToInsert = V_strlen( szHex ); + if ( nCurStrlen + nBytesToInsert + 1 >= nSize ) + Error( "Buffer overflow writing token hex codes" ); + + if ( m_bPutHexCodesAfterLines ) + { + // Put it at the end of the last line. + if ( pBuffer[nCurStrlen-1] == '\n' ) + pBuffer[nCurStrlen-1] = 0; + + V_strncat( pBuffer, &szHex[1], nSize ); + } + else + { + memmove( pBuffer + nLastStrlen + nBytesToInsert, pBuffer + nLastStrlen, nCurStrlen - nLastStrlen + 1 ); + memcpy( pBuffer + nLastStrlen, szHex, nBytesToInsert ); + } +} + +void D3DToGL::AddTokenHexCode() +{ + if ( m_pdwNextToken > m_pRecordedInputTokenStart ) + { + AddTokenHexCodeToBuffer( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size(), m_nRecordedParamCodeStrlen ); + AddTokenHexCodeToBuffer( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), m_nRecordedALUCodeStrlen ); + AddTokenHexCodeToBuffer( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size(), m_nRecordedAttribCodeStrlen ); + } +} + +uint32 D3DToGL::MaintainAttributeMap( uint32 dwToken, uint32 dwRegToken ) +{ + // Check that this reg index has not been used before - if it has, let Houston know + uint dwRegIndex = dwRegToken & D3DSP_REGNUM_MASK; + if ( m_dwAttribMap[ dwRegIndex ] == 0xFFFFFFFF ) + { + // log it + // semantic/usage in the higher nibble + // usage index in the low nibble + + uint usage = dwToken & D3DSP_DCL_USAGE_MASK; + uint usageindex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + m_dwAttribMap[ dwRegIndex ] = ( usage << 4 ) | usageindex; + + // avoid writing 0xBB since runtime code uses that for an 'unused' marker + if ( m_dwAttribMap[ dwRegIndex ] == 0xBB ) + { + Debugger(); + } + } + else + { + //not OK + Debugger(); + } + + return dwRegIndex; +} + +void D3DToGL::Handle_DCL() +{ + uint32 dwToken = GetNextToken(); // What kind of dcl is this... + uint32 dwRegToken = GetNextToken(); // Look ahead to register token + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + uint32 dwRegNum = dwRegToken & D3DSP_REGNUM_MASK; + uint32 nRegType = GetRegTypeFromToken( dwRegToken ); + + if ( m_bVertexShader ) + { + // If this is an output, remember the index (what the ASM code calls o0, o1, o2..) and the semantic. + // When GetParameterString( DST_REGISTER ) hits this one, we'll return "oN". + // At the end of the main() function, we'll insert a bunch of statements like "gl_Color = o2" based on what we remembered here. + if ( ( m_dwMajorVersion >= 3 ) && ( nRegType == D3DSPR_OUTPUT ) ) + { +// uint32 dwRegComponents = ( dwRegToken & D3DSP_WRITEMASK_ALL ) >> 16; // Components used by the output register (1 means float, 3 means vec2, 7 means vec3, f means vec4) + + if ( dwRegNum >= MAX_DECLARED_OUTPUTS ) + Error( "Output register number (%d) too high (only %d supported).", dwRegNum, MAX_DECLARED_OUTPUTS ); + + if ( m_DeclaredOutputs[dwRegNum] != UNDECLARED_OUTPUT ) + Error( "Output dcl_ hit for register #%d more than once!", dwRegNum ); + + Assert( dwToken != UNDECLARED_OUTPUT ); + m_DeclaredOutputs[dwRegNum] = dwToken; + + //uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + //uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + // Flag which o# output register maps to gl_Position + if ( dwUsage == D3DDECLUSAGE_POSITION ) + { + m_nVSPositionOutput = dwUsageIndex; + m_bDeclareVSOPos = true; + } + + if ( m_bAddHexCodeComments ) + { + CUtlString sParam2 = GetUsageAndIndexString( dwToken, SEMANTIC_OUTPUT ); + PrintToBuf( *m_pBufHeaderCode, "// [GL remembering that oT%d maps to %s]\n", dwRegNum, sParam2.String() ); + } + + } + else if ( GetRegType( dwRegToken ) == D3DSPR_SAMPLER ) + { + // We can support vertex texturing if necessary, but I can't find a use case in any branch. (HW morphing in L4D2 isn't enabled, and the comments indicate that r_hwmorph isn't compatible with mat_queue_mode anyway, and CS:GO/DoTA don't use vertex shader texturing.) + TranslationError(); + + int nRegNum = dwRegToken & D3DSP_REGNUM_MASK; + switch ( TextureType( dwToken ) ) + { + default: + case D3DSTT_UNKNOWN: + case D3DSTT_2D: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_2D; + break; + case D3DSTT_CUBE: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_CUBE; + break; + case D3DSTT_VOLUME: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_3D; + break; + } + + // Track sampler declarations + m_dwSamplerUsageMask |= 1 << nRegNum; + } + else + { + Assert( GetRegType( dwRegToken ) == D3DSPR_INPUT); + + CUtlString sParam1 = GetParameterString( dwRegToken, DST_REGISTER, false, NULL ); + CUtlString sParam2 = GetUsageAndIndexString( dwToken, SEMANTIC_INPUT ); + + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + PrintToBuf( *m_pBufHeaderCode, "attribute vec4 %s; // ", sParam1.String() ); + + MaintainAttributeMap( dwToken, dwRegToken ); + + char temp[128]; + // regnum goes straight into the vertex.attrib[n] index + sprintf( temp, "%08x %08x\n", dwToken, dwRegToken ); + StrcatToHeaderCode( temp ); + } + } + else // Pixel shader + { + // If the register is a sampler, the dcl has a dimension decorator that we have to save for subsequent TEX instructions + uint32 nRegType = GetRegType( dwRegToken ); + if ( nRegType == D3DSPR_SAMPLER ) + { + int nRegNum = dwRegToken & D3DSP_REGNUM_MASK; + switch ( TextureType( dwToken ) ) + { + default: + case D3DSTT_UNKNOWN: + case D3DSTT_2D: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_2D; + break; + case D3DSTT_CUBE: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_CUBE; + break; + case D3DSTT_VOLUME: + m_dwSamplerTypes[nRegNum] = SAMPLER_TYPE_3D; + break; + } + + // Track sampler declarations + m_dwSamplerUsageMask |= 1 << nRegNum; + } + else // Not a sampler, we're going to generate varying declaration code + { + // In pixel shaders we only declare texture coordinate varyings since they may be using centroid + if ( ( m_dwMajorVersion == 3 ) && ( nRegType == D3DSPR_INPUT ) ) + { + Assert( m_DeclaredInputs[dwRegNum] == UNDECLARED_INPUT ); + m_DeclaredInputs[dwRegNum] = dwToken; + + if ( ( dwUsage != D3DDECLUSAGE_COLOR ) && ( dwUsage != D3DDECLUSAGE_TEXCOORD ) ) + { + TranslationError(); // Not supported yet, but can be if we need it. + } + + if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + char buf[256]; + if ( m_nCentroidMask & ( 0x00000001 << dwUsageIndex ) ) + { + V_snprintf( buf, sizeof( buf ), "centroid varying vec4 oT%d;\n", dwUsageIndex ); // centroid varying + } + else + { + V_snprintf( buf, sizeof( buf ), "varying vec4 oT%d;\n", dwUsageIndex ); + } + StrcatToHeaderCode( buf ); + } + } + else if ( nRegType == D3DSPR_TEXTURE ) + { + char buff[256]; + PrintParameterToString( dwRegToken, DST_REGISTER, buff, sizeof( buff ), false, NULL ); + PrintToBuf( *m_pBufHeaderCode, "%s;\n",buff ); + } + else + { + // No need to declare anything (probably D3DSPR_MISCTYPE either VPOS or VFACE) + } + } + } +} + +static bool IsFloatNaN( float f ) +{ + const uint nBits = *reinterpret_cast<uint*>(&f); + const uint nExponent = ( nBits >> 23 ) & 0xFF; + + return ( nExponent == 255 ); +} + +static inline bool EqualTol( double a, double b, double t ) +{ + return fabs( a - b ) <= ( ( MAX( fabs( a ), fabs( b ) ) + 1.0 ) * t ); +} + +// Originally written by Bruce Dawson, see: +// See http://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/ +// This class represents a very limited high-precision number with 'count' 32-bit +// unsigned elements. +template <int count> +struct HighPrec +{ + typedef unsigned T; + typedef unsigned long long Product_t; + static const int kWordShift = 32; + HighPrec() + { + memset(m_data, 0, sizeof(m_data)); + m_nLowestNonZeroIndex = ARRAYSIZE(m_data); + } + + // Insert the bits from value into m_data, shifted in from the bottom (least + // significant end) by the specified number of bits. A shift of zero or less + // means that none of the bits will be shifted in. A shift of one means that + // the high bit of value will be in the bottom of the last element of m_data - + // the least significant bit. A shift of kWordShift means that value will be + // in the least significant element of m_data, and so on. + void InsertLowBits(T value, int shiftAmount) + { + if (shiftAmount <= 0) + return; + + int subShift = shiftAmount & (kWordShift - 1); + int bigShift = shiftAmount / kWordShift; + Product_t result = (Product_t)value << subShift; + T resultLow = (T)result; + T resultHigh = result >> kWordShift; + + // Use an unsigned type so that negative numbers will become large, + // which makes the range checking below simpler. + unsigned highIndex = ARRAYSIZE(m_data) - 1 - bigShift; + // Write the results to the data array. If the index is too large + // then that means that the data was shifted off the edge. + if ( (highIndex < ARRAYSIZE(m_data)) && ( resultHigh ) ) + { + m_data[highIndex] |= resultHigh; + m_nLowestNonZeroIndex = MIN( m_nLowestNonZeroIndex, highIndex ); + } + + if ( ( highIndex + 1 < ARRAYSIZE(m_data)) && ( resultLow ) ) + { + m_data[highIndex + 1] |= resultLow; + m_nLowestNonZeroIndex = MIN( m_nLowestNonZeroIndex, highIndex + 1 ); + } + } + + // Insert the bits from value into m_data, shifted in from the top (most + // significant end) by the specified number of bits. A shift of zero or less + // means that none of the bits will be shifted in. A shift of one means that + // the low bit of value will be in the top of the first element of m_data - + // the most significant bit. A shift of kWordShift means that value will be + // in the most significant element of m_data, and so on. + void InsertTopBits(T value, int shiftAmount) + { + InsertLowBits(value, (ARRAYSIZE(m_data) + 1) * kWordShift - shiftAmount); + } + + // Return true if all elements of m_data are zero. + bool IsZero() const + { + bool bIsZero = ( m_nLowestNonZeroIndex == ARRAYSIZE(m_data) ); + +#ifdef DEBUG + for (int i = 0; i < ARRAYSIZE(m_data); ++i) + { + if (m_data[i]) + { + Assert( !bIsZero ); + return false; + } + } + Assert( bIsZero ); +#endif + + return bIsZero; + } + + // Divide by div and return the remainder, from 0 to div-1. + // Standard long-division algorithm. + T DivReturnRemainder(T divisor) + { + T remainder = 0; + +#ifdef DEBUG + for (uint j = 0; j < m_nLowestNonZeroIndex; ++j) + { + Assert( m_data[j] == 0 ); + } +#endif + + int nNewLowestNonZeroIndex = ARRAYSIZE(m_data); + for (int i = m_nLowestNonZeroIndex; i < ARRAYSIZE(m_data); ++i) + { + Product_t dividend = ((Product_t)remainder << kWordShift) + m_data[i]; + Product_t result = dividend / divisor; + remainder = T(dividend % divisor); + + m_data[i] = T(result); + + if ( ( result ) && ( nNewLowestNonZeroIndex == ARRAYSIZE(m_data) ) ) + nNewLowestNonZeroIndex = i; + } + m_nLowestNonZeroIndex = nNewLowestNonZeroIndex; + + return remainder; + } + + // The individual 'digits' (32-bit unsigned integers actually) that + // make up the number. The most-significant digit is in m_data[0]. + T m_data[count]; + + uint m_nLowestNonZeroIndex; +}; + +union Double_t +{ + Double_t(double num = 0.0f) : f(num) {} + // Portable extraction of components. + bool Negative() const { return (i >> 63) != 0; } + int64_t RawMantissa() const { return i & ((1LL << 52) - 1); } + int64_t RawExponent() const { return (i >> 52) & 0x7FF; } + + int64_t i; + double f; +}; + +static uint PrintDoubleInt( char *pBuf, uint nBufSize, double f, uint nMinChars ) +{ + static const char *pDigits = "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"; + + Assert( !nMinChars || ( ( nMinChars % 6 ) == 0 ) ); + + char *pLastChar = pBuf + nBufSize - 1; + char *pDst = pLastChar; + *pDst-- = '\0'; + + // Put the double in our magic union so we can grab the components. + union Double_t num(f); + + // Get the character that represents the sign. + // Check for NaNs or infinity. + if (num.RawExponent() == 2047) + { + TranslationError(); + } + + // Adjust for the exponent bias. + int exponentValue = int(num.RawExponent() - 1023); + // Add the implied one to the mantissa. + uint64_t mantissaValue = (1ll << 52) + num.RawMantissa(); + // Special-case for denormals - no special exponent value and + // no implied one. + if (num.RawExponent() == 0) + { + exponentValue = -1022; + mantissaValue = num.RawMantissa(); + } + uint32_t mantissaHigh = mantissaValue >> 32; + uint32_t mantissaLow = mantissaValue & 0xFFFFFFFF; + + // The first bit of the mantissa has an implied value of one and this can + // be shifted 1023 positions to the left, so that's 1024 bits to the left + // of the binary point, or 32 32-bit words for the integer part. + HighPrec<32> intPart; + // When our exponentValue is zero (a number in the 1.0 to 2.0 range) + // we have a 53-bit mantissa and the implied value of the highest bit + // is 1. We need to shift 12 bits in from the bottom to get that 53rd bit + // into the ones spot in the integral portion. + // To complicate it a bit more we have to insert the mantissa as two parts. + intPart.InsertLowBits(mantissaHigh, 12 + exponentValue); + intPart.InsertLowBits(mantissaLow, 12 + exponentValue - 32); + + bool bAnyDigitsLeft; + do + { + uint remainder = intPart.DivReturnRemainder( 1000000 ); // 10^6 + uint origRemainer = remainder; (void)origRemainer; + + bAnyDigitsLeft = !intPart.IsZero(); + + if ( bAnyDigitsLeft ) + { + uint n = remainder % 100U; remainder /= 100U; *reinterpret_cast<uint16*>(pDst - 1) = reinterpret_cast<const uint16*>(pDigits)[n]; + n = remainder % 100U; remainder /= 100U; *reinterpret_cast<uint16*>(pDst - 1 - 2) = reinterpret_cast<const uint16*>(pDigits)[n]; + Assert( remainder < 100U ); + *reinterpret_cast<uint16*>(pDst - 1 - 4) = reinterpret_cast<const uint16*>(pDigits)[remainder]; + pDst -= 6; + } + else + { + uint n = remainder % 100U; remainder /= 100U; *reinterpret_cast<uint16*>(pDst - 1) = reinterpret_cast<const uint16*>(pDigits)[n]; --pDst; if ( ( n >= 10 ) || ( remainder ) ) --pDst; + if ( remainder ) + { + n = remainder % 100U; remainder /= 100U; *reinterpret_cast<uint16*>(pDst - 1) = reinterpret_cast<const uint16*>(pDigits)[n]; --pDst; if ( ( n >= 10 ) || ( remainder ) ) --pDst; + + if ( remainder ) + { + Assert( remainder < 100U ); + *reinterpret_cast<uint16*>(pDst - 1) = reinterpret_cast<const uint16*>(pDigits)[remainder]; --pDst; if ( remainder >= 10 ) --pDst; + } + } + } + + } while ( bAnyDigitsLeft ); + + uint l = pLastChar - pDst; + + while ( ( l - 1 ) < nMinChars ) + { + *pDst-- = '0'; + l++; + } + + Assert( (int)l == ( pLastChar - pDst ) ); + + Assert( l <= nBufSize ); + + memmove( pBuf, pDst + 1, l ); + return l - 1; +} + +// FloatToString is equivalent to sprintf( "%.12f" ), but doesn't have any dependencies on the current locale setting. +// Unfortunately, high accuracy radix conversion is actually pretty tricky to do right. +// Most importantly, this function has the same max roundtrip (IEEE->ASCII->IEEE) error as the MS CRT functions and can reliably handle extremely large inputs. +static void FloatToString( char *pBuf, uint nBufSize, double fConst ) +{ + char *pEnd = pBuf + nBufSize; + char *pDst = pBuf; + + double flVal = fConst; + if ( IsFloatNaN( flVal ) ) + { + flVal = 0; + } + + if ( flVal < 0.0f ) + { + *pDst++ = '-'; + flVal = -flVal; + } + + double flInt; + double flFract = modf( flVal, &flInt ); + + flFract = floor( flFract * 1000000000000.0 + .5 ); + + if ( !flInt ) + { + *pDst++ = '0'; + } + else + { + uint l = PrintDoubleInt( pDst, pEnd - pDst, flInt, 0 ); + pDst += l; + } + + *pDst++ = '.'; + if ( !flFract ) + { + *pDst++ = '0'; + *pDst++ = '\0'; + } + else + { + uint l = PrintDoubleInt( pDst, pEnd - pDst, flFract, 12 ); + pDst += l; + + StripExtraTrailingZeros( pBuf ); // Turn 1.00000 into 1.0 + } +} + +#if 0 +#include "vstdlib/random.h" +static void TestFloatConversion() +{ + for ( ; ; ) + { + double fConst; + switch ( rand() % 4 ) + { + case 0: + fConst = RandomFloat( -1e-30, 1e+30 ); break; + case 1: + fConst = RandomFloat( -1e-10, 1e+10 ); break; + case 2: + fConst = RandomFloat( -1e-5, 1e+5 ); break; + default: + fConst = RandomFloat( -1, 1 ); break; + } + + char szTemp[1024]; + + // FloatToString does not rely on V_snprintf(), so it can't be affected by the current locale setting. + FloatToString( szTemp, sizeof( szTemp ), fConst ); + + static double flMaxErr1; + static double flMaxErr2; + + // Compare FloatToString()'s results vs. V_snprintf()'s, also track maximum error of each. + double flCheck = atof( szTemp ); + double flErr = fabs( flCheck - fConst ); + flMaxErr1 = MAX( flMaxErr1, flErr ); + Assert( EqualTol( flCheck, fConst, .000000125 ) ); + + char szTemp2[256]; + V_snprintf( szTemp2, sizeof( szTemp2 ), "%.12f", fConst ); + StripExtraTrailingZeros( szTemp2 ); + + if ( !strchr( szTemp2, '.' ) ) + { + V_strncat( szTemp2, ".0", sizeof( szTemp2 ) ); + } + double flCheck2 = atof( szTemp2 ); + double flErr2 = fabs( flCheck2 - fConst ); + flMaxErr2 = MAX( flMaxErr2, flErr2 ); + Assert( EqualTol( flCheck2, fConst, .000000125 ) ); + + if ( flMaxErr1 > flMaxErr2 ) + { + GLMDebugPrintf( "!\n" ); + } + } +} +#endif + +void D3DToGL::Handle_DEFIB( uint32 instruction ) +{ + Assert( ( instruction == D3DSIO_DEFI ) || ( instruction == D3DSIO_DEFB ) ); + + // which register is being defined + uint32 dwToken = GetNextToken(); + + uint32 nRegNum = dwToken & D3DSP_REGNUM_MASK; + + uint32 regType = GetRegTypeFromToken( dwToken ); + + + if ( regType == D3DSPR_CONSTINT ) + { + m_dwDefConstIntUsageMask |= ( 1 << nRegNum ); + + uint x = GetNextToken(); + uint y = GetNextToken(); + uint z = GetNextToken(); + uint w = GetNextToken(); + NOTE_UNUSED(y); NOTE_UNUSED(z); NOTE_UNUSED(w); + + Assert( nRegNum < 32 ); + if ( nRegNum < 32 ) + { + m_dwDefConstIntIterCount[nRegNum] = x; + } + } + else + { + TranslationError(); + } + +} + +void D3DToGL::Handle_DEF() +{ + //TestFloatConversion(); + + // + // JasonM TODO: catch D3D's sincos-specific D3DSINCOSCONST1 and D3DSINCOSCONST2 constants and filter them out here + // + + // Which register is being defined + uint32 dwToken = GetNextToken(); + + // Note that this constant was explicitly defined + m_bConstantRegisterDefined[dwToken & D3DSP_REGNUM_MASK] = true; + CUtlString sParamName = GetParameterString( dwToken, DST_REGISTER, false, NULL ); + + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + PrintToBuf( *m_pBufParamCode, "vec4 %s = vec4( ", sParamName.String() ); + + // Run through the 4 floats + for ( int i=0; i < 4; i++ ) + { + float fConst = uint32ToFloat( GetNextToken() ); + + char szTemp[1024]; + + FloatToString( szTemp, sizeof( szTemp ), fConst ); + +#if 0 + static double flMaxErr1; + static double flMaxErr2; + + // Compare FloatToString()'s results vs. V_snprintf()'s, also track maximum error of each. + double flCheck = atof( szTemp ); + double flErr = fabs( flCheck - fConst ); + flMaxErr1 = MAX( flMaxErr1, flErr ); + Assert( EqualTol( flCheck, fConst, .000000125 ) ); + + char szTemp2[256]; + V_snprintf( szTemp2, sizeof( szTemp2 ), "%.12f", fConst ); + StripExtraTrailingZeros( szTemp2 ); + + if ( !strchr( szTemp2, '.' ) ) + { + V_strncat( szTemp2, ".0", sizeof( szTemp2 ) ); + } + double flCheck2 = atof( szTemp2 ); + double flErr2 = fabs( flCheck2 - fConst ); + flMaxErr2 = MAX( flMaxErr2, flErr2 ); + Assert( EqualTol( flCheck2, fConst, .000000125 ) ); + + if ( flMaxErr1 > flMaxErr2 ) + { + GLMDebugPrintf( "!\n" ); + } +#endif + + PrintToBuf( *m_pBufParamCode, i != 3 ? "%s, " : "%s", szTemp ); // end with comma-space + } + + PrintToBuf( *m_pBufParamCode, " );\n" ); +} + +void D3DToGL::Handle_MAD( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + int nARLComp2 = ARL_DEST_NONE; + CUtlString sParam4 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp2 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1, nARLComp2 ); + + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + sParam3 = FixGLSLSwizzle( sParam1, sParam3 ); + sParam4 = FixGLSLSwizzle( sParam1, sParam4 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s * %s + %s;\n", sParam1.String(), sParam2.String(), sParam3.String(), sParam4.String() ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_DP2ADD() +{ + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64], pSrc2Reg[64]; + uint32 nDestToken = GetNextToken(); + PrintParameterToString( nDestToken, DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc2Reg, sizeof( pSrc2Reg ), false, NULL ); + + // We should only be assigning to a single component of the dest. + Assert( GetNumSwizzleComponents( pDestReg ) == 1 ); + Assert( GetNumSwizzleComponents( pSrc2Reg ) == 1 ); + + // This is a 2D dot product, so we only want two entries from the middle components. + CUtlString sArg0 = EnsureNumSwizzleComponents( pSrc0Reg, 2 ); + CUtlString sArg1 = EnsureNumSwizzleComponents( pSrc1Reg, 2 ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = dot( %s, %s ) + %s;\n", pDestReg, sArg0.String(), sArg1.String(), pSrc2Reg ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( pDestReg ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", pDestReg, pDestReg, g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_SINCOS() +{ + char pDestReg[64], pSrc0Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), true, NULL ); + m_bNeedsSinCosDeclarations = true; + + + CUtlString sDest( pDestReg ); + CUtlString sArg0 = EnsureNumSwizzleComponents( pSrc0Reg, 1 );// Ensure input is scalar + CUtlString sResult( "vSinCosTmp.xy" ); // Always going to populate this + sResult = FixGLSLSwizzle( sDest, sResult ); // Make sure we match the desired output reg + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.z = %s * %s;\n", sArg0.String(), sArg0.String() ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.zz * scA.xy + scA.wz;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.zz + scB.xy;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.zz + scB.wz;\n" ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.x = vSinCosTmp.x * %s;\n", sArg0.String() ); + + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy * vSinCosTmp.xx;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.xy = vSinCosTmp.xy + vSinCosTmp.xy;\n" ); + PrintToBufWithIndents( *m_pBufALUCode, "vSinCosTmp.x = -vSinCosTmp.x + scB.z;\n" ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sDest.String(), sResult.String() ); + + if ( m_dwMajorVersion < 3 ) + { + // Eat two more tokens since D3D defines Taylor series constants that we won't need + // Only valid for pixel and vertex shader version earlier than 3_0 + // (http://msdn.microsoft.com/en-us/library/windows/hardware/ff569710(v=vs.85).aspx) + SkipTokens( 2 ); + } +} + + +void D3DToGL::Handle_LRP( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sDest = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam0 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam1 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + int nARLComp2 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp2 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1, nARLComp2 ); + + sParam0 = FixGLSLSwizzle( sDest, sParam0 ); + sParam1 = FixGLSLSwizzle( sDest, sParam1 ); + sParam2 = FixGLSLSwizzle( sDest, sParam2 ); + + // dest = src0 * (src1 - src2) + src2; + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s * ( %s - %s ) + %s;\n", sDest.String(), sParam0.String(), sParam1.String(), sParam2.String(), sParam2.String() ); + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sDest.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sDest.String(), sDest.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + + +void D3DToGL::Handle_TEX( uint32 dwToken, bool bIsTexLDL ) +{ + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + + DWORD dwSrc1Token = GetNextToken(); + PrintParameterToString( dwSrc1Token, SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + + Assert( (dwSrc1Token & D3DSP_REGNUM_MASK) < ARRAYSIZE( m_dwSamplerTypes ) ); + uint32 nSamplerType = m_dwSamplerTypes[dwSrc1Token & D3DSP_REGNUM_MASK]; + if ( nSamplerType == SAMPLER_TYPE_2D ) + { + const bool bIsShadowSampler = ( ( 1 << ( (int) ( dwSrc1Token & D3DSP_REGNUM_MASK ) ) ) & m_nShadowDepthSamplerMask ) != 0; + + if ( bIsTexLDL ) + { + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, bIsShadowSampler ? 3 : 2 ); + + // Strip out the W component of the pSrc0Reg and pass that as the LOD to texture2DLod. + char szLOD[128], szExtra[8]; + GetParamNameWithoutSwizzle( pSrc0Reg, szLOD, sizeof( szLOD ) ); + V_snprintf( szExtra, sizeof( szExtra ), ".%c", GetSwizzleComponent( pSrc0Reg, 3 ) ); + V_strncat( szLOD, szExtra, sizeof( szLOD ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s( %s, %s, %s );\n", pDestReg, bIsShadowSampler ? "shadow2DLod" : "texture2DLod", pSrc1Reg, sCoordVar.String(), szLOD ); + } + else if ( bIsShadowSampler ) + { + // .z is meant to contain the object depth, while .xy contains the 2D tex coords + CUtlString sCoordVar3D = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = shadow2D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar3D.String() ); + Assert( m_dwSamplerTypes[dwSrc1Token & D3DSP_REGNUM_MASK] == SAMPLER_TYPE_2D ); + } + else if( ( OpcodeSpecificData( dwToken ) << D3DSP_OPCODESPECIFICCONTROL_SHIFT ) == D3DSI_TEXLD_PROJECT ) + { + // This projective case is after the shadow case intentionally, due to the way that "projective" + // loads are overloaded in our D3D shaders for shadow lookups. + // + // We use the vec4 variant of texture2DProj() intentionally here, since it lines up well with Direct3D. + + CUtlString s4DProjCoords = EnsureNumSwizzleComponents( pSrc0Reg, 4 ); // Ensure vec4 variant + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture2DProj( %s, %s );\n", pDestReg, pSrc1Reg, s4DProjCoords.String() ); + } + else + { + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, bIsShadowSampler ? 3 : 2 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture2D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + } + else if ( nSamplerType == SAMPLER_TYPE_3D ) + { + if ( bIsTexLDL ) + { + TranslationError(); + } + + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = texture3D( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + else if ( nSamplerType == SAMPLER_TYPE_CUBE ) + { + if ( bIsTexLDL ) + { + TranslationError(); + } + + CUtlString sCoordVar = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = textureCube( %s, %s );\n", pDestReg, pSrc1Reg, sCoordVar.String() ); + } + else + { + Error( "TEX instruction: unsupported sampler type used" ); + } +} + +void D3DToGL::StrcatToHeaderCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufHeaderCode->Base(), m_pBufHeaderCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToALUCode( const char *pBuf ) +{ + PrintIndentation( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size() ); + + strcat_s( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToParamCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size(), pBuf ); +} + +void D3DToGL::StrcatToAttribCode( const char *pBuf ) +{ + strcat_s( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size(), pBuf ); +} + +void D3DToGL::Handle_TexLDD( uint32 nInstruction ) +{ + TranslationError(); // Not supported yet, but can be if we need it. +} + + +void D3DToGL::Handle_TexCoord() +{ + TranslationError(); + + // If ps_1_4, this is texcrd + if ( (m_dwMajorVersion == 1) && (m_dwMinorVersion == 4) && (!m_bVertexShader) ) + { + StrcatToALUCode( "texcrd" ); + } + else // else it's texcoord + { + TranslationError(); + StrcatToALUCode( "texcoord" ); + } + + char buff[256]; + PrintParameterToString( GetNextToken(), DST_REGISTER, buff, sizeof( buff ), false, NULL ); + StrcatToALUCode( buff ); + + // If ps_1_4, texcrd also has a source parameter + if ((m_dwMajorVersion == 1) && (m_dwMinorVersion == 4) && (!m_bVertexShader)) + { + StrcatToALUCode( ", " ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + StrcatToALUCode( buff ); + } + + StrcatToALUCode( ";\n" ); +} + +void D3DToGL::Handle_BREAKC( uint32 dwToken ) +{ + uint nComparison = ( dwToken & D3DSHADER_COMPARISON_MASK ) >> D3DSHADER_COMPARISON_SHIFT; + + const char *pComparison = "?"; + switch ( nComparison ) + { + case D3DSPC_GT: pComparison = ">"; break; + case D3DSPC_EQ: pComparison = "=="; break; + case D3DSPC_GE: pComparison = ">="; break; + case D3DSPC_LT: pComparison = "<"; break; + case D3DSPC_NE: pComparison = "!="; break; + case D3DSPC_LE: pComparison = "<="; break; + default: + TranslationError(); + } + + char src0[256]; + uint32 src0Token = GetNextToken(); + PrintParameterToString( src0Token, SRC_REGISTER, src0, sizeof( src0 ), false, NULL ); + + char src1[256]; + uint32 src1Token = GetNextToken(); + PrintParameterToString( src1Token, SRC_REGISTER, src1, sizeof( src1 ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if (%s %s %s) break;\n", src0, pComparison, src1 ); +} + +void D3DToGL::HandleBinaryOp_GLSL( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1 ); + + // Since DP3 and DP4 have a scalar as the dest and vectors as the src, don't screw with the swizzle specifications. + if ( nInstruction == D3DSIO_DP3 ) + { + sParam2 = EnsureNumSwizzleComponents( sParam2, 3 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 3 ); + } + else if ( nInstruction == D3DSIO_DP4 ) + { + sParam2 = EnsureNumSwizzleComponents( sParam2, 4 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 4 ); + } + else if ( nInstruction == D3DSIO_DST ) + { + m_bUsesDSTInstruction = true; + sParam2 = EnsureNumSwizzleComponents( sParam2, 4 ); + sParam3 = EnsureNumSwizzleComponents( sParam3, 4 ); + } + else + { + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + sParam3 = FixGLSLSwizzle( sParam1, sParam3 ); + } + + char buff[256]; + if ( nInstruction == D3DSIO_ADD || nInstruction == D3DSIO_SUB || nInstruction == D3DSIO_MUL ) + { + // These all look like x = y op z + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s %s %s;\n", sParam1.String(), sParam2.String(), GetGLSLOperatorString( nInstruction ), sParam3.String() ); + } + else + { + int nDestComponents = GetNumSwizzleComponents( sParam1.String() ); + int nSrcComponents = GetNumSwizzleComponents( sParam2.String() ); + + // All remaining instructions can use GLSL intrinsics like dot() and cross(). + bool bDoubleClose = OpenIntrinsic( nInstruction, buff, sizeof( buff ), nDestComponents, nSrcComponents ); + + if ( ( nSrcComponents == 1 ) && ( nInstruction == D3DSIO_SGE ) ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s >= %s );\n", sParam1.String(), buff, sParam2.String(), sParam3.String() ); + } + else if ( ( nSrcComponents == 1 ) && ( nInstruction == D3DSIO_SLT ) ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s < %s );\n", sParam1.String(), buff, sParam2.String(), sParam3.String() ); + } + else + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s%s, %s %s;\n", sParam1.String(), buff, sParam2.String(), sParam3.String(), bDoubleClose ? ") )" : ")" ); + } + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::HandleBinaryOp_ASM( uint32 nInstruction ) +{ + CUtlString sParam1 = GetParameterString( GetNextToken(), DST_REGISTER, false, NULL ); + int nARLComp0 = ARL_DEST_NONE; + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp0 ); + int nARLComp1 = ARL_DEST_NONE; + CUtlString sParam3 = GetParameterString( GetNextToken(), SRC_REGISTER, false, &nARLComp1 ); + + // This optionally inserts a move from our dummy address register to the .x component of the real one + InsertMoveFromAddressRegister( m_pBufALUCode, nARLComp0, nARLComp1 ); + + char buff[256]; + PrintOpcode( nInstruction, buff, sizeof( buff ) ); + PrintToBufWithIndents( *m_pBufALUCode, "%s%s, %s, %s;\n", buff, sParam1.String(), sParam2.String(), sParam3.String() ); +} + +void D3DToGL::WriteGLSLCmp( const char *pDestReg, const char *pSrc0Reg, const char *pSrc1Reg, const char *pSrc2Reg ) +{ + int nWriteMaskEntries = GetNumWriteMaskEntries( pDestReg ); + for ( int i=0; i < nWriteMaskEntries; i++ ) + { + char params[4][256]; + WriteParamWithSingleMaskEntry( pDestReg, i, params[0], sizeof( params[0] ) ); + WriteParamWithSingleMaskEntry( pSrc0Reg, i, params[1], sizeof( params[1] ) ); + WriteParamWithSingleMaskEntry( pSrc1Reg, i, params[2], sizeof( params[2] ) ); + WriteParamWithSingleMaskEntry( pSrc2Reg, i, params[3], sizeof( params[3] ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = ( %s >= 0.0 ) ? %s : %s;\n", params[0], params[1], params[2], params[3] ); + } +} + +void D3DToGL::Handle_CMP() +{ + // In Direct3D, result = (src0 >= 0.0) ? src1 : src2 + // In OpenGL, result = (src0 < 0.0) ? src1 : src2 + // + // As a result, arguments are effectively in a different order than Direct3D! !#$&*!%#$& + char pDestReg[64], pSrc0Reg[64], pSrc1Reg[64], pSrc2Reg[64]; + uint32 nDestToken = GetNextToken(); + PrintParameterToString( nDestToken, DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc1Reg, sizeof( pSrc1Reg ), false, NULL ); + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc2Reg, sizeof( pSrc2Reg ), false, NULL ); + + // These are a tricky case.. we have to expand it out into multiple statements. + char szDestBase[256]; + GetParamNameWithoutSwizzle( pDestReg, szDestBase, sizeof( szDestBase ) ); + + V_strncpy( pSrc0Reg, FixGLSLSwizzle( pDestReg, pSrc0Reg ), sizeof( pSrc0Reg ) ); + V_strncpy( pSrc1Reg, FixGLSLSwizzle( pDestReg, pSrc1Reg ), sizeof( pSrc1Reg ) ); + V_strncpy( pSrc2Reg, FixGLSLSwizzle( pDestReg, pSrc2Reg ), sizeof( pSrc2Reg ) ); + + // This isn't reliable! + //if ( DoParamNamesMatch( pDestReg, pSrc0Reg ) && GetNumSwizzleComponents( pDestReg ) > 1 ) + if ( 1 ) + { + // So the dest register is the same as the comparand. We're in danger of screwing up our results. + // + // For example, this code: + // CMP r0.xy, r0.xx, r1, r2 + // would generate this: + // r0.x = (r0.x >= 0) ? r1.x : r2.x; + // r0.y = (r0.x >= 0) ? r1.x : r2.x; + // + // But the first lines changes r0.x and thus screws the atomicity of the CMP instruction for the second line. + // So we assign r0 to a temporary first and then write to the temporary. + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", g_pAtomicTempVarName, szDestBase ); + + char szTempVar[256]; + ReplaceParamName( pDestReg, g_pAtomicTempVarName, szTempVar, sizeof( szTempVar ) ); + WriteGLSLCmp( szTempVar, pSrc0Reg, pSrc1Reg, pSrc2Reg ); + + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", szDestBase, g_pAtomicTempVarName ); + m_bUsedAtomicTempVar = true; + } + else + { + // Just write out the simple expanded version of the CMP. No need to use atomic_temp_var. + WriteGLSLCmp( pDestReg, pSrc0Reg, pSrc1Reg, pSrc2Reg ); + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( pDestReg ); + if ( nComponents == 0 ) + nComponents = 4; + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", pDestReg, pDestReg, g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::Handle_NRM() +{ + char pDestReg[64]; + char pSrc0Reg[64]; + PrintParameterToString( GetNextToken(), DST_REGISTER, pDestReg, sizeof( pDestReg ), false, NULL ); + int nARLSrcComp = ARL_DEST_NONE; + PrintParameterToString( GetNextToken(), SRC_REGISTER, pSrc0Reg, sizeof( pSrc0Reg ), false, &nARLSrcComp ); + + if ( nARLSrcComp != -1 ) + { + InsertMoveFromAddressRegister( m_pBufALUCode, nARLSrcComp, -1, -1 ); + } + + CUtlString sSrc = EnsureNumSwizzleComponents( pSrc0Reg, 3 ); + PrintToBufWithIndents( *m_pBufALUCode, "%s = normalize( %s );\n", pDestReg, sSrc.String() ); +} + +void D3DToGL::Handle_UnaryOp( uint32 nInstruction ) +{ + uint32 nDestToken = GetNextToken(); + CUtlString sParam1 = GetParameterString( nDestToken, DST_REGISTER, false, NULL ); + CUtlString sParam2 = GetParameterString( GetNextToken(), SRC_REGISTER, false, NULL ); + sParam2 = FixGLSLSwizzle( sParam1, sParam2 ); + + + if ( nInstruction == D3DSIO_MOV ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_RSQ ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = inversesqrt( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_RCP ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = 1.0 / %s;\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_EXP ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = exp2( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_FRC ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = fract( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_LOG ) // d3d 'log' is log base 2 + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = log2( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_ABS ) // rbarris did this one, Jason please check + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = abs( %s );\n", sParam1.String(), sParam2.String() ); + } + else if ( nInstruction == D3DSIO_MOVA ) + { + m_bDeclareAddressReg = true; + PrintToBufWithIndents( *m_pBufALUCode, "%s = %s;\n", sParam1.String(), sParam2.String() ); + + if ( !m_bGenerateBoneUniformBuffer ) + { + m_nHighestRegister = DXABSTRACT_VS_PARAM_SLOTS - 1; + } + } + else + { + Error( "Unsupported instruction" ); + } + + // If the _SAT instruction modifier is used, then do a saturate here. + if ( nDestToken & D3DSPDM_SATURATE ) + { + int nComponents = GetNumSwizzleComponents( sParam1.String() ); + if ( nComponents == 0 ) + { + nComponents = 4; + } + + PrintToBufWithIndents( *m_pBufALUCode, "%s = clamp( %s, %s, %s );\n", sParam1.String(), sParam1.String(), g_szVecZeros[nComponents], g_szVecOnes[nComponents] ); + } +} + +void D3DToGL::WriteGLSLSamplerDefinitions() +{ + int nSamplersWritten = 0; + for ( int i=0; i < ARRAYSIZE( m_dwSamplerTypes ); i++ ) + { + if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_2D ) + { + if ( ( ( 1 << i ) & m_nShadowDepthSamplerMask ) != 0 ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler2DShadow sampler%d;\n", i ); + } + else + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler2D sampler%d;\n", i ); + } + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_3D ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform sampler3D sampler%d;\n", i ); + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] == SAMPLER_TYPE_CUBE ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform samplerCube sampler%d;\n", i ); + ++nSamplersWritten; + } + else if ( m_dwSamplerTypes[i] != SAMPLER_TYPE_UNUSED ) + { + Error( "Unknown sampler type." ); + } + } + + if ( nSamplersWritten > 0 ) + PrintToBuf( *m_pBufHeaderCode, "\n\n" ); +} + +void D3DToGL::WriteGLSLOutputVariableAssignments() +{ + if ( m_bVertexShader ) + { + // Map output "oN" registers back to GLSL output variables. + if ( m_bAddHexCodeComments ) + { + PrintToBuf( *m_pBufAttribCode, "\n// Now we're storing the oN variables from the output dcl_ statements back into their GLSL equivalents.\n" ); + } + + for ( int i=0; i < ARRAYSIZE( m_DeclaredOutputs ); i++ ) + { + if ( m_DeclaredOutputs[i] == UNDECLARED_OUTPUT ) + continue; + + if ( ( m_dwTexCoordOutMask & ( 1 << i ) ) == 0 ) + continue; + + uint32 dwToken = m_DeclaredOutputs[i]; + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + if ( ( dwUsage == D3DDECLUSAGE_FOG ) || ( dwUsage == D3DDECLUSAGE_PSIZE ) ) + { + TranslationError(); // Not supported yet, but can be if we need it. + } + + if ( dwUsage == D3DDECLUSAGE_COLOR ) + { + PrintToBufWithIndents( *m_pBufALUCode, "%s = oTempT%d;\n", dwUsageIndex ? "gl_FrontSecondaryColor" : "gl_FrontColor", i ); + } + else if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + char buf[256]; + if ( m_nCentroidMask & ( 0x00000001 << dwUsageIndex ) ) + { + V_snprintf( buf, sizeof( buf ), "centroid varying vec4 oT%d;\n", dwUsageIndex ); // centroid varying + } + else + { + V_snprintf( buf, sizeof( buf ), "varying vec4 oT%d;\n", dwUsageIndex ); + } + StrcatToHeaderCode( buf ); + + PrintToBufWithIndents( *m_pBufALUCode, "oT%d = oTempT%d;\n", dwUsageIndex, i ); + } + } + } +} + +void D3DToGL::WriteGLSLInputVariableAssignments() +{ + if ( m_bVertexShader ) + return; + + for ( int i=0; i < ARRAYSIZE( m_DeclaredInputs ); i++ ) + { + if ( m_DeclaredInputs[i] == UNDECLARED_INPUT ) + continue; + + uint32 dwToken = m_DeclaredInputs[i]; + + uint32 dwUsage = ( dwToken & D3DSP_DCL_USAGE_MASK ); + uint32 dwUsageIndex = ( dwToken & D3DSP_DCL_USAGEINDEX_MASK ) >> D3DSP_DCL_USAGEINDEX_SHIFT; + + if ( dwUsage == D3DDECLUSAGE_COLOR ) + { + PrintToBufWithIndents( *m_pBufAttribCode, "vec4 oTempT%d = %s;\n", i, dwUsageIndex ? "gl_SecondaryColor" : "gl_Color" ); + } + else if ( dwUsage == D3DDECLUSAGE_TEXCOORD ) + { + PrintToBufWithIndents( *m_pBufAttribCode, "vec4 oTempT%d = oT%d;\n", i, dwUsageIndex ); + } + } +} + +void D3DToGL::Handle_DeclarativeNonDclOp( uint32 nInstruction ) +{ + char buff[128]; + uint32 dwToken = GetNextToken(); + PrintParameterToString( dwToken, DST_REGISTER, buff, sizeof( buff ), false, NULL ); + + if ( nInstruction == D3DSIO_TEXKILL ) + { + // TEXKILL is supposed to discard the pixel if any of the src register's X, Y, or Z components are less than zero. + // We have to translate it to something like: + // if ( r0.x < 0.0 || r0.y < 0.0 ) + // discard; + char c[3]; + c[0] = GetSwizzleComponent( buff, 0 ); + c[1] = GetSwizzleComponent( buff, 1 ); + c[2] = GetSwizzleComponent( buff, 2 ); + + // Get the unique components. + char cUnique[3]; + cUnique[0] = c[0]; + + int nUnique = 1; + if ( c[1] != c[0] ) + cUnique[nUnique++] = c[1]; + + if ( c[2] != c[1] && c[2] != c[0] ) + cUnique[nUnique++] = c[2]; + + // Get the src register base name. + char szBase[256]; + GetParamNameWithoutSwizzle( buff, szBase, sizeof( szBase ) ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s.%c < 0.0 ", szBase, cUnique[0] ); + for ( int i=1; i < nUnique; i++ ) + { + PrintToBuf( *m_pBufALUCode, "|| %s.%c < 0.0 ", szBase, cUnique[i] ); + } + PrintToBuf( *m_pBufALUCode, ")\n{\n\tdiscard;\n}\n" ); + } + else + { + char szOpcode[128]; + PrintOpcode( nInstruction, szOpcode, sizeof( szOpcode ) ); + StrcatToALUCode( szOpcode ); + + StrcatToALUCode( buff ); + StrcatToALUCode( ";\n" ); + } +} + + +void D3DToGL::NoteTangentInputUsed() +{ + if ( !m_bTangentInputUsed ) + { + m_bTangentInputUsed = true; +// PrintToBuf( *m_pBufParamCode, "attribute vec4 %s;\n", g_pTangentAttributeName ); + } +} + + +// These are the only ARL instructions that should appear in the instruction stream +void D3DToGL::InsertMoveInstruction( CUtlBuffer *pCode, int nARLComponent ) +{ + PrintIndentation( ( char * )pCode->Base(), pCode->Size() ); + + switch ( nARLComponent ) + { + case ARL_DEST_X: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.x );\n" ); + break; + case ARL_DEST_Y: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.y );\n" ); + break; + case ARL_DEST_Z: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.z );\n" ); + break; + case ARL_DEST_W: + strcat_s( ( char * )pCode->Base(), pCode->Size(), "a0 = int( va_r.w );\n" ); + break; + } +} + +// This optionally inserts a move from our dummy address register to the .x component of the real one +void D3DToGL::InsertMoveFromAddressRegister( CUtlBuffer *pCode, int nARLComp0, int nARLComp1, int nARLComp2 /* = ARL_DEST_NONE */ ) +{ + // We no longer need to do this in GLSL - we put the cast to int from the dummy address register va_r.x, va_r.y, etc. directly into the instruction + return; +} + + +//------------------------------------------------------------------------------ +// TranslateShader() +// +// This is the main function that the outside world sees. A pointer to the +// uint32 stream returned from the D3DX compile routine is parsed and used +// to write human-readable asm code into the character array pointed to by +// pDisassembledCode. An error code is returned. +//------------------------------------------------------------------------------ + + +int D3DToGL::TranslateShader( uint32* code, CUtlBuffer *pBufDisassembledCode, bool *bVertexShader, uint32 options, int32 nShadowDepthSamplerMask, uint32 nCentroidMask, char *debugLabel ) +{ + CUtlString sLine, sParamName; + uint32 i, dwToken, nInstruction, nNumTokensToSkip; + char buff[256]; + + // obey options + m_bUseEnvParams = (options & D3DToGL_OptionUseEnvParams) != 0; + m_bDoFixupZ = (options & D3DToGL_OptionDoFixupZ) != 0; + m_bDoFixupY = (options & D3DToGL_OptionDoFixupY) != 0; + m_bDoUserClipPlanes = (options & D3DToGL_OptionDoUserClipPlanes) != 0; + + m_bAddHexCodeComments = (options & D3DToGL_AddHexComments) != 0; + m_bPutHexCodesAfterLines = (options & D3DToGL_PutHexCommentsAfterLines) != 0; + m_bGeneratingDebugText = (options & D3DToGL_GeneratingDebugText) != 0; + m_bGenerateSRGBWriteSuffix = (options & D3DToGL_OptionSRGBWriteSuffix) != 0; + + m_NumIndentTabs = 1; // start code indented one tab + m_nLoopDepth = 0; + + // debugging + m_bSpew = (options & D3DToGL_OptionSpew) != 0; + + // These are not accessed below in a way that will cause them to glow, so + // we could overflow these and/or the buffer pointed to by pDisassembledCode + m_pBufAttribCode = new CUtlBuffer( 100, 10000, CUtlBuffer::TEXT_BUFFER ); + m_pBufParamCode = new CUtlBuffer( 100, 10000, CUtlBuffer::TEXT_BUFFER ); + m_pBufALUCode = new CUtlBuffer( 100, 60000, CUtlBuffer::TEXT_BUFFER ); + + // Pointers to text buffers for assembling sections of the program + m_pBufHeaderCode = pBufDisassembledCode; + char *pAttribMapStart = NULL; + ((char*)m_pBufHeaderCode->Base())[0] = 0; + ((char*)m_pBufAttribCode->Base())[0] = 0; + ((char*)m_pBufParamCode->Base())[0] = 0; + ((char*)m_pBufALUCode->Base())[0] = 0; + + + for ( i=0; i<MAX_SHADER_CONSTANTS; i++ ) + { + m_bConstantRegisterDefined[i] = false; + } + + // Track shadow sampler usage for proper declaration + m_nShadowDepthSamplerMask = nShadowDepthSamplerMask; + m_bDeclareShadowOption = false; + + // Various flags set while parsing code to drive various declaration instructions + m_bNeedsD2AddTemp = false; + m_bNeedsLerpTemp = false; + m_bNeedsNRMTemp = false; + m_bNeedsSinCosDeclarations = false; + m_bDeclareAddressReg = false; + m_bDeclareVSOPos = false; + m_bDeclareVSOFog = false; + m_dwTexCoordOutMask = 0x00000000; + m_nVSPositionOutput = -1; + m_bOutputColorRegister[0] = false; + m_bOutputColorRegister[1] = false; + m_bOutputColorRegister[2] = false; + m_bOutputColorRegister[3] = false; + m_bOutputDepthRegister = false; + m_bTangentInputUsed = false; + m_bUsesDSTInstruction = false; + m_dwTempUsageMask = 0x00000000; + m_dwSamplerUsageMask = 0x00000000; + m_dwConstIntUsageMask = 0x00000000; + m_dwDefConstIntUsageMask = 0x00000000; + memset( m_dwDefConstIntIterCount, 0, sizeof( m_dwDefConstIntIterCount ) ); + m_dwConstBoolUsageMask = 0x00000000; + m_nCentroidMask = nCentroidMask; + m_nHighestRegister = 0; + m_nHighestBoneRegister = -1; + m_bGenerateBoneUniformBuffer = false; + m_bUseBindlessTexturing = ((options & D3DToGL_OptionUseBindlessTexturing) != 0); + + m_bUsedAtomicTempVar = false; + for ( int i=0; i < ARRAYSIZE( m_dwSamplerTypes ); i++ ) + { + m_dwSamplerTypes[i] = SAMPLER_TYPE_UNUSED; + } + + for ( int i=0; i < ARRAYSIZE( m_DeclaredOutputs ); i++ ) + { + m_DeclaredOutputs[i] = UNDECLARED_OUTPUT; + } + + for ( int i=0; i < ARRAYSIZE( m_DeclaredInputs ); i++ ) + { + m_DeclaredInputs[i] = UNDECLARED_INPUT; + } + + memset( m_dwAttribMap, 0xFF, sizeof(m_dwAttribMap) ); + + m_pdwBaseToken = m_pdwNextToken = code; // Initialize dwToken pointers + + dwToken = GetNextToken(); + m_dwMajorVersion = D3DSHADER_VERSION_MAJOR( dwToken ); + m_dwMinorVersion = D3DSHADER_VERSION_MINOR( dwToken ); + + // If pixel shader + const char *glslExtText = "#extension GL_ARB_shader_texture_lod : require\n";//m_bUseBindlessTexturing ? "#extension GL_NV_bindless_texture : require\n" : ""; + // 7ls + const char *glslVersionText = m_bUseBindlessTexturing ? "330 compatibility" : "120"; + + if ( ( dwToken & 0xFFFF0000 ) == 0xFFFF0000 ) + { + // must explicitly enable extensions if emitting GLSL + V_snprintf( (char *)m_pBufHeaderCode->Base(), m_pBufHeaderCode->Size(), "#version %s\n%s", glslVersionText, glslExtText ); + m_bVertexShader = false; + } + else // vertex shader + { + m_bGenerateSRGBWriteSuffix = false; + + V_snprintf( (char *)m_pBufHeaderCode->Base(), m_pBufHeaderCode->Size(), "#version %s\n%s//ATTRIBMAP-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx\n", glslVersionText, glslExtText ); + + // find that first '-xx' which is where the attrib map will be written later. + pAttribMapStart = strstr( (char *)m_pBufHeaderCode->Base(), "-xx" ) + 1; + + m_bVertexShader = true; + } + + *bVertexShader = m_bVertexShader; + + m_bGenerateBoneUniformBuffer = m_bVertexShader && ((options & D3DToGL_OptionGenerateBoneUniformBuffer) != 0); + + if ( m_bAddHexCodeComments ) + { + RecordInputAndOutputPositions(); + } + + if ( m_bSpew ) + { + printf("\n************* translating shader " ); + } + + int opcounter = 0; + + // Loop until we hit the end dwToken...note that D3DPS_END() == D3DVS_END() so this works for either + while ( dwToken != D3DPS_END() ) + { + if ( m_bAddHexCodeComments ) + { + AddTokenHexCode(); + RecordInputAndOutputPositions(); + } + +#ifdef POSIX + int tokenIndex = m_pdwNextToken - code; +#endif + int aluCodeLength0 = V_strlen( (char *) m_pBufALUCode->Base() ); + + dwToken = GetNextToken(); // Get next dwToken in the stream + nInstruction = Opcode( dwToken ); // Mask out the instruction opcode + + if ( m_bSpew ) + { +#ifdef POSIX + printf("\n** token# %04x inst# %04d opcode %s (%08x)", tokenIndex, opcounter, GLMDecode(eD3D_SIO, nInstruction), dwToken ); +#endif + opcounter++; + } + + switch ( nInstruction ) + { + // -- No arguments at all ----------------------------------------------- + case D3DSIO_NOP: + // D3D compiler outputs NOPs when shader debugging/optimizations are disabled. + break; + + case D3DSIO_PHASE: + case D3DSIO_RET: + case D3DSIO_ENDLOOP: + case D3DSIO_BREAK: + TranslationError(); + PrintOpcode( nInstruction, buff, sizeof( buff ) ); + StrcatToALUCode( buff ); + StrcatToALUCode( ";\n" ); + break; + + // -- "Declarative" non dcl ops ---------------------------------------- + case D3DSIO_TEXDEPTH: + case D3DSIO_TEXKILL: + Handle_DeclarativeNonDclOp( nInstruction ); + break; + + // -- Unary ops ------------------------------------------------- + case D3DSIO_BEM: + case D3DSIO_TEXBEM: + case D3DSIO_TEXBEML: + case D3DSIO_TEXDP3: + case D3DSIO_TEXDP3TEX: + case D3DSIO_TEXM3x2DEPTH: + case D3DSIO_TEXM3x2TEX: + case D3DSIO_TEXM3x3: + case D3DSIO_TEXM3x3PAD: + case D3DSIO_TEXM3x3TEX: + case D3DSIO_TEXM3x3VSPEC: + case D3DSIO_TEXREG2AR: + case D3DSIO_TEXREG2GB: + case D3DSIO_TEXREG2RGB: + case D3DSIO_LABEL: + case D3DSIO_CALL: + case D3DSIO_LOOP: + case D3DSIO_BREAKP: + case D3DSIO_DSX: + case D3DSIO_DSY: + TranslationError(); + break; + + case D3DSIO_IFC: + { + static const char *s_szCompareStrings[ 7 ] = + { + "__INVALID__", + ">", + "==", + ">=", + "<", + "!=", + "<=" + }; + + // Compare mode is encoded in instruction token + uint32 dwCompareMode = OpcodeSpecificData( dwToken ); + + Assert( ( dwCompareMode >= 1 ) && ( dwCompareMode <= 6 ) ); + + // Get left side of compare + dwToken = GetNextToken(); + char szLeftSide[32]; + PrintParameterToString( dwToken, SRC_REGISTER, szLeftSide, sizeof( szLeftSide ), false, NULL ); + + // Get right side of compare + dwToken = GetNextToken(); + char szRightSide[32]; + PrintParameterToString( dwToken, SRC_REGISTER, szRightSide, sizeof( szRightSide ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s %s %s )\n", szLeftSide, s_szCompareStrings[dwCompareMode], szRightSide ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + } + case D3DSIO_IF: + dwToken = GetNextToken(); + PrintParameterToString( dwToken, SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + + PrintToBufWithIndents( *m_pBufALUCode, "if ( %s )\n", buff ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + + case D3DSIO_ELSE: + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + StrcatToALUCode( "else\n" ); + StrcatToALUCode( "{\n" ); + m_NumIndentTabs++; + + break; + + case D3DSIO_ENDIF: + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + + break; + + case D3DSIO_REP: + dwToken = GetNextToken(); + PrintParameterToString( dwToken, SRC_REGISTER, buff, sizeof( buff ), false, NULL ); + + // In practice, this is the only form of for loop that will appear in DX asm + PrintToBufWithIndents( *m_pBufALUCode, "for( int i=0; i < %s; i++ )\n", buff ); + StrcatToALUCode( "{\n" ); + + m_nLoopDepth++; + + // For now, we don't deal with loop nesting + // Easy enough to fix later with an array of loop names i, j, k etc + Assert( m_nLoopDepth <= 1 ); + + m_NumIndentTabs++; + + break; + + case D3DSIO_ENDREP: + m_nLoopDepth--; + m_NumIndentTabs--; + StrcatToALUCode( "}\n" ); + + break; + + case D3DSIO_NRM: + Handle_NRM(); + break; + + case D3DSIO_MOVA: + + Handle_UnaryOp( nInstruction ); + + break; + + // Unary operations + case D3DSIO_MOV: + case D3DSIO_RCP: + case D3DSIO_RSQ: + case D3DSIO_EXP: + case D3DSIO_EXPP: + case D3DSIO_LOG: + case D3DSIO_LOGP: + case D3DSIO_FRC: + case D3DSIO_LIT: + case D3DSIO_ABS: + Handle_UnaryOp( nInstruction ); + break; + + // -- Binary ops ------------------------------------------------- + case D3DSIO_TEXM3x3SPEC: + case D3DSIO_M4x4: + case D3DSIO_M4x3: + case D3DSIO_M3x4: + case D3DSIO_M3x3: + case D3DSIO_M3x2: + case D3DSIO_CALLNZ: + case D3DSIO_SETP: + TranslationError(); + break; + + case D3DSIO_BREAKC: + Handle_BREAKC( dwToken ); + break; + + // Binary Operations + case D3DSIO_ADD: + case D3DSIO_SUB: + case D3DSIO_MUL: + case D3DSIO_DP3: + case D3DSIO_DP4: + case D3DSIO_MIN: + case D3DSIO_MAX: + case D3DSIO_DST: + case D3DSIO_SLT: + case D3DSIO_SGE: + case D3DSIO_CRS: + case D3DSIO_POW: + HandleBinaryOp_GLSL( nInstruction ); + + break; + + // -- Ternary ops ------------------------------------------------- + case D3DSIO_DP2ADD: + Handle_DP2ADD(); + break; + case D3DSIO_LRP: + Handle_LRP( nInstruction ); + break; + case D3DSIO_SGN: + Assert( m_bVertexShader ); + TranslationError(); // TODO emulate with SLT etc + break; + case D3DSIO_CND: + TranslationError(); + break; + case D3DSIO_CMP: + Handle_CMP(); + break; + case D3DSIO_SINCOS: + Handle_SINCOS(); + break; + case D3DSIO_MAD: + Handle_MAD( nInstruction ); + break; + + // -- Quaternary op ------------------------------------------------ + case D3DSIO_TEXLDD: + Handle_TexLDD( nInstruction ); + break; + + // -- Special cases: texcoord vs texcrd and tex vs texld ----------- + case D3DSIO_TEXCOORD: + Handle_TexCoord(); + break; + + case D3DSIO_TEX: + Handle_TEX( dwToken, false ); + break; + + case D3DSIO_TEXLDL: + Handle_TEX( nInstruction, true ); + break; + + case D3DSIO_DCL: + Handle_DCL(); + break; + + case D3DSIO_DEFB: + case D3DSIO_DEFI: + Handle_DEFIB( nInstruction ); + break; + + case D3DSIO_DEF: + Handle_DEF(); + break; + + case D3DSIO_COMMENT: + // Using OpcodeSpecificData() can fail here since the comments can be longer than 0xff dwords + nNumTokensToSkip = ( dwToken & 0x0fff0000 ) >> 16; + SkipTokens( nNumTokensToSkip ); + break; + + case D3DSIO_END: + break; + } + + if ( m_bSpew ) + { + int aluCodeLength1 = V_strlen( (char *) m_pBufALUCode->Base() ); + if ( aluCodeLength1 != aluCodeLength0 ) + { + // code was emitted + printf( "\n > %s", ((char *)m_pBufALUCode->Base()) + aluCodeLength0 ); + + aluCodeLength0 = aluCodeLength1; + } + } + } + + // Note that this constant packing expects .wzyx swizzles in case we ever use the SINCOS code in a ps_2_x shader + // + // The Microsoft documentation on this is all kinds of broken and, strangely, these numbers don't even + // match the D3DSINCOSCONST1 and D3DSINCOSCONST2 constants used by the D3D assembly sincos instruction... + if ( m_bNeedsSinCosDeclarations ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 scA = vec4( -1.55009923e-6, -2.17013894e-5, 0.00260416674, 0.00026041668 );\n" ); + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 scB = vec4( -0.020833334, -0.125, 1.0, 0.5 );\n" ); + } + + // Stick in the sampler mask in hex + PrintToBuf( *m_pBufHeaderCode, "%sSAMPLERMASK-%x\n", "//", m_dwSamplerUsageMask ); + + uint nSamplerTypes = 0; + for ( int i = 0; i < 16; i++ ) + { + Assert( m_dwSamplerTypes[i] < 4); + nSamplerTypes |= ( m_dwSamplerTypes[i] << ( i * 2 ) ); + } + + PrintToBuf( *m_pBufHeaderCode, "%sSAMPLERTYPES-%x\n", "//", nSamplerTypes ); + + // fragData outputs referenced + uint nFragDataMask = 0; + for ( int i = 0; i < 4; i++ ) + { + nFragDataMask |= m_bOutputColorRegister[ i ] ? ( 1 << i ) : 0; + } + + PrintToBuf( *m_pBufHeaderCode, "%sFRAGDATAMASK-%x\n", "//", nFragDataMask ); + + // Uniforms + + PrintToBuf( *m_pBufHeaderCode, "//HIGHWATER-%d\n", m_nHighestRegister + 1 ); + if ( ( m_bVertexShader ) && ( m_bGenerateBoneUniformBuffer ) ) + { + PrintToBuf( *m_pBufHeaderCode, "//HIGHWATERBONE-%i\n", m_nHighestBoneRegister + 1 ); + } + + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 %s[%d];\n", m_bVertexShader ? "vc" : "pc", m_nHighestRegister + 1 ); + + if ( ( m_nHighestBoneRegister >= 0 ) && ( m_bVertexShader ) && ( m_bGenerateBoneUniformBuffer ) ) + { + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 %s[%d];\n", "vcbones", m_nHighestBoneRegister + 1 ); + } + + if ( m_bVertexShader ) + { + PrintToBuf( *m_pBufHeaderCode, "\nuniform vec4 vcscreen;\n" ); + } + + for( int i=0; i<32; i++ ) + { + if ( ( m_dwConstIntUsageMask & ( 0x00000001 << i ) ) && + ( !( m_dwDefConstIntUsageMask & ( 0x00000001 << i ) ) ) + ) + { + PrintToBuf( *m_pBufHeaderCode, "uniform int i%d ;\n", i ); + } + } + + for( int i=0; i<32; i++ ) + { + if ( m_dwDefConstIntUsageMask & ( 0x00000001 << i ) ) + { + PrintToBuf( *m_pBufHeaderCode, "const int i%d = %i;\n", i, m_dwDefConstIntIterCount[i] ); + } + } + + for( int i=0; i<32; i++ ) + { + if ( m_dwConstBoolUsageMask & ( 0x00000001 << i ) ) + { + PrintToBuf( *m_pBufHeaderCode, m_bVertexShader ? "uniform bool b%d;\n" : "uniform bool fb%d;\n", i ); + } + } + + // Control bit for sRGB Write suffix + if ( m_bGenerateSRGBWriteSuffix ) + { + // R500 Hookup + // Set this guy to 1 when the sRGBWrite state is true, otherwise 0 + StrcatToHeaderCode( "uniform float flSRGBWrite;\n" ); + } + + PrintToBuf( *m_pBufHeaderCode, "\n" ); + + // Write samplers + WriteGLSLSamplerDefinitions(); + + if ( m_bUsesDSTInstruction ) + { + PrintToBuf( *m_pBufHeaderCode, "vec4 dst(vec4 src0,vec4 src1) { return vec4(1.0f,src0.y*src1.y,src0.z,src1.w); }\n" ); + } + + if ( m_bDeclareAddressReg ) + { + if ( !m_bGenerateBoneUniformBuffer ) + { + m_nHighestRegister = DXABSTRACT_VS_PARAM_SLOTS - 1; + } + + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 va_r;\n" ); + } + + char *pTempVarStr = "TEMP"; + pTempVarStr = "vec4"; + + // Declare temps in Param code buffer + for( int i=0; i<32; i++ ) + { + if ( m_dwTempUsageMask & ( 0x00000001 << i ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + PrintToBuf( *m_pBufParamCode, "%s r%d;\n", pTempVarStr, i ); + } + } + + if ( m_bVertexShader && (m_bDoUserClipPlanes || m_bDoFixupZ || m_bDoFixupY ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + StrcatToParamCode( "vec4 vTempPos;\n" ); + } + + if ( ( m_bVertexShader ) && ( m_dwMajorVersion == 3 ) ) + { + for ( int i = 0; i < 32; i++ ) + { + if ( m_dwTexCoordOutMask & ( 1 << i ) ) + { + PrintIndentation( (char*)m_pBufParamCode->Base(), m_pBufParamCode->Size() ); + + char buf[256]; + V_snprintf( buf, sizeof( buf ), "vec4 oTempT%i = vec4( 0, 0, 0, 0 );\n", i ); + StrcatToParamCode( buf ); + } + } + } + + if ( m_bNeedsSinCosDeclarations ) + { + StrcatToParamCode( "vec3 vSinCosTmp;\n" ); // declare temp used by GLSL sin and cos intrinsics + } + + // Optional temps needed to emulate d2add instruction in DX pixel shaders + if ( m_bNeedsD2AddTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s DP2A0;\n%s DP2A1;\n", pTempVarStr, pTempVarStr ); + } + + // Optional temp needed to emulate lerp instruction in DX vertex shaders + if ( m_bNeedsLerpTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s LRP_TEMP;\n", pTempVarStr ); + } + + // Optional temp needed to emulate NRM instruction in DX shaders + if ( m_bNeedsNRMTemp ) + { + PrintToBuf( *m_pBufParamCode, "%s NRM_TEMP;\n", pTempVarStr ); + } + + if ( m_bDeclareVSOPos && m_bVertexShader ) + { + if ( m_bDoUserClipPlanes ) + { + StrcatToALUCode( "gl_ClipVertex = vTempPos;\n" ); // if user clip is enabled, jam clip space position into gl_ClipVertex + } + + if ( m_bDoFixupZ || m_bDoFixupY ) + { + // TODO: insert clip distance computation something like this: + // + // StrcatToALUCode( "DP4 oCLP[0].x, oPos, vc[215]; \n" ); + // + + if ( m_bDoFixupZ ) + { + StrcatToALUCode( "vTempPos.z = vTempPos.z * vc[0].z - vTempPos.w; // z' = (2*z)-w\n" ); + } + + if ( m_bDoFixupY ) + { + // append instructions to flip Y over + // new Y = -(old Y) + StrcatToALUCode( "vTempPos.y = -vTempPos.y; // y' = -y \n" ); + } + + // Apply half pixel offset (0.5f pixel offset D3D) to output vertices to account for the pixel center difference between D3D9 and OpenGL. + // This is the actual work in the shader. This works out to be 0.5 pixels wide because clip space is 2 units wide (-1, 1). + StrcatToALUCode( "vTempPos.xy += vcscreen.xy * vTempPos.w;\n" ); + + StrcatToALUCode( "gl_Position = vTempPos;\n" ); + } + else + { + StrcatToParamCode( "OUTPUT oPos = result.position;\n" ); + + // TODO: insert clip distance computation something like this: + // + // StrcatToALUCode( "DP4 oCLP[0].x, oPos, c[215]; \n" ); + // + } + } + + if ( m_bVertexShader ) + { + if ( m_dwMajorVersion == 3 ) + { + WriteGLSLOutputVariableAssignments(); + } + else + { + for ( int i=0; i<32; i++ ) + { + char outTexCoordBuff[64]; + + // Don't declare a varying for the output that is mapped to the position output + if ( i != m_nVSPositionOutput ) + { + if ( m_dwTexCoordOutMask & ( 0x00000001 << i ) ) + { + if ( m_nCentroidMask & ( 0x00000001 << i ) ) + { + V_snprintf( outTexCoordBuff, sizeof( outTexCoordBuff ), "centroid varying vec4 oT%d;\n", i ); // centroid varying + StrcatToHeaderCode( outTexCoordBuff ); + } + else + { + V_snprintf( outTexCoordBuff, sizeof( outTexCoordBuff ), "varying vec4 oT%d;\n", i ); + StrcatToHeaderCode( outTexCoordBuff ); + } + } + } + } + } + } + else + { + if ( m_dwMajorVersion == 3 ) + { + WriteGLSLInputVariableAssignments(); + } + } + + // do some annotation at the end of the attrib block + { + char temp[1000]; + + if ( m_bVertexShader ) + { + // write attrib map into the text starting at pAttribMapStart - two hex digits per attrib + for( int i=0; i<16; i++ ) + { + if ( m_dwAttribMap[i] != 0xFFFFFFFF ) + { + V_snprintf( temp, sizeof(temp), "%02X", m_dwAttribMap[i] ); + memcpy( pAttribMapStart + (i*3), temp, 2 ); + } + } + } + + PrintIndentation( (char*)m_pBufAttribCode->Base(), m_pBufAttribCode->Size() ); + + // This used to write out a translation counter into the shader as a comment. However, the order that shaders get in here + // is non-deterministic between runs, and the change in this comment would cause shaders to appear different to the GL disk cache, + // significantly increasing app load time. + // Other code looks for trans#%d, so we can't just remove it. Instead, output it as 0. + V_snprintf( temp, sizeof(temp), "%s trans#%d label:%s\n", "//", 0, debugLabel ? debugLabel : "none" ); + StrcatToAttribCode( temp ); + } + + // If we actually sample from a shadow depth sampler, we need to declare the shadow option at the top + if ( m_bDeclareShadowOption ) + { + StrcatToHeaderCode( "OPTION ARB_fragment_program_shadow;\n" ); + } + + StrcatToHeaderCode( "\nvoid main()\n{\n" ); + if ( m_bUsedAtomicTempVar ) + { + PrintToBufWithIndents( *m_pBufHeaderCode, "vec4 %s;\n\n", g_pAtomicTempVarName ); + } + + // sRGB Write suffix + if ( m_bGenerateSRGBWriteSuffix ) + { + StrcatToALUCode( "vec3 sRGBFragData;\n" ); + StrcatToALUCode( "sRGBFragData.xyz = log( gl_FragData[0].xyz );\n" ); + StrcatToALUCode( "sRGBFragData.xyz = sRGBFragData.xyz * vec3( 0.454545f, 0.454545f, 0.454545f );\n" ); + StrcatToALUCode( "sRGBFragData.xyz = exp( sRGBFragData.xyz );\n" ); + StrcatToALUCode( "gl_FragData[0].xyz = mix( gl_FragData[0].xyz, sRGBFragData, flSRGBWrite );\n" ); + } + + strcat_s( (char*)m_pBufALUCode->Base(), m_pBufALUCode->Size(), "}\n" ); + + // Put all of the strings together for final program ( pHeaderCode + pAttribCode + pParamCode + pALUCode ) + StrcatToHeaderCode( (char*)m_pBufAttribCode->Base() ); + StrcatToHeaderCode( (char*)m_pBufParamCode->Base() ); + StrcatToHeaderCode( (char*)m_pBufALUCode->Base() ); + + // Cleanup - don't touch m_pBufHeaderCode, as it is managed by the caller + delete m_pBufAttribCode; + delete m_pBufParamCode; + delete m_pBufALUCode; + m_pBufAttribCode = m_pBufParamCode = m_pBufALUCode = NULL; + + if ( m_bSpew ) + { + printf("\n************* translation complete\n\n " ); + } + + return DISASM_OK; +} diff --git a/togl/linuxwin/dx9asmtogl2.h b/togl/linuxwin/dx9asmtogl2.h new file mode 100644 index 0000000..2ab7e09 --- /dev/null +++ b/togl/linuxwin/dx9asmtogl2.h @@ -0,0 +1,261 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------ +// DX9AsmToGL2.h +//------------------------------------------------------------------------------ + +#ifndef DX9_ASM_TO_GL_2_H +#define DX9_ASM_TO_GL_2_H +#include "tier1/utlstring.h" + +#define DISASM_OK 0 +#define DISASM_ERROR 1 + +#define MAX_SHADER_CONSTANTS 512 + +#define MAX_DECLARED_OUTPUTS 32 +#define MAX_DECLARED_INPUTS 32 + +#define HEXCODE_HEADER "// Hex: " + +// Option bits +#define D3DToGL_OptionUseEnvParams 0x0001 +#define D3DToGL_OptionDoFixupZ 0x0002 // Add instructions to put Z in the right interval for GL +#define D3DToGL_OptionDoFixupY 0x0004 // Add instructions to flip the Y over for GL +#define D3DToGL_OptionDoUserClipPlanes 0x0008 // ARB mode: Include OPTION vertex_program_2 and append DP4's to write into oCLP[0] and oCLP[1] + // GLSL mode: generate code to write gl_ClipVertex + +#define D3DToGL_AddHexComments 0x0020 // Include hex comments in the code for debugging +#define D3DToGL_PutHexCommentsAfterLines 0x0040 // If D3DToGL_AddHexComments is set, this puts the codes to the right, rather than on separate lines +#define D3DToGL_GeneratingDebugText 0x0080 // This tells it that we're just getting info for debugging so go easy on asserts and errors +#define D3DToGL_OptionSRGBWriteSuffix 0x0400 // Tack sRGB conversion suffix on to pixel shaders +#define D3DToGL_OptionGenerateBoneUniformBuffer 0x0800 // if enabled, the vertex shader "bone" registers (all regs DXABSTRACT_VS_FIRST_BONE_SLOT and higher) will be separated out into another uniform buffer (vcbone) +#define D3DToGL_OptionUseBindlessTexturing 0x1000 +#define D3DToGL_OptionSpew 0x80000000 + +// Code for which component of the "dummy" address register is needed by an instruction +#define ARL_DEST_NONE -1 +#define ARL_DEST_X 0 +#define ARL_DEST_Y 1 +#define ARL_DEST_Z 2 +#define ARL_DEST_W 3 + +class D3DToGL +{ +private: + // Pointers for dwToken stream management + uint32* m_pdwBaseToken; + uint32* m_pdwNextToken; + + // Vertex shader or pixel shader, and version (necessary because some opcodes alias) + bool m_bVertexShader; + uint32 m_dwMinorVersion; + uint32 m_dwMajorVersion; + + // Option flags + bool m_bUseEnvParams; // set D3DToGL_OptionUseEnvParams in 'options' to use + bool m_bDoFixupZ; // set D3DToGL_OptionDoFixupZ + bool m_bDoFixupY; // set D3DToGL_OptionDoFixupZ + bool m_bDoUserClipPlanes; // set D3DToGL_OptionDoUserClipPlanes + bool m_bSpew; // set D3DToGL_OptionSpew + bool m_bGenerateSRGBWriteSuffix; // set D3DToGL_OptionSRGBWriteSuffix + bool m_bGenerateBoneUniformBuffer; + bool m_bUseBindlessTexturing; + + // Counter for dealing with nested loops + int m_nLoopDepth; + + // Add "// Hex: 0xFFEEF00"-type statements after each instruction is parsed. + bool m_bAddHexCodeComments; // set D3DToGL_AddHexComments + + // Only applicable if m_bAddHexCodeComments is true. + // If this is true, then it puts the hex code comments to the right of the instructions in a comment + // rather than preceding the instructions. + // Defaults to FALSE. + bool m_bPutHexCodesAfterLines; // set D3DToGL_PutHexCommentsAtEnd + + // This tells it that we're just getting info for debugging so go easy on asserts and errors. + // Defaults to FALSE. + bool m_bGeneratingDebugText; + + // Various scratch temps needed to handle mis-matches in instruction sets between D3D and OpenGL + bool m_bNeedsD2AddTemp; + bool m_bNeedsNRMTemp; + bool m_bDeclareAddressReg; + bool m_bNeedsLerpTemp; + bool m_bNeedsSinCosDeclarations; + + // Keep track of which vs outputs are used so we can declare them + bool m_bDeclareVSOPos; + bool m_bDeclareVSOFog; + uint32 m_dwTexCoordOutMask; + + int32 m_nVSPositionOutput; + + // Mask of varyings which need centroid decoration + uint32 m_nCentroidMask; + + // Keep track of which temps are used so they can be declared + uint32 m_dwTempUsageMask; + uint32 m_dwTempBoolUsageMask; + bool m_bOutputColorRegister[4]; + bool m_bOutputDepthRegister; + + // Declaration of integer and bool constants + uint32 m_dwConstIntUsageMask; + uint32 m_dwConstBoolUsageMask; + + uint32 m_dwDefConstIntUsageMask; + uint32 m_dwDefConstIntIterCount[32]; + + // Did we use atomic_temp_var? + bool m_bUsedAtomicTempVar; + + // Track constants so we know how to declare them + bool m_bConstantRegisterDefined[MAX_SHADER_CONSTANTS]; + + // Track sampler types when declared so we can properly decorate TEX instructions + uint32 m_dwSamplerTypes[32]; + + // Track sampler usage + uint32 m_dwSamplerUsageMask; + + // Track shadow sampler usage + int m_nShadowDepthSamplerMask; + bool m_bDeclareShadowOption; + + // Track attribute references + // init to 0xFFFFFFFF (unhit) + // index by (dwRegToken & D3DSP_REGNUM_MASK) in VS DCL insns + // fill with (usage<<4) | (usage index). + uint32 m_dwAttribMap[16]; + + // Register high water mark + uint32 m_nHighestRegister; + int32 m_nHighestBoneRegister; + + // GLSL does indentation for readability + int m_NumIndentTabs; + + // Output buffers. + CUtlBuffer *m_pBufHeaderCode; + CUtlBuffer *m_pBufAttribCode; + CUtlBuffer *m_pBufParamCode; + CUtlBuffer *m_pBufALUCode; + + char *m_pFinalAssignmentsCode; + int m_nFinalAssignmentsBufSize; + + // Recorded positions for debugging. + uint32* m_pRecordedInputTokenStart; + int m_nRecordedParamCodeStrlen; + int m_nRecordedALUCodeStrlen; + int m_nRecordedAttribCodeStrlen; + + // In GLSL mode, these store the semantic attached to each oN register. + // They are the values that you pass to GetUsageIndexAndString. + uint32 m_DeclaredOutputs[MAX_DECLARED_OUTPUTS]; + + uint32 m_DeclaredInputs[MAX_DECLARED_INPUTS]; + + // Have they used the tangent input semantic (i.e. is g_pTangentAttributeName declared)? + bool m_bTangentInputUsed; + + bool m_bUsesDSTInstruction; + +private: + // Utilities to aid in decoding token stream + uint32 GetNextToken( void ); + void SkipTokens( uint32 numToSkip ); + uint32 Opcode( uint32 dwToken ); + uint32 OpcodeSpecificData( uint32 dwToken ); + uint32 TextureType ( uint32 dwToken ); + uint32 GetRegType( uint32 dwRegToken ); + + // Write to the different buffers. + void StrcatToHeaderCode( const char *pBuf ); + void StrcatToALUCode( const char *pBuf ); + void StrcatToParamCode( const char *pBuf ); + void StrcatToAttribCode( const char *pBuf ); + void PrintToBufWithIndents( CUtlBuffer &buf, const char *pFormat, ... ); + + // This helps write the token hex codes into the output stream for debugging. + void AddTokenHexCodeToBuffer( char *pBuffer, int nSize, int nLastStrlen ); + void RecordInputAndOutputPositions(); + void AddTokenHexCode(); + + // Utilities for decoding tokens in to strings according to ASM syntax + void PrintOpcode( uint32 inst, char* buff, int nBufLen ); + + // fSemanticFlags is SEMANTIC_INPUT or SEMANTIC_OUTPUT. + void PrintUsageAndIndexToString( uint32 dwToken, char* strUsageUsageIndexName, int nBufLen, int fSemanticFlags ); + CUtlString GetUsageAndIndexString( uint32 dwToken, int fSemanticFlags ); + CUtlString GetParameterString( uint32 dwToken, uint32 dwSourceOrDest, bool bForceScalarSource, int *pARLDestReg ); + const char* GetGLSLOperatorString( uint32 inst ); + + void PrintParameterToString ( uint32 dwToken, uint32 dwSourceOrDest, char *pRegisterName, int nBufLen, bool bForceScalarSource, int *pARLDestReg ); + + void InsertMoveFromAddressRegister( CUtlBuffer *pCode, int nARLComp0, int nARLComp1, int nARLComp2 = ARL_DEST_NONE ); + void InsertMoveInstruction( CUtlBuffer *pCode, int nARLComponent ); + void FlagIndirectRegister( uint32 dwToken, int *pARLDestReg ); + + // Utilities for decoding tokens in to strings according to GLSL syntax + bool OpenIntrinsic( uint32 inst, char* buff, int nBufLen, uint32 destDimension, uint32 nArgumentDimension ); + void PrintIndentation( char *pBuf, int nBufLen ); + + uint32 MaintainAttributeMap( uint32 dwToken, uint32 dwRegToken ); + + CUtlString FixGLSLSwizzle( const char *pDestRegisterName, const char *pSrcRegisterName ); + void WriteGLSLCmp( const char *pDestReg, const char *pSrc0Reg, const char *pSrc1Reg, const char *pSrc2Reg ); + void WriteGLSLSamplerDefinitions(); + void WriteGLSLOutputVariableAssignments(); + void WriteGLSLInputVariableAssignments(); + void NoteTangentInputUsed(); + + void Handle_DCL(); + void Handle_DEF(); + void Handle_DEFIB( uint32 nInstruction ); + void Handle_MAD( uint32 nInstruction ); + void Handle_DP2ADD(); + void Handle_SINCOS(); + void Handle_LRP( uint32 nInstruction ); + void Handle_TEX( uint32 dwToken, bool bIsTexLDL ); + void Handle_TexLDD( uint32 nInstruction ); + void Handle_TexCoord(); + void Handle_UnaryOp( uint32 nInstruction ); + void Handle_BREAKC( uint32 dwToken ); + void HandleBinaryOp_GLSL( uint32 nInstruction ); + void HandleBinaryOp_ASM( uint32 nInstruction ); + void Handle_CMP(); + void Handle_NRM(); + void Handle_DeclarativeNonDclOp( uint32 nInstruction ); + +public: + D3DToGL(); + + int TranslateShader( uint32* code, CUtlBuffer *pBufDisassembledCode, bool *bVertexShader, uint32 options, int32 nShadowDepthSamplerMask, uint32 nCentroidMask, char *debugLabel ); +}; + + +#endif // DX9_ASM_TO_GL_2_H diff --git a/togl/linuxwin/dxabstract.cpp b/togl/linuxwin/dxabstract.cpp new file mode 100644 index 0000000..f4459e8 --- /dev/null +++ b/togl/linuxwin/dxabstract.cpp @@ -0,0 +1,6845 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// dxabstract.cpp +// +//================================================================================================== +#include "togl/rendermechanism.h" +#include "tier0/vprof_telemetry.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "tier0/vprof.h" +#include "tier1/strtools.h" +#include "tier1/utlbuffer.h" +#include "dx9asmtogl2.h" +#include "mathlib/vmatrix.h" +#include "materialsystem/IShader.h" + +#include "glmgr_flush.inl" + +#if defined(OSX) || defined(LINUX) || (defined (WIN32) && defined( DX_TO_GL_ABSTRACTION )) + #include "appframework/ilaunchermgr.h" + extern ILauncherMgr *g_pLauncherMgr; +#endif + +#include "tier0/icommandline.h" +#include "tier0/memdbgon.h" + +#ifdef USE_ACTUAL_DX + +#pragma comment( lib, "../../dx9sdk/lib/d3d9.lib" ) +#pragma comment( lib, "../../dx9sdk/lib/d3dx9.lib" ) + +#else + +#ifdef POSIX +#define strcat_s( a, b, c) V_strcat( a, c, b ) +#endif + +#define D3D_DEVICE_VALID_MARKER 0x12EBC845 +#define GL_PUBLIC_ENTRYPOINT_CHECKS( dev ) Assert( dev->GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); Assert( dev->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); +// ------------------------------------------------------------------------------------------------------------------------------ // +bool g_bNullD3DDevice; + +static D3DToGL g_D3DToOpenGLTranslatorGLSL; +static IDirect3DDevice9 *g_pD3D_Device; + +#if GL_BATCH_PERF_ANALYSIS + #include "../../thirdparty/miniz/simple_bitmap.h" + #include "../../thirdparty/miniz/miniz.c" + + ConVar gl_batch_vis_abs_scale( "gl_batch_vis_abs_scale", ".050" ); + ConVar gl_present_vis_abs_scale( "gl_present_vis_abs_scale", ".050" ); + //ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.007" ); + ConVar gl_batch_vis_y_scale( "gl_batch_vis_y_scale", "0.0" ); + static double s_rdtsc_to_ms; + + uint64 g_nTotalD3DCycles; + uint g_nTotalD3DCalls; + + class CGLBatchPerfCallTimer + { + public: + inline CGLBatchPerfCallTimer() { g_nTotalD3DCalls++; g_nTotalD3DCycles -= TM_FAST_TIME(); } + inline ~CGLBatchPerfCallTimer() { g_nTotalD3DCycles += TM_FAST_TIME(); } + }; + + #define GL_BATCH_PERF_CALL_TIMER CGLBatchPerfCallTimer scopedCallTimer; +#else + #define GL_BATCH_PERF_CALL_TIMER +#endif // GL_BATCH_PERF_ANALYSIS + +ConVar gl_batch_vis( "gl_batch_vis", "0" ); + +// ------------------------------------------------------------------------------------------------------------------------------ // +// functions that are dependant on g_pLauncherMgr + +inline GLMDisplayDB *GetDisplayDB( void ) +{ + return g_pLauncherMgr->GetDisplayDB(); +} + +inline void RenderedSize( uint &width, uint &height, bool set ) +{ + g_pLauncherMgr->RenderedSize( width, height, set ); +} + +inline void GetStackCrawl( CStackCrawlParams *params ) +{ + g_pLauncherMgr->GetStackCrawl(params); +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#if defined( WIN32 ) + +bool D3DMATRIX::operator == ( CONST D3DMATRIX& src) const +{ + return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) == 0; +} + +D3DMATRIX::operator void* () +{ + return (void*)this; +} + +#endif + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXMATRIX operators + +#endif + +D3DXMATRIX D3DXMATRIX::operator*( const D3DXMATRIX &o ) const +{ + D3DXMATRIX result; + + D3DXMatrixMultiply( &result, this, &o ); // this = lhs o = rhs result = this * o + + return result; +} + +D3DXMATRIX::operator FLOAT* () +{ + return (float*)this; +} + +float& D3DXMATRIX::operator()( int row, int column ) +{ + return m[row][column]; +} + +const float& D3DXMATRIX::operator()( int row, int column ) const +{ + return m[row][column]; +} + +bool D3DXMATRIX::operator != ( CONST D3DXMATRIX& src ) const +{ + return V_memcmp( (void*)this, (void*)&src, sizeof(this) ) != 0; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXPLANE operators + +#endif + +float& D3DXPLANE::operator[]( int i ) +{ + return ((float*)this)[i]; +} + +bool D3DXPLANE::operator==( const D3DXPLANE &o ) +{ + return a == o.a && b == o.b && c == o.c && d == o.d; +} + +bool D3DXPLANE::operator!=( const D3DXPLANE &o ) +{ + return !( *this == o ); +} + +D3DXPLANE::operator float*() +{ + return (float*)this; +} + +D3DXPLANE::operator const float*() const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR2 operators + +#endif + +D3DXVECTOR2::operator FLOAT* () +{ + return (float*)this; +} + +D3DXVECTOR2::operator CONST FLOAT* () const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR3 operators + +#endif + +D3DXVECTOR3::D3DXVECTOR3( float a, float b, float c ) +{ + x = a; + y = b; + z = c; +} + +D3DXVECTOR3::operator FLOAT* () +{ + return (float*)this; +} + +D3DXVECTOR3::operator CONST FLOAT* () const +{ + return (const float*)this; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- D3DXVECTOR4 operators + +#endif + +D3DXVECTOR4::D3DXVECTOR4( float a, float b, float c, float d ) +{ + x = a; + y = b; + z = c; + w = d; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +DWORD IDirect3DResource9::SetPriority(DWORD PriorityNew) +{ +// DXABSTRACT_BREAK_ON_ERROR(); +// GLMPRINTF(( "-X- SetPriority" )); + // no-op city + return 0; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DBaseTexture9 + +#endif + +IDirect3DBaseTexture9::~IDirect3DBaseTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DBaseTexture9" )); + + if (m_device) + { + Assert( m_device->m_ObjectStats.m_nTotalTextures >= 1 ); + m_device->m_ObjectStats.m_nTotalTextures--; + + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking normal delete path on %08x, device is %08x ", this, m_device )); + m_device->ReleasedTexture( this ); + + if (m_tex) + { + GLMPRINTF(("-A- ~IDirect3DBaseTexture9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); + + m_device->ReleasedCGLMTex( m_tex ); + + m_tex->m_ctx->DelTex( m_tex ); + m_tex = NULL; + } + else + { + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 : whoops, no tex to delete here ?" )); + } + m_device = NULL; // ** THIS ** is the only place to scrub this. Don't do it in the subclass destructors. + } + else + { + GLMPRINTF(( "-A- ~IDirect3DBaseTexture9 taking strange delete path on %08x, device is %08x ", this, m_device )); + } + + GLMPRINTF(( "<-A- ~IDirect3DBaseTexture9" )); +} + +D3DRESOURCETYPE IDirect3DBaseTexture9::GetType() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + return m_restype; //D3DRTYPE_TEXTURE; +} + +DWORD IDirect3DBaseTexture9::GetLevelCount() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + return m_tex->m_layout->m_mipCount; +} + +HRESULT IDirect3DBaseTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + Assert (Level < static_cast<uint>(m_tex->m_layout->m_mipCount)); + + D3DSURFACE_DESC result = m_descZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + + *pDesc = result; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateTexture(UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture9** ppTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + m_ObjectStats.m_nTotalTextures++; + + GLMPRINTF((">-A-IDirect3DDevice9::CreateTexture")); + IDirect3DTexture9 *dxtex = new IDirect3DTexture9; + dxtex->m_restype = D3DRTYPE_TEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_TEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = Width; + dxtex->m_descZero.Height = Height; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_2D; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB | D3DUSAGE_DEPTHSTENCIL); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + // GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + Assert( !(Usage & D3DUSAGE_DEPTHSTENCIL) ); + + m_ObjectStats.m_nTotalRenderTargets++; + + key.m_texFlags |= kGLMTexRenderable; + + const GLMTexFormatDesc *pFmtDesc = GetFormatDesc( key.m_texFormat ); + if ( pFmtDesc->m_glIntFormatSRGB != 0 ) + { + key.m_texFlags |= kGLMTexSRGB; // this catches callers of CreateTexture who set the "renderable" option - they get an SRGB tex + } + + if (m_ctx->Caps().m_cantAttachSRGB) + { + // this config can't support SRGB render targets. quietly turn off the sRGB bit. + key.m_texFlags &= ~kGLMTexSRGB; + } + } + + if ( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ) + { + key.m_texFlags |= kGLMTexIsDepth; + } + if ( Format == D3DFMT_D24S8 ) + { + key.m_texFlags |= kGLMTexIsStencil; + } + + key.m_xSize = Width; + key.m_ySize = Height; + key.m_zSize = 1; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero = new IDirect3DSurface9; + dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... + + // do not do an AddRef here. + + dxtex->m_surfZero->m_device = this; + + dxtex->m_surfZero->m_desc = dxtex->m_descZero; + dxtex->m_surfZero->m_tex = tex; + dxtex->m_surfZero->m_face = 0; + dxtex->m_surfZero->m_mip = 0; + + GLMPRINTF(("-A- IDirect3DDevice9::CreateTexture created '%s' @ %08x (GLM %08x) %s",tex->m_layout->m_layoutSummary, dxtex, tex, pDebugLabel ? pDebugLabel : "" )); + + *ppTexture = dxtex; + + GLMPRINTF(("<-A-IDirect3DDevice9::CreateTexture")); + return S_OK; +} + + +IDirect3DTexture9::~IDirect3DTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMPRINTF(( ">-A- IDirect3DTexture9" )); + + // IDirect3DBaseTexture9::~IDirect3DBaseTexture9 frees up m_tex + // we take care of surfZero + + if (m_device) + { + m_device->ReleasedTexture( this ); + + if (m_surfZero) + { + ULONG refc = m_surfZero->Release( 0, "~IDirect3DTexture9 public release (surfZero)" ); (void)refc; + Assert( !refc ); + m_surfZero = NULL; + } + // leave m_device alone! + } + + GLMPRINTF(( "<-A- IDirect3DTexture9" )); +} + +HRESULT IDirect3DTexture9::LockRect(UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DTexture9::UnlockRect(UINT Level) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DTexture9::GetSurfaceLevel(UINT Level,IDirect3DSurface9** ppSurfaceLevel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // we create and pass back a surface, and the client is on the hook to release it. tidy. + + m_device->m_ObjectStats.m_nTotalSurfaces++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + + // Dicey...higher level code seems to want this and not want this. Are we missing some AddRef/Release behavior elsewhere? + // trying to turn this off - experimental - 26Oct2010 surf->AddRef(); + + surf->m_device = this->m_device; + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + surf->m_desc = m_descZero; + surf->m_desc.Width = slice->m_xSize; + surf->m_desc.Height = slice->m_ySize; + + surf->m_tex = m_tex; + surf->m_face = 0; + surf->m_mip = Level; + + *ppSurfaceLevel = surf; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DCubeTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateCubeTexture(UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + GLMPRINTF((">-A- IDirect3DDevice9::CreateCubeTexture")); + + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalTextures++; + + IDirect3DCubeTexture9 *dxtex = new IDirect3DCubeTexture9; + dxtex->m_restype = D3DRTYPE_CUBETEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_CUBETEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = EdgeLength; + dxtex->m_descZero.Height = EdgeLength; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_CUBE_MAP; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + key.m_texFlags |= kGLMTexRenderable; + + m_ObjectStats.m_nTotalRenderTargets++; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + //GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + key.m_xSize = EdgeLength; + key.m_ySize = EdgeLength; + key.m_zSize = 1; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + for( int face = 0; face < 6; face ++) + { + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero[face] = new IDirect3DSurface9; + dxtex->m_surfZero[face]->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + // do not do an AddRef here. + + dxtex->m_surfZero[face]->m_device = this; + + dxtex->m_surfZero[face]->m_desc = dxtex->m_descZero; + dxtex->m_surfZero[face]->m_tex = tex; + dxtex->m_surfZero[face]->m_face = face; + dxtex->m_surfZero[face]->m_mip = 0; + } + + GLMPRINTF(("-A- IDirect3DDevice9::CreateCubeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); + + *ppCubeTexture = dxtex; + + GLMPRINTF(("<-A- IDirect3DDevice9::CreateCubeTexture")); + + return S_OK; +} + +IDirect3DCubeTexture9::~IDirect3DCubeTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DCubeTexture9" )); + + if (m_device) + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking normal delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); + m_device->ReleasedTexture( this ); + + // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex + // we handle the surfZero array for the faces + + for( int face = 0; face < 6; face ++) + { + if (m_surfZero[face]) + { + Assert( m_surfZero[face]->m_device == m_device ); + ULONG refc = m_surfZero[face]->Release( 0, "~IDirect3DCubeTexture9 public release (surfZero)"); + if ( refc!=0 ) + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 seeing non zero refcount on surfzero[%d] => %d ", face, refc )); + } + m_surfZero[face] = NULL; + } + } + // leave m_device alone! + } + else + { + GLMPRINTF(( "-A- ~IDirect3DCubeTexture9 taking strange delete path on %08x, device is %08x, surfzero[0] is %08x ", this, m_device, m_surfZero[0] )); + } + + GLMPRINTF(( "<-A- ~IDirect3DCubeTexture9" )); +} + +HRESULT IDirect3DCubeTexture9::GetCubeMapSurface(D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface9** ppCubeMapSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // we create and pass back a surface, and the client is on the hook to release it... + + m_device->m_ObjectStats.m_nTotalSurfaces++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = (D3DRESOURCETYPE)0; // 0 is special and means this 'surface' does not own its m_tex + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( FaceType, Level ) ]; + + surf->m_device = this->m_device; + + surf->m_desc = m_descZero; + surf->m_desc.Width = slice->m_xSize; + surf->m_desc.Height = slice->m_ySize; + + surf->m_tex = m_tex; + surf->m_face = FaceType; + surf->m_mip = Level; + + *ppCubeMapSurface = surf; + + return S_OK; +} + +HRESULT IDirect3DCubeTexture9::GetLevelDesc(UINT Level,D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert (Level < static_cast<uint>(m_tex->m_layout->m_mipCount)); + + D3DSURFACE_DESC result = m_descZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + + *pDesc = result; + + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DVolumeTexture9 + +#endif + +HRESULT IDirect3DDevice9::CreateVolumeTexture(UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture9** ppVolumeTexture,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-A- IDirect3DDevice9::CreateVolumeTexture")); + // set dxtex->m_restype to D3DRTYPE_VOLUMETEXTURE... + + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalTextures++; + + IDirect3DVolumeTexture9 *dxtex = new IDirect3DVolumeTexture9; + dxtex->m_restype = D3DRTYPE_VOLUMETEXTURE; + + dxtex->m_device = this; + + dxtex->m_descZero.Format = Format; + dxtex->m_descZero.Type = D3DRTYPE_VOLUMETEXTURE; + dxtex->m_descZero.Usage = Usage; + dxtex->m_descZero.Pool = Pool; + + dxtex->m_descZero.MultiSampleType = D3DMULTISAMPLE_NONE; + dxtex->m_descZero.MultiSampleQuality = 0; + dxtex->m_descZero.Width = Width; + dxtex->m_descZero.Height = Height; + + // also a volume specific desc + dxtex->m_volDescZero.Format = Format; + dxtex->m_volDescZero.Type = D3DRTYPE_VOLUMETEXTURE; + dxtex->m_volDescZero.Usage = Usage; + dxtex->m_volDescZero.Pool = Pool; + + dxtex->m_volDescZero.Width = Width; + dxtex->m_volDescZero.Height = Height; + dxtex->m_volDescZero.Depth = Depth; + + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_3D; + key.m_texFormat = Format; + + if (Levels>1) + { + key.m_texFlags |= kGLMTexMipped; + } + + // http://msdn.microsoft.com/en-us/library/bb172625(VS.85).aspx + // complain if any usage bits come down that I don't know. + uint knownUsageBits = (D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC | D3DUSAGE_TEXTURE_SRGB); + if ( (Usage & knownUsageBits) != Usage ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if (Usage & D3DUSAGE_AUTOGENMIPMAP) + { + key.m_texFlags |= kGLMTexMipped | kGLMTexMippedAuto; + } + + if (Usage & D3DUSAGE_RENDERTARGET) + { + key.m_texFlags |= kGLMTexRenderable; + + m_ObjectStats.m_nTotalRenderTargets++; + } + + if (Usage & D3DUSAGE_DYNAMIC) + { + GLMPRINTF(("-X- DYNAMIC tex usage ignored..")); //FIXME + } + + if (Usage & D3DUSAGE_TEXTURE_SRGB) + { + key.m_texFlags |= kGLMTexSRGB; + } + + key.m_xSize = Width; + key.m_ySize = Height; + key.m_zSize = Depth; + + CGLMTex *tex = m_ctx->NewTex( &key, Levels, pDebugLabel ); + if (!tex) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + dxtex->m_tex = tex; + + dxtex->m_tex->m_srgbFlipCount = 0; + + m_ObjectStats.m_nTotalSurfaces++; + + dxtex->m_surfZero = new IDirect3DSurface9; + dxtex->m_surfZero->m_restype = (D3DRESOURCETYPE)0; // this is a ref to a tex, not the owner... + // do not do an AddRef here. + + dxtex->m_surfZero->m_device = this; + + dxtex->m_surfZero->m_desc = dxtex->m_descZero; + dxtex->m_surfZero->m_tex = tex; + dxtex->m_surfZero->m_face = 0; + dxtex->m_surfZero->m_mip = 0; + + GLMPRINTF(("-A- IDirect3DDevice9::CreateVolumeTexture created '%s' @ %08x (GLM %08x)",tex->m_layout->m_layoutSummary, dxtex, tex )); + + *ppVolumeTexture = dxtex; + + GLMPRINTF(("<-A- IDirect3DDevice9::CreateVolumeTexture")); + + return S_OK; +} + +IDirect3DVolumeTexture9::~IDirect3DVolumeTexture9() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-A- ~IDirect3DVolumeTexture9")); + + if (m_device) + { + m_device->ReleasedTexture( this ); + + // let IDirect3DBaseTexture9::~IDirect3DBaseTexture9 free up m_tex + // we handle m_surfZero + + if (m_surfZero) + { + ULONG refc = m_surfZero->Release( 0, "~IDirect3DVolumeTexture9 public release (surfZero)" ); (void)refc; + Assert( !refc ); + m_surfZero = NULL; + } + // leave m_device alone! + } + + GLMPRINTF(("<-A- ~IDirect3DVolumeTexture9")); +} + +HRESULT IDirect3DVolumeTexture9::LockBox(UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = 0; + lockreq.m_mip = Level; + + lockreq.m_region.xmin = pBox->Left; + lockreq.m_region.ymin = pBox->Top; + lockreq.m_region.zmin = pBox->Front; + lockreq.m_region.xmax = pBox->Right; + lockreq.m_region.ymax = pBox->Bottom; + lockreq.m_region.zmax = pBox->Back; + + char *lockAddress; + int yStride; + int zStride; + + lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + pLockedVolume->RowPitch = yStride; + pLockedVolume->SlicePitch = yStride; + pLockedVolume->pBits = lockAddress; + + return S_OK; +} + +HRESULT IDirect3DVolumeTexture9::UnlockBox(UINT Level) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = 0; + lockreq.m_mip = Level; + + this->m_tex->Unlock( &lockreq ); + + return S_OK; +} + +HRESULT IDirect3DVolumeTexture9::GetLevelDesc( UINT Level, D3DVOLUME_DESC *pDesc ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + if (Level > static_cast<uint>(m_tex->m_layout->m_mipCount)) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + D3DVOLUME_DESC result = m_volDescZero; + // then mutate it for the level of interest + + GLMTexLayoutSlice *slice = &m_tex->m_layout->m_slices[ m_tex->CalcSliceIndex( 0, Level ) ]; + + result.Width = slice->m_xSize; + result.Height = slice->m_ySize; + result.Depth = slice->m_zSize; + + *pDesc = result; + + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DSurface9 + +#endif + +IDirect3DSurface9::~IDirect3DSurface9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + // not much to do here, but good to verify that these things are being freed (and they are) + //GLMPRINTF(("-A- ~IDirect3DSurface9 - signpost")); + + if (m_device) + { + GLMPRINTF(("-A- ~IDirect3DSurface9 - taking real delete path on %08x device %08x", this, m_device)); + m_device->ReleasedSurface( this ); + + memset( &m_desc, 0, sizeof(m_desc) ); + + if (m_restype != 0) // signal that we are a surface that owns this tex (render target) + { + if (m_tex) + { + GLMPRINTF(("-A- ~IDirect3DSurface9 deleted '%s' @ %08x (GLM %08x) %s",m_tex->m_layout->m_layoutSummary, this, m_tex, m_tex->m_debugLabel ? m_tex->m_debugLabel : "" )); + + m_device->ReleasedCGLMTex( m_tex ); + + m_tex->m_ctx->DelTex( m_tex ); + m_tex = NULL; + } + else + { + GLMPRINTF(( "-A- ~IDirect3DSurface9 : whoops, no tex to delete here ?" )); + } + } + else + { + m_tex = NULL; // we are just a view on the tex, we don't own the tex, do not delete it + } + + m_face = m_mip = 0; + + m_device = NULL; + } + else + { + GLMPRINTF(("-A- ~IDirect3DSurface9 - taking strange delete path on %08x device %08x", this, m_device)); + } +} + +HRESULT IDirect3DSurface9::LockRect(D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = this->m_face; + lockreq.m_mip = this->m_mip; + + lockreq.m_region.xmin = pRect->left; + lockreq.m_region.ymin = pRect->top; + lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = pRect->right; + lockreq.m_region.ymax = pRect->bottom; + lockreq.m_region.zmax = 1; + + if ((Flags & (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK)) == (D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK) ) + { + // smells like readback, force texel readout + lockreq.m_readback = true; + } + + char *lockAddress; + int yStride; + int zStride; + + lockreq.m_tex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + pLockedRect->Pitch = yStride; + pLockedRect->pBits = lockAddress; + + return S_OK; +} + +HRESULT IDirect3DSurface9::UnlockRect() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMTexLockParams lockreq; + memset( &lockreq, 0, sizeof(lockreq) ); + + lockreq.m_tex = this->m_tex; + lockreq.m_face = this->m_face; + lockreq.m_mip = this->m_mip; + + lockreq.m_tex->Unlock( &lockreq ); + + return S_OK; +} + +HRESULT IDirect3DSurface9::GetDesc(D3DSURFACE_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + *pDesc = m_desc; + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + + +#ifdef OSX + +#pragma mark ----- IDirect3D9 ------------------------------------------------------- + +#endif + +IDirect3D9::~IDirect3D9() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(("-A- ~IDirect3D9 - signpost")); +} + +UINT IDirect3D9::GetAdapterCount() +{ + GL_BATCH_PERF_CALL_TIMER; + GLMgr::NewGLMgr(); // init GL manager + + GLMDisplayDB *db = GetDisplayDB(); + int dxAdapterCount = db->GetFakeAdapterCount(); + + return dxAdapterCount; +} + +static void FillD3DCaps9( const GLMRendererInfoFields &glmRendererInfo, D3DCAPS9* pCaps ) +{ + // fill in the pCaps record for adapter... we zero most of it and just fill in the fields that we think the caller wants. + Q_memset( pCaps, 0, sizeof(*pCaps) ); + + + /* Device Info */ + pCaps->DeviceType = D3DDEVTYPE_HAL; + + /* Caps from DX7 Draw */ + pCaps->Caps = 0; // does anyone look at this ? + + pCaps->Caps2 = D3DCAPS2_DYNAMICTEXTURES; + /* Cursor Caps */ + pCaps->CursorCaps = 0; // nobody looks at this + + /* 3D Device Caps */ + pCaps->DevCaps = D3DDEVCAPS_HWTRANSFORMANDLIGHT; + + pCaps->TextureCaps = D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_MIPCUBEMAP | D3DPTEXTURECAPS_NONPOW2CONDITIONAL | D3DPTEXTURECAPS_PROJECTED; + // D3DPTEXTURECAPS_NOPROJECTEDBUMPENV ? + // D3DPTEXTURECAPS_POW2 ? + // caller looks at POT support like this: + // pCaps->m_SupportsNonPow2Textures = + // ( !( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ) || + // ( caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL ) ); + // so we should set D3DPTEXTURECAPS_NONPOW2CONDITIONAL bit ? + + + pCaps->PrimitiveMiscCaps = 0; //only the HDR setup looks at this for D3DPMISCCAPS_SEPARATEALPHABLEND. + // ? D3DPMISCCAPS_SEPARATEALPHABLEND + // ? D3DPMISCCAPS_BLENDOP + // ? D3DPMISCCAPS_CLIPPLANESCALEDPOINTS + // ? D3DPMISCCAPS_CLIPTLVERTS D3DPMISCCAPS_COLORWRITEENABLE D3DPMISCCAPS_MASKZ D3DPMISCCAPS_TSSARGTEMP + + + pCaps->RasterCaps = D3DPRASTERCAPS_SCISSORTEST + | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D + | D3DPRASTERCAPS_DEPTHBIAS // ref'd in CShaderDeviceMgrDx8::ComputeCapsFromD3D + ; + + pCaps->TextureFilterCaps = D3DPTFILTERCAPS_MINFANISOTROPIC | D3DPTFILTERCAPS_MAGFANISOTROPIC; + + pCaps->MaxTextureWidth = 4096; + pCaps->MaxTextureHeight = 4096; + pCaps->MaxVolumeExtent = 1024; //guesses + + pCaps->MaxTextureAspectRatio = 0; // imply no limit on AR + + pCaps->MaxAnisotropy = glmRendererInfo.m_maxAniso; + + pCaps->TextureOpCaps = D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_MODULATE2X; //guess + //DWORD MaxTextureBlendStages; + //DWORD MaxSimultaneousTextures; + + pCaps->VertexProcessingCaps = D3DVTXPCAPS_TEXGEN_SPHEREMAP; + + pCaps->MaxActiveLights = 8; // guess + + + // MaxUserClipPlanes. A bit complicated.. + // it's difficult to make this fluid without teaching the engine about a cap that could change during run. + + // start it out set to '2'. + // turn it off, if we're in GLSL mode but do not have native clip plane capability. + pCaps->MaxUserClipPlanes = 2; // assume good news + + // is user asking for it to be off ? + if ( CommandLine()->CheckParm( "-nouserclip" ) ) + { + pCaps->MaxUserClipPlanes = 0; + } + + pCaps->MaxVertexBlendMatrices = 0; // see if anyone cares + pCaps->MaxVertexBlendMatrixIndex = 0; // see if anyone cares + + pCaps->MaxPrimitiveCount = 32768; // guess + pCaps->MaxStreams = D3D_MAX_STREAMS; // guess + + pCaps->VertexShaderVersion = 0x300; // model 3.0 + pCaps->MaxVertexShaderConst = DXABSTRACT_VS_PARAM_SLOTS; // number of vertex shader constant registers + + pCaps->PixelShaderVersion = 0x300; // model 3.0 + + // Here are the DX9 specific ones + pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET; + + pCaps->PS20Caps.NumInstructionSlots = 512; // guess + // only examined once: + // pCaps->m_SupportsPixelShaders_2_b = ( ( caps.PixelShaderVersion & 0xffff ) >= 0x0200) && (caps.PS20Caps.NumInstructionSlots >= 512); + //pCaps->m_SupportsPixelShaders_2_b = 1; + + pCaps->NumSimultaneousRTs = 1; // Will be at least 1 + pCaps->MaxVertexShader30InstructionSlots = 0; + pCaps->MaxPixelShader30InstructionSlots = 0; + +#if DX_TO_GL_ABSTRACTION + pCaps->FakeSRGBWrite = !glmRendererInfo.m_hasGammaWrites; + pCaps->CanDoSRGBReadFromRTs = !glmRendererInfo.m_cantAttachSRGB; + pCaps->MixedSizeTargets = glmRendererInfo.m_hasMixedAttachmentSizes; +#endif +} + +HRESULT IDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) +{ + GL_BATCH_PERF_CALL_TIMER; + // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp + + // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + // just leave glmRendererInfo filled out for subsequent code to look at as needed. + + FillD3DCaps9( glmRendererInfo, pCaps ); + + return S_OK; +} + +HRESULT IDirect3D9::GetAdapterIdentifier( UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier ) +{ + GL_BATCH_PERF_CALL_TIMER; + // Generally called from "CShaderDeviceMgrDx8::ComputeCapsFromD3D" in ShaderDeviceDX8.cpp + + Assert( Flags == D3DENUM_WHQL_LEVEL ); // we're not handling any other queries than this yet + + Q_memset( pIdentifier, 0, sizeof(*pIdentifier) ); + + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + +#ifndef OSX + if( glmRendererInfo.m_rendererID ) +#endif + { + const char *pRenderer = GLMDecode( eGL_RENDERER, glmRendererInfo.m_rendererID & 0x00FFFF00 ); + + Q_snprintf( pIdentifier->Driver, sizeof(pIdentifier->Driver), "OpenGL %s (%08x)", + pRenderer, glmRendererInfo.m_rendererID ); + + Q_snprintf( pIdentifier->Description, sizeof(pIdentifier->Description), "%s - %dx%d - %dMB VRAM", + pRenderer, + glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight, + glmRendererInfo.m_vidMemory >> 20 ); + } +#ifndef OSX + else + { + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + + const char *pszStringVendor = ( const char * )glGetString( GL_VENDOR ); // NVIDIA Corporation + const char *pszStringRenderer = ( const char * )glGetString( GL_RENDERER ); // GeForce GTX 680/PCIe/SSE2 + const char *pszStringVersion = ( const char * )glGetString( GL_VERSION ); // 4.2.0 NVIDIA 304.22 + + Q_snprintf( pIdentifier->Driver, sizeof( pIdentifier->Driver ), "OpenGL %s (%s)", + pszStringVendor, pszStringRenderer ); + Q_snprintf( pIdentifier->Description, sizeof( pIdentifier->Description ), "%s (%s) %s - %dx%d", + pszStringVendor, pszStringRenderer, pszStringVersion, + glmDisplayInfo.m_displayPixelWidth, glmDisplayInfo.m_displayPixelHeight ); + } +#endif // !OSX + + pIdentifier->VendorId = glmRendererInfo.m_pciVendorID; // 4318; + pIdentifier->DeviceId = glmRendererInfo.m_pciDeviceID; // 401; + pIdentifier->SubSysId = 0; // 3358668866; + pIdentifier->Revision = 0; // 162; + pIdentifier->VideoMemory = glmRendererInfo.m_vidMemory; // amount of video memory in bytes + + #if 0 + // this came from the shaderapigl effort + Q_strncpy( pIdentifier->Driver, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); + Q_strncpy( pIdentifier->Description, "Fake-Video-Card", MAX_DEVICE_IDENTIFIER_STRING ); + pIdentifier->VendorId = 4318; + pIdentifier->DeviceId = 401; + pIdentifier->SubSysId = 3358668866; + pIdentifier->Revision = 162; + #endif + + return S_OK; +} + +HRESULT IDirect3D9::CheckDeviceFormat(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) +{ + GL_BATCH_PERF_CALL_TIMER; + if (0) // hush for now, less spew + { + GLMPRINTF(("-X- ** IDirect3D9::CheckDeviceFormat: \n -- Adapter=%d || DeviceType=%4x:%s || AdapterFormat=%8x:%s\n -- RType %8x: %s\n -- CheckFormat %8x: %s\n -- Usage %8x: %s", + Adapter, + DeviceType, GLMDecode(eD3D_DEVTYPE, DeviceType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + RType, GLMDecode(eD3D_RTYPE, RType), + CheckFormat, GLMDecode(eD3D_FORMAT, CheckFormat), + Usage, GLMDecodeMask( eD3D_USAGE, Usage ) )); + } + + HRESULT result = D3DERR_NOTAVAILABLE; // failure + + DWORD knownUsageMask = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL | D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP + | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING + | D3DUSAGE_QUERY_VERTEXTEXTURE; + (void)knownUsageMask; + + // FramebufferSRGB stuff. + // basically a format is only allowed to have SRGB usage for writing, if you have the framebuffer SRGB extension. + // so, check for that capability with GLM adapter db, and if it's not there, don't mark that bit as usable in any of our formats. + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool dbresult = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)dbresult; + Assert (!dbresult); + + Assert ((Usage & knownUsageMask) == Usage); + + DWORD legalUsage = 0; + switch( AdapterFormat ) + { + case D3DFMT_X8R8G8B8: + switch( RType ) + { + case D3DRTYPE_TEXTURE: + switch( CheckFormat ) + { + case D3DFMT_DXT1: + case D3DFMT_DXT3: + case D3DFMT_DXT5: + legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD; + + //open question: is auto gen of mipmaps is allowed or attempted on any DXT textures. + break; + + case D3DFMT_A8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + +//$ TODO: Need to merge bitmap changes over from Dota to get these formats. +#if 0 + case D3DFMT_A2R10G10B10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A2B10G10R10: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; +#endif + + case D3DFMT_R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A16B16G16R16: + legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + break; + + case D3DFMT_A16B16G16R16F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + + if ( !glmRendererInfo.m_atiR5xx ) + { + legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + } + break; + + case D3DFMT_A32B32G32R32F: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + + if ( !glmRendererInfo.m_atiR5xx && !glmRendererInfo.m_nvG7x ) + { + legalUsage |= D3DUSAGE_QUERY_FILTER | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + } + break; + + case D3DFMT_R5G6B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + //----------------------------------------------------------- + // these come in from TestTextureFormat in ColorFormatDX8.cpp which is being driven by InitializeColorInformation... + // which is going to try all 8 combinations of (vertex texturable / render targetable / filterable ) on every image format it knows. + + case D3DFMT_R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD; + break; + + case D3DFMT_X8R8G8B8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + legalUsage |= D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE; + break; + + // one and two channel textures... we'll have to fake these as four channel tex if we want to support them + case D3DFMT_L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A8L8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + // going to need to go back and double check all of these.. + case D3DFMT_X1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A4R4G4B4: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_A1R5G5B5: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + break; + + case D3DFMT_Q8W8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + // what the heck is QWVU8 ... ? + break; + + case D3DFMT_X8L8V8U8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_AUTOGENMIPMAP | D3DUSAGE_QUERY_FILTER; + // what the heck is XLVU8 ... ? + break; + + // formats with depth... + + case D3DFMT_D16: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; + // just a guess on the legal usages + break; + + case D3DFMT_D24S8: legalUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL; + // just a guess on the legal usages + break; + + // vendor formats... try marking these all invalid for now + case D3DFMT_NV_INTZ: + case D3DFMT_NV_RAWZ: + case D3DFMT_NV_NULL: + case D3DFMT_ATI_D16: + case D3DFMT_ATI_D24S8: + case D3DFMT_ATI_2N: + case D3DFMT_ATI_1N: + legalUsage = 0; + break; + + //----------------------------------------------------------- + + default: + Assert(!"Unknown check format"); + result = D3DERR_NOTAVAILABLE; + break; + } + + if ((Usage & legalUsage) == Usage) + { + result = S_OK; + } + else + { + DWORD unsatBits = Usage & (~legalUsage); // clear the bits of the req that were legal, leaving the illegal ones + unsatBits; + GLMPRINTF(( "-X- --> NOT OK: flags %8x:%s", unsatBits,GLMDecodeMask( eD3D_USAGE, unsatBits ) )); + result = D3DERR_NOTAVAILABLE; + } + break; + + case D3DRTYPE_SURFACE: + switch( static_cast<uint>(CheckFormat) ) + { + case 0x434f5441: + case 0x41415353: + result = D3DERR_NOTAVAILABLE; + break; + + case D3DFMT_D24S8: + result = S_OK; + break; + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=434f5441:UNKNOWN + //** IDirect3D9::CheckDeviceFormat adapter=0, DeviceType= 1:D3DDEVTYPE_HAL, AdapterFormat= 5:D3DFMT_X8R8G8B8, RType= 1:D3DRTYPE_SURFACE, CheckFormat=41415353:UNKNOWN + } + break; + + default: + Assert(!"Unknown resource type"); + result = D3DERR_NOTAVAILABLE; + break; + } + break; + + default: + Assert(!"Unknown adapter format"); + result = D3DERR_NOTAVAILABLE; + break; + } + + return result; +} + +UINT IDirect3D9::GetAdapterModeCount(UINT Adapter,D3DFORMAT Format) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( "-X- IDirect3D9::GetAdapterModeCount: Adapter=%d || Format=%8x:%s", Adapter, Format, GLMDecode(eD3D_FORMAT, Format) )); + + uint modeCount=0; + + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + + modeCount = db->GetModeCount( glmRendererIndex, glmDisplayIndex ); + GLMPRINTF(( "-X- --> result is %d", modeCount )); + + return modeCount; +} + +HRESULT IDirect3D9::EnumAdapterModes(UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes: Adapter=%d || Format=%8x:%s || Mode=%d", Adapter, Format, GLMDecode(eD3D_FORMAT, Format), Mode )); + + Assert(Format==D3DFMT_X8R8G8B8); + + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert (!result); + if (result) return D3DERR_NOTAVAILABLE; + + bool result2 = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, Mode, &glmModeInfo ); + Assert( !result2 ); + if (result2) return D3DERR_NOTAVAILABLE; + + pMode->Width = glmModeInfo.m_modePixelWidth; + pMode->Height = glmModeInfo.m_modePixelHeight; + pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" + pMode->Format = Format; // whatever you asked for ? + + GLMPRINTF(( "-X- IDirect3D9::EnumAdapterModes returning mode size (%d,%d) and D3DFMT_X8R8G8B8",pMode->Width,pMode->Height )); + return S_OK; +} + +HRESULT IDirect3D9::CheckDeviceType(UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) +{ + GL_BATCH_PERF_CALL_TIMER; + //FIXME: we just say "OK" on any query + + GLMPRINTF(( "-X- IDirect3D9::CheckDeviceType: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || BackBufferFormat=%d:%s || bWindowed=%d", + Adapter, + DevType, GLMDecode(eD3D_DEVTYPE,DevType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + BackBufferFormat, GLMDecode(eD3D_FORMAT, BackBufferFormat), + (int) bWindowed )); + + return S_OK; +} + +HRESULT IDirect3D9::GetAdapterDisplayMode(UINT Adapter,D3DDISPLAYMODE* pMode) +{ + GL_BATCH_PERF_CALL_TIMER; + // asking what the current mode is + GLMPRINTF(("-X- IDirect3D9::GetAdapterDisplayMode: Adapter=%d", Adapter )); + + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert(!result); + if (result) return D3DERR_INVALIDCALL; + + int modeIndex = -1; // pass -1 as a mode index to find out about whatever the current mode is on the selected display + + bool modeResult = db->GetModeInfo( glmRendererIndex, glmDisplayIndex, modeIndex, &glmModeInfo ); + Assert (!modeResult); + if (modeResult) return D3DERR_INVALIDCALL; + + pMode->Width = glmModeInfo.m_modePixelWidth; + pMode->Height = glmModeInfo.m_modePixelHeight; + pMode->RefreshRate = glmModeInfo.m_modeRefreshHz; // "adapter default" + pMode->Format = D3DFMT_X8R8G8B8; //FIXME, this is a SWAG + + return S_OK; +} + +HRESULT IDirect3D9::CheckDepthStencilMatch(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(("-X- IDirect3D9::CheckDepthStencilMatch: Adapter=%d || DevType=%d:%s || AdapterFormat=%d:%s || RenderTargetFormat=%d:%s || DepthStencilFormat=%d:%s", + Adapter, + DeviceType, GLMDecode(eD3D_DEVTYPE,DeviceType), + AdapterFormat, GLMDecode(eD3D_FORMAT, AdapterFormat), + RenderTargetFormat, GLMDecode(eD3D_FORMAT, RenderTargetFormat), + DepthStencilFormat, GLMDecode(eD3D_FORMAT, DepthStencilFormat) )); + + // one known request looks like this: + // AdapterFormat=5:D3DFMT_X8R8G8B8 || RenderTargetFormat=3:D3DFMT_A8R8G8B8 || DepthStencilFormat=2:D3DFMT_D24S8 + + // return S_OK for that one combo, DXABSTRACT_BREAK_ON_ERROR() on anything else + HRESULT result = D3DERR_NOTAVAILABLE; // failure + + switch( AdapterFormat ) + { + case D3DFMT_X8R8G8B8: + { + if ( (RenderTargetFormat == D3DFMT_A8R8G8B8) && (DepthStencilFormat == D3DFMT_D24S8) ) + { + result = S_OK; + } + } + break; + } + + Assert( result == S_OK ); + + return result; +} + +HRESULT IDirect3D9::CheckDeviceMultiSampleType( UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels ) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMDisplayDB *db = GetDisplayDB(); + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + //GLMDisplayModeInfoFields glmModeInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool result = db->GetFakeAdapterInfo( Adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert( !result ); + if ( result ) + return D3DERR_INVALIDCALL; + + + if ( !CommandLine()->FindParm("-glmenabletrustmsaa") ) + { + // These ghetto drivers don't get MSAA + if ( ( glmRendererInfo.m_nvG7x || glmRendererInfo.m_atiR5xx ) && ( MultiSampleType > D3DMULTISAMPLE_NONE ) ) + { + if ( pQualityLevels ) + { + *pQualityLevels = 0; + } + return D3DERR_NOTAVAILABLE; + } + } + + switch ( MultiSampleType ) + { + case D3DMULTISAMPLE_NONE: // always return true + if ( pQualityLevels ) + { + *pQualityLevels = 1; + } + return S_OK; + break; + + case D3DMULTISAMPLE_2_SAMPLES: + case D3DMULTISAMPLE_4_SAMPLES: + case D3DMULTISAMPLE_6_SAMPLES: + case D3DMULTISAMPLE_8_SAMPLES: + // note the fact that the d3d enums for 2, 4, 6, 8 samples are equal to 2,4,6,8... + if (glmRendererInfo.m_maxSamples >= (int)MultiSampleType ) + { + if ( pQualityLevels ) + { + *pQualityLevels = 1; + } + return S_OK; + } + else + { + return D3DERR_NOTAVAILABLE; + } + break; + + default: + if ( pQualityLevels ) + { + *pQualityLevels = 0; + } + return D3DERR_NOTAVAILABLE; + break; + } + return D3DERR_NOTAVAILABLE; +} + +HRESULT IDirect3D9::CreateDevice(UINT Adapter,D3DDEVTYPE DeviceType,VD3DHWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) +{ + GL_BATCH_PERF_CALL_TIMER; + +#if GLMDEBUG + GLMDebugPrintf( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); + Warning( "WARNING: GLMEBUG is 1, perf. is going to be low!" ); +#endif +#if !TOGL_SUPPORT_NULL_DEVICE + if (DeviceType == D3DDEVTYPE_NULLREF) + { + Error( "Must define TOGL_SUPPORT_NULL_DEVICE to use the NULL device" ); + DebuggerBreak(); + return E_FAIL; + } +#endif + + // constrain these inputs for the time being + // BackBufferFormat -> A8R8G8B8 + // BackBufferCount -> 1; + // MultiSampleType -> D3DMULTISAMPLE_NONE + // AutoDepthStencilFormat -> D3DFMT_D24S8 + + // NULL out the return pointer so if we exit early it is not set + *ppReturnedDeviceInterface = NULL; + + // assume success unless something is sour + HRESULT result = S_OK; + + // relax this check for now + //if (pPresentationParameters->BackBufferFormat != D3DFMT_A8R8G8B8) + //{ + // DXABSTRACT_BREAK_ON_ERROR(); + // result = -1; + //} + + //rbarris 24Aug10 - relaxing this check - we don't care if the game asks for two backbuffers, it's moot + //if ( pPresentationParameters->BackBufferCount != 1 ) + //{ + // DXABSTRACT_BREAK_ON_ERROR(); + // result = D3DERR_NOTAVAILABLE; + //} + + if ( pPresentationParameters->AutoDepthStencilFormat != D3DFMT_D24S8 ) + { + DXABSTRACT_BREAK_ON_ERROR(); + result = D3DERR_NOTAVAILABLE; + } + + if ( result == S_OK ) + { + // create an IDirect3DDevice9 + // it will make a GLMContext and set up some drawables + + IDirect3DDevice9Params devparams; + memset( &devparams, 0, sizeof(devparams) ); + + devparams.m_adapter = Adapter; + devparams.m_deviceType = DeviceType; + devparams.m_focusWindow = hFocusWindow; // is this meaningful? is this a WindowRef ? follow it up the chain.. + devparams.m_behaviorFlags = BehaviorFlags; + devparams.m_presentationParameters = *pPresentationParameters; + + IDirect3DDevice9 *dev = new IDirect3DDevice9; + + result = dev->Create( &devparams ); + + if ( result == S_OK ) + { + *ppReturnedDeviceInterface = dev; + } + + g_bNullD3DDevice = ( DeviceType == D3DDEVTYPE_NULLREF ); + } + return result; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DQuery9 + +#endif + +HRESULT IDirect3DQuery9::Issue(DWORD dwIssueFlags) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + // Flags field for Issue + // #define D3DISSUE_END (1 << 0) // Tells the runtime to issue the end of a query, changing it's state to "non-signaled". + // #define D3DISSUE_BEGIN (1 << 1) // Tells the runtime to issue the beginng of a query. + + // Make sure calling thread owns the GL context. + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + if (dwIssueFlags & D3DISSUE_BEGIN) + { + m_nIssueStartThreadID = ThreadGetCurrentId(); + m_nIssueStartDrawCallIndex = g_nTotalDrawsOrClears; + m_nIssueStartFrameIndex = m_ctx->m_nCurFrame; + m_nIssueStartQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + m_query->Start(); // drop "start counter" call into stream + break; + + default: + Assert(!"Can't use D3DISSUE_BEGIN on this query"); + break; + } + } + + if (dwIssueFlags & D3DISSUE_END) + { + m_nIssueEndThreadID = ThreadGetCurrentId(); + m_nIssueEndDrawCallIndex = g_nTotalDrawsOrClears; + m_nIssueEndFrameIndex = m_ctx->m_nCurFrame; + m_nIssueEndQueryCreationCounter = CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + m_query->Stop(); // drop "end counter" call into stream + break; + + case D3DQUERYTYPE_EVENT: + m_nIssueStartThreadID = m_nIssueEndThreadID; + m_nIssueStartDrawCallIndex = m_nIssueEndDrawCallIndex; + m_nIssueStartFrameIndex = m_nIssueEndFrameIndex; + m_nIssueStartQueryCreationCounter = m_nIssueEndQueryCreationCounter; + + // End is very weird with respect to Events (fences). + // DX9 docs say to use End to put the fence in the stream. So we map End to GLM's Start. + // http://msdn.microsoft.com/en-us/library/ee422167(VS.85).aspx + m_query->Start(); // drop "set fence" into stream + break; + } + } + return S_OK; +} + +HRESULT IDirect3DQuery9::GetData(void* pData,DWORD dwSize,DWORD dwGetDataFlags) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( m_device->m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + HRESULT result = S_FALSE ; + DWORD nCurThreadId = ThreadGetCurrentId(); + + // Make sure calling thread owns the GL context. + Assert( m_ctx->m_nCurOwnerThreadId == nCurThreadId ); + if ( pData ) + { + *(uint*)pData = 0; + } + + if ( !m_query->IsStarted() || !m_query->IsStopped() ) + { + Assert(!"Can't GetData before issue/start/stop"); + printf("\n** IDirect3DQuery9::GetData: can't GetData before issue/start/stop"); + return S_FALSE; + } + + // GetData is not always called with the flush bit. + + // if an answer is not yet available - return S_FALSE. + // if an answer is available - return S_OK and write the answer into *pData. + bool done = false; + bool flush = (dwGetDataFlags & D3DGETDATA_FLUSH) != 0; // aka spin until done + + // hmmm both of these paths are the same, maybe we could fold them up + if ( m_type == D3DQUERYTYPE_OCCLUSION ) + { + // Detect cases that are actually just not supported with the way we're using GL queries. (For example, beginning a query, then creating/deleting any query, the ending the same query is not supported.) + // Also extra paranoid to detect/work around various NV/AMD driver issues. + if ( ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) || + ( m_nIssueStartDrawCallIndex == m_nIssueEndDrawCallIndex ) || ( m_nIssueStartFrameIndex != m_nIssueEndFrameIndex ) || + ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) ) + { + // The thread Issue() was called on differs from GetData() - NV's driver doesn't like this, not sure about AMD. Just fake the results if a flush is requested. + // There are various ways to properly handle this scenario, but in practice it only seems to occur in non-critical times (during shutdown or when mat_queue_mode is changed in L4D2). + if ( flush ) + { + gGL->glFlush(); + } + +#if 0 + if ( ( m_nIssueStartThreadID != nCurThreadId ) || ( m_nIssueEndThreadID != nCurThreadId ) ) + { + GLMDebugPrintf( "IDirect3DQuery9::GetData: GetData() called from different thread verses the issueing thread()!\n" ); + } +#endif + if ( m_nIssueStartQueryCreationCounter != m_nIssueEndQueryCreationCounter ) + { + GLMDebugPrintf( "IDirect3DQuery9::GetData: One or more queries have been created or released while this query was still issued! This scenario is not supported in GL.\n"); + } + + // Return with a non-standard error code, so the caller has a chance to do something halfway intelligent. + return D3DERR_NOTAVAILABLE; + } + } + + switch( m_type ) + { + case D3DQUERYTYPE_OCCLUSION: + { + + if ( flush ) + { + uint oqValue = 0; + CFastTimer tm; + tm.Start(); + + + // Is this flush actually necessary? According to the extension it's not. + // It doesn't seem to matter if this is a glFlush() or glFinish() with NVidia's driver (tested in MT mode - not sure if it matters), it still can take several calls to IsDone() before we can stop waiting for the query results. + // On AMD, this flush logic fails during shutdown (the query results never become available) - tried a bunch of experiments and checks with no luck. + + m_query->Complete(&oqValue); + + + double flTotalTime = tm.GetDurationInProgress().GetSeconds() * 1000.0f; + if ( flTotalTime > .5f ) + { + // Give up - something silly has obviously gone wrong in the driver, lying is better than stalling potentially forever. + // This occurs on AMD (single threaded driver) during shutdown, not sure why yet. It has nothing to do with threading. It may have to do with releasing queries or other objects. + // We must return a result otherwise the app itself could hang, waiting infinitely. + //Assert( 0 ); + Warning( "IDirect3DQuery9::GetData(): Occlusion query flush took %3.3fms!\n", flTotalTime ); + } + if (pData) + { + *(uint*)pData = oqValue; + } + result = S_OK; + } + else + { + done = m_query->IsDone(); + if (done) + { + uint oqValue = 0; // or we could just pass pData directly to Complete... + m_query->Complete(&oqValue); + if (pData) + { + *(uint*)pData = oqValue; + } + result = S_OK; + } + else + { + result = S_FALSE; + Assert( !flush ); + } + } + } + break; + + case D3DQUERYTYPE_EVENT: + { + done = m_query->IsDone(); + if ( ( done ) || ( flush ) ) + { + m_query->Complete(NULL); // this will block on pre-SLGU + + result = S_OK; + } + else + { + result = S_FALSE; + Assert( !flush ); + } + } + break; + } + + return result; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DVertexBuffer9 + +#endif + +HRESULT IDirect3DDevice9::CreateVertexBuffer(UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer9** ppVertexBuffer,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF(( ">-A- IDirect3DDevice9::CreateVertexBuffer" )); + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + m_ObjectStats.m_nTotalVertexBuffers++; + + IDirect3DVertexBuffer9 *newbuff = new IDirect3DVertexBuffer9; + + newbuff->m_device = this; + + newbuff->m_ctx = m_ctx; + + // FIXME need to find home or use for the Usage, FVF, Pool values passed in + uint options = 0; + + if (Usage&D3DUSAGE_DYNAMIC) + { + options |= GLMBufferOptionDynamic; + } + + newbuff->m_vtxBuffer = m_ctx->NewBuffer( kGLMVertexBuffer, Length, options ) ; + + newbuff->m_vtxDesc.Type = D3DRTYPE_VERTEXBUFFER; + newbuff->m_vtxDesc.Usage = Usage; + newbuff->m_vtxDesc.Pool = Pool; + newbuff->m_vtxDesc.Size = Length; + + *ppVertexBuffer = newbuff; + + GLMPRINTF(( "<-A- IDirect3DDevice9::CreateVertexBuffer" )); + + return S_OK; +} + +IDirect3DVertexBuffer9::~IDirect3DVertexBuffer9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9" )); + + if (m_device) + { + m_device->ReleasedVertexBuffer( this ); + + if (m_ctx && m_vtxBuffer) + { + GLMPRINTF(( ">-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer" )); + m_ctx->DelBuffer( m_vtxBuffer ); + m_vtxBuffer = NULL; + GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9 deleting m_vtxBuffer - done" )); + } + m_device = NULL; + } + + GLMPRINTF(( "<-A- ~IDirect3DVertexBuffer9" )); +} + +HRESULT IDirect3DVertexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Lock" ); + + // FIXME would be good to have "can't lock twice" logic + + Assert( !(Flags & D3DLOCK_READONLY) ); // not impl'd +// Assert( !(Flags & D3DLOCK_NOSYSLOCK) ); // not impl'd - it triggers though + + GLMBuffLockParams lockreq; + lockreq.m_nOffset = OffsetToLock; + lockreq.m_nSize = SizeToLock; + lockreq.m_bNoOverwrite = (Flags & D3DLOCK_NOOVERWRITE) != 0; + lockreq.m_bDiscard = (Flags & D3DLOCK_DISCARD) != 0; + + m_vtxBuffer->Lock( &lockreq, (char**)ppbData ); + + GLMPRINTF(("-X- IDirect3DDevice9::Lock on D3D buf %p (GL name %d) offset %d, size %d => address %p", this, this->m_vtxBuffer->m_nHandle, OffsetToLock, SizeToLock, *ppbData)); + return S_OK; +} + +HRESULT IDirect3DVertexBuffer9::Unlock() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB Unlock" ); + + m_vtxBuffer->Unlock(); + return S_OK; +} + +void IDirect3DVertexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "VB UnlockActualSize" ); + + m_vtxBuffer->Unlock( nActualSize, pActualData ); +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + + +#ifdef OSX + +#pragma mark ----- IDirect3DIndexBuffer9 + +#endif + +HRESULT IDirect3DDevice9::CreateIndexBuffer(UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer9** ppIndexBuffer,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(( ">-A- IDirect3DDevice9::CreateIndexBuffer" )); + + // it is important to save all the create info, since GetDesc could get called later to query it + + m_ObjectStats.m_nTotalIndexBuffers++; + + IDirect3DIndexBuffer9 *newbuff = new IDirect3DIndexBuffer9; + + newbuff->m_device = this; + + newbuff->m_restype = D3DRTYPE_INDEXBUFFER; // hmmmmmmm why are we not derived from d3dresource.. + + newbuff->m_ctx = m_ctx; + + // FIXME need to find home or use for the Usage, Format, Pool values passed in + uint options = 0; + + if (Usage&D3DUSAGE_DYNAMIC) + { + options |= GLMBufferOptionDynamic; + } + + newbuff->m_idxBuffer = m_ctx->NewBuffer( kGLMIndexBuffer, Length, options ) ; + + newbuff->m_idxDesc.Format = Format; + newbuff->m_idxDesc.Type = D3DRTYPE_INDEXBUFFER; + newbuff->m_idxDesc.Usage = Usage; + newbuff->m_idxDesc.Pool = Pool; + newbuff->m_idxDesc.Size = Length; + + *ppIndexBuffer = newbuff; + + GLMPRINTF(( "<-A- IDirect3DDevice9::CreateIndexBuffer" )); + + return S_OK; +} + +IDirect3DIndexBuffer9::~IDirect3DIndexBuffer9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9" )); + + if (m_device) + { + m_device->ReleasedIndexBuffer( this ); + + if (m_ctx && m_idxBuffer) + { + GLMPRINTF(( ">-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer" )); + m_ctx->DelBuffer( m_idxBuffer ); + GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9 deleting m_idxBuffer - done" )); + } + m_device = NULL; + } + else + { + } + + GLMPRINTF(( "<-A- ~IDirect3DIndexBuffer9" )); +} + + +HRESULT IDirect3DIndexBuffer9::Lock(UINT OffsetToLock,UINT SizeToLock,void** ppbData,DWORD Flags) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + // FIXME would be good to have "can't lock twice" logic + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Lock" ); + + GLMBuffLockParams lockreq; + lockreq.m_nOffset = OffsetToLock; + lockreq.m_nSize = SizeToLock; + lockreq.m_bNoOverwrite = ( Flags & D3DLOCK_NOOVERWRITE ) != 0; + lockreq.m_bDiscard = ( Flags & D3DLOCK_DISCARD ) != 0; + + m_idxBuffer->Lock( &lockreq, (char**)ppbData ); + + return S_OK; +} + +HRESULT IDirect3DIndexBuffer9::Unlock() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB Unlock" ); + + m_idxBuffer->Unlock(); + + return S_OK; +} + +void IDirect3DIndexBuffer9::UnlockActualSize( uint nActualSize, const void *pActualData ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + tmZoneFiltered( TELEMETRY_LEVEL2, 25, TMZF_NONE, "IB UnlockActualSize" ); + + m_idxBuffer->Unlock( nActualSize, pActualData ); +} + +HRESULT IDirect3DIndexBuffer9::GetDesc(D3DINDEXBUFFER_DESC *pDesc) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + *pDesc = m_idxDesc; + return S_OK; +} + + +// ------------------------------------------------------------------------------------------------------------------------------ // + +#ifdef OSX + +#pragma mark ----- IDirect3DDevice9 ------------------------------------------------- + +#endif + +void ConvertPresentationParamsToGLMDisplayParams( D3DPRESENT_PARAMETERS *d3dp, GLMDisplayParams *gldp ) +{ + memset( gldp, 0, sizeof(*gldp) ); + + gldp->m_fsEnable = !d3dp->Windowed; + + // see http://msdn.microsoft.com/en-us/library/ee416515(VS.85).aspx + // note that the values below are the only ones mentioned by Source engine; there are many others + switch(d3dp->PresentationInterval) + { + case D3DPRESENT_INTERVAL_ONE: + gldp->m_vsyncEnable = true; // "The driver will wait for the vertical retrace period (the runtime will beam-follow to prevent tearing)." + break; + + case D3DPRESENT_INTERVAL_IMMEDIATE: + gldp->m_vsyncEnable = false; // "The runtime updates the window client area immediately and might do so more than once during the adapter refresh period." + break; + + default: + gldp->m_vsyncEnable = true; // if I don't know it, you're getting vsync enabled. + break; + } + + gldp->m_backBufferWidth = d3dp->BackBufferWidth; + gldp->m_backBufferHeight = d3dp->BackBufferHeight; + gldp->m_backBufferFormat = d3dp->BackBufferFormat; + gldp->m_multiSampleCount = d3dp->MultiSampleType; // it's a count really + + gldp->m_enableAutoDepthStencil = d3dp->EnableAutoDepthStencil != 0; + gldp->m_autoDepthStencilFormat = d3dp->AutoDepthStencilFormat; + + gldp->m_fsRefreshHz = d3dp->FullScreen_RefreshRateInHz; + + // some fields in d3d PB we're not acting on yet... + // UINT BackBufferCount; + // DWORD MultiSampleQuality; + // D3DSWAPEFFECT SwapEffect; + // VD3DHWND hDeviceWindow; + // DWORD Flags; +} + +void UnpackD3DRSITable( void ); + +HRESULT IDirect3DDevice9::Create( IDirect3DDevice9Params *params ) +{ + g_pD3D_Device = this; + + GLMDebugPrintf( "IDirect3DDevice9::Create: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", + params->m_presentationParameters.BackBufferWidth, + params->m_presentationParameters.BackBufferHeight, + params->m_presentationParameters.BackBufferFormat, + params->m_presentationParameters.BackBufferCount, + params->m_presentationParameters.MultiSampleType, + params->m_presentationParameters.MultiSampleQuality ); + + UnpackD3DRSITable(); + + m_ObjectStats.clear(); + m_PrevObjectStats.clear(); + +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + m_pBatch_vis_bitmap = NULL; +#endif + + GL_BATCH_PERF_CALL_TIMER; + GLMPRINTF((">-X-IDirect3DDevice9::Create")); + HRESULT result = S_OK; + + // create an IDirect3DDevice9 + // make a GLMContext and set up some drawables + m_params = *params; + + m_ctx = NULL; + + V_memset( m_pRenderTargets, 0, sizeof( m_pRenderTargets ) ); + m_pDepthStencil = NULL; + + m_pDefaultColorSurface = NULL; + m_pDefaultDepthStencilSurface = NULL; + + memset( m_streams, 0, sizeof(m_streams) ); + memset( m_vtx_buffers, 0, sizeof( m_vtx_buffers ) ); + memset( m_textures, 0, sizeof(m_textures) ); + //memset( m_samplers, 0, sizeof(m_samplers) ); + + m_indices.m_idxBuffer = NULL; + + m_vertexShader = NULL; + m_pixelShader = NULL; + + m_pVertDecl = NULL; + + //============================================================================ + // param block for GLM context create + GLMDisplayParams glmParams; + ConvertPresentationParamsToGLMDisplayParams( ¶ms->m_presentationParameters, &glmParams ); + + glmParams.m_mtgl = true; // forget this idea -> (params->m_behaviorFlags & D3DCREATE_MULTITHREADED) != 0; + // the call above fills in a bunch of things, but doesn't know about anything outside of the presentation params. + // those tend to be the things that do not change after create, so we do those here in Create. + + glmParams.m_focusWindow = params->m_focusWindow; + + #if 0 //FIXME-HACK + // map the D3D "adapter" to a renderer/display pair + // (that GPU will have to stay set as-is for any subsequent mode changes) + + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + // the D3D "Adapter" number feeds the fake adapter index + bool adaptResult = GLMgr::aGLMgr()->GetDisplayDB()->GetFakeAdapterInfo( params->m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); + Assert(!adaptResult); + + glmParams.m_rendererIndex = glmRendererIndex; + glmParams.m_displayIndex = glmDisplayIndex; + // glmParams.m_modeIndex hmmmmm, client doesn't give us a mode number, just a resolution.. + #endif + + m_ctx = GLMgr::aGLMgr()->NewContext( this, &glmParams ); + if (!m_ctx) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return (HRESULT) -1; + } + + // make an FBO to draw into and activate it. + m_ctx->m_drawingFBO = m_ctx->NewFBO(); + + // bind it to context. will receive attachments shortly. + m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + m_bFBODirty = false; + + m_pFBOs = new CGLMFBOMap(); + m_pFBOs->SetLessFunc( RenderTargetState_t::LessFunc ); + + // we create two IDirect3DSurface9's. These will be known as the internal render target 0 and the depthstencil. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target...")); + // color surface + result = this->CreateRenderTarget( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.BackBufferFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + true, // lockable + &m_pDefaultColorSurface, // ppSurface + NULL, // shared handle + "InternalRT0" + ); + + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + // do not do an AddRef.. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making color render target complete -> %08x", m_pDefaultColorSurface )); + + GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target...")); + result = this->SetRenderTarget(0, m_pDefaultColorSurface); + if (result != S_OK) + { + GLMPRINTF(("< IDirect3DDevice9::Create (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Create setting color render target complete.")); + + Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); + + GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil...")); + result = CreateDepthStencilSurface( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.AutoDepthStencilFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + TRUE, // enable z-buffer discard ???? + &m_pDefaultDepthStencilSurface, // ppSurface + NULL // shared handle + ); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Create making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); + GLMPRINTF(("-X- Direct3DDevice9::Create setting depth-stencil render target...")); + result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); + if (result != S_OK) + { + DXABSTRACT_BREAK_ON_ERROR(); + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Create setting depth-stencil render target complete.")); + + UpdateBoundFBO(); + + bool ready = m_ctx->m_drawingFBO->IsReady(); + if (!ready) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Create (error out)")); + return (HRESULT)-1; + } + + // this next part really needs to be inside GLMContext.. or replaced with D3D style viewport setup calls. + m_ctx->GenDebugFontTex(); + + // blast the gl state mirror... + memset( &this->gl, 0, sizeof( this->gl ) ); + + InitStates(); + + GLScissorEnable_t defScissorEnable = { true }; + GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; + GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; + GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; + GLCullFaceEnable_t defCullFaceEnable = { true }; + GLCullFrontFace_t defCullFrontFace = { GL_CCW }; + + gl.m_ScissorEnable = defScissorEnable; + gl.m_ScissorBox = defScissorBox; + gl.m_ViewportBox = defViewportBox; + gl.m_ViewportDepthRange = defViewportDepthRange; + gl.m_CullFaceEnable = defCullFaceEnable; + gl.m_CullFrontFace = defCullFrontFace; + + FullFlushStates(); + + GLMPRINTF(("<-X- IDirect3DDevice9::Create complete")); + + // so GetClientRect can return sane answers + //uint width, height; + RenderedSize( m_params.m_presentationParameters.BackBufferWidth, m_params.m_presentationParameters.BackBufferHeight, true ); // true = set + +#if GL_TELEMETRY_GPU_ZONES + g_TelemetryGPUStats.Clear(); +#endif + + GL_BATCH_PERF( + g_nTotalD3DCalls = 0, g_nTotalD3DCycles = 0, m_nBatchVisY = 0, m_nBatchVisFrameIndex = 0, m_nBatchVisFileIdx = 0, m_nNumProgramChanges = 0, m_flTotalD3DTime = 0, m_nTotalD3DCalls = 0, + m_flTotalD3DTime = 0, m_nTotalGLCalls = 0, m_flTotalGLTime = 0, m_nOverallDraws = 0, m_nOverallPrims = 0, m_nOverallD3DCalls = 0, m_flOverallD3DTime = 0, m_nOverallGLCalls = 0, m_flOverallGLTime = 0, m_nOverallProgramChanges = 0, + m_flOverallPresentTime = 0, m_flOverallPresentTimeSquared = 0, m_nOverallPresents = 0, m_flOverallSwapWindowTime = 0, m_flOverallSwapWindowTimeSquared = 0, m_nTotalPrims = 0; ); + + g_nTotalDrawsOrClears = 0; + + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + m_pDummy_vtx_buffer = new CGLMBuffer( m_ctx, kGLMVertexBuffer, 4096, 0 ); + m_vtx_buffers[0] = m_pDummy_vtx_buffer; + m_vtx_buffers[1] = m_pDummy_vtx_buffer; + m_vtx_buffers[2] = m_pDummy_vtx_buffer; + m_vtx_buffers[3] = m_pDummy_vtx_buffer; + + return result; +} + +IDirect3DDevice9::IDirect3DDevice9() : + m_nValidMarker( D3D_DEVICE_VALID_MARKER ) +{ +} +IDirect3DDevice9::~IDirect3DDevice9() +{ + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + delete m_pBatch_vis_bitmap; +#endif + + delete m_pDummy_vtx_buffer; + for ( int i = 0; i < 4; i++ ) + SetRenderTarget( i, NULL ); + SetDepthStencilSurface( NULL ); + if ( m_pDefaultColorSurface ) + { + m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release color surface" ); + m_pDefaultColorSurface = NULL; + } + if ( m_pDefaultDepthStencilSurface ) + { + m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::~IDirect3DDevice9 release depth surface" ); + m_pDefaultDepthStencilSurface = NULL; + } + + if ( m_pFBOs ) + { + ResetFBOMap(); + } + + GLMPRINTF(( "-D- IDirect3DDevice9::~IDirect3DDevice9 signpost" )); // want to know when this is called, if ever + + g_pD3D_Device = NULL; + if ( m_ObjectStats.m_nTotalFBOs ) GLMDebugPrintf( "Leaking %i FBOs\n", m_ObjectStats.m_nTotalFBOs ); + if ( m_ObjectStats.m_nTotalVertexShaders ) ConMsg( "Leaking %i vertex shaders\n", m_ObjectStats.m_nTotalVertexShaders ); + if ( m_ObjectStats.m_nTotalPixelShaders ) ConMsg( "Leaking %i pixel shaders\n", m_ObjectStats.m_nTotalPixelShaders ); + if ( m_ObjectStats.m_nTotalVertexDecls ) ConMsg( "Leaking %i vertex decls\n", m_ObjectStats.m_nTotalVertexDecls ); + if ( m_ObjectStats.m_nTotalIndexBuffers ) ConMsg( "Leaking %i index buffers\n", m_ObjectStats.m_nTotalIndexBuffers ); + if ( m_ObjectStats.m_nTotalVertexBuffers ) ConMsg( "Leaking %i vertex buffers\n", m_ObjectStats.m_nTotalVertexBuffers ); + if ( m_ObjectStats.m_nTotalTextures ) ConMsg( "Leaking %i textures\n", m_ObjectStats.m_nTotalTextures ); + if ( m_ObjectStats.m_nTotalSurfaces ) ConMsg( "Leaking %i surfaces\n", m_ObjectStats.m_nTotalSurfaces ); + if ( m_ObjectStats.m_nTotalQueries ) ConMsg( "Leaking %i queries\n", m_ObjectStats.m_nTotalQueries ); + if ( m_ObjectStats.m_nTotalRenderTargets ) ConMsg( "Leaking %i render targets\n", m_ObjectStats.m_nTotalRenderTargets ); + GLMgr::aGLMgr()->DelContext( m_ctx ); + m_ctx = NULL; + m_nValidMarker = 0xDEADBEEF; +} + +#ifdef OSX + +#pragma mark ----- Basics - (IDirect3DDevice9) + +#endif + + +HRESULT IDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + HRESULT result = S_OK; + + // define the task of reset as: + // provide new drawable RT's for the backbuffer (color and depthstencil). + // fix up viewport / scissor.. + // then pass the new presentation parameters through to GLM. + // (it will in turn notify appframework on the next present... which may be very soon, as mode changes are usually spotted inside Present() ). + + // so some of this looks a lot like Create - we're just a subset of what it does. + // with a little work you could refactor this to be common code. + + GLMDebugPrintf( "IDirect3DDevice9::Reset: BackBufWidth: %u, BackBufHeight: %u, D3DFMT: %u, BackBufCount: %u, MultisampleType: %u, MultisampleQuality: %u\n", + pPresentationParameters->BackBufferWidth, + pPresentationParameters->BackBufferHeight, + pPresentationParameters->BackBufferFormat, + pPresentationParameters->BackBufferCount, + pPresentationParameters->MultiSampleType, + pPresentationParameters->MultiSampleQuality ); + + //------------------------------------------------------------------------------- absorb new presentation params.. + + m_params.m_presentationParameters = *pPresentationParameters; + + //------------------------------------------------------------------------------- color buffer.. + // release old color surface if it's there.. + if ( m_pDefaultColorSurface ) + { + ULONG refc = m_pDefaultColorSurface->Release( 0, "IDirect3DDevice9::Reset public release color surface" ); (void)refc; + Assert( !refc ); + m_pDefaultColorSurface = NULL; + } + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making new color render target...")); + + // color surface + result = this->CreateRenderTarget( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.BackBufferFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + true, // lockable + &m_pDefaultColorSurface, // ppSurface + NULL // shared handle + ); + + if ( result != S_OK ) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making color render target complete -> %08x", m_pDefaultColorSurface )); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target...")); + + result = this->SetDepthStencilSurface( NULL ); + + result = this->SetRenderTarget( 0, m_pDefaultColorSurface ); + if (result != S_OK) + { + GLMPRINTF(("< IDirect3DDevice9::Reset (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting color render target complete.")); + + + //-------------------------------------------------------------------------------depth stencil buffer + // release old depthstencil surface if it's there.. + if ( m_pDefaultDepthStencilSurface ) + { + ULONG refc = m_pDefaultDepthStencilSurface->Release( 0, "IDirect3DDevice9::Reset public release depthstencil surface" ); (void)refc; + Assert(!refc); + m_pDefaultDepthStencilSurface = NULL; + } + + Assert (m_params.m_presentationParameters.EnableAutoDepthStencil); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil...")); + result = CreateDepthStencilSurface( + m_params.m_presentationParameters.BackBufferWidth, // width + m_params.m_presentationParameters.BackBufferHeight, // height + m_params.m_presentationParameters.AutoDepthStencilFormat, // format + m_params.m_presentationParameters.MultiSampleType, // MSAA depth + m_params.m_presentationParameters.MultiSampleQuality, // MSAA quality + TRUE, // enable z-buffer discard ???? + &m_pDefaultDepthStencilSurface, // ppSurface + NULL // shared handle + ); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + // do not do an AddRef here.. + + GLMPRINTF(("-X- IDirect3DDevice9::Reset making depth-stencil complete -> %08x", m_pDefaultDepthStencilSurface)); + + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target...")); + result = this->SetDepthStencilSurface(m_pDefaultDepthStencilSurface); + if (result != S_OK) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return result; + } + GLMPRINTF(("-X- IDirect3DDevice9::Reset setting depth-stencil render target complete.")); + + UpdateBoundFBO(); + + bool ready = m_ctx->m_drawingFBO->IsReady(); + if (!ready) + { + GLMPRINTF(("<-X- IDirect3DDevice9::Reset (error out)")); + return D3DERR_DEVICELOST; + } + + //-------------------------------------------------------------------------------zap viewport and scissor to new backbuffer size + + InitStates(); + + GLScissorEnable_t defScissorEnable = { true }; + GLScissorBox_t defScissorBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight }; + GLViewportBox_t defViewportBox = { 0,0, (GLsizei)m_params.m_presentationParameters.BackBufferWidth, (GLsizei)m_params.m_presentationParameters.BackBufferHeight, m_params.m_presentationParameters.BackBufferWidth | ( m_params.m_presentationParameters.BackBufferHeight << 16 ) }; + GLViewportDepthRange_t defViewportDepthRange = { 0.1, 1000.0 }; + GLCullFaceEnable_t defCullFaceEnable = { true }; + GLCullFrontFace_t defCullFrontFace = { GL_CCW }; + + gl.m_ScissorEnable = defScissorEnable; + gl.m_ScissorBox = defScissorBox; + gl.m_ViewportBox = defViewportBox; + gl.m_ViewportDepthRange = defViewportDepthRange; + gl.m_CullFaceEnable = defCullFaceEnable; + gl.m_CullFrontFace = defCullFrontFace; + + FullFlushStates(); + + //-------------------------------------------------------------------------------finally, propagate new display params to GLM context + GLMDisplayParams glmParams; + ConvertPresentationParamsToGLMDisplayParams( pPresentationParameters, &glmParams ); + + // steal back previously sent focus window... + glmParams.m_focusWindow = m_ctx->m_displayParams.m_focusWindow; + Assert( glmParams.m_focusWindow != NULL ); + + // so GetClientRect can return sane answers + //uint width, height; + RenderedSize( pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight, true ); // true = set + + m_ctx->Reset(); + + m_ctx->SetDisplayParams( &glmParams ); + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetViewport(CONST D3DVIEWPORT9* pViewport) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::SetViewport : minZ %f, maxZ %f",pViewport->MinZ, pViewport->MaxZ )); + + gl.m_ViewportBox.x = pViewport->X; + gl.m_ViewportBox.width = pViewport->Width; + + gl.m_ViewportBox.y = pViewport->Y; + gl.m_ViewportBox.height = pViewport->Height; + + gl.m_ViewportBox.widthheight = pViewport->Width | ( pViewport->Height << 16 ); + + m_ctx->WriteViewportBox( &gl.m_ViewportBox ); + + gl.m_ViewportDepthRange.flNear = pViewport->MinZ; + gl.m_ViewportDepthRange.flFar = pViewport->MaxZ; + m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetViewport( D3DVIEWPORT9* pViewport ) +{ + // 7LS - unfinished, used in scaleformuirenderimpl.cpp (only width and height required) + GL_BATCH_PERF_CALL_TIMER; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + GLMPRINTF(("-X- IDirect3DDevice9::GetViewport " )); + + pViewport->X = gl.m_ViewportBox.x; + pViewport->Width = gl.m_ViewportBox.width; + + pViewport->Y = gl.m_ViewportBox.y; + pViewport->Height = gl.m_ViewportBox.height; + + pViewport->MinZ = gl.m_ViewportDepthRange.flNear; + pViewport->MaxZ = gl.m_ViewportDepthRange.flFar; + + return S_OK; +} + +HRESULT IDirect3DDevice9::BeginScene() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->BeginFrame(); + + return S_OK; +} + +HRESULT IDirect3DDevice9::EndScene() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->EndFrame(); + return S_OK; +} + + +// stolen from glmgrbasics.cpp + +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +void IDirect3DDevice9::PrintObjectStats( const ObjectStats_t &stats ) +{ + ConMsg( "Total FBOs: %i\n", stats.m_nTotalFBOs ); + ConMsg( "Total vertex shaders: %i\n", stats.m_nTotalVertexShaders ); + ConMsg( "Total pixel shaders: %i\n", stats.m_nTotalPixelShaders ); + ConMsg( "Total vertex decls: %i\n", stats.m_nTotalVertexDecls ); + ConMsg( "Total index buffers: %i\n", stats.m_nTotalIndexBuffers ); + ConMsg( "Total vertex buffers: %i\n", stats.m_nTotalVertexBuffers ); + ConMsg( "Total textures: %i\n", stats.m_nTotalTextures ); + ConMsg( "Total surfaces: %i\n", stats.m_nTotalSurfaces ); + ConMsg( "Total queries: %i\n", stats.m_nTotalQueries ); + ConMsg( "Total render targets: %i\n", stats.m_nTotalRenderTargets ); +} + +void IDirect3DDevice9::DumpStatsToConsole( const CCommand *pArgs ) +{ +#if GL_BATCH_PERF_ANALYSIS + ConMsg( "Overall: Batches: %u, Prims: %u, Program Changes: %u\n", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); + ConMsg( "Overall: D3D Calls: %u D3D Time: %4.3fms, Avg D3D Time Per Call: %4.9fms\n", m_nOverallD3DCalls, m_flOverallD3DTime, m_nOverallD3DCalls ? ( m_flOverallD3DTime / m_nOverallD3DCalls ) : 0.0f ); + ConMsg( "Overall: GL Calls: %u GL Time: %4.3fms, Avg GL Time Per Call: %4.9fms\n", m_nOverallGLCalls, m_flOverallGLTime, m_nOverallGLCalls ? ( m_flOverallGLTime / m_nOverallGLCalls ) : 0.0f ); + + ConMsg( "D3DPresent: %u, Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", + m_nOverallPresents, + m_flOverallPresentTime, m_nOverallPresents ? ( m_flOverallPresentTime / m_nOverallPresents ) : 0.0f, + m_nOverallPresents ? ( sqrt( ( m_flOverallPresentTimeSquared / m_nOverallPresents ) - ( m_flOverallPresentTime / m_nOverallPresents ) * ( m_flOverallPresentTime / m_nOverallPresents ) ) ) : 0.0f ); + + ConMsg( "GL SwapWindow(): Overall Time: %4.3fms, Avg: %4.6fms, Std Dev: %4.6fms\n", + m_flOverallSwapWindowTime, m_nOverallPresents ? ( m_flOverallSwapWindowTime / m_nOverallPresents ) : 0.0f, + m_nOverallPresents ? ( sqrt( ( m_flOverallSwapWindowTimeSquared / m_nOverallPresents ) - ( m_flOverallSwapWindowTime / m_nOverallPresents ) * ( m_flOverallSwapWindowTime / m_nOverallPresents ) ) ) : 0.0f ); + + if ( ( pArgs ) && ( pArgs->ArgC() == 2 ) && (pArgs->Arg(1)[0] != '0') ) + { + m_nOverallDraws = 0; + m_nOverallPrims = 0; + m_nOverallProgramChanges = 0; + m_nOverallD3DCalls = 0; + m_flOverallD3DTime = 0; + m_nOverallGLCalls = 0; + m_flOverallGLTime = 0; + m_flOverallPresentTime = 0; + m_flOverallPresentTimeSquared = 0; + m_flOverallSwapWindowTime = 0; + m_flOverallSwapWindowTimeSquared = 0; + m_nOverallPresents = 0; + } +#endif + ConMsg( "Totals:\n" ); + m_ObjectStats.m_nTotalFBOs = m_pFBOs->Count(); + PrintObjectStats( m_ObjectStats ); + ObjectStats_t delta( m_ObjectStats ); + delta -= m_PrevObjectStats; + ConMsg( "Delta:\n" ); + PrintObjectStats( delta ); + m_PrevObjectStats = m_ObjectStats; +} + +static void gl_dump_stats_func( const CCommand &args ) +{ + if ( g_pD3D_Device ) + { + g_pD3D_Device->DumpStatsToConsole( &args ); + } +} + +static ConCommand gl_dump_stats( "gl_dump_stats", gl_dump_stats_func ); +#if GLMDEBUG +void IDirect3DDevice9::DumpTextures( const CCommand *pArgs ) +{ + Assert( m_nValidMarker == D3D_DEVICE_VALID_MARKER ); + (void)pArgs; + CGLMTex *pCurTex = g_pFirstCGMLTex; + if ( pCurTex ) + { + Assert( pCurTex->m_pPrevTex == NULL ); + } + ConMsg( "--- Internal CGLMTex's:\n" ); + uint nNumFound = 0; + while ( pCurTex ) + { + nNumFound++; + ConMsg( "Tex \"%s\", Layout: \"%s\", Size: %u, RT: %u, Depth: %u, Stencil: %u, MSAA: %u\n", + pCurTex->m_debugLabel ? pCurTex->m_debugLabel : "?", + pCurTex->m_layout->m_layoutSummary, + pCurTex->m_layout->m_storageTotalSize, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsDepth ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexIsStencil ) ? 1 : 0, + ( pCurTex->m_layout->m_key.m_texFlags & kGLMTexMultisampled ) ? 1 : 0 ); + CGLMTex *pNextTex = pCurTex->m_pNextTex; + if ( pNextTex ) + { + Assert( pNextTex->m_pPrevTex == pCurTex ); + } + pCurTex = pNextTex; + } + ConMsg( "--- Found %u total CGLMTex's\n", nNumFound ); +} +static void gl_dump_textures_func( const CCommand &args ) +{ + if ( g_pD3D_Device ) + { + g_pD3D_Device->DumpTextures( &args ); + } +} +static ConCommand gl_dump_textures( "gl_dump_textures", gl_dump_textures_func ); + +#endif + +ConVar gl_blitmode( "gl_blitmode", "1" ); +ConVar dxa_nullrefresh_capslock( "dxa_nullrefresh_capslock", "0" ); + +HRESULT IDirect3DDevice9::Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,VD3DHWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) +{ + GL_BATCH_PERF( g_nTotalD3DCalls++; ) + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + TOGL_NULL_DEVICE_CHECK; + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + // before attempting to present a tex, make sure it's been resolved if it was MSAA. + // if we push that responsibility down to m_ctx->Present, it could probably do it without an extra copy. + // i.e. anticipate the blit from the resolvedtex to GL_BACK, and just do that instead. + + // no explicit ResolveTex call first - that got pushed down into GLMContext::Present + +#if GL_BATCH_PERF_ANALYSIS + uint64 nStartGLCycles = gGL->m_nTotalGLCycles; + nStartGLCycles; + + CFastTimer tm; + tm.Start(); +#endif + + m_ctx->Present( m_pDefaultColorSurface->m_tex ); + +#if GL_BATCH_PERF_ANALYSIS + double flPresentTime = tm.GetDurationInProgress().GetMillisecondsF(); + double flGLSwapWindowTime = g_pLauncherMgr->GetPrevGLSwapWindowTime(); + + m_flOverallPresentTime += flPresentTime; + m_flOverallPresentTimeSquared += flPresentTime * flPresentTime; + m_flOverallSwapWindowTime += flGLSwapWindowTime; + m_flOverallSwapWindowTimeSquared += flGLSwapWindowTime * flGLSwapWindowTime; + m_nOverallPresents++; + + uint64 nEndGLCycles = gGL->m_nTotalGLCycles; + nEndGLCycles; + + m_flTotalD3DTime += flPresentTime + g_nTotalD3DCycles * s_rdtsc_to_ms; + m_nTotalD3DCalls += g_nTotalD3DCalls; + + m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + m_nTotalGLCalls += gGL->m_nTotalGLCalls; + + m_nOverallProgramChanges += m_nNumProgramChanges; + m_nOverallDraws += g_nTotalDrawsOrClears; + m_nOverallPrims += m_nTotalPrims; + m_nOverallD3DCalls += m_nTotalD3DCalls; + m_flOverallD3DTime += m_flTotalD3DTime; + m_nOverallGLCalls += m_nTotalGLCalls; + m_flOverallGLTime += m_flTotalGLTime; + + static int nPrevBatchVis = -1; + +#if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ((nPrevBatchVis == 1) && m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid()) + { + double flTotalGLPresentTime = ( nEndGLCycles - nStartGLCycles ) * s_rdtsc_to_ms; + + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 255, 16, 128); + m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLPresentTime / gl_present_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), 10, 0, 255, 128); + m_nBatchVisY += 10; + + uint y = MAX(m_nBatchVisY + 20, 600), l = 0; + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "OpenGL Frame: %u, Batches+Clears: %u, Prims: %u, Program Changes: %u", m_nOverallPresents, g_nTotalDrawsOrClears, m_nTotalPrims, m_nNumProgramChanges ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: D3D Calls: %u, D3D Time: %3.3fms", m_nTotalD3DCalls, m_flTotalD3DTime); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Frame: GL Calls: %u, GL Time: %3.3fms", m_nTotalGLCalls, m_flTotalGLTime); + l++; + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: Batches: %u, Prims: %u, Program Changes: %u", m_nOverallDraws, m_nOverallPrims, m_nOverallProgramChanges ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: D3D Calls: %u D3D Time: %4.3fms", m_nOverallD3DCalls, m_flOverallD3DTime ); + m_pBatch_vis_bitmap->draw_formatted_text(0, y+8*(l++), 1, 255, 255, 255, "Overall: GL Calls: %u GL Time: %4.3fms", m_nOverallGLCalls, m_flOverallGLTime ); + + size_t png_size = 0; + void *pPNG_data = tdefl_write_image_to_png_file_in_memory(m_pBatch_vis_bitmap->get_ptr(), m_pBatch_vis_bitmap->width(), m_pBatch_vis_bitmap->height(), 3, &png_size, true); + if (pPNG_data) + { + char filename[256]; + V_snprintf(filename, sizeof(filename), "left4dead2/batchvis_%u_%u.png", m_nBatchVisFileIdx, m_nBatchVisFrameIndex); + FILE* pFile = fopen(filename, "wb"); + if (pFile) + { + fwrite(pPNG_data, png_size, 1, pFile); + fclose(pFile); + } + free(pPNG_data); + } + m_nBatchVisFrameIndex++; + m_nBatchVisY = 0; + m_pBatch_vis_bitmap->cls(); + } +#endif + + if (nPrevBatchVis != (int)gl_batch_vis.GetBool()) + { + if ( !m_pBatch_vis_bitmap ) + m_pBatch_vis_bitmap = new simple_bitmap; + + nPrevBatchVis = gl_batch_vis.GetBool(); + if (!nPrevBatchVis) + { + DumpStatsToConsole( NULL ); + m_pBatch_vis_bitmap->clear(); + } + else + { + m_pBatch_vis_bitmap->init(768, 1024); + } + m_nBatchVisY = 0; + m_nBatchVisFrameIndex = 0; + m_nBatchVisFileIdx = (uint)time(NULL); //rand(); + + m_nOverallProgramChanges = 0; + m_nOverallDraws = 0; + m_nOverallD3DCalls = 0; + m_flOverallD3DTime = 0; + m_nOverallGLCalls = 0; + m_flOverallGLTime = 0; + m_flOverallPresentTime = 0; + m_flOverallPresentTimeSquared = 0; + m_flOverallSwapWindowTime = 0; + m_flOverallSwapWindowTimeSquared = 0; + m_nOverallPresents = 0; + } + + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + m_nNumProgramChanges = 0; + m_flTotalD3DTime = 0; + m_nTotalD3DCalls = 0; + m_flTotalGLTime = 0; + m_nTotalGLCalls = 0; + m_nTotalPrims = 0; +#else + if ( gl_batch_vis.GetBool() ) + { + gl_batch_vis.SetValue( false ); + + ConMsg( "Must define GL_BATCH_PERF_ANALYSIS to use this feature" ); + } +#endif + + g_nTotalDrawsOrClears = 0; + +#if GL_TELEMETRY_GPU_ZONES + g_TelemetryGPUStats.Clear(); +#endif + + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Textures - (IDirect3DDevice9) +#pragma mark ( create functions for each texture are now adjacent to the rest of the methods for each texture class) + +#endif + +HRESULT IDirect3DDevice9::GetTexture(DWORD Stage,IDirect3DBaseTexture9** ppTexture) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // if implemented, should it increase the ref count ?? + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- RTs and Surfaces - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateRenderTarget(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Lockable,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey rtkey; + memset( &rtkey, 0, sizeof(rtkey) ); + + rtkey.m_texGLTarget = GL_TEXTURE_2D; + rtkey.m_xSize = Width; + rtkey.m_ySize = Height; + rtkey.m_zSize = 1; + + rtkey.m_texFormat = Format; + rtkey.m_texFlags = kGLMTexRenderable; + + rtkey.m_texFlags |= kGLMTexSRGB; // all render target tex are SRGB mode + if (m_ctx->Caps().m_cantAttachSRGB) + { + // this config can't support SRGB render targets. quietly turn off the sRGB bit. + rtkey.m_texFlags &= ~kGLMTexSRGB; + } + + if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) + { + rtkey.m_texFlags |= kGLMTexMultisampled; + rtkey.m_texSamples = MultiSample; + // FIXME no support for "MS quality" yet + } + + surf->m_tex = m_ctx->NewTex( &rtkey, 1, pDebugLabel ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; //FIXME ??????????? + surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? + surf->m_desc.MultiSampleType = MultiSample; + surf->m_desc.MultiSampleQuality = MultisampleQuality; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + #if IUNKNOWN_ALLOC_SPEW + char scratch[1024]; + sprintf(scratch,"RT %s", surf->m_tex->m_layout->m_layoutSummary ); + surf->SetMark( true, scratch ); + #endif + + + return result; +} + +void IDirect3DDevice9::UpdateBoundFBO() +{ + RenderTargetState_t renderTargetState; + for ( uint i = 0; i < 4; i++ ) + { + renderTargetState.m_pRenderTargets[i] = m_pRenderTargets[i] ? m_pRenderTargets[i]->m_tex : NULL; + } + renderTargetState.m_pDepthStencil = m_pDepthStencil ? m_pDepthStencil->m_tex : NULL; + CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( renderTargetState ); + + if ( m_pFBOs->IsValidIndex( index ) ) + { + Assert( (*m_pFBOs)[index] ); + + m_ctx->m_drawingFBO = (*m_pFBOs)[index]; + } + else + { + CGLMFBO *newFBO = m_ctx->NewFBO(); + + m_pFBOs->Insert( renderTargetState, newFBO ); + + uint nNumBound = 0; + + for ( uint i = 0; i < 4; i++ ) + { + if ( !m_pRenderTargets[i] ) + continue; + + GLMFBOTexAttachParams rtParams; + memset( &rtParams, 0, sizeof(rtParams) ); + + rtParams.m_tex = m_pRenderTargets[i]->m_tex; + rtParams.m_face = m_pRenderTargets[i]->m_face; + rtParams.m_mip = m_pRenderTargets[i]->m_mip; + rtParams.m_zslice = 0; + + newFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + i) ); + nNumBound++; + } + + if ( m_pDepthStencil ) + { + GLMFBOTexAttachParams depthParams; + memset( &depthParams, 0, sizeof(depthParams) ); + + depthParams.m_tex = m_pDepthStencil->m_tex; + + EGLMFBOAttachment destAttach = (depthParams.m_tex->m_layout->m_format->m_glDataFormat != 34041) ? kAttDepth : kAttDepthStencil; + + newFBO->TexAttach( &depthParams, destAttach ); + nNumBound++; + } + + (void)nNumBound; + + Assert( nNumBound ); + +#if GLMDEBUG + Assert( newFBO->IsReady() ); +#endif + + m_ctx->m_drawingFBO = newFBO; + } + + m_ctx->BindFBOToCtx( m_ctx->m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + m_bFBODirty = false; +} + +void IDirect3DDevice9::ResetFBOMap() +{ + if ( !m_pFBOs ) + return; + + FOR_EACH_MAP_FAST( (*m_pFBOs), i ) + { + const RenderTargetState_t &rtState = m_pFBOs->Key( i ); (void)rtState; + CGLMFBO *pFBO = (*m_pFBOs)[i]; + + m_ctx->DelFBO( pFBO ); + } + + m_pFBOs->Purge(); + + m_bFBODirty = true; +} + +void IDirect3DDevice9::ScrubFBOMap( CGLMTex *pTex ) +{ + Assert( pTex ); + + if ( !m_pFBOs ) + return; + + CUtlVectorFixed< RenderTargetState_t, 128 > fbosToRemove; + + FOR_EACH_MAP_FAST( (*m_pFBOs), i ) + { + const RenderTargetState_t &rtState = m_pFBOs->Key( i ); + CGLMFBO *pFBO = (*m_pFBOs)[i]; (void)pFBO; + + if ( rtState.RefersTo( pTex ) ) + { + fbosToRemove.AddToTail( rtState ); + } + } + + for ( int i = 0; i < fbosToRemove.Count(); ++i ) + { + const RenderTargetState_t &rtState = fbosToRemove[i]; + + CUtlMap < RenderTargetState_t, CGLMFBO * >::IndexType_t index = m_pFBOs->Find( rtState ); + + if ( !m_pFBOs->IsValidIndex( index ) ) + { + Assert( 0 ); + continue; + } + + CGLMFBO *pFBO = (*m_pFBOs)[index]; + + m_ctx->DelFBO( pFBO ); + + m_pFBOs->RemoveAt( index ); + + m_bFBODirty = true; + } + + //GLMDebugPrintf( "IDirect3DDevice9::ScrubFBOMap: Removed %u entries\n", fbosToRemove.Count() ); +} +HRESULT IDirect3DDevice9::SetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9* pRenderTarget) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + + Assert( RenderTargetIndex < 4 ); + + HRESULT result = S_OK; + + GLMPRINTF(("-F- SetRenderTarget index=%d, surface=%8x (tex=%8x %s)", + RenderTargetIndex, + pRenderTarget, + pRenderTarget ? pRenderTarget->m_tex : NULL, + pRenderTarget ? pRenderTarget->m_tex->m_layout->m_layoutSummary : "" + )); + + // note that it is OK to pass NULL for pRenderTarget, it implies that you would like to detach any color buffer from that target index + + // behaviors... + // if new surf is same as old surf, no change in refcount, in fact, it's early exit + IDirect3DSurface9 *oldTarget = m_pRenderTargets[RenderTargetIndex]; + + if (pRenderTarget == oldTarget) + { + GLMPRINTF(("-F- --> no change",RenderTargetIndex)); + return S_OK; + } + + + // Fix this if porting to x86_64 + if ( m_pRenderTargets[RenderTargetIndex] ) + + + { + + + + + + + + + + + + // we now know that the new surf is not the same as the old surf. + // you can't assume either one is non NULL here though. + + m_pRenderTargets[RenderTargetIndex]->Release( 1, "-A SetRenderTarget private release" ); + } + + if (pRenderTarget) + { + pRenderTarget->AddRef( 1, "+A SetRenderTarget private addref" ); // again, private refcount being raised + } + m_pRenderTargets[RenderTargetIndex] = pRenderTarget; + + m_bFBODirty = true; + +/* + if (!pRenderTarget) + { + GLMPRINTF(("-F- --> Setting NULL render target on index=%d ",RenderTargetIndex)); + } + else + { + GLMPRINTF(("-F- --> attaching index=%d on drawing FBO (%8x)",RenderTargetIndex, m_drawableFBO)); + // attach color to FBO + GLMFBOTexAttachParams rtParams; + memset( &rtParams, 0, sizeof(rtParams) ); + + rtParams.m_tex = pRenderTarget->m_tex; + rtParams.m_face = pRenderTarget->m_face; + rtParams.m_mip = pRenderTarget->m_mip; + rtParams.m_zslice = 0; // FIXME if you ever want to be able to render to slices of a 3D tex.. + + m_drawableFBO->TexAttach( &rtParams, (EGLMFBOAttachment)(kAttColor0 + RenderTargetIndex) ); + } +*/ + +#if GL_BATCH_PERF_ANALYSIS && GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() && !RenderTargetIndex ) + { + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, m_pBatch_vis_bitmap->width(), 1, 30, 20, 20); + m_nBatchVisY += 1; + } +#endif + + return result; +} + + +HRESULT IDirect3DDevice9::GetRenderTarget(DWORD RenderTargetIndex,IDirect3DSurface9** ppRenderTarget) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if ( !m_pRenderTargets[ RenderTargetIndex ] ) + return D3DERR_NOTFOUND; + + if ( ( RenderTargetIndex > 4 ) || !ppRenderTarget ) + return D3DERR_INVALIDCALL; + + // safe because of early exit on NULL above + m_pRenderTargets[ RenderTargetIndex ]->AddRef(0, "+B GetRenderTarget public addref"); // per http://msdn.microsoft.com/en-us/library/bb174404(VS.85).aspx + + *ppRenderTarget = m_pRenderTargets[ RenderTargetIndex ]; + + return S_OK; +} + +HRESULT IDirect3DDevice9::CreateOffscreenPlainSurface( UINT Width,UINT Height,D3DFORMAT Format,D3DPOOL Pool,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // set surf->m_restype to D3DRTYPE_SURFACE... + + // this is almost identical to CreateRenderTarget.. + + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey rtkey; + memset( &rtkey, 0, sizeof(rtkey) ); + + rtkey.m_texGLTarget = GL_TEXTURE_2D; + rtkey.m_xSize = Width; + rtkey.m_ySize = Height; + rtkey.m_zSize = 1; + + rtkey.m_texFormat = Format; + rtkey.m_texFlags = kGLMTexRenderable; + + surf->m_tex = m_ctx->NewTex( &rtkey, 1, "offscreen plain surface" ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; + surf->m_desc.Pool = D3DPOOL_DEFAULT; + surf->m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; + surf->m_desc.MultiSampleQuality = 0; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + return result; +} + +HRESULT IDirect3DDevice9::CreateDepthStencilSurface(UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,DWORD MultisampleQuality,BOOL Discard,IDirect3DSurface9** ppSurface,VD3DHANDLE* pSharedHandle) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( ( Format == D3DFMT_D16 ) || ( Format == D3DFMT_D24X8 ) || ( Format == D3DFMT_D24S8 ) ); + HRESULT result = S_OK; + + m_ObjectStats.m_nTotalSurfaces++; + m_ObjectStats.m_nTotalRenderTargets++; + + IDirect3DSurface9 *surf = new IDirect3DSurface9; + surf->m_restype = D3DRTYPE_SURFACE; + + surf->m_device = this; // always set device on creations! + + GLMTexLayoutKey depthkey; + memset( &depthkey, 0, sizeof(depthkey) ); + + depthkey.m_texGLTarget = GL_TEXTURE_2D; + depthkey.m_xSize = Width; + depthkey.m_ySize = Height; + depthkey.m_zSize = 1; + + depthkey.m_texFormat = Format; + depthkey.m_texFlags = kGLMTexRenderable | kGLMTexIsDepth; + + if ( Format == D3DFMT_D24S8 ) + { + depthkey.m_texFlags |= kGLMTexIsStencil; + } + + if ( (MultiSample !=0) && (!m_ctx->Caps().m_nvG7x) ) + { + depthkey.m_texFlags |= kGLMTexMultisampled; + depthkey.m_texSamples = MultiSample; + // FIXME no support for "MS quality" yet + } + + surf->m_tex = m_ctx->NewTex( &depthkey, 1, "depth-stencil surface" ); + surf->m_face = 0; + surf->m_mip = 0; + + //desc + + surf->m_desc.Format = Format; + surf->m_desc.Type = D3DRTYPE_SURFACE; + surf->m_desc.Usage = 0; //FIXME ??????????? + surf->m_desc.Pool = D3DPOOL_DEFAULT; //FIXME ??????????? + surf->m_desc.MultiSampleType = MultiSample; + surf->m_desc.MultiSampleQuality = MultisampleQuality; + surf->m_desc.Width = Width; + surf->m_desc.Height = Height; + + *ppSurface = (result==S_OK) ? surf : NULL; + + return result; +} + +HRESULT IDirect3DDevice9::SetDepthStencilSurface( IDirect3DSurface9* pNewZStencil ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = S_OK; + + GLMPRINTF(("-F- SetDepthStencilSurface, surface=%8x (tex=%8x %s)", + pNewZStencil, + pNewZStencil ? pNewZStencil->m_tex : NULL, + pNewZStencil ? pNewZStencil->m_tex->m_layout->m_layoutSummary : "" + )); + + if ( pNewZStencil == m_pDepthStencil ) + { + GLMPRINTF(("-F- --> no change")); + return S_OK; + } + + if ( pNewZStencil ) + { + pNewZStencil->AddRef(1, "+A SetDepthStencilSurface private addref"); + } + + if ( m_pDepthStencil ) + { + // Note this Release() could cause the surface to be deleted! + m_pDepthStencil->Release(1, "-A SetDepthStencilSurface private release"); + } + + m_pDepthStencil = pNewZStencil; + + m_bFBODirty = true; + + return result; +} + +HRESULT IDirect3DDevice9::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if ( !ppZStencilSurface ) + { + return D3DERR_INVALIDCALL; + } + + if ( !m_pDepthStencil ) + { + *ppZStencilSurface = NULL; + return D3DERR_NOTFOUND; + } + + m_pDepthStencil->AddRef(0, "+B GetDepthStencilSurface public addref"); // per http://msdn.microsoft.com/en-us/library/bb174384(VS.85).aspx + + *ppZStencilSurface = m_pDepthStencil; + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetRenderTargetData(IDirect3DSurface9* pRenderTarget,IDirect3DSurface9* pDestSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // is it just a blit ? + + this->StretchRect( pRenderTarget, NULL, pDestSurface, NULL, D3DTEXF_NONE ); // is this good enough ??? + + return S_OK; +} + +HRESULT IDirect3DDevice9::GetFrontBufferData(UINT iSwapChain,IDirect3DSurface9* pDestSurface) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::StretchRect(IDirect3DSurface9* pSourceSurface,CONST RECT* pSourceRect,IDirect3DSurface9* pDestSurface,CONST RECT* pDestRect,D3DTEXTUREFILTERTYPE Filter) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // find relevant slices in GLM tex + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + CGLMTex *srcTex = pSourceSurface->m_tex; + int srcSliceIndex = srcTex->CalcSliceIndex( pSourceSurface->m_face, pSourceSurface->m_mip ); + GLMTexLayoutSlice *srcSlice = &srcTex->m_layout->m_slices[ srcSliceIndex ]; + + CGLMTex *dstTex = pDestSurface->m_tex; + int dstSliceIndex = dstTex->CalcSliceIndex( pDestSurface->m_face, pDestSurface->m_mip ); + GLMTexLayoutSlice *dstSlice = &dstTex->m_layout->m_slices[ dstSliceIndex ]; + + if ( dstTex->m_rboName != 0 ) + { + Assert(!"No path yet for blitting into an MSAA tex"); + return S_OK; + } + + bool useFastBlit = (gl_blitmode.GetInt() != 0); + + if ( !useFastBlit && (srcTex->m_rboName !=0)) // old way, we do a resolve to scratch tex first (necessitating two step blit) + { + m_ctx->ResolveTex( srcTex, true ); + } + + // set up source/dest rect in GLM form + GLMRect srcRect, dstRect; + + // d3d nomenclature: + // Y=0 is the visual top and also aligned with V=0. + + srcRect.xmin = pSourceRect ? pSourceRect->left : 0; + srcRect.xmax = pSourceRect ? pSourceRect->right : srcSlice->m_xSize; + srcRect.ymin = pSourceRect ? pSourceRect->top : 0; + srcRect.ymax = pSourceRect ? pSourceRect->bottom : srcSlice->m_ySize; + + dstRect.xmin = pDestRect ? pDestRect->left : 0; + dstRect.xmax = pDestRect ? pDestRect->right : dstSlice->m_xSize; + dstRect.ymin = pDestRect ? pDestRect->top : 0; + dstRect.ymax = pDestRect ? pDestRect->bottom : dstSlice->m_ySize; + + GLenum filterGL = 0; + switch(Filter) + { + case D3DTEXF_NONE: + case D3DTEXF_POINT: + filterGL = GL_NEAREST; + break; + + case D3DTEXF_LINEAR: + filterGL = GL_LINEAR; + break; + + default: // D3DTEXF_ANISOTROPIC + Assert(!"Impl aniso stretch"); + break; + } + + if (useFastBlit) + { + m_ctx->Blit2( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, + dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, + filterGL + ); + } + else + { + m_ctx->BlitTex( srcTex, &srcRect, pSourceSurface->m_face, pSourceSurface->m_mip, + dstTex, &dstRect, pDestSurface->m_face, pDestSurface->m_mip, + filterGL + ); + } + + return S_OK; +} + + +// This totally sucks, but this information can't be gleaned any +// other way when translating from D3D to GL at this level +// +// This returns a mask, since multiple GLSL "varyings" can be tagged with centroid +static uint32 CentroidMaskFromName( bool bPixelShader, const char *pName ) +{ + // Important note: This code has been customized for TF2 - don't blindly merge it into other branches! + if ( !pName ) + return 0; + + // Important: The centroid bitflags must match between all linked vertex/pixel shaders! + if ( bPixelShader ) + { + if ( V_stristr( pName, "lightmappedgeneric_ps" ) || V_strstr( pName, "worldtwotextureblend_ps" ) ) + { + return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 + } + else if ( V_stristr( pName, "lightmappedreflective_ps" ) ) + { + return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 + } + else if ( V_stristr( pName, "water_ps" ) ) + { + return 0xC0; + } + else if ( V_stristr( pName, "shadow_ps" ) ) + { + return 0x1F; + } + else if ( V_stristr( pName, "ShatteredGlass_ps" ) ) + { + return 0xC; + } + else if ( V_stristr( pName, "WorldVertexAlpha_ps" ) || V_stristr( pName, "WorldVertexTransition_ps" ) ) + { + // These pixel shaders want centroid but shouldn't be used + Assert(0); + return 0; + } + else if ( V_stristr( pName, "flashlight_ps" ) ) + { + return 0xC; + } + } + else // vertex shader + { + // Vertex shaders also + if ( V_stristr( pName, "lightmappedgeneric_vs" ) ) + { + return (0x01 << 2) | (0x01 << 3); // iterators 2 and 3 + } + else if ( V_stristr( pName, "lightmappedreflective_vs" ) ) + { + return (0x01 << 6) | (0x01 << 7); // iterators 6 and 7 + } + else if ( V_stristr( pName, "water_vs" ) ) + { + return 0xC0; + } + else if ( V_stristr( pName, "shadow_vs" ) ) + { + return 0x1F; + } + else if ( V_stristr( pName, "ShatteredGlass_vs" ) ) + { + return 0xC; + } + else if ( V_stristr( pName, "flashlight_vs" ) ) + { + return 0xC; + } + } + + // This shader doesn't have any centroid iterators + return 0; +} + + +// This totally sucks, but this information can't be gleaned any +// other way when translating from D3D to GL at this level +static int ShadowDepthSamplerMaskFromName( const char *pName ) +{ + if ( !pName ) + return 0; + + if ( V_stristr( pName, "water_ps" ) ) + { + return (1<<7); + } + else if ( V_stristr( pName, "infected_ps" ) ) + { + return (1<<1); + } + else if ( V_stristr( pName, "phong_ps" ) ) + { + return (1<<4) | (1<<15); + } + else if ( V_stristr( pName, "vertexlit_and_unlit_generic_bump_ps" ) ) + { + return (1<<8) | (1<<15); + } + else if ( V_stristr( pName, "vertexlit_and_unlit_generic_ps" ) ) + { + return (1<<8) | (1<<15); + } + else if ( V_stristr( pName, "eye_refract_ps" ) ) + { + return (1<<6); + } + else if ( V_stristr( pName, "eyes_flashlight_ps" ) ) + { + return (1<<4); + } + else if ( V_stristr( pName, "worldtwotextureblend_ps" ) ) + { + return (1<<7); + } + else if ( V_stristr( pName, "teeth_flashlight_ps" ) ) + { + return (1<<2); + } + else if ( V_stristr( pName, "flashlight_ps" ) ) // substring of above, make sure this comes last!! + { + return (1<<7); + } + else if ( V_stristr( pName, "lightmappedgeneric_ps" ) ) + { + return (1<<15); + } + else if ( V_stristr( pName, "deferred_global_light_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "global_lit_simple_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "lightshafts_ps" ) ) + { + return (1<<1); + } + else if ( V_stristr( pName, "multiblend_combined_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "multiblend_ps" ) ) + { + return (1<<14); + } + else if ( V_stristr( pName, "customhero_ps" ) ) + { + return (1<<14); + } + + // This shader doesn't have a shadow depth map sampler + return 0; +} + +#ifdef OSX + +#pragma mark ----- Pixel Shaders - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreatePixelShader(CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader, const char *pShaderName, char *pDebugLabel, const uint32 *pCentroidMask ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = D3DERR_INVALIDCALL; + *ppShader = NULL; + + int nShadowDepthSamplerMask = ShadowDepthSamplerMaskFromName( pShaderName ); + uint nCentroidMask = CentroidMaskFromName( true, pShaderName ); + + if ( pCentroidMask ) + { + if ( *pCentroidMask != nCentroidMask ) + { + GLMDebugPrintf( "IDirect3DDevice9::CreatePixelShader: shaderapi's centroid mask (0x%08X) differs from mask derived from shader name (0x%08X) for shader %s\n", *pCentroidMask, nCentroidMask, pDebugLabel ); + } + // It would be great if we could use these centroid masks passed in from shaderapi - but unfortunately they're only available for pixel shaders, and we also need to compute matching masks for vertex shaders! + //nCentroidMask = *pCentroidMask; + } + + { + int numTranslations = 1; + + bool bVertexShader = false; + + // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. + // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. + + int maxTranslationSize = 50000; // size of any one translation + + CUtlBuffer transbuf( 3000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer tempbuf( 3000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + + transbuf.PutString( "//GLSLfp\n" ); // this is required so GLM can crack the text apart + + // note the GLSL translator wants its own buffer + tempbuf.EnsureCapacity( maxTranslationSize ); + + uint glslPixelShaderOptions = D3DToGL_OptionUseEnvParams;// | D3DToGL_OptionAllowStaticControlFlow; + + + // Fake SRGB mode - needed on R500, probably indefinitely. + // Do this stuff if caps show m_needsFakeSRGB=true and the sRGBWrite state is true + // (but not if it's engine_post which is special) + + if (!m_ctx->Caps().m_hasGammaWrites) + { + if ( pShaderName ) + { + if ( !V_stristr( pShaderName, "engine_post" ) ) + { + glslPixelShaderOptions |= D3DToGL_OptionSRGBWriteSuffix; + } + } + } + + g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslPixelShaderOptions, nShadowDepthSamplerMask, nCentroidMask, pDebugLabel ); + + transbuf.PutString( (char*)tempbuf.Base() ); + transbuf.PutString( "\n\n" ); // whitespace + + if ( bVertexShader ) + { + // don't cross the streams + Assert(!"Can't accept vertex shader in CreatePixelShader"); + result = D3DERR_INVALIDCALL; + } + else + { + m_ObjectStats.m_nTotalPixelShaders++; + + IDirect3DPixelShader9 *newprog = new IDirect3DPixelShader9; + + newprog->m_pixHighWater = 0; + newprog->m_pixSamplerMask = 0; + newprog->m_pixSamplerTypes = 0; + + newprog->m_pixProgram = m_ctx->NewProgram( kGLMFragmentProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; + newprog->m_pixProgram->m_nCentroidMask = nCentroidMask; + newprog->m_pixProgram->m_nShadowDepthSamplerMask = nShadowDepthSamplerMask; + + newprog->m_pixProgram->m_bTranslatedProgram = true; + newprog->m_pixProgram->m_maxVertexAttrs = 0; + + newprog->m_device = this; + + //------ find the frag program metadata and extract it.. + + + { + // find the highwater mark + char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); + if (highWaterStr) + { + char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_pixHighWater = value; + newprog->m_pixProgram->m_descs[kGLMGLSL].m_highWater = value; + } + else + { + Assert(!"couldn't find sampler map in pixel shader"); + } + } + + { + // find the sampler map + char *samplerMaskPrefix = "//SAMPLERMASK-"; // try to arrange this so it can work with pure GLSL if needed + + char *samplerMaskStr = strstr( (char *)transbuf.Base(), samplerMaskPrefix ); + if (samplerMaskStr) + { + char *samplerMaskActualData = samplerMaskStr + strlen( samplerMaskPrefix ); + + int value = -1; + sscanf( samplerMaskActualData, "%04x", &value ); + + newprog->m_pixSamplerMask = value; + newprog->m_pixProgram->m_samplerMask = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes + + int nMaxReg; + for ( nMaxReg = 31; nMaxReg >= 0; --nMaxReg ) + if ( value & ( 1 << nMaxReg ) ) + break; + + newprog->m_pixProgram->m_maxSamplers = nMaxReg + 1; + + int nNumUsedSamplers = 0; + for ( int i = 31; i >= 0; --i) + if ( value & ( 1 << i ) ) + nNumUsedSamplers++; + newprog->m_pixProgram->m_nNumUsedSamplers = nNumUsedSamplers; + } + else + { + Assert(!"couldn't find sampler map in pixel shader"); + } + } + + { + // find the sampler map + char *samplerTypesPrefix = "//SAMPLERTYPES-"; // try to arrange this so it can work with pure GLSL if needed + + char *samplerTypesStr = strstr( (char *)transbuf.Base(), samplerTypesPrefix ); + if (samplerTypesStr) + { + char *samplerTypesActualData = samplerTypesStr + strlen( samplerTypesPrefix ); + + int value = -1; + sscanf( samplerTypesActualData, "%08x", &value ); + + newprog->m_pixSamplerTypes = value; + newprog->m_pixProgram->m_samplerTypes = value; // helps GLM maintain a better linked pair cache even when SRGB sampler state changes + } + else + { + Assert(!"couldn't find sampler types in pixel shader"); + } + } + + { + // find the fb outputs used by this shader/combo + const GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT }; + + char *fragDataMaskPrefix = "//FRAGDATAMASK-"; + + char *fragDataMaskStr = strstr( (char *)transbuf.Base(), fragDataMaskPrefix ); + if ( fragDataMaskStr ) + { + char *fragDataActualData = fragDataMaskStr + strlen( fragDataMaskPrefix ); + + int value = -1; + sscanf( fragDataActualData, "%04x", &value ); + + newprog->m_pixFragDataMask = value; + newprog->m_pixProgram->m_fragDataMask = value; + + newprog->m_pixProgram->m_numDrawBuffers = 0; + for( int i = 0; i < 4; i++ ) + { + if( newprog->m_pixProgram->m_fragDataMask & ( 1 << i ) ) + { + newprog->m_pixProgram->m_drawBuffers[ newprog->m_pixProgram->m_numDrawBuffers ] = buffers[ i ]; + newprog->m_pixProgram->m_numDrawBuffers++; + } + } + + if( newprog->m_pixProgram->m_numDrawBuffers == 0 ) + { + Assert(!"couldn't find fragment output in pixel shader"); + newprog->m_pixProgram->m_drawBuffers[ 0 ] = buffers[ 0 ]; + newprog->m_pixProgram->m_numDrawBuffers = 1; + } + } + else + { + newprog->m_pixFragDataMask = 0; + newprog->m_pixProgram->m_fragDataMask = 0; + } + + } + + *ppShader = newprog; + + result = S_OK; + } + } + + + return result; +} + +IDirect3DPixelShader9::~IDirect3DPixelShader9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DPixelShader9" )); + + if (m_device) + { + m_device->ReleasedPixelShader( this ); + + if (m_pixProgram) + { + m_pixProgram->m_ctx->DelProgram( m_pixProgram ); + m_pixProgram = NULL; + } + m_device = NULL; + } + + GLMPRINTF(( "<-A- ~IDirect3DPixelShader9" )); +} + + +HRESULT IDirect3DDevice9::SetPixelShaderNonInline(IDirect3DPixelShader9* pShader) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + m_ctx->SetFragmentProgram( pShader ? pShader->m_pixProgram : NULL ); + m_pixelShader = pShader; + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; +#if 0 + const uint nRegToWatch = 3; + if ( ( ( StartRegister + Vector4fCount ) > nRegToWatch ) && ( StartRegister <= nRegToWatch ) ) + { + GLMDebugPrintf( "-- %f %f %f %f\n", pConstantData[(nRegToWatch - StartRegister)*4+0], pConstantData[(nRegToWatch - StartRegister)*4+1], pConstantData[(nRegToWatch - StartRegister)*4+2], pConstantData[(nRegToWatch - StartRegister)*4+3] ); + } +#endif + m_ctx->SetProgramParametersF( kGLMFragmentProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantB(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersB( kGLMFragmentProgram, StartRegister, (int *)pConstantData, BoolCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetPixelShaderConstantI(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + GLMPRINTF(("-X- Ignoring IDirect3DDevice9::SetPixelShaderConstantI call, count was %d", Vector4iCount )); +// m_ctx->SetProgramParametersI( kGLMFragmentProgram, StartRegister, pConstantData, Vector4iCount ); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Shaders - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader, const char *pShaderName, char *pDebugLabel) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + HRESULT result = D3DERR_INVALIDCALL; + *ppShader = NULL; + + uint32 nCentroidMask = CentroidMaskFromName( false, pShaderName ); + + { + int numTranslations = 1; + + bool bVertexShader = false; + + // we can do one or two translated forms. they go together in a single buffer with some markers to allow GLM to break it up. + // this also lets us mirror each set of translations to disk with a single file making it easier to view and edit side by side. + + int maxTranslationSize = 500000; // size of any one translation + + CUtlBuffer transbuf( 1000, numTranslations * maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + CUtlBuffer tempbuf( 1000, maxTranslationSize, CUtlBuffer::TEXT_BUFFER ); + + transbuf.PutString( "//GLSLvp\n" ); // this is required so GLM can crack the text apart + + // note the GLSL translator wants its own buffer + tempbuf.EnsureCapacity( maxTranslationSize ); + + uint glslVertexShaderOptions = D3DToGL_OptionUseEnvParams | D3DToGL_OptionDoFixupZ | D3DToGL_OptionDoFixupY; + + if ( m_ctx->Caps().m_hasNativeClipVertexMode ) + { + // note the matched trickery over in IDirect3DDevice9::FlushStates - + // if on a chipset that does no have native gl_ClipVertex support, then + // omit writes to gl_ClipVertex, and instead submit plane equations that have been altered, + // and clipping will take place in GL space using gl_Position instead of gl_ClipVertex. + + // note that this is very much a hack to mate up with ATI R5xx hardware constraints, and with older + // drivers even for later ATI parts like r6xx/r7xx. And it doesn't work on NV parts, so you really + // do have to choose the right way to go. + + glslVertexShaderOptions |= D3DToGL_OptionDoUserClipPlanes; + } + + if ( !CommandLine()->CheckParm("-disableboneuniformbuffers") ) + { + // If using GLSL, enabling a uniform buffer specifically for bone registers. (Not currently supported with ARB shaders, which are not optimized at all anyway.) + glslVertexShaderOptions |= D3DToGL_OptionGenerateBoneUniformBuffer; + } + + g_D3DToOpenGLTranslatorGLSL.TranslateShader( (uint32 *) pFunction, &tempbuf, &bVertexShader, glslVertexShaderOptions, -1, nCentroidMask, pDebugLabel ); + + transbuf.PutString( (char*)tempbuf.Base() ); + transbuf.PutString( "\n\n" ); // whitespace + + if ( !bVertexShader ) + { + // don't cross the streams + Assert(!"Can't accept pixel shader in CreateVertexShader"); + result = D3DERR_INVALIDCALL; + } + else + { + m_ObjectStats.m_nTotalVertexShaders++; + + IDirect3DVertexShader9 *newprog = new IDirect3DVertexShader9; + + newprog->m_device = this; + + newprog->m_vtxProgram = m_ctx->NewProgram( kGLMVertexProgram, (char *)transbuf.Base(), pShaderName ? pShaderName : "?" ) ; + newprog->m_vtxProgram->m_nCentroidMask = nCentroidMask; + + newprog->m_vtxProgram->m_bTranslatedProgram = true; + newprog->m_vtxProgram->m_maxVertexAttrs = 0; + newprog->m_maxVertexAttrs = 0; + + // find the highwater mark.. + + char *highWaterPrefix = "//HIGHWATER-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterStr = strstr( (char *)transbuf.Base(), highWaterPrefix ); + if (highWaterStr) + { + char *highWaterActualData = highWaterStr + strlen( highWaterPrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_vtxHighWater = value; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_highWater = value; + } + else + { + Assert(!"couldn't find highwater mark in vertex shader"); + } + + char *highWaterBonePrefix = "//HIGHWATERBONE-"; // try to arrange this so it can work with pure GLSL if needed + char *highWaterBoneStr = strstr( (char *)transbuf.Base(), highWaterBonePrefix ); + if (highWaterBoneStr) + { + char *highWaterActualData = highWaterBoneStr + strlen( highWaterBonePrefix ); + + int value = -1; + sscanf( highWaterActualData, "%d", &value ); + + newprog->m_vtxHighWaterBone = value; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = value; + } + else + { + newprog->m_vtxHighWaterBone = 0; + newprog->m_vtxProgram->m_descs[kGLMGLSL].m_VSHighWaterBone = 0; + } + + // find the attrib map.. + char *attribMapPrefix = "//ATTRIBMAP-"; // try to arrange this so it can work with pure GLSL if needed + char *attribMapStr = strstr( (char *)transbuf.Base(), attribMapPrefix ); + + if (attribMapStr) + { + char *attribMapActualData = attribMapStr + strlen( attribMapPrefix ); + uint nMaxVertexAttribs = 0; + for( int i=0; i<16; i++) + { + int value = -1; + char *dataItem = attribMapActualData + (i*3); + sscanf( dataItem, "%02x", &value ); + if (value >=0) + { + // make sure it's not a terminator + if (value == 0xBB) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + } + else + { + // probably an 'xx'... check + if ( (dataItem[0] != 'x') || (dataItem[1] != 'x') ) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad news + } + else + { + value = 0xBB; // not likely to see one of these... "fog with usage index 11" + } + } + + if ( value != 0xBB ) + nMaxVertexAttribs = i; + + newprog->m_vtxAttribMap[i] = value; + } + + newprog->m_vtxProgram->m_maxVertexAttrs = nMaxVertexAttribs + 1; + newprog->m_maxVertexAttrs = nMaxVertexAttribs + 1; + } + else + { + DXABSTRACT_BREAK_ON_ERROR(); // that's bad... + } + + *ppShader = newprog; + + result = S_OK; + } + } + + return result; +} + +IDirect3DVertexShader9::~IDirect3DVertexShader9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(( ">-A- ~IDirect3DVertexShader9" )); + + if (m_device) + { + m_device->ReleasedVertexShader( this ); + + if (m_vtxProgram) + { + m_vtxProgram->m_ctx->DelProgram( m_vtxProgram ); + m_vtxProgram = NULL; + } + m_device = NULL; + } + else + { + } + + + GLMPRINTF(( "<-A- ~IDirect3DVertexShader9" )); +} + +HRESULT IDirect3DDevice9::SetVertexShaderNonInline(IDirect3DVertexShader9* pShader) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_ctx->SetVertexProgram( pShader ? pShader->m_vtxProgram : NULL ); + m_vertexShader = pShader; + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantFNonInline(UINT StartRegister,CONST float* pConstantData,UINT Vector4fCount) // groups of 4 floats! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersF( kGLMVertexProgram, StartRegister, (float *)pConstantData, Vector4fCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantBNonInline(UINT StartRegister,CONST BOOL* pConstantData,UINT BoolCount) // individual bool count! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersB( kGLMVertexProgram, StartRegister, (int *)pConstantData, BoolCount ); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetVertexShaderConstantINonInline(UINT StartRegister,CONST int* pConstantData,UINT Vector4iCount) // groups of 4 ints! +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + m_ctx->SetProgramParametersI( kGLMVertexProgram, StartRegister, (int *)pConstantData, Vector4iCount ); + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Shader Pairs - (IDirect3DDevice9) + +#endif + +// callers need to ifdef POSIX this, because this method does not exist on real DX9 +HRESULT IDirect3DDevice9::LinkShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage + if (vs->m_vtxProgram && ps->m_pixProgram) + { + m_ctx->LinkShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); + } + return S_OK; +} + +HRESULT IDirect3DDevice9::ValidateShaderPair( IDirect3DVertexShader9* vs, IDirect3DPixelShader9* ps ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" but the old reference to "program" persists due to the assembler heritage + if (vs->m_vtxProgram && ps->m_pixProgram) + { + m_ctx->ValidateShaderPair( vs->m_vtxProgram, ps->m_pixProgram ); + } + return S_OK; +} + +// callers need to ifdef POSIX this, because this method does not exist on real DX9 +// +HRESULT IDirect3DDevice9::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + GL_BATCH_PERF_CALL_TIMER; + // these are really GLSL "shaders" not "programs" ... + + m_ctx->QueryShaderPair( index, infoOut ); + + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Buffers and Vertex Declarations - (IDirect3DDevice9) + +#endif + +HRESULT IDirect3DDevice9::CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements,IDirect3DVertexDeclaration9** ppDecl) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + *ppDecl = NULL; + + // the goal here is to arrive at something which lets us quickly generate GLMVertexSetups. + + // the information we don't have, that must be inferred from the decls, is: + // -> how many unique streams (buffers) are used - pure curiosity + // -> what the stride and offset is for each decl. Size you can figure out on the spot, stride requires surveying all the components in each stream first. + // so init an array of per-stream offsets to 0. + // each one is a cursor that gets bumped by decls. + uint streamOffsets[ D3D_MAX_STREAMS ]; + uint streamCount = 0; + + uint attribMap[16]; + uint attribMapIndex = 0; + memset( attribMap, 0xFF, sizeof( attribMap ) ); + + memset( streamOffsets, 0, sizeof( streamOffsets ) ); + + m_ObjectStats.m_nTotalVertexDecls++; + + IDirect3DVertexDeclaration9 *decl9 = new IDirect3DVertexDeclaration9; + decl9->m_device = this; + + decl9->m_elemCount = 0; + + for (const D3DVERTEXELEMENT9 *src = pVertexElements; (src->Stream != 0xFF); src++) + { + // element + D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ decl9->m_elemCount++ ]; + + // copy the D3D decl wholesale. + elem->m_dxdecl = *src; + + // latch current offset in this stream. + elem->m_gldecl.m_offset = streamOffsets[ elem->m_dxdecl.Stream ]; + + // figure out size of this attr and move the cursor + // if cursor was on zero, bump the active stream count + + if (!streamOffsets[ elem->m_dxdecl.Stream ]) + streamCount++; + + int bytes = 0; + switch( elem->m_dxdecl.Type ) + { + case D3DDECLTYPE_FLOAT1: elem->m_gldecl.m_nCompCount = 1; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 4; break; + case D3DDECLTYPE_FLOAT2: elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 8; break; + + //case D3DVSDT_FLOAT3: + case D3DDECLTYPE_FLOAT3: elem->m_gldecl.m_nCompCount = 3; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 12; break; + + //case D3DVSDT_FLOAT4: + case D3DDECLTYPE_FLOAT4: elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_FLOAT; elem->m_gldecl.m_normalized=0; bytes = 16; break; + + // case D3DVSDT_UBYTE4: + case D3DDECLTYPE_D3DCOLOR: + case D3DDECLTYPE_UBYTE4: + case D3DDECLTYPE_UBYTE4N: + + // Force this path since we're on 10.6.2 and can't rely on EXT_vertex_array_bgra + if ( 1 ) + { + // pass 4 UB's but we know this is out of order compared to D3DCOLOR data + elem->m_gldecl.m_nCompCount = 4; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; + } + else + { + // pass a GL BGRA color courtesy of http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt + elem->m_gldecl.m_nCompCount = GL_BGRA; elem->m_gldecl.m_datatype = GL_UNSIGNED_BYTE; + } + + elem->m_gldecl.m_normalized = ( (elem->m_dxdecl.Type == D3DDECLTYPE_D3DCOLOR) || + (elem->m_dxdecl.Type == D3DDECLTYPE_UBYTE4N) ); + + bytes = 4; + break; + + case D3DDECLTYPE_SHORT2: + // pass 2 US's but we know this is out of order compared to D3DCOLOR data + elem->m_gldecl.m_nCompCount = 2; elem->m_gldecl.m_datatype = GL_SHORT; + + elem->m_gldecl.m_normalized = 0; + bytes = 4; + break; + + default: DXABSTRACT_BREAK_ON_ERROR(); return D3DERR_INVALIDCALL; break; + + /* + typedef enum _D3DDECLTYPE + { + D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.) + D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.) + D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.) + D3DDECLTYPE_FLOAT4 = 3, // 4D float + D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) + D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte + D3DDECLTYPE_SHORT2 = 6, // 2D signed short expanded to (value, value, 0., 1.) + D3DDECLTYPE_SHORT4 = 7, // 4D signed short + + // The following types are valid only with vertex shaders >= 2.0 + + + D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 + D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1) + D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) + D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1) + D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0) + D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1) + D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) + D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) + D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values + D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. + } D3DDECLTYPE; + */ + } + + // write the offset and move the cursor + elem->m_gldecl.m_offset = streamOffsets[elem->m_dxdecl.Stream]; + streamOffsets[ elem->m_dxdecl.Stream ] += bytes; + + // cannot write m_stride yet, so zero it + elem->m_gldecl.m_stride = 0; + + elem->m_gldecl.m_pBuffer = NULL; // must be filled in at draw time.. + + // elem count was already bumped. + + // update attrib map + attribMap[ attribMapIndex++ ] = (elem->m_dxdecl.Usage << 4) | (elem->m_dxdecl.UsageIndex); + } + // the loop is done, we now know how many active streams there are, how many atribs are active in the declaration, + // and how big each one is in terms of stride. + + // all that is left is to go back and write the strides - the stride comes from the stream offset cursors accumulated earlier. + for( uint j=0; j< decl9->m_elemCount; j++) + { + D3DVERTEXELEMENT9_GL *elem = &decl9->m_elements[ j ]; + + elem->m_gldecl.m_stride = streamOffsets[ elem->m_dxdecl.Stream ]; + } + + memset( decl9->m_VertexAttribDescToStreamIndex, 0xFF, sizeof( decl9->m_VertexAttribDescToStreamIndex ) ); + D3DVERTEXELEMENT9_GL *pDeclElem = decl9->m_elements; + for( uint j = 0; j < decl9->m_elemCount; j++, pDeclElem++) + { + uint nPackedVertexAttribDesc = ( pDeclElem->m_dxdecl.Usage << 4 ) | pDeclElem->m_dxdecl.UsageIndex; + if ( nPackedVertexAttribDesc == 0xBB ) + { + // 0xBB is a reserved packed vertex attrib value - shouldn't encounter in practice + DXABSTRACT_BREAK_ON_ERROR(); + } + decl9->m_VertexAttribDescToStreamIndex[ nPackedVertexAttribDesc ] = j; + } + + *ppDecl = decl9; + + return S_OK; +} + +IDirect3DVertexDeclaration9::~IDirect3DVertexDeclaration9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF(("-A- ~IDirect3DVertexDeclaration9 signpost")); + + m_device->ReleasedVertexDeclaration( this ); +} + +HRESULT IDirect3DDevice9::SetVertexDeclarationNonInline(IDirect3DVertexDeclaration9* pDecl) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + m_pVertDecl = pDecl; + return S_OK; +} + +HRESULT IDirect3DDevice9::SetFVF(DWORD FVF) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::GetFVF(DWORD* pFVF) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Vertex Buffers and Streams - (IDirect3DDevice9) + +#pragma mark ----- Create function moved to be adjacent to other buffer methods + +#endif + +HRESULT IDirect3DDevice9::SetStreamSourceNonInline(UINT StreamNumber,IDirect3DVertexBuffer9* pStreamData,UINT OffsetInBytes,UINT Stride) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( StreamNumber < D3D_MAX_STREAMS ); + Assert( ( Stride & 3 ) == 0 ); // we support non-DWORD aligned strides, but on some drivers (like AMD's) perf goes off a cliff + + // perfectly legal to see a vertex buffer of NULL get passed in here. + // so we need an array to track these. + // OK, we are being given the stride, we don't need to calc it.. + + GLMPRINTF(("-X- IDirect3DDevice9::SetStreamSource setting stream #%d to D3D buf %p (GL name %d); offset %d, stride %d", StreamNumber, pStreamData, (pStreamData) ? pStreamData->m_vtxBuffer->m_nHandle: -1, OffsetInBytes, Stride)); + + if ( !pStreamData ) + { + OffsetInBytes = 0; + Stride = 0; + + m_vtx_buffers[ StreamNumber ] = m_pDummy_vtx_buffer; + } + else + { + // We do not support strides of 0 + Assert( Stride > 0 ); + m_vtx_buffers[ StreamNumber ] = pStreamData->m_vtxBuffer; + } + + m_streams[ StreamNumber ].m_vtxBuffer = pStreamData; + m_streams[ StreamNumber ].m_offset = OffsetInBytes; + m_streams[ StreamNumber ].m_stride = Stride; + + return S_OK; +} + +#ifdef OSX + +#pragma mark ----- Index Buffers - (IDirect3DDevice9) +#pragma mark ----- Creatue function relocated to be adjacent to the rest of the index buffer methods + +#endif + +HRESULT IDirect3DDevice9::SetIndicesNonInline(IDirect3DIndexBuffer9* pIndexData) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // just latch it. + m_indices.m_idxBuffer = pIndexData; + + return S_OK; +} + + +#ifdef OSX + +#pragma mark ----- Release Handlers - (IDirect3DDevice9) + +#endif + +void IDirect3DDevice9::ReleasedVertexDeclaration( IDirect3DVertexDeclaration9 *pDecl ) +{ + m_ctx->ClearCurAttribs(); + + Assert( m_ObjectStats.m_nTotalVertexDecls >= 1 ); + m_ObjectStats.m_nTotalVertexDecls--; +} + +void IDirect3DDevice9::ReleasedTexture( IDirect3DBaseTexture9 *baseTex ) +{ + GL_BATCH_PERF_CALL_TIMER; + TOGL_NULL_DEVICE_CHECK_RET_VOID; + + // see if this texture is referenced in any of the texture units and scrub it if so. + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + if (m_textures[i] == baseTex) + { + m_textures[i] = NULL; + m_ctx->SetSamplerTex( i, NULL ); // texture sets go straight through to GLM, no dirty bit + } + } +} + +void IDirect3DDevice9::ReleasedCGLMTex( CGLMTex *pTex) +{ + GL_BATCH_PERF_CALL_TIMER; + TOGL_NULL_DEVICE_CHECK_RET_VOID; + + ScrubFBOMap( pTex ); + if ( pTex->m_layout ) + { + if ( pTex->m_layout->m_key.m_texFlags & kGLMTexRenderable ) + { + Assert( m_ObjectStats.m_nTotalRenderTargets >= 1 ); + m_ObjectStats.m_nTotalRenderTargets--; + } + } +} +void IDirect3DDevice9::ReleasedSurface( IDirect3DSurface9 *pSurface ) +{ + for( int i = 0; i < 4; i++ ) + { + if ( m_pRenderTargets[i] == pSurface ) + { + // this was a surprise release... scrub it + m_pRenderTargets[i] = NULL; + m_bFBODirty = true; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pRenderTargets[%d]", pSurface, i )); + } + } + + if ( m_pDepthStencil == pSurface ) + { + m_pDepthStencil = NULL; + m_bFBODirty = true; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDepthStencil", pSurface )); + } + + if ( m_pDefaultColorSurface == pSurface ) + { + m_pDefaultColorSurface = NULL; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultColorSurface", pSurface )); + } + + if ( m_pDefaultDepthStencilSurface == pSurface ) + { + m_pDefaultDepthStencilSurface = NULL; + GLMPRINTF(( "-A- Scrubbed pSurface %08x from m_pDefaultDepthStencilSurface", pSurface )); + } + + Assert( m_ObjectStats.m_nTotalSurfaces >= 1 ); + m_ObjectStats.m_nTotalSurfaces--; +} + +void IDirect3DDevice9::ReleasedPixelShader( IDirect3DPixelShader9 *pixelShader ) +{ + if ( m_pixelShader == pixelShader ) + { + m_pixelShader = NULL; + GLMPRINTF(( "-A- Scrubbed pixel shader %08x from m_pixelShader", pixelShader )); + } + m_ctx->ReleasedShader(); + + Assert( m_ObjectStats.m_nTotalPixelShaders >= 1 ); + m_ObjectStats.m_nTotalPixelShaders--; +} + +void IDirect3DDevice9::ReleasedVertexShader( IDirect3DVertexShader9 *vertexShader ) +{ + if ( m_vertexShader == vertexShader ) + { + m_vertexShader = NULL; + GLMPRINTF(( "-A- Scrubbed vertex shader %08x from m_vertexShader", vertexShader )); + } + m_ctx->ClearCurAttribs(); + m_ctx->ReleasedShader(); + + Assert( m_ObjectStats.m_nTotalVertexShaders >= 1 ); + m_ObjectStats.m_nTotalVertexShaders--; +} + +void IDirect3DDevice9::ReleasedVertexBuffer( IDirect3DVertexBuffer9 *vertexBuffer ) +{ + for (int i=0; i< D3D_MAX_STREAMS; i++) + { + if ( m_streams[i].m_vtxBuffer == vertexBuffer ) + { + m_streams[i].m_vtxBuffer = NULL; + m_vtx_buffers[i] = m_pDummy_vtx_buffer; + + GLMPRINTF(( "-A- Scrubbed vertex buffer %08x from m_streams[%d]", vertexBuffer, i )); + } + } + m_ctx->ClearCurAttribs(); + + Assert( m_ObjectStats.m_nTotalVertexBuffers >= 1 ); + m_ObjectStats.m_nTotalVertexBuffers--; +} + +void IDirect3DDevice9::ReleasedIndexBuffer( IDirect3DIndexBuffer9 *indexBuffer ) +{ + if ( m_indices.m_idxBuffer == indexBuffer ) + { + m_indices.m_idxBuffer = NULL; + GLMPRINTF(( "-A- Scrubbed index buffer %08x from m_indices", indexBuffer )); + } + + Assert( m_ObjectStats.m_nTotalIndexBuffers >= 1 ); + m_ObjectStats.m_nTotalIndexBuffers--; +} + + +void IDirect3DDevice9::ReleasedQuery( IDirect3DQuery9 *query ) +{ + Assert( m_ObjectStats.m_nTotalQueries >= 1 ); + m_ObjectStats.m_nTotalQueries--; +} + +#ifdef OSX + +#pragma mark ----- Queries - (IDirect3DDevice9) + +#endif + +// note that detection of whether queries are supported is done by trying to create one. +// so for GL, be observant here of whether we have that capability or not. +// pretty much have this everywhere but i950. + +HRESULT IDirect3DDevice9::CreateQuery(D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + if (m_ctx->Caps().m_hasOcclusionQuery) + { + m_ObjectStats.m_nTotalQueries++; + + IDirect3DQuery9 *newquery = new IDirect3DQuery9; + + newquery->m_device = this; + + newquery->m_type = Type; + newquery->m_ctx = m_ctx; + newquery->m_nIssueStartThreadID = 0; + newquery->m_nIssueEndThreadID = 0; + newquery->m_nIssueStartDrawCallIndex = 0; + newquery->m_nIssueEndDrawCallIndex = 0; + + GLMQueryParams params; + memset( ¶ms, 0, sizeof(params) ); + + //bool known = false; + switch(newquery->m_type) + { + case D3DQUERYTYPE_OCCLUSION: /* D3DISSUE_BEGIN, D3DISSUE_END */ + // create an occlusion query + params.m_type = EOcclusion; + break; + + case D3DQUERYTYPE_EVENT: /* D3DISSUE_END */ + params.m_type = EFence; + break; + + case D3DQUERYTYPE_RESOURCEMANAGER: /* D3DISSUE_END */ + case D3DQUERYTYPE_TIMESTAMP: /* D3DISSUE_END */ + case D3DQUERYTYPE_TIMESTAMPFREQ: /* D3DISSUE_END */ + case D3DQUERYTYPE_INTERFACETIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ + case D3DQUERYTYPE_PIXELTIMINGS: /* D3DISSUE_BEGIN, D3DISSUE_END */ + case D3DQUERYTYPE_CACHEUTILIZATION: /* D3DISSUE_BEGIN, D3DISSUE_END */ + Assert( !"Un-implemented query type" ); + break; + + default: + Assert( !"Unknown query type" ); + break; + } + newquery->m_query = m_ctx->NewQuery( ¶ms ); + + *ppQuery = newquery; + return S_OK; + } + else + { + *ppQuery = NULL; + return -1; // failed + } + +} + +IDirect3DQuery9::~IDirect3DQuery9() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( m_device ); + GLMPRINTF((">-A- ~IDirect3DQuery9")); + + if (m_device) + { + m_device->ReleasedQuery( this ); + + if (m_query) + { + GLMPRINTF((">-A- ~IDirect3DQuery9 freeing m_query")); + + m_query->m_ctx->DelQuery( m_query ); + m_query = NULL; + + GLMPRINTF(("<-A- ~IDirect3DQuery9 freeing m_query done")); + } + m_device = NULL; + } + + GLMPRINTF(("<-A- ~IDirect3DQuery9")); +} + +#ifdef OSX + +#pragma mark ----- Render States - (IDirect3DDevice9) + +#endif + +#define D3DRS_VALUE_LIMIT 210 + +struct D3D_RSINFO +{ + int m_class; + D3DRENDERSTATETYPE m_state; + DWORD m_defval; + // m_class runs 0-3. + // 3 = must implement - fully general - "obey" + // 2 = implement setup to the default value (it has a GL effect but does not change later) "obey once" + // 1 = "fake implement" setup to the default value no GL effect, debug break if anything but default value comes through - "ignore" + // 0 = game never ever sets this one, break if someone even tries. "complain" +}; + +D3D_RSINFO g_D3DRS_INFO_unpacked[ D3DRS_VALUE_LIMIT+1 ]; + +#ifdef D3D_RSI + #error macro collision... rename this +#else + #define D3D_RSI(nclass,nstate,ndefval) { nclass, nstate, ndefval } +#endif + +// FP conversions to hex courtesy of http://babbage.cs.qc.cuny.edu/IEEE-754/Decimal.html +#define CONST_DZERO 0x00000000 +#define CONST_DONE 0x3F800000 +#define CONST_D64 0x42800000 +#define DONT_KNOW_YET 0x31415926 + + +// see http://www.toymaker.info/Games/html/render_states.html + +D3D_RSINFO g_D3DRS_INFO_packed[] = +{ + // these do not have to be in any particular order. they get unpacked into the empty array above for direct indexing. + + D3D_RSI( 3, D3DRS_ZENABLE, DONT_KNOW_YET ), // enable Z test (or W buffering) + D3D_RSI( 3, D3DRS_ZWRITEENABLE, DONT_KNOW_YET ), // enable Z write + D3D_RSI( 3, D3DRS_ZFUNC, DONT_KNOW_YET ), // select Z func + + D3D_RSI( 3, D3DRS_COLORWRITEENABLE, TRUE ), // see transitiontable.cpp "APPLY_RENDER_STATE_FUNC( D3DRS_COLORWRITEENABLE, ColorWriteEnable )" + + D3D_RSI( 3, D3DRS_CULLMODE, D3DCULL_CCW ), // backface cull control + + D3D_RSI( 3, D3DRS_ALPHABLENDENABLE, DONT_KNOW_YET ), // ->CTransitionTable::ApplySeparateAlphaBlend and ApplyAlphaBlend + D3D_RSI( 3, D3DRS_BLENDOP, D3DBLENDOP_ADD ), + D3D_RSI( 3, D3DRS_SRCBLEND, DONT_KNOW_YET ), + D3D_RSI( 3, D3DRS_DESTBLEND, DONT_KNOW_YET ), + + D3D_RSI( 1, D3DRS_SEPARATEALPHABLENDENABLE, FALSE ), // hit in CTransitionTable::ApplySeparateAlphaBlend + D3D_RSI( 1, D3DRS_SRCBLENDALPHA, D3DBLEND_ONE ), // going to demote these to class 1 until I figure out if they are implementable + D3D_RSI( 1, D3DRS_DESTBLENDALPHA, D3DBLEND_ZERO ), + D3D_RSI( 1, D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD ), + + // what is the deal with alpha test... looks like it is inited to off. + D3D_RSI( 3, D3DRS_ALPHATESTENABLE, 0 ), + D3D_RSI( 3, D3DRS_ALPHAREF, 0 ), + D3D_RSI( 3, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ), + + D3D_RSI( 3, D3DRS_STENCILENABLE, FALSE ), + D3D_RSI( 3, D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_STENCILFUNC, D3DCMP_ALWAYS ), + D3D_RSI( 3, D3DRS_STENCILREF, 0 ), + D3D_RSI( 3, D3DRS_STENCILMASK, 0xFFFFFFFF ), + D3D_RSI( 3, D3DRS_STENCILWRITEMASK, 0xFFFFFFFF ), + + D3D_RSI( 3, D3DRS_TWOSIDEDSTENCILMODE, FALSE ), + D3D_RSI( 3, D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP ), + D3D_RSI( 3, D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS ), + + D3D_RSI( 3, D3DRS_FOGENABLE, FALSE ), // see CShaderAPIDx8::FogMode and friends - be ready to do the ARB fog linear option madness + D3D_RSI( 3, D3DRS_FOGCOLOR, 0 ), + D3D_RSI( 3, D3DRS_FOGTABLEMODE, D3DFOG_NONE ), + D3D_RSI( 3, D3DRS_FOGSTART, CONST_DZERO ), + D3D_RSI( 3, D3DRS_FOGEND, CONST_DONE ), + D3D_RSI( 3, D3DRS_FOGDENSITY, CONST_DZERO ), + D3D_RSI( 3, D3DRS_RANGEFOGENABLE, FALSE ), + D3D_RSI( 3, D3DRS_FOGVERTEXMODE, D3DFOG_NONE ), // watch out for CShaderAPIDx8::CommitPerPassFogMode.... + + D3D_RSI( 3, D3DRS_MULTISAMPLEANTIALIAS, TRUE ), + D3D_RSI( 3, D3DRS_MULTISAMPLEMASK, 0xFFFFFFFF ), + + D3D_RSI( 3, D3DRS_SCISSORTESTENABLE, FALSE ), // heed IDirect3DDevice9::SetScissorRect + + D3D_RSI( 3, D3DRS_DEPTHBIAS, CONST_DZERO ), + D3D_RSI( 3, D3DRS_SLOPESCALEDEPTHBIAS, CONST_DZERO ), + + D3D_RSI( 3, D3DRS_COLORWRITEENABLE1, 0x0000000f ), + D3D_RSI( 3, D3DRS_COLORWRITEENABLE2, 0x0000000f ), + D3D_RSI( 3, D3DRS_COLORWRITEENABLE3, 0x0000000f ), + + D3D_RSI( 3, D3DRS_SRGBWRITEENABLE, 0 ), // heeded but ignored.. + + D3D_RSI( 2, D3DRS_CLIPPING, TRUE ), // um, yeah, clipping is enabled (?) + D3D_RSI( 3, D3DRS_CLIPPLANEENABLE, 0 ), // mask 1<<n of active user clip planes. + + D3D_RSI( 0, D3DRS_LIGHTING, 0 ), // strange, someone turns it on then off again. move to class 0 and just ignore it (lie)? + + D3D_RSI( 3, D3DRS_FILLMODE, D3DFILL_SOLID ), + + D3D_RSI( 1, D3DRS_SHADEMODE, D3DSHADE_GOURAUD ), + D3D_RSI( 1, D3DRS_LASTPIXEL, TRUE ), + D3D_RSI( 1, D3DRS_DITHERENABLE, 0 ), //set to false by game, no one sets it to true + D3D_RSI( 1, D3DRS_SPECULARENABLE, FALSE ), + D3D_RSI( 1, D3DRS_TEXTUREFACTOR, 0xFFFFFFFF ), // watch out for CShaderAPIDx8::Color3f et al. + D3D_RSI( 1, D3DRS_WRAP0, 0 ), + D3D_RSI( 1, D3DRS_WRAP1, 0 ), + D3D_RSI( 1, D3DRS_WRAP2, 0 ), + D3D_RSI( 1, D3DRS_WRAP3, 0 ), + D3D_RSI( 1, D3DRS_WRAP4, 0 ), + D3D_RSI( 1, D3DRS_WRAP5, 0 ), + D3D_RSI( 1, D3DRS_WRAP6, 0 ), + D3D_RSI( 1, D3DRS_WRAP7, 0 ), + D3D_RSI( 1, D3DRS_AMBIENT, 0 ), // FF lighting, no + D3D_RSI( 1, D3DRS_COLORVERTEX, TRUE ), // FF lighing again + D3D_RSI( 1, D3DRS_LOCALVIEWER, TRUE ), // FF lighting + D3D_RSI( 1, D3DRS_NORMALIZENORMALS, FALSE ), // FF mode I think. CShaderAPIDx8::SetVertexBlendState says it might switch this on when skinning is in play + D3D_RSI( 1, D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ), // hit only in CShaderAPIDx8::ResetRenderState + D3D_RSI( 1, D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2 ), + D3D_RSI( 1, D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ), + D3D_RSI( 1, D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ), + D3D_RSI( 1, D3DRS_VERTEXBLEND, D3DVBF_DISABLE ), // also being set by CShaderAPIDx8::SetVertexBlendState, so might be FF + D3D_RSI( 1, D3DRS_POINTSIZE, CONST_DONE ), + D3D_RSI( 1, D3DRS_POINTSIZE_MIN, CONST_DONE ), + D3D_RSI( 1, D3DRS_POINTSPRITEENABLE, FALSE ), + D3D_RSI( 1, D3DRS_POINTSCALEENABLE, FALSE ), + D3D_RSI( 1, D3DRS_POINTSCALE_A, CONST_DONE ), + D3D_RSI( 1, D3DRS_POINTSCALE_B, CONST_DZERO ), + D3D_RSI( 1, D3DRS_POINTSCALE_C, CONST_DZERO ), + D3D_RSI( 1, D3DRS_PATCHEDGESTYLE, D3DPATCHEDGE_DISCRETE ), + D3D_RSI( 1, D3DRS_DEBUGMONITORTOKEN, D3DDMT_ENABLE ), + D3D_RSI( 1, D3DRS_POINTSIZE_MAX, CONST_D64 ), + D3D_RSI( 1, D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ), + D3D_RSI( 1, D3DRS_TWEENFACTOR, CONST_DZERO ), + D3D_RSI( 1, D3DRS_POSITIONDEGREE, D3DDEGREE_CUBIC ), + D3D_RSI( 1, D3DRS_NORMALDEGREE, D3DDEGREE_LINEAR ), + D3D_RSI( 1, D3DRS_ANTIALIASEDLINEENABLE, FALSE ), // just ignore it + D3D_RSI( 1, D3DRS_MINTESSELLATIONLEVEL, CONST_DONE ), + D3D_RSI( 1, D3DRS_MAXTESSELLATIONLEVEL, CONST_DONE ), + D3D_RSI( 1, D3DRS_ADAPTIVETESS_X, CONST_DZERO ), + D3D_RSI( 1, D3DRS_ADAPTIVETESS_Y, CONST_DZERO ), // Overridden as Alpha-to-coverage contrl + D3D_RSI( 1, D3DRS_ADAPTIVETESS_Z, CONST_DONE ), + D3D_RSI( 1, D3DRS_ADAPTIVETESS_W, CONST_DZERO ), + D3D_RSI( 1, D3DRS_ENABLEADAPTIVETESSELLATION, FALSE ), + D3D_RSI( 1, D3DRS_BLENDFACTOR, 0xffffffff ), + D3D_RSI( 1, D3DRS_WRAP8, 0 ), + D3D_RSI( 1, D3DRS_WRAP9, 0 ), + D3D_RSI( 1, D3DRS_WRAP10, 0 ), + D3D_RSI( 1, D3DRS_WRAP11, 0 ), + D3D_RSI( 1, D3DRS_WRAP12, 0 ), + D3D_RSI( 1, D3DRS_WRAP13, 0 ), + D3D_RSI( 1, D3DRS_WRAP14, 0 ), + D3D_RSI( 1, D3DRS_WRAP15, 0 ), + D3D_RSI( -1, (D3DRENDERSTATETYPE)0, 0 ) // terminator +}; + +void UnpackD3DRSITable( void ) +{ + memset (g_D3DRS_INFO_unpacked, 0, sizeof(g_D3DRS_INFO_unpacked) ); + + for( D3D_RSINFO *packed = g_D3DRS_INFO_packed; packed->m_class >= 0; packed++ ) + { + if ( (packed->m_state <0) || (packed->m_state >= D3DRS_VALUE_LIMIT) ) + { + // bad + DXABSTRACT_BREAK_ON_ERROR(); + } + else + { + // dispatch it to the unpacked array + g_D3DRS_INFO_unpacked[ packed->m_state ] = *packed; + } + } +} + +// convenience functions + +#ifdef OSX + +#pragma mark ----- Sampler States - (IDirect3DDevice9) + +#endif + +void IDirect3DDevice9::FlushClipPlaneEquation() +{ + for( int x=0; x<kGLMUserClipPlanes; x++) + { + GLClipPlaneEquation_t temp1; // Antonio's way + GLClipPlaneEquation_t temp2; // our way + + // if we don't have native clip vertex support. then munge the plane coeffs + // this should engage on ALL ATI PARTS < 10.6.4 + // and should continue to engage on R5xx forever. + + if ( !m_ctx->Caps().m_hasNativeClipVertexMode ) + { + // hacked coeffs = { src->x, -src->y, 0.5f * src->z, src->w + (0.5f * src->z) }; + // Antonio's trick - so we can use gl_Position as the clippee, not gl_ClipVertex. + + GLClipPlaneEquation_t *equ = &gl.m_ClipPlaneEquation[x]; + + ///////////////// temp1 + temp1.x = equ->x; + temp1.y = equ->y * -1.0; + temp1.z = equ->z * 0.5; + temp1.w = equ->w + (equ->z * 0.5); + + + //////////////// temp2 + VMatrix mat1( 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 2, -1, + 0, 0, 0, 1 + ); + //mat1 = mat1.Transpose(); + + VMatrix mat2; + bool success = mat1.InverseGeneral( mat2 ); + + if (success) + { + VMatrix mat3; + mat3 = mat2.Transpose(); + + VPlane origPlane( Vector( equ->x, equ->y, equ->z ), equ->w ); + VPlane newPlane; + + newPlane = mat3 * origPlane /* * mat3 */; + + VPlane finalPlane = newPlane; + + temp2.x = newPlane.m_Normal.x; + temp2.y = newPlane.m_Normal.y; + temp2.z = newPlane.m_Normal.z; + temp2.w = newPlane.m_Dist; + } + else + { + temp2.x = 0; + temp2.y = 0; + temp2.z = 0; + temp2.w = 0; + } + } + else + { + temp1 = temp2 = gl.m_ClipPlaneEquation[x]; + } + + if (1) //GLMKnob("caps-key",NULL)==0.0) + { + m_ctx->WriteClipPlaneEquation( &temp1, x ); // no caps lock = Antonio or classic + + /* + if (x<1) + { + GLMPRINTF(( " plane %d √vers1[ %5.2f %5.2f %5.2f %5.2f ] vers2[ %5.2f %5.2f %5.2f %5.2f ]", + x, + temp1.x,temp1.y,temp1.z,temp1.w, + temp2.x,temp2.y,temp2.z,temp2.w + )); + } + */ + } + else + { + m_ctx->WriteClipPlaneEquation( &temp2, x ); // caps = our way or classic + + /* + if (x<1) + { + GLMPRINTF(( " plane %d vers1[ %5.2f %5.2f %5.2f %5.2f ] √vers2[ %5.2f %5.2f %5.2f %5.2f ]", + x, + temp1.x,temp1.y,temp1.z,temp1.w, + temp2.x,temp2.y,temp2.z,temp2.w + )); + } + */ + } + } +} + +void IDirect3DDevice9::InitStates() +{ + m_ctx->m_AlphaTestEnable.Read( &gl.m_AlphaTestEnable, 0 ); + m_ctx->m_AlphaTestFunc.Read( &gl.m_AlphaTestFunc, 0 ); + m_ctx->m_CullFaceEnable.Read( &gl.m_CullFaceEnable, 0 ); + m_ctx->m_DepthBias.Read( &gl.m_DepthBias, 0 ); + m_ctx->m_ScissorEnable.Read( &gl.m_ScissorEnable, 0 ); + m_ctx->m_ScissorBox.Read( &gl.m_ScissorBox, 0 ); + m_ctx->m_ViewportBox.Read( &gl.m_ViewportBox, 0 ); + m_ctx->m_ViewportDepthRange.Read( &gl.m_ViewportDepthRange, 0 ); + + for( int x=0; x<kGLMUserClipPlanes; x++) + m_ctx->m_ClipPlaneEnable.ReadIndex( &gl.m_ClipPlaneEnable[x], x, 0 ); + + m_ctx->m_PolygonMode.Read( &gl.m_PolygonMode, 0 ); + m_ctx->m_CullFrontFace.Read( &gl.m_CullFrontFace, 0 ); + m_ctx->m_AlphaToCoverageEnable.Read( &gl.m_AlphaToCoverageEnable, 0 ); + m_ctx->m_BlendEquation.Read( &gl.m_BlendEquation, 0 ); + m_ctx->m_BlendColor.Read( &gl.m_BlendColor, 0 ); + + for( int x=0; x<kGLMUserClipPlanes; x++) + m_ctx->m_ClipPlaneEquation.ReadIndex( &gl.m_ClipPlaneEquation[x], x, 0 ); + + m_ctx->m_ColorMaskSingle.Read( &gl.m_ColorMaskSingle, 0 ); + + m_ctx->m_BlendEnable.Read( &gl.m_BlendEnable, 0 ); + m_ctx->m_BlendFactor.Read( &gl.m_BlendFactor, 0 ); + m_ctx->m_BlendEnableSRGB.Read( &gl.m_BlendEnableSRGB, 0 ); + m_ctx->m_DepthTestEnable.Read( &gl.m_DepthTestEnable, 0 ); + m_ctx->m_DepthFunc.Read( &gl.m_DepthFunc, 0 ); + m_ctx->m_DepthMask.Read( &gl.m_DepthMask, 0 ); + m_ctx->m_StencilTestEnable.Read( &gl.m_StencilTestEnable, 0 ); + m_ctx->m_StencilFunc.Read( &gl.m_StencilFunc, 0 ); + + m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 0, 0 ); + m_ctx->m_StencilOp.ReadIndex( &gl.m_StencilOp, 1, 0 ); + + m_ctx->m_StencilWriteMask.Read( &gl.m_StencilWriteMask, 0 ); + m_ctx->m_ClearColor.Read( &gl.m_ClearColor, 0 ); + m_ctx->m_ClearDepth.Read( &gl.m_ClearDepth, 0 ); + m_ctx->m_ClearStencil.Read( &gl.m_ClearStencil, 0 ); +} + +void IDirect3DDevice9::FullFlushStates() +{ + m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); + m_ctx->WriteScissorBox( &gl.m_ScissorBox ); + m_ctx->WriteViewportBox( &gl.m_ViewportBox ); + m_ctx->WriteViewportDepthRange( &gl.m_ViewportDepthRange ); + + for( int x=0; x<kGLMUserClipPlanes; x++) + m_ctx->WriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); + + m_ctx->WritePolygonMode( &gl.m_PolygonMode ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); + m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); + m_ctx->WriteBlendColor( &gl.m_BlendColor ); + FlushClipPlaneEquation(); + m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); + + m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); + m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); + m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); + m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); + m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); + m_ctx->WriteDepthMask( &gl.m_DepthMask ); + m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + + m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); + m_ctx->WriteClearColor( &gl.m_ClearColor ); + m_ctx->WriteClearDepth( &gl.m_ClearDepth ); + m_ctx->WriteClearStencil( &gl.m_ClearStencil ); +} + +HRESULT IDirect3DDevice9::DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +// 7LS - TODO +#ifndef DX_TO_GL_ABSTRACTION +HRESULT IDirect3DDevice9::DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCountx,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) +{ +// 7LS + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} +#endif + +// Type +// [in] Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. See Remarks. + +// BaseVertexIndex +// [in] Offset from the start of the vertex buffer to the first vertex. See Scenario 4. + +// MinIndex +// [in] Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. + +// NumVertices +// [in] Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. + +// StartIndex +// [in] Index of the first index to use when accessing the index buffer. + +// PrimitiveCount +// [in] Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. The maximum number of primitives allowed is determined by checking the MaxPrimitiveCount member of the D3DCAPS9 structure. + +// BE VERY CAREFUL what you do in this function. It's extremely hot, and calling the wrong GL API's in here will crush perf. on NVidia threaded drivers. +#ifndef OSX + +HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount ) +{ + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "%s", __FUNCTION__ ); + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + TOGL_NULL_DEVICE_CHECK; + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalPrims += primCount; + CFastTimer tm; + CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; + tm.Start(); + flushStats.Clear(); +#endif + +#if GLMDEBUG + if ( gl.m_FogEnable ) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } +#endif + + if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) + goto draw_failed; + + { + GL_BATCH_PERF_CALL_TIMER; + + m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, BaseVertexIndex ); + + { +#if !GL_TELEMETRY_ZONES && GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "glDrawRangeElements %u", primCount ); +#endif + Assert( ( D3DPT_LINELIST == 2 ) && ( D3DPT_TRIANGLELIST == 4 ) && ( D3DPT_TRIANGLESTRIP == 5 ) ); + + static const struct prim_t + { + GLenum m_nType; + uint m_nPrimMul; + uint m_nPrimAdd; + } s_primTypes[6] = + { + { 0, 0, 0 }, // 0 + { 0, 0, 0 }, // 1 + { GL_LINES, 2, 0 }, // 2 D3DPT_LINELIST + { 0, 0, 0 }, // 3 + { GL_TRIANGLES, 3, 0 }, // 4 D3DPT_TRIANGLELIST + { GL_TRIANGLE_STRIP, 1, 2 } // 5 D3DPT_TRIANGLESTRIP + }; + + if ( Type <= D3DPT_TRIANGLESTRIP ) + { + const prim_t& p = s_primTypes[Type]; + Assert( p.m_nType ); + Assert( NumVertices >= 1 ); + + m_ctx->DrawRangeElements( p.m_nType, (GLuint)MinVertexIndex, (GLuint)( MinVertexIndex + NumVertices - 1 ), (GLsizei)p.m_nPrimAdd + primCount * p.m_nPrimMul, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)( startIndex * sizeof(short) ), BaseVertexIndex, m_indices.m_idxBuffer->m_idxBuffer ); + } + } + } + +#if GL_BATCH_PERF_ANALYSIS + if ( s_rdtsc_to_ms == 0.0f ) + { + TmU64 t0 = Plat_Rdtsc(); + double d0 = Plat_FloatTime(); + + ThreadSleep( 1000 ); + + TmU64 t1 = Plat_Rdtsc(); + double d1 = Plat_FloatTime(); + + s_rdtsc_to_ms = ( 1000.0f * ( d1 - d0 ) ) / ( t1 - t0 ); + } + +#if GL_BATCH_PERF_ANALYSIS_WRITE_PNGS + if ( m_pBatch_vis_bitmap && m_pBatch_vis_bitmap->is_valid() ) + { + double t = tm.GetDurationInProgress().GetMillisecondsF(); + + uint h = 1; + if ( gl_batch_vis_y_scale.GetFloat() > 0.0f) + { + h = ceil( t / gl_batch_vis_y_scale.GetFloat() ); + h = MAX(h, 1); + } + + // Total time spent inside any and all our "D3D9" calls + double flTotalD3DTime = g_nTotalD3DCycles * s_rdtsc_to_ms; + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalD3DTime / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 150, 150, 150); + + // Total total spent processing just DrawIndexedPrimitive() for this batch. + m_pBatch_vis_bitmap->fill_box(0, m_nBatchVisY, (uint)(.5f + t / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 70, 70, 70); + + double flTotalGLMS = gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + + // Total time spent inside of all OpenGL calls + m_pBatch_vis_bitmap->additive_fill_box(0, m_nBatchVisY, (uint)(.5f + flTotalGLMS / gl_batch_vis_abs_scale.GetFloat() * m_pBatch_vis_bitmap->width()), h, 0, 0, 64); + + if (flushStats.m_nNewVS) m_pBatch_vis_bitmap->additive_fill_box(80-16, m_nBatchVisY, 8, h, 0, 110, 0); + if (flushStats.m_nNewPS) m_pBatch_vis_bitmap->additive_fill_box(80-8, m_nBatchVisY, 8, h, 110, 0, 110); + + int lm = 80; + m_pBatch_vis_bitmap->fill_box(lm+0+flushStats.m_nFirstVSConstant, m_nBatchVisY, flushStats.m_nNumVSConstants, h, 64, 255, 255); + m_pBatch_vis_bitmap->fill_box(lm+64, m_nBatchVisY, flushStats.m_nNumVSBoneConstants, h, 255, 64, 64); + m_pBatch_vis_bitmap->fill_box(lm+64+256+flushStats.m_nFirstPSConstant, m_nBatchVisY, flushStats.m_nNumPSConstants, h, 64, 64, 255); + + m_pBatch_vis_bitmap->fill_box(lm+64+256+32, m_nBatchVisY, flushStats.m_nNumChangedSamplers, h, 255, 255, 255); + m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16, m_nBatchVisY, flushStats.m_nNumSamplingParamsChanged, h, 92, 128, 255); + + if ( flushStats.m_nVertexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64, m_nBatchVisY, 16, h, 128, 128, 128); + if ( flushStats.m_nIndexBufferChanged ) m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16, m_nBatchVisY, 16, h, 128, 128, 255); + + m_pBatch_vis_bitmap->fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( ( g_nTotalVBLockBytes + g_nTotalIBLockBytes ) * 64 + 2047 ) / 2048, h, 120, 120, 120 ); + m_pBatch_vis_bitmap->additive_fill_box(lm+64+256+32+16+64+16+16, m_nBatchVisY, ( g_nTotalVBLockBytes * 64 + 2047 ) / 2048, h, 120, 0, 0); + + m_nBatchVisY += h; + } +#endif + + m_nNumProgramChanges += ((flushStats.m_nNewVS + flushStats.m_nNewPS) != 0); + + m_flTotalD3DTime += g_nTotalD3DCycles * s_rdtsc_to_ms; + m_nTotalD3DCalls += g_nTotalD3DCalls; + g_nTotalD3DCycles = 0; + g_nTotalD3DCalls = 0; + + m_flTotalGLTime += gGL->m_nTotalGLCycles * s_rdtsc_to_ms; + m_nTotalGLCalls += gGL->m_nTotalGLCalls; + gGL->m_nTotalGLCycles = 0; + gGL->m_nTotalGLCalls = 0; + + g_nTotalVBLockBytes = 0; + g_nTotalIBLockBytes = 0; +#endif + + return S_OK; + +draw_failed: + Assert( 0 ); + return E_FAIL; +} + +#else + +// OSX 10.6 support + +HRESULT IDirect3DDevice9::FlushIndexBindings( void ) +{ + // push index buffer state + m_ctx->SetIndexBuffer( m_indices.m_idxBuffer->m_idxBuffer ); + return S_OK; +} + +HRESULT IDirect3DDevice9::FlushVertexBindings( uint baseVertexIndex ) +{ + // push vertex buffer state for the current vertex decl + // in this variant we just walk the attrib map in the VS and do a pull for each one. + // if we can't find a match in the vertex decl, we may fall back to the secret 'dummy' VBO that GLM maintains + + GLMVertexSetup setup; + memset( &setup, 0, sizeof( setup ) ); + + IDirect3DVertexDeclaration9 *vxdecl = m_pVertDecl; + unsigned char *vshAttribMap = m_vertexShader->m_vtxAttribMap; + + // this loop could be tightened if we knew the number of live entries in the shader attrib map. + // which of course would be easy to do in the create shader function or even in the translator. + + GLMVertexAttributeDesc *dstAttr = setup.m_attrs; + for( int i=0; i<16; i++,dstAttr++ ) + { + unsigned char vshattrib = vshAttribMap[ i ]; + if (vshattrib != 0xBB) + { + // try to find the match in the decl. + // idea: put some inverse table in the decl which could accelerate this search. + + D3DVERTEXELEMENT9_GL *elem = m_pVertDecl->m_elements; + for( int j=0; j< m_pVertDecl->m_elemCount; j++,elem++) + { + // if it matches, install it, change vshattrib so the code below does not trigger, then end the loop + if ( ((vshattrib>>4) == elem->m_dxdecl.Usage) && ((vshattrib & 0x0F) == elem->m_dxdecl.UsageIndex) ) + { + // targeting attribute #i in the setup with element data #j from the decl + + *dstAttr = elem->m_gldecl; + + // then fix buffer, stride, offset - note that we honor the base vertex index here by fiddling the offset + int streamIndex = elem->m_dxdecl.Stream; + dstAttr->m_pBuffer = m_streams[ streamIndex ].m_vtxBuffer->m_vtxBuffer; + dstAttr->m_stride = m_streams[ streamIndex ].m_stride; + dstAttr->m_offset += m_streams[ streamIndex ].m_offset + (baseVertexIndex * dstAttr->m_stride); + + // set mask + setup.m_attrMask |= (1 << i); + + // end loop + vshattrib = 0xBB; + j = 999; + } + } + + // if vshattrib is not 0xBB here, that means we could not find a source in the decl for it + if (vshattrib != 0xBB) + { + // fill out attr the same way as usual, we just pass NULL for the buffer and ask GLM to have mercy on us + + dstAttr->m_pBuffer = NULL; + dstAttr->m_stride = 0; + dstAttr->m_offset = 0; + + // only implement certain usages... if we haven't seen it before, stop. + switch (vshattrib >> 4) // aka usage + { + case D3DDECLUSAGE_POSITION: + case D3DDECLUSAGE_BLENDWEIGHT: + case D3DDECLUSAGE_BLENDINDICES: + Debugger(); + break; + + case D3DDECLUSAGE_NORMAL: + dstAttr->m_nCompCount = 3; + dstAttr->m_datatype = GL_FLOAT; + dstAttr->m_normalized = false; + break; + + case D3DDECLUSAGE_PSIZE: + Debugger(); + break; + + case D3DDECLUSAGE_TEXCOORD: + dstAttr->m_nCompCount = 3; + dstAttr->m_datatype = GL_FLOAT; + dstAttr->m_normalized = false; + break; + + case D3DDECLUSAGE_TANGENT: + case D3DDECLUSAGE_BINORMAL: + case D3DDECLUSAGE_TESSFACTOR: + case D3DDECLUSAGE_PLUGH: + Debugger(); + break; + + case D3DDECLUSAGE_COLOR: + dstAttr->m_nCompCount = 4; + dstAttr->m_datatype = GL_UNSIGNED_BYTE; + dstAttr->m_normalized = true; + break; + + case D3DDECLUSAGE_FOG: + case D3DDECLUSAGE_DEPTH: + case D3DDECLUSAGE_SAMPLE: + Debugger(); + break; + } + } + } + } + + // copy active program's vertex attrib map into the vert setup info + memcpy(&setup.m_vtxAttribMap, m_vertexShader->m_vtxAttribMap, sizeof(m_vertexShader->m_vtxAttribMap)); + + m_ctx->SetVertexAttributes(&setup); + return S_OK; +} + + +// OSX path offering support for 10.6 (we do not have support for glDrawRangeElementsBaseVertex) +HRESULT IDirect3DDevice9::DrawIndexedPrimitive( D3DPRIMITIVETYPE Type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount ) +{ + Assert( m_ctx->m_nCurOwnerThreadId == ThreadGetCurrentId() ); + + TOGL_NULL_DEVICE_CHECK; + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalPrims += primCount; + CFastTimer tm; + CFlushDrawStatesStats& flushStats = m_ctx->m_FlushStats; + tm.Start(); + flushStats.Clear(); +#endif + +#if GLMDEBUG + if ( gl.m_FogEnable ) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } +#endif + + if ( ( !m_indices.m_idxBuffer ) || ( !m_vertexShader ) ) + goto draw_failed; + + this->FlushIndexBindings( ); + this->FlushVertexBindings( BaseVertexIndex ); + m_ctx->FlushDrawStates( MinVertexIndex, MinVertexIndex + NumVertices - 1, 0 ); + + if (gl.m_FogEnable) + { + GLMPRINTF(("-D- IDirect3DDevice9::DrawIndexedPrimitive is seeing enabled fog...")); + } + + switch(Type) + { + case D3DPT_POINTLIST: + Debugger(); + break; + + case D3DPT_LINELIST: + GLMPRINTF(("-X- IDirect3DDevice9::DrawIndexedPrimitive( D3DPT_LINELIST ) - ignored.")); + // Debugger(); + m_ctx->DrawRangeElements( (GLenum)GL_LINES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*2, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + + case D3DPT_TRIANGLELIST: + m_ctx->DrawRangeElements(GL_TRIANGLES, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)primCount*3, (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + + case D3DPT_TRIANGLESTRIP: + // enabled... Debugger(); + m_ctx->DrawRangeElements(GL_TRIANGLE_STRIP, (GLuint)MinVertexIndex, (GLuint)(MinVertexIndex + NumVertices), (GLsizei)(2+primCount), (GLenum)GL_UNSIGNED_SHORT, (const GLvoid *)(startIndex * sizeof(short)), m_indices.m_idxBuffer->m_idxBuffer ); + break; + } + + return S_OK; + +draw_failed: + Assert( 0 ); + return E_FAIL; +} + +#endif // #ifndef OSX + +HRESULT IDirect3DDevice9::DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +BOOL IDirect3DDevice9::ShowCursor(BOOL bShow) +{ + // FIXME NOP + //DXABSTRACT_BREAK_ON_ERROR(); + return TRUE; +} + +void d3drect_to_glmbox( D3DRECT *src, GLScissorBox_t *dst ) +{ + // to convert from a d3d rect to a GL rect you have to fix up the vertical axis, since D3D Y=0 is the top, but GL Y=0 is the bottom. + // you can't fix it without knowing the height. + + dst->width = src->x2 - src->x1; + dst->x = src->x1; // left edge + + dst->height = src->y2 - src->y1; + dst->y = src->y1; // bottom edge - take large Y from d3d and subtract from surf height. +} + +HRESULT IDirect3DDevice9::Clear(DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) +{ + GL_BATCH_PERF_CALL_TIMER; + + if ( m_bFBODirty ) + { + UpdateBoundFBO(); + } + + g_nTotalDrawsOrClears++; + + m_ctx->FlushDrawStatesNoShaders(); + + + //debugging Color = (rand() | 0xFF0000FF) & 0xFF3F3FFF; + if (!Count) + { + // run clear with no added rectangle + m_ctx->Clear( (Flags&D3DCLEAR_TARGET)!=0, Color, + (Flags&D3DCLEAR_ZBUFFER)!=0, Z, + (Flags&D3DCLEAR_STENCIL)!=0, Stencil, + NULL + ); + } + else + { + GLScissorBox_t tempbox; + + // do the rects one by one and convert each one to GL form + for( uint i=0; i<Count; i++) + { + D3DRECT d3dtempbox = pRects[i]; + d3drect_to_glmbox( &d3dtempbox, &tempbox ); + + m_ctx->Clear( (Flags&D3DCLEAR_TARGET)!=0, Color, + (Flags&D3DCLEAR_ZBUFFER)!=0, Z, + (Flags&D3DCLEAR_STENCIL)!=0, Stencil, + &tempbox + ); + } + } + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetTransform(D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetTextureStageState(DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::ValidateDevice(DWORD* pNumPasses) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9* pMaterial) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::SetMaterial - ignored.")); +// DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +HRESULT IDirect3DDevice9::LightEnable(DWORD Index,BOOL Enable) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetScissorRect(CONST RECT* pRect) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + //int nSurfaceHeight = m_ctx->m_drawingFBO->m_attach[ kAttColor0 ].m_tex->m_layout->m_key.m_ySize; + + GLScissorBox_t newScissorBox = { pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top }; + gl.m_ScissorBox = newScissorBox; + m_ctx->WriteScissorBox( &gl.m_ScissorBox ); + return S_OK; +} + +HRESULT IDirect3DDevice9::GetDeviceCaps(D3DCAPS9* pCaps) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + // "Adapter" is used to index amongst the set of fake-adapters maintained in the display DB + GLMDisplayDB *db = GetDisplayDB(); + int glmRendererIndex = -1; + int glmDisplayIndex = -1; + + GLMRendererInfoFields glmRendererInfo; + GLMDisplayInfoFields glmDisplayInfo; + + bool result = db->GetFakeAdapterInfo( m_params.m_adapter, &glmRendererIndex, &glmDisplayIndex, &glmRendererInfo, &glmDisplayInfo ); (void)result; + Assert (!result); + // just leave glmRendererInfo filled out for subsequent code to look at as needed. + + FillD3DCaps9( glmRendererInfo, pCaps ); + + return S_OK; +} + + +HRESULT IDirect3DDevice9::TestCooperativeLevel() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + // game calls this to see if device was lost. + // last I checked the device was still attached to the computer. + // so, return OK. + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetClipPlane(DWORD Index,CONST float* pPlane) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert(Index<2); + + // We actually push the clip plane coeffs to two places + // - into a shader param for ARB mode + // - and into the API defined clip plane slots for GLSL mode. + + // if ARB mode... THIS NEEDS TO GO... it's messing up the dirty ranges.. + { + // this->SetVertexShaderConstantF( DXABSTRACT_VS_CLIP_PLANE_BASE+Index, pPlane, 1 ); // stash the clip plane values into shader param - translator knows where to look + } + + // if GLSL mode... latch it and let FlushStates push it out + { + GLClipPlaneEquation_t peq; + peq.x = pPlane[0]; + peq.y = pPlane[1]; + peq.z = pPlane[2]; + peq.w = pPlane[3]; + + gl.m_ClipPlaneEquation[ Index ] = peq; + FlushClipPlaneEquation(); + + // m_ctx->WriteClipPlaneEquation( &peq, Index ); + } + + return S_OK; +} + +HRESULT IDirect3DDevice9::EvictManagedResources() +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + GLMPRINTF(("-X- IDirect3DDevice9::EvictManagedResources --> IGNORED")); + return S_OK; +} + +HRESULT IDirect3DDevice9::SetLight(DWORD Index,CONST D3DLIGHT9*) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +void IDirect3DDevice9::SetGammaRamp(UINT iSwapChain,DWORD Flags,CONST D3DGAMMARAMP* pRamp) +{ + GL_BATCH_PERF_CALL_TIMER; + Assert( GetCurrentOwnerThreadId() == ThreadGetCurrentId() ); + + if ( g_pLauncherMgr ) + { + g_pLauncherMgr->SetGammaRamp( pRamp->red, pRamp->green, pRamp->blue ); + } +} + +void TOGLMETHODCALLTYPE IDirect3DDevice9::SaveGLState() +{ +} + +void TOGLMETHODCALLTYPE IDirect3DDevice9::RestoreGLState() +{ + m_ctx->ForceFlushStates(); + + m_bFBODirty = true; +} + +void IDirect3DDevice9::AcquireThreadOwnership( ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->MakeCurrent( true ); +} + + +void IDirect3DDevice9::ReleaseThreadOwnership( ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->ReleaseCurrent( true ); +} + +void IDirect3DDevice9::SetMaxUsedVertexShaderConstantsHintNonInline( uint nMaxReg ) +{ + GL_BATCH_PERF_CALL_TIMER; + m_ctx->SetMaxUsedVertexShaderConstantsHint( nMaxReg ); +} + +HRESULT IDirect3DDevice9::SetRenderState( D3DRENDERSTATETYPE State, DWORD Value ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + TOGL_NULL_DEVICE_CHECK; + +#if GLMDEBUG + // None of this is needed normally. + char rsSpew = 1; + char ignored = 0; + + if (State >= D3DRS_VALUE_LIMIT) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad + return S_OK; + } + + const D3D_RSINFO *info = &g_D3DRS_INFO_unpacked[ State ]; + if (info->m_state != State) + { + DXABSTRACT_BREAK_ON_ERROR(); // bad - we never set up that state in our list + return S_OK; + } + + if (rsSpew) + { + GLMPRINTF(("-X- IDirect3DDevice9::SetRenderState: set %s(%d) to %d(0x%08x) ( class %d, defval is %d(0x%08x) )", GLMDecode( eD3D_RSTATE,State),State, Value,Value, info->m_class, info->m_defval,info->m_defval )); + } + + switch( info->m_class ) + { + case 0: // just ignore quietly. example: D3DRS_LIGHTING + ignored = 1; + break; + + case 1: + { + // no GL response - and no error as long as the write value matches the default + if (Value != info->m_defval) + { + static char stop_here_1 = 0; + if (stop_here_1) + DXABSTRACT_BREAK_ON_ERROR(); + } + break; + } + + case 2: + { + // provide GL response, but only support known default value + if (Value != info->m_defval) + { + static char stop_here_2 = 0; + if (stop_here_2) + DXABSTRACT_BREAK_ON_ERROR(); + } + // fall through to mode 3 + } + case 3: + // normal case - the switch statement below will handle this + break; + } +#endif + + switch (State) + { + case D3DRS_ZENABLE: // kGLDepthTestEnable + { + gl.m_DepthTestEnable.enable = Value; + m_ctx->WriteDepthTestEnable( &gl.m_DepthTestEnable ); + break; + } + case D3DRS_ZWRITEENABLE: // kGLDepthMask + { + gl.m_DepthMask.mask = Value; + m_ctx->WriteDepthMask( &gl.m_DepthMask ); + break; + } + case D3DRS_ZFUNC: + { + // kGLDepthFunc + GLenum func = D3DCompareFuncToGL( Value ); + gl.m_DepthFunc.func = func; + m_ctx->WriteDepthFunc( &gl.m_DepthFunc ); + break; + } + + case D3DRS_COLORWRITEENABLE: // kGLColorMaskSingle + { + gl.m_ColorMaskSingle.r = ((Value & D3DCOLORWRITEENABLE_RED) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.g = ((Value & D3DCOLORWRITEENABLE_GREEN)!= 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.b = ((Value & D3DCOLORWRITEENABLE_BLUE) != 0) ? 0xFF : 0x00; + gl.m_ColorMaskSingle.a = ((Value & D3DCOLORWRITEENABLE_ALPHA)!= 0) ? 0xFF : 0x00; + m_ctx->WriteColorMaskSingle( &gl.m_ColorMaskSingle ); + break; + } + + case D3DRS_CULLMODE: // kGLCullFaceEnable / kGLCullFrontFace + { + switch (Value) + { + case D3DCULL_NONE: + { + gl.m_CullFaceEnable.enable = false; + gl.m_CullFrontFace.value = GL_CCW; //doesn't matter + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + + case D3DCULL_CW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CW; //origGL_CCW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + case D3DCULL_CCW: + { + gl.m_CullFaceEnable.enable = true; + gl.m_CullFrontFace.value = GL_CCW; //origGL_CW; + + m_ctx->WriteCullFaceEnable( &gl.m_CullFaceEnable ); + m_ctx->WriteCullFrontFace( &gl.m_CullFrontFace ); + break; + } + default: + { + DXABSTRACT_BREAK_ON_ERROR(); + break; + } + } + break; + } + + //-------------------------------------------------------------------------------------------- alphablend stuff + + case D3DRS_ALPHABLENDENABLE: // kGLBlendEnable + { + gl.m_BlendEnable.enable = Value; + m_ctx->WriteBlendEnable( &gl.m_BlendEnable ); + break; + } + + case D3DRS_BLENDOP: // kGLBlendEquation // D3D blend-op ==> GL blend equation + { + GLenum equation = D3DBlendOperationToGL( Value ); + gl.m_BlendEquation.equation = equation; + m_ctx->WriteBlendEquation( &gl.m_BlendEquation ); + break; + } + + case D3DRS_SRCBLEND: // kGLBlendFactor // D3D blend-factor ==> GL blend factor + case D3DRS_DESTBLEND: // kGLBlendFactor + { + GLenum factor = D3DBlendFactorToGL( Value ); + + if (State==D3DRS_SRCBLEND) + { + gl.m_BlendFactor.srcfactor = factor; + } + else + { + gl.m_BlendFactor.dstfactor = factor; + } + m_ctx->WriteBlendFactor( &gl.m_BlendFactor ); + break; + } + + case D3DRS_SRGBWRITEENABLE: // kGLBlendEnableSRGB + { + gl.m_BlendEnableSRGB.enable = Value; + m_ctx->WriteBlendEnableSRGB( &gl.m_BlendEnableSRGB ); + break; + } + + //-------------------------------------------------------------------------------------------- alphatest stuff + + case D3DRS_ALPHATESTENABLE: + { + gl.m_AlphaTestEnable.enable = Value; + m_ctx->WriteAlphaTestEnable( &gl.m_AlphaTestEnable ); + break; + } + + case D3DRS_ALPHAREF: + { + gl.m_AlphaTestFunc.ref = Value / 255.0f; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + + case D3DRS_ALPHAFUNC: + { + GLenum func = D3DCompareFuncToGL( Value );; + gl.m_AlphaTestFunc.func = func; + m_ctx->WriteAlphaTestFunc( &gl.m_AlphaTestFunc ); + break; + } + + //-------------------------------------------------------------------------------------------- stencil stuff + + case D3DRS_STENCILENABLE: // GLStencilTestEnable_t + { + gl.m_StencilTestEnable.enable = Value; + m_ctx->WriteStencilTestEnable( &gl.m_StencilTestEnable ); + break; + } + + case D3DRS_STENCILFAIL: // GLStencilOp_t "what do you do if stencil test fails" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.sfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILZFAIL: // GLStencilOp_t "what do you do if stencil test passes *but* depth test fails, if depth test happened" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dpfail = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILPASS: // GLStencilOp_t "what do you do if stencil test and depth test both pass" + { + GLenum stencilop = D3DStencilOpToGL( Value ); + gl.m_StencilOp.dppass = stencilop; + + m_ctx->WriteStencilOp( &gl.m_StencilOp,0 ); + m_ctx->WriteStencilOp( &gl.m_StencilOp,1 ); // ********* need to recheck this + break; + } + + case D3DRS_STENCILFUNC: // GLStencilFunc_t + { + GLenum stencilfunc = D3DCompareFuncToGL( Value ); + gl.m_StencilFunc.frontfunc = gl.m_StencilFunc.backfunc = stencilfunc; + + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILREF: // GLStencilFunc_t + { + gl.m_StencilFunc.ref = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILMASK: // GLStencilFunc_t + { + gl.m_StencilFunc.mask = Value; + m_ctx->WriteStencilFunc( &gl.m_StencilFunc ); + break; + } + + case D3DRS_STENCILWRITEMASK: // GLStencilWriteMask_t + { + gl.m_StencilWriteMask.mask = Value; + m_ctx->WriteStencilWriteMask( &gl.m_StencilWriteMask ); + break; + } + + case D3DRS_FOGENABLE: // none of these are implemented yet... erk + { + gl.m_FogEnable = (Value != 0); + GLMPRINTF(("-D- fogenable = %d",Value )); + break; + } + + case D3DRS_SCISSORTESTENABLE: // kGLScissorEnable + { + gl.m_ScissorEnable.enable = Value; + m_ctx->WriteScissorEnable( &gl.m_ScissorEnable ); + break; + } + + case D3DRS_DEPTHBIAS: // kGLDepthBias + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.units = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + + // good ref on these: http://aras-p.info/blog/2008/06/12/depth-bias-and-the-power-of-deceiving-yourself/ + case D3DRS_SLOPESCALEDEPTHBIAS: + { + // the value in the dword is actually a float + float fvalue = *(float*)&Value; + gl.m_DepthBias.factor = fvalue; + + m_ctx->WriteDepthBias( &gl.m_DepthBias ); + break; + } + + // Alpha to coverage + case D3DRS_ADAPTIVETESS_Y: + { + gl.m_AlphaToCoverageEnable.enable = Value; + m_ctx->WriteAlphaToCoverageEnable( &gl.m_AlphaToCoverageEnable ); + break; + } + + case D3DRS_CLIPPLANEENABLE: // kGLClipPlaneEnable + { + // d3d packs all the enables into one word. + // we break that out so we don't do N glEnable calls to sync - + // GLM is tracking one unique enable per plane. + for( int i=0; i<kGLMUserClipPlanes; i++) + { + gl.m_ClipPlaneEnable[i].enable = (Value & (1<<i)) != 0; + } + + for( int x=0; x<kGLMUserClipPlanes; x++) + m_ctx->WriteClipPlaneEnable( &gl.m_ClipPlaneEnable[x], x ); + break; + } + + //-------------------------------------------------------------------------------------------- polygon/fill mode + + case D3DRS_FILLMODE: + { + GLuint mode = 0; + switch(Value) + { + case D3DFILL_POINT: mode = GL_POINT; break; + case D3DFILL_WIREFRAME: mode = GL_LINE; break; + case D3DFILL_SOLID: mode = GL_FILL; break; + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + gl.m_PolygonMode.values[0] = gl.m_PolygonMode.values[1] = mode; + m_ctx->WritePolygonMode( &gl.m_PolygonMode ); + break; + } + +#if GLMDEBUG + case D3DRS_MULTISAMPLEANTIALIAS: + case D3DRS_MULTISAMPLEMASK: + case D3DRS_FOGCOLOR: + case D3DRS_FOGTABLEMODE: + case D3DRS_FOGSTART: + case D3DRS_FOGEND: + case D3DRS_FOGDENSITY: + case D3DRS_RANGEFOGENABLE: + case D3DRS_FOGVERTEXMODE: + case D3DRS_COLORWRITEENABLE1: // kGLColorMaskMultiple + case D3DRS_COLORWRITEENABLE2: // kGLColorMaskMultiple + case D3DRS_COLORWRITEENABLE3: // kGLColorMaskMultiple + case D3DRS_SEPARATEALPHABLENDENABLE: + case D3DRS_BLENDOPALPHA: + case D3DRS_SRCBLENDALPHA: + case D3DRS_DESTBLENDALPHA: + case D3DRS_TWOSIDEDSTENCILMODE: // -> GL_STENCIL_TEST_TWO_SIDE_EXT... not yet implemented ? + case D3DRS_CCW_STENCILFAIL: // GLStencilOp_t + case D3DRS_CCW_STENCILZFAIL: // GLStencilOp_t + case D3DRS_CCW_STENCILPASS: // GLStencilOp_t + case D3DRS_CCW_STENCILFUNC: // GLStencilFunc_t + case D3DRS_CLIPPING: // ???? is clipping ever turned off ?? + case D3DRS_LASTPIXEL: + case D3DRS_DITHERENABLE: + case D3DRS_SHADEMODE: + default: + ignored = 1; + break; +#endif + } + +#if GLMDEBUG + if (rsSpew && ignored) + { + GLMPRINTF(("-X- (ignored)")); + } +#endif + + return S_OK; +} + +HRESULT IDirect3DDevice9::SetSamplerStateNonInline( DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value ) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + Assert( Sampler < GLM_SAMPLER_COUNT ); + + m_ctx->SetSamplerDirty( Sampler ); + + switch( Type ) + { + case D3DSAMP_ADDRESSU: + m_ctx->SetSamplerAddressU( Sampler, Value ); + break; + case D3DSAMP_ADDRESSV: + m_ctx->SetSamplerAddressV( Sampler, Value ); + break; + case D3DSAMP_ADDRESSW: + m_ctx->SetSamplerAddressW( Sampler, Value ); + break; + case D3DSAMP_BORDERCOLOR: + m_ctx->SetSamplerBorderColor( Sampler, Value ); + break; + case D3DSAMP_MAGFILTER: + m_ctx->SetSamplerMagFilter( Sampler, Value ); + break; + case D3DSAMP_MIPFILTER: + m_ctx->SetSamplerMipFilter( Sampler, Value ); + break; + case D3DSAMP_MINFILTER: + m_ctx->SetSamplerMinFilter( Sampler, Value ); + break; + case D3DSAMP_MIPMAPLODBIAS: + m_ctx->SetSamplerMipMapLODBias( Sampler, Value ); + break; + case D3DSAMP_MAXMIPLEVEL: + m_ctx->SetSamplerMaxMipLevel( Sampler, Value); + break; + case D3DSAMP_MAXANISOTROPY: + m_ctx->SetSamplerMaxAnisotropy( Sampler, Value); + break; + case D3DSAMP_SRGBTEXTURE: + //m_samplers[ Sampler ].m_srgb = Value; + m_ctx->SetSamplerSRGBTexture(Sampler, Value); + break; + case D3DSAMP_SHADOWFILTER: + m_ctx->SetShadowFilter(Sampler, Value); + break; + + default: DXABSTRACT_BREAK_ON_ERROR(); break; + } + + return S_OK; +} + +void IDirect3DDevice9::SetSamplerStatesNonInline( + DWORD Sampler, DWORD AddressU, DWORD AddressV, DWORD AddressW, + DWORD MinFilter, DWORD MagFilter, DWORD MipFilter, + DWORD MinLod, float LodBias) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + + Assert( Sampler < GLM_SAMPLER_COUNT); + + m_ctx->SetSamplerDirty( Sampler ); + + m_ctx->SetSamplerStates( Sampler, AddressU, AddressV, AddressW, MinFilter, MagFilter, MipFilter, MinLod, LodBias ); +} + +HRESULT IDirect3DDevice9::SetTextureNonInline(DWORD Stage,IDirect3DBaseTexture9* pTexture) +{ + GL_BATCH_PERF_CALL_TIMER; + GL_PUBLIC_ENTRYPOINT_CHECKS( this ); + Assert( Stage < GLM_SAMPLER_COUNT ); + m_textures[Stage] = pTexture; + m_ctx->SetSamplerTex( Stage, pTexture ? pTexture->m_tex : NULL ); + return S_OK; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +void* ID3DXBuffer::GetBufferPointer() +{ + GL_BATCH_PERF_CALL_TIMER; + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + +DWORD ID3DXBuffer::GetBufferSize() +{ + GL_BATCH_PERF_CALL_TIMER; + DXABSTRACT_BREAK_ON_ERROR(); + return 0; +} + + + +#ifndef _MSC_VER +#pragma mark ----- More D3DX stuff +#endif + +// ------------------------------------------------------------------------------------------------------------------------------ // +// D3DX stuff. +// ------------------------------------------------------------------------------------------------------------------------------ // + +// matrix stack... + +HRESULT D3DXCreateMatrixStack( DWORD Flags, LPD3DXMATRIXSTACK* ppStack) +{ + + *ppStack = new ID3DXMatrixStack; + + (*ppStack)->Create(); + + return S_OK; +} + +ID3DXMatrixStack::ID3DXMatrixStack( void ) +{ + m_refcount[0] = 1; + m_refcount[1] = 0; +}; + +void ID3DXMatrixStack::AddRef( int which, char *comment ) +{ + Assert( which >= 0 ); + Assert( which < 2 ); + m_refcount[which]++; +}; + +ULONG ID3DXMatrixStack::Release( int which, char *comment ) +{ + Assert( which >= 0 ); + Assert( which < 2 ); + + bool deleting = false; + + m_refcount[which]--; + if ( (!m_refcount[0]) && (!m_refcount[1]) ) + { + deleting = true; + } + + if (deleting) + { + if (m_mark) + { + GLMPRINTF(("")) ; // place to hang a breakpoint + } + delete this; + return 0; + } + else + { + return m_refcount[0]; + } +}; + + +HRESULT ID3DXMatrixStack::Create() +{ + m_stack.EnsureCapacity( 16 ); // 1KB ish + m_stack.AddToTail(); + m_stackTop = 0; // top of stack is at index 0 currently + + LoadIdentity(); + + return S_OK; +} + +D3DXMATRIX* ID3DXMatrixStack::GetTop() +{ + return (D3DXMATRIX*)&m_stack[ m_stackTop ]; +} + +void ID3DXMatrixStack::Push() +{ + D3DMATRIX temp = m_stack[ m_stackTop ]; + m_stack.AddToTail( temp ); + m_stackTop ++; +} + +void ID3DXMatrixStack::Pop() +{ + int elem = m_stackTop--; + m_stack.Remove( elem ); +} + +void ID3DXMatrixStack::LoadIdentity() +{ + D3DXMATRIX *mat = GetTop(); + + D3DXMatrixIdentity( mat ); +} + +void ID3DXMatrixStack::LoadMatrix( const D3DXMATRIX *pMat ) +{ + *(GetTop()) = *pMat; +} + + +void ID3DXMatrixStack::MultMatrix( const D3DXMATRIX *pMat ) +{ + + // http://msdn.microsoft.com/en-us/library/bb174057(VS.85).aspx + // This method right-multiplies the given matrix to the current matrix + // (transformation is about the current world origin). + // m_pstack[m_currentPos] = m_pstack[m_currentPos] * (*pMat); + // This method does not add an item to the stack, it replaces the current + // matrix with the product of the current matrix and the given matrix. + + + DXABSTRACT_BREAK_ON_ERROR(); +} + +void ID3DXMatrixStack::MultMatrixLocal( const D3DXMATRIX *pMat ) +{ + // http://msdn.microsoft.com/en-us/library/bb174058(VS.85).aspx + // This method left-multiplies the given matrix to the current matrix + // (transformation is about the local origin of the object). + // m_pstack[m_currentPos] = (*pMat) * m_pstack[m_currentPos]; + // This method does not add an item to the stack, it replaces the current + // matrix with the product of the given matrix and the current matrix. + + + DXABSTRACT_BREAK_ON_ERROR(); +} + +HRESULT ID3DXMatrixStack::ScaleLocal(FLOAT x, FLOAT y, FLOAT z) +{ + // http://msdn.microsoft.com/en-us/library/bb174066(VS.85).aspx + // Scale the current matrix about the object origin. + // This method left-multiplies the current matrix with the computed + // scale matrix. The transformation is about the local origin of the object. + // + // D3DXMATRIX tmp; + // D3DXMatrixScaling(&tmp, x, y, z); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + +HRESULT ID3DXMatrixStack::RotateAxisLocal(CONST D3DXVECTOR3* pV, FLOAT Angle) +{ + // http://msdn.microsoft.com/en-us/library/bb174062(VS.85).aspx + // Left multiply the current matrix with the computed rotation + // matrix, counterclockwise about the given axis with the given angle. + // (rotation is about the local origin of the object) + + // D3DXMATRIX tmp; + // D3DXMatrixRotationAxis( &tmp, pV, angle ); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + // Because the rotation is left-multiplied to the matrix stack, the rotation + // is relative to the object's local coordinate space. + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + +HRESULT ID3DXMatrixStack::TranslateLocal(FLOAT x, FLOAT y, FLOAT z) +{ + // http://msdn.microsoft.com/en-us/library/bb174068(VS.85).aspx + // Left multiply the current matrix with the computed translation + // matrix. (transformation is about the local origin of the object) + + // D3DXMATRIX tmp; + // D3DXMatrixTranslation( &tmp, x, y, z ); + // m_stack[m_currentPos] = tmp * m_stack[m_currentPos]; + + DXABSTRACT_BREAK_ON_ERROR(); + return S_OK; +} + + + + +const char* D3DXGetPixelShaderProfile( IDirect3DDevice9 *pDevice ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return ""; +} + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4701) // potentially uninitialized local variable 'temp' used +#endif +D3DXMATRIX* D3DXMatrixMultiply( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2 ) +{ + D3DXMATRIX temp; + + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + temp.m[i][j] = (pM1->m[ i ][ 0 ] * pM2->m[ 0 ][ j ]) + + (pM1->m[ i ][ 1 ] * pM2->m[ 1 ][ j ]) + + (pM1->m[ i ][ 2 ] * pM2->m[ 2 ][ j ]) + + (pM1->m[ i ][ 3 ] * pM2->m[ 3 ][ j ]); + } + } + *pOut = temp; + return pOut; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// Transform a 3D vector by a given matrix, projecting the result back into w = 1 +// http://msdn.microsoft.com/en-us/library/ee417622(VS.85).aspx +D3DXVECTOR3* D3DXVec3TransformCoord(D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM) +{ + D3DXVECTOR3 vOut; + + float norm = (pM->m[0][3] * pV->x) + (pM->m[1][3] * pV->y) + (pM->m[2][3] *pV->z) + pM->m[3][3]; + if ( norm ) + { + float norm_inv = 1.0f / norm; + vOut.x = (pM->m[0][0] * pV->x + pM->m[1][0] * pV->y + pM->m[2][0] * pV->z + pM->m[3][0]) * norm_inv; + vOut.y = (pM->m[0][1] * pV->x + pM->m[1][1] * pV->y + pM->m[2][1] * pV->z + pM->m[3][1]) * norm_inv; + vOut.z = (pM->m[0][2] * pV->x + pM->m[1][2] * pV->y + pM->m[2][2] * pV->z + pM->m[3][2]) * norm_inv; + } + else + { + vOut.x = vOut.y = vOut.z = 0.0f; + } + + *pOut = vOut; + + return pOut; +} + + +void D3DXMatrixIdentity( D3DXMATRIX *mat ) +{ + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + mat->m[i][j] = (i==j) ? 1.0f : 0.0f; // 1's on the diagonal. + } + } +} + +D3DXMATRIX* D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ) +{ + D3DXMatrixIdentity( pOut ); + pOut->m[3][0] = x; + pOut->m[3][1] = y; + pOut->m[3][2] = z; + return pOut; +} + +D3DXMATRIX* D3DXMatrixInverse( D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM ) +{ + Assert( sizeof( D3DXMATRIX ) == (16 * sizeof(float) ) ); + Assert( sizeof( VMatrix ) == (16 * sizeof(float) ) ); + Assert( pDeterminant == NULL ); // homey don't play that + + VMatrix *origM = (VMatrix*)pM; + VMatrix *destM = (VMatrix*)pOut; + + bool success = MatrixInverseGeneral( *origM, *destM ); (void)success; + Assert( success ); + + return pOut; +} + + +D3DXMATRIX* D3DXMatrixTranspose( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM ) +{ + if (pOut != pM) + { + for( int i=0; i<4; i++) + { + for( int j=0; j<4; j++) + { + pOut->m[i][j] = pM->m[j][i]; + } + } + } + else + { + D3DXMATRIX temp = *pM; + D3DXMatrixTranspose( pOut, &temp ); + } + + return NULL; +} + + +D3DXPLANE* D3DXPlaneNormalize( D3DXPLANE *pOut, CONST D3DXPLANE *pP) +{ + // not very different from normalizing a vector. + // figure out the square root of the sum-of-squares of the x,y,z components + // make sure that's non zero + // then divide all four components by that value + // or return some dummy plane like 0,0,1,0 if it fails + + float len = sqrt( (pP->a * pP->a) + (pP->b * pP->b) + (pP->c * pP->c) ); + if (len > 1e-10) //FIXME need a real epsilon here ? + { + pOut->a = pP->a / len; pOut->b = pP->b / len; pOut->c = pP->c / len; pOut->d = pP->d / len; + } + else + { + pOut->a = 0.0f; pOut->b = 0.0f; pOut->c = 1.0f; pOut->d = 0.0f; + } + return pOut; +} + + +D3DXVECTOR4* D3DXVec4Transform( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV, CONST D3DXMATRIX *pM ) +{ + VMatrix *mat = (VMatrix*)pM; + Vector4D *vIn = (Vector4D*)pV; + Vector4D *vOut = (Vector4D*)pOut; + + Vector4DMultiplyTranspose( *mat, *vIn, *vOut ); + + return pOut; +} + + + +D3DXVECTOR4* D3DXVec4Normalize( D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pV ) +{ + Vector4D *vIn = (Vector4D*) pV; + Vector4D *vOut = (Vector4D*) pOut; + + *vOut = *vIn; + Vector4DNormalize( *vOut ); + + return pOut; +} + + +D3DXMATRIX* D3DXMatrixOrthoOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn,FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXMATRIX* D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXMATRIX* D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) +{ + DXABSTRACT_BREAK_ON_ERROR(); + return NULL; +} + + +D3DXPLANE* D3DXPlaneTransform( D3DXPLANE *pOut, CONST D3DXPLANE *pP, CONST D3DXMATRIX *pM ) +{ + float *out = &pOut->a; + + // dot dot dot + for( int x=0; x<4; x++ ) + { + out[x] = (pM->m[0][x] * pP->a) + + (pM->m[1][x] * pP->b) + + (pM->m[2][x] * pP->c) + + (pM->m[3][x] * pP->d); + } + + return pOut; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +IDirect3D9 *Direct3DCreate9(UINT SDKVersion) +{ + GLMPRINTF(( "-X- Direct3DCreate9: %d", SDKVersion )); + + return new IDirect3D9; +} + +// ------------------------------------------------------------------------------------------------------------------------------ // + +void D3DPERF_SetOptions( DWORD dwOptions ) +{ +} + + +HRESULT D3DXCompileShader( + LPCSTR pSrcData, + UINT SrcDataLen, + CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, + LPCSTR pFunctionName, + LPCSTR pProfile, + DWORD Flags, + LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE* ppConstantTable) +{ + DXABSTRACT_BREAK_ON_ERROR(); // is anyone calling this ? + return S_OK; +} + +#if defined(DX_TO_GL_ABSTRACTION) +void toglGetClientRect( void *hWnd, RECT *destRect ) +{ + // the only useful answer this call can offer, is the size of the canvas. + // actually getting the window bounds is not useful. + // so, see if a D3D device is up and running, and if so, + // dig in and find out its backbuffer size and use that. + + uint width, height; + g_pLauncherMgr->RenderedSize( width, height, false ); // false = get them, don't set them + Assert( width!=0 && height!=0 ); + + destRect->left = 0; + destRect->top = 0; + destRect->right = width; + destRect->bottom = height; + + //GLMPRINTF(( "-D- GetClientRect returning rect of (0,0, %d,%d)",width,height )); + + return; +} + +#endif + +#endif diff --git a/togl/linuxwin/glentrypoints.cpp b/togl/linuxwin/glentrypoints.cpp new file mode 100644 index 0000000..25e49ab --- /dev/null +++ b/togl/linuxwin/glentrypoints.cpp @@ -0,0 +1,510 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// glentrypoints.cpp +// +//=============================================================================// +// Immediately include gl.h, etc. here to avoid compilation warnings. + +#include "togl/rendermechanism.h" + +#include "appframework/AppFramework.h" +#include "appframework/IAppSystemGroup.h" +#include "tier0/dbg.h" +#include "tier0/icommandline.h" +#include "tier0/platform.h" +#include "interface.h" +#include "filesystem.h" +#include "filesystem_init.h" +#include "tier1/convar.h" +#include "vstdlib/cvar.h" +#include "inputsystem/ButtonCode.h" +#include "tier1.h" +#include "tier2/tier2.h" + +#ifdef _LINUX +#include <GL/glx.h> +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +#if !defined(DX_TO_GL_ABSTRACTION) +#error +#endif + +#if defined(OSX) || defined(LINUX) || (defined (WIN32) && defined( DX_TO_GL_ABSTRACTION )) + #include "appframework/ilaunchermgr.h" + ILauncherMgr *g_pLauncherMgr = NULL; +#endif + +#define DEBUG_ALL_GLCALLS 0 + +#if DEBUG_ALL_GLCALLS +bool g_bDebugOpenGLCalls = true; +bool g_bPrintOpenGLCalls = false; + +#define GL_EXT(x,glmajor,glminor) +#define GL_FUNC(ext,req,ret,fn,arg,call) \ + static ret (*fn##_gldebugptr) arg = NULL; \ + static ret fn##_gldebug arg { \ + if (!g_bDebugOpenGLCalls) { return fn##_gldebugptr call; } \ + if (g_bPrintOpenGLCalls) { \ + printf("Calling %s ... ", #fn); \ + fflush(stdout); \ + } \ + ret retval = fn##_gldebugptr call; \ + if (g_bPrintOpenGLCalls) { \ + printf("%s returned!\n", #fn); \ + fflush(stdout); \ + } \ + const GLenum err = glGetError_gldebugptr(); \ + if ( err == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) { \ + const GLenum fberr = gGL->glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT ); \ + printf("%s triggered error GL_INVALID_FRAMEBUFFER_OPERATION_EXT! (0x%X)\n\n\n", #fn, (int) fberr); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } else if (err != GL_NO_ERROR) { \ + printf("%s triggered error 0x%X!\n\n\n", #fn, (int) err); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } \ + return retval; \ +} + +#define GL_FUNC_VOID(ext,req,fn,arg,call) \ + static void (*fn##_gldebugptr) arg = NULL; \ + static void fn##_gldebug arg { \ + if (!g_bDebugOpenGLCalls) { fn##_gldebugptr call; return; } \ + if (g_bPrintOpenGLCalls) { \ + printf("Calling %s ... ", #fn); \ + fflush(stdout); \ + } \ + fn##_gldebugptr call; \ + if (g_bPrintOpenGLCalls) { \ + printf("%s returned!\n", #fn); \ + fflush(stdout); \ + } \ + const GLenum err = glGetError_gldebugptr(); \ + if ( err == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) { \ + const GLenum fberr = gGL->glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT ); \ + printf("%s triggered error GL_INVALID_FRAMEBUFFER_OPERATION_EXT! (0x%X)\n\n\n", #fn, (int) fberr); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } else if (err != GL_NO_ERROR) { \ + printf("%s triggered error 0x%X!\n\n\n", #fn, (int) err); \ + fflush(stdout); \ + __asm__ __volatile__ ( "int $3\n\t" ); \ + } \ +} + +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +#endif + +COpenGLEntryPoints *gGL = NULL; +GL_GetProcAddressCallbackFunc_t gGL_GetProcAddressCallback = NULL; + +void *VoidFnPtrLookup_GlMgr(const char *fn, bool &okay, const bool bRequired, void *fallback) +{ + void *retval = NULL; + if ((!okay) && (!bRequired)) // always look up if required (so we get a complete list of crucial missing symbols). + return NULL; + + // SDL does the right thing, so we never need to use tier0 in this case. + retval = (*gGL_GetProcAddressCallback)(fn, okay, bRequired, fallback); + //printf("CDynamicFunctionOpenGL: SDL_GL_GetProcAddress(\"%s\") returned %p\n", fn, retval); + if ((retval == NULL) && (fallback != NULL)) + { + //printf("CDynamicFunctionOpenGL: Using fallback %p for \"%s\"\n", fallback, fn); + retval = fallback; + } + + // Note that a non-NULL response doesn't mean it's safe to call the function! + // You always have to check that the extension is supported; + // an implementation MAY return NULL in this case, but it doesn't have to (and doesn't, with the DRI drivers). + okay = (okay && (retval != NULL)); + if (bRequired && !okay) + fprintf(stderr, "Could not find required OpenGL entry point '%s'!\n", fn); + + return retval; +} + +COpenGLEntryPoints *GetOpenGLEntryPoints(GL_GetProcAddressCallbackFunc_t callback) +{ + if (gGL == NULL) + { + gGL_GetProcAddressCallback = callback; + gGL = new COpenGLEntryPoints(); + if (!gGL->m_bHave_OpenGL) + Error( "Missing basic required OpenGL functionality." ); + + } + return gGL; +} + +void ClearOpenGLEntryPoints() +{ + if ( gGL ) + { + gGL->ClearEntryPoints(); + } +} +COpenGLEntryPoints *ToGLConnectLibraries( CreateInterfaceFn factory ) +{ + ConnectTier1Libraries( &factory, 1 ); + ConVar_Register(); + ConnectTier2Libraries( &factory, 1 ); + + if ( !g_pFullFileSystem ) + { + Warning( "ToGL was unable to access the required interfaces!\n" ); + } + + // NOTE! : Overbright is 1.0 so that Hammer will work properly with the white bumped and unbumped lightmaps. + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); + + #if defined( USE_SDL ) + g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL ); + #endif + + return gGL; +} + +void ToGLDisconnectLibraries() +{ + DisconnectTier2Libraries(); + ConVar_Unregister(); + DisconnectTier1Libraries(); +} + +#define GLVERNUM(Major, Minor, Patch) (((Major) * 100000) + ((Minor) * 1000) + (Patch)) + +static void GetOpenGLVersion(int *major, int *minor, int *patch) +{ + *major = *minor = *patch = 0; + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + if (glGetString) + { + const char *version = (const char *) glGetString(GL_VERSION); + if (version) + { + sscanf( version, "%d.%d.%d", major, minor, patch ); + } + } +} + +static int GetOpenGLVersionMajor() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return major; +} + +static int GetOpenGLVersionMinor() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return minor; +} + +static int GetOpenGLVersionPatch() +{ + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + return patch; +} + +static bool CheckBaseOpenGLVersion() +{ + const int NEED_MAJOR = 2; + const int NEED_MINOR = 0; + const int NEED_PATCH = 0; + + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + + const int need = GLVERNUM(NEED_MAJOR, NEED_MINOR, NEED_PATCH); + const int have = GLVERNUM(major, minor, patch); + if (have < need) + { + fprintf(stderr, "PROBLEM: You appear to have OpenGL %d.%d.%d, but we need at least %d.%d.%d!\n", + major, minor, patch, NEED_MAJOR, NEED_MINOR, NEED_PATCH); + return false; + } + return true; +} + +static bool CheckOpenGLExtension_internal(const char *ext, const int coremajor, const int coreminor) +{ + if ((coremajor >= 0) && (coreminor >= 0)) // we know that this extension is part of the base spec as of GL_VERSION coremajor.coreminor. + { + int major, minor, patch; + GetOpenGLVersion(&major, &minor, &patch); + const int need = GLVERNUM(coremajor, coreminor, 0); + const int have = GLVERNUM(major, minor, patch); + if (have >= need) + return true; // we definitely have access to this "extension," as it is part of this version of the GL's core functionality. + } + + // okay, see if the GL_EXTENSIONS string reports it. + static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString"); + if (!glGetString) + return false; + + // hacky scanning of this string, because I don't want to spend time breaking it into a vector like I should have. + const char *extensions = (const char *) glGetString(GL_EXTENSIONS); + const size_t extlen = strlen(ext); + while ((extensions) && (*extensions)) + { + const char *ptr = strstr(extensions, ext); +#if _WIN32 + if (!ptr) + { + static CDynamicFunctionOpenGL< true, const char *( APIENTRY *)( ), const char * > wglGetExtensionsStringEXT("wglGetExtensionsStringEXT"); + if (wglGetExtensionsStringEXT) + { + extensions = wglGetExtensionsStringEXT(); + ptr = strstr(extensions, ext); + } + + if (!ptr) + { + return false; + } + } +#elif !defined ( OSX ) + if (!ptr) + { + static CDynamicFunctionOpenGL< true, Display *( APIENTRY *)( ), Display* > glXGetCurrentDisplay("glXGetCurrentDisplay"); + static CDynamicFunctionOpenGL< true, const char *( APIENTRY *)( Display*, int ), const char * > glXQueryExtensionsString("glXQueryExtensionsString"); + if (glXQueryExtensionsString && glXGetCurrentDisplay) + { + extensions = glXQueryExtensionsString(glXGetCurrentDisplay(), 0); + ptr = strstr(extensions, ext); + } + } +#endif + + if (!ptr) + return false; + + // make sure this matches the entire string, and isn't a substring match of some other extension. + // if ( ( (string is at start of extension list) or (the char before the string is a space) ) and + // (the next char after the string is a space or a null terminator) ) + if ( ((ptr == extensions) || (ptr[-1] == ' ')) && + ((ptr[extlen] == ' ') || (ptr[extlen] == '\0')) ) + return true; // found it! + + extensions = ptr + extlen; // skip ahead, search again. + } + return false; +} + +static bool CheckOpenGLExtension(const char *ext, const int coremajor, const int coreminor) +{ + const bool retval = CheckOpenGLExtension_internal(ext, coremajor, coreminor); + printf("This system %s the OpenGL extension %s.\n", retval ? "supports" : "DOES NOT support", ext); + return retval; +} + +// The GL context you want entry points for must be current when you hit this constructor! +COpenGLEntryPoints::COpenGLEntryPoints() + : m_nTotalGLCycles(0) + , m_nTotalGLCalls(0) + , m_nOpenGLVersionMajor(GetOpenGLVersionMajor()) + , m_nOpenGLVersionMinor(GetOpenGLVersionMinor()) + , m_nOpenGLVersionPatch(GetOpenGLVersionPatch()) + , m_bHave_OpenGL(CheckBaseOpenGLVersion()) // may reset to false as these lookups happen. +#define GL_EXT(x,glmajor,glminor) , m_bHave_##x(CheckOpenGLExtension(#x, glmajor, glminor)) +#define GL_FUNC(ext,req,ret,fn,arg,call) , fn(#fn, m_bHave_##ext) +#define GL_FUNC_VOID(ext,req,fn,arg,call) , fn(#fn, m_bHave_##ext) +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +{ + // Locally cache the copy of the GL device strings, to avoid needing to call these glGet's (which can be extremely slow) more than once. + const char *pszString = ( const char * )glGetString(GL_VENDOR); + m_pGLDriverStrings[cGLVendorString] = strdup( pszString ? pszString : "" ); + + m_nDriverProvider = cGLDriverProviderUnknown; + if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "nvidia" ) ) + m_nDriverProvider = cGLDriverProviderNVIDIA; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "amd" ) || V_stristr( m_pGLDriverStrings[cGLVendorString], "ati" ) ) + m_nDriverProvider = cGLDriverProviderAMD; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "intel" ) ) + m_nDriverProvider = cGLDriverProviderIntelOpenSource; + else if ( V_stristr( m_pGLDriverStrings[cGLVendorString], "apple" ) ) + m_nDriverProvider = cGLDriverProviderApple; + + pszString = ( const char * )glGetString(GL_RENDERER); + m_pGLDriverStrings[cGLRendererString] = strdup( pszString ? pszString : "" ); + + pszString = ( const char * )glGetString(GL_VERSION); + m_pGLDriverStrings[cGLVersionString] = strdup( pszString ? pszString : "" ); + + pszString = ( const char * )glGetString(GL_EXTENSIONS); + m_pGLDriverStrings[cGLExtensionsString] = strdup( pszString ? pszString : "" ); + + printf( "OpenGL: %s %s (%d.%d.%d)\n", m_pGLDriverStrings[ cGLRendererString ], m_pGLDriverStrings[ cGLVersionString ], + m_nOpenGLVersionMajor, m_nOpenGLVersionMinor, m_nOpenGLVersionPatch ); + + // !!! FIXME: Alfred says the original GL_APPLE_fence code only exists to + // !!! FIXME: hint Apple's drivers and not because we rely on the + // !!! FIXME: functionality. If so, just remove this check (and the + // !!! FIXME: GL_NV_fence code entirely). + if ((m_bHave_OpenGL) && ((!m_bHave_GL_NV_fence) && (!m_bHave_GL_ARB_sync) && (!m_bHave_GL_APPLE_fence))) + { + Error( "Required OpenGL extension \"GL_NV_fence\", \"GL_ARB_sync\", or \"GL_APPLE_fence\" is not supported. Please upgrade your OpenGL driver." ); + } + + // same extension, different name. + if (m_bHave_GL_EXT_vertex_array_bgra || m_bHave_GL_ARB_vertex_array_bgra) + { + m_bHave_GL_EXT_vertex_array_bgra = m_bHave_GL_ARB_vertex_array_bgra = true; + } + + // GL_ARB_framebuffer_object is a superset of GL_EXT_framebuffer_object, + // (etc) but if you don't call in through the ARB entry points, you won't + // get the relaxed restrictions on mismatched attachment dimensions. + if (m_bHave_GL_ARB_framebuffer_object) + { + m_bHave_GL_EXT_framebuffer_object = true; + m_bHave_GL_EXT_framebuffer_blit = true; + m_bHave_GL_EXT_framebuffer_multisample = true; + glBindFramebufferEXT.Force(glBindFramebuffer.Pointer()); + glBindRenderbufferEXT.Force(glBindRenderbuffer.Pointer()); + glCheckFramebufferStatusEXT.Force(glCheckFramebufferStatus.Pointer()); + glDeleteRenderbuffersEXT.Force(glDeleteRenderbuffers.Pointer()); + glFramebufferRenderbufferEXT.Force(glFramebufferRenderbuffer.Pointer()); + glFramebufferTexture2DEXT.Force(glFramebufferTexture2D.Pointer()); + glFramebufferTexture3DEXT.Force(glFramebufferTexture3D.Pointer()); + glGenFramebuffersEXT.Force(glGenFramebuffers.Pointer()); + glGenRenderbuffersEXT.Force(glGenRenderbuffers.Pointer()); + glDeleteFramebuffersEXT.Force(glDeleteFramebuffers.Pointer()); + glBlitFramebufferEXT.Force(glBlitFramebuffer.Pointer()); + glRenderbufferStorageMultisampleEXT.Force(glRenderbufferStorageMultisample.Pointer()); + } + +#if DEBUG_ALL_GLCALLS + // push all GL calls through the debug wrappers. +#define GL_EXT(x,glmajor,glminor) +#define GL_FUNC(ext,req,ret,fn,arg,call) \ + fn##_gldebugptr = this->fn; \ + this->fn.Force(fn##_gldebug); +#define GL_FUNC_VOID(ext,req,fn,arg,call) \ + fn##_gldebugptr = this->fn; \ + this->fn.Force(fn##_gldebug); +#include "togl/glfuncs.inl" +#undef GL_FUNC_VOID +#undef GL_FUNC +#undef GL_EXT +#endif + +#ifdef OSX + m_bHave_GL_NV_bindless_texture = false; + m_bHave_GL_AMD_pinned_memory = false; +#else + if ( ( m_bHave_GL_NV_bindless_texture ) && ( !CommandLine()->CheckParm( "-gl_nv_bindless_texturing" ) ) ) + { + m_bHave_GL_NV_bindless_texture = false; + glGetTextureHandleNV.Force( NULL ); + glGetTextureSamplerHandleNV.Force( NULL ); + glMakeTextureHandleResidentNV.Force( NULL ); + glMakeTextureHandleNonResidentNV.Force( NULL ); + glUniformHandleui64NV.Force( NULL ); + glUniformHandleui64vNV.Force( NULL ); + glProgramUniformHandleui64NV.Force( NULL ); + glProgramUniformHandleui64vNV.Force( NULL ); + glIsTextureHandleResidentNV.Force( NULL ); + } + + if ( !CommandLine()->CheckParm( "-gl_amd_pinned_memory" ) ) + { + m_bHave_GL_AMD_pinned_memory = false; + } +#endif // !OSX + + // Getting reports of black screens, etc. with ARB_buffer_storage and AMD drivers. This type of thing: + // http://forums.steampowered.com/forums/showthread.php?t=3266806 + // So disable it for now. + if ( ( m_nDriverProvider == cGLDriverProviderAMD ) || CommandLine()->CheckParm( "-gl_disable_arb_buffer_storage" ) ) + { + m_bHave_GL_ARB_buffer_storage = false; + } + + printf( "GL_NV_bindless_texture: %s\n", m_bHave_GL_NV_bindless_texture ? "ENABLED" : "DISABLED" ); + printf( "GL_AMD_pinned_memory: %s\n", m_bHave_GL_AMD_pinned_memory ? "ENABLED" : "DISABLED" ); + printf( "GL_ARB_buffer_storage: %s\n", m_bHave_GL_ARB_buffer_storage ? "AVAILABLE" : "NOT AVAILABLE" ); + printf( "GL_EXT_texture_sRGB_decode: %s\n", m_bHave_GL_EXT_texture_sRGB_decode ? "AVAILABLE" : "NOT AVAILABLE" ); + + bool bGLCanDecodeS3TCTextures = m_bHave_GL_EXT_texture_compression_s3tc || ( m_bHave_GL_EXT_texture_compression_dxt1 && m_bHave_GL_ANGLE_texture_compression_dxt3 && m_bHave_GL_ANGLE_texture_compression_dxt5 ); + if ( !bGLCanDecodeS3TCTextures ) + { + Error( "This application requires either the GL_EXT_texture_compression_s3tc, or the GL_EXT_texture_compression_dxt1 + GL_ANGLE_texture_compression_dxt3 + GL_ANGLE_texture_compression_dxt5 OpenGL extensions. Please install S3TC texture support.\n" ); + } + +#ifdef OSX + if ( CommandLine()->FindParm( "-glmnosrgbdecode" ) ) + { + Msg( "Forcing m_bHave_GL_EXT_texture_sRGB_decode off.\n" ); + m_bHave_GL_EXT_texture_sRGB_decode = false; + } +#endif + +#ifndef OSX + if ( !m_bHave_GL_EXT_texture_sRGB_decode ) + { + Error( "Required OpenGL extension \"GL_EXT_texture_sRGB_decode\" is not supported. Please update your OpenGL driver.\n" ); + } +#endif +} + +COpenGLEntryPoints::~COpenGLEntryPoints() +{ + for ( uint i = 0; i < cGLTotalDriverProviders; ++i ) + { + free( m_pGLDriverStrings[i] ); + m_pGLDriverStrings[i] = NULL; + } +} + +void COpenGLEntryPoints::ClearEntryPoints() +{ + #define GL_EXT(x,glmajor,glminor) + #define GL_FUNC(ext,req,ret,fn,arg,call) fn.Force( NULL ); + #define GL_FUNC_VOID(ext,req,fn,arg,call) fn.Force( NULL ); + #include "togl/glfuncs.inl" + #undef GL_FUNC_VOID + #undef GL_FUNC + #undef GL_EXT +} +// Turn off memdbg macros (turned on up top) since this is included like a header +#include "tier0/memdbgoff.h" + + + diff --git a/togl/linuxwin/glmgr.cpp b/togl/linuxwin/glmgr.cpp new file mode 100644 index 0000000..1c18720 --- /dev/null +++ b/togl/linuxwin/glmgr.cpp @@ -0,0 +1,6092 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// glmgr.cpp +// +//=============================================================================== +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" + +#include "tier0/vprof.h" +#include "glmtexinlines.h" + +#include "materialsystem/IShader.h" +#include "appframework/ilaunchermgr.h" + +#include "convar.h" + +#include "glmgr_flush.inl" + +#ifdef OSX +#include <OpenGL/OpenGL.h> +#include "intelglmallocworkaround.h" +#endif + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + + +// Whether the code should use gl_arb_debug_output. This causes error messages to be streamed, via callback, to the application. +// It is much friendlier to the MTGL driver. +// NOTE: This can be turned off after launch, but it cannot be turned on after launch--it implies a context-creation-time +// behavior. +ConVar gl_debug_output( "gl_debug_output", "1" ); + + +// Whether or not we should batch up our creation and deletion behavior. +ConVar gl_batch_tex_creates( "gl_batch_tex_creates", "0" ); +ConVar gl_batch_tex_destroys( "gl_batch_tex_destroys", "0" ); + +//=============================================================================== + +// g_nTotalDrawsOrClears is reset to 0 in Present() +uint g_nTotalDrawsOrClears, g_nTotalVBLockBytes, g_nTotalIBLockBytes; + +#if GL_TELEMETRY_GPU_ZONES +TelemetryGPUStats_t g_TelemetryGPUStats; +#endif + +const int kGLMInitialTexCount = 4096; +const int kGLMReUpTexCount = 1024; +const int kGLMHighWaterUndeleted = 2048; +const int kDeletedTextureDim = 4; +const uint32 g_garbageTextureBits[ 4 * kDeletedTextureDim * kDeletedTextureDim ] = { 0 }; + +char g_nullFragmentProgramText [] = +{ + "!!ARBfp1.0 \n" + "PARAM black = { 0.0, 0.0, 0.0, 1.0 }; \n" // opaque black + "MOV result.color, black; \n" + "END \n\n\n" + "//GLSLfp\n" + "void main()\n" + "{\n" + "gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n" + "}\n" + +}; + +// make dummy programs for doing texture preload via dummy draw +char g_preloadTexVertexProgramText[] = +{ + "//GLSLvp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "void main() \n" + "{ \n" + "vec4 pos = ftransform(); // vec4( 0.1, 0.1, 0.1, 0.1 ); \n" + "vec4 tex = vec4( 0.0, 0.0, 0.0, 0.0 ); \n" + " \n" + "gl_Position = pos; \n" + "otex = tex; \n" + "} \n" +}; + +char g_preload2DTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform sampler2D sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = texture2D( sampler15, otex.xy ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +char g_preload3DTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform sampler3D sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = texture3D( sampler15, otex.xyz ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +char g_preloadCubeTexFragmentProgramText[] = +{ + "//GLSLfp \n" + "#version 120 \n" + "varying vec4 otex; \n" + "//SAMPLERMASK-8000 // may not be needed \n" + "//HIGHWATER-30 // may not be needed \n" + " \n" + "uniform vec4 pc[31]; \n" + "uniform samplerCube sampler15; \n" + " \n" + "void main() \n" + "{ \n" + "vec4 r0; \n" + "r0 = textureCube( sampler15, otex.xyz ); \n" + "gl_FragColor = r0; //discard; \n" + "} \n" +}; + +const char* glSourceToString(GLenum source) +{ + switch (source) + { + case GL_DEBUG_SOURCE_API_ARB: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: return "WINDOW_SYSTEM"; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: return "SHADER_COMPILER"; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: return "THIRD_PARTY"; + case GL_DEBUG_SOURCE_APPLICATION_ARB: return "APPLICATION"; + case GL_DEBUG_SOURCE_OTHER_ARB: return "OTHER"; + default: break; + } + return "UNKNOWN"; +} + +const char* glTypeToString(GLenum type) +{ + switch (type) + { + case GL_DEBUG_TYPE_ERROR_ARB: return "ERROR"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: return "DEPRECATION"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: return "UNDEFINED_BEHAVIOR"; + case GL_DEBUG_TYPE_PORTABILITY_ARB: return "PORTABILITY"; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: return "PERFORMANCE"; + case GL_DEBUG_TYPE_OTHER_ARB: return "OTHER"; + default: break; + } + return "UNKNOWN"; +} + +const char* glSeverityToString(GLenum severity) +{ + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH_ARB: return "HIGH"; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: return "MEDIUM"; + case GL_DEBUG_SEVERITY_LOW_ARB: return "LOW"; + default: break; + } + return "UNKNOWN"; +} + +bool g_bDebugOutputBreakpoints = true; + +void APIENTRY GL_Debug_Output_Callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) +{ + const char *sSource = glSourceToString(source), + *sType = glTypeToString(type), + *sSeverity = glSeverityToString(severity); + + // According to NVidia, this error is a bug in the driver and not really an error (it's a warning in newer drivers): "Texture X is base level inconsistent. Check texture size" + if ( ( type == GL_DEBUG_TYPE_ERROR_ARB ) && strstr( message, "base level inconsistent" ) ) + { + return; + } + + if ( gl_debug_output.GetBool() || type == GL_DEBUG_TYPE_ERROR_ARB ) + { + Msg( "GL: [%s][%s][%s][%d]: %s\n", sSource, sType, sSeverity, id, message ); + } + +#ifdef WIN32 + OutputDebugStringA( message ); +#endif + + if ( ( type == GL_DEBUG_TYPE_ERROR_ARB ) && ( g_bDebugOutputBreakpoints ) ) + { + DebuggerBreak(); + } +} + +void GLMDebugPrintf( const char *pMsg, ... ) +{ + //$ TODO: Should this call Warning()? + //$ TODO: Should replace call these calls with Warning() / Msg() / DevMsg()? + + va_list args; + va_start( args, pMsg ); + vprintf( pMsg, args ); + va_end( args ); +} + +//=============================================================================== +// functions that are dependant on g_pLauncherMgr + +inline bool MakeContextCurrent( PseudoGLContextPtr hContext ) +{ + return g_pLauncherMgr->MakeContextCurrent( hContext ); +} + +inline PseudoGLContextPtr GetMainContext() +{ + return g_pLauncherMgr->GetMainContext(); +} + +inline PseudoGLContextPtr GetGLContextForWindow( void* windowref ) +{ + return g_pLauncherMgr->GetGLContextForWindow( windowref ); +} + +inline void IncrementWindowRefCount() +{ + g_pLauncherMgr->IncWindowRefCount(); +} +inline void DecrementWindowRefCount() +{ + g_pLauncherMgr->DecWindowRefCount(); +} +inline void ShowPixels( CShowPixelsParams *params ) +{ + g_pLauncherMgr->ShowPixels(params); +} + +inline void DisplayedSize( uint &width, uint &height ) +{ + g_pLauncherMgr->DisplayedSize( width, height ); +} + +inline void GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut ) +{ + g_pLauncherMgr->GetDesiredPixelFormatAttribsAndRendererInfo( ptrOut, countOut, rendInfoOut ); +} + +inline void GetStackCrawl( CStackCrawlParams *params ) +{ + g_pLauncherMgr->GetStackCrawl(params); +} + +#if GLMDEBUG +inline void PumpWindowsMessageLoop() +{ + g_pLauncherMgr->PumpWindowsMessageLoop(); +} +inline int GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvents = false ) +{ + return g_pLauncherMgr->GetEvents( pEvents, nMaxEventsToReturn, debugEvents ); +} +#endif + +//=============================================================================== +// helper routines for debug + +static bool hasnonzeros( float *values, int count ) +{ + for( int i=0; i<count; i++) + { + if (values[i] != 0.0) + { + return true; + } + } + return false; +} + +static void printmat( char *label, int baseSlotNumber, int slots, float *m00 ) +{ + // print label.. + // fetch 4 from row, print as a row + // fetch 4 from column, print as a row + + float row[4]; + float col[4]; + + if (hasnonzeros( m00, slots*4) ) + { + GLMPRINTF(("-D- %s", label )); + for( int islot=0; islot<4; islot++ ) // we always run this loop til 4, but we special case the printing if there are only 3 submitted + { + // extract row and column floats + for( int slotcol=0; slotcol<4; slotcol++) + { + //copy + row[slotcol] = m00[(islot*4)+slotcol]; + + // transpose + col[slotcol] = m00[(slotcol*4)+islot]; + } + if (slots==4) + { + GLMPRINTF(( "-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] T=> [ %10.5f %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + row[0],row[1],row[2],row[3], + col[0],col[1],col[2],col[3] + )); + } + else + { + if (islot<3) + { + GLMPRINTF(( "-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] T=> [ %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + row[0],row[1],row[2],row[3], + col[0],col[1],col[2] + )); + } + else + { + GLMPRINTF(( "-D- %03d: T=> [ %10.5f %10.5f %10.5f ]", + baseSlotNumber+islot, + col[0],col[1],col[2] + )); + } + } + } + GLMPRINTSTR(("-D-")); + } + else + { + GLMPRINTF(("-D- %s - (all 0.0)", label )); + } + +} + + +static void transform_dp4( float *in4, float *m00, int slots, float *out4 ) +{ + // m00 points to a column. + // each DP is one column of the matrix ( m00[4*n] + // if we are passed a three slot matrix, this is three columns, the source W plays into all three columns, but we must set the final output W to 1 ? + for( int n=0; n<slots; n++) + { + float col4[4]; + + col4[0] = m00[(4*n)+0]; + col4[1] = m00[(4*n)+1]; + col4[2] = m00[(4*n)+2]; + col4[3] = m00[(4*n)+3]; + + out4[n] = 0.0; + for( int inner = 0; inner < 4; inner++ ) + { + out4[n] += in4[inner] * col4[inner]; + } + } + if (slots==3) + { + out4[3] = 1.0; + } +} + +//=============================================================================== + + +//=============================================================================== +// GLMgr static methods + +GLMgr *g_glmgr = NULL; + +void GLMgr::NewGLMgr( void ) +{ + if (!g_glmgr) + { + #if GLMDEBUG + // check debug mode early in program lifetime + GLMDebugInitialize( true ); + #endif + + g_glmgr = new GLMgr; + } +} + +GLMgr *GLMgr::aGLMgr( void ) +{ + assert( g_glmgr != NULL); + return g_glmgr; +} + +void GLMgr::DelGLMgr( void ) +{ + if (g_glmgr) + { + delete g_glmgr; + g_glmgr = NULL; + } +} + +// GLMgr class methods + +GLMgr::GLMgr() +{ +} + + +GLMgr::~GLMgr() +{ +} + +//=============================================================================== + +GLMContext *GLMgr::NewContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params ) +{ + // this now becomes really simple. We just pass through the params. + + return new GLMContext( pDevice, params ); +} + +void GLMgr::DelContext( GLMContext *context ) +{ + delete context; +} + +void GLMgr::SetCurrentContext( GLMContext *context ) +{ +#if defined( USE_SDL ) + context->m_nCurOwnerThreadId = ThreadGetCurrentId(); + if ( !MakeContextCurrent( context->m_ctx ) ) + { + // give up + GLMStop(); + } + Assert( 0 ); +#endif +} + +GLMContext *GLMgr::GetCurrentContext( void ) +{ +#if defined( USE_SDL ) + PseudoGLContextPtr context = GetMainContext(); + return (GLMContext*) context; +#else + Assert( 0 ); + return NULL; +#endif +} + + +// #define CHECK_THREAD_USAGE 1 + + +//=============================================================================== +// GLMContext public methods +void GLMContext::MakeCurrent( bool bRenderThread ) +{ + tmZone( TELEMETRY_LEVEL0, 0, "GLMContext::MakeCurrent" ); + Assert( m_nCurOwnerThreadId == 0 || m_nCurOwnerThreadId == ThreadGetCurrentId() ); + +#if defined( USE_SDL ) + +#ifndef CHECK_THREAD_USAGE + if ( bRenderThread ) + { +// Msg( "******************************************** %08x Acquiring Context\n", ThreadGetCurrentId() ); + m_nCurOwnerThreadId = ThreadGetCurrentId(); + bool bSuccess = MakeContextCurrent( m_ctx ); + if ( !bSuccess ) + { + Assert( 0 ); + } + } +#else + uint32 dwThreadId = ThreadGetCurrentId(); + + if ( bRenderThread || dwThreadId == m_dwRenderThreadId ) + { + m_nCurOwnerThreadId = ThreadGetCurrentId(); + m_dwRenderThreadId = dwThreadId; + MakeContextCurrent( m_ctx ); + m_bIsThreading = true; + } + else if ( !m_bIsThreading ) + { + m_nCurOwnerThreadId = ThreadGetCurrentId(); + MakeContextCurrent( m_ctx ); + } + else + { + Assert( 0 ); + } +#endif + +#else + Assert( 0 ); +#endif +} + + +void GLMContext::ReleaseCurrent( bool bRenderThread ) +{ + tmZone( TELEMETRY_LEVEL0, 0, "GLMContext::ReleaseCurrent" ); + Assert( m_nCurOwnerThreadId == ThreadGetCurrentId() ); + +#if defined( USE_SDL ) + +#ifndef CHECK_THREAD_USAGE + if ( bRenderThread ) + { +// Msg( "******************************************** %08x Releasing Context\n", ThreadGetCurrentId() ); + m_nCurOwnerThreadId = 0; + m_nThreadOwnershipReleaseCounter++; + MakeContextCurrent( NULL ); + } +#else + m_nCurOwnerThreadId = 0; + m_nThreadOwnershipReleaseCounter++; + MakeContextCurrent( NULL ); + if ( bRenderThread ) + { + m_bIsThreading = false; + } +#endif + +#else + Assert( 0 ); +#endif +} + + +// This function forces all GL state to be re-sent to the context. Some state will only be set on the next batch flush. +void GLMContext::ForceFlushStates() +{ + // Flush various render states + m_AlphaTestEnable.Flush(); + m_AlphaTestFunc.Flush(); + + m_DepthBias.Flush(); + + m_ScissorEnable.Flush(); + m_ScissorBox.Flush(); + + m_ViewportBox.Flush(); + m_ViewportDepthRange.Flush(); + + m_ColorMaskSingle.Flush(); + + m_BlendEnable.Flush(); + m_BlendFactor.Flush(); + + m_BlendEnableSRGB.Flush(); + + m_DepthTestEnable.Flush(); + m_DepthFunc.Flush(); + m_DepthMask.Flush(); + + m_StencilTestEnable.Flush(); + m_StencilFunc.Flush(); + m_StencilOp.Flush(); + m_StencilWriteMask.Flush(); + + m_ClearColor.Flush(); + m_ClearDepth.Flush(); + m_ClearStencil.Flush(); + + m_ClipPlaneEnable.Flush(); // always push clip state + m_ClipPlaneEquation.Flush(); + + m_CullFaceEnable.Flush(); + + m_CullFrontFace.Flush(); + + m_PolygonMode.Flush(); + + m_AlphaToCoverageEnable.Flush(); + m_ColorMaskMultiple.Flush(); + m_BlendEquation.Flush(); + m_BlendColor.Flush(); + // Reset various things so they get reset on the next batch flush + m_activeTexture = -1; + + for ( int i = 0; i < GLM_SAMPLER_COUNT; i++ ) + { + SetSamplerTex( i, m_samplers[i].m_pBoundTex ); + SetSamplerDirty( i ); + } + + // Attributes/vertex attribs + ClearCurAttribs(); + + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + memset( &m_boundVertexAttribs[0], 0xFF, sizeof( m_boundVertexAttribs ) ); + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + gGL->glDisableVertexAttribArray( index ); + + // Program + NullProgram(); + + // FBO + BindFBOToCtx( m_boundReadFBO, GL_READ_FRAMEBUFFER_EXT ); + BindFBOToCtx( m_boundDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + // Current VB/IB/pinned memory buffers + gGL->glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nBoundGLBuffer[ kGLMIndexBuffer] ); + gGL->glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nBoundGLBuffer[ kGLMVertexBuffer] ); + +#ifndef OSX + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } +#endif +} + +const GLMRendererInfoFields& GLMContext::Caps( void ) +{ + return m_caps; +} + +void GLMContext::DumpCaps( void ) +{ + /* + #define dumpfield( fff ) printf( "\n "#fff" : %d", (int) m_caps.fff ) + #define dumpfield_hex( fff ) printf( "\n "#fff" : 0x%08x", (int) m_caps.fff ) + #define dumpfield_str( fff ) printf( "\n "#fff" : %s", m_caps.fff ) + */ + + #define dumpfield( fff ) printf( "\n %-30s : %d", #fff, (int) m_caps.fff ) + #define dumpfield_hex( fff ) printf( "\n %-30s : 0x%08x", #fff, (int) m_caps.fff ) + #define dumpfield_str( fff ) printf( "\n %-30s : %s", #fff, m_caps.fff ) + + printf("\n-------------------------------- context caps for context %08x", (uint)this); + + dumpfield( m_fullscreen ); + dumpfield( m_accelerated ); + dumpfield( m_windowed ); + dumpfield_hex( m_rendererID ); + dumpfield( m_displayMask ); + dumpfield( m_bufferModes ); + dumpfield( m_colorModes ); + dumpfield( m_accumModes ); + dumpfield( m_depthModes ); + dumpfield( m_stencilModes ); + dumpfield( m_maxAuxBuffers ); + dumpfield( m_maxSampleBuffers ); + dumpfield( m_maxSamples ); + dumpfield( m_sampleModes ); + dumpfield( m_sampleAlpha ); + dumpfield_hex( m_vidMemory ); + dumpfield_hex( m_texMemory ); + + dumpfield_hex( m_pciVendorID ); + dumpfield_hex( m_pciDeviceID ); + dumpfield_str( m_pciModelString ); + dumpfield_str( m_driverInfoString ); + + printf( "\n m_osComboVersion: 0x%08x (%d.%d.%d)", m_caps.m_osComboVersion, (m_caps.m_osComboVersion>>16)&0xFF, (m_caps.m_osComboVersion>>8)&0xFF, (m_caps.m_osComboVersion)&0xFF ); + + dumpfield( m_ati ); + if (m_caps.m_ati) + { + dumpfield( m_atiR5xx ); + dumpfield( m_atiR6xx ); + dumpfield( m_atiR7xx ); + dumpfield( m_atiR8xx ); + dumpfield( m_atiNewer ); + } + + dumpfield( m_intel ); + if (m_caps.m_intel) + { + dumpfield( m_intel95x ); + dumpfield( m_intel3100 ); + dumpfield( m_intelHD4000 ); + } + + dumpfield( m_nv ); + if (m_caps.m_nv) + { + //dumpfield( m_nvG7x ); + dumpfield( m_nvG8x ); + dumpfield( m_nvNewer ); + } + + dumpfield( m_hasGammaWrites ); + dumpfield( m_hasMixedAttachmentSizes ); + dumpfield( m_hasBGRA ); + dumpfield( m_hasNewFullscreenMode ); + dumpfield( m_hasNativeClipVertexMode ); + dumpfield( m_maxAniso ); + + dumpfield( m_hasBindableUniforms ); + dumpfield( m_maxVertexBindableUniforms ); + dumpfield( m_maxFragmentBindableUniforms ); + dumpfield( m_maxBindableUniformSize ); + + dumpfield( m_hasUniformBuffers ); + dumpfield( m_hasPerfPackage1 ); + + dumpfield( m_cantBlitReliably ); + dumpfield( m_cantAttachSRGB ); + dumpfield( m_cantResolveFlipped ); + dumpfield( m_cantResolveScaled ); + dumpfield( m_costlyGammaFlips ); + dumpfield( m_badDriver1064NV ); + dumpfield( m_badDriver108Intel ); + + printf("\n--------------------------------"); + + #undef dumpfield + #undef dumpfield_hex + #undef dumpfield_str +} + +CGLMTex *GLMContext::NewTex( GLMTexLayoutKey *key, uint levels, const char *debugLabel ) +{ + // get a layout based on the key + GLMTexLayout *layout = m_texLayoutTable->NewLayoutRef( key ); + + CGLMTex *tex = new CGLMTex( this, layout, levels, debugLabel ); + + return tex; +} + +void GLMContext::DelTex( CGLMTex * tex ) +{ + //Queue the texture for deletion in ProcessTextureDeletes + //when we are sure we will hold the context. + m_DeleteTextureQueue.PushItem(tex); +} + +void GLMContext::ProcessTextureDeletes() +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmEvent( "GLMContext::ProcessTextureDeletes" ); +#endif + + CGLMTex* tex = nullptr; + while ( m_DeleteTextureQueue.PopItem( &tex ) ) + { + for( int i = 0; i < GLM_SAMPLER_COUNT; i++) + { + if ( m_samplers[i].m_pBoundTex == tex ) + { + BindTexToTMU( NULL, i ); + } + } + + if ( tex->m_rtAttachCount != 0 ) + { + // RG - huh? wtf? TODO: fix this code which seems to be purposely leaking + // leak it and complain - we may have to implement a deferred-delete system for tex like these + + GLMDebugPrintf("GLMContext::DelTex: Leaking tex %08x [ %s ] - was attached for drawing at time of delete",tex, tex->m_layout->m_layoutSummary ); + + #if 0 + // can't actually do this yet as the draw calls will tank + FOR_EACH_VEC( m_fboTable, i ) + { + CGLMFBO *fbo = m_fboTable[i]; + fbo->TexScrub( tex ); + } + tex->m_rtAttachCount = 0; + #endif + } + else + { + delete tex; + } + } +} + +// push and pop attrib when blit has mixed srgb source and dest? +ConVar gl_radar7954721_workaround_mixed ( "gl_radar7954721_workaround_mixed", "1" ); + +// push and pop attrib on any blit? +ConVar gl_radar7954721_workaround_all ( "gl_radar7954721_workaround_all", "0" ); + +// what attrib mask to use ? +ConVar gl_radar7954721_workaround_maskval ( "gl_radar7954721_workaround_maskval", "0" ); + +enum eBlitFormatClass +{ + eColor, + eDepth, // may not get used. not sure.. + eDepthStencil +}; + +uint glAttachFromClass[ 3 ] = { GL_COLOR_ATTACHMENT0_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_DEPTH_STENCIL_ATTACHMENT_EXT }; + +void glScrubFBO ( GLenum target ) +{ + gGL->glFramebufferRenderbufferEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0); + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + gGL->glFramebufferRenderbufferEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferTexture2DEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0 ); + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + gGL->glFramebufferTexture2DEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); +} + +void glAttachRBOtoFBO ( GLenum target, eBlitFormatClass formatClass, uint rboName ) +{ + switch( formatClass ) + { + case eColor: + gGL->glFramebufferRenderbufferEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + + case eDepth: + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + + case eDepthStencil: + gGL->glFramebufferRenderbufferEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + gGL->glFramebufferRenderbufferEXT ( target, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboName); + break; + } +} + +void glAttachTex2DtoFBO ( GLenum target, eBlitFormatClass formatClass, uint texName, uint texMip ) +{ + switch( formatClass ) + { + case eColor: + gGL->glFramebufferTexture2DEXT ( target, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + + case eDepth: + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + + case eDepthStencil: + gGL->glFramebufferTexture2DEXT ( target, GL_DEPTH_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texName, texMip ); + break; + } +} + +ConVar gl_can_resolve_flipped("gl_can_resolve_flipped", "0" ); +ConVar gl_cannot_resolve_flipped("gl_cannot_resolve_flipped", "0" ); + +// these are only consulted if the m_cant_resolve_scaled cap bool is false. + +ConVar gl_minify_resolve_mode("gl_minify_resolve_mode", "1" ); // if scaled resolve available, for downscaled resolve blits only (i.e. internal blits) +ConVar gl_magnify_resolve_mode("gl_magnify_resolve_mode", "2" ); // if scaled resolve available, for upscaled resolve blits only + + // 0 == old style, two steps + // 1 == faster, one step blit aka XGL_SCALED_RESOLVE_FASTEST_EXT - if available. + // 2 == faster, one step blit aka XGL_SCALED_RESOLVE_NICEST_EXT - if available. + +void GLMContext::SaveColorMaskAndSetToDefault() +{ + // NVidia's driver doesn't ignore the colormask during blitframebuffer calls, so we need to save/restore it: + // “The bug here is that our driver fails to ignore colormask for BlitFramebuffer calls. This was unclear in the original spec, but we resolved it in Khronos last year (https://cvs.khronos.org/bugzilla/show_bug.cgi?id=7969).” + m_ColorMaskSingle.Read( &m_SavedColorMask, 0 ); + + GLColorMaskSingle_t newColorMask; + newColorMask.r = newColorMask.g = newColorMask.b = newColorMask.a = -1; + m_ColorMaskSingle.Write( &newColorMask ); +} + +void GLMContext::RestoreSavedColorMask() +{ + m_ColorMaskSingle.Write( &m_SavedColorMask ); +} + +void GLMContext::Blit2( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "Blit2" ); + g_TelemetryGPUStats.m_nTotalBlit2++; +#endif + + SaveColorMaskAndSetToDefault(); + + Assert( srcFace == 0 ); + Assert( dstFace == 0 ); + + //----------------------------------------------------------------- format assessment + + eBlitFormatClass formatClass = eColor; + uint blitMask= 0; + + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_RED: case GL_BGRA: case GL_RGB: case GL_RGBA: case GL_ALPHA: case GL_LUMINANCE: case GL_LUMINANCE_ALPHA: + formatClass = eColor; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + case GL_DEPTH_COMPONENT: + formatClass = eDepth; + blitMask = GL_DEPTH_BUFFER_BIT; + break; + + case GL_DEPTH_STENCIL_EXT: + formatClass = eDepthStencil; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(!"Unsupported format for blit" ); + GLMStop(); + break; + } + + //----------------------------------------------------------------- blit assessment + + + bool blitResolves = srcTex->m_rboName != 0; + bool blitScales = ((srcRect->xmax - srcRect->xmin) != (dstRect->xmax - dstRect->xmin)) || ((srcRect->ymax - srcRect->ymin) != (dstRect->ymax - dstRect->ymin)); + + bool blitToBack = (dstTex == NULL); + bool blitFlips = blitToBack; // implicit y-flip upon blit to GL_BACK supplied + + //should we support blitFromBack ? + + bool srcGamma = srcTex && ((srcTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0); + bool dstGamma = dstTex && ((dstTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0); + + bool doPushPop = (srcGamma != dstGamma) && gl_radar7954721_workaround_mixed.GetInt() && m_caps.m_nv; // workaround for cross gamma blit problems on NV + // ^^ need to re-check this on some post-10.6.3 build on NV to see if it was fixed + + if (doPushPop) + { + gGL->glPushAttrib( 0 ); + } + + //----------------------------------------------------------------- figure out the plan + + bool blitTwoStep = false; // think positive + + // each subsequent segment here can only set blitTwoStep, not clear it. + // the common case where these get hit is resolve out to presentation + // there may be GL extensions or driver revisions which start doing these safely. + // ideally many blits internally resolve without scaling and can thus go direct without using the scratch tex. + + if (blitResolves && (blitFlips||blitToBack)) // flips, blit to back, same thing (for now) + { + if( gl_cannot_resolve_flipped.GetInt() ) + { + blitTwoStep = true; + } + else if (!gl_can_resolve_flipped.GetInt()) + { + blitTwoStep = blitTwoStep || m_caps.m_cantResolveFlipped; // if neither convar renders an opinion, fall back to the caps to decide if we have to two-step. + } + } + + // only consider trying to use the scaling resolve filter, + // if we are confident we are not headed for two step mode already. + if (!blitTwoStep) + { + if (blitResolves && blitScales) + { + if (m_caps.m_cantResolveScaled) + { + // filter is unchanged, two step mode switches on + blitTwoStep = true; + } + else + { + bool blitScalesDown = ((srcRect->xmax - srcRect->xmin) > (dstRect->xmax - dstRect->xmin)) || ((srcRect->ymax - srcRect->ymin) > (dstRect->ymax - dstRect->ymin)); + int mode = (blitScalesDown) ? gl_minify_resolve_mode.GetInt() : gl_magnify_resolve_mode.GetInt(); + + // roughly speaking, resolve blits that minify represent setup for special effects ("copy framebuffer to me") + // resolve blits that magnify are almost always on the final present in the case where remder size < display size + + switch( mode ) + { + case 0: + default: + // filter is unchanged, two step mode + blitTwoStep = true; + break; + + case 1: + // filter goes to fastest, one step mode + blitTwoStep = false; + filter = XGL_SCALED_RESOLVE_FASTEST_EXT; + break; + + case 2: + // filter goes to nicest, one step mode + blitTwoStep = false; + filter = XGL_SCALED_RESOLVE_NICEST_EXT; + break; + } + } + } + } + + //----------------------------------------------------------------- save old scissor state and disable scissor + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + if (oldsciss.enable) + { + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + } + + //----------------------------------------------------------------- fork in the road, depending on two-step or not + if (blitTwoStep) + { + // a resolve that can't be done directly due to constraints on scaling or flipping. + + // bind scratch FBO0 to read, scrub it, attach RBO + BindFBOToCtx ( m_scratchFBO[0], GL_READ_FRAMEBUFFER_EXT ); + glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); + glAttachRBOtoFBO ( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + + // bind scratch FBO1 to write, scrub it, attach scratch tex + BindFBOToCtx ( m_scratchFBO[1], GL_DRAW_FRAMEBUFFER_EXT ); + glScrubFBO ( GL_DRAW_FRAMEBUFFER_EXT ); + glAttachTex2DtoFBO ( GL_DRAW_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, 0 ); + + // set read and draw buffers appropriately + gGL->glReadBuffer ( glAttachFromClass[formatClass] ); + gGL->glDrawBuffer ( glAttachFromClass[formatClass] ); + + // blit#1 - to resolve to scratch + // implicitly means no scaling, thus will be done with NEAREST sampling + + GLenum resolveFilter = GL_NEAREST; + + gGL->glBlitFramebufferEXT( 0, 0, srcTex->m_layout->m_key.m_xSize, srcTex->m_layout->m_key.m_ySize, + 0, 0, srcTex->m_layout->m_key.m_xSize, srcTex->m_layout->m_key.m_ySize, // same source and dest rect, whole surface + blitMask, resolveFilter ); + + // FBO1 now holds the interesting content. + // scrub FBO0, bind FBO1 to READ, fall through to next stage of blit where 1 goes onto 0 (or BACK) + + glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); // zap FBO0 + BindFBOToCtx ( m_scratchFBO[1], GL_READ_FRAMEBUFFER_EXT ); + + srcTex->ForceRBONonDirty(); + } + else + { +#if 1 + if (srcTex->m_pBlitSrcFBO == NULL) + { + srcTex->m_pBlitSrcFBO = NewFBO(); + BindFBOToCtx( srcTex->m_pBlitSrcFBO, GL_READ_FRAMEBUFFER_EXT ); + if (blitResolves) + { + glAttachRBOtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, srcMip ); + } + } + else + { + BindFBOToCtx ( srcTex->m_pBlitSrcFBO, GL_READ_FRAMEBUFFER_EXT ); + // GLMCheckError(); + } +#else + // arrange source surface on FBO1 for blit directly to dest (which could be FBO0 or BACK) + BindFBOToCtx( m_scratchFBO[1], GL_READ_FRAMEBUFFER_EXT ); + glScrubFBO( GL_READ_FRAMEBUFFER_EXT ); + GLMCheckError(); + if (blitResolves) + { + glAttachRBOtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_READ_FRAMEBUFFER_EXT, formatClass, srcTex->m_texName, srcMip ); + } +#endif + + gGL->glReadBuffer( glAttachFromClass[formatClass] ); + } + + //----------------------------------------------------------------- zero or one blits may have happened above, whichever took place, FBO1 is now on read + + bool yflip = false; + if (blitToBack) + { + // backbuffer is special - FBO0 is left out (either scrubbed already, or not used) + + BindFBOToCtx ( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + gGL->glDrawBuffer ( GL_BACK ); + + yflip = true; + } + else + { + // not going to GL_BACK - use FBO0. set up dest tex or RBO on it. i.e. it's OK to blit from MSAA to MSAA if needed, though unlikely. + Assert( dstTex != NULL ); +#if 1 + if (dstTex->m_pBlitDstFBO == NULL) + { + dstTex->m_pBlitDstFBO = NewFBO(); + BindFBOToCtx( dstTex->m_pBlitDstFBO, GL_DRAW_FRAMEBUFFER_EXT ); + if (dstTex->m_rboName) + { + glAttachRBOtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_texName, dstMip ); + } + } + else + { + BindFBOToCtx( dstTex->m_pBlitDstFBO, GL_DRAW_FRAMEBUFFER_EXT ); + } +#else + BindFBOToCtx( m_scratchFBO[0], GL_DRAW_FRAMEBUFFER_EXT ); GLMCheckError(); + glScrubFBO( GL_DRAW_FRAMEBUFFER_EXT ); + + if (dstTex->m_rboName) + { + glAttachRBOtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_rboName ); + } + else + { + glAttachTex2DtoFBO( GL_DRAW_FRAMEBUFFER_EXT, formatClass, dstTex->m_texName, dstMip ); + } + + gGL->glDrawBuffer ( glAttachFromClass[formatClass] ); GLMCheckError(); +#endif + } + + // final blit + + // i think in general, if we are blitting same size, gl_nearest is the right filter to pass. + // this re-steering won't kick in if there is scaling or a special scaled resolve going on. + if (!blitScales) + { + // steer it + filter = GL_NEAREST; + } + + // this is blit #1 or #2 depending on what took place above. + if (yflip) + { + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymax, dstRect->xmax, dstRect->ymin, // note dest Y's are flipped + blitMask, filter ); + } + else + { + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax, + blitMask, filter ); + } + + //----------------------------------------------------------------- scrub READ and maybe DRAW FBO, and unbind + +// glScrubFBO ( GL_READ_FRAMEBUFFER_EXT ); + BindFBOToCtx ( NULL, GL_READ_FRAMEBUFFER_EXT ); + if (!blitToBack) + { +// glScrubFBO ( GL_DRAW_FRAMEBUFFER_EXT ); + BindFBOToCtx ( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + } + + //----------------------------------------------------------------- restore GLM's drawing FBO + + // restore GLM drawing FBO + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + if (doPushPop) + { + gGL->glPopAttrib( ); + } + + + //----------------------------------------------------------------- restore old scissor state + if (oldsciss.enable) + { + m_ScissorEnable.Write( &oldsciss ); + } + + RestoreSavedColorMask(); +} + + +void GLMContext::BlitTex( CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, GLenum filter, bool useBlitFB ) +{ + // This path doesn't work anymore (or did it ever work in the L4D2 Linux branch?) + DXABSTRACT_BREAK_ON_ERROR(); + return; + + SaveColorMaskAndSetToDefault(); + + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + #if 0 + if (GLMKnob("caps-key",NULL) > 0.0) + { + useBlitFB = false; + } + #endif + + if ( m_caps.m_cantBlitReliably ) // this is referring to a problem with the x3100.. + { + useBlitFB = false; + } + break; + } + + if (0) + { + GLMPRINTF(("-D- Blit from %d %d %d %d to %d %d %d %d", + srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax + )); + + GLMPRINTF(( "-D- src tex layout is %s", srcTex->m_layout->m_layoutSummary )); + GLMPRINTF(( "-D- dst tex layout is %s", dstTex->m_layout->m_layoutSummary )); + } + + int pushed = 0; + uint pushmask = gl_radar7954721_workaround_maskval.GetInt(); + //GL_COLOR_BUFFER_BIT + //| GL_CURRENT_BIT + //| GL_ENABLE_BIT + //| GL_FOG_BIT + //| GL_PIXEL_MODE_BIT + //| GL_SCISSOR_BIT + //| GL_STENCIL_BUFFER_BIT + //| GL_TEXTURE_BIT + //GL_VIEWPORT_BIT + //; + + if (gl_radar7954721_workaround_all.GetInt()!=0) + { + gGL->glPushAttrib( pushmask ); + pushed++; + } + else + { + bool srcGamma = (srcTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + bool dstGamma = (dstTex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + + if (srcGamma != dstGamma) + { + if (gl_radar7954721_workaround_mixed.GetInt()) + { + gGL->glPushAttrib( pushmask ); + pushed++; + } + } + } + + if (useBlitFB) + { + // state we need to save + // current setting of scissor + // current setting of the drawing fbo (no explicit save, it's in the context) + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + // remember to restore m_drawingFBO at end of effort + + // setup + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + + // select which attachment enum we're going to use for the blit + // default to color0, unless it's a depth or stencil flava + + Assert( srcTex->m_layout->m_format->m_glDataFormat == dstTex->m_layout->m_format->m_glDataFormat ); + + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + GLuint blitMask = 0; + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + case GL_DEPTH_COMPONENT: + attachIndex = kAttDepth; + attachIndexGL = GL_DEPTH_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT; + break; + + case GL_DEPTH_STENCIL_EXT: + attachIndex = kAttDepthStencil; + attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(0); + break; + } + + // set the read fb, attach read tex at appropriate attach point, set read buffer + BindFBOToCtx( m_blitReadFBO, GL_READ_FRAMEBUFFER_EXT ); + + GLMFBOTexAttachParams attparams; + attparams.m_tex = srcTex; + attparams.m_face = srcFace; + attparams.m_mip = srcMip; + attparams.m_zslice = 0; + m_blitReadFBO->TexAttach( &attparams, attachIndex, GL_READ_FRAMEBUFFER_EXT ); + + gGL->glReadBuffer( attachIndexGL ); + + + // set the write fb and buffer, and attach write tex + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + attparams.m_tex = dstTex; + attparams.m_face = dstFace; + attparams.m_mip = dstMip; + attparams.m_zslice = 0; + m_blitDrawFBO->TexAttach( &attparams, attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + gGL->glDrawBuffer( attachIndexGL ); + + // do the blit + gGL->glBlitFramebufferEXT( srcRect->xmin, srcRect->ymin, srcRect->xmax, srcRect->ymax, + dstRect->xmin, dstRect->ymin, dstRect->xmax, dstRect->ymax, + blitMask, filter ); + + // cleanup + // unset the read fb and buffer, detach read tex + // unset the write fb and buffer, detach write tex + + m_blitReadFBO->TexDetach( attachIndex, GL_READ_FRAMEBUFFER_EXT ); + + m_blitDrawFBO->TexDetach( attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // set the read and write buffers back to... what ? does it matter for anything but copies ? don't worry about it + + // restore the scissor state + m_ScissorEnable.Write( &oldsciss ); + } + else + { + // textured quad style + + // we must attach the dest tex as the color buffer on the blit draw FBO + // so that means we need to re-set the drawing FBO on exit + + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + switch( srcTex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + break; + + default: + Assert(!"Can't blit that format"); + break; + } + + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + GLMFBOTexAttachParams attparams; + attparams.m_tex = dstTex; + attparams.m_face = dstFace; + attparams.m_mip = dstMip; + attparams.m_zslice = 0; + m_blitDrawFBO->TexAttach( &attparams, attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + gGL->glDrawBuffer( attachIndexGL ); + + // attempt to just set states directly the way we want them, then use the latched states to repair them afterward. + NullProgram(); // out of program mode + + gGL->glDisable ( GL_ALPHA_TEST ); + gGL->glDisable ( GL_CULL_FACE ); + gGL->glDisable ( GL_POLYGON_OFFSET_FILL ); + gGL->glDisable ( GL_SCISSOR_TEST ); + + gGL->glDisable ( GL_CLIP_PLANE0 ); + gGL->glDisable ( GL_CLIP_PLANE1 ); + + gGL->glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + gGL->glDisable ( GL_BLEND ); + + gGL->glDepthMask ( GL_FALSE ); + gGL->glDisable ( GL_DEPTH_TEST ); + + gGL->glDisable ( GL_STENCIL_TEST ); + gGL->glStencilMask ( GL_FALSE ); + + + // now do the unlit textured quad... + gGL->glActiveTexture( GL_TEXTURE0 ); + gGL->glBindTexture( GL_TEXTURE_2D, srcTex->m_texName ); + + gGL->glEnable(GL_TEXTURE_2D); + + // immediate mode is fine + + float topv = 1.0; + float botv = 0.0; + + gGL->glBegin(GL_QUADS); + gGL->glTexCoord2f ( 0.0, botv ); + gGL->glVertex3f ( -1.0, -1.0, 0.0 ); + + gGL->glTexCoord2f ( 1.0, botv ); + gGL->glVertex3f ( 1.0, -1.0, 0.0 ); + + gGL->glTexCoord2f ( 1.0, topv ); + gGL->glVertex3f ( 1.0, 1.0, 0.0 ); + + gGL->glTexCoord2f ( 0.0, topv ); + gGL->glVertex3f ( -1.0, 1.0, 0.0 ); + gGL->glEnd(); + + gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + + gGL->glDisable(GL_TEXTURE_2D); + + BindTexToTMU( m_samplers[0].m_pBoundTex, 0 ); + + // leave active program empty - flush draw states will fix + + // then restore states using the scoreboard + + m_AlphaTestEnable.Flush(); + m_AlphaToCoverageEnable.Flush(); + m_CullFaceEnable.Flush(); + m_DepthBias.Flush(); + m_ScissorEnable.Flush(); + + m_ClipPlaneEnable.FlushIndex( 0 ); + m_ClipPlaneEnable.FlushIndex( 1 ); + + m_ColorMaskSingle.Flush(); + m_BlendEnable.Flush(); + + m_DepthMask.Flush(); + m_DepthTestEnable.Flush(); + + m_StencilWriteMask.Flush(); + m_StencilTestEnable.Flush(); + + // unset the write fb and buffer, detach write tex + + m_blitDrawFBO->TexDetach( attachIndex, GL_DRAW_FRAMEBUFFER_EXT ); + + // put the original FB back in place (both read and draw) + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + } + + while(pushed) + { + gGL->glPopAttrib(); + pushed--; + } + + RestoreSavedColorMask(); +} + +void GLMContext::ResolveTex( CGLMTex *tex, bool forceDirty ) +{ +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "ResolveTex" ); + g_TelemetryGPUStats.m_nTotalResolveTex++; +#endif + + // only run resolve if it's (a) possible and (b) dirty or force-dirtied + if ( ( tex->m_rboName ) && ( tex->IsRBODirty() || forceDirty ) ) + { + // state we need to save + // current setting of scissor + // current setting of the drawing fbo (no explicit save, it's in the context) + GLScissorEnable_t oldsciss,newsciss; + m_ScissorEnable.Read( &oldsciss, 0 ); + + // remember to restore m_drawingFBO at end of effort + + // setup + // turn off scissor + newsciss.enable = false; + m_ScissorEnable.Write( &newsciss ); + + // select which attachment enum we're going to use for the blit + // default to color0, unless it's a depth or stencil flava + + // for resolve, only handle a modest subset of the possible formats + EGLMFBOAttachment attachIndex = (EGLMFBOAttachment)0; + GLenum attachIndexGL = 0; + GLuint blitMask = 0; + switch( tex->m_layout->m_format->m_glDataFormat ) + { + case GL_BGRA: + case GL_RGB: + case GL_RGBA: + // case GL_ALPHA: + // case GL_LUMINANCE: + // case GL_LUMINANCE_ALPHA: + attachIndex = kAttColor0; + attachIndexGL = GL_COLOR_ATTACHMENT0_EXT; + blitMask = GL_COLOR_BUFFER_BIT; + break; + + // case GL_DEPTH_COMPONENT: + // attachIndex = kAttDepth; + // attachIndexGL = GL_DEPTH_ATTACHMENT_EXT; + // blitMask = GL_DEPTH_BUFFER_BIT; + // break; + + case GL_DEPTH_STENCIL_EXT: + attachIndex = kAttDepthStencil; + attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT_EXT; + blitMask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + break; + + default: + Assert(!"Unsupported format for MSAA resolve" ); + break; + } + + + // set the read fb, attach read RBO at appropriate attach point, set read buffer + BindFBOToCtx( m_blitReadFBO, GL_READ_FRAMEBUFFER_EXT ); + + // going to avoid the TexAttach / TexDetach calls due to potential confusion, implement it directly here + + //----------------------------------------------------------------------------------- + // put tex->m_rboName on the read FB's attachment + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // you have to attach it both places... + // http://www.opengl.org/wiki/GL_EXT_framebuffer_object + + // bind the RBO to the GL_RENDERBUFFER_EXT target - is this extraneous ? + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + // attach the GL_RENDERBUFFER_EXT target to the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName); + + // no need to leave the RBO hanging on + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + else + { + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName ); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, tex->m_rboName); + + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + } + + gGL->glReadBuffer( attachIndexGL ); + + //----------------------------------------------------------------------------------- + // put tex->m_texName on the draw FBO attachment + + // set the write fb and buffer, and attach write tex + BindFBOToCtx( m_blitDrawFBO, GL_DRAW_FRAMEBUFFER_EXT ); + + // regular path - attaching a texture2d + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, tex->m_texName, 0 ); + + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, tex->m_texName, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, attachIndexGL, GL_TEXTURE_2D, tex->m_texName, 0 ); + } + + gGL->glDrawBuffer( attachIndexGL ); + + //----------------------------------------------------------------------------------- + + // blit + gGL->glBlitFramebufferEXT( 0, 0, tex->m_layout->m_key.m_xSize, tex->m_layout->m_key.m_ySize, + 0, 0, tex->m_layout->m_key.m_xSize, tex->m_layout->m_key.m_ySize, + blitMask, GL_NEAREST ); + // or should it be GL_LINEAR? does it matter ? + + //----------------------------------------------------------------------------------- + // cleanup + //----------------------------------------------------------------------------------- + + + // unset the read fb and buffer, detach read RBO + //glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + // detach the GL_RENDERBUFFER_EXT target from the depth and stencil attach points + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + } + else + { + gGL->glFramebufferRenderbufferEXT( GL_READ_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, 0); + } + + //----------------------------------------------------------------------------------- + // unset the write fb and buffer, detach write tex + + + if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT) + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0 ); + } + else + { + gGL->glFramebufferTexture2DEXT( GL_DRAW_FRAMEBUFFER_EXT, attachIndexGL, GL_TEXTURE_2D, 0, 0 ); + } + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // set the read and write buffers back to... what ? does it matter for anything but copies ? don't worry about it + + // restore the scissor state + m_ScissorEnable.Write( &oldsciss ); + + // mark the RBO clean on the resolved tex + tex->ForceRBONonDirty(); + } +} + +void GLMContext::PreloadTex( CGLMTex *tex, bool force ) +{ + // if conditions allow (i.e. a drawing surface is active) + // bind the texture on TMU 15 + // set up a dummy program to sample it but not write (use 'discard') + // draw a teeny little triangle that won't generate a lot of fragments + if (!m_pairCache) + return; + + if (!m_drawingFBO) + return; + + if (tex->m_texPreloaded && !force) // only do one preload unless forced to re-do + { + //printf("\nnot-preloading %s", tex->m_debugLabel ? tex->m_debugLabel : "(unknown)"); + return; + } + + //printf("\npreloading %s", tex->m_debugLabel ? tex->m_debugLabel : "(unknown)"); + + CGLMProgram *vp = m_preloadTexVertexProgram; + CGLMProgram *fp = NULL; + switch(tex->m_layout->m_key.m_texGLTarget) + { + case GL_TEXTURE_2D: fp = m_preload2DTexFragmentProgram; + break; + + case GL_TEXTURE_3D: fp = m_preload3DTexFragmentProgram; + break; + + case GL_TEXTURE_CUBE_MAP: fp = m_preloadCubeTexFragmentProgram; + break; + } + if (!fp) + return; + + CGLMShaderPair *preloadPair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + if (!preloadPair) + return; + + if ( !preloadPair->m_valid ) + { + if ( !preloadPair->ValidateProgramPair() ) + { + return; + } + } + + gGL->glUseProgram( (GLuint)preloadPair->m_program ); + + m_pBoundPair = preloadPair; + m_bDirtyPrograms = true; + + // almost ready to draw... + + //int tmuForPreload = 15; + + // shut down all the generic attribute arrays on the detention level - next real draw will activate them again + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + memset( &m_boundVertexAttribs[0], 0xFF, sizeof( m_boundVertexAttribs ) ); + + // Force the next flush to reset the attributes. + ClearCurAttribs(); + + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + { + gGL->glDisableVertexAttribArray( index ); + } + + // bind texture and sampling params + CGLMTex *pPrevTex = m_samplers[15].m_pBoundTex; + +#ifndef OSX // 10.6 + if ( m_bUseSamplerObjects ) + { + gGL->glBindSampler( 15, 0 ); + } +#endif // !OSX + + BindTexToTMU( tex, 15 ); + + // unbind vertex/index buffers + BindBufferToCtx( kGLMVertexBuffer, NULL ); + BindBufferToCtx( kGLMIndexBuffer, NULL ); + + // draw + static float posns[] = { 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f }; + + static int indices[] = { 0, 1, 2 }; + + + gGL->glEnableVertexAttribArray( 0 ); + + gGL->glVertexAttribPointer( 0, 3, GL_FLOAT, 0, 0, posns ); + + gGL->glDrawRangeElements( GL_TRIANGLES, 0, 3, 3, GL_UNSIGNED_INT, indices); + + gGL->glDisableVertexAttribArray( 0 ); + + SetSamplerDirty( 15 ); + + BindTexToTMU( pPrevTex, 15 ); + + tex->m_texPreloaded = true; +} + + + +CGLMFBO *GLMContext::NewFBO( void ) +{ + GLM_FUNC; + + CGLMFBO *fbo = new CGLMFBO( this ); + + m_fboTable.AddToTail( fbo ); + + return fbo; +} + +void GLMContext::DelFBO( CGLMFBO *fbo ) +{ + GLM_FUNC; + + if (m_drawingFBO == fbo) + { + m_drawingFBO = NULL; //poof! + } + + if (m_boundReadFBO == fbo ) + { + BindFBOToCtx( NULL, GL_READ_FRAMEBUFFER_EXT ); + m_boundReadFBO = NULL; + } + + if (m_boundDrawFBO == fbo ) + { + BindFBOToCtx( NULL, GL_DRAW_FRAMEBUFFER_EXT ); + m_boundDrawFBO = NULL; + } + + int idx = m_fboTable.Find( fbo ); + Assert( idx >= 0 ); + if ( idx >= 0 ) + { + m_fboTable.FastRemove( idx ); + } + + delete fbo; +} + +//=============================================================================== + +CGLMProgram *GLMContext::NewProgram( EGLMProgramType type, char *progString, const char *pShaderName ) +{ + //hushed GLM_FUNC; + + CGLMProgram *prog = new CGLMProgram( this, type ); + + prog->SetProgramText( progString ); + prog->SetShaderName( pShaderName ); + prog->CompileActiveSources(); + + return prog; +} + +void GLMContext::DelProgram( CGLMProgram *pProg ) +{ + GLM_FUNC; + + if ( m_drawingProgram[ pProg->m_type ] == pProg ) + { + SetProgram( pProg->m_type, ( pProg->m_type == kGLMFragmentProgram ) ? m_pNullFragmentProgram : NULL ); + } + + // make sure to eliminate any cached pairs using this shader + bool purgeResult = m_pairCache->PurgePairsWithShader( pProg ); + (void)purgeResult; + Assert( !purgeResult ); // very unlikely to trigger + + NullProgram(); + + delete pProg; +} + +void GLMContext::NullProgram( void ) +{ + gGL->glUseProgram( 0 ); + m_pBoundPair = NULL; + m_bDirtyPrograms = true; +} + +void GLMContext::SetDrawingLang( EGLMProgramLang lang, bool immediate ) +{ + if ( !m_caps.m_hasDualShaders ) return; // ignore attempts to change language when -glmdualshaders is not engaged + + m_drawingLangAtFrameStart = lang; + if (immediate) + { + NullProgram(); + + m_drawingLang = m_drawingLangAtFrameStart; + } +} + +void GLMContext::LinkShaderPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + if ( (m_pairCache) && (m_drawingLang==kGLMGLSL) && (vp) && (fp) ) + { + CGLMShaderPair *pair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + (void)pair; + + Assert( pair != NULL ); + + NullProgram(); // clear out any binds that were done - next draw will set it right + } +} + +void GLMContext::ValidateShaderPair( CGLMProgram *vp, CGLMProgram *fp ) +{ + if ((m_pairCache) && (m_drawingLang == kGLMGLSL) && (vp) && (fp)) + { + CGLMShaderPair *pair = m_pairCache->SelectShaderPair( vp, fp, 0 ); + Assert( pair != NULL ); + pair->ValidateProgramPair(); + + NullProgram(); // clear out any binds that were done - next draw will set it right + } +} + +void GLMContext::ClearShaderPairCache( void ) +{ + if (m_pairCache) + { + NullProgram(); + m_pairCache->Purge(); // bye bye all linked pairs + NullProgram(); + } +} + +void GLMContext::QueryShaderPair( int index, GLMShaderPairInfo *infoOut ) +{ + if (m_pairCache) + { + m_pairCache->QueryShaderPair( index, infoOut ); + } + else + { + memset( infoOut, 0, sizeof( *infoOut ) ); + infoOut->m_status = -1; + } +} + +CGLMBuffer *GLMContext::NewBuffer( EGLMBufferType type, uint size, uint options ) +{ + //hushed GLM_FUNC; + + CGLMBuffer *prog = new CGLMBuffer( this, type, size, options ); + + return prog; +} + +void GLMContext::DelBuffer( CGLMBuffer *buff ) +{ + GLM_FUNC; + + for( int index = 0; index < kGLMVertexAttributeIndexMax; index++ ) + { + if ( m_drawVertexSetup.m_attrs[index].m_pBuffer == buff ) + { + // just clear the enable mask - this will force all the attrs to get re-sent on next sync + m_drawVertexSetup.m_attrMask = 0; + } + } + + BindGLBufferToCtx( buff->m_buffGLTarget, NULL, false ); + + delete buff; +} + +GLMVertexSetup g_blank_setup; + +void GLMContext::Clear( bool color, unsigned long colorValue, bool depth, float depthValue, bool stencil, unsigned int stencilValue, GLScissorBox_t *box ) +{ + GLM_FUNC; + + ++m_nBatchCounter; + +#if GLMDEBUG + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eClear; + + do + { +#endif + uint mask = 0; + + GLClearColor_t clearcol; + GLClearDepth_t cleardep = { depthValue }; + GLClearStencil_t clearsten = { (GLint)stencilValue }; + + // depth write mask must be saved&restored + GLDepthMask_t olddepthmask; + GLDepthMask_t newdepthmask = { true }; + + // stencil write mask must be saved and restored + GLStencilWriteMask_t oldstenmask; + GLStencilWriteMask_t newstenmask = { (GLint)0xFFFFFFFF }; + + GLColorMaskSingle_t oldcolormask; + GLColorMaskSingle_t newcolormask = { -1,-1,-1,-1 }; // D3D clears do not honor color mask, so force it + + if (color) + { + // #define D3DCOLOR_ARGB(a,r,g,b) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) + + clearcol.r = ((colorValue >> 16) & 0xFF) / 255.0f; //R + clearcol.g = ((colorValue >> 8) & 0xFF) / 255.0f; //G + clearcol.b = ((colorValue ) & 0xFF) / 255.0f; //B + clearcol.a = ((colorValue >> 24) & 0xFF) / 255.0f; //A + + m_ClearColor.Write( &clearcol ); // no check, no wait + mask |= GL_COLOR_BUFFER_BIT; + + // save and set color mask + m_ColorMaskSingle.Read( &oldcolormask, 0 ); + m_ColorMaskSingle.Write( &newcolormask ); + } + + if (depth) + { + // get old depth write mask + m_DepthMask.Read( &olddepthmask, 0 ); + m_DepthMask.Write( &newdepthmask ); + m_ClearDepth.Write( &cleardep ); // no check, no wait + mask |= GL_DEPTH_BUFFER_BIT; + } + + if (stencil) + { + m_ClearStencil.Write( &clearsten ); // no check, no wait + mask |= GL_STENCIL_BUFFER_BIT; + + // save and set sten mask + m_StencilWriteMask.Read( &oldstenmask, 0 ); + m_StencilWriteMask.Write( &newstenmask ); + } + + bool subrect = (box != NULL); + GLScissorEnable_t scissorEnableSave; + GLScissorEnable_t scissorEnableNew = { true }; + + GLScissorBox_t scissorBoxSave; + GLScissorBox_t scissorBoxNew; + + if (subrect) + { + // save current scissorbox and enable + m_ScissorEnable.Read( &scissorEnableSave, 0 ); + m_ScissorBox.Read( &scissorBoxSave, 0 ); + + if(0) + { + // calc new scissorbox as intersection against *box + + // max of the mins + scissorBoxNew.x = MAX(scissorBoxSave.x, box->x); + scissorBoxNew.y = MAX(scissorBoxSave.y, box->y); + + // min of the maxes + scissorBoxNew.width = ( MIN(scissorBoxSave.x+scissorBoxSave.width, box->x+box->width)) - scissorBoxNew.x; + + // height is just min of the max y's, minus the new base Y + scissorBoxNew.height = ( MIN(scissorBoxSave.y+scissorBoxSave.height, box->y+box->height)) - scissorBoxNew.y; + } + else + { + // ignore old scissor box completely. + scissorBoxNew = *box; + } + // set new box and enable + m_ScissorEnable.Write( &scissorEnableNew ); + m_ScissorBox.Write( &scissorBoxNew ); + } + + gGL->glClear( mask ); + + if (subrect) + { + // put old scissor box and enable back + m_ScissorEnable.Write( &scissorEnableSave ); + m_ScissorBox.Write( &scissorBoxSave ); + } + + if (depth) + { + // put old depth write mask + m_DepthMask.Write( &olddepthmask ); + } + + if (color) + { + // put old color write mask + m_ColorMaskSingle.Write( &oldcolormask ); + } + + if (stencil) + { + // put old sten mask + m_StencilWriteMask.Write( &oldstenmask ); + } + +#if GLMDEBUG + DebugHook( &info ); + } while (info.m_loop); +#endif +} + + +// stolen from glmgrbasics.cpp +extern "C" uint GetCurrentKeyModifiers( void ); +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +static ConVar gl_flushpaircache ("gl_flushpaircache", "0"); +static ConVar gl_paircachestats ("gl_paircachestats", "0"); +static ConVar gl_mtglflush_at_tof ("gl_mtglflush_at_tof", "0"); +static ConVar gl_texlayoutstats ("gl_texlayoutstats", "0" ); + +void GLMContext::BeginFrame( void ) +{ + GLM_FUNC; + + m_debugFrameIndex++; + + // check for lang change at TOF + if (m_caps.m_hasDualShaders) + { + if (m_drawingLang != m_drawingLangAtFrameStart) + { + // language change. unbind everything.. + NullProgram(); + + m_drawingLang = m_drawingLangAtFrameStart; + } + } + + // scrub some critical shock absorbers + for( int i=0; i< 16; i++) + { + gGL->glDisableVertexAttribArray( i ); // enable GLSL attribute- this is just client state - will be turned back off + } + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 0; + + //FIXME should we also zap the m_lastKnownAttribs array ? (worst case it just sets them all again on first batch) + + BindBufferToCtx( kGLMVertexBuffer, NULL, true ); + BindBufferToCtx( kGLMIndexBuffer, NULL, true ); + + if (gl_flushpaircache.GetInt()) + { + // do the flush and then set back to zero + ClearShaderPairCache(); + + printf("\n\n##### shader pair cache cleared\n\n"); + gl_flushpaircache.SetValue( 0 ); + } + + if (gl_paircachestats.GetInt()) + { + // do the flush and then set back to zero + m_pairCache->DumpStats(); + + gl_paircachestats.SetValue( 0 ); + } + + if (gl_texlayoutstats.GetInt()) + { + m_texLayoutTable->DumpStats(); + + gl_texlayoutstats.SetValue( 0 ); + } + + if (gl_mtglflush_at_tof.GetInt()) + { + gGL->glFlush(); // TOF flush - skip this if benchmarking, enable it if human playing (smoothness) + } + +#if GLMDEBUG + // init debug hook information + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eBeginFrame; + + do + { + DebugHook( &info ); + } while (info.m_loop); + +#endif + +} + +void GLMContext::EndFrame( void ) +{ + GLM_FUNC; + +#if GLMDEBUG + // init debug hook information + GLMDebugHookInfo info; + memset( &info, 0, sizeof(info) ); + info.m_caller = eEndFrame; + + do + { + DebugHook( &info ); + } while (info.m_loop); +#endif +} + +//=============================================================================== + +CGLMQuery *GLMContext::NewQuery( GLMQueryParams *params ) +{ + CGLMQuery *query = new CGLMQuery( this, params ); + + return query; +} + +void GLMContext::DelQuery( CGLMQuery *query ) +{ + // may want to do some finish/ + delete query; +} + +static ConVar mat_vsync( "mat_vsync", "0", 0, "Force sync to vertical retrace", true, 0.0, true, 1.0 ); + +//=============================================================================== + +ConVar glm_nullrefresh_capslock( "glm_nullrefresh_capslock", "0" ); +ConVar glm_literefresh_capslock( "glm_literefresh_capslock", "0" ); + +extern ConVar gl_blitmode; + +void GLMContext::Present( CGLMTex *tex ) +{ + GLM_FUNC; + + { +#if GL_TELEMETRY_GPU_ZONES + CScopedGLMPIXEvent glmPIXEvent( "GLMContext::Present" ); + g_TelemetryGPUStats.m_nTotalPresent++; +#endif + + ProcessTextureDeletes(); + +#ifdef HAVE_GL_ARB_SYNC + + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].InsertFence(); + + m_nCurPinnedMemoryBuffer = ( m_nCurPinnedMemoryBuffer + 1 ) % cNumPinnedMemoryBuffers; + + m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].BlockUntilNotBusy(); + + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } + + if ( gGL->m_bHave_GL_ARB_buffer_storage ) + { + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + m_persistentBuffer[m_nCurPersistentBuffer][lpType].InsertFence(); + } + + m_nCurPersistentBuffer = ( m_nCurPersistentBuffer + 1 ) % cNumPersistentBuffers; + + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + m_persistentBuffer[m_nCurPersistentBuffer][lpType].BlockUntilNotBusy(); + } + } + +#endif // HAVE_GL_ARB_SYNC + + bool newRefreshMode = false; + // two ways to go: + + // old school, do the resolve, had the tex down to cocoamgr to actually blit. + // that way is required if you are not in one-context mode (10.5.8) + + if ( (gl_blitmode.GetInt() != 0) ) + { + newRefreshMode = true; + } + + // this is the path whether full screen or windowed... we always blit. + CShowPixelsParams showparams; + memset( &showparams, 0, sizeof(showparams) ); + + showparams.m_srcTexName = tex->m_texName; + showparams.m_width = tex->m_layout->m_key.m_xSize; + showparams.m_height = tex->m_layout->m_key.m_ySize; + showparams.m_vsyncEnable = m_displayParams.m_vsyncEnable = mat_vsync.GetBool(); + showparams.m_fsEnable = m_displayParams.m_fsEnable; + showparams.m_useBlit = m_caps.m_hasFramebufferBlit; + + // we call showpixels once with the "only sync view" arg set, so we know what the latest surface size is, before trying to do our own blit ! + showparams.m_onlySyncView = true; + ShowPixels(&showparams); // doesn't actually show anything, just syncs window/fs state (would make a useful separate call) + showparams.m_onlySyncView = false; + + bool refresh = true; + #ifdef OSX + if ( (glm_nullrefresh_capslock.GetInt()) && (GetCurrentKeyModifiers() & EalphaLock) ) + { + refresh = false; + } + #endif + static int counter; + counter ++; + + #ifdef OSX + if ( (glm_literefresh_capslock.GetInt()) && (GetCurrentKeyModifiers() & EalphaLock) && (counter & 127) ) + { + // just show every 128th frame + refresh = false; + } + #endif + + if (refresh) + { + if (newRefreshMode) + { + // blit to GL_BACK done here, not in CocoaMgr, this lets us do resolve directly if conditions are right + + GLMRect srcRect, dstRect; + + uint dstWidth,dstHeight; + DisplayedSize( dstWidth,dstHeight ); + + srcRect.xmin = 0; + srcRect.ymin = 0; + srcRect.xmax = showparams.m_width; + srcRect.ymax = showparams.m_height; + + dstRect.xmin = 0; + dstRect.ymin = 0; + dstRect.xmax = dstWidth; + dstRect.ymax = dstHeight; + + // do not ask for LINEAR if blit is unscaled + // NULL means targeting GL_BACK. Blit2 will break it down into two steps if needed, and will handle resolve, scale, flip. + bool blitScales = (showparams.m_width != static_cast<int>(dstWidth)) || (showparams.m_height != static_cast<int>(dstHeight)); + Blit2( tex, &srcRect, 0,0, + NULL, &dstRect, 0,0, + blitScales ? GL_LINEAR : GL_NEAREST ); + + // we set showparams.m_noBlit, and just let CocoaMgr handle the swap (flushbuffer / page flip) + showparams.m_noBlit = true; + + BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + } + else + { + ResolveTex( tex, true ); // dxabstract used to do this unconditionally.we still do if new refresh mode doesn't engage. + + BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // showparams.m_noBlit is left set to 0. CocoaMgr does the blit. + } + + ShowPixels(&showparams); + } + + // put the original FB back in place (both read and draw) + // this bind will hit both read and draw bindings + BindFBOToCtx( m_drawingFBO, GL_FRAMEBUFFER_EXT ); + + // put em back !! + m_ScissorEnable.Flush(); + m_ScissorBox.Flush(); + m_ViewportBox.Flush(); + } + + m_nCurFrame++; + +#if GL_BATCH_PERF_ANALYSIS + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_EXCLAMATION, "VS Uniform Calls: %u, VS Uniforms: %u|VS Uniform Bone Calls: %u, VS Bone Uniforms: %u|PS Uniform Calls: %u, PS Uniforms: %u", m_nTotalVSUniformCalls, m_nTotalVSUniformsSet, m_nTotalVSUniformBoneCalls, m_nTotalVSUniformsBoneSet, m_nTotalPSUniformCalls, m_nTotalPSUniformsSet ); + m_nTotalVSUniformCalls = 0, m_nTotalVSUniformBoneCalls = 0, m_nTotalVSUniformsSet = 0, m_nTotalVSUniformsBoneSet = 0, m_nTotalPSUniformCalls = 0, m_nTotalPSUniformsSet = 0; +#endif + +#ifndef OSX + GLMGPUTimestampManagerTick(); +#endif +} + +//=============================================================================== +// GLMContext protected methods + +// a naive implementation of this would just clear-drawable on the context at entry, +// and then capture and set fullscreen if requested. +// however that would glitch thescreen every time the user changed resolution while staying in full screen. +// but in windowed mode there's really not much to do in here. Yeah, this routine centers around obtaining +// drawables for fullscreen mode, and/or dropping those drawables if we're going back to windowed. + +// um, are we expected to re-make the standard surfaces (color, depthstencil) if the res changes? is that now this routine's job ? + +// so, kick it off with an assessment of whather we were FS previously or not. +// if there was no prior display params latched, then it wasn't. + +// changes in here take place immediately. If you want to defer display changes then that's going to be a different method. +// common assumption is that there will be two places that call this: context create and the implementation of the DX9 Reset method. +// in either case the client code is aware of what it signed up for. + +bool GLMContext::SetDisplayParams( GLMDisplayParams *params ) +{ + m_displayParams = *params; // latch em + m_displayParamsValid = true; + + return true; +} + + +ConVar gl_can_query_fast("gl_can_query_fast", "0"); + +static uint gPersistentBufferSize[kGLMNumBufferTypes] = +{ + 2 * 1024 * 1024, // kGLMVertexBuffer + 1 * 1024 * 1024, // kGLMIndexBuffer + 0, // kGLMUniformBuffer + 0, // kGLMPixelBuffer +}; + +GLMContext::GLMContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params ) +{ +// m_bUseSamplerObjects = true; +// +// // On most AMD drivers (like the current latest, 12.10 Windows), the PCF depth comparison mode doesn't work on sampler objects, so just punt them. +// if ( gGL->m_nDriverProvider == cGLDriverProviderAMD ) +// { +// m_bUseSamplerObjects = false; +// } + +// if ( CommandLine()->CheckParm( "-gl_disablesamplerobjects" ) ) +// { + // Disable sampler object usage for now since ScaleForm isn't aware of them + // and doesn't know how to push/pop their binding state. It seems we don't + // really use them in this codebase anyhow, except to preload textures. + m_bUseSamplerObjects = false; + if ( CommandLine()->CheckParm( "-gl_enablesamplerobjects" ) ) + m_bUseSamplerObjects = true; + + // Try to get some more free memory by relying on driver host copies instead of ours. + // In some cases the driver will be able to discard their own host copy and rely on GPU + // memory, reducing memory usage. + // Sadly, we have to enable tex client storage for srgb decoding. This should only happen + // on Macs w/ OSX 10.6. + m_bTexClientStorage = !gGL->m_bHave_GL_EXT_texture_sRGB_decode; + if ( CommandLine()->CheckParm( "-gl_texclientstorage" ) ) + m_bTexClientStorage = true; + + GLMDebugPrintf( "GL sampler object usage: %s\n", m_bUseSamplerObjects ? "ENABLED" : "DISABLED" ); + + m_nCurOwnerThreadId = ThreadGetCurrentId(); + m_nThreadOwnershipReleaseCounter = 0; + + m_pDevice = pDevice; + m_nCurFrame = 0; + m_nBatchCounter = 0; + + ClearCurAttribs(); + +#ifndef OSX + m_nCurPinnedMemoryBuffer = 0; + if ( gGL->m_bHave_GL_AMD_pinned_memory ) + { + for ( uint t = 0; t < cNumPinnedMemoryBuffers; t++ ) + { + m_PinnedMemoryBuffers[t].Init( GLMGR_PINNED_MEMORY_BUFFER_SIZE ); + } + + gGL->glBindBufferARB( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer].GetHandle() ); + } +#endif // OSX + + m_nCurPersistentBuffer = 0; + if ( gGL->m_bHave_GL_ARB_buffer_storage ) + { + for ( uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType ) + { + for ( uint lpNum = 0; lpNum < cNumPersistentBuffers; ++lpNum ) + { + m_persistentBuffer[lpNum][lpType].Init( (EGLMBufferType)lpType, gPersistentBufferSize[lpType] ); + } + } + } + + m_bUseBoneUniformBuffers = true; + if (CommandLine()->CheckParm("-disableboneuniformbuffers")) + { + m_bUseBoneUniformBuffers = false; + } + + m_nMaxUsedVertexProgramConstantsHint = 256; + + // flag our copy of display params as blank + m_displayParamsValid = false; + + // peek at any CLI options + m_slowAssertEnable = CommandLine()->FindParm("-glmassertslow") != 0; + m_slowSpewEnable = CommandLine()->FindParm("-glmspewslow") != 0; + m_checkglErrorsAfterEveryBatch = CommandLine()->FindParm("-glcheckerrors") != 0; + m_slowCheckEnable = m_slowAssertEnable || m_slowSpewEnable || m_checkglErrorsAfterEveryBatch; + + m_drawingLangAtFrameStart = m_drawingLang = kGLMGLSL; // default to GLSL + + // this affects FlushDrawStates which will route program bindings, uniform delivery, sampler setup, and enables accordingly. + + if ( CommandLine()->FindParm("-glslmode") ) + { + m_drawingLangAtFrameStart = m_drawingLang = kGLMGLSL; + } + if ( CommandLine()->FindParm("-arbmode") && !CommandLine()->FindParm("-glslcontrolflow") ) + { + m_drawingLangAtFrameStart = m_drawingLang = kGLMARB; + } + + // proceed with rest of init + + m_dwRenderThreadId = 0; + m_bIsThreading = false; + + m_nsctx = NULL; + m_ctx = NULL; + + int *selAttribs = NULL; + uint selWords = 0; + + memset( &m_caps, 0, sizeof( m_caps ) ); + GetDesiredPixelFormatAttribsAndRendererInfo( (uint**)&selAttribs, &selWords, &m_caps ); + uint selBytes = selWords * sizeof( uint ); selBytes; + +#if defined( USE_SDL ) + m_ctx = (SDL_GLContext)GetGLContextForWindow( params ? (void*)params->m_focusWindow : NULL ); + MakeCurrent( true ); +#else +#error +#endif + IncrementWindowRefCount(); + + // If we're using GL_ARB_debug_output, go ahead and setup the callback here. + if ( gGL->m_bHave_GL_ARB_debug_output && CommandLine()->FindParm( "-gl_debug" ) ) + { +#if GLMDEBUG + // Turning this on is a perf loss, but it ensures that you can (at least) swap to the other + // threads to see what call is currently being made. + // Note that if the driver is in multithreaded mode, you can put it back into singlethreaded mode + // and get a real stack for the offending gl call. + gGL->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + +#ifdef WIN32 + // This happens early enough during init that DevMsg() does nothing. + OutputDebugStringA( "GLMContext::GLMContext: GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB enabled!\n" ); +#else + printf( "GLMContext::GLMContext: GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB enabled!\n" ); +#endif + +#endif + // This should be there if we get in here--make sure. + Assert(gGL->glDebugMessageControlARB); + gGL->glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, (const GLuint *)NULL, GL_TRUE); + + // Gonna filter these out, they're "chatty". + gGL->glDebugMessageControlARB(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_LOW_ARB, 0, (const GLuint *)NULL, GL_FALSE); + gGL->glDebugMessageCallbackARB(GL_Debug_Output_Callback, (void*)NULL); + + GLMDebugPrintf( "GLMContext::GLMContext: Debug output (gl_arb_debug_output) enabled!\n" ); + } + + + if (CommandLine()->FindParm("-glmspewcaps")) + { + DumpCaps(); + } + + SetDisplayParams( params ); + + m_texLayoutTable = new CGLMTexLayoutTable; + +#ifndef OSX + if ( m_bUseSamplerObjects ) + { + memset( m_samplerObjectHash, 0, sizeof( m_samplerObjectHash ) ); + m_nSamplerObjectHashNumEntries = 0; + + for ( uint i = 0; i < cSamplerObjectHashSize; ++i ) + { + gGL->glGenSamplers( 1, &m_samplerObjectHash[i].m_samplerObject ); + } + } +#endif // !OSX + + memset( m_samplers, 0, sizeof( m_samplers ) ); + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + GLMTexSamplingParams ¶ms = m_samplers[i].m_samp; + params.m_packed.m_addressU = D3DTADDRESS_WRAP; + params.m_packed.m_addressV = D3DTADDRESS_WRAP; + params.m_packed.m_addressW = D3DTADDRESS_WRAP; + params.m_packed.m_minFilter = D3DTEXF_POINT; + params.m_packed.m_magFilter = D3DTEXF_POINT; + params.m_packed.m_mipFilter = D3DTEXF_NONE; + params.m_packed.m_maxAniso = 1; + params.m_packed.m_isValid = true; + params.m_packed.m_compareMode = 0; + } + + MarkAllSamplersDirty(); + + m_activeTexture = -1; + + m_texLocks.EnsureCapacity( 16 ); // should be sufficient + + // FIXME need a texture tracking table so we can reliably delete CGLMTex objects at context teardown + + m_boundReadFBO = NULL; + m_boundDrawFBO = NULL; + m_drawingFBO = NULL; + + memset( m_drawingProgram, 0, sizeof( m_drawingProgram ) ); + m_bDirtyPrograms = true; + memset( m_programParamsF , 0, sizeof( m_programParamsF ) ); + memset( m_programParamsB , 0, sizeof( m_programParamsB ) ); + memset( m_programParamsI , 0, sizeof( m_programParamsI ) ); + + for (uint i = 0; i < ARRAYSIZE(m_programParamsF); i++) + { + m_programParamsF[i].m_firstDirtySlotNonBone = 256; + m_programParamsF[i].m_dirtySlotHighWaterNonBone = 0; + + m_programParamsF[i].m_dirtySlotHighWaterBone = 0; + } + + m_paramWriteMode = eParamWriteDirtySlotRange; // default to fastest mode + + if (CommandLine()->FindParm("-glmwriteallslots")) m_paramWriteMode = eParamWriteAllSlots; + if (CommandLine()->FindParm("-glmwriteshaderslots")) m_paramWriteMode = eParamWriteShaderSlots; + if (CommandLine()->FindParm("-glmwriteshaderslotsoptional")) m_paramWriteMode = eParamWriteShaderSlotsOptional; + if (CommandLine()->FindParm("-glmwritedirtyslotrange")) m_paramWriteMode = eParamWriteDirtySlotRange; + + m_attribWriteMode = eAttribWriteDirty; + + if (CommandLine()->FindParm("-glmwriteallattribs")) m_attribWriteMode = eAttribWriteAll; + if (CommandLine()->FindParm("-glmwritedirtyattribs")) m_attribWriteMode = eAttribWriteDirty; + + m_pairCache = new CGLMShaderPairCache( this ); + m_pBoundPair = NULL; + + m_fragDataMask = 0; + + memset( m_nBoundGLBuffer, 0xFF, sizeof( m_nBoundGLBuffer ) ); + + memset( m_boundVertexAttribs, 0xFF, sizeof(m_boundVertexAttribs) ); + m_lastKnownVertexAttribMask = 0; + m_nNumSetVertexAttributes = 16; + + // make a null program for use when client asks for NULL FP + m_pNullFragmentProgram = NewProgram(kGLMFragmentProgram, g_nullFragmentProgramText, "null" ); + SetProgram( kGLMFragmentProgram, m_pNullFragmentProgram ); + + // make dummy programs for doing texture preload via dummy draw + m_preloadTexVertexProgram = NewProgram(kGLMVertexProgram, g_preloadTexVertexProgramText, "preloadTex" ); + m_preload2DTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preload2DTexFragmentProgramText, "preload2DTex" ); + m_preload3DTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preload3DTexFragmentProgramText, "preload3DTex" ); + m_preloadCubeTexFragmentProgram = NewProgram(kGLMFragmentProgram, g_preloadCubeTexFragmentProgramText, "preloadCube" ); + + //memset( &m_drawVertexSetup, 0, sizeof(m_drawVertexSetup) ); + SetVertexAttributes( NULL ); // will set up all the entries in m_drawVertexSetup + + m_debugFontTex = NULL; + + // debug state + m_debugFrameIndex = -1; + +#if GLMDEBUG + // ####################################################################################### + + // DebugHook state - we could set these to more interesting values in response to a CLI arg like "startpaused" or something if desired + //m_paused = false; + m_holdFrameBegin = -1; + m_holdFrameEnd = -1; + m_holdBatch = m_holdBatchFrame = -1; + + m_debugDelayEnable = false; + m_debugDelay = 1<<19; // ~0.5 sec delay + + m_autoClearColor = m_autoClearDepth = m_autoClearStencil = false; + m_autoClearColorValues[0] = 0.0; //red + m_autoClearColorValues[1] = 1.0; //green + m_autoClearColorValues[2] = 0.0; //blue + m_autoClearColorValues[3] = 1.0; //alpha + + m_selKnobIndex = 0; + m_selKnobMinValue = -10.0f; + + m_selKnobMaxValue = 10.0f; + m_selKnobIncrement = 1/256.0f; + + // ####################################################################################### +#endif + + // make two scratch FBO's for blit purposes + m_blitReadFBO = NewFBO(); + m_blitDrawFBO = NewFBO(); + + for( int i=0; i<kGLMScratchFBOCount; i++) + { + m_scratchFBO[i] = NewFBO(); + } + + // Create a PBO that we can use to fill textures with bogus data asyncronously. + m_nBoundGLBuffer[ kGLMPixelBuffer ] = 0; + + gGL->glGenBuffersARB( 1, &m_destroyPBO ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_destroyPBO ); + gGL->glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, sizeof( g_garbageTextureBits ), g_garbageTextureBits, GL_STATIC_DRAW ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_nBoundGLBuffer[ kGLMPixelBuffer ] ); + + // Create a bunch of texture names for us to use forever and ever ramen. + FillTexCache( false, kGLMInitialTexCount ); + +#ifdef OSX + bool new_mtgl = m_caps.m_hasPerfPackage1; // i.e. 10.6.4 plus new driver + + if ( CommandLine()->FindParm("-glmenablemtgl2") ) + { + new_mtgl = true; + } + + if ( CommandLine()->FindParm("-glmdisablemtgl2") ) + { + new_mtgl = false; + } + + bool mtgl_on = params->m_mtgl; + if (CommandLine()->FindParm("-glmenablemtgl")) + { + mtgl_on = true; + } + + if (CommandLine()->FindParm("-glmdisablemtgl")) + { + mtgl_on = false; + } + + CGLError result = (CGLError)0; + if (mtgl_on) + { + bool ready = false; + CGLContextObj context = GetCGLContextFromNSGL(m_ctx); + if (new_mtgl) + { + // afterburner + CGLContextEnable kCGLCPGCDMPEngine = ((CGLContextEnable)1314); + result = CGLEnable( context, kCGLCPGCDMPEngine ); + if (!result) + { + ready = true; // succeeded - no need to try non-MTGL + printf("\nMTGL detected.\n"); + } + else + { + printf("\nMTGL *not* detected, falling back.\n"); + } + } + + if (!ready) + { + // try old MTGL + result = CGLEnable( context, kCGLCEMPEngine ); + if (!result) + { + printf("\nMTGL has been detected.\n"); + ready = true; // succeeded - no need to try non-MTGL + } + } + } + + if ( m_caps.m_badDriver108Intel ) + { + // this way we have something to look for in terminal spew if users report issues related to this in the future. + printf( "\nEnabling GLSL compiler `malloc' workaround.\n" ); + if ( !IntelGLMallocWorkaround::Get()->Enable() ) + { + Warning( "Unable to enable OSX 10.8 / Intel HD4000 workaround, there might be crashes.\n" ); + } + } + +#endif + // also, set the remote convar "gl_can_query_fast" to 1 if perf package present, else 0. + gl_can_query_fast.SetValue( m_caps.m_hasPerfPackage1?1:0 ); + +#if GL_BATCH_PERF_ANALYSIS + m_nTotalVSUniformCalls = 0; + m_nTotalVSUniformBoneCalls = 0; + m_nTotalVSUniformsSet = 0; + m_nTotalVSUniformsBoneSet = 0; + m_nTotalPSUniformCalls = 0; + m_nTotalPSUniformsSet = 0; +#endif + + // See g_D3DRS_INFO_packed in dxabstract.cpp; dithering is a non-managed + // piece of state that we consider off by default. However it is actually + // enabled by default in the GL spec, so account for that here. + // See: https://bugs.freedesktop.org/show_bug.cgi?id=74700 + gGL->glDisable( GL_DITHER ); +} + +void GLMContext::Reset() +{ +} + +GLMContext::~GLMContext () +{ +#ifndef OSX + GLMGPUTimestampManagerDeinit(); + + for ( uint t = 0; t < cNumPinnedMemoryBuffers; t++ ) + { + m_PinnedMemoryBuffers[t].Deinit(); + } + + if (gGL->m_bHave_GL_ARB_buffer_storage) + { + for (uint lpType = 0; lpType < kGLMNumBufferTypes; ++lpType) + { + for (uint lpNum = 0; lpNum < cNumPersistentBuffers; ++lpNum) + { + m_persistentBuffer[lpNum][lpType].Deinit(); + } + } + } + + if ( m_bUseSamplerObjects ) + { + for( int i=0; i< GLM_SAMPLER_COUNT; i++) + { + gGL->glBindSampler( i, 0 ); + } + + for( int i=0; i< cSamplerObjectHashSize; i++) + { + gGL->glDeleteSamplers( 1, &m_samplerObjectHash[i].m_samplerObject ); + m_samplerObjectHash[i].m_samplerObject = 0; + } + } +#endif // !OSX + + if (m_debugFontTex) + { + DelTex( m_debugFontTex ); + m_debugFontTex = NULL; + } + + ProcessTextureDeletes(); + + if ( m_pNullFragmentProgram ) + { + DelProgram( m_pNullFragmentProgram ); + m_pNullFragmentProgram = NULL; + } + + // walk m_fboTable and free them up.. + FOR_EACH_VEC( m_fboTable, i ) + { + CGLMFBO *fbo = m_fboTable[i]; + DelFBO( fbo ); + } + m_fboTable.SetSize( 0 ); + + if (m_pairCache) + { + delete m_pairCache; + m_pairCache = NULL; + } + + // we need a m_texTable I think.. + + // m_texLayoutTable can be scrubbed once we know that all the tex are freed + + gGL->glDeleteBuffersARB( 1, &m_destroyPBO ); + + PurgeTexCache(); + + DecrementWindowRefCount(); +} + +// This method must call SelectTMU()/glActiveTexture() (it's expected as a side effect). +// This method is no longer called from any performance sensitive code paths. +void GLMContext::BindTexToTMU( CGLMTex *pTex, int tmu ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + + GLMPRINTF(("--- GLMContext::BindTexToTMU tex %p GL name %d -> TMU %d ", pTex, pTex ? pTex->m_texName : -1, tmu )); + + CheckCurrent(); + + SelectTMU( tmu ); + + if ( !pTex ) + { + gGL->glBindTexture( GL_TEXTURE_1D, 0 ); + gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + gGL->glBindTexture( GL_TEXTURE_3D, 0 ); + gGL->glBindTexture( GL_TEXTURE_CUBE_MAP, 0 ); + } + else + { + const GLenum texGLTarget = pTex->m_texGLTarget; + if ( texGLTarget != GL_TEXTURE_1D ) gGL->glBindTexture( GL_TEXTURE_1D, 0 ); + if ( texGLTarget != GL_TEXTURE_2D ) gGL->glBindTexture( GL_TEXTURE_2D, 0 ); + if ( texGLTarget != GL_TEXTURE_3D ) gGL->glBindTexture( GL_TEXTURE_3D, 0 ); + if ( texGLTarget != GL_TEXTURE_CUBE_MAP ) gGL->glBindTexture( GL_TEXTURE_CUBE_MAP, 0 ); + gGL->glBindTexture( texGLTarget, pTex->m_texName ); + } + + m_samplers[tmu].m_pBoundTex = pTex; +} + +void GLMContext::BindFBOToCtx( CGLMFBO *fbo, GLenum bindPoint ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + GLMPRINTF(( "--- GLMContext::BindFBOToCtx fbo %p, GL name %d", fbo, (fbo) ? fbo->m_name : -1 )); + + CheckCurrent(); + + if ( bindPoint == GL_FRAMEBUFFER_EXT ) + { + gGL->glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo ? fbo->m_name : 0 ); + m_boundReadFBO = fbo; + m_boundDrawFBO = fbo; + return; + } + + bool targetRead = (bindPoint==GL_READ_FRAMEBUFFER_EXT); + bool targetDraw = (bindPoint==GL_DRAW_FRAMEBUFFER_EXT); + + if (targetRead) + { + if (fbo) // you can pass NULL to go back to no-FBO + { + gGL->glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, fbo->m_name ); + + m_boundReadFBO = fbo; + //dontcare fbo->m_bound = true; + } + else + { + gGL->glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, 0 ); + + m_boundReadFBO = NULL; + } + } + + if (targetDraw) + { + if (fbo) // you can pass NULL to go back to no-FBO + { + gGL->glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, fbo->m_name ); + + m_boundDrawFBO = fbo; + //dontcare fbo->m_bound = true; + } + else + { + gGL->glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0 ); + + m_boundDrawFBO = NULL; + } + } +} + +void GLMContext::BindBufferToCtx( EGLMBufferType type, CGLMBuffer *pBuff, bool bForce ) +{ +#if GLMDEBUG + GLM_FUNC; +#endif + GLMPRINTF(( "--- GLMContext::BindBufferToCtx buff %p, GL name %d", pBuff, (pBuff) ? pBuff->m_nHandle : -1 )); + + CheckCurrent(); + + GLuint nGLName = pBuff ? pBuff->GetHandle() : 0; + if ( !bForce ) + { + if ( m_nBoundGLBuffer[type] == nGLName ) + return; + } + + GLenum target = 0; + switch( type ) + { + case kGLMVertexBuffer: target = GL_ARRAY_BUFFER_ARB; break; + case kGLMIndexBuffer: target = GL_ELEMENT_ARRAY_BUFFER_ARB; break; + case kGLMUniformBuffer: target = GL_UNIFORM_BUFFER_EXT; break; + case kGLMPixelBuffer: target = GL_PIXEL_UNPACK_BUFFER_ARB; break; + default: Assert(!"Unknown buffer type" ); + } + + Assert( !pBuff || ( pBuff->m_buffGLTarget == target ) ); + + m_nBoundGLBuffer[type] = nGLName; + gGL->glBindBufferARB( target, nGLName ); +} + + +GLuint GLMContext::CreateTex( GLenum texBind, GLenum internalFormat ) +{ + GLM_FUNC; + + // If we're not doing batch create, just return one here. + if ( !gl_batch_tex_creates.GetBool() ) + { + GLuint tex = 0; + gGL->glGenTextures( 1, &tex ); + return tex; + } + + FOR_EACH_VEC( m_availableTextures, i ) + { + TextureEntry_t& tex = m_availableTextures[ i ]; + if ( ( tex.m_nTexBind == GL_NONE || tex.m_nTexBind == texBind ) + && ( tex.m_nInternalFormat == GL_NONE || tex.m_nInternalFormat == internalFormat ) ) + { + // Hit! + GLuint retVal = tex.m_nTexName; + m_availableTextures.Remove( i ); + return retVal; + } + } + + if ( m_availableTextures.Count() >= kGLMHighWaterUndeleted ) + { + PurgeTexCache(); + } + + return FillTexCache( true, kGLMReUpTexCount ); +} + +void GLMContext::CleanupTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ) +{ + // If the total + if ( pLayout->m_storageTotalSize <= ( kDeletedTextureDim * kDeletedTextureDim * sizeof( uint32 ) ) ) + return; + + const GLuint oldPBO = m_nBoundGLBuffer[ kGLMPixelBuffer ]; + const GLuint oldTex = ( m_samplers[ m_activeTexture ].m_pBoundTex != NULL ) ? m_samplers[ m_activeTexture ].m_pBoundTex->GetTexName() : 0; + + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_destroyPBO ); + gGL->glBindTexture( texBind, tex ); + + // Clear out old data. + for ( int i = 0; i < pLayout->m_mipCount; ++i ) + { + int mipDim = ( i == 0 ) ? kDeletedTextureDim : 0; + if ( pLayout->m_format->m_chunkSize != 1 ) + { + const int chunks = ( mipDim + ( pLayout->m_format->m_chunkSize - 1 ) ) / pLayout->m_format->m_chunkSize; + const int dataSize = ( chunks * chunks ) * pLayout->m_format->m_bytesPerSquareChunk; + Assert( dataSize <= ( sizeof( uint32) * ARRAYSIZE( g_garbageTextureBits ) ) ); + + gGL->glCompressedTexImage2D( texBind, i, pLayout->m_format->m_glIntFormat, mipDim, mipDim, 0, dataSize, 0 ); + } + else + { + gGL->glTexImage2D( texBind, i, pLayout->m_format->m_glIntFormat, mipDim, mipDim, 0, pLayout->m_format->m_glDataFormat, pLayout->m_format->m_glDataType, 0 ); + } + } + + gGL->glBindTexture( texBind, oldTex ); + gGL->glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, oldPBO ); +} + +void GLMContext::DestroyTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ) +{ + GLM_FUNC; + + // Code only handles 2D for now. + if ( texBind != GL_TEXTURE_2D || !gl_batch_tex_destroys.GetBool() ) + { + gGL->glDeleteTextures( 1, &tex ); + return; + } + + CleanupTex( texBind, pLayout, tex ); + + TextureEntry_t entry; + entry.m_nTexBind = texBind; + entry.m_nInternalFormat = pLayout->m_format->m_glIntFormat; + entry.m_nTexName = tex; + + m_availableTextures.AddToTail( entry ); +} + +GLuint GLMContext::FillTexCache( bool holdOne, int newTextures ) +{ + // If we aren't doing batch creates, then don't fill the cache. + if ( !gl_batch_tex_creates.GetBool() ) + return 0; + + // If we have to hit the name table, might as well hit it a bunch because this causes + // serialization either way--at least we can do it less often. + GLuint* textures = (GLuint*) stackalloc( newTextures * sizeof( GLuint ) ); + gGL->glGenTextures( newTextures, textures ); + + Assert( textures[ 0 ] ); + + TextureEntry_t entry; + entry.m_nTexBind = GL_NONE; + entry.m_nInternalFormat = GL_NONE; + + // We may return 0, so skip adding it here. + for ( int i = 1; i < newTextures; ++i ) + { + Assert( textures[ i ] ); + if ( textures[ i ] ) + { + // We still add these to the tail because we'd prefer to reuse old textures (rather + // than these new ones). + entry.m_nTexName = textures[ i ]; + m_availableTextures.AddToTail( entry ); + } + } + + if ( holdOne ) + return textures[ 0 ]; + + // If not, stick that last one in the list and return 0. + entry.m_nTexName = textures[ 0 ]; + m_availableTextures.AddToTail( entry ); + + return 0; +} + +void GLMContext::PurgeTexCache() +{ + GLM_FUNC; + + int textureCount = m_availableTextures.Count(); + + if ( textureCount == 0 ) + return; + + GLuint* textures = (GLuint*) stackalloc( textureCount * sizeof( GLuint ) ); + + FOR_EACH_VEC( m_availableTextures, i ) + { + TextureEntry_t& tex = m_availableTextures[ i ]; + textures[ i ] = tex.m_nTexName; + } + + gGL->glDeleteTextures( textureCount, textures ); + + m_availableTextures.RemoveAll(); +} + +#ifdef OSX +// As far as I can tell this stuff is only useful under OSX. +ConVar gl_can_mix_shader_gammas( "gl_can_mix_shader_gammas", 0 ); +ConVar gl_cannot_mix_shader_gammas( "gl_cannot_mix_shader_gammas", 0 ); +#endif + +// ConVar param_write_mode("param_write_mode", "0"); + +void GLMContext::MarkAllSamplersDirty() +{ + m_nNumDirtySamplers = GLM_SAMPLER_COUNT; + for (uint i = 0; i < GLM_SAMPLER_COUNT; i++) + { + m_nDirtySamplerFlags[i] = 0; + m_nDirtySamplers[i] = (uint8)i; + } +} + +void GLMContext::FlushDrawStatesNoShaders( ) +{ + Assert( ( m_drawingFBO == m_boundDrawFBO ) && ( m_drawingFBO == m_boundReadFBO ) ); // this check MUST succeed + + GLM_FUNC; + + GL_BATCH_PERF( m_FlushStats.m_nTotalBatchFlushes++; ) + + NullProgram(); +} + +#if GLMDEBUG + +enum EGLMDebugDumpOptions +{ + eDumpBatchInfo, + eDumpSurfaceInfo, + eDumpStackCrawl, + eDumpShaderLinks, +// eDumpShaderText, // we never use this one + eDumpShaderParameters, + eDumpTextureSetup, + eDumpVertexAttribSetup, + eDumpVertexData, + eOpenShadersForEdit +}; + +enum EGLMVertDumpMode +{ + // options that affect eDumpVertexData above + eDumpVertsNoTransformDump, + eDumpVertsTransformedByViewProj, + eDumpVertsTransformedByModelViewProj, + eDumpVertsTransformedByBoneZeroThenViewProj, + eDumpVertsTransformedByBonesThenViewProj, + eLastDumpVertsMode +}; + +char *g_vertDumpModeNames[] = +{ + "noTransformDump", + "transformedByViewProj", + "transformedByModelViewProj", + "transformedByBoneZeroThenViewProj", + "transformedByBonesThenViewProj" +}; + +static void CopyTilEOL( char *dst, char *src, int dstSize ) +{ + dstSize--; + + int i=0; + while ( (i<dstSize) && (src[i] != 0) && (src[i] != '\n') && (src[i] != '\r') ) + { + dst[i] = src[i]; + i++; + } + dst[i] = 0; +} + +static uint g_maxVertsToDumpLog2 = 4; +static uint g_maxFramesToCrawl = 20; // usually enough. Not enough? change it.. + +extern char sg_pPIXName[128]; + +// min is eDumpVertsNormal, max is the one before eLastDumpVertsMode +static enum EGLMVertDumpMode g_vertDumpMode = eDumpVertsNoTransformDump; + +void GLMContext::DebugDump( GLMDebugHookInfo *info, uint options, uint vertDumpMode ) +{ + int oldIndent = GLMGetIndent(); + GLMSetIndent(0); + + CGLMProgram *vp = m_drawingProgram[kGLMVertexProgram]; + CGLMProgram *fp = m_drawingProgram[kGLMFragmentProgram]; + + bool is_draw = (info->m_caller==eDrawElements); + const char *batchtype = is_draw ? "draw" : "clear"; + + if (options & (1<<eDumpBatchInfo)) + { + GLMPRINTF(("-D- %s === %s %d ======================================================== %s %d frame %d", sg_pPIXName, batchtype, m_nBatchCounter, batchtype, m_nBatchCounter, m_debugFrameIndex )); + } + + if (options & (1<<eDumpSurfaceInfo)) + { + GLMPRINTF(("-D-" )); + GLMPRINTF(("-D- surface info:")); + GLMPRINTF(("-D- drawing FBO: %8x bound draw-FBO: %8x (%s)", m_drawingFBO, m_boundDrawFBO, (m_drawingFBO==m_boundDrawFBO) ? "in sync" : "desync!" )); + + CGLMFBO *fbo = m_boundDrawFBO; + for( int i=0; i<kAttCount; i++) + { + CGLMTex *tex = fbo->m_attach[i].m_tex; + if (tex) + { + GLMPRINTF(("-D- bound FBO (%8x) attachment %d = tex %8x (GL %d) (%s)", fbo, i, tex, tex->m_texName, tex->m_layout->m_layoutSummary )); + } + else + { + // warning if no depthstencil attachment + switch(i) + { + case kAttDepth: + case kAttStencil: + case kAttDepthStencil: + GLMPRINTF(("-D- bound FBO (%8x) attachment %d = NULL, warning!", fbo, i )); + break; + } + } + } + } + + if (options & (1<<eDumpStackCrawl)) + { + CStackCrawlParams cp; + memset( &cp, 0, sizeof(cp) ); + cp.m_frameLimit = g_maxFramesToCrawl; + + GetStackCrawl(&cp); + + GLMPRINTF(("-D-" )); + GLMPRINTF(("-D- stack crawl")); + for( uint i=0; i< cp.m_frameCount; i++) + { + GLMPRINTF(("-D-\t%s", cp.m_crawlNames[i] )); + } + } + + if ( (options & (1<<eDumpShaderLinks)) && is_draw) + { + // we want to print out - GL name, pathname to disk copy if editable, extra credit would include the summary translation line + // so grep for "#// trans#" + char attribtemp[1000]; + char transtemp[1000]; + + if (vp) + { + char *attribmap = strstr(vp->m_text, "#//ATTRIBMAP"); + if (attribmap) + { + CopyTilEOL( attribtemp, attribmap, sizeof(attribtemp) ); + } + else + { + strcpy( attribtemp, "no attrib map" ); + } + + char *trans = strstr(vp->m_text, "#// trans#"); + if (trans) + { + CopyTilEOL( transtemp, trans, sizeof(transtemp) ); + } + else + { + strcpy( transtemp, "no translation info" ); + } + + char *linkpath = "no file link"; + + #if GLMDEBUG + linkpath = vp->m_editable->m_mirror->m_path; + #endif + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- ARBVP || GL %d || Path %s ", vp->m_descs[kGLMARB].m_object.arb, linkpath )); + GLMPRINTF(("-D- Attribs %s", attribtemp )); + GLMPRINTF(("-D- Trans %s", transtemp )); + + /* + if ( (options & (1<<eDumpShaderText)) && is_draw ) + { + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- VP text " )); + GLMPRINTTEXT(vp->m_string, eDebugDump )); + } + */ + } + else + { + GLMPRINTF(("-D- VP (none)" )); + } + + if (fp) + { + char *trans = strstr(fp->m_text, "#// trans#"); + if (trans) + { + CopyTilEOL( transtemp, trans, sizeof(transtemp) ); + } + else + { + strcpy( transtemp, "no translation info" ); + } + + char *linkpath = "no file link"; + + #if GLMDEBUG + linkpath = fp->m_editable->m_mirror->m_path; + #endif + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- FP || GL %d || Path %s ", fp->m_descs[kGLMARB].m_object.arb, linkpath )); + GLMPRINTF(("-D- Trans %s", transtemp )); + + /* + if ( (options & (1<<eDumpShaderText)) && is_draw ) + { + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- FP text " )); + GLMPRINTTEXT((fp->m_string, eDebugDump)); + } + */ + } + else + { + GLMPRINTF(("-D- FP (none)" )); + } + } + + if ( (options & (1<<eDumpShaderParameters)) && is_draw ) + { + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- VP parameters" )); + char *label = ""; + //int labelcounter = 0; + + static int vmaskranges[] = { /*18,47,*/ -1,-1 }; + //float transposeTemp; // row, column for printing + + int slotIndex = 0; + int upperSlotLimit = 61; + + // take a peek at the vertex attrib setup. If it has an attribute for bone weights, then raise the shader param dump limit to 256. + bool usesSkinning = false; + GLMVertexSetup *pSetup = &m_drawVertexSetup; + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + { + usesSkinning |= (pSetup->m_attrMask & (1<<index)) && ((pSetup->m_vtxAttribMap[index]>>4)== D3DDECLUSAGE_BLENDWEIGHT); + } + if (usesSkinning) + { + upperSlotLimit = 256; + } + + while( slotIndex < upperSlotLimit ) + { + // if slot index is in a masked range, skip it + // if slot index is the start of a matrix, label it, print it, skip ahead 4 slots + for( int maski=0; vmaskranges[maski] >=0; maski+=2) + { + if ( (slotIndex >= vmaskranges[maski]) && (slotIndex <= vmaskranges[maski+1]) ) + { + // that index is masked. set to one past end of range, print a blank line for clarity + slotIndex = vmaskranges[maski+1]+1; + GLMPrintStr("-D- ....."); + } + } + + if (slotIndex < upperSlotLimit) + { + float *values = &m_programParamsF[ kGLMVertexProgram ].m_values[slotIndex][0]; + switch( slotIndex ) + { + case 4: + printmat( "MODELVIEWPROJ", slotIndex, 4, values ); + slotIndex += 4; + break; + + case 8: + printmat( "VIEWPROJ", slotIndex, 4, values ); + slotIndex += 4; + break; + + default: + if (slotIndex>=58) + { + // bone + char bonelabel[100]; + + sprintf(bonelabel, "MODEL_BONE%-2d", (slotIndex-58)/3 ); + printmat( bonelabel, slotIndex, 3, values ); + + slotIndex += 3; + } + else + { + // just print the one slot + GLMPRINTF(("-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] %s", slotIndex, values[0], values[1], values[2], values[3], label )); + slotIndex++; + } + break; + } + } + } + + // VP stage still, if in GLSL mode, find the bound pair and see if it has live i0, b0-b3 uniforms + if (m_pBoundPair) // should only be non-NULL in GLSL mode + { +#if 0 + if (m_pBoundPair->m_locVertexBool0>=0) + { + GLMPRINTF(("-D- GLSL 'b0': %d", m_programParamsB[kGLMVertexProgram].m_values[0] )); + } + + if (m_pBoundPair->m_locVertexBool1>=0) + { + GLMPRINTF(("-D- GLSL 'b1': %d", m_programParamsB[kGLMVertexProgram].m_values[1] )); + } + + if (m_pBoundPair->m_locVertexBool2>=0) + { + GLMPRINTF(("-D- GLSL 'b2': %d", m_programParamsB[kGLMVertexProgram].m_values[2] )); + } + + if (m_pBoundPair->m_locVertexBool3>=0) + { + GLMPRINTF(("-D- GLSL 'b3': %d", m_programParamsB[kGLMVertexProgram].m_values[3] )); + } + + if (m_pBoundPair->m_locVertexInteger0>=0) + { + GLMPRINTF(("-D- GLSL 'i0': %d", m_programParamsI[kGLMVertexProgram].m_values[0][0] )); + } +#endif + } + + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- FP parameters " )); + + static int fmaskranges[] = { 40,41, -1,-1 }; + + slotIndex = 0; + label = ""; + while(slotIndex < 40) + { + // if slot index is in a masked range, skip it + // if slot index is the start of a matrix, label it, print it, skip ahead 4 slots + for( int maski=0; fmaskranges[maski] >=0; maski+=2) + { + if ( (slotIndex >= fmaskranges[maski]) && (slotIndex <= fmaskranges[maski+1]) ) + { + // that index is masked. set to one past end of range, print a blank line for clarity + slotIndex = fmaskranges[maski+1]+1; + GLMPrintStr("-D- ....."); + } + } + + if (slotIndex < 40) + { + float *values = &m_programParamsF[ kGLMFragmentProgram ].m_values[slotIndex][0]; + switch( slotIndex ) + { + case 0: label = "g_EnvmapTint"; break; + case 1: label = "g_DiffuseModulation"; break; + case 2: label = "g_EnvmapContrast_ShadowTweaks"; break; + case 3: label = "g_EnvmapSaturation_SelfIllumMask (xyz, and w)"; break; + case 4: label = "g_SelfIllumTint_and_BlendFactor (xyz, and w)"; break; + + case 12: label = "g_ShaderControls"; break; + case 13: label = "g_DepthFeatheringConstants"; break; + + case 20: label = "g_EyePos"; break; + case 21: label = "g_FogParams"; break; + case 22: label = "g_FlashlightAttenuationFactors"; break; + case 23: label = "g_FlashlightPos"; break; + case 24: label = "g_FlashlightWorldToTexture"; break; + + case 28: label = "cFlashlightColor"; break; + case 29: label = "g_LinearFogColor"; break; + case 30: label = "cLightScale"; break; + case 31: label = "cFlashlightScreenScale"; break; + + default: + label = ""; + break; + } + + GLMPRINTF(("-D- %03d: [ %10.5f %10.5f %10.5f %10.5f ] %s", slotIndex, values[0], values[1], values[2], values[3], label )); + + slotIndex ++; + } + } + + if (m_pBoundPair->m_locFragmentFakeSRGBEnable) + { + GLMPRINTF(("-D- GLSL 'flEnableSRGBWrite': %f", m_pBoundPair->m_fakeSRGBEnableValue )); + } + } + + if ( (options & (1<<eDumpTextureSetup)) && is_draw ) + { + GLMPRINTF(( "-D-" )); + GLMPRINTF(( "-D- Texture / Sampler setup" )); + GLMPRINTF(( "-D- TODO" )); +#if 0 + for( int i=0; i<GLM_SAMPLER_COUNT; i++ ) + { + if (m_samplers[i].m_pBoundTex) + { + GLMTexSamplingParams *samp = &m_samplers[i].m_samp; + GLMPRINTF(( "-D-" )); + GLMPRINTF(("-D- Sampler %-2d tex %08x layout %s", i, m_samplers[i].m_pBoundTex, m_samplers[i].m_pBoundTex->m_layout->m_layoutSummary )); + + GLMPRINTF(("-D- addressMode[ %s %s %s ]", + GLMDecode( eGL_ENUM, samp->m_addressModes[0] ), + GLMDecode( eGL_ENUM, samp->m_addressModes[1] ), + GLMDecode( eGL_ENUM, samp->m_addressModes[2] ) + )); + + GLMPRINTF(("-D- magFilter [ %s ]", GLMDecode( eGL_ENUM, samp->m_magFilter ) )); + GLMPRINTF(("-D- minFilter [ %s ]", GLMDecode( eGL_ENUM, samp->m_minFilter ) )); + GLMPRINTF(("-D- srgb [ %s ]", samp->m_srgb ? "T" : "F" )); + GLMPRINTF(("-D- shadowFilter [ %s ]", samp->m_compareMode == GL_COMPARE_R_TO_TEXTURE_ARB ? "T" : "F" )); + + // add more as needed later.. + } + } +#endif + } + + if ( (options & (1<<eDumpVertexAttribSetup)) && is_draw ) + { + GLMVertexSetup *pSetup = &m_drawVertexSetup; + + uint nRelevantMask = pSetup->m_attrMask; + for( int index=0; index < kGLMVertexAttributeIndexMax; index++ ) + { + uint mask = 1<<index; + if (nRelevantMask & mask) + { + GLMVertexAttributeDesc *setdesc = &pSetup->m_attrs[index]; + + char sizestr[100]; + if (setdesc->m_nCompCount < 32) + { + sprintf( sizestr, "%d", setdesc->m_nCompCount); + } + else + { + strcpy( sizestr, GLMDecode( eGL_ENUM, setdesc->m_nCompCount ) ); + } + + if (pSetup->m_vtxAttribMap[index] != 0xBB) + { + GLMPRINTF(("-D- attr=%-2d decl=$%s%1d stride=%-2d offset=%-3d buf=%08x size=%s type=%s normalized=%s ", + index, + GLMDecode(eD3D_VTXDECLUSAGE, pSetup->m_vtxAttribMap[index]>>4 ), + pSetup->m_vtxAttribMap[index]&0x0F, + setdesc->m_stride, + setdesc->m_offset, + setdesc->m_pBuffer, + sizestr, + GLMDecode( eGL_ENUM, setdesc->m_datatype), + setdesc->m_normalized?"Y":"N" + )); + } + else + { + // the attrib map is referencing an attribute that is not wired up in the vertex setup... + DebuggerBreak(); + } + } + } + } + + if ( (options & (1<<eDumpVertexData)) && is_draw ) + { + GLMVertexSetup *pSetup = &m_drawVertexSetup; + int start = info->m_drawStart; + int end = info->m_drawEnd; + int endLimit = start + (1<<g_maxVertsToDumpLog2); + int realEnd = MIN( end, endLimit ); + + // vertex data + GLMPRINTF(("-D-")); + GLMPRINTF(("-D- Vertex Data : %d of %d verts (index %d through %d)", realEnd-start, end-start, start, realEnd-1)); + + for( int vtxIndex=-1; vtxIndex < realEnd; vtxIndex++ ) // vtxIndex will jump from -1 to start after first spin, not necessarily to 0 + { + char buf[64000]; + char *mark = buf; + + // index -1 is the first run through the loop, we just print a header + + // iterate attrs + if (vtxIndex>=0) + { + mark += sprintf(mark, "-D- %04d: ", vtxIndex ); + } + + // for transform dumping, we latch values as we spot them + float vtxPos[4]; + int vtxBoneIndices[4]; // only three get used + float vtxBoneWeights[4]; // only three get used and index 2 is synthesized from 0 and 1 + + vtxPos[0] = vtxPos[1] = vtxPos[2] = 0.0; + vtxPos[3] = 1.0; + + vtxBoneIndices[0] = vtxBoneIndices[1] = vtxBoneIndices[2] = vtxBoneIndices[3] = 0; + vtxBoneWeights[0] = vtxBoneWeights[1] = vtxBoneWeights[2] = vtxBoneWeights[3] = 0.0; + + for( int attr = 0; attr < kGLMVertexAttributeIndexMax; attr++ ) + { + if (pSetup->m_attrMask & (1<<attr) ) + { + GLMVertexAttributeDesc *desc = &pSetup->m_attrs[ attr ]; + + // print that attribute. + + // on OSX, VB's never move unless resized. You can peek at them when unmapped. Safe enough for debug.. + char *bufferBase = (char*)desc->m_pBuffer->m_pLastMappedAddress; + + uint stride = desc->m_stride; + uint fieldoffset = desc->m_offset; + uint baseoffset = vtxIndex * stride; + + char *attrBase = bufferBase + baseoffset + fieldoffset; + + uint usage = pSetup->m_vtxAttribMap[attr]>>4; + uint usageindex = pSetup->m_vtxAttribMap[attr]&0x0F; + + if (vtxIndex <0) + { + mark += sprintf(mark, "[%s%1d @ offs=%04d / strd %03d] ", GLMDecode(eD3D_VTXDECLUSAGE, usage ), usageindex, fieldoffset, stride ); + } + else + { + mark += sprintf(mark, "[%s%1d ", GLMDecode(eD3D_VTXDECLUSAGE, usage ), usageindex ); + + if (desc->m_nCompCount<32) + { + for( uint which = 0; which < desc->m_nCompCount; which++ ) + { + static char *fieldname = "xyzw"; + switch( desc->m_datatype ) + { + case GL_FLOAT: + { + float *floatbase = (float*)attrBase; + mark += sprintf(mark, (usage != D3DDECLUSAGE_TEXCOORD) ? "%c%7.3f " : "%c%.3f", fieldname[which], floatbase[which] ); + + if (usage==D3DDECLUSAGE_POSITION) + { + if (which<4) + { + // latch pos + vtxPos[which] = floatbase[which]; + } + } + + if (usage==D3DDECLUSAGE_BLENDWEIGHT) + { + if (which<4) + { + // latch weight + vtxBoneWeights[which] = floatbase[which]; + } + } + } + break; + + case GL_UNSIGNED_BYTE: + { + unsigned char *unchbase = (unsigned char*)attrBase; + mark += sprintf(mark, "%c$%02X ", fieldname[which], unchbase[which] ); + } + break; + + default: + // hold off on other formats for now + mark += sprintf(mark, "%c????? ", fieldname[which] ); + break; + } + } + } + else // special path for BGRA bytes which are expressed in GL by setting the *size* to GL_BGRA (gross large enum) + { + switch(desc->m_nCompCount) + { + case GL_BGRA: // byte reversed color + { + for( int which = 0; which < 4; which++ ) + { + static const char *fieldname = "BGRA"; + switch( desc->m_datatype ) + { + case GL_UNSIGNED_BYTE: + { + unsigned char *unchbase = (unsigned char*)attrBase; + mark += sprintf(mark, "%c$%02X ", fieldname[which], unchbase[which] ); + + if (usage==D3DDECLUSAGE_BLENDINDICES) + { + if (which<4) + { + // latch index + vtxBoneIndices[which] = unchbase[which]; // ignoring the component reverse which BGRA would inflict, but we also ignore it below so it matches up. + } + } + } + break; + + default: + DebuggerBreak(); + break; + } + } + } + break; + } + } + mark += sprintf(mark, "] " ); + } + } + } + GLMPrintStr( buf, eDebugDump ); + + if (vtxIndex >=0) + { + // if transform dumping requested, and we've reached the actual vert dump phase, do it + float vtxout[4]; + char *translabel = NULL; // NULL means no print... + + switch( g_vertDumpMode ) + { + case eDumpVertsNoTransformDump: break; + + case eDumpVertsTransformedByViewProj: // viewproj is slot 8 + { + float *viewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[8][0]; + transform_dp4( vtxPos, viewproj, 4, vtxout ); + translabel = "post-viewproj"; + } + break; + + case eDumpVertsTransformedByModelViewProj: // modelviewproj is slot 4 + { + float *modelviewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[4][0]; + transform_dp4( vtxPos, modelviewproj, 4, vtxout ); + translabel = "post-modelviewproj"; + } + break; + + case eDumpVertsTransformedByBoneZeroThenViewProj: + { + float postbone[4]; + postbone[3] = 1.0; + + float *bonemat = &m_programParamsF[ kGLMVertexProgram ].m_values[58][0]; + transform_dp4( vtxPos, bonemat, 3, postbone ); + + float *viewproj = &m_programParamsF[ kGLMVertexProgram ].m_values[8][0]; // viewproj is slot 8 + transform_dp4( postbone, viewproj, 4, vtxout ); + + translabel = "post-bone0-viewproj"; + } + break; + + case eDumpVertsTransformedByBonesThenViewProj: + { + //float bone[4][4]; // [bone index][bone member] // members are adjacent + + vtxout[0] = vtxout[1] = vtxout[2] = vtxout[3] = 0; + + // unpack the third weight + vtxBoneWeights[2] = 1.0 - (vtxBoneWeights[0] + vtxBoneWeights[1]); + + for( int ibone=0; ibone<3; ibone++ ) + { + int boneindex = vtxBoneIndices[ ibone ]; + float *bonemat = &m_programParamsF[ kGLMVertexProgram ].m_values[58+(boneindex*3)][0]; + + float boneweight = vtxBoneWeights[ibone]; + + float postbonevtx[4]; + + transform_dp4( vtxPos, bonemat, 3, postbonevtx ); + + // add weighted sum into output + for( int which=0; which<4; which++ ) + { + vtxout[which] += boneweight * postbonevtx[which]; + } + } + + // fix W ? do we care ? check shaders to see what they do... + translabel = "post-skin3bone-viewproj"; + } + break; + } + if(translabel) + { + // for extra credit, do the perspective divide and viewport + + GLMPRINTF(("-D- %-24s: [ %7.4f %7.4f %7.4f %7.4f ]", translabel, vtxout[0],vtxout[1],vtxout[2],vtxout[3] )); + GLMPRINTF(("-D-" )); + } + } + + if (vtxIndex<0) + { + vtxIndex = start-1; // for printing of the data (note it will be incremented at bottom of loop, so bias down by 1) + } + else + { // no more < and > around vert dump lines + //mark += sprintf(mark, "" ); + } + } + } + + if (options & (1<<eOpenShadersForEdit) ) + { + #if GLMDEBUG + if (m_drawingProgram[ kGLMVertexProgram ]) + { + m_drawingProgram[ kGLMVertexProgram ]->m_editable->OpenInEditor(); + } + + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + m_drawingProgram[ kGLMFragmentProgram ]->m_editable->OpenInEditor(); + } + #endif + } +/* + if (options & (1<<)) + { + } +*/ + // trailer line + GLMPRINTF(("-D- ===================================================================================== end %s %d frame %d", batchtype, m_nBatchCounter, m_debugFrameIndex )); + + GLMSetIndent(oldIndent); +} + +// here is the table that binds knob numbers to names. change at will. +char *g_knobnames[] = +{ +/*0*/ "dummy", + +/*1*/ "FB-SRGB", + #if 0 + /*1*/ "tex-U0-bias", // src left + /*2*/ "tex-V0-bias", // src upper + /*3*/ "tex-U1-bias", // src right + /*4*/ "tex-V1-bias", // src bottom + + /*5*/ "pos-X0-bias", // dst left + /*6*/ "pos-Y0-bias", // dst upper + /*7*/ "pos-X1-bias", // dst right + /*8*/ "pos-Y1-bias", // dst bottom + #endif + +}; +int g_knobcount = sizeof( g_knobnames ) / sizeof( g_knobnames[0] ); + +void GLMContext::DebugHook( GLMDebugHookInfo *info ) +{ + // FIXME: This has seriously bitrotted. + return; + + bool debughook = false; + // debug hook is called after an action has taken place. + // that would be the initial action, or a repeat. + // if paused, we stay inside this function until return. + // when returning, we inform the caller if it should repeat its last action or continue. + // there is no global pause state. The rest of the app runs at the best speed it can. + + // initial stuff we do unconditionally + + // increment iteration + info->m_iteration++; // can be thought of as "number of times the caller's action has now occurred - starting at 1" + + // now set initial state guess for the info block (outcome may change below) + info->m_loop = false; + + // check prior hold-conditions to see if any of them hit. + // note we disarm each trigger once the hold has occurred (one-shot style) + + switch( info->m_caller ) + { + case eBeginFrame: + if (debughook) GLMPRINTF(("-D- Caller: BeginFrame" )); + if ( (m_holdFrameBegin>=0) && (m_holdFrameBegin==m_debugFrameIndex) ) // did we hit a frame breakpoint? + { + if (debughook) GLMPRINTF(("-D- BeginFrame trigger match, clearing m_holdFrameBegin, hold=true" )); + + m_holdFrameBegin = -1; + + info->m_holding = true; + } + break; + + case eClear: + if (debughook) GLMPRINTF(("-D- Caller: Clear" )); + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && ((int)m_holdBatch==(int)m_nBatchCounter) && ((int)m_holdBatchFrame==(int)m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- Clear trigger match, clearing m_holdBatch&Frame, hold=true" )); + + m_holdBatch = m_holdBatchFrame = -1; + + info->m_holding = true; + } + break; + + case eDrawElements: + if (debughook) GLMPRINTF(( (info->m_caller==eClear) ? "-D- Caller: Clear" : "-D- Caller: Draw" )); + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && ((int)m_holdBatch==(int)m_nBatchCounter) && ((int)m_holdBatchFrame==(int)m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- Draw trigger match, clearing m_holdBatch&Frame, hold=true" )); + + m_holdBatch = m_holdBatchFrame = -1; + + info->m_holding = true; + } + break; + + case eEndFrame: + if (debughook) GLMPRINTF(("-D- Caller: EndFrame" )); + + // check for any expired batch hold req + if ( (m_holdBatch>=0) && (m_holdBatchFrame>=0) && (m_holdBatchFrame==m_debugFrameIndex) ) + { + // you tried to say 'next batch', but there wasn't one in this frame. + // target first batch of next frame instead + if (debughook) GLMPRINTF(("-D- EndFrame noticed an expired draw hold trigger, rolling to next frame, hold=false")); + + m_holdBatch = 0; + m_holdBatchFrame++; + + info->m_holding = false; + } + + // now check for an explicit hold on end of this frame.. + if ( (m_holdFrameEnd>=0) && (m_holdFrameEnd==m_debugFrameIndex) ) + { + if (debughook) GLMPRINTF(("-D- EndFrame trigger match, clearing m_holdFrameEnd, hold=true" )); + + m_holdFrameEnd = -1; + + info->m_holding = true; + } + break; + } + + // spin until event queue is empty *and* hold is false + + int evtcount=0; + + bool refresh = info->m_holding || m_debugDelayEnable; // only refresh once per initial visit (if paused!) or follow up event input + int breakToDebugger = 0; + // 1 = break to GDB + // 2 = break to OpenGL Profiler if attached + + do + { + if (refresh) + { + if (debughook) GLMPRINTF(("-D- pushing pixels" )); + DebugPresent(); // show pixels + + uint minidumpOptions = (1<<eDumpBatchInfo) /* | (1<<eDumpSurfaceInfo) */; + DebugDump( info, minidumpOptions, g_vertDumpMode ); + + ThreadSleep( 10000 / 1000 ); // lil sleep + + refresh = false; + } + + bool eventCheck = true; // event pull will be skipped if we detect a shader edit being done + // keep editable shaders in sync + #if GLMDEBUG + + bool redrawBatch = false; + if (m_drawingProgram[ kGLMVertexProgram ]) + { + if( m_drawingProgram[ kGLMVertexProgram ]->SyncWithEditable() ) + { + redrawBatch = true; + } + } + + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + if( m_drawingProgram[ kGLMFragmentProgram ]->SyncWithEditable() ) + { + redrawBatch = true; + } + } + + if (redrawBatch) + { + // act as if user pressed the option-\ key + + if (m_drawingLang == kGLMGLSL) + { + // if GLSL mode, force relink - and refresh the pair cache as needed + if (m_pBoundPair) + { + // fix it in place + m_pBoundPair->RefreshProgramPair(); + } + } + + // TODO - need to retest this whole path + FlushDrawStates( 0, 0, 0 ); // this is key, because the linked shader pair may have changed (note call to PurgePairsWithShader in cglmprogram.cpp) + + GLMPRINTF(("-- Shader changed, re-running batch" )); + + m_holdBatch = m_nBatchCounter; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; + + info->m_holding = false; + info->m_loop = true; + + eventCheck = false; + } + #endif + + if(eventCheck) + { + PumpWindowsMessageLoop(); + CCocoaEvent evt; + evtcount = GetEvents( &evt, 1, true ); // asking for debug events only. + if (evtcount) + { + // print it + if (debughook) GLMPRINTF(("-D- Received debug key '%c' with modifiers %x", evt.m_UnicodeKeyUnmodified, evt.m_ModifierKeyMask )); + + // flag for refresh if we spin again + refresh = 1; + + switch(evt.m_UnicodeKeyUnmodified) + { + case ' ': // toggle pause + // clear all the holds to be sure + m_holdFrameBegin = m_holdFrameEnd = m_holdBatch = m_holdBatchFrame = -1; + info->m_holding = !info->m_holding; + + if (!info->m_holding) + { + m_debugDelayEnable = false; // coming out of pause means no slow mo + } + + GLMPRINTF((info->m_holding ? "-D- Paused." : "-D- Unpaused." )); + break; + + case 'f': // frame advance + GLMPRINTF(("-D- Command: next frame" )); + m_holdFrameBegin = m_debugFrameIndex+1; // stop at top of next numbered frame + m_debugDelayEnable = false; // get there fast + + info->m_holding = false; + break; + + case ']': // ahead 1 batch + case '}': // ahead ten batches + { + int delta = evt.m_UnicodeKeyUnmodified == ']' ? 1 : 10; + m_holdBatch = m_nBatchCounter+delta; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; // get there fast + info->m_holding = false; + GLMPRINTF(("-D- Command: advance %d batches to %d", delta, m_holdBatch )); + } + break; + + case '[': // back one batch + case '{': // back 10 batches + { + int delta = evt.m_UnicodeKeyUnmodified == '[' ? -1 : -10; + m_holdBatch = m_nBatchCounter + delta; + if (m_holdBatch<0) + { + m_holdBatch = 0; + } + m_holdBatchFrame = m_debugFrameIndex+1; // next frame, but prev batch # + m_debugDelayEnable = false; // get there fast + info->m_holding = false; + GLMPRINTF(("-D- Command: rewind %d batches to %d", delta, m_holdBatch )); + } + break; + + case '\\': // batch rerun + + m_holdBatch = m_nBatchCounter; + m_holdBatchFrame = m_debugFrameIndex; + m_debugDelayEnable = false; + info->m_holding = false; + info->m_loop = true; + GLMPRINTF(("-D- Command: re-run batch %d", m_holdBatch )); + break; + + case 'c': // toggle auto color clear + m_autoClearColor = !m_autoClearColor; + GLMPRINTF((m_autoClearColor ? "-D- Auto color clear ON" : "-D- Auto color clear OFF" )); + break; + + case 's': // toggle auto stencil clear + m_autoClearStencil = !m_autoClearStencil; + GLMPRINTF((m_autoClearStencil ? "-D- Auto stencil clear ON" : "-D- Auto stencil clear OFF" )); + break; + + case 'd': // toggle auto depth clear + m_autoClearDepth = !m_autoClearDepth; + GLMPRINTF((m_autoClearDepth ? "-D- Auto depth clear ON" : "-D- Auto depth clear OFF" )); + break; + + case '.': // break to debugger or insta-quit + if (evt.m_ModifierKeyMask & (1<<eControlKey)) + { + GLMPRINTF(( "-D- INSTA QUIT! (TM) (PAT PEND)" )); + abort(); + } + else + { + GLMPRINTF(( "-D- Breaking to debugger" )); + breakToDebugger = 1; + + info->m_holding = true; + info->m_loop = true; // so when you come back from debugger, you get another spin (i.e. you enter paused mode) + } + break; + + case 'g': // break to OGLP and enable OGLP logging of spew + if (GLMDetectOGLP()) // if this comes back true, there will be a breakpoint set on glColor4sv. + { + uint channelMask = GLMDetectAvailableChannels(); // will re-assert whether spew goes to OGLP log + + if (channelMask & (1<<eGLProfiler)) + { + GLMDebugChannelMask(&channelMask); + breakToDebugger = 2; + + info->m_holding = true; + info->m_loop = true; // so when you come back from debugger, you get another spin (i.e. you enter paused mode) + } + } + break; + + case '_': // toggle slow mo + m_debugDelayEnable = !m_debugDelayEnable; + break; + + case '-': // go slower + if (m_debugDelayEnable) + { + // already in slow mo, so lower speed + m_debugDelay <<= 1; // double delay + if (m_debugDelay > (1<<24)) + { + m_debugDelay = (1<<24); + } + } + else + { + // enter slow mo + m_debugDelayEnable = true; + } + break; + + case '=': // go faster + if (m_debugDelayEnable) + { + // already in slow mo, so raise speed + m_debugDelay >>= 1; // halve delay + if (m_debugDelay < (1<<17)) + { + m_debugDelay = (1<<17); + } + } + else + { + // enter slow mo + m_debugDelayEnable = true; + } + break; + + case 'v': + // open vs in editor (foreground pop) + #if GLMDEBUG + if (m_drawingProgram[ kGLMVertexProgram ]) + { + m_drawingProgram[ kGLMVertexProgram ]->m_editable->OpenInEditor( true ); + } + #endif + break; + + case 'p': + // open fs/ps in editor (foreground pop) + #if GLMDEBUG + if (m_drawingProgram[ kGLMFragmentProgram ]) + { + m_drawingProgram[ kGLMFragmentProgram ]->m_editable->OpenInEditor( true ); + } + #endif + break; + + case '<': // dump fewer verts + case '>': // dump more verts + { + int delta = (evt.m_UnicodeKeyUnmodified=='>') ? 1 : -1; + g_maxVertsToDumpLog2 = MIN( MAX( g_maxVertsToDumpLog2+delta, 0 ), 16 ); + + // just re-dump the verts + DebugDump( info, 1<<eDumpVertexData, g_vertDumpMode ); + } + break; + + case 'x': // adjust transform dump mode + { + int newmode = g_vertDumpMode+1; + if (newmode >= eLastDumpVertsMode) + { + // wrap + newmode = eDumpVertsNoTransformDump; + } + g_vertDumpMode = (EGLMVertDumpMode)newmode; + + GLMPRINTF(("-D- New vert dump mode is %s", g_vertDumpModeNames[g_vertDumpMode] )); + } + break; + + case 'u': // more crawl + { + CStackCrawlParams cp; + memset( &cp, 0, sizeof(cp) ); + cp.m_frameLimit = kMaxCrawlFrames; + + GetStackCrawl(&cp); + + GLMPRINTF(("-D-" )); + GLMPRINTF(("-D- extended stack crawl:")); + for( uint i=0; i< cp.m_frameCount; i++) + { + GLMPRINTF(("-D-\t%s", cp.m_crawlNames[i] )); + } + } + + break; + + case 'q': + DebugDump( info, 0xFFFFFFFF, g_vertDumpMode ); + break; + + + case 'H': + case 'h': + { + // toggle drawing language. hold down shift key to do it immediately. + + if (m_caps.m_hasDualShaders) + { + bool immediate; + + immediate = evt.m_UnicodeKeyUnmodified == 'H'; // (evt.m_ModifierKeyMask & (1<<eShiftKey)) != 0; + + if (m_drawingLang==kGLMARB) + { + GLMPRINTF(( "-D- Setting GLSL language mode %s.", immediate ? "immediately" : "for next frame start" )); + SetDrawingLang( kGLMGLSL, immediate ); + } + else + { + GLMPRINTF(( "-D- Setting ARB language mode %s.", immediate ? "immediately" : "for next frame start" )); + SetDrawingLang( kGLMARB, immediate ); + } + refresh = immediate; + } + else + { + GLMPRINTF(("You can't change shader languages unless you launch with -glmdualshaders enabled")); + } + + } + break; + + + // ======================================================== debug knobs. change these as needed to troubleshoot stuff + + // keys to select a knob + // or, toggle a debug flavor, if control is being held down + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + { + if (evt.m_ModifierKeyMask & (1<<eControlKey)) + { + // '0' toggles the all-channels on or off + int flavorSelect = evt.m_UnicodeKeyUnmodified - '0'; + + if ( (flavorSelect >=0) && (flavorSelect<eFlavorCount) ) + { + uint mask = GLMDebugFlavorMask(); + + mask ^= (1<<flavorSelect); + + GLMDebugFlavorMask(&mask); + } + } + else + { + // knob selection + m_selKnobIndex = evt.m_UnicodeKeyUnmodified - '0'; + + GLMPRINTF(("-D- Knob # %d (%s) selected.", m_selKnobIndex, g_knobnames[ m_selKnobIndex ] )); + + m_selKnobIncrement = (m_selKnobIndex<5) ? (1.0f / 2048.0f) : (1.0 / 256.0f); + ThreadSleep( 500000 / 1000 ); + } + refresh = false; + } + break; + + // keys to adjust or zero a knob + case 't': // toggle + { + if (m_selKnobIndex < g_knobcount) + { + GLMKnobToggle( g_knobnames[ m_selKnobIndex ] ); + } + } + break; + + case 'l': // less + case 'm': // more + case 'z': // zero + { + if (m_selKnobIndex < g_knobcount) + { + float val = GLMKnob( g_knobnames[ m_selKnobIndex ], NULL ); + + if (evt.m_UnicodeKeyUnmodified == 'l') + { + // minus (less) + val -= m_selKnobIncrement; + if (val < m_selKnobMinValue) + { + val = m_selKnobMinValue; + } + // send new value back to the knob + GLMKnob( g_knobnames[ m_selKnobIndex ], &val ); + } + + if (evt.m_UnicodeKeyUnmodified == 'm') + { + // plus (more) + val += m_selKnobIncrement; + if (val > m_selKnobMaxValue) + { + val = m_selKnobMaxValue; + } + // send new value back to the knob + GLMKnob( g_knobnames[ m_selKnobIndex ], &val ); + } + + if (evt.m_UnicodeKeyUnmodified == 'z') + { + // zero + val = 0.0f; + + // send new value back to the knob + GLMKnob( g_knobnames[ m_selKnobIndex ], &val ); + } + + GLMPRINTF(("-D- Knob # %d (%s) set to %f (%f/1024.0)", m_selKnobIndex, g_knobnames[ m_selKnobIndex ], val, val * 1024.0 )); + + ThreadSleep( 500000 / 1000 ); + + refresh = false; + } + } + break; + + } + } + } + } while( ((evtcount>0) || info->m_holding) && (!breakToDebugger) ); + + if (m_debugDelayEnable) + { + ThreadSleep( m_debugDelay / 1000 ); + } + + if (breakToDebugger) + { + switch (breakToDebugger) + { + case 1: + DebuggerBreak(); + break; + + case 2: + short fakecolor[4] = { 0, 0, 0, 0 }; + gGL->glColor4sv( fakecolor ); // break to OGLP + break; + } + // re-flush all GLM states so you can fiddle with them in the debugger. then run the batch again and spin.. + ForceFlushStates(); + } +} + +void GLMContext::DebugPresent( void ) +{ + CGLMTex *drawBufferTex = m_drawingFBO->m_attach[kAttColor0].m_tex; + gGL->glFinish(); + Present( drawBufferTex ); +} + +void GLMContext::DebugClear( void ) +{ + // get old clear color + GLClearColor_t clearcol_orig; + m_ClearColor.Read( &clearcol_orig,0 ); + + // new clear color + GLClearColor_t clearcol; + clearcol.r = m_autoClearColorValues[0]; + clearcol.g = m_autoClearColorValues[1]; + clearcol.b = m_autoClearColorValues[2]; + clearcol.a = m_autoClearColorValues[3]; + m_ClearColor.Write( &clearcol ); // don't check, don't defer + + uint mask = 0; + + if (m_autoClearColor) mask |= GL_COLOR_BUFFER_BIT; + if (m_autoClearDepth) mask |= GL_DEPTH_BUFFER_BIT; + if (m_autoClearStencil) mask |= GL_STENCIL_BUFFER_BIT; + + gGL->glClear( mask ); + gGL->glFinish(); + + // put old color back + m_ClearColor.Write( &clearcol_orig ); // don't check, don't defer +} + +#endif + +void GLMContext::CheckNative( void ) +{ + // note that this is available in release. We don't use GLMPRINTF for that reason. + // note we do not get called unless either slow-batch asserting or logging is enabled. +#ifdef OSX + bool gpuProcessing; + GLint fragmentGPUProcessing, vertexGPUProcessing; + + CGLGetParameter (CGLGetCurrentContext(), kCGLCPGPUFragmentProcessing, &fragmentGPUProcessing); + CGLGetParameter(CGLGetCurrentContext(), kCGLCPGPUVertexProcessing, &vertexGPUProcessing); + + // spews then asserts. + // that way you can enable both, get log output on a pair if it's slow, and then the debugger will pop. + if(m_slowSpewEnable) + { + if ( !vertexGPUProcessing ) + { + m_drawingProgram[ kGLMVertexProgram ]->LogSlow( m_drawingLang ); + } + if ( !fragmentGPUProcessing ) + { + m_drawingProgram[ kGLMFragmentProgram ]->LogSlow( m_drawingLang ); + } + } + + if(m_slowAssertEnable) + { + if ( !vertexGPUProcessing || !fragmentGPUProcessing) + { + Assert( !"slow batch" ); + } + } +#else + //Assert( !"impl GLMContext::CheckNative()" ); + + if (m_checkglErrorsAfterEveryBatch) + { + // This is slow, and somewhat redundant (-gldebugoutput uses the GL_ARB_debug_output extension, which can be at least asynchronous), but having a straightforward backup can be useful. + // This is useful for callstack purposes - GL_ARB_debug_output may break in a different thread that the thread triggering the GL error. + //gGL->glFlush(); + GLenum errorcode = (GLenum)gGL->glGetError(); + if ( errorcode != GL_NO_ERROR ) + { + const char *decodedStr = GLMDecode( eGL_ERROR, errorcode ); + + char buf[512]; + V_snprintf( buf, sizeof( buf), "\nGL ERROR! %08x = '%s'\n", errorcode, decodedStr ); + + // Make sure the dev sees something, because these errors can happen early enough that DevMsg() does nothing. +#ifdef WIN32 + OutputDebugStringA( buf ); +#else + printf( "%s", buf ); +#endif + } + } + +#endif + +} + + + +// debug font +void GLMContext::GenDebugFontTex( void ) +{ + if(!m_debugFontTex) + { + // make a 128x128 RGBA texture + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = GL_TEXTURE_2D; + key.m_xSize = 128; + key.m_ySize = 128; + key.m_zSize = 1; + key.m_texFormat = D3DFMT_A8R8G8B8; + key.m_texFlags = 0; + + m_debugFontTex = NewTex( &key, 1, "GLM debug font" ); + + + //----------------------------------------------------- + GLMTexLockParams lockreq; + + lockreq.m_tex = m_debugFontTex; + lockreq.m_face = 0; + lockreq.m_mip = 0; + + GLMTexLayoutSlice *slice = &m_debugFontTex->m_layout->m_slices[ lockreq.m_tex->CalcSliceIndex( lockreq.m_face, lockreq.m_mip ) ]; + + lockreq.m_region.xmin = lockreq.m_region.ymin = lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = slice->m_xSize; + lockreq.m_region.ymax = slice->m_ySize; + lockreq.m_region.zmax = slice->m_zSize; + + lockreq.m_readback = false; + + char *lockAddress; + int yStride; + int zStride; + + m_debugFontTex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + + //----------------------------------------------------- + // fetch elements of font data and make texels... we're doing the whole slab so we don't really need the stride info + unsigned long *destTexelPtr = (unsigned long *)lockAddress; + + for( int index = 0; index < 16384; index++ ) + { + if (g_glmDebugFontMap[index] == ' ') + { + // clear + *destTexelPtr = 0x00000000; + } + else + { + // opaque white (drawing code can modulate if desired) + *destTexelPtr = 0xFFFFFFFF; + } + destTexelPtr++; + } + + //----------------------------------------------------- + GLMTexLockParams unlockreq; + + unlockreq.m_tex = m_debugFontTex; + unlockreq.m_face = 0; + unlockreq.m_mip = 0; + + // region need not matter for unlocks + unlockreq.m_region.xmin = unlockreq.m_region.ymin = unlockreq.m_region.zmin = 0; + unlockreq.m_region.xmax = unlockreq.m_region.ymax = unlockreq.m_region.zmax = 0; + + unlockreq.m_readback = false; + + m_debugFontTex->Unlock( &unlockreq ); + + //----------------------------------------------------- + // change up the tex sampling on this texture to be "nearest" not linear + + //----------------------------------------------------- + + // don't leave texture bound on the TMU + BindTexToTMU( NULL, 0 ); + + // also make the index and vertex buffers for use - up to 1K indices and 1K verts + + uint indexBufferSize = 1024*2; + + m_debugFontIndices = NewBuffer(kGLMIndexBuffer, indexBufferSize, 0); // two byte indices + + // we go ahead and lock it now, and fill it with indices 0-1023. + char *indices = NULL; + GLMBuffLockParams idxLock; + idxLock.m_nOffset = 0; + idxLock.m_nSize = indexBufferSize; + idxLock.m_bNoOverwrite = false; + idxLock.m_bDiscard = true; + m_debugFontIndices->Lock( &idxLock, &indices ); + for( int i=0; i<1024; i++) + { + unsigned short *idxPtr = &((unsigned short*)indices)[i]; + *idxPtr = i; + } + m_debugFontIndices->Unlock(); + + m_debugFontVertices = NewBuffer(kGLMVertexBuffer, 1024 * 128, 0); // up to 128 bytes per vert + } +} + +#define MAX_DEBUG_CHARS 256 +struct GLMDebugTextVertex +{ + float x,y,z; + float u,v; + char rgba[4]; +}; + +void GLMContext::DrawDebugText( float x, float y, float z, float drawCharWidth, float drawCharHeight, char *string ) +{ + if (!m_debugFontTex) + { + GenDebugFontTex(); + } + + // setup needed to draw text + + // we're assuming that +x goes left to right on screen, no billboarding math in here + // and that +y goes bottom up + // caller knows projection / rectangle so it gets to decide vertex spacing + + // debug font must be bound to TMU 0 + // texturing enabled + // alpha blending enabled + // generate a quad per character + // characters are 6px wide by 11 px high. + // upper left character in tex is 0x20 + // y axis will need to be flipped for display + + // for any character in 0x20 - 0x7F - here are the needed UV's + + // leftU = ((character % 16) * 6.0f / 128.0f) + // rightU = lowU + (6.0 / 128.0); + // topV = ((character - 0x20) * 11.0f / 128.0f) + // bottomV = lowV + (11.0f / 128.0f) + + int stringlen = strlen( string ); + if (stringlen > MAX_DEBUG_CHARS) + { + stringlen = MAX_DEBUG_CHARS; + } + + // lock + char *vertices = NULL; + GLMBuffLockParams vtxLock; + vtxLock.m_nOffset = 0; + vtxLock.m_nSize = 1024 * stringlen; + vtxLock.m_bNoOverwrite = false; + vtxLock.m_bDiscard = false; + m_debugFontVertices->Lock( &vtxLock, &vertices ); + + GLMDebugTextVertex *vtx = (GLMDebugTextVertex*)vertices; + GLMDebugTextVertex *vtxOutPtr = vtx; + + for( int charindex = 0; charindex < stringlen; charindex++ ) + { + float leftU,rightU,topV,bottomV; + + int character = (int)string[charindex]; + character -= 0x20; + if ( (character<0) || (character > 0x7F) ) + { + character = '*' - 0x20; + } + + leftU = ((character & 0x0F) * 6.0f ) / 128.0f; + rightU = leftU + (6.0f / 128.0f); + + topV = ((character >> 4) * 11.0f ) / 128.0f; + bottomV = topV + (11.0f / 128.0f); + + float posx,posy,posz; + + posx = x + (drawCharWidth * (float)charindex); + posy = y; + posz = z; + + // generate four verts + // first vert will be upper left of displayed quad (low X, high Y) then we go clockwise + for( int quadvert = 0; quadvert < 4; quadvert++ ) + { + bool isTop = (quadvert <2); // verts 0 and 1 + bool isLeft = (quadvert & 1) == (quadvert >> 1); // verts 0 and 3 + + vtxOutPtr->x = posx + (isLeft ? 0.0f : drawCharWidth); + vtxOutPtr->y = posy + (isTop ? drawCharHeight : 0.0f); + vtxOutPtr->z = posz; + + vtxOutPtr->u = isLeft ? leftU : rightU; + vtxOutPtr->v = isTop ? topV : bottomV; + + vtxOutPtr++; + } + } + + // verts are done. + // unlock... + + m_debugFontVertices->Unlock(); + + // make a vertex setup + GLMVertexSetup vertSetup; + + // position, color, tc = 0, 3, 8 + vertSetup.m_attrMask = (1<<kGLMGenericAttr00) | (1<<kGLMGenericAttr03) | (1<<kGLMGenericAttr08); + + vertSetup.m_attrs[kGLMGenericAttr00].m_pBuffer = m_debugFontVertices; + vertSetup.m_attrs[kGLMGenericAttr00].m_nCompCount = 3; // 3 floats + vertSetup.m_attrs[kGLMGenericAttr00].m_datatype = GL_FLOAT; + vertSetup.m_attrs[kGLMGenericAttr00].m_stride = sizeof(GLMDebugTextVertex); + vertSetup.m_attrs[kGLMGenericAttr00].m_offset = offsetof(GLMDebugTextVertex, x); + vertSetup.m_attrs[kGLMGenericAttr00].m_normalized= false; + + vertSetup.m_attrs[kGLMGenericAttr03].m_pBuffer = m_debugFontVertices; + vertSetup.m_attrs[kGLMGenericAttr03].m_nCompCount = 4; // four bytes + vertSetup.m_attrs[kGLMGenericAttr03].m_datatype = GL_UNSIGNED_BYTE; + vertSetup.m_attrs[kGLMGenericAttr03].m_stride = sizeof(GLMDebugTextVertex); + vertSetup.m_attrs[kGLMGenericAttr03].m_offset = offsetof(GLMDebugTextVertex, rgba); + vertSetup.m_attrs[kGLMGenericAttr03].m_normalized= true; + + vertSetup.m_attrs[kGLMGenericAttr08].m_pBuffer = m_debugFontVertices; + vertSetup.m_attrs[kGLMGenericAttr08].m_nCompCount = 2; // 2 floats + vertSetup.m_attrs[kGLMGenericAttr08].m_datatype = GL_FLOAT; + vertSetup.m_attrs[kGLMGenericAttr08].m_stride = sizeof(GLMDebugTextVertex); + vertSetup.m_attrs[kGLMGenericAttr08].m_offset = offsetof(GLMDebugTextVertex, u); + vertSetup.m_attrs[kGLMGenericAttr03].m_normalized= false; + + + // bind texture and draw it.. + CGLMTex *pPrevTex = m_samplers[0].m_pBoundTex; + BindTexToTMU( m_debugFontTex, 0 ); + + SelectTMU(0); // somewhat redundant + + gGL->glDisable( GL_DEPTH_TEST ); + + gGL->glEnable(GL_TEXTURE_2D); + + if (0) + { + gGL->glEnableClientState(GL_VERTEX_ARRAY); + + gGL->glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + gGL->glVertexPointer( 3, GL_FLOAT, sizeof( vtx[0] ), &vtx[0].x ); + + gGL->glClientActiveTexture(GL_TEXTURE0); + + gGL->glTexCoordPointer( 2, GL_FLOAT, sizeof( vtx[0] ), &vtx[0].u ); + } + else + { + SetVertexAttributes( &vertSetup ); + } + + gGL->glDrawArrays( GL_QUADS, 0, stringlen * 4 ); + + // disable all the input streams + if (0) + { + gGL->glDisableClientState(GL_VERTEX_ARRAY); + + gGL->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + else + { + SetVertexAttributes( NULL ); + } + + gGL->glDisable(GL_TEXTURE_2D); + + BindTexToTMU( pPrevTex, 0 ); +} + +//=============================================================================== + +void GLMgrSelfTests( void ) +{ + return; // until such time as the tests are revised or axed + + GLMDisplayParams glmParams; + glmParams.m_fsEnable = false; + + glmParams.m_vsyncEnable = false; // "The runtime updates the window client area immediately and might do so more + glmParams.m_backBufferWidth = 1024; + glmParams.m_backBufferHeight = 768; + glmParams.m_backBufferFormat = D3DFMT_A8R8G8B8; + glmParams.m_multiSampleCount = 2; + + glmParams.m_enableAutoDepthStencil = true; + glmParams.m_autoDepthStencilFormat = D3DFMT_D24S8; + + glmParams.m_fsRefreshHz = 60; + + glmParams.m_mtgl = true; + glmParams.m_focusWindow = 0; + + // make a new context on renderer 0. + GLMContext *ctx = GLMgr::aGLMgr()->NewContext( NULL, &glmParams ); ////FIXME you can't make contexts this way any more. + if (!ctx) + { + DebuggerBreak(); // no go + return; + } + + // make a test object based on that context. + //int alltests[] = {0,1,2,3, -1}; + //int newtests[] = {3, -1}; + int twotests[] = {2, -1}; + //int notests[] = {-1}; + + int *testlist = twotests; + + GLMTestParams params; + memset( ¶ms, 0, sizeof(params) ); + + params.m_ctx = ctx; + params.m_testList = testlist; + + params.m_glErrToDebugger = true; + params.m_glErrToConsole = true; + + params.m_intlErrToDebugger = true; + params.m_intlErrToConsole = true; + + params.m_frameCount = 1000; + + GLMTester testobj( ¶ms ); + + testobj.RunTests( ); + + GLMgr::aGLMgr()->DelContext( ctx ); +} + +void GLMContext::SetDefaultStates( void ) +{ + GLM_FUNC; + CheckCurrent(); + + m_AlphaTestEnable.Default(); + m_AlphaTestFunc.Default(); + + m_AlphaToCoverageEnable.Default(); + + m_CullFaceEnable.Default(); + m_CullFrontFace.Default(); + + m_PolygonMode.Default(); + m_DepthBias.Default(); + + m_ClipPlaneEnable.Default(); + m_ClipPlaneEquation.Default(); + + m_ScissorEnable.Default(); + m_ScissorBox.Default(); + + m_ViewportBox.Default(); + m_ViewportDepthRange.Default(); + + m_ColorMaskSingle.Default(); + m_ColorMaskMultiple.Default(); + + m_BlendEnable.Default(); + m_BlendFactor.Default(); + m_BlendEquation.Default(); + m_BlendColor.Default(); + //m_BlendEnableSRGB.Default(); // this isn't useful until there is an FBO bound - in fact it will trip a GL error. + + m_DepthTestEnable.Default(); + m_DepthFunc.Default(); + m_DepthMask.Default(); + + m_StencilTestEnable.Default(); + m_StencilFunc.Default(); + m_StencilOp.Default(); + m_StencilWriteMask.Default(); + + m_ClearColor.Default(); + m_ClearDepth.Default(); + m_ClearStencil.Default(); +} + +void GLMContext::VerifyStates ( void ) +{ + GLM_FUNC; + CheckCurrent(); + + // bare bones sanity check, head over to the debugger if our sense of the current context state is not correct + // we should only want to call this after a flush or the checks will flunk. + + if( m_AlphaTestEnable.Check() ) GLMStop(); + if( m_AlphaTestFunc.Check() ) GLMStop(); + + if( m_AlphaToCoverageEnable.Check() ) GLMStop(); + + if( m_CullFaceEnable.Check() ) GLMStop(); + if( m_CullFrontFace.Check() ) GLMStop(); + + if( m_PolygonMode.Check() ) GLMStop(); + if( m_DepthBias.Check() ) GLMStop(); + + if( m_ClipPlaneEnable.Check() ) GLMStop(); + //if( m_ClipPlaneEquation.Check() ) GLMStop(); + + if( m_ScissorEnable.Check() ) GLMStop(); + if( m_ScissorBox.Check() ) GLMStop(); + + + if( m_ViewportBox.Check() ) GLMStop(); + if( m_ViewportDepthRange.Check() ) GLMStop(); + + if( m_ColorMaskSingle.Check() ) GLMStop(); + if( m_ColorMaskMultiple.Check() ) GLMStop(); + + if( m_BlendEnable.Check() ) GLMStop(); + if( m_BlendFactor.Check() ) GLMStop(); + if( m_BlendEquation.Check() ) GLMStop(); + if( m_BlendColor.Check() ) GLMStop(); + + // only do this as caps permit + if (m_caps.m_hasGammaWrites) + { + if( m_BlendEnableSRGB.Check() ) GLMStop(); + } + + if( m_DepthTestEnable.Check() ) GLMStop(); + if( m_DepthFunc.Check() ) GLMStop(); + if( m_DepthMask.Check() ) GLMStop(); + + if( m_StencilTestEnable.Check() ) GLMStop(); + if( m_StencilFunc.Check() ) GLMStop(); + if( m_StencilOp.Check() ) GLMStop(); + if( m_StencilWriteMask.Check() ) GLMStop(); + + if( m_ClearColor.Check() ) GLMStop(); + if( m_ClearDepth.Check() ) GLMStop(); + if( m_ClearStencil.Check() ) GLMStop(); +} + +static inline uint GetDataTypeSizeInBytes( GLenum dataType ) +{ + switch ( dataType ) + { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + case GL_INT: + case GL_FLOAT: + return 4; + default: + Assert( 0 ); + break; + } + return 0; +} + +#ifndef OSX + +void GLMContext::DrawRangeElementsNonInline( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf ) +{ +#if GLMDEBUG + GLM_FUNC; +#else + //tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d-%d count:%d mode:%d type:%d", __FUNCTION__, start, end, count, mode, type ); +#endif + + ++m_nBatchCounter; + + SetIndexBuffer( pIndexBuf ); + + void *indicesActual = (void*)indices; + + if ( pIndexBuf->m_bPseudo ) + { + // you have to pass actual address, not offset + indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_pPseudoBuf ); + } + if (pIndexBuf->m_bUsingPersistentBuffer) + { + indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_nPersistentBufferStartOffset ); + } + +#if GL_ENABLE_INDEX_VERIFICATION + // Obviously only for debugging. + if ( !pIndexBuf->IsSpanValid( (uint)indices, count * GetDataTypeSizeInBytes( type ) ) ) + { + // The consumption range crosses more than one lock span, or the lock is trying to consume a bad IB range. + DXABSTRACT_BREAK_ON_ERROR(); + } + + if ( ( type == GL_UNSIGNED_SHORT ) && ( pIndexBuf->m_bPseudo ) ) + { + Assert( start <= end ); + for ( int i = 0; i < count; i++) + { + uint n = ((const uint16*)indicesActual)[i]; + if ( ( n < start ) || ( n > end ) ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + } + + unsigned char *pVertexShaderAttribMap = m_pDevice->m_vertexShader->m_vtxAttribMap; + const int nMaxVertexAttributesToCheck = m_drawingProgram[ kGLMVertexProgram ]->m_maxVertexAttrs; + + IDirect3DVertexDeclaration9 *pVertDecl = m_pDevice->m_pVertDecl; + const uint8 *pVertexAttribDescToStreamIndex = pVertDecl->m_VertexAttribDescToStreamIndex; + + // FIXME: Having to duplicate all this flush logic is terrible here + for( int nMask = 1, nIndex = 0; nIndex < nMaxVertexAttributesToCheck; ++nIndex, nMask <<= 1 ) + { + uint8 vertexShaderAttrib = pVertexShaderAttribMap[ nIndex ]; + + uint nDeclIndex = pVertexAttribDescToStreamIndex[vertexShaderAttrib]; + if ( nDeclIndex == 0xFF ) + continue; + + D3DVERTEXELEMENT9_GL *pDeclElem = &pVertDecl->m_elements[nDeclIndex]; + + Assert( ( ( vertexShaderAttrib >> 4 ) == pDeclElem->m_dxdecl.Usage ) && ( ( vertexShaderAttrib & 0x0F ) == pDeclElem->m_dxdecl.UsageIndex) ); + + const uint nStreamIndex = pDeclElem->m_dxdecl.Stream; + const D3DStreamDesc *pStream = &m_pDevice->m_streams[ nStreamIndex ]; + + CGLMBuffer *pBuf = m_pDevice->m_vtx_buffers[ nStreamIndex ]; + if ( pBuf == m_pDevice->m_pDummy_vtx_buffer ) + continue; + + Assert( pStream->m_vtxBuffer->m_vtxBuffer == pBuf ); + + int nBufOffset = pDeclElem->m_gldecl.m_offset + pStream->m_offset; + Assert( nBufOffset >= 0 ); + Assert( nBufOffset < (int)pBuf->m_nSize ); + + uint nBufSize = pStream->m_vtxBuffer->m_vtxBuffer->m_nSize; + uint nDataTypeSize = GetDataTypeSizeInBytes( pDeclElem->m_gldecl.m_datatype ); + uint nActualStride = pStream->m_stride ? pStream->m_stride : nDataTypeSize; + uint nStart = nBufOffset + ( start + baseVertex ) * nActualStride; + uint nEnd = nBufOffset + ( end + baseVertex ) * nActualStride + nDataTypeSize; + + if ( nEnd > nBufSize ) + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + if ( !pStream->m_vtxBuffer->m_vtxBuffer->IsSpanValid( nStart, nEnd - nStart ) ) + { + // The draw is trying to consume a range of the bound VB that hasn't been set to valid data! + DXABSTRACT_BREAK_ON_ERROR(); + } + } + } +#endif + + Assert( m_drawingLang == kGLMGLSL ); + + if ( m_pBoundPair ) + { + gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex ); + +#if GLMDEBUG + if ( m_slowCheckEnable ) + { + CheckNative(); + } +#endif + } +} + +#else + +// support for OSX 10.6 (no support for glDrawRangeElementsBaseVertex) +void GLMContext::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, CGLMBuffer *pIndexBuf) +{ + GLM_FUNC; + + // CheckCurrent(); + ++m_nBatchCounter; // batch index increments unconditionally on entry + + SetIndexBuffer( pIndexBuf ); + void *indicesActual = (void*)indices; + if ( pIndexBuf->m_bPseudo ) + { + // you have to pass actual address, not offset + indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_pPseudoBuf ); + } + if (pIndexBuf->m_bUsingPersistentBuffer) + { + indicesActual = (void*)( (int)indicesActual + (int)pIndexBuf->m_nPersistentBufferStartOffset ); + } + +#if GLMDEBUG + // init debug hook information + GLMDebugHookInfo info; + memset(&info, 0, sizeof(info)); + info.m_caller = eDrawElements; + + // relay parameters we're operating under + info.m_drawMode = mode; + info.m_drawStart = start; + info.m_drawEnd = end; + info.m_drawCount = count; + info.m_drawType = type; + info.m_drawIndices = indices; + + do + { + // obey global options re pre-draw clear + if (m_autoClearColor || m_autoClearDepth || m_autoClearStencil) + { + GLMPRINTF(("-- DrawRangeElements auto clear")); + this->DebugClear(); + } + + // always sync with editable shader text prior to draw +#if GLMDEBUG + // TODO - fixup OSX 10.6 m_boundProg not used in this version togl (m_pBoundPair) + //FIXME disengage this path if context is in GLSL mode.. + // it will need fixes to get the shader pair re-linked etc if edits happen anyway. + + if (m_boundProgram[kGLMVertexProgram]) + { + m_boundProgram[kGLMVertexProgram]->SyncWithEditable(); + } + else + { + AssertOnce(!"drawing with no vertex program bound"); + } + + if (m_boundProgram[kGLMFragmentProgram]) + { + m_boundProgram[kGLMFragmentProgram]->SyncWithEditable(); + } + else + { + AssertOnce(!"drawing with no fragment program bound"); + } +#endif + + // do the drawing + if ( m_pBoundPair ) + { + gGL->glDrawRangeElements(mode, start, end, count, type, indicesActual); + // GLMCheckError(); + + if (m_slowCheckEnable) + { + CheckNative(); + } + } + this->DebugHook(&info); + } while (info.m_loop); +#else + if ( m_pBoundPair ) + { + gGL->glDrawRangeElements(mode, start, end, count, type, indicesActual); + +#if GLMDEBUG + if ( m_slowCheckEnable ) + { + CheckNative(); + } +#endif + } +#endif +} + +#endif // !OSX + +#if 0 +// helper function to do enable or disable in one step +void glSetEnable( GLenum which, bool enable ) +{ + if (enable) + gGL->glEnable(which); + else + gGL->glDisable(which); +} + +// helper function for int vs enum clarity +void glGetEnumv( GLenum which, GLenum *dst ) +{ + gGL->glGetIntegerv( which, (int*)dst ); +} +#endif + +//=============================================================================== + + +GLMTester::GLMTester(GLMTestParams *params) +{ + m_params = *params; + + m_drawFBO = NULL; + m_drawColorTex = NULL; + m_drawDepthTex = NULL; +} + +GLMTester::~GLMTester() +{ +} + +void GLMTester::StdSetup( void ) +{ + GLMContext *ctx = m_params.m_ctx; + + m_drawWidth = 1024; + m_drawHeight = 768; + + // make an FBO to draw into and activate it. no depth buffer yet + m_drawFBO = ctx->NewFBO(); + + // make color buffer texture + + GLMTexLayoutKey colorkey; + //CGLMTex *colortex; + memset( &colorkey, 0, sizeof(colorkey) ); + + colorkey.m_texGLTarget = GL_TEXTURE_2D; + colorkey.m_xSize = m_drawWidth; + colorkey.m_ySize = m_drawHeight; + colorkey.m_zSize = 1; + + colorkey.m_texFormat = D3DFMT_A8R8G8B8; + colorkey.m_texFlags = kGLMTexRenderable; + + m_drawColorTex = ctx->NewTex( &colorkey ); + + // do not leave that texture bound on the TMU + ctx->BindTexToTMU(NULL, 0 ); + + + // attach color to FBO + GLMFBOTexAttachParams colorParams; + memset( &colorParams, 0, sizeof(colorParams) ); + + colorParams.m_tex = m_drawColorTex; + colorParams.m_face = 0; + colorParams.m_mip = 0; + colorParams.m_zslice= 0; // for clarity.. + + m_drawFBO->TexAttach( &colorParams, kAttColor0 ); + + // check it. + bool ready = m_drawFBO->IsReady(); + InternalError( !ready, "drawing FBO no go"); + + // bind it + ctx->BindFBOToCtx( m_drawFBO, GL_FRAMEBUFFER_EXT ); + + gGL->glViewport(0, 0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + CheckGLError("stdsetup viewport"); + + gGL->glScissor( 0,0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + CheckGLError("stdsetup scissor"); + + gGL->glOrtho( -1,1, -1,1, -1,1 ); + CheckGLError("stdsetup ortho"); + + // activate debug font + ctx->GenDebugFontTex(); +} + +void GLMTester::StdCleanup( void ) +{ + GLMContext *ctx = m_params.m_ctx; + + // unbind + ctx->BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // del FBO + if (m_drawFBO) + { + ctx->DelFBO( m_drawFBO ); + m_drawFBO = NULL; + } + + // del tex + if (m_drawColorTex) + { + ctx->DelTex( m_drawColorTex ); + m_drawColorTex = NULL; + } + + if (m_drawDepthTex) + { + ctx->DelTex( m_drawDepthTex ); + m_drawDepthTex = NULL; + } +} + + +void GLMTester::Clear( void ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + gGL->glViewport(0, 0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + gGL->glScissor( 0,0, (GLsizei) m_drawWidth, (GLsizei) m_drawHeight ); + gGL->glOrtho( -1,1, -1,1, -1,1 ); + CheckGLError("clearing viewport"); + + // clear to black + gGL->glClearColor(0.0f, 0.0f, 0.0, 1.0f); + CheckGLError("clearing color"); + + gGL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + CheckGLError("clearing"); + + //glFinish(); + //CheckGLError("clear finish"); +} + +void GLMTester::Present( int seed ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->Present( m_drawColorTex ); +} + +void GLMTester::CheckGLError( const char *comment ) +{ +return; + char errbuf[1024]; + + //borrowed from GLMCheckError.. slightly different + + if (!comment) + { + comment = ""; + } + + GLenum errorcode = (GLenum)gGL->glGetError(); + GLenum errorcode2 = 0; + if ( errorcode != GL_NO_ERROR ) + { + const char *decodedStr = GLMDecode( eGL_ERROR, errorcode ); + const char *decodedStr2 = ""; + + if ( errorcode == GL_INVALID_FRAMEBUFFER_OPERATION_EXT ) + { + // dig up the more detailed FBO status + errorcode2 = gGL->glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); + + decodedStr2 = GLMDecode( eGL_ERROR, errorcode2 ); + + sprintf( errbuf, "\n%s - GL Error %08x/%08x = '%s / %s'\n", comment, errorcode, errorcode2, decodedStr, decodedStr2 ); + } + else + { + sprintf( errbuf, "\n%s - GL Error %08x = '%s'\n", comment, errorcode, decodedStr ); + } + + if ( m_params.m_glErrToConsole ) + { + printf("%s", errbuf ); + } + + if ( m_params.m_glErrToDebugger ) + { + DebuggerBreak(); + } + } +} + +void GLMTester::InternalError( int errcode, char *comment ) +{ + if (errcode) + { + if (m_params.m_intlErrToConsole) + { + printf("%s - error %d\n", comment, errcode ); + } + + if (m_params.m_intlErrToDebugger) + { + DebuggerBreak(); + } + } +} + + +void GLMTester::RunTests( void ) +{ + int *testList = m_params.m_testList; + + while( (*testList >=0) && (*testList < 20) ) + { + RunOneTest( *testList++ ); + } +} + +void GLMTester::RunOneTest( int testindex ) +{ + // this might be better with 'ptmf' style + switch(testindex) + { + case 0: Test0(); break; + case 1: Test1(); break; + case 2: Test2(); break; + case 3: Test3(); break; + + default: + DebuggerBreak(); // unrecognized + } +} + +// ##################################################################################################################### + +// some fixed lists which may be useful to all tests + +D3DFORMAT g_drawTexFormatsGLMT[] = // -1 terminated +{ + D3DFMT_A8R8G8B8, + D3DFMT_A4R4G4B4, + D3DFMT_X8R8G8B8, + D3DFMT_X1R5G5B5, + D3DFMT_A1R5G5B5, + D3DFMT_L8, + D3DFMT_A8L8, + D3DFMT_R8G8B8, + D3DFMT_A8, + D3DFMT_R5G6B5, + D3DFMT_DXT1, + D3DFMT_DXT3, + D3DFMT_DXT5, + D3DFMT_A32B32G32R32F, + D3DFMT_A16B16G16R16, + + (D3DFORMAT)-1 +}; + +D3DFORMAT g_fboColorTexFormatsGLMT[] = // -1 terminated +{ + D3DFMT_A8R8G8B8, + //D3DFMT_A4R4G4B4, //unsupported + D3DFMT_X8R8G8B8, + D3DFMT_X1R5G5B5, + //D3DFMT_A1R5G5B5, //unsupported + D3DFMT_A16B16G16R16F, + D3DFMT_A32B32G32R32F, + D3DFMT_R5G6B5, + + (D3DFORMAT)-1 +}; + +D3DFORMAT g_fboDepthTexFormatsGLMT[] = // -1 terminated, but note 0 for "no depth" mode +{ + (D3DFORMAT)0, + D3DFMT_D16, + D3DFMT_D24X8, + D3DFMT_D24S8, + + (D3DFORMAT)-1 +}; + + +// ##################################################################################################################### + +void GLMTester::Test0( void ) +{ + // make and delete a bunch of textures. + // lock and unlock them. + // use various combos of - + + // √texel format + // √2D | 3D | cube map + // √mipped / not + // √POT / NPOT + // large / small / square / rect + // square / rect + + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + CUtlVector< CGLMTex* > testTextures; // will hold all the built textures + + // test stage loop + // 0 is creation + // 1 is lock/unlock + // 2 is deletion + + for( int teststage = 0; teststage < 3; teststage++) + { + int innerindex = 0; // increment at stage switch + // format loop + for( D3DFORMAT *fmtPtr = g_drawTexFormatsGLMT; *fmtPtr != ((D3DFORMAT)-1); fmtPtr++ ) + { + // form loop + GLenum forms[] = { GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, (GLenum)-1 }; + + for( GLenum *formPtr = forms; *formPtr != ((GLenum)-1); formPtr++ ) + { + // mip loop + for( int mipped = 0; mipped < 2; mipped++ ) + { + // large / square / pot loop + // &4 == large &2 == square &1 == POT + // NOTE you *have to be square* for cube maps. + + for( int aspect = 0; aspect < 8; aspect++ ) + { + switch( teststage ) + { + case 0: + { + GLMTexLayoutKey key; + memset( &key, 0, sizeof(key) ); + + key.m_texGLTarget = *formPtr; + key.m_texFormat = *fmtPtr; + if (mipped) + key.m_texFlags |= kGLMTexMipped; + + // assume big, square, POT, and 3D, then adjust as needed + key.m_xSize = key.m_ySize = key.m_zSize = 256; + + if ( !(aspect&4) ) // big or little ? + { + // little + key.m_xSize >>= 2; + key.m_ySize >>= 2; + key.m_zSize >>= 2; + } + + if ( key.m_texGLTarget != GL_TEXTURE_CUBE_MAP ) + { + if ( !(aspect & 2) ) // square or rect? + { + // rect + key.m_ySize >>= 1; + key.m_zSize >>= 2; + } + } + + if ( !(aspect&1) ) // POT or NPOT? + { + // NPOT + key.m_xSize += 56; + key.m_ySize += 56; + key.m_zSize += 56; + } + + // 2D, 3D, cube map ? + if (key.m_texGLTarget!=GL_TEXTURE_3D) + { + // 2D or cube map: flatten Z extent to one texel + key.m_zSize = 1; + } + else + { + // 3D: knock down Z quite a bit so our test case does not run out of RAM + key.m_zSize >>= 3; + if (!key.m_zSize) + { + key.m_zSize = 1; + } + } + + CGLMTex *newtex = ctx->NewTex( &key ); + CheckGLError( "tex create test"); + InternalError( newtex==NULL, "tex create test" ); + + testTextures.AddToTail( newtex ); + printf("\n[%5d] created tex %s",innerindex,newtex->m_layout->m_layoutSummary ); + } + break; + + case 1: + { + CGLMTex *ptex = testTextures[innerindex]; + + for( int face=0; face <ptex->m_layout->m_faceCount; face++) + { + for( int mip=0; mip <ptex->m_layout->m_mipCount; mip++) + { + GLMTexLockParams lockreq; + + lockreq.m_tex = ptex; + lockreq.m_face = face; + lockreq.m_mip = mip; + + GLMTexLayoutSlice *slice = &ptex->m_layout->m_slices[ ptex->CalcSliceIndex( face, mip ) ]; + + lockreq.m_region.xmin = lockreq.m_region.ymin = lockreq.m_region.zmin = 0; + lockreq.m_region.xmax = slice->m_xSize; + lockreq.m_region.ymax = slice->m_ySize; + lockreq.m_region.zmax = slice->m_zSize; + + char *lockAddress; + int yStride; + int zStride; + + ptex->Lock( &lockreq, &lockAddress, &yStride, &zStride ); + CheckGLError( "tex lock test"); + InternalError( lockAddress==NULL, "null lock address"); + + // write some texels of this flavor: + // red 75% green 40% blue 15% alpha 80% + + GLMGenTexelParams gtp; + + gtp.m_format = ptex->m_layout->m_format->m_d3dFormat; + gtp.m_dest = lockAddress; + gtp.m_chunkCount = (slice->m_xSize * slice->m_ySize * slice->m_zSize) / (ptex->m_layout->m_format->m_chunkSize * ptex->m_layout->m_format->m_chunkSize); + gtp.m_byteCountLimit = slice->m_storageSize; + gtp.r = 0.75; + gtp.g = 0.40; + gtp.b = 0.15; + gtp.a = 0.80; + + GLMGenTexels( >p ); + + InternalError( gtp.m_bytesWritten != gtp.m_byteCountLimit, "byte count mismatch from GLMGenTexels" ); + } + } + + for( int face=0; face <ptex->m_layout->m_faceCount; face++) + { + for( int mip=0; mip <ptex->m_layout->m_mipCount; mip++) + { + GLMTexLockParams unlockreq; + + unlockreq.m_tex = ptex; + unlockreq.m_face = face; + unlockreq.m_mip = mip; + + // region need not matter for unlocks + unlockreq.m_region.xmin = unlockreq.m_region.ymin = unlockreq.m_region.zmin = 0; + unlockreq.m_region.xmax = unlockreq.m_region.ymax = unlockreq.m_region.zmax = 0; + + //char *lockAddress; + //int yStride; + //int zStride; + + ptex->Unlock( &unlockreq ); + + CheckGLError( "tex unlock test"); + } + } + printf("\n[%5d] locked/wrote/unlocked tex %s",innerindex, ptex->m_layout->m_layoutSummary ); + } + break; + + case 2: + { + CGLMTex *dtex = testTextures[innerindex]; + + printf("\n[%5d] deleting tex %s",innerindex, dtex->m_layout->m_layoutSummary ); + ctx->DelTex( dtex ); + CheckGLError( "tex delete test"); + } + break; + } // end stage switch + innerindex++; + } // end aspect loop + } // end mip loop + } // end form loop + } // end format loop + } // end stage loop +} + +// ##################################################################################################################### +void GLMTester::Test1( void ) +{ + // FBO exercises + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + // FBO color format loop + for( D3DFORMAT *colorFmtPtr = g_fboColorTexFormatsGLMT; *colorFmtPtr != ((D3DFORMAT)-1); colorFmtPtr++ ) + { + // FBO depth format loop + for( D3DFORMAT *depthFmtPtr = g_fboDepthTexFormatsGLMT; *depthFmtPtr != ((D3DFORMAT)-1); depthFmtPtr++ ) + { + // mip loop + for( int mipped = 0; mipped < 2; mipped++ ) + { + GLenum forms[] = { GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, (GLenum)-1 }; + + // form loop + for( GLenum *formPtr = forms; *formPtr != ((GLenum)-1); formPtr++ ) + { + //=============================================== make an FBO + CGLMFBO *fbo = ctx->NewFBO(); + + //=============================================== make a color texture + GLMTexLayoutKey colorkey; + memset( &colorkey, 0, sizeof(colorkey) ); + + switch(*formPtr) + { + case GL_TEXTURE_2D: + colorkey.m_texGLTarget = GL_TEXTURE_2D; + colorkey.m_xSize = 800; + colorkey.m_ySize = 600; + colorkey.m_zSize = 1; + break; + + case GL_TEXTURE_3D: + colorkey.m_texGLTarget = GL_TEXTURE_3D; + colorkey.m_xSize = 800; + colorkey.m_ySize = 600; + colorkey.m_zSize = 32; + break; + + case GL_TEXTURE_CUBE_MAP: + colorkey.m_texGLTarget = GL_TEXTURE_CUBE_MAP; + colorkey.m_xSize = 800; + colorkey.m_ySize = 800; // heh, cube maps have to have square sides... + colorkey.m_zSize = 1; + break; + } + + colorkey.m_texFormat = *colorFmtPtr; + colorkey.m_texFlags = kGLMTexRenderable; + // decide if we want mips + if (mipped) + { + colorkey.m_texFlags |= kGLMTexMipped; + } + + CGLMTex *colorTex = ctx->NewTex( &colorkey ); + // Note that GLM will notice the renderable flag, and force texels to be written + // so the FBO will be complete + + //=============================================== attach color + GLMFBOTexAttachParams colorParams; + memset( &colorParams, 0, sizeof(colorParams) ); + + colorParams.m_tex = colorTex; + colorParams.m_face = (colorkey.m_texGLTarget == GL_TEXTURE_CUBE_MAP) ? 2 : 0; // just steer to an alternate face as a test + + colorParams.m_mip = (colorkey.m_texFlags & kGLMTexMipped) ? 2 : 0; // pick non-base mip slice + + colorParams.m_zslice= (colorkey.m_texGLTarget == GL_TEXTURE_3D) ? 3 : 0; // just steer to an alternate slice as a test; + + fbo->TexAttach( &colorParams, kAttColor0 ); + + + //=============================================== optional depth tex + CGLMTex *depthTex = NULL; + + if (*depthFmtPtr > 0 ) + { + GLMTexLayoutKey depthkey; + memset( &depthkey, 0, sizeof(depthkey) ); + + depthkey.m_texGLTarget = GL_TEXTURE_2D; + depthkey.m_xSize = colorkey.m_xSize >> colorParams.m_mip; // scale depth tex to match color tex + depthkey.m_ySize = colorkey.m_ySize >> colorParams.m_mip; + depthkey.m_zSize = 1; + + depthkey.m_texFormat = *depthFmtPtr; + depthkey.m_texFlags = kGLMTexRenderable | kGLMTexIsDepth; // no mips. + if (depthkey.m_texFormat==D3DFMT_D24S8) + { + depthkey.m_texFlags |= kGLMTexIsStencil; + } + + depthTex = ctx->NewTex( &depthkey ); + + + //=============================================== attach depth + GLMFBOTexAttachParams depthParams; + memset( &depthParams, 0, sizeof(depthParams) ); + + depthParams.m_tex = depthTex; + depthParams.m_face = 0; + depthParams.m_mip = 0; + depthParams.m_zslice= 0; + + EGLMFBOAttachment depthAttachIndex = (depthkey.m_texFlags & kGLMTexIsStencil) ? kAttDepthStencil : kAttDepth; + fbo->TexAttach( &depthParams, depthAttachIndex ); + } + + printf("\n FBO:\n color tex %s\n depth tex %s", + colorTex->m_layout->m_layoutSummary, + depthTex ? depthTex->m_layout->m_layoutSummary : "none" + ); + + // see if FBO is happy + bool ready = fbo->IsReady(); + + printf("\n -> %s\n", ready ? "pass" : "fail" ); + + // unbind + ctx->BindFBOToCtx( NULL, GL_FRAMEBUFFER_EXT ); + + // del FBO + ctx->DelFBO(fbo); + + // del texes + ctx->DelTex( colorTex ); + if (depthTex) ctx->DelTex( depthTex ); + } // end form loop + } // end mip loop + } // end depth loop + } // end color loop +} + +// ##################################################################################################################### + +static int selftest2_seed = 0; // inc this every run to force main thread to teardown/reset display view +void GLMTester::Test2( void ) +{ + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + StdSetup(); // default test case drawing setup + + // draw stuff (loop...) + for( int i=0; i<m_params.m_frameCount; i++) + { + // ramping shades of blue... + GLfloat clear_color[4] = { 0.50f, 0.05f, ((float)(i%100)) / 100.0f, 1.0f }; + gGL->glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + CheckGLError("test2 clear color"); + + gGL->glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT); + CheckGLError("test2 clearing"); + + // try out debug text + for( int j=0; j<16; j++) + { + char text[256]; + sprintf(text, "The quick brown fox jumped over the lazy dog %d times", i ); + + float theta = ( (i*0.10f) + (j * 6.28f) ) / 16.0f; + + float posx = cos(theta) * 0.5; + float posy = sin(theta) * 0.5; + + float charwidth = 6.0 * (2.0 / 1024.0); + float charheight = 11.0 * (2.0 / 768.0); + + ctx->DrawDebugText( posx, posy, 0.0f, charwidth, charheight, text ); + } + gGL->glFinish(); + CheckGLError("test2 finish"); + + Present( selftest2_seed ); + } + + StdCleanup(); + + selftest2_seed++; +} + +// ##################################################################################################################### + +static char g_testVertexProgram01 [] = +{ + "!!ARBvp1.0 \n" + "TEMP vertexClip; \n" + "DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.position; \n" + "DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.position; \n" + "DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.position; \n" + "DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.position; \n" + "ADD vertexClip.y, vertexClip.x, vertexClip.y; \n" + "MOV result.position, vertexClip; \n" + "MOV result.color, vertex.color; \n" + "MOV result.texcoord[0], vertex.texcoord; \n" + "END \n" +}; + +static char g_testFragmentProgram01 [] = +{ + "!!ARBfp1.0 \n" + "TEMP color; \n" + "MUL color, fragment.texcoord[0].y, 2.0; \n" + "ADD color, 1.0, -color; \n" + "ABS color, color; \n" + "ADD result.color, 1.0, -color; \n" + "MOV result.color.a, 1.0; \n" + "END \n" +}; + + +// generic attrib versions.. + +static char g_testVertexProgram01_GA [] = +{ + "!!ARBvp1.0 \n" + "TEMP vertexClip; \n" + "DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.attrib[0]; \n" + "DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.attrib[0]; \n" + "DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.attrib[0]; \n" + "DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.attrib[0]; \n" + "ADD vertexClip.y, vertexClip.x, vertexClip.y; \n" + "MOV result.position, vertexClip; \n" + "MOV result.color, vertex.attrib[3]; \n" + "MOV result.texcoord[0], vertex.attrib[8]; \n" + "END \n" +}; + +static char g_testFragmentProgram01_GA [] = +{ + "!!ARBfp1.0 \n" + "TEMP color; \n" + "TEX color, fragment.texcoord[0], texture[0], 2D;" + //"MUL color, fragment.texcoord[0].y, 2.0; \n" + //"ADD color, 1.0, -color; \n" + //"ABS color, color; \n" + //"ADD result.color, 1.0, -color; \n" + //"MOV result.color.a, 1.0; \n" + "MOV result.color, color; \n" + "END \n" +}; + + +void GLMTester::Test3( void ) +{ + /************************** + XXXXXXXXXXXXXXXXXXXXXX stale test code until we revise the program interface + + GLMContext *ctx = m_params.m_ctx; + ctx->MakeCurrent(); + + StdSetup(); // default test case drawing setup + + // make vertex&pixel shader + CGLMProgram *vprog = ctx->NewProgram( kGLMVertexProgram, g_testVertexProgram01_GA ); + ctx->BindProgramToCtx( kGLMVertexProgram, vprog ); + + CGLMProgram *fprog = ctx->NewProgram( kGLMFragmentProgram, g_testFragmentProgram01_GA ); + ctx->BindProgramToCtx( kGLMFragmentProgram, fprog ); + + // draw stuff (loop...) + for( int i=0; i<m_params.m_frameCount; i++) + { + // ramping shades of blue... + GLfloat clear_color[4] = { 0.50f, 0.05f, ((float)(i%100)) / 100.0, 1.0f }; + glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + CheckGLError("test3 clear color"); + + glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT); + CheckGLError("test3 clearing"); + + // try out debug text + for( int j=0; j<16; j++) + { + char text[256]; + sprintf(text, "This here is running through a trivial vertex shader"); + + float theta = ( (i*0.10f) + (j * 6.28f) ) / 16.0f; + + float posx = cos(theta) * 0.5; + float posy = sin(theta) * 0.5; + + float charwidth = 6.0 * (2.0 / 800.0); + float charheight = 11.0 * (2.0 / 640.0); + + ctx->DrawDebugText( posx, posy, 0.0f, charwidth, charheight, text ); + } + glFinish(); + CheckGLError("test3 finish"); + + Present( 3333 ); + } + + StdCleanup(); + *****************************/ +} + +#if GLMDEBUG +void GLMTriggerDebuggerBreak() +{ + // we call an obscure GL function which we know has been breakpointed in the OGLP function list + static signed short nada[] = { -1,-1,-1,-1 }; + gGL->glColor4sv( nada ); +} +#endif diff --git a/togl/linuxwin/glmgr_flush.inl b/togl/linuxwin/glmgr_flush.inl new file mode 100644 index 0000000..fcddd61 --- /dev/null +++ b/togl/linuxwin/glmgr_flush.inl @@ -0,0 +1,622 @@ +// BE VERY VERY CAREFUL what you do in these function. They are extremely hot, and calling the wrong GL API's in here will crush perf. (especially on NVidia threaded drivers). + +FORCEINLINE uint32 bitmix32(uint32 a) +{ + a -= (a<<6); + //a ^= (a>>17); + //a -= (a<<9); + a ^= (a<<4); + //a -= (a<<3); + //a ^= (a<<10); + a ^= (a>>15); + return a; +} + +#ifndef OSX + +FORCEINLINE GLuint GLMContext::FindSamplerObject( const GLMTexSamplingParams &desiredParams ) +{ + int h = bitmix32( desiredParams.m_bits + desiredParams.m_borderColor ) & ( cSamplerObjectHashSize - 1 ); + while ( ( m_samplerObjectHash[h].m_params.m_bits != desiredParams.m_bits ) || ( m_samplerObjectHash[h].m_params.m_borderColor != desiredParams.m_borderColor ) ) + { + if ( !m_samplerObjectHash[h].m_params.m_packed.m_isValid ) + break; + if ( ++h >= cSamplerObjectHashSize ) + h = 0; + } + + if ( !m_samplerObjectHash[h].m_params.m_packed.m_isValid ) + { + GLMTexSamplingParams &hashParams = m_samplerObjectHash[h].m_params; + hashParams = desiredParams; + hashParams.SetToSamplerObject( m_samplerObjectHash[h].m_samplerObject ); + if ( ++m_nSamplerObjectHashNumEntries == cSamplerObjectHashSize ) + { + // TODO: Support resizing + Error( "Sampler object hash is full, increase cSamplerObjectHashSize" ); + } + } + + return m_samplerObjectHash[h].m_samplerObject; +} + +#endif // !OSX + +// BE VERY CAREFUL WHAT YOU DO IN HERE. This is called on every batch, even seemingly simple changes can kill perf. +FORCEINLINE void GLMContext::FlushDrawStates( uint nStartIndex, uint nEndIndex, uint nBaseVertex ) // shadersOn = true for draw calls, false for clear calls +{ + Assert( m_drawingLang == kGLMGLSL ); // no support for ARB shaders right now (and NVidia reports that they aren't worth targeting under Windows/Linux for various reasons anyway) + Assert( ( m_drawingFBO == m_boundDrawFBO ) && ( m_drawingFBO == m_boundReadFBO ) ); // this check MUST succeed + Assert( m_pDevice->m_pVertDecl ); + +#if GLMDEBUG + GLM_FUNC; +#endif + + GL_BATCH_PERF( m_FlushStats.m_nTotalBatchFlushes++; ) + +#if GLMDEBUG + bool tex0_srgb = (m_boundDrawFBO[0].m_attach[0].m_tex->m_layout->m_key.m_texFlags & kGLMTexSRGB) != 0; + + // you can only actually use the sRGB FB state on some systems.. check caps + if (m_caps.m_hasGammaWrites) + { + GLBlendEnableSRGB_t writeSRGBState; + m_BlendEnableSRGB.Read( &writeSRGBState, 0 ); // the client set value, not the API-written value yet.. + bool draw_srgb = writeSRGBState.enable != 0; + + if (draw_srgb) + { + if (tex0_srgb) + { + // good - draw mode and color tex agree + } + else + { + // bad + + // Client has asked to write sRGB into a texture that can't do it. + // there is no way to satisfy this unless we change the RT tex and we avoid doing that. + // (although we might consider a ** ONE TIME ** promotion. + // this shouldn't be a big deal if the tex format is one where it doesn't matter like 32F. + + GLMPRINTF(("-Z- srgb-enabled FBO conflict: attached tex %08x [%s] is not SRGB", m_boundDrawFBO[0].m_attach[0].m_tex, m_boundDrawFBO[0].m_attach[0].m_tex->m_layout->m_layoutSummary )); + + // do we shoot down the srgb-write state for this batch? + // I think the runtime will just ignore it. + } + } + else + { + if (tex0_srgb) + { + // odd - client is not writing sRGB into a texture which *can* do it. + //GLMPRINTF(( "-Z- srgb-disabled FBO conflict: attached tex %08x [%s] is SRGB", m_boundFBO[0].m_attach[0].m_tex, m_boundFBO[0].m_attach[0].m_tex->m_layout->m_layoutSummary )); + //writeSRGBState.enable = true; + //m_BlendEnableSRGB.Write( &writeSRGBState ); + } + else + { + // good - draw mode and color tex agree + } + } + } +#endif + + Assert( m_drawingProgram[ kGLMVertexProgram ] ); + Assert( m_drawingProgram[ kGLMFragmentProgram ] ); + + Assert( ( m_drawingProgram[kGLMVertexProgram]->m_type == kGLMVertexProgram ) && ( m_drawingProgram[kGLMFragmentProgram]->m_type == kGLMFragmentProgram ) ); + Assert( m_drawingProgram[ kGLMVertexProgram ]->m_bTranslatedProgram && m_drawingProgram[ kGLMFragmentProgram ]->m_bTranslatedProgram ); + +#if GLMDEBUG + // Depth compare mode check + uint nCurMask = 1, nShaderSamplerMask = m_drawingProgram[kGLMFragmentProgram]->m_samplerMask; + for ( int nSamplerIndex = 0; nSamplerIndex < GLM_SAMPLER_COUNT; ++nSamplerIndex, nCurMask <<= 1 ) + { + if ( !m_samplers[nSamplerIndex].m_pBoundTex ) + continue; + + if ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_mipCount == 1 ) + { + if ( m_samplers[nSamplerIndex].m_samp.m_packed.m_mipFilter == D3DTEXF_LINEAR ) + { + GLMDebugPrintf( "Sampler %u has mipmap filtering enabled on a texture without mipmaps! (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + + if ( ( nShaderSamplerMask & nCurMask ) == 0 ) + continue; + + if ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_mipCount == 1 ) + { + if ( m_samplers[nSamplerIndex].m_samp.m_packed.m_mipFilter == D3DTEXF_LINEAR ) + { + // Note this is not always an error - shadow buffer debug visualization shaders purposely want to read shadow depths (and not do the comparison) + GLMDebugPrintf( "Sampler %u has mipmap filtering enabled on a texture without mipmaps! (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + + bool bSamplerIsDepth = ( m_samplers[nSamplerIndex].m_pBoundTex->m_layout->m_key.m_texFlags & kGLMTexIsDepth ) != 0; + bool bSamplerShadow = m_samplers[nSamplerIndex].m_samp.m_packed.m_compareMode != 0; + + bool bShaderShadow = ( m_drawingProgram[kGLMFragmentProgram]->m_nShadowDepthSamplerMask & nCurMask ) != 0; + + if ( bShaderShadow ) + { + // Shader expects shadow depth sampling at this sampler index + // Must have a depth texture and compare mode must be enabled + if ( !bSamplerIsDepth || !bSamplerShadow ) + { + // FIXME: This occasionally occurs in L4D2 when CShaderAPIDx8::ExecuteCommandBuffer() sets the TEXTURE_WHITE texture in the flashlight depth texture slot. + GLMDebugPrintf( "Sampler %u's compare mode (%u) or format (depth=%u) is not consistent with pixel shader's compare mode (%u) (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, bSamplerShadow, bSamplerIsDepth, bShaderShadow, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + else + { + // Shader does not expect shadow depth sampling as this sampler index + // We don't care if comparemode is enabled, but we can't have a depth texture in this sampler + if ( bSamplerIsDepth ) + { + GLMDebugPrintf( "Sampler %u is a depth texture but the pixel shader's shadow depth sampler mask does not expect depth here (texture name: %s, pixel shader: %s)!\n", + nSamplerIndex, + m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel ? m_samplers[nSamplerIndex].m_pBoundTex->m_debugLabel : "?", + m_drawingProgram[kGLMFragmentProgram]->m_shaderName ); + } + } + } +#endif + + if ( m_bDirtyPrograms ) + { + m_bDirtyPrograms = false; + + CGLMShaderPair *pNewPair = m_pairCache->SelectShaderPair( m_drawingProgram[ kGLMVertexProgram ], m_drawingProgram[ kGLMFragmentProgram ], 0 ); + + if ( pNewPair != m_pBoundPair ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "NewProgram" ); +#endif + + if ( !pNewPair->m_valid ) + { + if ( !pNewPair->ValidateProgramPair() ) + { + goto flush_error_exit; + } + } + + gGL->glUseProgram( (GLuint)pNewPair->m_program ); + + GL_BATCH_PERF( m_FlushStats.m_nTotalProgramPairChanges++; ) + + if ( !m_pBoundPair ) + { + GL_BATCH_PERF( m_FlushStats.m_nNewPS++; ) + GL_BATCH_PERF( m_FlushStats.m_nNewVS++; ) + } + else + { + GL_BATCH_PERF( if ( pNewPair->m_fragmentProg != m_pBoundPair->m_fragmentProg ) m_FlushStats.m_nNewPS++; ) + GL_BATCH_PERF( if ( pNewPair->m_vertexProg != m_pBoundPair->m_vertexProg ) m_FlushStats.m_nNewVS++; ) + } + +#if GL_BATCH_PERF_ANALYSIS + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_NOTE, "V:%s (V Regs:%u V Bone Regs:%u) F:%s (F Regs:%u)", + m_drawingProgram[ kGLMVertexProgram ]->m_shaderName, + m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_highWater, + m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_VSHighWaterBone, + m_drawingProgram[ kGLMFragmentProgram ]->m_shaderName, + m_drawingProgram[ kGLMFragmentProgram ]->m_descs[kGLMGLSL].m_highWater ); +#endif + + m_pBoundPair = pNewPair; + + // set the dirty levels appropriately since the program changed and has never seen any of the current values. + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 0; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_highWater; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = m_drawingProgram[ kGLMVertexProgram ]->m_descs[kGLMGLSL].m_VSHighWaterBone; + + m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone = 0; + m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone = m_drawingProgram[ kGLMFragmentProgram ]->m_descs[kGLMGLSL].m_highWater; + + // bool and int dirty levels get set to max, we don't have actual high water marks for them + // code which sends the values must clamp on these types. + m_programParamsB[kGLMVertexProgram].m_dirtySlotCount = kGLMProgramParamBoolLimit; + m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount = kGLMProgramParamBoolLimit; + + m_programParamsI[kGLMVertexProgram].m_dirtySlotCount = kGLMProgramParamInt4Limit; + m_programParamsI[kGLMFragmentProgram].m_dirtySlotCount = 0; + + // check fragment buffers used (MRT) + if( pNewPair->m_fragmentProg->m_fragDataMask != m_fragDataMask ) + { + gGL->glDrawBuffers( pNewPair->m_fragmentProg->m_numDrawBuffers, pNewPair->m_fragmentProg->m_drawBuffers ); + m_fragDataMask = pNewPair->m_fragmentProg->m_fragDataMask; + } + } + } + + Assert( m_ViewportBox.GetData().width == (int)( m_ViewportBox.GetData().widthheight & 0xFFFF ) ); + Assert( m_ViewportBox.GetData().height == (int)( m_ViewportBox.GetData().widthheight >> 16 ) ); + + m_pBoundPair->UpdateScreenUniform( m_ViewportBox.GetData().widthheight ); + + GL_BATCH_PERF( m_FlushStats.m_nNumChangedSamplers += m_nNumDirtySamplers ); + +#if !defined( OSX ) // no support for sampler objects in OSX 10.6 (GL 2.1 profile) + if ( m_bUseSamplerObjects) + { + while ( m_nNumDirtySamplers ) + { + const uint nSamplerIndex = m_nDirtySamplers[--m_nNumDirtySamplers]; + Assert( ( nSamplerIndex < GLM_SAMPLER_COUNT ) && ( !m_nDirtySamplerFlags[nSamplerIndex]) ); + + m_nDirtySamplerFlags[nSamplerIndex] = 1; + + gGL->glBindSampler( nSamplerIndex, FindSamplerObject( m_samplers[nSamplerIndex].m_samp ) ); + + GL_BATCH_PERF( m_FlushStats.m_nNumSamplingParamsChanged++ ); + +#if defined( OSX ) // valid for OSX only if using GL 3.3 context + CGLMTex *pTex = m_samplers[nSamplerIndex].m_pBoundTex; + + if( pTex && !( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) ) + { + // see if requested SRGB state differs from the known one + bool texSRGB = ( pTex->m_layout->m_key.m_texFlags & kGLMTexSRGB ) != 0; + bool glSampSRGB = m_samplers[nSamplerIndex].m_samp.m_packed.m_srgb; + + if ( texSRGB != glSampSRGB ) // mismatch + { + pTex->HandleSRGBMismatch( glSampSRGB, pTex->m_srgbFlipCount ); + } + } +#endif + } + } + else +#endif // if !defined( OSX ) + { + while ( m_nNumDirtySamplers ) + { + const uint nSamplerIndex = m_nDirtySamplers[--m_nNumDirtySamplers]; + Assert( ( nSamplerIndex < GLM_SAMPLER_COUNT ) && ( !m_nDirtySamplerFlags[nSamplerIndex]) ); + + m_nDirtySamplerFlags[nSamplerIndex] = 1; + + CGLMTex *pTex = m_samplers[nSamplerIndex].m_pBoundTex; + + if ( ( pTex ) && ( !( pTex->m_SamplingParams == m_samplers[nSamplerIndex].m_samp ) ) ) + { + SelectTMU( nSamplerIndex ); + + m_samplers[nSamplerIndex].m_samp.DeltaSetToTarget( pTex->m_texGLTarget, pTex->m_SamplingParams ); + + pTex->m_SamplingParams = m_samplers[nSamplerIndex].m_samp; + +#if defined( OSX ) + if( pTex && !( gGL->m_bHave_GL_EXT_texture_sRGB_decode ) ) + { + // see if requested SRGB state differs from the known one + bool texSRGB = ( pTex->m_layout->m_key.m_texFlags & kGLMTexSRGB ) != 0; + bool glSampSRGB = m_samplers[nSamplerIndex].m_samp.m_packed.m_srgb; + + if ( texSRGB != glSampSRGB ) // mismatch + { + pTex->HandleSRGBMismatch( glSampSRGB, pTex->m_srgbFlipCount ); + } + } +#endif + } + } + } + + // vertex stage -------------------------------------------------------------------- + if ( m_bUseBoneUniformBuffers ) + { + // vertex stage -------------------------------------------------------------------- + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ) + { + int firstDirtySlot = m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_highWater, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ); + + GLint vconstLoc = m_pBoundPair->m_locVertexParams; + if ( ( vconstLoc >= 0 ) && ( dirtySlotHighWater > firstDirtySlot ) ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSNonBoneUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); +#endif + int numSlots = dirtySlotHighWater - DXABSTRACT_VS_FIRST_BONE_SLOT; + + // consts after the bones (c217 onwards), since we use the concatenated destination array vc[], upload these consts starting from vc[58] + if( numSlots > 0 ) + { + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][DXABSTRACT_VS_FIRST_BONE_SLOT], numSlots, &m_programParamsF[kGLMVertexProgram].m_values[(DXABSTRACT_VS_LAST_BONE_SLOT+1)][0] ); + + dirtySlotHighWater = DXABSTRACT_VS_FIRST_BONE_SLOT; + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += numSlots; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = DXABSTRACT_VS_FIRST_BONE_SLOT; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += numSlots; ) + } + + numSlots = dirtySlotHighWater - firstDirtySlot; + + // consts before the bones (c0-c57) + if( numSlots > 0 ) + { + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMVertexProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + } + + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = 0; + } + + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone ) + { + const GLint vconstBoneLoc = m_pBoundPair->m_locVertexBoneParams; + if ( vconstBoneLoc >= 0 ) + { + int shaderSlotsBone = 0; + if ( ( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_VSHighWaterBone > 0 ) && ( m_nMaxUsedVertexProgramConstantsHint > DXABSTRACT_VS_FIRST_BONE_SLOT ) ) + { + shaderSlotsBone = MIN( m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_VSHighWaterBone, m_nMaxUsedVertexProgramConstantsHint - DXABSTRACT_VS_FIRST_BONE_SLOT ); + } + + int dirtySlotHighWaterBone = MIN( shaderSlotsBone, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone ); + if ( dirtySlotHighWaterBone ) + { + uint nNumBoneRegs = dirtySlotHighWaterBone; + +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSBoneUniformUpdate %u", nNumBoneRegs ); +#endif + + gGL->glUniform4fv( vconstBoneLoc, nNumBoneRegs, &m_programParamsF[kGLMVertexProgram].m_values[DXABSTRACT_VS_FIRST_BONE_SLOT][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformBoneCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsBoneSet += nNumBoneRegs; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSBoneConstants += nNumBoneRegs; ) + } + + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = 0; + } + } + + } + else + { + if ( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ) + { + const int nMaxUsedShaderSlots = m_drawingProgram[kGLMVertexProgram]->m_descs[kGLMGLSL].m_highWater; + + int firstDirtySlot = m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( nMaxUsedShaderSlots, m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone ); + + GLint vconstLoc = m_pBoundPair->m_locVertexParams; + if ( ( vconstLoc >= 0 ) && ( dirtySlotHighWater > firstDirtySlot ) ) + { + #if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "VSNonBoneUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); + #endif + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMVertexProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMVertexProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalVSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalVSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstVSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumVSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + + m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = 0; + } + } + + + // see if VS uses i0, b0, b1, b2, b3. + // use a glUniform1i to set any one of these if active. skip all of them if no dirties reported. + // my kingdom for the UBO extension! + + // ------- bools ---------- // + if ( m_pBoundPair->m_bHasBoolOrIntUniforms ) + { + if ( m_programParamsB[kGLMVertexProgram].m_dirtySlotCount ) // optimize this later after the float param pushes are proven out + { + const uint nLimit = MIN( CGLMShaderPair::cMaxVertexShaderBoolUniforms, m_programParamsB[kGLMVertexProgram].m_dirtySlotCount ); + for ( uint i = 0; i < nLimit; ++i ) + { + GLint constBoolLoc = m_pBoundPair->m_locVertexBool[i]; + if ( constBoolLoc >= 0 ) + gGL->glUniform1i( constBoolLoc, m_programParamsB[kGLMVertexProgram].m_values[i] ); + } + + m_programParamsB[kGLMVertexProgram].m_dirtySlotCount = 0; + } + + if ( m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount ) // optimize this later after the float param pushes are proven out + { + const uint nLimit = MIN( CGLMShaderPair::cMaxFragmentShaderBoolUniforms, m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount ); + for ( uint i = 0; i < nLimit; ++i ) + { + GLint constBoolLoc = m_pBoundPair->m_locFragmentBool[i]; + if ( constBoolLoc >= 0 ) + gGL->glUniform1i( constBoolLoc, m_programParamsB[kGLMFragmentProgram].m_values[i] ); + } + + m_programParamsB[kGLMFragmentProgram].m_dirtySlotCount = 0; + } + + if ( m_programParamsI[kGLMVertexProgram].m_dirtySlotCount ) + { + GLint vconstInt0Loc = m_pBoundPair->m_locVertexInteger0; //glGetUniformLocationARB( prog, "i0"); + if ( vconstInt0Loc >= 0 ) + { + gGL->glUniform1i( vconstInt0Loc, m_programParamsI[kGLMVertexProgram].m_values[0][0] ); //FIXME magic number + } + m_programParamsI[kGLMVertexProgram].m_dirtySlotCount = 0; + } + } + + Assert( ( m_pDevice->m_streams[0].m_vtxBuffer && ( m_pDevice->m_streams[0].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[0] ) ) || ( ( !m_pDevice->m_streams[0].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[0] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[1].m_vtxBuffer && ( m_pDevice->m_streams[1].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[1] ) ) || ( ( !m_pDevice->m_streams[1].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[1] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[2].m_vtxBuffer && ( m_pDevice->m_streams[2].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[2] ) ) || ( ( !m_pDevice->m_streams[2].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[2] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + Assert( ( m_pDevice->m_streams[3].m_vtxBuffer && ( m_pDevice->m_streams[3].m_vtxBuffer->m_vtxBuffer == m_pDevice->m_vtx_buffers[3] ) ) || ( ( !m_pDevice->m_streams[3].m_vtxBuffer ) && ( m_pDevice->m_vtx_buffers[3] == m_pDevice->m_pDummy_vtx_buffer ) ) ); + + uint nCurTotalBufferRevision; + nCurTotalBufferRevision = m_pDevice->m_vtx_buffers[0]->m_nRevision + m_pDevice->m_vtx_buffers[1]->m_nRevision + m_pDevice->m_vtx_buffers[2]->m_nRevision + m_pDevice->m_vtx_buffers[3]->m_nRevision; + + // If any of these inputs have changed, we need to enumerate through all of the expected GL vertex attribs and modify anything in the GL layer that have changed. + // This is not always a win, but it is a net win on NVidia (by 1-4.8% depending on whether driver threading is enabled). + if ( ( nCurTotalBufferRevision != m_CurAttribs.m_nTotalBufferRevision ) || + ( m_CurAttribs.m_pVertDecl != m_pDevice->m_pVertDecl ) || + ( m_CurAttribs.m_vtxAttribMap[0] != reinterpret_cast<const uint64 *>(m_pDevice->m_vertexShader->m_vtxAttribMap)[0] ) || + ( m_CurAttribs.m_vtxAttribMap[1] != reinterpret_cast<const uint64 *>(m_pDevice->m_vertexShader->m_vtxAttribMap)[1] ) || + ( memcmp( m_CurAttribs.m_streams, m_pDevice->m_streams, sizeof( m_pDevice->m_streams ) ) != 0 ) ) + { + // This branch is taken 52.2% of the time in the L4D2 test1 (long) timedemo. + +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "SetVertexAttribs" ); +#endif + + m_CurAttribs.m_nTotalBufferRevision = nCurTotalBufferRevision; + m_CurAttribs.m_pVertDecl = m_pDevice->m_pVertDecl; + m_CurAttribs.m_vtxAttribMap[0] = reinterpret_cast<const uint64 *>(m_pDevice->m_vertexShader->m_vtxAttribMap)[0]; + m_CurAttribs.m_vtxAttribMap[1] = reinterpret_cast<const uint64 *>(m_pDevice->m_vertexShader->m_vtxAttribMap)[1]; + memcpy( m_CurAttribs.m_streams, m_pDevice->m_streams, sizeof( m_pDevice->m_streams ) ); + + unsigned char *pVertexShaderAttribMap = m_pDevice->m_vertexShader->m_vtxAttribMap; + const int nMaxVertexAttributesToCheck = m_drawingProgram[ kGLMVertexProgram ]->m_maxVertexAttrs; + + IDirect3DVertexDeclaration9 *pVertDecl = m_pDevice->m_pVertDecl; + const uint8 *pVertexAttribDescToStreamIndex = pVertDecl->m_VertexAttribDescToStreamIndex; + + for( int nMask = 1, nIndex = 0; nIndex < nMaxVertexAttributesToCheck; ++nIndex, nMask <<= 1 ) + { + uint8 vertexShaderAttrib = pVertexShaderAttribMap[ nIndex ]; + + uint nDeclIndex = pVertexAttribDescToStreamIndex[vertexShaderAttrib]; + if ( nDeclIndex == 0xFF ) + { + // Not good - the vertex shader has an attribute which can't be located in the decl! + // The D3D9 debug runtime is also going to complain. + Assert( 0 ); + + if ( m_lastKnownVertexAttribMask & nMask ) + { + m_lastKnownVertexAttribMask &= ~nMask; + gGL->glDisableVertexAttribArray( nIndex ); + } + continue; + } + + D3DVERTEXELEMENT9_GL *pDeclElem = &pVertDecl->m_elements[nDeclIndex]; + + Assert( ( ( vertexShaderAttrib >> 4 ) == pDeclElem->m_dxdecl.Usage ) && ( ( vertexShaderAttrib & 0x0F ) == pDeclElem->m_dxdecl.UsageIndex) ); + + const uint nStreamIndex = pDeclElem->m_dxdecl.Stream; + const D3DStreamDesc *pStream = &m_pDevice->m_streams[ nStreamIndex ]; + + CGLMBuffer *pBuf = m_pDevice->m_vtx_buffers[ nStreamIndex ]; + if ( pBuf == m_pDevice->m_pDummy_vtx_buffer ) + { + Assert( pStream->m_vtxBuffer == NULL ); + + // this shader doesn't use that pair. + if ( m_lastKnownVertexAttribMask & nMask ) + { + m_lastKnownVertexAttribMask &= ~nMask; + gGL->glDisableVertexAttribArray( nIndex ); + } + continue; + } + Assert( pStream->m_vtxBuffer->m_vtxBuffer == pBuf ); + + int nBufOffset = pDeclElem->m_gldecl.m_offset + pStream->m_offset; + Assert( nBufOffset >= 0 ); + Assert( nBufOffset < (int)pBuf->m_nSize ); + if ( pBuf->m_bUsingPersistentBuffer ) + { + nBufOffset += pBuf->m_nPersistentBufferStartOffset; + } + + SetBufAndVertexAttribPointer( nIndex, pBuf->GetHandle(), + pStream->m_stride, pDeclElem->m_gldecl.m_datatype, pDeclElem->m_gldecl.m_normalized, pDeclElem->m_gldecl.m_nCompCount, + reinterpret_cast< const GLvoid * >( reinterpret_cast< int >( pBuf->m_pPseudoBuf ) + nBufOffset ), + pBuf->m_nRevision ); + + if ( !( m_lastKnownVertexAttribMask & nMask ) ) + { + m_lastKnownVertexAttribMask |= nMask; + gGL->glEnableVertexAttribArray( nIndex ); + } + } + + for( int nIndex = nMaxVertexAttributesToCheck; nIndex < m_nNumSetVertexAttributes; nIndex++ ) + { + gGL->glDisableVertexAttribArray( nIndex ); + m_lastKnownVertexAttribMask &= ~(1 << nIndex); + } + + m_nNumSetVertexAttributes = nMaxVertexAttributesToCheck; + } + + // fragment stage -------------------------------------------------------------------- + if ( m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone ) + { + GLint fconstLoc; + fconstLoc = m_pBoundPair->m_locFragmentParams; + if ( fconstLoc >= 0 ) + { + const int nMaxUsedShaderSlots = m_drawingProgram[kGLMFragmentProgram]->m_descs[kGLMGLSL].m_highWater; + + int firstDirtySlot = m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone; + int dirtySlotHighWater = MIN( nMaxUsedShaderSlots, m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone ); + + if ( dirtySlotHighWater > firstDirtySlot ) + { +#if GL_BATCH_TELEMETRY_ZONES + tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "PSUniformUpdate %u %u", firstDirtySlot, dirtySlotHighWater ); +#endif + + gGL->glUniform4fv( m_pBoundPair->m_UniformBufferParams[kGLMFragmentProgram][firstDirtySlot], dirtySlotHighWater - firstDirtySlot, &m_programParamsF[kGLMFragmentProgram].m_values[firstDirtySlot][0] ); + + GL_BATCH_PERF( m_nTotalPSUniformCalls++; ) + GL_BATCH_PERF( m_nTotalPSUniformsSet += dirtySlotHighWater - firstDirtySlot; ) + + GL_BATCH_PERF( m_FlushStats.m_nFirstPSConstant = firstDirtySlot; ) + GL_BATCH_PERF( m_FlushStats.m_nNumPSConstants += (dirtySlotHighWater - firstDirtySlot); ) + } + m_programParamsF[kGLMFragmentProgram].m_firstDirtySlotNonBone = 256; + m_programParamsF[kGLMFragmentProgram].m_dirtySlotHighWaterNonBone = 0; + } + } + + return; + +flush_error_exit: + m_pBoundPair = NULL; + m_bDirtyPrograms = true; + return; +} diff --git a/togl/linuxwin/glmgrbasics.cpp b/togl/linuxwin/glmgrbasics.cpp new file mode 100644 index 0000000..5a19ccc --- /dev/null +++ b/togl/linuxwin/glmgrbasics.cpp @@ -0,0 +1,4684 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// glmgrbasics.cpp +// +//=============================================================================== + +#include "togl/rendermechanism.h" + +#include "tier0/icommandline.h" +#include "tier1/utlhash.h" +#include "tier1/utlmap.h" +#include "tier0/vprof.h" + +#ifdef OSX +#include <OpenGL/OpenGL.h> +#ifdef CGLPROFILER_ENABLE +#include <OpenGL/CGLProfilerFunctionEnum.h> +#endif +#endif + +#include "tier0/valve_minmax_off.h" +#include <algorithm> + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +//=============================================================================== +// decoding tables for debug + +typedef struct +{ + unsigned long value; + const char *name; +} GLMValueEntry_t; + +#define TERMVALUE 0x31415926 + // terminator for value tables + +#define VE( x ) { x, #x } + // "value entry" + +const GLMValueEntry_t g_d3d_devtypes[] = +{ + VE( D3DDEVTYPE_HAL ), + VE( D3DDEVTYPE_REF ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_formats[] = +{ + VE( D3DFMT_INDEX16 ), + VE( D3DFMT_D16 ), + VE( D3DFMT_D24S8 ), + VE( D3DFMT_A8R8G8B8 ), + VE( D3DFMT_A4R4G4B4 ), + VE( D3DFMT_X8R8G8B8 ), + VE( D3DFMT_R5G6R5 ), + VE( D3DFMT_X1R5G5B5 ), + VE( D3DFMT_A1R5G5B5 ), + VE( D3DFMT_L8 ), + VE( D3DFMT_A8L8 ), + VE( D3DFMT_A ), + VE( D3DFMT_DXT1 ), + VE( D3DFMT_DXT3 ), + VE( D3DFMT_DXT5 ), + VE( D3DFMT_V8U8 ), + VE( D3DFMT_Q8W8V8U8 ), + VE( D3DFMT_X8L8V8U8 ), + VE( D3DFMT_A16B16G16R16F ), + VE( D3DFMT_A16B16G16R16 ), + VE( D3DFMT_R32F ), + VE( D3DFMT_A32B32G32R32F ), + VE( D3DFMT_R8G8B8 ), + VE( D3DFMT_D24X4S4 ), + VE( D3DFMT_A8 ), + VE( D3DFMT_R5G6B5 ), + VE( D3DFMT_D15S1 ), + VE( D3DFMT_D24X8 ), + VE( D3DFMT_VERTEXDATA ), + VE( D3DFMT_INDEX32 ), + + // vendor specific formats (fourcc's) + VE( D3DFMT_NV_INTZ ), + VE( D3DFMT_NV_RAWZ ), + VE( D3DFMT_NV_NULL ), + VE( D3DFMT_ATI_D16 ), + VE( D3DFMT_ATI_D24S8 ), + VE( D3DFMT_ATI_2N ), + VE( D3DFMT_ATI_1N ), + + VE( D3DFMT_UNKNOWN ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_rtypes[] = +{ + VE( D3DRTYPE_SURFACE ), + VE( D3DRTYPE_TEXTURE ), + VE( D3DRTYPE_VOLUMETEXTURE ), + VE( D3DRTYPE_CUBETEXTURE ), + VE( D3DRTYPE_VERTEXBUFFER ), + VE( D3DRTYPE_INDEXBUFFER ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_usages[] = +{ + VE( D3DUSAGE_RENDERTARGET ), + VE( D3DUSAGE_DEPTHSTENCIL ), + VE( D3DUSAGE_DYNAMIC ), + VE( D3DUSAGE_AUTOGENMIPMAP ), + //VE( D3DUSAGE_DMAP ), + //VE( D3DUSAGE_QUERY_LEGACYBUMPMAP ), + VE( D3DUSAGE_QUERY_SRGBREAD ), + VE( D3DUSAGE_QUERY_FILTER ), + VE( D3DUSAGE_QUERY_SRGBWRITE ), + VE( D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING ), + VE( D3DUSAGE_QUERY_VERTEXTEXTURE ), + //VE( D3DUSAGE_QUERY_WRAPANDMIP ), + VE( D3DUSAGE_WRITEONLY ), + VE( D3DUSAGE_SOFTWAREPROCESSING ), + VE( D3DUSAGE_DONOTCLIP ), + VE( D3DUSAGE_POINTS ), + VE( D3DUSAGE_RTPATCHES ), + VE( D3DUSAGE_NPATCHES ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_rstates[] = +{ + VE( D3DRS_ZENABLE ), + VE( D3DRS_FILLMODE ), + VE( D3DRS_SHADEMODE ), + VE( D3DRS_ZWRITEENABLE ), + VE( D3DRS_ALPHATESTENABLE ), + VE( D3DRS_LASTPIXEL ), + VE( D3DRS_SRCBLEND ), + VE( D3DRS_DESTBLEND ), + VE( D3DRS_CULLMODE ), + VE( D3DRS_ZFUNC ), + VE( D3DRS_ALPHAREF ), + VE( D3DRS_ALPHAFUNC ), + VE( D3DRS_DITHERENABLE ), + VE( D3DRS_ALPHABLENDENABLE ), + VE( D3DRS_FOGENABLE ), + VE( D3DRS_SPECULARENABLE ), + VE( D3DRS_FOGCOLOR ), + VE( D3DRS_FOGTABLEMODE ), + VE( D3DRS_FOGSTART ), + VE( D3DRS_FOGEND ), + VE( D3DRS_FOGDENSITY ), + VE( D3DRS_RANGEFOGENABLE ), + VE( D3DRS_STENCILENABLE ), + VE( D3DRS_STENCILFAIL ), + VE( D3DRS_STENCILZFAIL ), + VE( D3DRS_STENCILPASS ), + VE( D3DRS_STENCILFUNC ), + VE( D3DRS_STENCILREF ), + VE( D3DRS_STENCILMASK ), + VE( D3DRS_STENCILWRITEMASK ), + VE( D3DRS_TEXTUREFACTOR ), + VE( D3DRS_WRAP0 ), + VE( D3DRS_WRAP1 ), + VE( D3DRS_WRAP2 ), + VE( D3DRS_WRAP3 ), + VE( D3DRS_WRAP4 ), + VE( D3DRS_WRAP5 ), + VE( D3DRS_WRAP6 ), + VE( D3DRS_WRAP7 ), + VE( D3DRS_CLIPPING ), + VE( D3DRS_LIGHTING ), + VE( D3DRS_AMBIENT ), + VE( D3DRS_FOGVERTEXMODE ), + VE( D3DRS_COLORVERTEX ), + VE( D3DRS_LOCALVIEWER ), + VE( D3DRS_NORMALIZENORMALS ), + VE( D3DRS_DIFFUSEMATERIALSOURCE ), + VE( D3DRS_SPECULARMATERIALSOURCE ), + VE( D3DRS_AMBIENTMATERIALSOURCE ), + VE( D3DRS_EMISSIVEMATERIALSOURCE ), + VE( D3DRS_VERTEXBLEND ), + VE( D3DRS_CLIPPLANEENABLE ), + VE( D3DRS_POINTSIZE ), + VE( D3DRS_POINTSIZE_MIN ), + VE( D3DRS_POINTSPRITEENABLE ), + VE( D3DRS_POINTSCALEENABLE ), + VE( D3DRS_POINTSCALE_A ), + VE( D3DRS_POINTSCALE_B ), + VE( D3DRS_POINTSCALE_C ), + VE( D3DRS_MULTISAMPLEANTIALIAS ), + VE( D3DRS_MULTISAMPLEMASK ), + VE( D3DRS_PATCHEDGESTYLE ), + VE( D3DRS_DEBUGMONITORTOKEN ), + VE( D3DRS_POINTSIZE_MAX ), + VE( D3DRS_INDEXEDVERTEXBLENDENABLE ), + VE( D3DRS_COLORWRITEENABLE ), + VE( D3DRS_TWEENFACTOR ), + VE( D3DRS_BLENDOP ), + VE( D3DRS_POSITIONDEGREE ), + VE( D3DRS_NORMALDEGREE ), + VE( D3DRS_SCISSORTESTENABLE ), + VE( D3DRS_SLOPESCALEDEPTHBIAS ), + VE( D3DRS_ANTIALIASEDLINEENABLE ), + VE( D3DRS_MINTESSELLATIONLEVEL ), + VE( D3DRS_MAXTESSELLATIONLEVEL ), + VE( D3DRS_ADAPTIVETESS_X ), + VE( D3DRS_ADAPTIVETESS_Y ), + VE( D3DRS_ADAPTIVETESS_Z ), + VE( D3DRS_ADAPTIVETESS_W ), + VE( D3DRS_ENABLEADAPTIVETESSELLATION ), + VE( D3DRS_TWOSIDEDSTENCILMODE ), + VE( D3DRS_CCW_STENCILFAIL ), + VE( D3DRS_CCW_STENCILZFAIL ), + VE( D3DRS_CCW_STENCILPASS ), + VE( D3DRS_CCW_STENCILFUNC ), + VE( D3DRS_COLORWRITEENABLE1 ), + VE( D3DRS_COLORWRITEENABLE2 ), + VE( D3DRS_COLORWRITEENABLE3 ), + VE( D3DRS_BLENDFACTOR ), + VE( D3DRS_SRGBWRITEENABLE ), + VE( D3DRS_DEPTHBIAS ), + VE( D3DRS_WRAP8 ), + VE( D3DRS_WRAP9 ), + VE( D3DRS_WRAP10 ), + VE( D3DRS_WRAP11 ), + VE( D3DRS_WRAP12 ), + VE( D3DRS_WRAP13 ), + VE( D3DRS_WRAP14 ), + VE( D3DRS_WRAP15 ), + VE( D3DRS_SEPARATEALPHABLENDENABLE ), + VE( D3DRS_SRCBLENDALPHA ), + VE( D3DRS_DESTBLENDALPHA ), + VE( D3DRS_BLENDOPALPHA ), + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_d3d_opcodes[] = +{ + VE( D3DSIO_NOP ), + VE( D3DSIO_PHASE ), + VE( D3DSIO_RET ), + VE( D3DSIO_ELSE ), + VE( D3DSIO_ENDIF ), + VE( D3DSIO_ENDLOOP ), + VE( D3DSIO_ENDREP ), + VE( D3DSIO_BREAK ), + VE( D3DSIO_TEXDEPTH ), + VE( D3DSIO_TEXKILL ), + VE( D3DSIO_BEM ), + VE( D3DSIO_TEXBEM ), + VE( D3DSIO_TEXBEML ), + VE( D3DSIO_TEXDP3 ), + VE( D3DSIO_TEXDP3TEX ), + VE( D3DSIO_TEXM3x2DEPTH ), + VE( D3DSIO_TEXM3x2TEX ), + VE( D3DSIO_TEXM3x3 ), + VE( D3DSIO_TEXM3x3PAD ), + VE( D3DSIO_TEXM3x3TEX ), + VE( D3DSIO_TEXM3x3VSPEC ), + VE( D3DSIO_TEXREG2AR ), + VE( D3DSIO_TEXREG2GB ), + VE( D3DSIO_TEXREG2RGB ), + VE( D3DSIO_LABEL ), + VE( D3DSIO_CALL ), + VE( D3DSIO_IF ), + VE( D3DSIO_LOOP ), + VE( D3DSIO_REP ), + VE( D3DSIO_BREAKP ), + VE( D3DSIO_DSX ), + VE( D3DSIO_DSY ), + VE( D3DSIO_NRM ), + VE( D3DSIO_MOVA ), + VE( D3DSIO_MOV ), + VE( D3DSIO_RCP ), + VE( D3DSIO_RSQ ), + VE( D3DSIO_EXP ), + VE( D3DSIO_EXPP ), + VE( D3DSIO_LOG ), + VE( D3DSIO_LOGP ), + VE( D3DSIO_FRC ), + VE( D3DSIO_LIT ), + VE( D3DSIO_ABS ), + VE( D3DSIO_TEXM3x3SPEC ), + VE( D3DSIO_M4x4 ), + VE( D3DSIO_M4x3 ), + VE( D3DSIO_M3x4 ), + VE( D3DSIO_M3x3 ), + VE( D3DSIO_M3x2 ), + VE( D3DSIO_CALLNZ ), + VE( D3DSIO_IFC ), + VE( D3DSIO_BREAKC ), + VE( D3DSIO_SETP ), + VE( D3DSIO_TEXLDL ), + VE( D3DSIO_ADD ), + VE( D3DSIO_SUB ), + VE( D3DSIO_MUL ), + VE( D3DSIO_DP3 ), + VE( D3DSIO_DP4 ), + VE( D3DSIO_MIN ), + VE( D3DSIO_MAX ), + VE( D3DSIO_DST ), + VE( D3DSIO_SLT ), + VE( D3DSIO_SGE ), + VE( D3DSIO_CRS ), + VE( D3DSIO_POW ), + VE( D3DSIO_DP2ADD ), + VE( D3DSIO_LRP ), + VE( D3DSIO_SGN ), + VE( D3DSIO_CND ), + VE( D3DSIO_CMP ), + VE( D3DSIO_SINCOS ), + VE( D3DSIO_MAD ), + VE( D3DSIO_TEXLDD ), + VE( D3DSIO_TEXCOORD ), + VE( D3DSIO_TEX ), + VE( D3DSIO_DCL ), + VE( D3DSTT_UNKNOWN ), + VE( D3DSTT_2D ), + VE( D3DSTT_CUBE ), + VE( D3DSTT_VOLUME ), + VE( D3DSIO_DEFB ), + VE( D3DSIO_DEFI ), + VE( D3DSIO_DEF ), + VE( D3DSIO_COMMENT ), + VE( D3DSIO_END ), +}; + + +const GLMValueEntry_t g_d3d_vtxdeclusages[] = +{ + { D3DDECLUSAGE_POSITION ,"POSN" }, // P + { D3DDECLUSAGE_BLENDWEIGHT ,"BLWT" }, // W + { D3DDECLUSAGE_BLENDINDICES ,"BLIX" }, // I + { D3DDECLUSAGE_NORMAL ,"NORM" }, // N + { D3DDECLUSAGE_PSIZE ,"PSIZ" }, // S + { D3DDECLUSAGE_TEXCOORD ,"TEXC" }, // T + { D3DDECLUSAGE_TANGENT ,"TANG" }, // G + { D3DDECLUSAGE_BINORMAL ,"BINO" }, // B + { D3DDECLUSAGE_TESSFACTOR ,"TESS" }, // S + { D3DDECLUSAGE_PLUGH ,"????" }, // ? + { D3DDECLUSAGE_COLOR ,"COLR" }, // C + { D3DDECLUSAGE_FOG ,"FOG " }, // F + { D3DDECLUSAGE_DEPTH ,"DEPT" }, // D + { D3DDECLUSAGE_SAMPLE ,"SAMP" } // M +}; + +const GLMValueEntry_t g_d3d_vtxdeclusages_short[] = +{ + { D3DDECLUSAGE_POSITION ,"P" }, + { D3DDECLUSAGE_BLENDWEIGHT ,"W" }, + { D3DDECLUSAGE_BLENDINDICES ,"I" }, + { D3DDECLUSAGE_NORMAL ,"N" }, + { D3DDECLUSAGE_PSIZE ,"S" }, + { D3DDECLUSAGE_TEXCOORD ,"T" }, + { D3DDECLUSAGE_TANGENT ,"G" }, + { D3DDECLUSAGE_BINORMAL ,"B" }, + { D3DDECLUSAGE_TESSFACTOR ,"S" }, + { D3DDECLUSAGE_PLUGH ,"?" }, + { D3DDECLUSAGE_COLOR ,"C" }, + { D3DDECLUSAGE_FOG ,"F" }, + { D3DDECLUSAGE_DEPTH ,"D" }, + { D3DDECLUSAGE_SAMPLE ,"M" } +}; + +const GLMValueEntry_t g_cgl_rendids[] = // need to mask with 0xFFFFFF00 to match on these (ex: 8800GT == 0x00022608 +{ +#ifdef OSX + VE( kCGLRendererGenericID ), + VE( kCGLRendererGenericFloatID ), + VE( kCGLRendererAppleSWID ), + VE( kCGLRendererATIRage128ID ), + VE( kCGLRendererATIRadeonID ), + VE( kCGLRendererATIRageProID ), + VE( kCGLRendererATIRadeon8500ID ), + VE( kCGLRendererATIRadeon9700ID ), + VE( kCGLRendererATIRadeonX1000ID ), + VE( kCGLRendererATIRadeonX2000ID ), + VE( kCGLRendererGeForce2MXID ), + VE( kCGLRendererGeForce3ID ), + VE( kCGLRendererGeForceFXID ), // also for GF6 and GF7 + VE( kCGLRendererGeForce8xxxID ), + VE( kCGLRendererVTBladeXP2ID ), + VE( kCGLRendererIntel900ID ), + VE( kCGLRendererMesa3DFXID ), +#endif + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_gl_errors[] = +{ + VE( GL_INVALID_ENUM ), + VE( GL_INVALID_VALUE ), + VE( GL_INVALID_OPERATION ), + VE( GL_STACK_OVERFLOW ), + VE( GL_STACK_UNDERFLOW ), + VE( GL_OUT_OF_MEMORY ), + VE( GL_INVALID_FRAMEBUFFER_OPERATION_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT ), + VE( GL_FRAMEBUFFER_UNSUPPORTED_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT ), + VE( GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT ) +}; + +// there are some ARB/EXT dupes in this table but that doesn't matter too much +const GLMValueEntry_t g_gl_enums[] = +{ + { 0x0000, "GL_ZERO" }, + { 0x0001, "GL_ONE" }, + { 0x0004, "GL_TRIANGLES" }, + { 0x0005, "GL_TRIANGLE_STRIP" }, + { 0x0006, "GL_TRIANGLE_FAN" }, + { 0x0007, "GL_QUADS" }, + { 0x0008, "GL_QUAD_STRIP" }, + { 0x0009, "GL_POLYGON" }, + { 0x0200, "GL_NEVER" }, + { 0x0201, "GL_LESS" }, + { 0x0202, "GL_EQUAL" }, + { 0x0203, "GL_LEQUAL" }, + { 0x0204, "GL_GREATER" }, + { 0x0205, "GL_NOTEQUAL" }, + { 0x0206, "GL_GEQUAL" }, + { 0x0207, "GL_ALWAYS" }, + { 0x0300, "GL_SRC_COLOR" }, + { 0x0301, "GL_ONE_MINUS_SRC_COLOR" }, + { 0x0302, "GL_SRC_ALPHA" }, + { 0x0303, "GL_ONE_MINUS_SRC_ALPHA" }, + { 0x0304, "GL_DST_ALPHA" }, + { 0x0305, "GL_ONE_MINUS_DST_ALPHA" }, + { 0x0306, "GL_DST_COLOR" }, + { 0x0307, "GL_ONE_MINUS_DST_COLOR" }, + { 0x0308, "GL_SRC_ALPHA_SATURATE" }, + { 0x0400, "GL_FRONT_LEFT" }, + { 0x0401, "GL_FRONT_RIGHT" }, + { 0x0402, "GL_BACK_LEFT" }, + { 0x0403, "GL_BACK_RIGHT" }, + { 0x0404, "GL_FRONT" }, + { 0x0405, "GL_BACK" }, + { 0x0406, "GL_LEFT" }, + { 0x0407, "GL_RIGHT" }, + { 0x0408, "GL_FRONT_AND_BACK" }, + { 0x0409, "GL_AUX0" }, + { 0x040A, "GL_AUX1" }, + { 0x040B, "GL_AUX2" }, + { 0x040C, "GL_AUX3" }, + { 0x0500, "GL_INVALID_ENUM" }, + { 0x0501, "GL_INVALID_VALUE" }, + { 0x0502, "GL_INVALID_OPERATION" }, + { 0x0503, "GL_STACK_OVERFLOW" }, + { 0x0504, "GL_STACK_UNDERFLOW" }, + { 0x0505, "GL_OUT_OF_MEMORY" }, + { 0x0506, "GL_INVALID_FRAMEBUFFER_OPERATION" }, + { 0x0600, "GL_2D" }, + { 0x0601, "GL_3D" }, + { 0x0602, "GL_3D_COLOR" }, + { 0x0603, "GL_3D_COLOR_TEXTURE" }, + { 0x0604, "GL_4D_COLOR_TEXTURE" }, + { 0x0700, "GL_PASS_THROUGH_TOKEN" }, + { 0x0701, "GL_POINT_TOKEN" }, + { 0x0702, "GL_LINE_TOKEN" }, + { 0x0703, "GL_POLYGON_TOKEN" }, + { 0x0704, "GL_BITMAP_TOKEN" }, + { 0x0705, "GL_DRAW_PIXEL_TOKEN" }, + { 0x0706, "GL_COPY_PIXEL_TOKEN" }, + { 0x0707, "GL_LINE_RESET_TOKEN" }, + { 0x0800, "GL_EXP" }, + { 0x0801, "GL_EXP2" }, + { 0x0900, "GL_CW" }, + { 0x0901, "GL_CCW" }, + { 0x0A00, "GL_COEFF" }, + { 0x0A01, "GL_ORDER" }, + { 0x0A02, "GL_DOMAIN" }, + { 0x0B00, "GL_CURRENT_COLOR" }, + { 0x0B01, "GL_CURRENT_INDEX" }, + { 0x0B02, "GL_CURRENT_NORMAL" }, + { 0x0B03, "GL_CURRENT_TEXTURE_COORDS" }, + { 0x0B04, "GL_CURRENT_RASTER_COLOR" }, + { 0x0B05, "GL_CURRENT_RASTER_INDEX" }, + { 0x0B06, "GL_CURRENT_RASTER_TEXTURE_COORDS" }, + { 0x0B07, "GL_CURRENT_RASTER_POSITION" }, + { 0x0B08, "GL_CURRENT_RASTER_POSITION_VALID" }, + { 0x0B09, "GL_CURRENT_RASTER_DISTANCE" }, + { 0x0B10, "GL_POINT_SMOOTH" }, + { 0x0B11, "GL_POINT_SIZE" }, + { 0x0B12, "GL_POINT_SIZE_RANGE" }, + { 0x0B12, "GL_SMOOTH_POINT_SIZE_RANGE" }, + { 0x0B13, "GL_POINT_SIZE_GRANULARITY" }, + { 0x0B13, "GL_SMOOTH_POINT_SIZE_GRANULARITY" }, + { 0x0B20, "GL_LINE_SMOOTH" }, + { 0x0B21, "GL_LINE_WIDTH" }, + { 0x0B22, "GL_LINE_WIDTH_RANGE" }, + { 0x0B22, "GL_SMOOTH_LINE_WIDTH_RANGE" }, + { 0x0B23, "GL_LINE_WIDTH_GRANULARITY" }, + { 0x0B23, "GL_SMOOTH_LINE_WIDTH_GRANULARITY" }, + { 0x0B24, "GL_LINE_STIPPLE" }, + { 0x0B25, "GL_LINE_STIPPLE_PATTERN" }, + { 0x0B26, "GL_LINE_STIPPLE_REPEAT" }, + { 0x0B30, "GL_LIST_MODE" }, + { 0x0B31, "GL_MAX_LIST_NESTING" }, + { 0x0B32, "GL_LIST_BASE" }, + { 0x0B33, "GL_LIST_INDEX" }, + { 0x0B40, "GL_POLYGON_MODE" }, + { 0x0B41, "GL_POLYGON_SMOOTH" }, + { 0x0B42, "GL_POLYGON_STIPPLE" }, + { 0x0B43, "GL_EDGE_FLAG" }, + { 0x0B44, "GL_CULL_FACE" }, + { 0x0B45, "GL_CULL_FACE_MODE" }, + { 0x0B46, "GL_FRONT_FACE" }, + { 0x0B50, "GL_LIGHTING" }, + { 0x0B51, "GL_LIGHT_MODEL_LOCAL_VIEWER" }, + { 0x0B52, "GL_LIGHT_MODEL_TWO_SIDE" }, + { 0x0B53, "GL_LIGHT_MODEL_AMBIENT" }, + { 0x0B54, "GL_SHADE_MODEL" }, + { 0x0B55, "GL_COLOR_MATERIAL_FACE" }, + { 0x0B56, "GL_COLOR_MATERIAL_PARAMETER" }, + { 0x0B57, "GL_COLOR_MATERIAL" }, + { 0x0B60, "GL_FOG" }, + { 0x0B61, "GL_FOG_INDEX" }, + { 0x0B62, "GL_FOG_DENSITY" }, + { 0x0B63, "GL_FOG_START" }, + { 0x0B64, "GL_FOG_END" }, + { 0x0B65, "GL_FOG_MODE" }, + { 0x0B66, "GL_FOG_COLOR" }, + { 0x0B70, "GL_DEPTH_RANGE" }, + { 0x0B71, "GL_DEPTH_TEST" }, + { 0x0B72, "GL_DEPTH_WRITEMASK" }, + { 0x0B73, "GL_DEPTH_CLEAR_VALUE" }, + { 0x0B74, "GL_DEPTH_FUNC" }, + { 0x0B80, "GL_ACCUM_CLEAR_VALUE" }, + { 0x0B90, "GL_STENCIL_TEST" }, + { 0x0B91, "GL_STENCIL_CLEAR_VALUE" }, + { 0x0B92, "GL_STENCIL_FUNC" }, + { 0x0B93, "GL_STENCIL_VALUE_MASK" }, + { 0x0B94, "GL_STENCIL_FAIL" }, + { 0x0B95, "GL_STENCIL_PASS_DEPTH_FAIL" }, + { 0x0B96, "GL_STENCIL_PASS_DEPTH_PASS" }, + { 0x0B97, "GL_STENCIL_REF" }, + { 0x0B98, "GL_STENCIL_WRITEMASK" }, + { 0x0BA0, "GL_MATRIX_MODE" }, + { 0x0BA1, "GL_NORMALIZE" }, + { 0x0BA2, "GL_VIEWPORT" }, + { 0x0BA3, "GL_MODELVIEW_STACK_DEPTH" }, + { 0x0BA4, "GL_PROJECTION_STACK_DEPTH" }, + { 0x0BA5, "GL_TEXTURE_STACK_DEPTH" }, + { 0x0BA6, "GL_MODELVIEW_MATRIX" }, + { 0x0BA7, "GL_PROJECTION_MATRIX" }, + { 0x0BA8, "GL_TEXTURE_MATRIX" }, + { 0x0BB0, "GL_ATTRIB_STACK_DEPTH" }, + { 0x0BB1, "GL_CLIENT_ATTRIB_STACK_DEPTH" }, + { 0x0BC0, "GL_ALPHA_TEST" }, + { 0x0BC1, "GL_ALPHA_TEST_FUNC" }, + { 0x0BC2, "GL_ALPHA_TEST_REF" }, + { 0x0BD0, "GL_DITHER" }, + { 0x0BE0, "GL_BLEND_DST" }, + { 0x0BE1, "GL_BLEND_SRC" }, + { 0x0BE2, "GL_BLEND" }, + { 0x0BF0, "GL_LOGIC_OP_MODE" }, + { 0x0BF1, "GL_INDEX_LOGIC_OP" }, + { 0x0BF2, "GL_COLOR_LOGIC_OP" }, + { 0x0C00, "GL_AUX_BUFFERS" }, + { 0x0C01, "GL_DRAW_BUFFER" }, + { 0x0C02, "GL_READ_BUFFER" }, + { 0x0C10, "GL_SCISSOR_BOX" }, + { 0x0C11, "GL_SCISSOR_TEST" }, + { 0x0C20, "GL_INDEX_CLEAR_VALUE" }, + { 0x0C21, "GL_INDEX_WRITEMASK" }, + { 0x0C22, "GL_COLOR_CLEAR_VALUE" }, + { 0x0C23, "GL_COLOR_WRITEMASK" }, + { 0x0C30, "GL_INDEX_MODE" }, + { 0x0C31, "GL_RGBA_MODE" }, + { 0x0C32, "GL_DOUBLEBUFFER" }, + { 0x0C33, "GL_STEREO" }, + { 0x0C40, "GL_RENDER_MODE" }, + { 0x0C50, "GL_PERSPECTIVE_CORRECTION_HINT" }, + { 0x0C51, "GL_POINT_SMOOTH_HINT" }, + { 0x0C52, "GL_LINE_SMOOTH_HINT" }, + { 0x0C53, "GL_POLYGON_SMOOTH_HINT" }, + { 0x0C54, "GL_FOG_HINT" }, + { 0x0C60, "GL_TEXTURE_GEN_S" }, + { 0x0C61, "GL_TEXTURE_GEN_T" }, + { 0x0C62, "GL_TEXTURE_GEN_R" }, + { 0x0C63, "GL_TEXTURE_GEN_Q" }, + { 0x0C70, "GL_PIXEL_MAP_I_TO_I" }, + { 0x0C71, "GL_PIXEL_MAP_S_TO_S" }, + { 0x0C72, "GL_PIXEL_MAP_I_TO_R" }, + { 0x0C73, "GL_PIXEL_MAP_I_TO_G" }, + { 0x0C74, "GL_PIXEL_MAP_I_TO_B" }, + { 0x0C75, "GL_PIXEL_MAP_I_TO_A" }, + { 0x0C76, "GL_PIXEL_MAP_R_TO_R" }, + { 0x0C77, "GL_PIXEL_MAP_G_TO_G" }, + { 0x0C78, "GL_PIXEL_MAP_B_TO_B" }, + { 0x0C79, "GL_PIXEL_MAP_A_TO_A" }, + { 0x0CB0, "GL_PIXEL_MAP_I_TO_I_SIZE" }, + { 0x0CB1, "GL_PIXEL_MAP_S_TO_S_SIZE" }, + { 0x0CB2, "GL_PIXEL_MAP_I_TO_R_SIZE" }, + { 0x0CB3, "GL_PIXEL_MAP_I_TO_G_SIZE" }, + { 0x0CB4, "GL_PIXEL_MAP_I_TO_B_SIZE" }, + { 0x0CB5, "GL_PIXEL_MAP_I_TO_A_SIZE" }, + { 0x0CB6, "GL_PIXEL_MAP_R_TO_R_SIZE" }, + { 0x0CB7, "GL_PIXEL_MAP_G_TO_G_SIZE" }, + { 0x0CB8, "GL_PIXEL_MAP_B_TO_B_SIZE" }, + { 0x0CB9, "GL_PIXEL_MAP_A_TO_A_SIZE" }, + { 0x0CF0, "GL_UNPACK_SWAP_BYTES" }, + { 0x0CF1, "GL_UNPACK_LSB_FIRST" }, + { 0x0CF2, "GL_UNPACK_ROW_LENGTH" }, + { 0x0CF3, "GL_UNPACK_SKIP_ROWS" }, + { 0x0CF4, "GL_UNPACK_SKIP_PIXELS" }, + { 0x0CF5, "GL_UNPACK_ALIGNMENT" }, + { 0x0D00, "GL_PACK_SWAP_BYTES" }, + { 0x0D01, "GL_PACK_LSB_FIRST" }, + { 0x0D02, "GL_PACK_ROW_LENGTH" }, + { 0x0D03, "GL_PACK_SKIP_ROWS" }, + { 0x0D04, "GL_PACK_SKIP_PIXELS" }, + { 0x0D05, "GL_PACK_ALIGNMENT" }, + { 0x0D10, "GL_MAP_COLOR" }, + { 0x0D11, "GL_MAP_STENCIL" }, + { 0x0D12, "GL_INDEX_SHIFT" }, + { 0x0D13, "GL_INDEX_OFFSET" }, + { 0x0D14, "GL_RED_SCALE" }, + { 0x0D15, "GL_RED_BIAS" }, + { 0x0D16, "GL_ZOOM_X" }, + { 0x0D17, "GL_ZOOM_Y" }, + { 0x0D18, "GL_GREEN_SCALE" }, + { 0x0D19, "GL_GREEN_BIAS" }, + { 0x0D1A, "GL_BLUE_SCALE" }, + { 0x0D1B, "GL_BLUE_BIAS" }, + { 0x0D1C, "GL_ALPHA_SCALE" }, + { 0x0D1D, "GL_ALPHA_BIAS" }, + { 0x0D1E, "GL_DEPTH_SCALE" }, + { 0x0D1F, "GL_DEPTH_BIAS" }, + { 0x0D30, "GL_MAX_EVAL_ORDER" }, + { 0x0D31, "GL_MAX_LIGHTS" }, + { 0x0D32, "GL_MAX_CLIP_PLANES" }, + { 0x0D33, "GL_MAX_TEXTURE_SIZE" }, + { 0x0D34, "GL_MAX_PIXEL_MAP_TABLE" }, + { 0x0D35, "GL_MAX_ATTRIB_STACK_DEPTH" }, + { 0x0D36, "GL_MAX_MODELVIEW_STACK_DEPTH" }, + { 0x0D37, "GL_MAX_NAME_STACK_DEPTH" }, + { 0x0D38, "GL_MAX_PROJECTION_STACK_DEPTH" }, + { 0x0D39, "GL_MAX_TEXTURE_STACK_DEPTH" }, + { 0x0D3A, "GL_MAX_VIEWPORT_DIMS" }, + { 0x0D3B, "GL_MAX_CLIENT_ATTRIB_STACK_DEPTH" }, + { 0x0D50, "GL_SUBPIXEL_BITS" }, + { 0x0D51, "GL_INDEX_BITS" }, + { 0x0D52, "GL_RED_BITS" }, + { 0x0D53, "GL_GREEN_BITS" }, + { 0x0D54, "GL_BLUE_BITS" }, + { 0x0D55, "GL_ALPHA_BITS" }, + { 0x0D56, "GL_DEPTH_BITS" }, + { 0x0D57, "GL_STENCIL_BITS" }, + { 0x0D58, "GL_ACCUM_RED_BITS" }, + { 0x0D59, "GL_ACCUM_GREEN_BITS" }, + { 0x0D5A, "GL_ACCUM_BLUE_BITS" }, + { 0x0D5B, "GL_ACCUM_ALPHA_BITS" }, + { 0x0D70, "GL_NAME_STACK_DEPTH" }, + { 0x0D80, "GL_AUTO_NORMAL" }, + { 0x0D90, "GL_MAP1_COLOR_4" }, + { 0x0D91, "GL_MAP1_INDEX" }, + { 0x0D92, "GL_MAP1_NORMAL" }, + { 0x0D93, "GL_MAP1_TEXTURE_COORD_1" }, + { 0x0D94, "GL_MAP1_TEXTURE_COORD_2" }, + { 0x0D95, "GL_MAP1_TEXTURE_COORD_3" }, + { 0x0D96, "GL_MAP1_TEXTURE_COORD_4" }, + { 0x0D97, "GL_MAP1_VERTEX_3" }, + { 0x0D98, "GL_MAP1_VERTEX_4" }, + { 0x0DB0, "GL_MAP2_COLOR_4" }, + { 0x0DB1, "GL_MAP2_INDEX" }, + { 0x0DB2, "GL_MAP2_NORMAL" }, + { 0x0DB3, "GL_MAP2_TEXTURE_COORD_1" }, + { 0x0DB4, "GL_MAP2_TEXTURE_COORD_2" }, + { 0x0DB5, "GL_MAP2_TEXTURE_COORD_3" }, + { 0x0DB6, "GL_MAP2_TEXTURE_COORD_4" }, + { 0x0DB7, "GL_MAP2_VERTEX_3" }, + { 0x0DB8, "GL_MAP2_VERTEX_4" }, + { 0x0DD0, "GL_MAP1_GRID_DOMAIN" }, + { 0x0DD1, "GL_MAP1_GRID_SEGMENTS" }, + { 0x0DD2, "GL_MAP2_GRID_DOMAIN" }, + { 0x0DD3, "GL_MAP2_GRID_SEGMENTS" }, + { 0x0DE0, "GL_TEXTURE_1D" }, + { 0x0DE1, "GL_TEXTURE_2D" }, + { 0x0DF0, "GL_FEEDBACK_BUFFER_POINTER" }, + { 0x0DF1, "GL_FEEDBACK_BUFFER_SIZE" }, + { 0x0DF2, "GL_FEEDBACK_BUFFER_TYPE" }, + { 0x0DF3, "GL_SELECTION_BUFFER_POINTER" }, + { 0x0DF4, "GL_SELECTION_BUFFER_SIZE" }, + { 0x1000, "GL_TEXTURE_WIDTH" }, + { 0x1001, "GL_TEXTURE_HEIGHT" }, + { 0x1003, "GL_TEXTURE_INTERNAL_FORMAT" }, + { 0x1004, "GL_TEXTURE_BORDER_COLOR" }, + { 0x1005, "GL_TEXTURE_BORDER" }, + { 0x1100, "GL_DONT_CARE" }, + { 0x1101, "GL_FASTEST" }, + { 0x1102, "GL_NICEST" }, + { 0x1200, "GL_AMBIENT" }, + { 0x1201, "GL_DIFFUSE" }, + { 0x1202, "GL_SPECULAR" }, + { 0x1203, "GL_POSITION" }, + { 0x1204, "GL_SPOT_DIRECTION" }, + { 0x1205, "GL_SPOT_EXPONENT" }, + { 0x1206, "GL_SPOT_CUTOFF" }, + { 0x1207, "GL_CONSTANT_ATTENUATION" }, + { 0x1208, "GL_LINEAR_ATTENUATION" }, + { 0x1209, "GL_QUADRATIC_ATTENUATION" }, + { 0x1300, "GL_COMPILE" }, + { 0x1301, "GL_COMPILE_AND_EXECUTE" }, + { 0x1400, "GL_BYTE " }, + { 0x1401, "GL_UBYTE" }, + { 0x1402, "GL_SHORT" }, + { 0x1403, "GL_USHRT" }, + { 0x1404, "GL_INT " }, + { 0x1405, "GL_UINT " }, + { 0x1406, "GL_FLOAT" }, + { 0x1407, "GL_2_BYTES" }, + { 0x1408, "GL_3_BYTES" }, + { 0x1409, "GL_4_BYTES" }, + { 0x140A, "GL_DOUBLE" }, + { 0x140B, "GL_HALF_FLOAT" }, + { 0x1500, "GL_CLEAR" }, + { 0x1501, "GL_AND" }, + { 0x1502, "GL_AND_REVERSE" }, + { 0x1503, "GL_COPY" }, + { 0x1504, "GL_AND_INVERTED" }, + { 0x1505, "GL_NOOP" }, + { 0x1506, "GL_XOR" }, + { 0x1507, "GL_OR" }, + { 0x1508, "GL_NOR" }, + { 0x1509, "GL_EQUIV" }, + { 0x150A, "GL_INVERT" }, + { 0x150B, "GL_OR_REVERSE" }, + { 0x150C, "GL_COPY_INVERTED" }, + { 0x150D, "GL_OR_INVERTED" }, + { 0x150E, "GL_NAND" }, + { 0x150F, "GL_SET" }, + { 0x1600, "GL_EMISSION" }, + { 0x1601, "GL_SHININESS" }, + { 0x1602, "GL_AMBIENT_AND_DIFFUSE" }, + { 0x1603, "GL_COLOR_INDEXES" }, + { 0x1700, "GL_MODELVIEW" }, + { 0x1700, "GL_MODELVIEW0_ARB" }, + { 0x1701, "GL_PROJECTION" }, + { 0x1702, "GL_TEXTURE" }, + { 0x1800, "GL_COLOR" }, + { 0x1801, "GL_DEPTH" }, + { 0x1802, "GL_STENCIL" }, + { 0x1900, "GL_COLOR_INDEX" }, + { 0x1901, "GL_STENCIL_INDEX" }, + { 0x1902, "GL_DEPTH_COMPONENT" }, + { 0x1903, "GL_RED" }, + { 0x1904, "GL_GREEN" }, + { 0x1905, "GL_BLUE" }, + { 0x1906, "GL_ALPHA" }, + { 0x1907, "GL_RGB" }, + { 0x1908, "GL_RGBA" }, + { 0x1909, "GL_LUMINANCE" }, + { 0x190A, "GL_LUMINANCE_ALPHA" }, + { 0x1A00, "GL_BITMAP" }, + { 0x1B00, "GL_POINT" }, + { 0x1B01, "GL_LINE" }, + { 0x1B02, "GL_FILL" }, + { 0x1C00, "GL_RENDER" }, + { 0x1C01, "GL_FEEDBACK" }, + { 0x1C02, "GL_SELECT" }, + { 0x1D00, "GL_FLAT" }, + { 0x1D01, "GL_SMOOTH" }, + { 0x1E00, "GL_KEEP" }, + { 0x1E01, "GL_REPLACE" }, + { 0x1E02, "GL_INCR" }, + { 0x1E03, "GL_DECR" }, + { 0x1F00, "GL_VENDOR" }, + { 0x1F01, "GL_RENDERER" }, + { 0x1F02, "GL_VERSION" }, + { 0x1F03, "GL_EXTENSIONS" }, + { 0x2000, "GL_S" }, + { 0x2001, "GL_T" }, + { 0x2002, "GL_R" }, + { 0x2003, "GL_Q" }, + { 0x2100, "GL_MODULATE" }, + { 0x2101, "GL_DECAL" }, + { 0x2200, "GL_TEXTURE_ENV_MODE" }, + { 0x2201, "GL_TEXTURE_ENV_COLOR" }, + { 0x2300, "GL_TEXTURE_ENV" }, + { 0x2400, "GL_EYE_LINEAR" }, + { 0x2401, "GL_OBJECT_LINEAR" }, + { 0x2402, "GL_SPHERE_MAP" }, + { 0x2500, "GL_TEXTURE_GEN_MODE" }, + { 0x2501, "GL_OBJECT_PLANE" }, + { 0x2502, "GL_EYE_PLANE" }, + { 0x2600, "GL_NEAREST" }, + { 0x2601, "GL_LINEAR" }, + { 0x2700, "GL_NEAREST_MIPMAP_NEAREST" }, + { 0x2701, "GL_LINEAR_MIPMAP_NEAREST" }, + { 0x2702, "GL_NEAREST_MIPMAP_LINEAR" }, + { 0x2703, "GL_LINEAR_MIPMAP_LINEAR" }, + { 0x2800, "GL_TEXTURE_MAG_FILTER" }, + { 0x2801, "GL_TEXTURE_MIN_FILTER" }, + { 0x2802, "GL_TEXTURE_WRAP_S" }, + { 0x2803, "GL_TEXTURE_WRAP_T" }, + { 0x2900, "GL_CLAMP" }, + { 0x2901, "GL_REPEAT" }, + { 0x2A00, "GL_POLYGON_OFFSET_UNITS" }, + { 0x2A01, "GL_POLYGON_OFFSET_POINT" }, + { 0x2A02, "GL_POLYGON_OFFSET_LINE" }, + { 0x2A10, "GL_R3_G3_B2" }, + { 0x2A20, "GL_V2F" }, + { 0x2A21, "GL_V3F" }, + { 0x2A22, "GL_C4UB_V2F" }, + { 0x2A23, "GL_C4UB_V3F" }, + { 0x2A24, "GL_C3F_V3F" }, + { 0x2A25, "GL_N3F_V3F" }, + { 0x2A26, "GL_C4F_N3F_V3F" }, + { 0x2A27, "GL_T2F_V3F" }, + { 0x2A28, "GL_T4F_V4F" }, + { 0x2A29, "GL_T2F_C4UB_V3F" }, + { 0x2A2A, "GL_T2F_C3F_V3F" }, + { 0x2A2B, "GL_T2F_N3F_V3F" }, + { 0x2A2C, "GL_T2F_C4F_N3F_V3F" }, + { 0x2A2D, "GL_T4F_C4F_N3F_V4F" }, + { 0x3000, "GL_CLIP_PLANE0" }, + { 0x3001, "GL_CLIP_PLANE1" }, + { 0x3002, "GL_CLIP_PLANE2" }, + { 0x3003, "GL_CLIP_PLANE3" }, + { 0x3004, "GL_CLIP_PLANE4" }, + { 0x3005, "GL_CLIP_PLANE5" }, + { 0x4000, "GL_LIGHT0" }, + { 0x4001, "GL_LIGHT1" }, + { 0x4002, "GL_LIGHT2" }, + { 0x4003, "GL_LIGHT3" }, + { 0x4004, "GL_LIGHT4" }, + { 0x4005, "GL_LIGHT5" }, + { 0x4006, "GL_LIGHT6" }, + { 0x4007, "GL_LIGHT7" }, + { 0x8000, "GL_ABGR_EXT" }, + { 0x8001, "GL_CONSTANT_COLOR" }, + { 0x8002, "GL_ONE_MINUS_CONSTANT_COLOR" }, + { 0x8003, "GL_CONSTANT_ALPHA" }, + { 0x8004, "GL_ONE_MINUS_CONSTANT_ALPHA" }, + { 0x8005, "GL_BLEND_COLOR" }, + { 0x8006, "GL_FUNC_ADD" }, + { 0x8007, "GL_MIN" }, + { 0x8008, "GL_MAX" }, + { 0x8009, "GL_BLEND_EQUATION_RGB" }, + { 0x8009, "GL_BLEND_EQUATION" }, + { 0x800A, "GL_FUNC_SUBTRACT" }, + { 0x800B, "GL_FUNC_REVERSE_SUBTRACT" }, + { 0x8010, "GL_CONVOLUTION_1D" }, + { 0x8011, "GL_CONVOLUTION_2D" }, + { 0x8012, "GL_SEPARABLE_2D" }, + { 0x8013, "GL_CONVOLUTION_BORDER_MODE" }, + { 0x8014, "GL_CONVOLUTION_FILTER_SCALE" }, + { 0x8015, "GL_CONVOLUTION_FILTER_BIAS" }, + { 0x8016, "GL_REDUCE" }, + { 0x8017, "GL_CONVOLUTION_FORMAT" }, + { 0x8018, "GL_CONVOLUTION_WIDTH" }, + { 0x8019, "GL_CONVOLUTION_HEIGHT" }, + { 0x801A, "GL_MAX_CONVOLUTION_WIDTH" }, + { 0x801B, "GL_MAX_CONVOLUTION_HEIGHT" }, + { 0x801C, "GL_POST_CONVOLUTION_RED_SCALE" }, + { 0x801D, "GL_POST_CONVOLUTION_GREEN_SCALE" }, + { 0x801E, "GL_POST_CONVOLUTION_BLUE_SCALE" }, + { 0x801F, "GL_POST_CONVOLUTION_ALPHA_SCALE" }, + { 0x8020, "GL_POST_CONVOLUTION_RED_BIAS" }, + { 0x8021, "GL_POST_CONVOLUTION_GREEN_BIAS" }, + { 0x8022, "GL_POST_CONVOLUTION_BLUE_BIAS" }, + { 0x8023, "GL_POST_CONVOLUTION_ALPHA_BIAS" }, + { 0x8024, "GL_HISTOGRAM" }, + { 0x8025, "GL_PROXY_HISTOGRAM" }, + { 0x8026, "GL_HISTOGRAM_WIDTH" }, + { 0x8027, "GL_HISTOGRAM_FORMAT" }, + { 0x8028, "GL_HISTOGRAM_RED_SIZE" }, + { 0x8029, "GL_HISTOGRAM_GREEN_SIZE" }, + { 0x802A, "GL_HISTOGRAM_BLUE_SIZE" }, + { 0x802B, "GL_HISTOGRAM_ALPHA_SIZE" }, + { 0x802C, "GL_HISTOGRAM_LUMINANCE_SIZE" }, + { 0x802D, "GL_HISTOGRAM_SINK" }, + { 0x802E, "GL_MINMAX" }, + { 0x802F, "GL_MINMAX_FORMAT" }, + { 0x8030, "GL_MINMAX_SINK" }, + { 0x8031, "GL_TABLE_TOO_LARGE" }, + { 0x8032, "GL_UNSIGNED_BYTE_3_3_2" }, + { 0x8033, "GL_UNSIGNED_SHORT_4_4_4_4" }, + { 0x8034, "GL_UNSIGNED_SHORT_5_5_5_1" }, + { 0x8035, "GL_UNSIGNED_INT_8_8_8_8" }, + { 0x8036, "GL_UNSIGNED_INT_10_10_10_2" }, + { 0x8037, "GL_POLYGON_OFFSET_FILL" }, + { 0x8038, "GL_POLYGON_OFFSET_FACTOR" }, + { 0x803A, "GL_RESCALE_NORMAL" }, + { 0x803B, "GL_ALPHA4" }, + { 0x803C, "GL_ALPHA8" }, + { 0x803D, "GL_ALPHA12" }, + { 0x803E, "GL_ALPHA16" }, + { 0x803F, "GL_LUMINANCE4" }, + { 0x8040, "GL_LUMINANCE8" }, + { 0x8041, "GL_LUMINANCE12" }, + { 0x8042, "GL_LUMINANCE16" }, + { 0x8043, "GL_LUMINANCE4_ALPHA4" }, + { 0x8044, "GL_LUMINANCE6_ALPHA2" }, + { 0x8045, "GL_LUMINANCE8_ALPHA8" }, + { 0x8046, "GL_LUMINANCE12_ALPHA4" }, + { 0x8047, "GL_LUMINANCE12_ALPHA12" }, + { 0x8048, "GL_LUMINANCE16_ALPHA16" }, + { 0x8049, "GL_INTENSITY" }, + { 0x804A, "GL_INTENSITY4" }, + { 0x804B, "GL_INTENSITY8" }, + { 0x804C, "GL_INTENSITY12" }, + { 0x804D, "GL_INTENSITY16" }, + { 0x804F, "GL_RGB4" }, + { 0x8050, "GL_RGB5" }, + { 0x8051, "GL_RGB8" }, + { 0x8052, "GL_RGB10" }, + { 0x8053, "GL_RGB12" }, + { 0x8054, "GL_RGB16" }, + { 0x8055, "GL_RGBA2" }, + { 0x8056, "GL_RGBA4" }, + { 0x8057, "GL_RGB5_A1" }, + { 0x8058, "GL_RGBA8" }, + { 0x8059, "GL_RGB10_A2" }, + { 0x805A, "GL_RGBA12" }, + { 0x805B, "GL_RGBA16" }, + { 0x805C, "GL_TEXTURE_RED_SIZE" }, + { 0x805D, "GL_TEXTURE_GREEN_SIZE" }, + { 0x805E, "GL_TEXTURE_BLUE_SIZE" }, + { 0x805F, "GL_TEXTURE_ALPHA_SIZE" }, + { 0x8060, "GL_TEXTURE_LUMINANCE_SIZE" }, + { 0x8061, "GL_TEXTURE_INTENSITY_SIZE" }, + { 0x8063, "GL_PROXY_TEXTURE_1D" }, + { 0x8064, "GL_PROXY_TEXTURE_2D" }, + { 0x8066, "GL_TEXTURE_PRIORITY" }, + { 0x8067, "GL_TEXTURE_RESIDENT" }, + { 0x8068, "GL_TEXTURE_BINDING_1D" }, + { 0x8069, "GL_TEXTURE_BINDING_2D" }, + { 0x806A, "GL_TEXTURE_BINDING_3D" }, + { 0x806B, "GL_PACK_SKIP_IMAGES" }, + { 0x806C, "GL_PACK_IMAGE_HEIGHT" }, + { 0x806D, "GL_UNPACK_SKIP_IMAGES" }, + { 0x806E, "GL_UNPACK_IMAGE_HEIGHT" }, + { 0x806F, "GL_TEXTURE_3D" }, + { 0x8070, "GL_PROXY_TEXTURE_3D" }, + { 0x8071, "GL_TEXTURE_DEPTH" }, + { 0x8072, "GL_TEXTURE_WRAP_R" }, + { 0x8073, "GL_MAX_3D_TEXTURE_SIZE" }, + { 0x8074, "GL_VERTEX_ARRAY" }, + { 0x8075, "GL_NORMAL_ARRAY" }, + { 0x8076, "GL_COLOR_ARRAY" }, + { 0x8077, "GL_INDEX_ARRAY" }, + { 0x8078, "GL_TEXTURE_COORD_ARRAY" }, + { 0x8079, "GL_EDGE_FLAG_ARRAY" }, + { 0x807A, "GL_VERTEX_ARRAY_SIZE" }, + { 0x807B, "GL_VERTEX_ARRAY_TYPE" }, + { 0x807C, "GL_VERTEX_ARRAY_STRIDE" }, + { 0x807E, "GL_NORMAL_ARRAY_TYPE" }, + { 0x807F, "GL_NORMAL_ARRAY_STRIDE" }, + { 0x8081, "GL_COLOR_ARRAY_SIZE" }, + { 0x8082, "GL_COLOR_ARRAY_TYPE" }, + { 0x8083, "GL_COLOR_ARRAY_STRIDE" }, + { 0x8085, "GL_INDEX_ARRAY_TYPE" }, + { 0x8086, "GL_INDEX_ARRAY_STRIDE" }, + { 0x8088, "GL_TEXTURE_COORD_ARRAY_SIZE" }, + { 0x8089, "GL_TEXTURE_COORD_ARRAY_TYPE" }, + { 0x808A, "GL_TEXTURE_COORD_ARRAY_STRIDE" }, + { 0x808C, "GL_EDGE_FLAG_ARRAY_STRIDE" }, + { 0x808E, "GL_VERTEX_ARRAY_POINTER" }, + { 0x808F, "GL_NORMAL_ARRAY_POINTER" }, + { 0x8090, "GL_COLOR_ARRAY_POINTER" }, + { 0x8091, "GL_INDEX_ARRAY_POINTER" }, + { 0x8092, "GL_TEXTURE_COORD_ARRAY_POINTER" }, + { 0x8093, "GL_EDGE_FLAG_ARRAY_POINTER" }, + { 0x809D, "GL_MULTISAMPLE_ARB" }, + { 0x809D, "GL_MULTISAMPLE" }, + { 0x809E, "GL_SAMPLE_ALPHA_TO_COVERAGE_ARB" }, + { 0x809E, "GL_SAMPLE_ALPHA_TO_COVERAGE" }, + { 0x809F, "GL_SAMPLE_ALPHA_TO_ONE_ARB" }, + { 0x809F, "GL_SAMPLE_ALPHA_TO_ONE" }, + { 0x80A0, "GL_SAMPLE_COVERAGE_ARB" }, + { 0x80A0, "GL_SAMPLE_COVERAGE" }, + { 0x80A0, "GL_SAMPLE_MASK_EXT" }, + { 0x80A1, "GL_1PASS_EXT" }, + { 0x80A2, "GL_2PASS_0_EXT" }, + { 0x80A3, "GL_2PASS_1_EXT" }, + { 0x80A4, "GL_4PASS_0_EXT" }, + { 0x80A5, "GL_4PASS_1_EXT" }, + { 0x80A6, "GL_4PASS_2_EXT" }, + { 0x80A7, "GL_4PASS_3_EXT" }, + { 0x80A8, "GL_SAMPLE_BUFFERS" }, + { 0x80A9, "GL_SAMPLES" }, + { 0x80AA, "GL_SAMPLE_COVERAGE_VALUE" }, + { 0x80AB, "GL_SAMPLE_COVERAGE_INVERT" }, + { 0x80AC, "GL_SAMPLE_PATTERN_EXT" }, + { 0x80B1, "GL_COLOR_MATRIX" }, + { 0x80B2, "GL_COLOR_MATRIX_STACK_DEPTH" }, + { 0x80B3, "GL_MAX_COLOR_MATRIX_STACK_DEPTH" }, + { 0x80B4, "GL_POST_COLOR_MATRIX_RED_SCALE" }, + { 0x80B5, "GL_POST_COLOR_MATRIX_GREEN_SCALE" }, + { 0x80B6, "GL_POST_COLOR_MATRIX_BLUE_SCALE" }, + { 0x80B7, "GL_POST_COLOR_MATRIX_ALPHA_SCALE" }, + { 0x80B8, "GL_POST_COLOR_MATRIX_RED_BIAS" }, + { 0x80B9, "GL_POST_COLOR_MATRIX_GREEN_BIAS" }, + { 0x80BA, "GL_POST_COLOR_MATRIX_BLUE_BIAS" }, + { 0x80BB, "GL_POST_COLOR_MATRIX_ALPHA_BIAS" }, + { 0x80BF, "GL_TEXTURE_COMPARE_FAIL_VALUE_ARB" }, + { 0x80C8, "GL_BLEND_DST_RGB" }, + { 0x80C9, "GL_BLEND_SRC_RGB" }, + { 0x80CA, "GL_BLEND_DST_ALPHA" }, + { 0x80CB, "GL_BLEND_SRC_ALPHA" }, + { 0x80CC, "GL_422_EXT" }, + { 0x80CD, "GL_422_REV_EXT" }, + { 0x80CE, "GL_422_AVERAGE_EXT" }, + { 0x80CF, "GL_422_REV_AVERAGE_EXT" }, + { 0x80D0, "GL_COLOR_TABLE" }, + { 0x80D1, "GL_POST_CONVOLUTION_COLOR_TABLE" }, + { 0x80D2, "GL_POST_COLOR_MATRIX_COLOR_TABLE" }, + { 0x80D3, "GL_PROXY_COLOR_TABLE" }, + { 0x80D4, "GL_PROXY_POST_CONVOLUTION_COLOR_TABLE" }, + { 0x80D5, "GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE" }, + { 0x80D6, "GL_COLOR_TABLE_SCALE" }, + { 0x80D7, "GL_COLOR_TABLE_BIAS" }, + { 0x80D8, "GL_COLOR_TABLE_FORMAT" }, + { 0x80D9, "GL_COLOR_TABLE_WIDTH" }, + { 0x80DA, "GL_COLOR_TABLE_RED_SIZE" }, + { 0x80DB, "GL_COLOR_TABLE_GREEN_SIZE" }, + { 0x80DC, "GL_COLOR_TABLE_BLUE_SIZE" }, + { 0x80DD, "GL_COLOR_TABLE_ALPHA_SIZE" }, + { 0x80DE, "GL_COLOR_TABLE_LUMINANCE_SIZE" }, + { 0x80DF, "GL_COLOR_TABLE_INTENSITY_SIZE" }, + { 0x80E0, "GL_BGR_EXT" }, + { 0x80E0, "GL_BGR" }, + { 0x80E1, "GL_BGRA_EXT" }, + { 0x80E1, "GL_BGRA" }, + { 0x80E1, "GL_BGRA" }, + { 0x80E2, "GL_COLOR_INDEX1_EXT" }, + { 0x80E3, "GL_COLOR_INDEX2_EXT" }, + { 0x80E4, "GL_COLOR_INDEX4_EXT" }, + { 0x80E5, "GL_COLOR_INDEX8_EXT" }, + { 0x80E6, "GL_COLOR_INDEX12_EXT" }, + { 0x80E7, "GL_COLOR_INDEX16_EXT" }, + { 0x80E8, "GL_MAX_ELEMENTS_VERTICES_EXT" }, + { 0x80E8, "GL_MAX_ELEMENTS_VERTICES" }, + { 0x80E9, "GL_MAX_ELEMENTS_INDICES_EXT" }, + { 0x80E9, "GL_MAX_ELEMENTS_INDICES" }, + { 0x80ED, "GL_TEXTURE_INDEX_SIZE_EXT" }, + { 0x80F0, "GL_CLIP_VOLUME_CLIPPING_HINT_EXT" }, + { 0x8126, "GL_POINT_SIZE_MIN_ARB" }, + { 0x8126, "GL_POINT_SIZE_MIN" }, + { 0x8127, "GL_POINT_SIZE_MAX_ARB" }, + { 0x8127, "GL_POINT_SIZE_MAX" }, + { 0x8128, "GL_POINT_FADE_THRESHOLD_SIZE_ARB" }, + { 0x8128, "GL_POINT_FADE_THRESHOLD_SIZE" }, + { 0x8129, "GL_POINT_DISTANCE_ATTENUATION_ARB" }, + { 0x8129, "GL_POINT_DISTANCE_ATTENUATION" }, + { 0x812D, "GL_CLAMP_TO_BORDER_ARB" }, + { 0x812D, "GL_CLAMP_TO_BORDER" }, + { 0x812F, "GL_CLAMP_TO_EDGE" }, + { 0x813A, "GL_TEXTURE_MIN_LOD" }, + { 0x813B, "GL_TEXTURE_MAX_LOD" }, + { 0x813C, "GL_TEXTURE_BASE_LEVEL" }, + { 0x813D, "GL_TEXTURE_MAX_LEVEL" }, + { 0x8151, "GL_CONSTANT_BORDER" }, + { 0x8153, "GL_REPLICATE_BORDER" }, + { 0x8154, "GL_CONVOLUTION_BORDER_COLOR" }, + { 0x8191, "GL_GENERATE_MIPMAP" }, + { 0x8192, "GL_GENERATE_MIPMAP_HINT" }, + { 0x81A5, "GL_DEPTH_COMPONENT16_ARB" }, + { 0x81A5, "GL_DEPTH_COMPONENT16" }, + { 0x81A6, "GL_DEPTH_COMPONENT24_ARB" }, + { 0x81A6, "GL_DEPTH_COMPONENT24" }, + { 0x81A7, "GL_DEPTH_COMPONENT32_ARB" }, + { 0x81A7, "GL_DEPTH_COMPONENT32" }, + { 0x81A8, "GL_ARRAY_ELEMENT_LOCK_FIRST_EXT" }, + { 0x81A9, "GL_ARRAY_ELEMENT_LOCK_COUNT_EXT" }, + { 0x81AA, "GL_CULL_VERTEX_EXT" }, + { 0x81AB, "GL_CULL_VERTEX_EYE_POSITION_EXT" }, + { 0x81AC, "GL_CULL_VERTEX_OBJECT_POSITION_EXT" }, + { 0x81AD, "GL_IUI_V2F_EXT" }, + { 0x81AE, "GL_IUI_V3F_EXT" }, + { 0x81AF, "GL_IUI_N3F_V2F_EXT" }, + { 0x81B0, "GL_IUI_N3F_V3F_EXT" }, + { 0x81B1, "GL_T2F_IUI_V2F_EXT" }, + { 0x81B2, "GL_T2F_IUI_V3F_EXT" }, + { 0x81B3, "GL_T2F_IUI_N3F_V2F_EXT" }, + { 0x81B4, "GL_T2F_IUI_N3F_V3F_EXT" }, + { 0x81B5, "GL_INDEX_TEST_EXT" }, + { 0x81B6, "GL_INDEX_TEST_FUNC_EXT" }, + { 0x81B7, "GL_INDEX_TEST_REF_EXT" }, + { 0x81B8, "GL_INDEX_MATERIAL_EXT" }, + { 0x81B9, "GL_INDEX_MATERIAL_PARAMETER_EXT" }, + { 0x81BA, "GL_INDEX_MATERIAL_FACE_EXT" }, + { 0x81F8, "GL_LIGHT_MODEL_COLOR_CONTROL_EXT" }, + { 0x81F8, "GL_LIGHT_MODEL_COLOR_CONTROL" }, + { 0x81F9, "GL_SINGLE_COLOR_EXT" }, + { 0x81F9, "GL_SINGLE_COLOR" }, + { 0x81FA, "GL_SEPARATE_SPECULAR_COLOR_EXT" }, + { 0x81FA, "GL_SEPARATE_SPECULAR_COLOR" }, + { 0x81FB, "GL_SHARED_TEXTURE_PALETTE_EXT" }, + { 0x8210, "GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING" }, + { 0x8211, "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" }, + { 0x8212, "GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE" }, + { 0x8213, "GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE" }, + { 0x8214, "GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE" }, + { 0x8215, "GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE" }, + { 0x8216, "GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE" }, + { 0x8217, "GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE" }, + { 0x8218, "GL_FRAMEBUFFER_DEFAULT" }, + { 0x8219, "GL_FRAMEBUFFER_UNDEFINED" }, + { 0x821A, "GL_DEPTH_STENCIL_ATTACHMENT" }, + { 0x8225, "GL_COMPRESSED_RED" }, + { 0x8226, "GL_COMPRESSED_RG" }, + { 0x8227, "GL_RG" }, + { 0x8228, "GL_RG_INTEGER" }, + { 0x8229, "GL_R8" }, + { 0x822A, "GL_R16" }, + { 0x822B, "GL_RG8" }, + { 0x822C, "GL_RG16" }, + { 0x822D, "GL_R16F" }, + { 0x822E, "GL_R32F" }, + { 0x822F, "GL_RG16F" }, + { 0x8230, "GL_RG32F" }, + { 0x8231, "GL_R8I" }, + { 0x8232, "GL_R8UI" }, + { 0x8233, "GL_R16I" }, + { 0x8234, "GL_R16UI" }, + { 0x8235, "GL_R32I" }, + { 0x8236, "GL_R32UI" }, + { 0x8237, "GL_RG8I" }, + { 0x8238, "GL_RG8UI" }, + { 0x8239, "GL_RG16I" }, + { 0x823A, "GL_RG16UI" }, + { 0x823B, "GL_RG32I" }, + { 0x823C, "GL_RG32UI" }, + { 0x8330, "GL_PIXEL_TRANSFORM_2D_EXT" }, + { 0x8331, "GL_PIXEL_MAG_FILTER_EXT" }, + { 0x8332, "GL_PIXEL_MIN_FILTER_EXT" }, + { 0x8333, "GL_PIXEL_CUBIC_WEIGHT_EXT" }, + { 0x8334, "GL_CUBIC_EXT" }, + { 0x8335, "GL_AVERAGE_EXT" }, + { 0x8336, "GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT" }, + { 0x8337, "GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT" }, + { 0x8338, "GL_PIXEL_TRANSFORM_2D_MATRIX_EXT" }, + { 0x8349, "GL_FRAGMENT_MATERIAL_EXT" }, + { 0x834A, "GL_FRAGMENT_NORMAL_EXT" }, + { 0x834C, "GL_FRAGMENT_COLOR_EXT" }, + { 0x834D, "GL_ATTENUATION_EXT" }, + { 0x834E, "GL_SHADOW_ATTENUATION_EXT" }, + { 0x834F, "GL_TEXTURE_APPLICATION_MODE_EXT" }, + { 0x8350, "GL_TEXTURE_LIGHT_EXT" }, + { 0x8351, "GL_TEXTURE_MATERIAL_FACE_EXT" }, + { 0x8352, "GL_TEXTURE_MATERIAL_PARAMETER_EXT" }, + { 0x8362, "GL_UNSIGNED_BYTE_2_3_3_REV" }, + { 0x8363, "GL_UNSIGNED_SHORT_5_6_5" }, + { 0x8364, "GL_UNSIGNED_SHORT_5_6_5_REV" }, + { 0x8365, "GL_UNSIGNED_SHORT_4_4_4_4_REV" }, + { 0x8366, "GL_UNSIGNED_SHORT_1_5_5_5_REV" }, + { 0x8367, "GL_UNSIGNED_INT_8_8_8_8_REV" }, + { 0x8368, "GL_UNSIGNED_INT_2_10_10_10_REV" }, + { 0x8370, "GL_MIRRORED_REPEAT_ARB" }, + { 0x8370, "GL_MIRRORED_REPEAT" }, + { 0x83F0, "GL_COMPRESSED_RGB_S3TC_DXT1_EXT" }, + { 0x83F1, "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT" }, + { 0x83F2, "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT" }, + { 0x83F3, "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT" }, + { 0x8439, "GL_TANGENT_ARRAY_EXT" }, + { 0x843A, "GL_BINORMAL_ARRAY_EXT" }, + { 0x843B, "GL_CURRENT_TANGENT_EXT" }, + { 0x843C, "GL_CURRENT_BINORMAL_EXT" }, + { 0x843E, "GL_TANGENT_ARRAY_TYPE_EXT" }, + { 0x843F, "GL_TANGENT_ARRAY_STRIDE_EXT" }, + { 0x8440, "GL_BINORMAL_ARRAY_TYPE_EXT" }, + { 0x8441, "GL_BINORMAL_ARRAY_STRIDE_EXT" }, + { 0x8442, "GL_TANGENT_ARRAY_POINTER_EXT" }, + { 0x8443, "GL_BINORMAL_ARRAY_POINTER_EXT" }, + { 0x8444, "GL_MAP1_TANGENT_EXT" }, + { 0x8445, "GL_MAP2_TANGENT_EXT" }, + { 0x8446, "GL_MAP1_BINORMAL_EXT" }, + { 0x8447, "GL_MAP2_BINORMAL_EXT" }, + { 0x8450, "GL_FOG_COORD_SRC" }, + { 0x8450, "GL_FOG_COORDINATE_SOURCE_EXT" }, + { 0x8450, "GL_FOG_COORDINATE_SOURCE" }, + { 0x8451, "GL_FOG_COORD" }, + { 0x8451, "GL_FOG_COORDINATE_EXT" }, + { 0x8451, "GL_FOG_COORDINATE" }, + { 0x8452, "GL_FRAGMENT_DEPTH_EXT" }, + { 0x8452, "GL_FRAGMENT_DEPTH" }, + { 0x8453 , "GL_CURRENT_FOG_COORD" }, + { 0x8453 , "GL_CURRENT_FOG_COORDINATE" }, + { 0x8453, "GL_CURRENT_FOG_COORDINATE_EXT" }, + { 0x8454, "GL_FOG_COORD_ARRAY_TYPE" }, + { 0x8454, "GL_FOG_COORDINATE_ARRAY_TYPE_EXT" }, + { 0x8454, "GL_FOG_COORDINATE_ARRAY_TYPE" }, + { 0x8455, "GL_FOG_COORD_ARRAY_STRIDE" }, + { 0x8455, "GL_FOG_COORDINATE_ARRAY_STRIDE_EXT" }, + { 0x8455, "GL_FOG_COORDINATE_ARRAY_STRIDE" }, + { 0x8456, "GL_FOG_COORD_ARRAY_POINTER" }, + { 0x8456, "GL_FOG_COORDINATE_ARRAY_POINTER_EXT" }, + { 0x8456, "GL_FOG_COORDINATE_ARRAY_POINTER" }, + { 0x8457, "GL_FOG_COORD_ARRAY" }, + { 0x8457, "GL_FOG_COORDINATE_ARRAY_EXT" }, + { 0x8457, "GL_FOG_COORDINATE_ARRAY" }, + { 0x8458, "GL_COLOR_SUM_ARB" }, + { 0x8458, "GL_COLOR_SUM_EXT" }, + { 0x8458, "GL_COLOR_SUM" }, + { 0x8459, "GL_CURRENT_SECONDARY_COLOR_EXT" }, + { 0x8459, "GL_CURRENT_SECONDARY_COLOR" }, + { 0x845A, "GL_SECONDARY_COLOR_ARRAY_SIZE_EXT" }, + { 0x845A, "GL_SECONDARY_COLOR_ARRAY_SIZE" }, + { 0x845B, "GL_SECONDARY_COLOR_ARRAY_TYPE_EXT" }, + { 0x845B, "GL_SECONDARY_COLOR_ARRAY_TYPE" }, + { 0x845C, "GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT" }, + { 0x845C, "GL_SECONDARY_COLOR_ARRAY_STRIDE" }, + { 0x845D, "GL_SECONDARY_COLOR_ARRAY_POINTER_EXT" }, + { 0x845D, "GL_SECONDARY_COLOR_ARRAY_POINTER" }, + { 0x845E, "GL_SECONDARY_COLOR_ARRAY_EXT" }, + { 0x845E, "GL_SECONDARY_COLOR_ARRAY" }, + { 0x845F, "GL_CURRENT_RASTER_SECONDARY_COLOR" }, + { 0x846D, "GL_ALIASED_POINT_SIZE_RANGE" }, + { 0x846E, "GL_ALIASED_LINE_WIDTH_RANGE" }, + { 0x84C0, "GL_TEXTURE0" }, + { 0x84C1, "GL_TEXTURE1" }, + { 0x84C2, "GL_TEXTURE2" }, + { 0x84C3, "GL_TEXTURE3" }, + { 0x84C4, "GL_TEXTURE4" }, + { 0x84C5, "GL_TEXTURE5" }, + { 0x84C6, "GL_TEXTURE6" }, + { 0x84C7, "GL_TEXTURE7" }, + { 0x84C8, "GL_TEXTURE8" }, + { 0x84C9, "GL_TEXTURE9" }, + { 0x84CA, "GL_TEXTURE10" }, + { 0x84CB, "GL_TEXTURE11" }, + { 0x84CC, "GL_TEXTURE12" }, + { 0x84CD, "GL_TEXTURE13" }, + { 0x84CE, "GL_TEXTURE14" }, + { 0x84CF, "GL_TEXTURE15" }, + { 0x84D0, "GL_TEXTURE16" }, + { 0x84D1, "GL_TEXTURE17" }, + { 0x84D2, "GL_TEXTURE18" }, + { 0x84D3, "GL_TEXTURE19" }, + { 0x84D4, "GL_TEXTURE20" }, + { 0x84D5, "GL_TEXTURE21" }, + { 0x84D6, "GL_TEXTURE22" }, + { 0x84D7, "GL_TEXTURE23" }, + { 0x84D8, "GL_TEXTURE24" }, + { 0x84D9, "GL_TEXTURE25" }, + { 0x84DA, "GL_TEXTURE26" }, + { 0x84DB, "GL_TEXTURE27" }, + { 0x84DC, "GL_TEXTURE28" }, + { 0x84DD, "GL_TEXTURE29" }, + { 0x84DE, "GL_TEXTURE30" }, + { 0x84DF, "GL_TEXTURE31" }, + { 0x84E0, "GL_ACTIVE_TEXTURE" }, + { 0x84E1, "GL_CLIENT_ACTIVE_TEXTURE" }, + { 0x84E2, "GL_MAX_TEXTURE_UNITS" }, + { 0x84E3, "GL_TRANSPOSE_MODELVIEW_MATRIX" }, + { 0x84E4, "GL_TRANSPOSE_PROJECTION_MATRIX" }, + { 0x84E5, "GL_TRANSPOSE_TEXTURE_MATRIX" }, + { 0x84E6, "GL_TRANSPOSE_COLOR_MATRIX" }, + { 0x84E7, "GL_SUBTRACT" }, + { 0x84E8, "GL_MAX_RENDERBUFFER_SIZE" }, + { 0x84E9, "GL_COMPRESSED_ALPHA" }, + { 0x84EA, "GL_COMPRESSED_LUMINANCE" }, + { 0x84EB, "GL_COMPRESSED_LUMINANCE_ALPHA" }, + { 0x84EC, "GL_COMPRESSED_INTENSITY" }, + { 0x84ED, "GL_COMPRESSED_RGB" }, + { 0x84EE, "GL_COMPRESSED_RGBA" }, + { 0x84EF, "GL_TEXTURE_COMPRESSION_HINT" }, + { 0x84F5, "GL_TEXTURE_RECTANGLE_EXT" }, + { 0x84F6, "GL_TEXTURE_BINDING_RECTANGLE_EXT" }, + { 0x84F7, "GL_PROXY_TEXTURE_RECTANGLE_EXT" }, + { 0x84F8, "GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT" }, + { 0x84F9, "GL_DEPTH_STENCIL" }, + { 0x84FA, "GL_UNSIGNED_INT_24_8" }, + { 0x84FD, "GL_MAX_TEXTURE_LOD_BIAS" }, + { 0x84FE, "GL_TEXTURE_MAX_ANISOTROPY_EXT" }, + { 0x84FF, "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" }, + { 0x8500, "GL_TEXTURE_FILTER_CONTROL" }, + { 0x8501, "GL_TEXTURE_LOD_BIAS" }, + { 0x8502, "GL_MODELVIEW1_STACK_DEPTH_EXT" }, + { 0x8506, "GL_MODELVIEW_MATRIX1_EXT" }, + { 0x8507, "GL_INCR_WRAP" }, + { 0x8508, "GL_DECR_WRAP" }, + { 0x8509, "GL_VERTEX_WEIGHTING_EXT" }, + { 0x850A, "GL_MODELVIEW1_ARB" }, + { 0x850B, "GL_CURRENT_VERTEX_WEIGHT_EXT" }, + { 0x850C, "GL_VERTEX_WEIGHT_ARRAY_EXT" }, + { 0x850D, "GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT" }, + { 0x850E, "GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT" }, + { 0x850F, "GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT" }, + { 0x8510, "GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT" }, + { 0x8511, "GL_NORMAL_MAP_ARB" }, + { 0x8511, "GL_NORMAL_MAP_EXT" }, + { 0x8511, "GL_NORMAL_MAP" }, + { 0x8512, "GL_REFLECTION_MAP_ARB" }, + { 0x8512, "GL_REFLECTION_MAP_EXT" }, + { 0x8512, "GL_REFLECTION_MAP" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP_ARB" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP_EXT" }, + { 0x8513, "GL_TEXTURE_CUBE_MAP" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP_ARB" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP_EXT" }, + { 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP" }, + { 0x8515, "GL_TEXTURE_CUBE_MAP_POSITIVE_X" }, + { 0x8516, "GL_TEXTURE_CUBE_MAP_NEGATIVE_X" }, + { 0x8517, "GL_TEXTURE_CUBE_MAP_POSITIVE_Y" }, + { 0x8518, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y" }, + { 0x8519, "GL_TEXTURE_CUBE_MAP_POSITIVE_Z" }, + { 0x851A, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z" }, + { 0x851B, "GL_PROXY_TEXTURE_CUBE_MAP" }, + { 0x851C, "GL_MAX_CUBE_MAP_TEXTURE_SIZE" }, + { 0x851D, "GL_VERTEX_ARRAY_RANGE_APPLE" }, + { 0x851E, "GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE" }, + { 0x851F, "GL_VERTEX_ARRAY_STORAGE_HINT_APPLE" }, + { 0x8520, "GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_APPLE" }, + { 0x8521, "GL_VERTEX_ARRAY_RANGE_POINTER_APPLE" }, + { 0x8570, "GL_COMBINE_ARB" }, + { 0x8570, "GL_COMBINE_EXT" }, + { 0x8570, "GL_COMBINE" }, + { 0x8571, "GL_COMBINE_RGB_ARB" }, + { 0x8571, "GL_COMBINE_RGB_EXT" }, + { 0x8571, "GL_COMBINE_RGB" }, + { 0x8572, "GL_COMBINE_ALPHA_ARB" }, + { 0x8572, "GL_COMBINE_ALPHA_EXT" }, + { 0x8572, "GL_COMBINE_ALPHA" }, + { 0x8573, "GL_RGB_SCALE_ARB" }, + { 0x8573, "GL_RGB_SCALE_EXT" }, + { 0x8573, "GL_RGB_SCALE" }, + { 0x8574, "GL_ADD_SIGNED_ARB" }, + { 0x8574, "GL_ADD_SIGNED_EXT" }, + { 0x8574, "GL_ADD_SIGNED" }, + { 0x8575, "GL_INTERPOLATE_ARB" }, + { 0x8575, "GL_INTERPOLATE_EXT" }, + { 0x8575, "GL_INTERPOLATE" }, + { 0x8576, "GL_CONSTANT_ARB" }, + { 0x8576, "GL_CONSTANT_EXT" }, + { 0x8576, "GL_CONSTANT" }, + { 0x8577, "GL_PRIMARY_COLOR_ARB" }, + { 0x8577, "GL_PRIMARY_COLOR_EXT" }, + { 0x8577, "GL_PRIMARY_COLOR" }, + { 0x8578, "GL_PREVIOUS_ARB" }, + { 0x8578, "GL_PREVIOUS_EXT" }, + { 0x8578, "GL_PREVIOUS" }, + { 0x8580, "GL_SOURCE0_RGB_ARB" }, + { 0x8580, "GL_SOURCE0_RGB_EXT" }, + { 0x8580, "GL_SOURCE0_RGB" }, + { 0x8580, "GL_SRC0_RGB" }, + { 0x8581, "GL_SOURCE1_RGB_ARB" }, + { 0x8581, "GL_SOURCE1_RGB_EXT" }, + { 0x8581, "GL_SOURCE1_RGB" }, + { 0x8581, "GL_SRC1_RGB" }, + { 0x8582, "GL_SOURCE2_RGB_ARB" }, + { 0x8582, "GL_SOURCE2_RGB_EXT" }, + { 0x8582, "GL_SOURCE2_RGB" }, + { 0x8582, "GL_SRC2_RGB" }, + { 0x8583, "GL_SOURCE3_RGB_ARB" }, + { 0x8583, "GL_SOURCE3_RGB_EXT" }, + { 0x8583, "GL_SOURCE3_RGB" }, + { 0x8583, "GL_SRC3_RGB" }, + { 0x8584, "GL_SOURCE4_RGB_ARB" }, + { 0x8584, "GL_SOURCE4_RGB_EXT" }, + { 0x8584, "GL_SOURCE4_RGB" }, + { 0x8584, "GL_SRC4_RGB" }, + { 0x8585, "GL_SOURCE5_RGB_ARB" }, + { 0x8585, "GL_SOURCE5_RGB_EXT" }, + { 0x8585, "GL_SOURCE5_RGB" }, + { 0x8585, "GL_SRC5_RGB" }, + { 0x8586, "GL_SOURCE6_RGB_ARB" }, + { 0x8586, "GL_SOURCE6_RGB_EXT" }, + { 0x8586, "GL_SOURCE6_RGB" }, + { 0x8586, "GL_SRC6_RGB" }, + { 0x8587, "GL_SOURCE7_RGB_ARB" }, + { 0x8587, "GL_SOURCE7_RGB_EXT" }, + { 0x8587, "GL_SOURCE7_RGB" }, + { 0x8587, "GL_SRC7_RGB" }, + { 0x8588, "GL_SOURCE0_ALPHA_ARB" }, + { 0x8588, "GL_SOURCE0_ALPHA_EXT" }, + { 0x8588, "GL_SOURCE0_ALPHA" }, + { 0x8588, "GL_SRC0_ALPHA" }, + { 0x8589, "GL_SOURCE1_ALPHA_ARB" }, + { 0x8589, "GL_SOURCE1_ALPHA_EXT" }, + { 0x8589, "GL_SOURCE1_ALPHA" }, + { 0x8589, "GL_SRC1_ALPHA" }, + { 0x858A, "GL_SOURCE2_ALPHA_ARB" }, + { 0x858A, "GL_SOURCE2_ALPHA_EXT" }, + { 0x858A, "GL_SOURCE2_ALPHA" }, + { 0x858A, "GL_SRC2_ALPHA" }, + { 0x858B, "GL_SOURCE3_ALPHA_ARB" }, + { 0x858B, "GL_SOURCE3_ALPHA_EXT" }, + { 0x858B, "GL_SOURCE3_ALPHA" }, + { 0x858B, "GL_SRC3_ALPHA" }, + { 0x858C, "GL_SOURCE4_ALPHA_ARB" }, + { 0x858C, "GL_SOURCE4_ALPHA_EXT" }, + { 0x858C, "GL_SOURCE4_ALPHA" }, + { 0x858C, "GL_SRC4_ALPHA" }, + { 0x858D, "GL_SOURCE5_ALPHA_ARB" }, + { 0x858D, "GL_SOURCE5_ALPHA_EXT" }, + { 0x858D, "GL_SOURCE5_ALPHA" }, + { 0x858D, "GL_SRC5_ALPHA" }, + { 0x858E, "GL_SOURCE6_ALPHA_ARB" }, + { 0x858E, "GL_SOURCE6_ALPHA_EXT" }, + { 0x858E, "GL_SOURCE6_ALPHA" }, + { 0x858E, "GL_SRC6_ALPHA" }, + { 0x858F, "GL_SOURCE7_ALPHA_ARB" }, + { 0x858F, "GL_SOURCE7_ALPHA_EXT" }, + { 0x858F, "GL_SOURCE7_ALPHA" }, + { 0x858F, "GL_SRC7_ALPHA" }, + { 0x8590, "GL_OPERAND0_RGB_ARB" }, + { 0x8590, "GL_OPERAND0_RGB_EXT" }, + { 0x8590, "GL_OPERAND0_RGB" }, + { 0x8591, "GL_OPERAND1_RGB_ARB" }, + { 0x8591, "GL_OPERAND1_RGB_EXT" }, + { 0x8591, "GL_OPERAND1_RGB" }, + { 0x8592, "GL_OPERAND2_RGB_ARB" }, + { 0x8592, "GL_OPERAND2_RGB_EXT" }, + { 0x8592, "GL_OPERAND2_RGB" }, + { 0x8593, "GL_OPERAND3_RGB_ARB" }, + { 0x8593, "GL_OPERAND3_RGB_EXT" }, + { 0x8593, "GL_OPERAND3_RGB" }, + { 0x8594, "GL_OPERAND4_RGB_ARB" }, + { 0x8594, "GL_OPERAND4_RGB_EXT" }, + { 0x8594, "GL_OPERAND4_RGB" }, + { 0x8595, "GL_OPERAND5_RGB_ARB" }, + { 0x8595, "GL_OPERAND5_RGB_EXT" }, + { 0x8595, "GL_OPERAND5_RGB" }, + { 0x8596, "GL_OPERAND6_RGB_ARB" }, + { 0x8596, "GL_OPERAND6_RGB_EXT" }, + { 0x8596, "GL_OPERAND6_RGB" }, + { 0x8597, "GL_OPERAND7_RGB_ARB" }, + { 0x8597, "GL_OPERAND7_RGB_EXT" }, + { 0x8597, "GL_OPERAND7_RGB" }, + { 0x8598, "GL_OPERAND0_ALPHA_ARB" }, + { 0x8598, "GL_OPERAND0_ALPHA_EXT" }, + { 0x8598, "GL_OPERAND0_ALPHA" }, + { 0x8599, "GL_OPERAND1_ALPHA_ARB" }, + { 0x8599, "GL_OPERAND1_ALPHA_EXT" }, + { 0x8599, "GL_OPERAND1_ALPHA" }, + { 0x859A, "GL_OPERAND2_ALPHA_ARB" }, + { 0x859A, "GL_OPERAND2_ALPHA_EXT" }, + { 0x859A, "GL_OPERAND2_ALPHA" }, + { 0x859B, "GL_OPERAND3_ALPHA_ARB" }, + { 0x859B, "GL_OPERAND3_ALPHA_EXT" }, + { 0x859B, "GL_OPERAND3_ALPHA" }, + { 0x859C, "GL_OPERAND4_ALPHA_ARB" }, + { 0x859C, "GL_OPERAND4_ALPHA_EXT" }, + { 0x859C, "GL_OPERAND4_ALPHA" }, + { 0x859D, "GL_OPERAND5_ALPHA_ARB" }, + { 0x859D, "GL_OPERAND5_ALPHA_EXT" }, + { 0x859D, "GL_OPERAND5_ALPHA" }, + { 0x859E, "GL_OPERAND6_ALPHA_ARB" }, + { 0x859E, "GL_OPERAND6_ALPHA_EXT" }, + { 0x859E, "GL_OPERAND6_ALPHA" }, + { 0x859F, "GL_OPERAND7_ALPHA_ARB" }, + { 0x859F, "GL_OPERAND7_ALPHA_EXT" }, + { 0x859F, "GL_OPERAND7_ALPHA" }, + { 0x85AE, "GL_PERTURB_EXT" }, + { 0x85AF, "GL_TEXTURE_NORMAL_EXT" }, + { 0x85B4, "GL_STORAGE_CLIENT_APPLE" }, + { 0x85B5, "GL_VERTEX_ARRAY_BINDING_APPLE" }, + { 0x85BD, "GL_STORAGE_PRIVATE_APPLE" }, + { 0x85BE, "GL_STORAGE_CACHED_APPLE" }, + { 0x85BF, "GL_STORAGE_SHARED_APPLE" }, + { 0x8620, "GL_VERTEX_PROGRAM_ARB" }, + { 0x8620, "GL_VERTEX_PROGRAM_NV" }, + { 0x8621, "GL_VERTEX_STATE_PROGRAM_NV" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB" }, + { 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED" }, + { 0x8623, "GL_ATTRIB_ARRAY_SIZE_NV" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB" }, + { 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE" }, + { 0x8624, "GL_ATTRIB_ARRAY_STRIDE_NV" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB" }, + { 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE" }, + { 0x8625, "GL_ATTRIB_ARRAY_TYPE_NV" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB" }, + { 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE" }, + { 0x8626, "GL_CURRENT_ATTRIB_NV" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB_ARB" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB_ARB" }, + { 0x8626, "GL_CURRENT_VERTEX_ATTRIB" }, + { 0x8627, "GL_PROGRAM_LENGTH_ARB" }, + { 0x8627, "GL_PROGRAM_LENGTH_NV" }, + { 0x8628, "GL_PROGRAM_STRING_ARB" }, + { 0x8628, "GL_PROGRAM_STRING_NV" }, + { 0x8629, "GL_MODELVIEW_PROJECTION_NV" }, + { 0x862A, "GL_IDENTITY_NV" }, + { 0x862B, "GL_INVERSE_NV" }, + { 0x862C, "GL_TRANSPOSE_NV" }, + { 0x862D, "GL_INVERSE_TRANSPOSE_NV" }, + { 0x862E, "GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB" }, + { 0x862E, "GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV" }, + { 0x862F, "GL_MAX_PROGRAM_MATRICES_ARB" }, + { 0x862F, "GL_MAX_TRACK_MATRICES_NV" }, + { 0x8630, "GL_MATRIX0_NV" }, + { 0x8631, "GL_MATRIX1_NV" }, + { 0x8632, "GL_MATRIX2_NV" }, + { 0x8633, "GL_MATRIX3_NV" }, + { 0x8634, "GL_MATRIX4_NV" }, + { 0x8635, "GL_MATRIX5_NV" }, + { 0x8636, "GL_MATRIX6_NV" }, + { 0x8637, "GL_MATRIX7_NV" }, + { 0x8640, "GL_CURRENT_MATRIX_STACK_DEPTH_ARB" }, + { 0x8640, "GL_CURRENT_MATRIX_STACK_DEPTH_NV" }, + { 0x8641, "GL_CURRENT_MATRIX_ARB" }, + { 0x8641, "GL_CURRENT_MATRIX_NV" }, + { 0x8642, "GL_PROGRAM_POINT_SIZE_EXT" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_ARB" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_ARB" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE_NV" }, + { 0x8642, "GL_VERTEX_PROGRAM_POINT_SIZE" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_ARB" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_ARB" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE_NV" }, + { 0x8643, "GL_VERTEX_PROGRAM_TWO_SIDE" }, + { 0x8644, "GL_PROGRAM_PARAMETER_NV" }, + { 0x8645, "GL_ATTRIB_ARRAY_POINTER_NV" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB" }, + { 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER" }, + { 0x8646, "GL_PROGRAM_TARGET_NV" }, + { 0x8647, "GL_PROGRAM_RESIDENT_NV" }, + { 0x8648, "GL_TRACK_MATRIX_NV" }, + { 0x8649, "GL_TRACK_MATRIX_TRANSFORM_NV" }, + { 0x864A, "GL_VERTEX_PROGRAM_BINDING_NV" }, + { 0x864B, "GL_PROGRAM_ERROR_POSITION_ARB" }, + { 0x864B, "GL_PROGRAM_ERROR_POSITION_NV" }, + { 0x8650, "GL_VERTEX_ATTRIB_ARRAY0_NV" }, + { 0x8651, "GL_VERTEX_ATTRIB_ARRAY1_NV" }, + { 0x8652, "GL_VERTEX_ATTRIB_ARRAY2_NV" }, + { 0x8653, "GL_VERTEX_ATTRIB_ARRAY3_NV" }, + { 0x8654, "GL_VERTEX_ATTRIB_ARRAY4_NV" }, + { 0x8655, "GL_VERTEX_ATTRIB_ARRAY5_NV" }, + { 0x8656, "GL_VERTEX_ATTRIB_ARRAY6_NV" }, + { 0x8657, "GL_VERTEX_ATTRIB_ARRAY7_NV" }, + { 0x8658, "GL_VERTEX_ATTRIB_ARRAY8_NV" }, + { 0x8659, "GL_VERTEX_ATTRIB_ARRAY9_NV" }, + { 0x865A, "GL_VERTEX_ATTRIB_ARRAY10_NV" }, + { 0x865B, "GL_VERTEX_ATTRIB_ARRAY11_NV" }, + { 0x865C, "GL_VERTEX_ATTRIB_ARRAY12_NV" }, + { 0x865D, "GL_VERTEX_ATTRIB_ARRAY13_NV" }, + { 0x865E, "GL_VERTEX_ATTRIB_ARRAY14_NV" }, + { 0x865F, "GL_VERTEX_ATTRIB_ARRAY15_NV" }, + { 0x8660, "GL_MAP1_VERTEX_ATTRIB0_4_NV" }, + { 0x8661, "GL_MAP1_VERTEX_ATTRIB1_4_NV" }, + { 0x8662, "GL_MAP1_VERTEX_ATTRIB2_4_NV" }, + { 0x8663, "GL_MAP1_VERTEX_ATTRIB3_4_NV" }, + { 0x8664, "GL_MAP1_VERTEX_ATTRIB4_4_NV" }, + { 0x8665, "GL_MAP1_VERTEX_ATTRIB5_4_NV" }, + { 0x8666, "GL_MAP1_VERTEX_ATTRIB6_4_NV" }, + { 0x8667, "GL_MAP1_VERTEX_ATTRIB7_4_NV" }, + { 0x8668, "GL_MAP1_VERTEX_ATTRIB8_4_NV" }, + { 0x8669, "GL_MAP1_VERTEX_ATTRIB9_4_NV" }, + { 0x866A, "GL_MAP1_VERTEX_ATTRIB10_4_NV" }, + { 0x866B, "GL_MAP1_VERTEX_ATTRIB11_4_NV" }, + { 0x866C, "GL_MAP1_VERTEX_ATTRIB12_4_NV" }, + { 0x866D, "GL_MAP1_VERTEX_ATTRIB13_4_NV" }, + { 0x866E, "GL_MAP1_VERTEX_ATTRIB14_4_NV" }, + { 0x866F, "GL_MAP1_VERTEX_ATTRIB15_4_NV" }, + { 0x8670, "GL_MAP2_VERTEX_ATTRIB0_4_NV" }, + { 0x8671, "GL_MAP2_VERTEX_ATTRIB1_4_NV" }, + { 0x8672, "GL_MAP2_VERTEX_ATTRIB2_4_NV" }, + { 0x8673, "GL_MAP2_VERTEX_ATTRIB3_4_NV" }, + { 0x8674, "GL_MAP2_VERTEX_ATTRIB4_4_NV" }, + { 0x8675, "GL_MAP2_VERTEX_ATTRIB5_4_NV" }, + { 0x8676, "GL_MAP2_VERTEX_ATTRIB6_4_NV" }, + { 0x8677, "GL_MAP2_VERTEX_ATTRIB7_4_NV" }, + { 0x8677, "GL_PROGRAM_BINDING_ARB" }, + { 0x8677, "GL_PROGRAM_NAME_ARB" }, + { 0x8678, "GL_MAP2_VERTEX_ATTRIB8_4_NV" }, + { 0x8679, "GL_MAP2_VERTEX_ATTRIB9_4_NV" }, + { 0x867A, "GL_MAP2_VERTEX_ATTRIB10_4_NV" }, + { 0x867B, "GL_MAP2_VERTEX_ATTRIB11_4_NV" }, + { 0x867C, "GL_MAP2_VERTEX_ATTRIB12_4_NV" }, + { 0x867D, "GL_MAP2_VERTEX_ATTRIB13_4_NV" }, + { 0x867E, "GL_MAP2_VERTEX_ATTRIB14_4_NV" }, + { 0x867F, "GL_MAP2_VERTEX_ATTRIB15_4_NV" }, + { 0x86A0, "GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB" }, + { 0x86A0, "GL_TEXTURE_COMPRESSED_IMAGE_SIZE" }, + { 0x86A1, "GL_TEXTURE_COMPRESSED_ARB" }, + { 0x86A1, "GL_TEXTURE_COMPRESSED" }, + { 0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB" }, + { 0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS" }, + { 0x86A3, "GL_COMPRESSED_TEXTURE_FORMATS_ARB" }, + { 0x86A3, "GL_COMPRESSED_TEXTURE_FORMATS" }, + { 0x86A4, "GL_MAX_VERTEX_UNITS_ARB" }, + { 0x86A5, "GL_ACTIVE_VERTEX_UNITS_ARB" }, + { 0x86A6, "GL_WEIGHT_SUM_UNITY_ARB" }, + { 0x86A7, "GL_VERTEX_BLEND_ARB" }, + { 0x86A8, "GL_CURRENT_WEIGHT_ARB" }, + { 0x86A9, "GL_WEIGHT_ARRAY_TYPE_ARB" }, + { 0x86AA, "GL_WEIGHT_ARRAY_STRIDE_ARB" }, + { 0x86AB, "GL_WEIGHT_ARRAY_SIZE_ARB" }, + { 0x86AC, "GL_WEIGHT_ARRAY_POINTER_ARB" }, + { 0x86AD, "GL_WEIGHT_ARRAY_ARB" }, + { 0x86AE, "GL_DOT3_RGB_ARB" }, + { 0x86AE, "GL_DOT3_RGB" }, + { 0x86AF, "GL_DOT3_RGBA_ARB" }, + { 0x86AF, "GL_DOT3_RGBA" }, + { 0x8722, "GL_MODELVIEW2_ARB" }, + { 0x8723, "GL_MODELVIEW3_ARB" }, + { 0x8724, "GL_MODELVIEW4_ARB" }, + { 0x8725, "GL_MODELVIEW5_ARB" }, + { 0x8726, "GL_MODELVIEW6_ARB" }, + { 0x8727, "GL_MODELVIEW7_ARB" }, + { 0x8728, "GL_MODELVIEW8_ARB" }, + { 0x8729, "GL_MODELVIEW9_ARB" }, + { 0x872A, "GL_MODELVIEW10_ARB" }, + { 0x872B, "GL_MODELVIEW11_ARB" }, + { 0x872C, "GL_MODELVIEW12_ARB" }, + { 0x872D, "GL_MODELVIEW13_ARB" }, + { 0x872E, "GL_MODELVIEW14_ARB" }, + { 0x872F, "GL_MODELVIEW15_ARB" }, + { 0x8730, "GL_MODELVIEW16_ARB" }, + { 0x8731, "GL_MODELVIEW17_ARB" }, + { 0x8732, "GL_MODELVIEW18_ARB" }, + { 0x8733, "GL_MODELVIEW19_ARB" }, + { 0x8734, "GL_MODELVIEW20_ARB" }, + { 0x8735, "GL_MODELVIEW21_ARB" }, + { 0x8736, "GL_MODELVIEW22_ARB" }, + { 0x8737, "GL_MODELVIEW23_ARB" }, + { 0x8738, "GL_MODELVIEW24_ARB" }, + { 0x8739, "GL_MODELVIEW25_ARB" }, + { 0x873A, "GL_MODELVIEW26_ARB" }, + { 0x873B, "GL_MODELVIEW27_ARB" }, + { 0x873C, "GL_MODELVIEW28_ARB" }, + { 0x873D, "GL_MODELVIEW29_ARB" }, + { 0x873E, "GL_MODELVIEW30_ARB" }, + { 0x873F, "GL_MODELVIEW31_ARB" }, + { 0x8742, "GL_MIRROR_CLAMP_EXT" }, + { 0x8743, "GL_MIRROR_CLAMP_TO_EDGE_EXT" }, + { 0x8764, "GL_BUFFER_SIZE_ARB" }, + { 0x8764, "GL_BUFFER_SIZE" }, + { 0x8765, "GL_BUFFER_USAGE_ARB" }, + { 0x8765, "GL_BUFFER_USAGE" }, + { 0x8780, "GL_VERTEX_SHADER_EXT" }, + { 0x8781, "GL_VERTEX_SHADER_BINDING_EXT" }, + { 0x8782, "GL_OP_INDEX_EXT" }, + { 0x8783, "GL_OP_NEGATE_EXT" }, + { 0x8784, "GL_OP_DOT3_EXT" }, + { 0x8785, "GL_OP_DOT4_EXT" }, + { 0x8786, "GL_OP_MUL_EXT" }, + { 0x8787, "GL_OP_ADD_EXT" }, + { 0x8788, "GL_OP_MADD_EXT" }, + { 0x8789, "GL_OP_FRAC_EXT" }, + { 0x878A, "GL_OP_MAX_EXT" }, + { 0x878B, "GL_OP_MIN_EXT" }, + { 0x878C, "GL_OP_SET_GE_EXT" }, + { 0x878D, "GL_OP_SET_LT_EXT" }, + { 0x878E, "GL_OP_CLAMP_EXT" }, + { 0x878F, "GL_OP_FLOOR_EXT" }, + { 0x8790, "GL_OP_ROUND_EXT" }, + { 0x8791, "GL_OP_EXP_BASE_2_EXT" }, + { 0x8792, "GL_OP_LOG_BASE_2_EXT" }, + { 0x8793, "GL_OP_POWER_EXT" }, + { 0x8794, "GL_OP_RECIP_EXT" }, + { 0x8795, "GL_OP_RECIP_SQRT_EXT" }, + { 0x8796, "GL_OP_SUB_EXT" }, + { 0x8797, "GL_OP_CROSS_PRODUCT_EXT" }, + { 0x8798, "GL_OP_MULTIPLY_MATRIX_EXT" }, + { 0x8799, "GL_OP_MOV_EXT" }, + { 0x879A, "GL_OUTPUT_VERTEX_EXT" }, + { 0x879B, "GL_OUTPUT_COLOR0_EXT" }, + { 0x879C, "GL_OUTPUT_COLOR1_EXT" }, + { 0x879D, "GL_OUTPUT_TEXTURE_COORD0_EXT" }, + { 0x879E, "GL_OUTPUT_TEXTURE_COORD1_EXT" }, + { 0x879F, "GL_OUTPUT_TEXTURE_COORD2_EXT" }, + { 0x87A0, "GL_OUTPUT_TEXTURE_COORD3_EXT" }, + { 0x87A1, "GL_OUTPUT_TEXTURE_COORD4_EXT" }, + { 0x87A2, "GL_OUTPUT_TEXTURE_COORD5_EXT" }, + { 0x87A3, "GL_OUTPUT_TEXTURE_COORD6_EXT" }, + { 0x87A4, "GL_OUTPUT_TEXTURE_COORD7_EXT" }, + { 0x87A5, "GL_OUTPUT_TEXTURE_COORD8_EXT" }, + { 0x87A6, "GL_OUTPUT_TEXTURE_COORD9_EXT" }, + { 0x87A7, "GL_OUTPUT_TEXTURE_COORD10_EXT" }, + { 0x87A8, "GL_OUTPUT_TEXTURE_COORD11_EXT" }, + { 0x87A9, "GL_OUTPUT_TEXTURE_COORD12_EXT" }, + { 0x87AA, "GL_OUTPUT_TEXTURE_COORD13_EXT" }, + { 0x87AB, "GL_OUTPUT_TEXTURE_COORD14_EXT" }, + { 0x87AC, "GL_OUTPUT_TEXTURE_COORD15_EXT" }, + { 0x87AD, "GL_OUTPUT_TEXTURE_COORD16_EXT" }, + { 0x87AE, "GL_OUTPUT_TEXTURE_COORD17_EXT" }, + { 0x87AF, "GL_OUTPUT_TEXTURE_COORD18_EXT" }, + { 0x87B0, "GL_OUTPUT_TEXTURE_COORD19_EXT" }, + { 0x87B1, "GL_OUTPUT_TEXTURE_COORD20_EXT" }, + { 0x87B2, "GL_OUTPUT_TEXTURE_COORD21_EXT" }, + { 0x87B3, "GL_OUTPUT_TEXTURE_COORD22_EXT" }, + { 0x87B4, "GL_OUTPUT_TEXTURE_COORD23_EXT" }, + { 0x87B5, "GL_OUTPUT_TEXTURE_COORD24_EXT" }, + { 0x87B6, "GL_OUTPUT_TEXTURE_COORD25_EXT" }, + { 0x87B7, "GL_OUTPUT_TEXTURE_COORD26_EXT" }, + { 0x87B8, "GL_OUTPUT_TEXTURE_COORD27_EXT" }, + { 0x87B9, "GL_OUTPUT_TEXTURE_COORD28_EXT" }, + { 0x87BA, "GL_OUTPUT_TEXTURE_COORD29_EXT" }, + { 0x87BB, "GL_OUTPUT_TEXTURE_COORD30_EXT" }, + { 0x87BC, "GL_OUTPUT_TEXTURE_COORD31_EXT" }, + { 0x87BD, "GL_OUTPUT_FOG_EXT" }, + { 0x87BE, "GL_SCALAR_EXT" }, + { 0x87BF, "GL_VECTOR_EXT" }, + { 0x87C0, "GL_MATRIX_EXT" }, + { 0x87C1, "GL_VARIANT_EXT" }, + { 0x87C2, "GL_INVARIANT_EXT" }, + { 0x87C3, "GL_LOCAL_CONSTANT_EXT" }, + { 0x87C4, "GL_LOCAL_EXT" }, + { 0x87C5, "GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87C6, "GL_MAX_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87C7, "GL_MAX_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87C8, "GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87C9, "GL_MAX_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87CA, "GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87CB, "GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87CC, "GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87CD, "GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87CE, "GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87CF, "GL_VERTEX_SHADER_INSTRUCTIONS_EXT" }, + { 0x87D0, "GL_VERTEX_SHADER_VARIANTS_EXT" }, + { 0x87D1, "GL_VERTEX_SHADER_INVARIANTS_EXT" }, + { 0x87D2, "GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT" }, + { 0x87D3, "GL_VERTEX_SHADER_LOCALS_EXT" }, + { 0x87D4, "GL_VERTEX_SHADER_OPTIMIZED_EXT" }, + { 0x87D5, "GL_X_EXT" }, + { 0x87D6, "GL_Y_EXT" }, + { 0x87D7, "GL_Z_EXT" }, + { 0x87D8, "GL_W_EXT" }, + { 0x87D9, "GL_NEGATIVE_X_EXT" }, + { 0x87DA, "GL_NEGATIVE_Y_EXT" }, + { 0x87DB, "GL_NEGATIVE_Z_EXT" }, + { 0x87DC, "GL_NEGATIVE_W_EXT" }, + { 0x87DF, "GL_NEGATIVE_ONE_EXT" }, + { 0x87E0, "GL_NORMALIZED_RANGE_EXT" }, + { 0x87E1, "GL_FULL_RANGE_EXT" }, + { 0x87E2, "GL_CURRENT_VERTEX_EXT" }, + { 0x87E3, "GL_MVP_MATRIX_EXT" }, + { 0x87E4, "GL_VARIANT_VALUE_EXT" }, + { 0x87E5, "GL_VARIANT_DATATYPE_EXT" }, + { 0x87E6, "GL_VARIANT_ARRAY_STRIDE_EXT" }, + { 0x87E7, "GL_VARIANT_ARRAY_TYPE_EXT" }, + { 0x87E8, "GL_VARIANT_ARRAY_EXT" }, + { 0x87E9, "GL_VARIANT_ARRAY_POINTER_EXT" }, + { 0x87EA, "GL_INVARIANT_VALUE_EXT" }, + { 0x87EB, "GL_INVARIANT_DATATYPE_EXT" }, + { 0x87EC, "GL_LOCAL_CONSTANT_VALUE_EXT" }, + { 0x87Ed, "GL_LOCAL_CONSTANT_DATATYPE_EXT" }, + { 0x8800, "GL_STENCIL_BACK_FUNC_ATI" }, + { 0x8800, "GL_STENCIL_BACK_FUNC" }, + { 0x8801, "GL_STENCIL_BACK_FAIL_ATI" }, + { 0x8801, "GL_STENCIL_BACK_FAIL" }, + { 0x8802, "GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI" }, + { 0x8802, "GL_STENCIL_BACK_PASS_DEPTH_FAIL" }, + { 0x8803, "GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI" }, + { 0x8803, "GL_STENCIL_BACK_PASS_DEPTH_PASS" }, + { 0x8804, "GL_FRAGMENT_PROGRAM_ARB" }, + { 0x8805, "GL_PROGRAM_ALU_INSTRUCTIONS_ARB" }, + { 0x8806, "GL_PROGRAM_TEX_INSTRUCTIONS_ARB" }, + { 0x8807, "GL_PROGRAM_TEX_INDIRECTIONS_ARB" }, + { 0x8808, "GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB" }, + { 0x8809, "GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB" }, + { 0x880A, "GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB" }, + { 0x880B, "GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB" }, + { 0x880C, "GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB" }, + { 0x880D, "GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB" }, + { 0x880E, "GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB" }, + { 0x880F, "GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB" }, + { 0x8810, "GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB" }, + { 0x8814, "GL_RGBA_FLOAT32_APPLE" }, + { 0x8814, "GL_RGBA_FLOAT32_ATI" }, + { 0x8814, "GL_RGBA32F_ARB" }, + { 0x8815, "GL_RGB_FLOAT32_APPLE" }, + { 0x8815, "GL_RGB_FLOAT32_ATI" }, + { 0x8815, "GL_RGB32F_ARB" }, + { 0x8816, "GL_ALPHA_FLOAT32_APPLE" }, + { 0x8816, "GL_ALPHA_FLOAT32_ATI" }, + { 0x8816, "GL_ALPHA32F_ARB" }, + { 0x8817, "GL_INTENSITY_FLOAT32_APPLE" }, + { 0x8817, "GL_INTENSITY_FLOAT32_ATI" }, + { 0x8817, "GL_INTENSITY32F_ARB" }, + { 0x8818, "GL_LUMINANCE_FLOAT32_APPLE" }, + { 0x8818, "GL_LUMINANCE_FLOAT32_ATI" }, + { 0x8818, "GL_LUMINANCE32F_ARB" }, + { 0x8819, "GL_LUMINANCE_ALPHA_FLOAT32_APPLE" }, + { 0x8819, "GL_LUMINANCE_ALPHA_FLOAT32_ATI" }, + { 0x8819, "GL_LUMINANCE_ALPHA32F_ARB" }, + { 0x881A, "GL_RGBA_FLOAT16_APPLE" }, + { 0x881A, "GL_RGBA_FLOAT16_ATI" }, + { 0x881A, "GL_RGBA16F_ARB" }, + { 0x881B, "GL_RGB_FLOAT16_APPLE" }, + { 0x881B, "GL_RGB_FLOAT16_ATI" }, + { 0x881B, "GL_RGB16F_ARB" }, + { 0x881C, "GL_ALPHA_FLOAT16_APPLE" }, + { 0x881C, "GL_ALPHA_FLOAT16_ATI" }, + { 0x881C, "GL_ALPHA16F_ARB" }, + { 0x881D, "GL_INTENSITY_FLOAT16_APPLE" }, + { 0x881D, "GL_INTENSITY_FLOAT16_ATI" }, + { 0x881D, "GL_INTENSITY16F_ARB" }, + { 0x881E, "GL_LUMINANCE_FLOAT16_APPLE" }, + { 0x881E, "GL_LUMINANCE_FLOAT16_ATI" }, + { 0x881E, "GL_LUMINANCE16F_ARB" }, + { 0x881F, "GL_LUMINANCE_ALPHA_FLOAT16_APPLE" }, + { 0x881F, "GL_LUMINANCE_ALPHA_FLOAT16_ATI" }, + { 0x881F, "GL_LUMINANCE_ALPHA16F_ARB" }, + { 0x8820, "GL_RGBA_FLOAT_MODE_ARB" }, + { 0x8824, "GL_MAX_DRAW_BUFFERS_ARB" }, + { 0x8824, "GL_MAX_DRAW_BUFFERS" }, + { 0x8825, "GL_DRAW_BUFFER0_ARB" }, + { 0x8825, "GL_DRAW_BUFFER0" }, + { 0x8826, "GL_DRAW_BUFFER1_ARB" }, + { 0x8826, "GL_DRAW_BUFFER1" }, + { 0x8827, "GL_DRAW_BUFFER2_ARB" }, + { 0x8827, "GL_DRAW_BUFFER2" }, + { 0x8828, "GL_DRAW_BUFFER3_ARB" }, + { 0x8828, "GL_DRAW_BUFFER3" }, + { 0x8829, "GL_DRAW_BUFFER4_ARB" }, + { 0x8829, "GL_DRAW_BUFFER4" }, + { 0x882A, "GL_DRAW_BUFFER5_ARB" }, + { 0x882A, "GL_DRAW_BUFFER5" }, + { 0x882B, "GL_DRAW_BUFFER6_ARB" }, + { 0x882B, "GL_DRAW_BUFFER6" }, + { 0x882C, "GL_DRAW_BUFFER7_ARB" }, + { 0x882C, "GL_DRAW_BUFFER7" }, + { 0x882D, "GL_DRAW_BUFFER8_ARB" }, + { 0x882D, "GL_DRAW_BUFFER8" }, + { 0x882E, "GL_DRAW_BUFFER9_ARB" }, + { 0x882E, "GL_DRAW_BUFFER9" }, + { 0x882F, "GL_DRAW_BUFFER10_ARB" }, + { 0x882F, "GL_DRAW_BUFFER10" }, + { 0x8830, "GL_DRAW_BUFFER11_ARB" }, + { 0x8830, "GL_DRAW_BUFFER11" }, + { 0x8831, "GL_DRAW_BUFFER12_ARB" }, + { 0x8831, "GL_DRAW_BUFFER12" }, + { 0x8832, "GL_DRAW_BUFFER13_ARB" }, + { 0x8832, "GL_DRAW_BUFFER13" }, + { 0x8833, "GL_DRAW_BUFFER14_ARB" }, + { 0x8833, "GL_DRAW_BUFFER14" }, + { 0x8834, "GL_DRAW_BUFFER15_ARB" }, + { 0x8834, "GL_DRAW_BUFFER15" }, + { 0x883D, "GL_ALPHA_BLEND_EQUATION_ATI" }, + { 0x883D, "GL_BLEND_EQUATION_ALPHA_EXT" }, + { 0x883D, "GL_BLEND_EQUATION_ALPHA" }, + { 0x884A, "GL_TEXTURE_DEPTH_SIZE_ARB" }, + { 0x884A, "GL_TEXTURE_DEPTH_SIZE" }, + { 0x884B, "GL_DEPTH_TEXTURE_MODE_ARB" }, + { 0x884B, "GL_DEPTH_TEXTURE_MODE" }, + { 0x884C, "GL_TEXTURE_COMPARE_MODE_ARB" }, + { 0x884C, "GL_TEXTURE_COMPARE_MODE" }, + { 0x884D, "GL_TEXTURE_COMPARE_FUNC_ARB" }, + { 0x884D, "GL_TEXTURE_COMPARE_FUNC" }, + { 0x884E, "GL_COMPARE_R_TO_TEXTURE_ARB" }, + { 0x884E, "GL_COMPARE_R_TO_TEXTURE" }, + { 0x884E, "GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT" }, + { 0x8861, "GL_POINT_SPRITE_ARB" }, + { 0x8861, "GL_POINT_SPRITE" }, + { 0x8862, "GL_COORD_REPLACE_ARB" }, + { 0x8862, "GL_COORD_REPLACE" }, + { 0x8864, "GL_QUERY_COUNTER_BITS_ARB" }, + { 0x8864, "GL_QUERY_COUNTER_BITS" }, + { 0x8865, "GL_CURRENT_QUERY_ARB" }, + { 0x8865, "GL_CURRENT_QUERY" }, + { 0x8866, "GL_QUERY_RESULT_ARB" }, + { 0x8866, "GL_QUERY_RESULT" }, + { 0x8867, "GL_QUERY_RESULT_AVAILABLE_ARB" }, + { 0x8867, "GL_QUERY_RESULT_AVAILABLE" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS_ARB" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS_ARB" }, + { 0x8869, "GL_MAX_VERTEX_ATTRIBS" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB" }, + { 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS_ARB" }, + { 0x8871, "GL_MAX_TEXTURE_COORDS" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS" }, + { 0x8874, "GL_PROGRAM_ERROR_STRING_ARB" }, + { 0x8875, "GL_PROGRAM_FORMAT_ASCII_ARB" }, + { 0x8876, "GL_PROGRAM_FORMAT_ARB" }, + { 0x8890, "GL_DEPTH_BOUNDS_TEST_EXT" }, + { 0x8891, "GL_DEPTH_BOUNDS_EXT" }, + { 0x8892, "GL_ARRAY_BUFFER_ARB" }, + { 0x8892, "GL_ARRAY_BUFFER" }, + { 0x8893, "GL_ELEMENT_ARRAY_BUFFER_ARB" }, + { 0x8893, "GL_ELEMENT_ARRAY_BUFFER" }, + { 0x8894, "GL_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8894, "GL_ARRAY_BUFFER_BINDING" }, + { 0x8895, "GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8895, "GL_ELEMENT_ARRAY_BUFFER_BINDING" }, + { 0x8896, "GL_VERTEX_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8896, "GL_VERTEX_ARRAY_BUFFER_BINDING" }, + { 0x8897, "GL_NORMAL_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8897, "GL_NORMAL_ARRAY_BUFFER_BINDING" }, + { 0x8898, "GL_COLOR_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8898, "GL_COLOR_ARRAY_BUFFER_BINDING" }, + { 0x8899, "GL_INDEX_ARRAY_BUFFER_BINDING_ARB" }, + { 0x8899, "GL_INDEX_ARRAY_BUFFER_BINDING" }, + { 0x889A, "GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889A, "GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING" }, + { 0x889B, "GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889B, "GL_EDGE_FLAG_ARRAY_BUFFER_BINDING" }, + { 0x889C, "GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889C, "GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING" }, + { 0x889D, "GL_FOG_COORD_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889D, "GL_FOG_COORD_ARRAY_BUFFER_BINDING" }, + { 0x889D, "GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889D, "GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING" }, + { 0x889E, "GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889E, "GL_WEIGHT_ARRAY_BUFFER_BINDING" }, + { 0x889F, "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB" }, + { 0x889F, "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING" }, + { 0x88A0, "GL_PROGRAM_INSTRUCTIONS_ARB" }, + { 0x88A1, "GL_MAX_PROGRAM_INSTRUCTIONS_ARB" }, + { 0x88A2, "GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB" }, + { 0x88A3, "GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB" }, + { 0x88A4, "GL_PROGRAM_TEMPORARIES_ARB" }, + { 0x88A5, "GL_MAX_PROGRAM_TEMPORARIES_ARB" }, + { 0x88A6, "GL_PROGRAM_NATIVE_TEMPORARIES_ARB" }, + { 0x88A7, "GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB" }, + { 0x88A8, "GL_PROGRAM_PARAMETERS_ARB" }, + { 0x88A9, "GL_MAX_PROGRAM_PARAMETERS_ARB" }, + { 0x88AA, "GL_PROGRAM_NATIVE_PARAMETERS_ARB" }, + { 0x88AB, "GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB" }, + { 0x88AC, "GL_PROGRAM_ATTRIBS_ARB" }, + { 0x88AD, "GL_MAX_PROGRAM_ATTRIBS_ARB" }, + { 0x88AE, "GL_PROGRAM_NATIVE_ATTRIBS_ARB" }, + { 0x88AF, "GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB" }, + { 0x88B0, "GL_PROGRAM_ADDRESS_REGISTERS_ARB" }, + { 0x88B1, "GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB" }, + { 0x88B2, "GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB" }, + { 0x88B3, "GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB" }, + { 0x88B4, "GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB" }, + { 0x88B5, "GL_MAX_PROGRAM_ENV_PARAMETERS_ARB" }, + { 0x88B6, "GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB" }, + { 0x88B7, "GL_TRANSPOSE_CURRENT_MATRIX_ARB" }, + { 0x88B8, "GL_READ_ONLY_ARB" }, + { 0x88B8, "GL_READ_ONLY" }, + { 0x88B9, "GL_WRITE_ONLY_ARB" }, + { 0x88B9, "GL_WRITE_ONLY" }, + { 0x88BA, "GL_READ_WRITE_ARB" }, + { 0x88BA, "GL_READ_WRITE" }, + { 0x88BB, "GL_BUFFER_ACCESS_ARB" }, + { 0x88BB, "GL_BUFFER_ACCESS" }, + { 0x88BC, "GL_BUFFER_MAPPED_ARB" }, + { 0x88BC, "GL_BUFFER_MAPPED" }, + { 0x88BD, "GL_BUFFER_MAP_POINTER_ARB" }, + { 0x88BD, "GL_BUFFER_MAP_POINTER" }, + { 0x88C0, "GL_MATRIX0_ARB" }, + { 0x88C1, "GL_MATRIX1_ARB" }, + { 0x88C2, "GL_MATRIX2_ARB" }, + { 0x88C3, "GL_MATRIX3_ARB" }, + { 0x88C4, "GL_MATRIX4_ARB" }, + { 0x88C5, "GL_MATRIX5_ARB" }, + { 0x88C6, "GL_MATRIX6_ARB" }, + { 0x88C7, "GL_MATRIX7_ARB" }, + { 0x88C8, "GL_MATRIX8_ARB" }, + { 0x88C9, "GL_MATRIX9_ARB" }, + { 0x88CA, "GL_MATRIX10_ARB" }, + { 0x88CB, "GL_MATRIX11_ARB" }, + { 0x88CC, "GL_MATRIX12_ARB" }, + { 0x88CD, "GL_MATRIX13_ARB" }, + { 0x88CE, "GL_MATRIX14_ARB" }, + { 0x88CF, "GL_MATRIX15_ARB" }, + { 0x88D0, "GL_MATRIX16_ARB" }, + { 0x88D1, "GL_MATRIX17_ARB" }, + { 0x88D2, "GL_MATRIX18_ARB" }, + { 0x88D3, "GL_MATRIX19_ARB" }, + { 0x88D4, "GL_MATRIX20_ARB" }, + { 0x88D5, "GL_MATRIX21_ARB" }, + { 0x88D6, "GL_MATRIX22_ARB" }, + { 0x88D7, "GL_MATRIX23_ARB" }, + { 0x88D8, "GL_MATRIX24_ARB" }, + { 0x88D9, "GL_MATRIX25_ARB" }, + { 0x88DA, "GL_MATRIX26_ARB" }, + { 0x88DB, "GL_MATRIX27_ARB" }, + { 0x88DC, "GL_MATRIX28_ARB" }, + { 0x88DD, "GL_MATRIX29_ARB" }, + { 0x88DE, "GL_MATRIX30_ARB" }, + { 0x88DF, "GL_MATRIX31_ARB" }, + { 0x88E0, "GL_STREAM_DRAW_ARB" }, + { 0x88E0, "GL_STREAM_DRAW" }, + { 0x88E1, "GL_STREAM_READ_ARB" }, + { 0x88E1, "GL_STREAM_READ" }, + { 0x88E2, "GL_STREAM_COPY_ARB" }, + { 0x88E2, "GL_STREAM_COPY" }, + { 0x88E4, "GL_STATIC_DRAW_ARB" }, + { 0x88E4, "GL_STATIC_DRAW" }, + { 0x88E5, "GL_STATIC_READ_ARB" }, + { 0x88E5, "GL_STATIC_READ" }, + { 0x88E6, "GL_STATIC_COPY_ARB" }, + { 0x88E6, "GL_STATIC_COPY" }, + { 0x88E8, "GL_DYNAMIC_DRAW_ARB" }, + { 0x88E8, "GL_DYNAMIC_DRAW" }, + { 0x88E9, "GL_DYNAMIC_READ_ARB" }, + { 0x88E9, "GL_DYNAMIC_READ" }, + { 0x88EA, "GL_DYNAMIC_COPY_ARB" }, + { 0x88EA, "GL_DYNAMIC_COPY" }, + { 0x88EB, "GL_PIXEL_PACK_BUFFER_ARB" }, + { 0x88EB, "GL_PIXEL_PACK_BUFFER" }, + { 0x88EC, "GL_PIXEL_UNPACK_BUFFER_ARB" }, + { 0x88EC, "GL_PIXEL_UNPACK_BUFFER" }, + { 0x88ED, "GL_PIXEL_PACK_BUFFER_BINDING_ARB" }, + { 0x88ED, "GL_PIXEL_PACK_BUFFER_BINDING" }, + { 0x88EF, "GL_PIXEL_UNPACK_BUFFER_BINDING_ARB" }, + { 0x88EF, "GL_PIXEL_UNPACK_BUFFER_BINDING" }, + { 0x88F0, "GL_DEPTH24_STENCIL8_EXT" }, + { 0x88F0, "GL_DEPTH24_STENCIL8" }, + { 0x88F1, "GL_TEXTURE_STENCIL_SIZE_EXT" }, + { 0x88F1, "GL_TEXTURE_STENCIL_SIZE" }, + { 0x88FD, "GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT" }, + { 0x88FE, "GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB" }, + { 0x88FF, "GL_MAX_ARRAY_TEXTURE_LAYERS_EXT" }, + { 0x8904, "GL_MIN_PROGRAM_TEXEL_OFFSET_EXT" }, + { 0x8905, "GL_MAX_PROGRAM_TEXEL_OFFSET_EXT" }, + { 0x8910, "GL_STENCIL_TEST_TWO_SIDE_EXT" }, + { 0x8911, "GL_ACTIVE_STENCIL_FACE_EXT" }, + { 0x8912, "GL_MIRROR_CLAMP_TO_BORDER_EXT" }, + { 0x8914, "GL_SAMPLES_PASSED_ARB" }, + { 0x8914, "GL_SAMPLES_PASSED" }, + { 0x891A, "GL_CLAMP_VERTEX_COLOR_ARB" }, + { 0x891B, "GL_CLAMP_FRAGMENT_COLOR_ARB" }, + { 0x891C, "GL_CLAMP_READ_COLOR_ARB" }, + { 0x891D, "GL_FIXED_ONLY_ARB" }, + { 0x8920, "GL_FRAGMENT_SHADER_EXT" }, + { 0x896D, "GL_SECONDARY_INTERPOLATOR_EXT" }, + { 0x896E, "GL_NUM_FRAGMENT_REGISTERS_EXT" }, + { 0x896F, "GL_NUM_FRAGMENT_CONSTANTS_EXT" }, + { 0x8A0C, "GL_ELEMENT_ARRAY_APPLE" }, + { 0x8A0D, "GL_ELEMENT_ARRAY_TYPE_APPLE" }, + { 0x8A0E, "GL_ELEMENT_ARRAY_POINTER_APPLE" }, + { 0x8A0F, "GL_COLOR_FLOAT_APPLE" }, + { 0x8A11, "GL_UNIFORM_BUFFER" }, + { 0x8A28, "GL_UNIFORM_BUFFER_BINDING" }, + { 0x8A29, "GL_UNIFORM_BUFFER_START" }, + { 0x8A2A, "GL_UNIFORM_BUFFER_SIZE" }, + { 0x8A2B, "GL_MAX_VERTEX_UNIFORM_BLOCKS" }, + { 0x8A2C, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS" }, + { 0x8A2D, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS" }, + { 0x8A2E, "GL_MAX_COMBINED_UNIFORM_BLOCKS" }, + { 0x8A2F, "GL_MAX_UNIFORM_BUFFER_BINDINGS" }, + { 0x8A30, "GL_MAX_UNIFORM_BLOCK_SIZE" }, + { 0x8A31, "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS" }, + { 0x8A32, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS" }, + { 0x8A33, "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS" }, + { 0x8A34, "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" }, + { 0x8A35, "GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH" }, + { 0x8A36, "GL_ACTIVE_UNIFORM_BLOCKS" }, + { 0x8A37, "GL_UNIFORM_TYPE" }, + { 0x8A38, "GL_UNIFORM_SIZE" }, + { 0x8A39, "GL_UNIFORM_NAME_LENGTH" }, + { 0x8A3A, "GL_UNIFORM_BLOCK_INDEX" }, + { 0x8A3B, "GL_UNIFORM_OFFSET" }, + { 0x8A3C, "GL_UNIFORM_ARRAY_STRIDE" }, + { 0x8A3D, "GL_UNIFORM_MATRIX_STRIDE" }, + { 0x8A3E, "GL_UNIFORM_IS_ROW_MAJOR" }, + { 0x8A3F, "GL_UNIFORM_BLOCK_BINDING" }, + { 0x8A40, "GL_UNIFORM_BLOCK_DATA_SIZE" }, + { 0x8A41, "GL_UNIFORM_BLOCK_NAME_LENGTH" }, + { 0x8A42, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS" }, + { 0x8A43, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES" }, + { 0x8A44, "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER" }, + { 0x8A45, "GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER" }, + { 0x8A46, "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER" }, + { 0x8B30, "GL_FRAGMENT_SHADER_ARB" }, + { 0x8B30, "GL_FRAGMENT_SHADER" }, + { 0x8B31, "GL_VERTEX_SHADER_ARB" }, + { 0x8B31, "GL_VERTEX_SHADER" }, + { 0x8B40, "GL_PROGRAM_OBJECT_ARB" }, + { 0x8B48, "GL_SHADER_OBJECT_ARB" }, + { 0x8B49, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB" }, + { 0x8B49, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS" }, + { 0x8B4A, "GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB" }, + { 0x8B4A, "GL_MAX_VERTEX_UNIFORM_COMPONENTS" }, + { 0x8B4B, "GL_MAX_VARYING_COMPONENTS_EXT" }, + { 0x8B4B, "GL_MAX_VARYING_FLOATS_ARB" }, + { 0x8B4B, "GL_MAX_VARYING_FLOATS" }, + { 0x8B4C, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8B4C, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS" }, + { 0x8B4D, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB" }, + { 0x8B4D, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" }, + { 0x8B4E, "GL_OBJECT_TYPE_ARB" }, + { 0x8B4F, "GL_OBJECT_SUBTYPE_ARB" }, + { 0x8B4F, "GL_SHADER_TYPE" }, + { 0x8B50, "GL_FLOAT_VEC2_ARB" }, + { 0x8B50, "GL_FLOAT_VEC2" }, + { 0x8B51, "GL_FLOAT_VEC3_ARB" }, + { 0x8B51, "GL_FLOAT_VEC3" }, + { 0x8B52, "GL_FLOAT_VEC4_ARB" }, + { 0x8B52, "GL_FLOAT_VEC4" }, + { 0x8B53, "GL_INT_VEC2_ARB" }, + { 0x8B53, "GL_INT_VEC2" }, + { 0x8B54, "GL_INT_VEC3_ARB" }, + { 0x8B54, "GL_INT_VEC3" }, + { 0x8B55, "GL_INT_VEC4_ARB" }, + { 0x8B55, "GL_INT_VEC4" }, + { 0x8B56, "GL_BOOL_ARB" }, + { 0x8B56, "GL_BOOL" }, + { 0x8B57, "GL_BOOL_VEC2_ARB" }, + { 0x8B57, "GL_BOOL_VEC2" }, + { 0x8B58, "GL_BOOL_VEC3_ARB" }, + { 0x8B58, "GL_BOOL_VEC3" }, + { 0x8B59, "GL_BOOL_VEC4_ARB" }, + { 0x8B59, "GL_BOOL_VEC4" }, + { 0x8B5A, "GL_FLOAT_MAT2_ARB" }, + { 0x8B5A, "GL_FLOAT_MAT2" }, + { 0x8B5B, "GL_FLOAT_MAT3_ARB" }, + { 0x8B5B, "GL_FLOAT_MAT3" }, + { 0x8B5C, "GL_FLOAT_MAT4_ARB" }, + { 0x8B5C, "GL_FLOAT_MAT4" }, + { 0x8B5D, "GL_SAMPLER_1D_ARB" }, + { 0x8B5D, "GL_SAMPLER_1D" }, + { 0x8B5E, "GL_SAMPLER_2D_ARB" }, + { 0x8B5E, "GL_SAMPLER_2D" }, + { 0x8B5F, "GL_SAMPLER_3D_ARB" }, + { 0x8B5F, "GL_SAMPLER_3D" }, + { 0x8B60, "GL_SAMPLER_CUBE_ARB" }, + { 0x8B60, "GL_SAMPLER_CUBE" }, + { 0x8B61, "GL_SAMPLER_1D_SHADOW_ARB" }, + { 0x8B61, "GL_SAMPLER_1D_SHADOW" }, + { 0x8B62, "GL_SAMPLER_2D_SHADOW_ARB" }, + { 0x8B62, "GL_SAMPLER_2D_SHADOW" }, + { 0x8B63, "GL_SAMPLER_2D_RECT_ARB" }, + { 0x8B64, "GL_SAMPLER_2D_RECT_SHADOW_ARB" }, + { 0x8B65, "GL_FLOAT_MAT2x3" }, + { 0x8B66, "GL_FLOAT_MAT2x4" }, + { 0x8B67, "GL_FLOAT_MAT3x2" }, + { 0x8B68, "GL_FLOAT_MAT3x4" }, + { 0x8B69, "GL_FLOAT_MAT4x2" }, + { 0x8B6A, "GL_FLOAT_MAT4x3" }, + { 0x8B80, "GL_DELETE_STATUS" }, + { 0x8B80, "GL_OBJECT_DELETE_STATUS_ARB" }, + { 0x8B81, "GL_COMPILE_STATUS" }, + { 0x8B81, "GL_OBJECT_COMPILE_STATUS_ARB" }, + { 0x8B82, "GL_LINK_STATUS" }, + { 0x8B82, "GL_OBJECT_LINK_STATUS_ARB" }, + { 0x8B83, "GL_OBJECT_VALIDATE_STATUS_ARB" }, + { 0x8B83, "GL_VALIDATE_STATUS" }, + { 0x8B84, "GL_INFO_LOG_LENGTH" }, + { 0x8B84, "GL_OBJECT_INFO_LOG_LENGTH_ARB" }, + { 0x8B85, "GL_ATTACHED_SHADERS" }, + { 0x8B85, "GL_OBJECT_ATTACHED_OBJECTS_ARB" }, + { 0x8B86, "GL_ACTIVE_UNIFORMS" }, + { 0x8B86, "GL_OBJECT_ACTIVE_UNIFORMS_ARB" }, + { 0x8B87, "GL_ACTIVE_UNIFORM_MAX_LENGTH" }, + { 0x8B87, "GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB" }, + { 0x8B88, "GL_OBJECT_SHADER_SOURCE_LENGTH_ARB" }, + { 0x8B88, "GL_SHADER_SOURCE_LENGTH" }, + { 0x8B89, "GL_ACTIVE_ATTRIBUTES" }, + { 0x8B89, "GL_OBJECT_ACTIVE_ATTRIBUTES_ARB" }, + { 0x8B8A, "GL_ACTIVE_ATTRIBUTE_MAX_LENGTH" }, + { 0x8B8A, "GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB" }, + { 0x8B8B, "GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB" }, + { 0x8B8B, "GL_FRAGMENT_SHADER_DERIVATIVE_HINT" }, + { 0x8B8C, "GL_SHADING_LANGUAGE_VERSION_ARB" }, + { 0x8B8C, "GL_SHADING_LANGUAGE_VERSION" }, + { 0x8B8D, "GL_CURRENT_PROGRAM" }, + { 0x8C10, "GL_TEXTURE_RED_TYPE_ARB" }, + { 0x8C10, "GL_TEXTURE_RED_TYPE" }, + { 0x8C11, "GL_TEXTURE_GREEN_TYPE_ARB" }, + { 0x8C11, "GL_TEXTURE_GREEN_TYPE" }, + { 0x8C12, "GL_TEXTURE_BLUE_TYPE_ARB" }, + { 0x8C12, "GL_TEXTURE_BLUE_TYPE" }, + { 0x8C13, "GL_TEXTURE_ALPHA_TYPE_ARB" }, + { 0x8C13, "GL_TEXTURE_ALPHA_TYPE" }, + { 0x8C14, "GL_TEXTURE_LUMINANCE_TYPE_ARB" }, + { 0x8C15, "GL_TEXTURE_INTENSITY_TYPE_ARB" }, + { 0x8C16, "GL_TEXTURE_DEPTH_TYPE_ARB" }, + { 0x8C16, "GL_TEXTURE_DEPTH_TYPE" }, + { 0x8C17, "GL_UNSIGNED_NORMALIZED_ARB" }, + { 0x8C17, "GL_UNSIGNED_NORMALIZED" }, + { 0x8C18, "GL_TEXTURE_1D_ARRAY_EXT" }, + { 0x8C19, "GL_PROXY_TEXTURE_1D_ARRAY_EXT" }, + { 0x8C1A, "GL_TEXTURE_2D_ARRAY_EXT" }, + { 0x8C1B, "GL_PROXY_TEXTURE_2D_ARRAY_EXT" }, + { 0x8C1C, "GL_TEXTURE_BINDING_1D_ARRAY_EXT" }, + { 0x8C1D, "GL_TEXTURE_BINDING_2D_ARRAY_EXT" }, + { 0x8C29, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT" }, + { 0x8C3A, "GL_R11F_G11F_B10F_EXT" }, + { 0x8C3B, "GL_UNSIGNED_INT_10F_11F_11F_REV_EXT" }, + { 0x8C3C, "GL_RGBA_SIGNED_COMPONENTS_EXT" }, + { 0x8C3D, "GL_RGB9_E5_EXT" }, + { 0x8C3E, "GL_UNSIGNED_INT_5_9_9_9_REV_EXT" }, + { 0x8C3F, "GL_TEXTURE_SHARED_SIZE_EXT" }, + { 0x8C40, "GL_SRGB_EXT" }, + { 0x8C40, "GL_SRGB" }, + { 0x8C41, "GL_SRGB8_EXT" }, + { 0x8C41, "GL_SRGB8" }, + { 0x8C42, "GL_SRGB_ALPHA_EXT" }, + { 0x8C42, "GL_SRGB_ALPHA" }, + { 0x8C43, "GL_SRGB8_ALPHA8_EXT" }, + { 0x8C43, "GL_SRGB8_ALPHA8" }, + { 0x8C44, "GL_SLUMINANCE_ALPHA_EXT" }, + { 0x8C44, "GL_SLUMINANCE_ALPHA" }, + { 0x8C45, "GL_SLUMINANCE8_ALPHA8_EXT" }, + { 0x8C45, "GL_SLUMINANCE8_ALPHA8" }, + { 0x8C46, "GL_SLUMINANCE_EXT" }, + { 0x8C46, "GL_SLUMINANCE" }, + { 0x8C47, "GL_SLUMINANCE8_EXT" }, + { 0x8C47, "GL_SLUMINANCE8" }, + { 0x8C48, "GL_COMPRESSED_SRGB_EXT" }, + { 0x8C48, "GL_COMPRESSED_SRGB" }, + { 0x8C49, "GL_COMPRESSED_SRGB_ALPHA_EXT" }, + { 0x8C49, "GL_COMPRESSED_SRGB_ALPHA" }, + { 0x8C4A, "GL_COMPRESSED_SLUMINANCE_EXT" }, + { 0x8C4A, "GL_COMPRESSED_SLUMINANCE" }, + { 0x8C4B, "GL_COMPRESSED_SLUMINANCE_ALPHA_EXT" }, + { 0x8C4B, "GL_COMPRESSED_SLUMINANCE_ALPHA" }, + { 0x8C4C, "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT" }, + { 0x8C4D, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT" }, + { 0x8C4E, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT" }, + { 0x8C4F, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT" }, + { 0x8C76, "GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT" }, + { 0x8C7F, "GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT" }, + { 0x8C80, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT" }, + { 0x8C83, "GL_TRANSFORM_FEEDBACK_VARYINGS_EXT" }, + { 0x8C84, "GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT" }, + { 0x8C85, "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT" }, + { 0x8C87, "GL_PRIMITIVES_GENERATED_EXT" }, + { 0x8C88, "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT" }, + { 0x8C89, "GL_RASTERIZER_DISCARD_EXT" }, + { 0x8C8A, "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT" }, + { 0x8C8B, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT" }, + { 0x8C8C, "GL_INTERLEAVED_ATTRIBS_EXT" }, + { 0x8C8D, "GL_SEPARATE_ATTRIBS_EXT" }, + { 0x8C8E, "GL_TRANSFORM_FEEDBACK_BUFFER_EXT" }, + { 0x8C8F, "GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT" }, + { 0x8CA0, "GL_POINT_SPRITE_COORD_ORIGIN" }, + { 0x8CA1, "GL_LOWER_LEFT" }, + { 0x8CA2, "GL_UPPER_LEFT" }, + { 0x8CA3, "GL_STENCIL_BACK_REF" }, + { 0x8CA4, "GL_STENCIL_BACK_VALUE_MASK" }, + { 0x8CA5, "GL_STENCIL_BACK_WRITEMASK" }, + { 0x8CA6, "GL_DRAW_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CA6, "GL_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CA6, "GL_FRAMEBUFFER_BINDING" }, + { 0x8CA7, "GL_RENDERBUFFER_BINDING_EXT" }, + { 0x8CA7, "GL_RENDERBUFFER_BINDING" }, + { 0x8CA8, "GL_READ_FRAMEBUFFER_EXT" }, + { 0x8CA8, "GL_READ_FRAMEBUFFER" }, + { 0x8CA9, "GL_DRAW_FRAMEBUFFER_EXT" }, + { 0x8CA9, "GL_DRAW_FRAMEBUFFER" }, + { 0x8CAA, "GL_READ_FRAMEBUFFER_BINDING_EXT" }, + { 0x8CAA, "GL_READ_FRAMEBUFFER_BINDING" }, + { 0x8CAB, "GL_RENDERBUFFER_SAMPLES_EXT" }, + { 0x8CAB, "GL_RENDERBUFFER_SAMPLES" }, + { 0x8CAC, "GL_DEPTH_COMPONENT32F" }, + { 0x8CAD, "GL_DEPTH32F_STENCIL8" }, + { 0x8CD0, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT" }, + { 0x8CD0, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE" }, + { 0x8CD1, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT" }, + { 0x8CD1, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME" }, + { 0x8CD2, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT" }, + { 0x8CD2, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL" }, + { 0x8CD3, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT" }, + { 0x8CD3, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT" }, + { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER" }, + { 0x8CD5, "GL_FRAMEBUFFER_COMPLETE_EXT" }, + { 0x8CD5, "GL_FRAMEBUFFER_COMPLETE" }, + { 0x8CD6, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT" }, + { 0x8CD6, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" }, + { 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT" }, + { 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" }, + { 0x8CD9, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT" }, + { 0x8CDA, "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT" }, + { 0x8CDB, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT" }, + { 0x8CDB, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" }, + { 0x8CDC, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT" }, + { 0x8CDC, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" }, + { 0x8CDD, "GL_FRAMEBUFFER_UNSUPPORTED_EXT" }, + { 0x8CDD, "GL_FRAMEBUFFER_UNSUPPORTED" }, + { 0x8CDF, "GL_MAX_COLOR_ATTACHMENTS_EXT" }, + { 0x8CDF, "GL_MAX_COLOR_ATTACHMENTS" }, + { 0x8CE0, "GL_COLOR_ATTACHMENT0_EXT" }, + { 0x8CE0, "GL_COLOR_ATTACHMENT0" }, + { 0x8CE1, "GL_COLOR_ATTACHMENT1_EXT" }, + { 0x8CE1, "GL_COLOR_ATTACHMENT1" }, + { 0x8CE2, "GL_COLOR_ATTACHMENT2_EXT" }, + { 0x8CE2, "GL_COLOR_ATTACHMENT2" }, + { 0x8CE3, "GL_COLOR_ATTACHMENT3_EXT" }, + { 0x8CE3, "GL_COLOR_ATTACHMENT3" }, + { 0x8CE4, "GL_COLOR_ATTACHMENT4_EXT" }, + { 0x8CE4, "GL_COLOR_ATTACHMENT4" }, + { 0x8CE5, "GL_COLOR_ATTACHMENT5_EXT" }, + { 0x8CE5, "GL_COLOR_ATTACHMENT5" }, + { 0x8CE6, "GL_COLOR_ATTACHMENT6_EXT" }, + { 0x8CE6, "GL_COLOR_ATTACHMENT6" }, + { 0x8CE7, "GL_COLOR_ATTACHMENT7_EXT" }, + { 0x8CE7, "GL_COLOR_ATTACHMENT7" }, + { 0x8CE8, "GL_COLOR_ATTACHMENT8_EXT" }, + { 0x8CE8, "GL_COLOR_ATTACHMENT8" }, + { 0x8CE9, "GL_COLOR_ATTACHMENT9_EXT" }, + { 0x8CE9, "GL_COLOR_ATTACHMENT9" }, + { 0x8CEA, "GL_COLOR_ATTACHMENT10_EXT" }, + { 0x8CEA, "GL_COLOR_ATTACHMENT10" }, + { 0x8CEB, "GL_COLOR_ATTACHMENT11_EXT" }, + { 0x8CEB, "GL_COLOR_ATTACHMENT11" }, + { 0x8CEC, "GL_COLOR_ATTACHMENT12_EXT" }, + { 0x8CEC, "GL_COLOR_ATTACHMENT12" }, + { 0x8CED, "GL_COLOR_ATTACHMENT13_EXT" }, + { 0x8CED, "GL_COLOR_ATTACHMENT13" }, + { 0x8CEE, "GL_COLOR_ATTACHMENT14_EXT" }, + { 0x8CEE, "GL_COLOR_ATTACHMENT14" }, + { 0x8CEF, "GL_COLOR_ATTACHMENT15_EXT" }, + { 0x8CEF, "GL_COLOR_ATTACHMENT15" }, + { 0x8D00, "GL_DEPTH_ATTACHMENT_EXT" }, + { 0x8D00, "GL_DEPTH_ATTACHMENT" }, + { 0x8D20, "GL_STENCIL_ATTACHMENT_EXT" }, + { 0x8D20, "GL_STENCIL_ATTACHMENT" }, + { 0x8D40, "GL_FRAMEBUFFER_EXT" }, + { 0x8D40, "GL_FRAMEBUFFER" }, + { 0x8D41, "GL_RENDERBUFFER_EXT" }, + { 0x8D41, "GL_RENDERBUFFER" }, + { 0x8D42, "GL_RENDERBUFFER_WIDTH_EXT" }, + { 0x8D42, "GL_RENDERBUFFER_WIDTH" }, + { 0x8D43, "GL_RENDERBUFFER_HEIGHT_EXT" }, + { 0x8D43, "GL_RENDERBUFFER_HEIGHT" }, + { 0x8D44, "GL_RENDERBUFFER_INTERNAL_FORMAT_EXT" }, + { 0x8D44, "GL_RENDERBUFFER_INTERNAL_FORMAT" }, + { 0x8D46, "GL_STENCIL_INDEX1_EXT" }, + { 0x8D46, "GL_STENCIL_INDEX1" }, + { 0x8D47, "GL_STENCIL_INDEX4_EXT" }, + { 0x8D47, "GL_STENCIL_INDEX4" }, + { 0x8D48, "GL_STENCIL_INDEX8_EXT" }, + { 0x8D48, "GL_STENCIL_INDEX8" }, + { 0x8D49, "GL_STENCIL_INDEX16_EXT" }, + { 0x8D49, "GL_STENCIL_INDEX16" }, + { 0x8D50, "GL_RENDERBUFFER_RED_SIZE_EXT" }, + { 0x8D50, "GL_RENDERBUFFER_RED_SIZE" }, + { 0x8D51, "GL_RENDERBUFFER_GREEN_SIZE_EXT" }, + { 0x8D51, "GL_RENDERBUFFER_GREEN_SIZE" }, + { 0x8D52, "GL_RENDERBUFFER_BLUE_SIZE_EXT" }, + { 0x8D52, "GL_RENDERBUFFER_BLUE_SIZE" }, + { 0x8D53, "GL_RENDERBUFFER_ALPHA_SIZE_EXT" }, + { 0x8D53, "GL_RENDERBUFFER_ALPHA_SIZE" }, + { 0x8D54, "GL_RENDERBUFFER_DEPTH_SIZE_EXT" }, + { 0x8D54, "GL_RENDERBUFFER_DEPTH_SIZE" }, + { 0x8D55, "GL_RENDERBUFFER_STENCIL_SIZE_EXT" }, + { 0x8D55, "GL_RENDERBUFFER_STENCIL_SIZE" }, + { 0x8D56, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT" }, + { 0x8D56, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" }, + { 0x8D57, "GL_MAX_SAMPLES_EXT" }, + { 0x8D57, "GL_MAX_SAMPLES" }, + { 0x8D70, "GL_RGBA32UI_EXT" }, + { 0x8D71, "GL_RGB32UI_EXT" }, + { 0x8D72, "GL_ALPHA32UI_EXT" }, + { 0x8D73, "GL_INTENSITY32UI_EXT" }, + { 0x8D74, "GL_LUMINANCE32UI_EXT" }, + { 0x8D75, "GL_LUMINANCE_ALPHA32UI_EXT" }, + { 0x8D76, "GL_RGBA16UI_EXT" }, + { 0x8D77, "GL_RGB16UI_EXT" }, + { 0x8D78, "GL_ALPHA16UI_EXT" }, + { 0x8D79, "GL_INTENSITY16UI_EXT" }, + { 0x8D7A, "GL_LUMINANCE16UI_EXT" }, + { 0x8D7B, "GL_LUMINANCE_ALPHA16UI_EXT" }, + { 0x8D7C, "GL_RGBA8UI_EXT" }, + { 0x8D7D, "GL_RGB8UI_EXT" }, + { 0x8D7E, "GL_ALPHA8UI_EXT" }, + { 0x8D7F, "GL_INTENSITY8UI_EXT" }, + { 0x8D80, "GL_LUMINANCE8UI_EXT" }, + { 0x8D81, "GL_LUMINANCE_ALPHA8UI_EXT" }, + { 0x8D82, "GL_RGBA32I_EXT" }, + { 0x8D83, "GL_RGB32I_EXT" }, + { 0x8D84, "GL_ALPHA32I_EXT" }, + { 0x8D85, "GL_INTENSITY32I_EXT" }, + { 0x8D86, "GL_LUMINANCE32I_EXT" }, + { 0x8D87, "GL_LUMINANCE_ALPHA32I_EXT" }, + { 0x8D88, "GL_RGBA16I_EXT" }, + { 0x8D89, "GL_RGB16I_EXT" }, + { 0x8D8A, "GL_ALPHA16I_EXT" }, + { 0x8D8B, "GL_INTENSITY16I_EXT" }, + { 0x8D8C, "GL_LUMINANCE16I_EXT" }, + { 0x8D8D, "GL_LUMINANCE_ALPHA16I_EXT" }, + { 0x8D8E, "GL_RGBA8I_EXT" }, + { 0x8D8F, "GL_RGB8I_EXT" }, + { 0x8D90, "GL_ALPHA8I_EXT" }, + { 0x8D91, "GL_INTENSITY8I_EXT" }, + { 0x8D92, "GL_LUMINANCE8I_EXT" }, + { 0x8D93, "GL_LUMINANCE_ALPHA8I_EXT" }, + { 0x8D94, "GL_RED_INTEGER_EXT" }, + { 0x8D95, "GL_GREEN_INTEGER_EXT" }, + { 0x8D96, "GL_BLUE_INTEGER_EXT" }, + { 0x8D97, "GL_ALPHA_INTEGER_EXT" }, + { 0x8D98, "GL_RGB_INTEGER_EXT" }, + { 0x8D99, "GL_RGBA_INTEGER_EXT" }, + { 0x8D9A, "GL_BGR_INTEGER_EXT" }, + { 0x8D9B, "GL_BGRA_INTEGER_EXT" }, + { 0x8D9C, "GL_LUMINANCE_INTEGER_EXT" }, + { 0x8D9D, "GL_LUMINANCE_ALPHA_INTEGER_EXT" }, + { 0x8D9E, "GL_RGBA_INTEGER_MODE_EXT" }, + { 0x8DA7, "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT" }, + { 0x8DA8, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT" }, + { 0x8DA9, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT" }, + { 0x8DAD, "GL_FLOAT_32_UNSIGNED_INT_24_8_REV" }, + { 0x8DB9, "GL_FRAMEBUFFER_SRGB_EXT" }, + { 0x8DBA, "GL_FRAMEBUFFER_SRGB_CAPABLE_EXT" }, + { 0x8DBB, "GL_COMPRESSED_RED_RGTC1" }, + { 0x8DBC, "GL_COMPRESSED_SIGNED_RED_RGTC1" }, + { 0x8DBD, "GL_COMPRESSED_RG_RGTC2" }, + { 0x8DBE, "GL_COMPRESSED_SIGNED_RG_RGTC2" }, + { 0x8DC0, "GL_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DC1, "GL_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DC2, "GL_SAMPLER_BUFFER_EXT" }, + { 0x8DC3, "GL_SAMPLER_1D_ARRAY_SHADOW_EXT" }, + { 0x8DC4, "GL_SAMPLER_2D_ARRAY_SHADOW_EXT" }, + { 0x8DC5, "GL_SAMPLER_CUBE_SHADOW_EXT" }, + { 0x8DC6, "GL_UNSIGNED_INT_VEC2_EXT" }, + { 0x8DC7, "GL_UNSIGNED_INT_VEC3_EXT" }, + { 0x8DC8, "GL_UNSIGNED_INT_VEC4_EXT" }, + { 0x8DC9, "GL_INT_SAMPLER_1D_EXT" }, + { 0x8DCA, "GL_INT_SAMPLER_2D_EXT" }, + { 0x8DCB, "GL_INT_SAMPLER_3D_EXT" }, + { 0x8DCC, "GL_INT_SAMPLER_CUBE_EXT" }, + { 0x8DCD, "GL_INT_SAMPLER_2D_RECT_EXT" }, + { 0x8DCE, "GL_INT_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DCF, "GL_INT_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DD0, "GL_INT_SAMPLER_BUFFER_EXT" }, + { 0x8DD1, "GL_UNSIGNED_INT_SAMPLER_1D_EXT" }, + { 0x8DD2, "GL_UNSIGNED_INT_SAMPLER_2D_EXT" }, + { 0x8DD3, "GL_UNSIGNED_INT_SAMPLER_3D_EXT" }, + { 0x8DD4, "GL_UNSIGNED_INT_SAMPLER_CUBE_EXT" }, + { 0x8DD5, "GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT" }, + { 0x8DD6, "GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT" }, + { 0x8DD7, "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT" }, + { 0x8DD8, "GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT" }, + { 0x8DD9, "GL_GEOMETRY_SHADER_EXT" }, + { 0x8DDA, "GL_GEOMETRY_VERTICES_OUT_EXT" }, + { 0x8DDB, "GL_GEOMETRY_INPUT_TYPE_EXT" }, + { 0x8DDC, "GL_GEOMETRY_OUTPUT_TYPE_EXT" }, + { 0x8DDD, "GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT" }, + { 0x8DDE, "GL_MAX_VERTEX_VARYING_COMPONENTS_EXT" }, + { 0x8DDF, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT" }, + { 0x8DE0, "GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT" }, + { 0x8DE1, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT" }, + { 0x8DE2, "GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT" }, + { 0x8DE3, "GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT" }, + { 0x8DE4, "GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT" }, + { 0x8DED, "GL_MAX_BINDABLE_UNIFORM_SIZE_EXT" }, + { 0x8DEE, "GL_UNIFORM_BUFFER_EXT" }, + { 0x8DEF, "GL_UNIFORM_BUFFER_BINDING_EXT" }, + { 0x8E4C, "GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT" }, + { 0x8E4D, "GL_FIRST_VERTEX_CONVENTION_EXT" }, + { 0x8E4E, "GL_LAST_VERTEX_CONVENTION_EXT" }, + { 0x8E4F, "GL_PROVOKING_VERTEX_EXT" }, + + VE( TERMVALUE ) +}; + +const GLMValueEntry_t g_gl_renderers[] = +{ + { 0x00020200, "Generic" }, + { 0x00020400, "GenericFloat" }, + { 0x00020600, "AppleSW" }, + { 0x00021000, "ATIRage128" }, + { 0x00021200, "ATIRadeon" }, + { 0x00021400, "ATIRagePro" }, + { 0x00021600, "ATIRadeon8500" }, + { 0x00021800, "ATIRadeon9700" }, + { 0x00021900, "ATIRadeonX1000" }, + { 0x00021A00, "ATIRadeonX2000" }, + { 0x00022000, "NVGeForce2MX" }, + { 0x00022200, "NVGeForce3" }, + { 0x00022400, "NVGeForceFX" }, + { 0x00022600, "NVGeForce8xxx" }, + { 0x00023000, "VTBladeXP2" }, + { 0x00024000, "Intel900" }, + { 0x00024200, "IntelX3100" }, + { 0x00040000, "Mesa3DFX" }, + + VE( TERMVALUE ) +}; + + +//=============================================================================== +// decode helper funcs + +char s_glmStrScratch[65536]; +int s_glmStrCursor = 0; + +const char * GLMDecode( GLMThing_t thingtype, unsigned long value ) +{ + const GLMValueEntry_t *table = NULL; + //char isflags = 0; + + switch( thingtype ) + { + case eD3D_DEVTYPE: table = g_d3d_devtypes; + break; + + case eD3D_FORMAT: table = g_d3d_formats; + break; + + case eD3D_RTYPE: table = g_d3d_rtypes; + break; + + case eD3D_USAGE: table = g_d3d_usages; + break; + + case eD3D_RSTATE: table = g_d3d_rstates; + break; + + case eD3D_SIO: table = g_d3d_opcodes; + break; + + case eD3D_VTXDECLUSAGE: table = g_d3d_vtxdeclusages; + break; + + case eCGL_RENDID: table = g_cgl_rendids; + break; + + case eGL_ERROR: table = g_gl_errors; + break; + + case eGL_ENUM: table = g_gl_enums; + break; + + case eGL_RENDERER: table = g_gl_renderers; + break; + + default: + GLMStop(); + return "UNKNOWNTYPE"; + break; + } + + if (table) + { + while( table->value != TERMVALUE ) + { + if (table->value == value) + { + return table->name; + } + table++; + } + } + return "UNKNOWN"; +} + +const char *GLMDecodeMask( GLMThing_t kind, unsigned long value ) +{ + // if cursor to scratch buffer is within 1K of EOB, rewind + // nobody is going to decode 63K of flag string values in a single call.. + + // this means that strings returned by this function have a short lifetime.. print them and do not save the pointer.. + + if ( (sizeof(s_glmStrScratch) - s_glmStrCursor) < 1000 ) + { + s_glmStrCursor = 0; + } + + char *start = &s_glmStrScratch[ s_glmStrCursor ]; + char *dest = start; + char first = 1; + + DWORD mask = static_cast<DWORD>(1L<<31); + while(mask) + { + if (mask & value) + { + sprintf(dest,"%s%s", (first) ? "" : "|", GLMDecode( kind, value&mask ) ); + first = 0; + + dest += strlen(dest); // leaves dest pointing at the end null + } + mask >>= 1; + } + s_glmStrCursor = (dest - s_glmStrScratch) + 1; // +1 so the next decoded flag set doesn't land on the ending null + return start; + +} + +#undef VE +#undef TERMVALUE + +//=============================================================================== + +bool GLMDetectOGLP( void ) +{ + bool result = false; +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + GLint forceFlush; + CGLError error = CGLGetParameter(CGLGetCurrentContext(), kCGLCPEnableForceFlush, &forceFlush); + result = error == 0; + if (result) + { + // enable a breakpoint on color4sv + int oglp_bkpt[3] = { kCGLFEglColor4sv, kCGLProfBreakBefore, 1 }; + + CGLSetOption( kCGLGOEnableBreakpoint, (GLint)oglp_bkpt ); + } + +#endif + return result; +} + + +// from http://blog.timac.org/?p=190 + +#ifndef _WIN32 + #include <stdbool.h> +#endif +#include <sys/types.h> +#ifndef _WIN32 + #include <unistd.h> + #include <sys/sysctl.h> +#endif + +// From Technical Q&A QA1361 +// Returns true if the current process +// is being debugged (either running +// under the debugger or has a debugger +// attached post facto). + +bool GLMDetectGDB( void ) // aka AmIBeingDebugged() +{ +#ifdef OSX + bool result; + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, + // if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info + // we want, in this case we're looking for + // information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + + assert(junk == 0); + + // We're being debugged if the P_TRACED + // flag is set. + + result = ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + return result; +#else + return Sys_IsDebuggerPresent(); +#endif +} + + +static uint g_glmDebugChannelMask = 0; // which output channels are available (can be more than one) +static uint g_glmDebugFlavorMask = 0; // which message flavors are enabled for output (can be more than one) + +uint GLMDetectAvailableChannels( void ) +{ + uint result = 0; + + // printf is always available (except maybe in release... ?) + result |= (1 << ePrintf); + + // gdb + if (GLMDetectGDB()) + { + result |= (1 << eDebugger); + printf("\n############# GDB Detected"); + } + + // oglp + if (GLMDetectOGLP()) + { + result |= (1 << eGLProfiler); + printf("\n############# OGLP Detected"); + } + + return result; +} + + +static bool g_debugInitDone = false; + +#if GLMDEBUG + + // following funcs vanish if GLMDEBUG not set + +void GLMDebugInitialize( bool forceReinit ) +{ + if ( !g_debugInitDone || forceReinit ) + { + // detect channels + uint channelMask = GLMDetectAvailableChannels(); + + // see if there are any prohibitions on the commandline + // (also add any other desired reasons, release build say).. + + if ( CommandLine()->FindParm("-noprintconsole") ) + { + channelMask &= ~(1<<ePrintf); + } + + if ( CommandLine()->FindParm("-noprintdebugger") ) + { + channelMask &= ~(1<<eDebugger); + } + + if ( CommandLine()->FindParm("-noprintoglp") ) + { + channelMask &= ~(1<<eGLProfiler); + } + + // finally, disable all of them if commandline did not say "enable spew" + if (!CommandLine()->FindParm("-glmspew")) + { + channelMask = 0; + } + + // set the output channel mask + GLMDebugChannelMask( &channelMask ); + + // if any channels are enabled, enable some output + if ( channelMask ) + { + // start mostly quiet unless the -glmbootspew option is there + if ( CommandLine()->FindParm( "-glmbootspew" ) ) + { + g_glmDebugFlavorMask = 0xFFFFFFFF; + } + else + { + g_glmDebugFlavorMask = + (1<<eAllFlavors) + | (1<<eDebugDump) // -D- stuff + // | (1<<eFrameBufData) // -F- + // | (1<<eDXStuff) // -X- + // | (1<<eTenure) // > < + // | (1<<eAllocations) // -A- + | (1<<eSlowness) // -Z- + | (1<<eDefaultFlavor); // adjust to suit + } + } + else + { + g_glmDebugFlavorMask = 0; + } + } +} + +uint GLMDebugChannelMask( uint *newValue ) +{ + if (newValue) + { + g_glmDebugChannelMask = *newValue; + } + + uint result = g_glmDebugChannelMask; + + // leave space for any override / mute mechanism we might want to inject here + + return result; +} + +uint GLMDebugFlavorMask( uint *newValue ) +{ + if (newValue) + { + g_glmDebugFlavorMask = *newValue; + } + + uint result = g_glmDebugFlavorMask; + + // leave space for any override / mute mechanism we might want to inject here + + return result; +} + +#endif + +//=============================================================================== +void GLMEnableTrace( bool on ) +{ +#if GLMDEBUG +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + if ( GLMDebugChannelMask() & (1<<eGLProfiler) ) + { + CGLSetOption(kCGLGOEnableFunctionTrace, on ? GL_TRUE : GL_FALSE ); + } +#endif +#endif +} + +//=============================================================================== + +#if GLMDEBUG + // following funcs vanish if GLMDEBUG not set + +void GLMStringOut( char *string ) +{ + if ( GLMDebugChannelMask() & ( (1<<ePrintf) | (1<<eDebugger) ) ) + { +#ifdef WIN32 + OutputDebugStringA( string ); + OutputDebugStringA( "\n"); +#else + puts( string ); +#endif + } + +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + if ( GLMDebugChannelMask() & (1<<eGLProfiler) ) + { + CGLSetOption( kCGLGOComment, (GLint)string ); + } +#endif + + if ( GLMDebugChannelMask() & (1<<eGLProfiler) ) + { + if (gGL->m_bHave_GL_GREMEDY_string_marker) + { + gGL->glStringMarkerGREMEDY(0, string); + } + } +} + +int g_glm_indent = 0; +int g_glm_indent_max = 40; // 40 tabs max + +#ifndef OSX +const char *strnstr( const char *haystack, const char *needle, int len ) +{ + return strstr( haystack, needle ); +} +#endif + +EGLMDebugFlavor GLMAssessFlavor( char *str ) +{ + EGLMDebugFlavor flavor = eDefaultFlavor; + + if (strnstr(str, "-D-", 4)) + { + // debug dump + flavor = eDebugDump; + } + else if (strnstr(str, "-M-", 4)) + { + // matrix data + flavor = eMatrixData; + } + else if (strnstr(str, "-S-", 4)) + { + // shader data + flavor = eShaderData; + } + else if (strnstr(str, "-F-", 4)) + { + // framebuf data + flavor = eFrameBufData; + } + else if (strnstr(str, "-X-", 4)) + { + // DirectX data + flavor = eDXStuff; + } + else if (strnstr(str, "-A-", 4)) + { + // allocation data + flavor = eAllocations; + } + else if (strnstr(str, "-Z-", 4)) + { + // allocation data + flavor = eSlowness; + } + else if (str[0] == '<' || str[0] == '>') + { + // entry/exit (aka tenure) + flavor = eTenure; + } + else if (strnstr(str, "---", 4)) // note we check four chars worth so you can do >-M- and it will indent and be filterable + { + // comment + flavor = eComment; + } + + return flavor; +} + +void GLMPrintfVA( const char *fmt, va_list vargs ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<<eAllFlavors) ) ) + return; + + // characterize the flavor of the string + // if flavor hits against the flavor mask, print it + EGLMDebugFlavor flavor = GLMAssessFlavor( (char *)fmt ); + if ( !(flavorMask & (1<<flavor)) ) + return; + + // print the formatted string, with indenting + // if first char is a '>' - raise indent level after print. + // if first char is a '<' - lower indent level before print. + + char buf[100000]; + + if (fmt[0] == '<') + { + GLMIncIndent( -1 ); + } + + memset( buf, '\t', g_glm_indent ); + vsprintf( buf+g_glm_indent, fmt, vargs ); + GLMStringOut( buf ); + + if (fmt[0] == '>') + { + GLMIncIndent( 1 ); + } +} + +void GLMPrintf( const char *fmt, ... ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<<eAllFlavors) ) ) + return; + + va_list vargs; + va_start(vargs, fmt); + GLMPrintfVA( fmt, vargs ); + va_end( vargs ); +} + +void GLMPrintStr( const char *str, EGLMDebugFlavor flavor ) // will indent +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<<eAllFlavors) ) ) + return; + + // if flavor argument hits against the flavor mask, print it + if ( !(flavorMask & (1<<flavor)) ) + return; + + // just print the plain string, with indenting + // if first char is a '>' - raise indent level after print. + // if first char is a '<' - lower indent level before print. + + char buf[64000]; + + if (str[0] == '<') + { + GLMIncIndent( -1 ); + } + + memset( buf, '\t', g_glm_indent ); + + if (strlen(str) < sizeof(buf)-g_glm_indent-1) + { + strcpy( buf + g_glm_indent, str ); + } + else + { + DXABSTRACT_BREAK_ON_ERROR(); + } + + + GLMStringOut( buf ); // single string out with indenting + + if (str[0] == '>') + { + GLMIncIndent( 1 ); + } +} + + +void GLMPrintText( const char *str, EGLMDebugFlavor flavor, uint options ) +{ + // if no channels open, return + uint channelMask = GLMDebugChannelMask(); + if (!channelMask) + return; + + // if "all flavors" is off, return + uint flavorMask = GLMDebugFlavorMask(); + if (! ( flavorMask & (1<<eAllFlavors) ) ) + return; + + // if flavor argument hits against the flavor mask, print it + if ( !(flavorMask & (1<<flavor)) ) + return; + + char buf[64000]; + char lineout[64000]; + int linenum = 1; + + V_strncpy( buf, str, sizeof(buf) ); + + // walk the text and treat each newline as an indentation opportunity.. + const char *mark = buf; + const char *end = mark + strlen(buf); + //const char *next = NULL; + + while(mark < end) + { + // starting at mark, see if there is a newline between there and end + char *next = (char*)strchr( mark, '\n' ); + const char *printfrom = mark; + if (next) + { + // print text from mark up through next. move next to char after newline. + *next = 0; + mark = next+1; + } + else + { + // print all text after mark and stop + mark = end; + } + if (options & GLMPRINTTEXT_NUMBEREDLINES) + { + sprintf( lineout, "%-5d| %s", linenum, printfrom ); + GLMPrintStr( lineout, flavor ); + linenum++; + } + else + { + GLMPrintStr( printfrom, flavor ); + } + } +} + +int GLMIncIndent( int indentDelta ) +{ + g_glm_indent += indentDelta; + + if (indentDelta>0) + { + if (g_glm_indent > g_glm_indent_max) + { + g_glm_indent = g_glm_indent_max; + } + } + else + { + if (g_glm_indent < 0) + { + g_glm_indent = 0; + } + } + return g_glm_indent; +} + +int GLMGetIndent( void ) +{ + return g_glm_indent; +} + +void GLMSetIndent( int indent ) +{ + g_glm_indent = indent; +} + +#endif + +// PIX tracking - you can call these outside of GLMDEBUG=true +char sg_pPIXName[128]; + + +#ifndef OSX +ConVar gl_telemetry_gpu_pipeline_flushing( "gl_telemetry_gpu_pipeline_flushing", "0" ); + +class CGPUTimestampManager +{ + CGPUTimestampManager( const CGPUTimestampManager & ); + CGPUTimestampManager& operator= ( CGPUTimestampManager & ); + +public: + CGPUTimestampManager() : + m_bInitialized( false ), + m_nCurFrame( 0 ), + m_flGPUToCPUOffsetInS( 0 ), + m_flGPUToS( 0 ), + m_flRdtscToS( 0 ), + m_flSToRdtsc( 0 ), + m_nFreeQueryPoolSize( 0 ), + m_nOutstandingQueriesHead( 0 ), + m_nOutstandingQueriesTail( 0 ), + m_nNumOutstandingQueryZones( 0 ), + m_nQueryZoneStackSize( 0 ), + m_nNumFinishedZones( 0 ), + m_nTotalSpanWorkCount( 0 ) + { + memset( m_FreeQueryPool, 0, sizeof( m_FreeQueryPool ) ) ; + memset( m_QueryZoneStack, 0, sizeof( m_QueryZoneStack ) ); + memset( m_OutstandingQueryZones, 0, sizeof( m_OutstandingQueryZones ) ); + memset( m_FinishedZones, 0, sizeof( m_FinishedZones ) ); + } + + ~CGPUTimestampManager() + { + Deinit(); + } + + inline bool IsInitialized() const { return m_bInitialized; } + inline uint GetCurFrame() const { return m_nCurFrame; } + + void Init() + { + Deinit(); + + memset( m_FreeQueryPool, 0, sizeof( m_FreeQueryPool ) ) ; + memset( m_QueryZoneStack, 0, sizeof( m_QueryZoneStack ) ); + memset( m_OutstandingQueryZones, 0, sizeof( m_OutstandingQueryZones ) ); + memset( m_FinishedZones, 0, sizeof( m_FinishedZones ) ); + + InitRdtsc(); + + m_nCurFrame = 0; + + gGL->glGenQueries( cFreeQueryPoolSize, m_FreeQueryPool ); + m_nFreeQueryPoolSize = cFreeQueryPoolSize; + + m_nOutstandingQueriesHead = 0; + m_nOutstandingQueriesTail = 0; + m_nNumOutstandingQueryZones = 0; + + m_nQueryZoneStackSize = 0; + m_nNumFinishedZones = 0; + + m_bInitialized = true; + + m_nTotalSpanWorkCount = 0; + + Calibrate(); + } + + void Calibrate() + { + if ( !m_bInitialized ) + return; + + PipelineFlush(); + + m_flGPUToS = 1.0 / 1000000000.0; + + //0.99997541250006794; + //0.99997530000006662; + // Correction factor to prevent excessive drift, only calibrated on my system, we need a better way of computing/recording this. + double flGPURatio = 0.99997425000007034000; + + const uint NT = 1; + for ( uint nTrial = 0; nTrial < NT; nTrial++ ) + { + const uint R = 16; + double flClockOffsetsInS[R]; + for ( uint q = 0; q < R; q++) + { + uint64 nBestTotalCPUTimestamp = (uint64)-1; + uint64 nBestCPUTimestamp = 0; + GLuint64 nBestGPUTimestamp = 0; + + for ( uint i = 0; i < 10; i++) + { + const uint64 nStartCPUTimestamp = Plat_Rdtsc(); + + gGL->glQueryCounter( m_FreeQueryPool[0], GL_TIMESTAMP); + PipelineFlush(); + + const uint64 nEndCPUTimestamp = Plat_Rdtsc(); + + GLint nAvailable; + do + { + gGL->glGetQueryObjectiv( m_FreeQueryPool[0], GL_QUERY_RESULT_AVAILABLE, &nAvailable ); + } while ( !nAvailable ); + + GLuint64 nGPUTimestamp; + gGL->glGetQueryObjectui64v( m_FreeQueryPool[0], GL_QUERY_RESULT, &nGPUTimestamp ); + + const uint64 nTotalCPUTimestamp = nEndCPUTimestamp - nStartCPUTimestamp; + if ( nTotalCPUTimestamp < nBestTotalCPUTimestamp ) + { + nBestTotalCPUTimestamp = nTotalCPUTimestamp; + nBestCPUTimestamp = nStartCPUTimestamp; + nBestGPUTimestamp = nGPUTimestamp; + } + } + + double flCPUTimestampTimeInSeconds = nBestCPUTimestamp * m_flRdtscToS; + double flGPUTimestampTimeInSeconds = nBestGPUTimestamp * m_flGPUToS * flGPURatio; + + flClockOffsetsInS[q] = flCPUTimestampTimeInSeconds - flGPUTimestampTimeInSeconds; + + ThreadSleep(100); + + DbgPrintf("%f %f %1.20f\n", flCPUTimestampTimeInSeconds, flGPUTimestampTimeInSeconds, flClockOffsetsInS[q] ); + } + + m_flGPUToCPUOffsetInS = 0.0f; + for ( uint i = 0; i < R; i++ ) + m_flGPUToCPUOffsetInS += flClockOffsetsInS[i]; + m_flGPUToCPUOffsetInS /= R; + + if ( NT > 1 ) + { + DbgPrintf("------- Ratio: %2.20f\n", flGPURatio ); + + double flDelta = flClockOffsetsInS[0] - flClockOffsetsInS[R - 1]; + + DbgPrintf("------- %1.20f\n", flDelta ); + +#if 1 + if ( flDelta < 0.0000005f ) + { + flGPURatio += .000000125f; + } + else if ( flDelta > 0.0000005f ) + { + flGPURatio -= .000000125f; + } +#else + if ( flDelta < 0.0000005f ) + { + flGPURatio += .0000000125f; + } + else if ( flDelta > 0.0000005f ) + { + flGPURatio -= .0000000125f; + } +#endif + } + } + + m_flGPUToS *= flGPURatio; + +#if 0 + // dump drift over time to debugger output + double flLatency = 0; + for ( ; ; ) + { + // test + const uint64 nStartCPUTime = Plat_Rdtsc(); + + gGL->glQueryCounter( m_FreeQueryPool[0], GL_TIMESTAMP); + + PipelineFlush(); + + GLint nAvailable; + do + { + gGL->glGetQueryObjectiv( m_FreeQueryPool[0], GL_QUERY_RESULT_AVAILABLE, &nAvailable ); + } while ( !nAvailable ); + + GLuint64 nGPUTime; + gGL->glGetQueryObjectui64v( m_FreeQueryPool[0], GL_QUERY_RESULT, &nGPUTime ); + + double flStartGPUTime = ( ( nGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ); + + flLatency = flStartGPUTime - nStartCPUTime * m_flRdtscToS; + DbgPrintf("%f\n", flLatency ); + } +#endif + } + + void Deinit() + { + if ( !m_bInitialized ) + return; + + if ( m_nFreeQueryPoolSize ) + { + gGL->glDeleteQueries( m_nFreeQueryPoolSize, m_FreeQueryPool ); + } + m_nFreeQueryPoolSize = 0; + + for ( uint i = 0; i < m_nNumOutstandingQueryZones; i++ ) + { + QueryZone_t &query = m_OutstandingQueryZones[ ( m_nOutstandingQueriesHead + i ) % cMaxQueryZones ]; + if ( query.m_nBeginQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nBeginQuery ); + } + if ( query.m_nEndQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nEndQuery ); + } + } + m_nOutstandingQueriesHead = 0; + m_nOutstandingQueriesTail = 0; + m_nNumOutstandingQueryZones = 0; + + for ( uint i = 0; i < m_nQueryZoneStackSize; i++ ) + { + QueryZone_t &query = m_QueryZoneStack[i]; + if ( query.m_nBeginQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nBeginQuery ); + } + if ( query.m_nEndQuery ) + { + gGL->glDeleteQueries( 1, &query.m_nEndQuery ); + } + } + m_nQueryZoneStackSize = 0; + + m_flGPUToCPUOffsetInS = 0; + m_flGPUToS = 0; + m_flRdtscToS = 0; + m_flSToRdtsc = 0; + + m_bInitialized = false; + } + + // pName is assumed to be a telemetry dynamic string! + void BeginZone( const char *pName ) + { + if ( !m_bInitialized ) + return; + + if ( m_nQueryZoneStackSize >= cMaxQueryZoneStackSize ) + { + Panic( "Increase cMaxQueryZoneStackSize!" ); + } + + QueryZone_t &zone = m_QueryZoneStack[m_nQueryZoneStackSize]; + + zone.m_pName = pName; + + zone.m_nBeginQuery = AllocQueryHandle(); + zone.m_nEndQuery = 0; + zone.m_nStackLevel = m_nQueryZoneStackSize; + + zone.m_nTotalGPUWorkCount = g_nTotalDrawsOrClears; +#if GL_TELEMETRY_GPU_ZONES + zone.m_nTotalGPUWorkCount += g_TelemetryGPUStats.GetTotal(); +#endif + + gGL->glQueryCounter( m_QueryZoneStack[m_nQueryZoneStackSize].m_nBeginQuery, GL_TIMESTAMP ); + + m_nQueryZoneStackSize++; + } + + void EndZone() + { + if ( !m_bInitialized ) + return; + + if ( ( !m_nQueryZoneStackSize ) || ( m_nNumOutstandingQueryZones == cMaxQueryZones ) ) + { + Panic( "Query zone error!" ); + } + + m_nQueryZoneStackSize--; + + uint nCurGPUWorkCount = g_nTotalDrawsOrClears; +#if GL_TELEMETRY_GPU_ZONES + nCurGPUWorkCount += g_TelemetryGPUStats.GetTotal(); +#endif + + uint nTotalDraws = nCurGPUWorkCount - m_QueryZoneStack[m_nQueryZoneStackSize].m_nTotalGPUWorkCount; + + m_QueryZoneStack[m_nQueryZoneStackSize].m_nEndQuery = AllocQueryHandle(); + gGL->glQueryCounter( m_QueryZoneStack[m_nQueryZoneStackSize].m_nEndQuery, GL_TIMESTAMP ); + m_QueryZoneStack[m_nQueryZoneStackSize].m_nTotalGPUWorkCount = nTotalDraws; + + m_OutstandingQueryZones[m_nOutstandingQueriesHead] = m_QueryZoneStack[m_nQueryZoneStackSize]; + m_nOutstandingQueriesHead = ( m_nOutstandingQueriesHead + 1 ) % cMaxQueryZones; + m_nNumOutstandingQueryZones++; + + COMPILE_TIME_ASSERT( ( int )cMaxQueryZones > ( int )cMaxQueryZoneStackSize ); + if ( m_nNumOutstandingQueryZones >= ( cMaxQueryZones - cMaxQueryZoneStackSize ) ) + { + tmMessage( TELEMETRY_LEVEL2, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "CGPUTimestampManager::EndZone: Too many outstanding query zones - forcing a pipeline flush! This is probably expensive." ); + + FlushOutstandingQueries( true ); + } + + if ( gl_telemetry_gpu_pipeline_flushing.GetBool() ) + { + PipelineFlush(); + } + } + + void Tick() + { + m_nCurFrame++; + + if ( !m_bInitialized ) + return; + + if ( m_nQueryZoneStackSize > 0 ) + { + Panic( "Zone stack is not empty!" ); + } + + FlushOutstandingQueries( false ); + + tmMessage( TELEMETRY_LEVEL2, 0, "Total PIX timespan GPU work count: %u", m_nTotalSpanWorkCount ); + + m_nTotalSpanWorkCount = 0; + } + + void FlushOutstandingQueries( bool bForce ) + { + tmZone( TELEMETRY_LEVEL2, 0, "FlushOutstandingQueries: %u", m_nNumOutstandingQueryZones ); + + if ( bForce ) + { + PipelineFlush(); + } + + while ( m_nNumOutstandingQueryZones ) + { + QueryZone_t &zone = m_OutstandingQueryZones[m_nOutstandingQueriesTail]; + + GLint nEndAvailable = 0; + do + { + gGL->glGetQueryObjectiv( zone.m_nEndQuery, GL_QUERY_RESULT_AVAILABLE, &nEndAvailable ); + + } while ( ( bForce ) && ( nEndAvailable == 0 ) ); + + if ( !nEndAvailable ) + { + if ( bForce ) + { + Panic( "Query results not available after a full pipeline flush!" ); + } + break; + } + + GLuint64 nBeginGPUTime, nEndGPUTime; + gGL->glGetQueryObjectui64v( zone.m_nBeginQuery, GL_QUERY_RESULT, &nBeginGPUTime ); + gGL->glGetQueryObjectui64v( zone.m_nEndQuery, GL_QUERY_RESULT, &nEndGPUTime ); + + ReleaseQueryHandle( zone.m_nBeginQuery ); + zone.m_nBeginQuery = 0; + + ReleaseQueryHandle( zone.m_nEndQuery ); + zone.m_nEndQuery = 0; + + if ( m_nNumFinishedZones >= cMaxQueryZones ) + { + Panic( "Too many finished zones!" ); + } + + FinishedQueryZone_t &finishedZone = m_FinishedZones[m_nNumFinishedZones]; + finishedZone.m_pName = zone.m_pName; + finishedZone.m_nBeginGPUTime = nBeginGPUTime; + finishedZone.m_nEndGPUTime = nEndGPUTime; + finishedZone.m_nStackLevel = zone.m_nStackLevel; + finishedZone.m_nTotalGPUWorkCount = zone.m_nTotalGPUWorkCount; + m_nNumFinishedZones++; + + if ( !zone.m_nStackLevel ) + { + std::sort( m_FinishedZones, m_FinishedZones + m_nNumFinishedZones ); + FlushFinishedZones(); + m_nNumFinishedZones = 0; + } + + m_nOutstandingQueriesTail = ( m_nOutstandingQueriesTail + 1 ) % cMaxQueryZones; + m_nNumOutstandingQueryZones--; + } + } + +private: + bool m_bInitialized; + uint m_nCurFrame; + + double m_flGPUToCPUOffsetInS; + double m_flGPUToS; + double m_flRdtscToS; + double m_flSToRdtsc; + + enum { cMaxQueryZones = 4096, cFreeQueryPoolSize = cMaxQueryZones * 2 }; + GLuint m_FreeQueryPool[cFreeQueryPoolSize ]; + uint m_nFreeQueryPoolSize; + + GLuint AllocQueryHandle() + { + if ( !m_nFreeQueryPoolSize ) + { + Panic( "Out of query handles!"); + } + return m_FreeQueryPool[--m_nFreeQueryPoolSize]; + } + + void ReleaseQueryHandle( GLuint nHandle ) + { + if ( m_nFreeQueryPoolSize >= cFreeQueryPoolSize ) + { + Panic( "Query handle error!" ); + } + m_FreeQueryPool[m_nFreeQueryPoolSize++] = nHandle; + } + + struct QueryZone_t + { + const char *m_pName; + GLuint m_nBeginQuery; + GLuint m_nEndQuery; + uint m_nStackLevel; + uint m_nTotalGPUWorkCount; + }; + + QueryZone_t m_OutstandingQueryZones[cMaxQueryZones]; + uint m_nOutstandingQueriesHead; // index of first outstanding query (oldest) + uint m_nOutstandingQueriesTail; // index where next query goes (newest) + uint m_nNumOutstandingQueryZones; + + enum { cMaxQueryZoneStackSize = 256 }; + QueryZone_t m_QueryZoneStack[cMaxQueryZoneStackSize]; + uint m_nQueryZoneStackSize; + + struct FinishedQueryZone_t + { + const char *m_pName; + GLuint64 m_nBeginGPUTime; + GLuint64 m_nEndGPUTime; + uint m_nStackLevel; + uint m_nTotalGPUWorkCount; + + inline bool operator< ( const FinishedQueryZone_t &rhs ) const + { + if ( m_nBeginGPUTime == rhs.m_nBeginGPUTime) + return m_nStackLevel < rhs.m_nStackLevel; + + return m_nBeginGPUTime < rhs.m_nBeginGPUTime; + } + }; + + FinishedQueryZone_t m_FinishedZones[cMaxQueryZones]; + uint m_nNumFinishedZones; + + uint m_nTotalSpanWorkCount; + + void InitRdtsc() + { + m_flRdtscToS = 0.0f; + m_flSToRdtsc = 0.0f; + + for ( uint i = 0; i < 10; i++ ) + { + uint64 t0 = Plat_Rdtsc(); + double d0 = Plat_FloatTime(); + + ThreadSleep( 250 ); + + uint64 t1 = Plat_Rdtsc(); + double d1 = Plat_FloatTime(); + + double flRdtscToS = ( d1 - d0 ) / ( t1 - t0 ); + double flSToRdtsc = ( t1 - t0 ) / ( d1 - d0 ); + if ( flSToRdtsc > m_flSToRdtsc ) + { + m_flRdtscToS = flRdtscToS; + m_flSToRdtsc = flSToRdtsc; + } + } + } + + void PipelineFlush() + { +#ifdef HAVE_GL_ARB_SYNC + GLsync nSyncObj = gGL->glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); + if ( nSyncObj ) + { + gGL->glClientWaitSync( nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 300000000000ULL ); + gGL->glDeleteSync( nSyncObj ); + } +#endif + } + + inline void NewTimeSpan( uint64 nStartGPUTime, uint64 nEndGPUTime, const char *pName, uint nTotalDraws ) + { + // apparently we must use level0 for timespans? + tmBeginTimeSpanAt( TELEMETRY_LEVEL0, 1, 0, nStartGPUTime, "%s [C:%u]", pName ? pName : "", nTotalDraws ); + tmEndTimeSpanAt( TELEMETRY_LEVEL0, 1, 0, nEndGPUTime, "%s [C:%u]", pName ? pName : "", nTotalDraws ); + } + + void FlushFinishedZones() + { + for ( uint i = 0; i < m_nNumFinishedZones; i++ ) + { + FinishedQueryZone_t &zone = m_FinishedZones[i]; + if ( !zone.m_nTotalGPUWorkCount ) + continue; + + bool bEmit = false; + if ( i == ( m_nNumFinishedZones - 1 ) ) + bEmit = true; + else + { + FinishedQueryZone_t &nextZone = m_FinishedZones[i + 1]; + bEmit = zone.m_nEndGPUTime <= nextZone.m_nBeginGPUTime; + } + + if ( bEmit ) + { + uint64 nStartGPUTime = ( ( zone.m_nBeginGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ) * m_flSToRdtsc; + uint64 nEndGPUTime = ( ( zone.m_nEndGPUTime * m_flGPUToS ) + m_flGPUToCPUOffsetInS ) * m_flSToRdtsc; + + NewTimeSpan( nStartGPUTime, nEndGPUTime, zone.m_pName, zone.m_nTotalGPUWorkCount ); + + m_nTotalSpanWorkCount += zone.m_nTotalGPUWorkCount; + } + } + } + + void Panic( const char *pMsg ) + { + DXABSTRACT_BREAK_ON_ERROR(); + Error( "%s", pMsg ); + } + + static void DbgPrintf( const char *pFmt, ... ) + { + va_list vargs; + va_start( vargs, pFmt ); + char buf[1024]; + V_vsnprintf( buf, sizeof( buf ), pFmt, vargs ); + +#ifdef WIN32 + OutputDebugStringA( buf ); +#else + printf( "%s", buf ); +#endif + + va_end( vargs ); + } +}; + + +static CGPUTimestampManager g_GPUTimestampManager; + +void GLMGPUTimestampManagerInit() +{ + g_GPUTimestampManager.Init(); +} + +void GLMGPUTimestampManagerDeinit() +{ + g_GPUTimestampManager.Deinit(); +} + +ConVar gl_telemetry_gpu( "gl_telemetry_gpu", "0" ); +static bool g_bPrevTelemetryGPU; + +void GLMGPUTimestampManagerTick() +{ + if ( g_bPrevTelemetryGPU != gl_telemetry_gpu.GetBool() ) + { + if ( !gl_telemetry_gpu.GetBool() ) + g_GPUTimestampManager.Deinit(); + else + { +#if !PIX_ENABLE || !GL_TELEMETRY_GPU_ZONES + ConMsg( "Must define PIX_ENABLE and GL_TELEMETRY_GPU_ZONES to use this feature" ); +#else + g_GPUTimestampManager.Init(); +#endif + } + + g_bPrevTelemetryGPU = gl_telemetry_gpu.GetBool(); + } + + g_GPUTimestampManager.Tick(); +} + +#endif // !OSX + +static uint g_nPIXEventIndex; + +void GLMBeginPIXEvent( const char *str ) +{ +#ifndef OSX + char szName[1024]; + V_snprintf( szName, sizeof( szName ), "[ID:%u FR:%u] %s", g_nPIXEventIndex, g_GPUTimestampManager.GetCurFrame(), str ); + const char *p = tmDynamicString( TELEMETRY_LEVEL2, szName ); //p can be null if tm is getting shut down + tmEnter( TELEMETRY_LEVEL2, TMZF_NONE, "PIX %s", p ? p : "" ); + + g_nPIXEventIndex++; + + g_GPUTimestampManager.BeginZone( p ); +#endif // !OSX + V_strncpy( sg_pPIXName, str, 128 ); + +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOComment, (GLint)sg_pPIXName ); +#endif + + if ( gGL->m_bHave_GL_GREMEDY_string_marker ) + { + gGL->glStringMarkerGREMEDY( 0, sg_pPIXName ); + } +} + +void GLMEndPIXEvent( void ) +{ +#ifndef OSX + g_GPUTimestampManager.EndZone(); +#endif + +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOComment, (GLint)sg_pPIXName ); +#endif + + if ( gGL->m_bHave_GL_GREMEDY_string_marker ) + { + gGL->glStringMarkerGREMEDY( 0, sg_pPIXName ); + } + + sg_pPIXName[0] = '\0'; + + tmLeave( TELEMETRY_LEVEL2 ); +} + +//=============================================================================== + +// Knob - hash table mapping string names to float values + + +struct GLMKnobKey +{ + char *m_knobName; +}; + +struct GLMKnobValue +{ + float m_value; +}; + +bool LessFunc_GLMKnobKey( const GLMKnobKey &a, const GLMKnobKey &b ) +{ + // Umm, what should this really be? + //return strcmp( a.m_knobName, b.m_knobName ); + return strcmp( a.m_knobName, b.m_knobName ) < 0; +} + +CUtlMap< GLMKnobKey, GLMKnobValue* > *g_knobMap = NULL; + +// add some special knob-names that just read mod keys +extern "C" uint GetCurrentKeyModifiers( void ); + +enum ECarbonModKeyIndex +{ + EcmdKeyBit = 8, /* command key down?*/ + EshiftKeyBit = 9, /* shift key down?*/ + EalphaLockBit = 10, /* alpha lock down?*/ + EoptionKeyBit = 11, /* option key down?*/ + EcontrolKeyBit = 12 /* control key down?*/ +}; + +enum ECarbonModKeyMask +{ + EcmdKey = 1 << EcmdKeyBit, + EshiftKey = 1 << EshiftKeyBit, + EalphaLock = 1 << EalphaLockBit, + EoptionKey = 1 << EoptionKeyBit, + EcontrolKey = 1 << EcontrolKeyBit +}; + +float GLMKnob( char *knobname, float *setvalue ) +{ +#if GLMDEBUG + float result = 0.0f; + + if (!g_knobMap) + { + g_knobMap = new CUtlMap< GLMKnobKey, GLMKnobValue* >; + g_knobMap->SetLessFunc( LessFunc_GLMKnobKey ); + } + +#ifdef OSX + uint mods = GetCurrentKeyModifiers(); +#else + uint mods = 0; +#endif + // is it a special key name ? + if (!strcmp(knobname,"caps-key")) + { + return (mods & (EalphaLock)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"control-key")) + { + return (mods & (EcontrolKey)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"shift-key")) + { + return (mods & (EshiftKey)) ? 1.0 : 0.0; + } + else if (!strcmp(knobname,"option-key")) + { + return (mods & (EoptionKey)) ? 1.0 : 0.0; + } + else + { + // does the key exist in the map ? + GLMKnobKey key; + key.m_knobName = knobname; + + GLMKnobValue *knob = NULL; + + unsigned short index = g_knobMap->Find( key ); + if (index != g_knobMap->InvalidIndex()) + { + // found it + knob = (*g_knobMap)[ index ]; + + if (setvalue) + { + knob->m_value = *setvalue; + } + + result = knob->m_value; + } + else + { + // need to make a new one + knob = new GLMKnobValue; + + knob->m_value = (setvalue) ? *setvalue : 0.0f; + + g_knobMap->Insert( key, knob ); + } + + return knob->m_value; + } + +#else + // GLM knobs just return 0.0 all the time when no GLMDEBUG + return 0.0f; +#endif +} + +// for boolean knobs.. +float GLMKnobToggle( char *knobname ) +{ + // if not 0.0, make it 0.0 + // if 0.0, make it 1.0 + + float newValue = 0.0; // assume falling edge + float curValue = GLMKnob( knobname, NULL ); + if (curValue == 0.0) + { + newValue = 1.0; + } + + return GLMKnob( knobname, &newValue ); +} + + +//=============================================================================== + +// helpers for CGLSetOption - no op if no profiler +void GLMProfilerClearTrace( void ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOResetFunctionTrace, 0 ); +#else + Assert( !"impl me" ); +#endif +} + +void GLMProfilerEnableTrace( bool enable ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLSetOption( kCGLGOEnableFunctionTrace, enable ? GL_TRUE : GL_FALSE ); +#else + Assert( !"impl me" ); +#endif +} + +// helpers for CGLSetParameter - no op if no profiler +void GLMProfilerDumpState( void ) +{ +#if defined( OSX ) && defined( CGLPROFILER_ENABLE ) + CGLContextObj curr = CGLGetCurrentContext(); + CGLSetParameter( curr, kCGLCPDumpState, (const GLint*)1 ); +#else + Assert( !"impl me" ); +#endif +} + + +//=============================================================================== + +CGLMFileMirror::CGLMFileMirror( char *fullpath ) +{ + m_path = strdup( fullpath ); + m_data = (char *)malloc(1); + m_size = 0; + UpdateStatInfo(); + if (m_exists) + { + ReadFile(); + } +} + +CGLMFileMirror::~CGLMFileMirror( ) +{ + if (m_path) + { + free (m_path); + m_path = NULL; + } + + if (m_data) + { + free (m_data); + m_data = NULL; + } +} + +bool CGLMFileMirror::HasData( void ) +{ + return (m_size != 0); +} + + +// return direct pointer to buffer. Will be invalidated if file is re-loaded or if data is written to +void CGLMFileMirror::GetData( char **dataPtr, uint *dataSizePtr ) +{ + *dataPtr = m_data; + *dataSizePtr = m_size; +} + + +void CGLMFileMirror::SetData( char *data, uint dataSize ) +{ + if (m_data) + { + free( m_data ); + m_data = NULL; + } + + m_size = dataSize; + + m_data = (char *)malloc( m_size +1 ); + m_data[ m_size ] = 0; // extra NULL terminator, no charge + + memcpy( m_data, data, m_size ); // copy data in + + WriteFile(); // keep disk copy sync'd +} + +static bool stat_diff( struct stat *a, struct stat *b ) +{ + if (a->st_size != b->st_size) + { + return true; + } + +#ifdef OSX + if (memcmp( &a->st_mtimespec, &b->st_mtimespec, sizeof( struct timespec ) ) ) +#else + if (memcmp( &a->st_mtime, &b->st_mtime, sizeof( time_t ) ) ) +#endif + { + return true; + } + + return false; +} + +bool CGLMFileMirror::PollForChanges( void ) +{ + // snapshot old stat + //bool old_exists = m_exists; + struct stat old_stat = m_stat; + + UpdateStatInfo(); + + if (m_exists) + { + if ( stat_diff( &old_stat, &m_stat ) ) + { + // initial difference detected. continue to poll at 0.1s intervals until it stops changing, then read it. + int stablecount = 0; + do + { + ThreadSleep(100000); + + struct stat last_stat = m_stat; + UpdateStatInfo(); + + if (stat_diff( &last_stat, &m_stat )) + { + stablecount = 0; + } + else + { + stablecount++; + } + } while(stablecount<3); + + // changes have settled down, now re-read it + ReadFile(); + return true; + } + else + { + return false; // no change + } + } + else + { + // file does not exist. remake it. not considered to be a change. + WriteFile(); + return false; + } +} + + + +void CGLMFileMirror::UpdateStatInfo( void ) +{ + // stat the path + struct stat newstat; + memset (&newstat, 0, sizeof(newstat) ); + int result = stat( m_path, &newstat ); + + if (!result) + { + m_exists = true; + m_stat = newstat; + } + else + { + m_exists = false; + memset( &m_stat, 0, sizeof( m_stat ) ); + } +} + + +void CGLMFileMirror::ReadFile( void ) +{ + // unconditional - we discard any old buffer, make a new one, + UpdateStatInfo(); + + if (m_data) + { + free( m_data ); + m_data = NULL; + } + + if (m_exists) + { + FILE *infile = fopen( m_path, "rb" ); + if (infile) + { + // get size from stat + m_size = m_stat.st_size; + + m_data = (char *)malloc( m_size +1 ); + m_data[ m_size ] = 0; // extra NULL terminator, no charge + + fread( m_data, 1, m_size, infile ); + + fclose( infile ); + } + else + { + GLMDebugger(); // ouch + } + + } + else + { + // hmmmmmm + m_data = (char *)malloc(1); + m_data[0] = 0; + m_size = 0; + } +} + + +void CGLMFileMirror::WriteFile( void ) +{ + FILE *outfile = fopen( m_path, "wb" ); + + if (outfile) + { + fwrite( m_data, 1, m_size, outfile ); + fclose( outfile ); + + UpdateStatInfo(); // sets m_stat and m_exists + } + else + { + GLMDebugger(); // ouch + } +} + +void CGLMFileMirror::OpenInEditor( bool foreground ) +{ + char temp[64000]; + + // pass -b if no desire to bring editor to foreground + sprintf(temp,"/usr/bin/bbedit %s %s", foreground ? "" : "-b", m_path ); + system( temp ); +} + + + +CGLMEditableTextItem::CGLMEditableTextItem( char *text, uint size, bool forceOverwrite, char *prefix, char *suffix ) +{ + // clone input text (exact size copy) + m_origSize = size; + m_origText = (char *)malloc( m_origSize ); + memcpy( m_origText, text, m_origSize ); + + // null out munged form til we generate it + m_mungedSize = 0; + m_mungedText = NULL; + + // null out mirror until we create it + m_mirrorBaseName = NULL; + m_mirrorFullPath = NULL; + m_mirror = NULL; + + GenHashOfOrigText(); // will fill out m_origDigest + GenMungedText( false ); + GenBaseNameAndFullPath( prefix, suffix ); // figure out where the mirror will go + + if (!strcmp(m_mirrorBaseName, "96c7e9d2faf76b1148f7274afd684d4b.fsh")) + { + printf("\nhello there\n"); + } + + // make the mirror from the filename. + // see if there was any content on disk + // if so, honor that content *unless* the force-option is set. + m_mirror = new CGLMFileMirror( m_mirrorFullPath ); + + // the logic is simple. + // the only time we will choose the copy on disk, is if + // a - forceOverwrite is false + // AND b - the copy on disk is bigger than 10 bytes. + + bool replaceDiskCopy = true; + + char *mirrorData = NULL; + uint mirrorSize = 0; + + if (!forceOverwrite) + { + if (m_mirror->HasData()) + { + // peek at it, and use it if it is more than some minimum number of bytes. + m_mirror->GetData( &mirrorData, &mirrorSize ); + if (mirrorSize > 10) + { + replaceDiskCopy = false; + } + } + } + + if (replaceDiskCopy) + { + // push our generated data to the mirror - disk copy is overwritten + m_mirror->SetData( m_mungedText, m_mungedSize ); + } + else + { + GenMungedText( true ); + } + +} + +CGLMEditableTextItem::~CGLMEditableTextItem( ) +{ + if (m_origText) + { + free (m_origText); + } + + if (m_mungedText) + { + free (m_mungedText); + } + + if (m_mirrorBaseName) + { + free (m_mirrorBaseName); + } + + if (m_mirrorFullPath) + { + free (m_mirrorFullPath); + } + + if (m_mirror) + { + free( m_mirror ); + } +} + +bool CGLMEditableTextItem::HasData( void ) +{ + return m_mirror->HasData(); +} + +bool CGLMEditableTextItem::PollForChanges( void ) +{ + bool changed = m_mirror->PollForChanges(); + if (changed) + { + // re-gen munged text from mirror (means "copy") + GenMungedText( true ); + } + return changed; +} + +void CGLMEditableTextItem::GetCurrentText( char **textOut, uint *sizeOut ) +{ + if (!m_mungedText) GLMDebugger(); + + *textOut = m_mungedText; + *sizeOut = m_mungedSize; +} + +void CGLMEditableTextItem::OpenInEditor( bool foreground ) +{ + m_mirror->OpenInEditor( foreground ); +} + + +void CGLMEditableTextItem::GenHashOfOrigText( void ) +{ + MD5Context_t md5ctx; + MD5Init( &md5ctx ); + MD5Update( &md5ctx, (unsigned char*)m_origText, m_origSize ); + MD5Final( m_origDigest, &md5ctx ); +} + + +void CGLMEditableTextItem::GenBaseNameAndFullPath( char *prefix, char *suffix ) +{ + // base name is hash digest in hex, plus the suffix. + char temp[5000]; + + Q_binarytohex( m_origDigest, sizeof(m_origDigest), temp, sizeof( temp ) ); + if (suffix) + { + strcat( temp, suffix ); + } + if (m_mirrorBaseName) free(m_mirrorBaseName); + m_mirrorBaseName = strdup( temp ); + + sprintf( temp, "%s%s", prefix, m_mirrorBaseName ); + if (m_mirrorFullPath) free(m_mirrorFullPath); + m_mirrorFullPath = strdup( temp ); +} + + +void CGLMEditableTextItem::GenMungedText( bool fromMirror ) +{ + if (fromMirror) + { + // just import the text as is from the mirror file. + + char *mirrorData = NULL; + uint mirrorSize = 0; + + if (m_mirror->HasData()) + { + // peek at it, and use it if it is more than some minimum number of bytes. + m_mirror->GetData( &mirrorData, &mirrorSize ); + + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedText = (char *)malloc( mirrorSize+1 ); + m_mungedText[ mirrorSize ] = 0; + memcpy( m_mungedText, mirrorData, mirrorSize ); + + m_mungedSize = mirrorSize; + } + else + { + GLMDebugger(); + } + } + else + { + #if 1 + // we don't actually clone/munge any more. + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedText = (char *)malloc( m_origSize+1 ); + m_mungedText[ m_origSize ] = 0; + memcpy( m_mungedText, m_origText, m_origSize ); + + m_mungedSize = m_origSize; + + #else + // take pure 'orig' text that came in from the engine, and clone it + // do not clone the first line + char temp[100000]; + char *dst = temp; + char *lim = &temp[ sizeof(temp) ]; + + // zero temp + memset( temp, 0, sizeof(temp) ); + + // write orig text to temp + if (m_origSize >= (sizeof(temp)/2) ) + { + GLMDebugger(); + } + + memcpy( dst, m_origText, m_origSize ); + dst += m_origSize; + + // add a newline if the last character wasn't + if ( (*(dst-1)) != '\n' ) + { + *dst++ = '\n'; + } + + // walk orig text again and copy it over, with these caveats + // don't copy the first line + // insert a # before all the other lines. + char *src = temp; + + // walk to end of first line + char *firstNewline = strchr( src, '\n' ); + if (!firstNewline) + { + GLMDebugger(); + } + else + { + // advance 'src' to that newline- we're not copying the !! line + src = firstNewline; + } + + + // now walk the rest - insert a # after each newline + while( (dst < lim) && ((src-temp) < m_origSize) ) + { + switch( *src ) + { + case '\n': + *dst++ = *src++; + *dst++ = '#'; + break; + + default: + *dst++ = *src++; + } + } + if (dst >= lim) + { + GLMDebugger(); + } + + // final newline + *dst++ = '\n'; + + // copyout + if (m_mungedText) + { + free( m_mungedText ); + m_mungedText = NULL; + } + + m_mungedSize = dst - temp; + m_mungedText = (char *)malloc( m_mungedSize ); + memcpy( m_mungedText, temp, m_mungedSize ); + #endif + } +} + +//=============================================================================== + +// class for cracking multi-part text blobs +// sections are demarcated by beginning-of-line markers submitted in a table by the caller +// typically +// asm flavors have first-line rules so we use those tags as is +// !!ARBvp (etc) +// !!ARBfp (etc) +// //!!GLSLF // slashes required +// //!GLSLV + +// maybe also introduce "present but disabled" markers like these +// -!!ARBvp (etc) +// -!!ARBfp (etc) +// -//!!GLSLF +// -//!GLSLV + +// resolved. there is no default section for text that doesn't have a marker in front of it. mark it or miss it. + +CGLMTextSectioner::CGLMTextSectioner( char *text, int textLength, const char **markers ) +{ + // find lines + // for each line, see if it starts with a marker + // if so, open a new section based at that line + + GLMTextSection *curSection = NULL; // no current section until we see a marker + + char *cursor = text; + char *textLimit = text+textLength; + + int foundMarker; + const char **markerCursor; + while( cursor < textLimit ) + { + // top of loop. cursor points to start of a line. + // find the end of the line and keep that handy. + char *eol = strchr( cursor, '\n' ); + int charsInLine = (eol) ? (eol-cursor)+1 : strlen(cursor); + + //see if any of the marker strings is located here. + foundMarker = -1; + markerCursor = markers; + + while( (foundMarker<0) && (*markerCursor!=NULL) ) + { + // see if the n'th marker is a hit + int markerLen = strlen(*markerCursor); + + if (!strncmp( cursor, *markerCursor, markerLen ) ) + { + // hit + foundMarker = markerCursor - markers; + } + markerCursor++; + } + + // outcome is either "marker spotted" or "no". + // if marker seen, open new section using that marker. + // else, grow active section if underway. + // then, move cursor to next line. + + if (foundMarker >= 0) + { + // found marker. start new section. + // no need to do anything special with prior section - it was up to date before seeing this marker. + int index = m_sectionTable.AddToTail(); + curSection = &m_sectionTable[ index ]; + + curSection->m_markerIndex = foundMarker; + curSection->m_textOffset = cursor - text; // text includes the marker + curSection->m_textLength = charsInLine; // this line goes in the tally, later lines add to it + } + else + { + // add this line to current section if live + if (curSection) + { + curSection->m_textLength += charsInLine; + } + } + cursor += charsInLine; + } +} + +CGLMTextSectioner::~CGLMTextSectioner( ) +{ + // not much to do. +} + + +int CGLMTextSectioner::Count( void ) +{ + return m_sectionTable.Count(); +} + +void CGLMTextSectioner::GetSection( int index, uint *offsetOut, uint *lengthOut, int *markerIndexOut ) +{ + Assert( index < m_sectionTable.Count() ); + + GLMTextSection *section = &m_sectionTable[ index ]; + + *offsetOut = section->m_textOffset; + *lengthOut = section->m_textLength; + *markerIndexOut = section->m_markerIndex; +} + +//=============================================================================== + +// how to make a compiled-in font: +// a. type in a matrix of characters in your fav editor +// b. take a screen shot of the characters (128x128 pixels in this case) +// c. export as TIFF raw. +// d. hex dump it +// e. column-copy just the hex data +// f. find and replace: chop out all the spaces and line feeds, change FFFFFF and 000000 to your marker chars of choice. +// g. wrap each line with quotes and a comma. + +unsigned char g_glmDebugFontMap[ 128 * 128 ] = +{ +" * " +" * * * * * " +" * * * * * *** * * ** * * * * * " +" * * * ***** * * * * * * * * * * * * * * * * " +" * * * * * * * * * * * * * *** * * " +" * ***** *** * * * * * * * ***** ***** * " +" * * * * * * * * * * * * * * * " +" * * * * * * * * * * * ** ** * " +" * *** * * ** * * * ** ** * " +" * * * * * " +" * " +" " +" " +" *** * *** *** * ***** *** ***** *** *** * * *** " +"* * ** * * * * ** * * * * * * * ** ** * * * * " +"* ** * * * * * **** **** * * * * * ** ** * ***** * * " +"* * * * * ** * * * * * * *** * * * * * " +"** * * * * ***** * * * * * * **** * ***** * * " +"* * * * * * * * * * * * * * * ** ** * * " +" *** * ***** *** * *** *** * *** *** ** ** * * * " +" * " +" * " +" " +" *** " +"* * *** **** *** **** ***** ***** *** * * * * * * * * * * * *** " +"* * * * * * * * * * * * * * * * * * * * * ** ** ** * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * * * * " +"*** * ***** **** * * * **** **** * ** ***** * * ** * * * * ** * * " +"* ** * * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * **** *** **** ***** * *** * * * *** * * ***** * * * * *** " +" *** " +" " +" * " +" ** * ** * " +"**** *** **** *** ***** * * * * * * * * * * ***** * * * * * " +"* * * * * * * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * * " +"**** * * **** *** * * * * * * * * * * * * * * " +"* * * * * * * * * * * * * * * * * * * * " +"* * * * * * * * * * * ** ** * * * * * * * " +"* *** * * *** * *** * * * * * * ***** * * * ****** " +" * ** * ** " +" " +" " +" * " +" * * * ** * * * * * " +" * * * * * * * " +" **** **** *** **** *** *** **** **** * * * * * **** * ** *** " +" * * * * * * * * * * * * * * * * * * * * * * * ** * * * " +" * * * * * * * ***** * * * * * * * *** * * * * * * * * " +" * ** * * * * * * * * * * * * * * * * * * * * * * * " +" ** * **** **** **** **** * **** * * * * * * ** * * * * * *** " +" * * " +" *** ** " +" * " +" ** * ** " +" * * * * ** * " +" * * * * * ** *** " +"**** **** * ** **** **** * * * * * * * * * * * ***** * * * ***** " +"* * * * ** * * * * * * * * * * * * * * * ** * ** ***** " +"* * * * * *** * * * * * * * * * * * * * * * ***** " +"* * * * * * * * ** * * * * * * * * * * * * * *** " +"**** **** * **** ** ** * * * * * * **** ***** * * * " +"* * * ** * ** " +"* * *** * " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +}; + + + + + + + + diff --git a/togl/linuxwin/glmgrcocoa.mm b/togl/linuxwin/glmgrcocoa.mm new file mode 100644 index 0000000..717c913 --- /dev/null +++ b/togl/linuxwin/glmgrcocoa.mm @@ -0,0 +1,34 @@ +//========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============//
+//
+// Purpose: provide some call-out glue to ObjC from the C++ GLMgr code
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include <Cocoa/Cocoa.h>
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+
+#undef MIN
+#undef MAX
+#define DONT_DEFINE_BOOL // Don't define BOOL!
+#include "tier0/threadtools.h"
+#include "tier1/interface.h"
+#include "tier1/strtools.h"
+#include "tier1/utllinkedlist.h"
+#include "togl/rendermechanism.h"
+
+
+
+// ------------------------------------------------------------------------------------ //
+// some glue to let GLMgr call into NS/ObjC classes.
+// ------------------------------------------------------------------------------------ //
+
+CGLContextObj GetCGLContextFromNSGL( PseudoNSGLContextPtr nsglCtx )
+{
+ return (CGLContextObj)[ (NSOpenGLContext*)nsglCtx CGLContextObj];
+}
+
diff --git a/togl/linuxwin/glmtexinlines.h b/togl/linuxwin/glmtexinlines.h new file mode 100644 index 0000000..cbdcc54 --- /dev/null +++ b/togl/linuxwin/glmtexinlines.h @@ -0,0 +1,29 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +#ifndef CGLMTEXINLINES_H +#define CGLMTEXINLINES_H + +#pragma once + +#endif // CGLMTEXINLINES_H diff --git a/togl/linuxwin/intelglmallocworkaround.cpp b/togl/linuxwin/intelglmallocworkaround.cpp new file mode 100644 index 0000000..efe0e0a --- /dev/null +++ b/togl/linuxwin/intelglmallocworkaround.cpp @@ -0,0 +1,71 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +#include "intelglmallocworkaround.h" +#include "mach_override.h" + +// memdbgon -must- be the last include file in a .cpp file. +#include "tier0/memdbgon.h" + +IntelGLMallocWorkaround* IntelGLMallocWorkaround::s_pWorkaround = NULL; + +void *IntelGLMallocWorkaround::ZeroingAlloc(size_t size) +{ + // We call into this pointer that resumes the original malloc. + void *memory = s_pWorkaround->m_pfnMallocReentry(size); + if (size < 96) + { + // Since the Intel driver has an issue with a small allocation + // that's left uninitialized, we use memset to ensure it's zero-initialized. + memset(memory, 0, size); + } + + return memory; +} + +IntelGLMallocWorkaround* IntelGLMallocWorkaround::Get() +{ + if (!s_pWorkaround) + { + s_pWorkaround = new IntelGLMallocWorkaround(); + } + + return s_pWorkaround; +} + +bool IntelGLMallocWorkaround::Enable() +{ + if ( m_pfnMallocReentry != NULL ) + { + return true; + } + + mach_error_t error = mach_override_ptr( (void*)&malloc, (const void*)&ZeroingAlloc, (void**)&m_pfnMallocReentry ); + if ( error == err_cannot_override ) + { + m_pfnMallocReentry = NULL; + return false; + } + + return true; +}
\ No newline at end of file diff --git a/togl/linuxwin/intelglmallocworkaround.h b/togl/linuxwin/intelglmallocworkaround.h new file mode 100644 index 0000000..630972d --- /dev/null +++ b/togl/linuxwin/intelglmallocworkaround.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// TOGL CODE LICENSE +// +// Copyright 2011-2014 Valve Corporation +// All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// intelglmallocworkaround.h +// class responsible for setting up a malloc override that zeroes allocated +// memory of less than 96 bytes. this is to work around a bug +// in the Intel GLSL compiler on Mac OS X 10.8 due to uninitialized memory. +// +// 96 was chosen due to this quote from Apple: +// "I verified that the size of the structure is exactly 64 bytes on 10.8.3, 10.8.4 and will be on 10.8.5." +// +// certain GLSL shaders would (intermittently) cause a crash the first time they +// were drawn, and the bug has supposedly been fixed in 10.9, but is unlikely to +// ever make it to 10.8. +// +//=============================================================================== + +#ifndef INTELGLMALLOCWORKAROUND_H +#define INTELGLMALLOCWORKAROUND_H + +#include <stdlib.h> + +class IntelGLMallocWorkaround +{ +public: + static IntelGLMallocWorkaround *Get(); + bool Enable(); + +protected: + IntelGLMallocWorkaround() :m_pfnMallocReentry(NULL) {} + ~IntelGLMallocWorkaround() {} + + static IntelGLMallocWorkaround *s_pWorkaround; + static void* ZeroingAlloc(size_t); + + typedef void* (*pfnMalloc_t)(size_t); + pfnMalloc_t m_pfnMallocReentry; +}; + +#endif // INTELGLMALLOCWORKAROUND_H
\ No newline at end of file diff --git a/togl/linuxwin/mach_override.c b/togl/linuxwin/mach_override.c new file mode 100644 index 0000000..7630276 --- /dev/null +++ b/togl/linuxwin/mach_override.c @@ -0,0 +1,765 @@ +// mach_override.c semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#include "mach_override.h" + +#include <mach-o/dyld.h> +#include <mach/mach_host.h> +#include <mach/mach_init.h> +#include <mach/vm_map.h> +#include <sys/mman.h> +#include <libkern/OSAtomic.h> + +#include <CoreServices/CoreServices.h> + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#define kPageSize 4096 +#if defined(__ppc__) || defined(__POWERPC__) + +long kIslandTemplate[] = { + 0x9001FFFC, // stw r0,-4(SP) + 0x3C00DEAD, // lis r0,0xDEAD + 0x6000BEEF, // ori r0,r0,0xBEEF + 0x7C0903A6, // mtctr r0 + 0x8001FFFC, // lwz r0,-4(SP) + 0x60000000, // nop ; optionally replaced + 0x4E800420 // bctr +}; + +#define kAddressHi 3 +#define kAddressLo 5 +#define kInstructionHi 10 +#define kInstructionLo 11 + +#elif defined(__i386__) + +#define kOriginalInstructionsSize 16 +// On X86 we migh need to instert an add with a 32 bit immediate after the +// original instructions. +#define kMaxFixupSizeIncrease 5 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE +}; + +#define kInstructions 0 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 +#elif defined(__x86_64__) + +#define kOriginalInstructionsSize 32 +// On X86-64 we never need to instert a new instruction. +#define kMaxFixupSizeIncrease 0 + +#define kJumpAddress kOriginalInstructionsSize + 6 + +unsigned char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#endif + +/************************** +* +* Data Types +* +**************************/ +#pragma mark - +#pragma mark (Data Types) + +typedef struct { + char instructions[sizeof(kIslandTemplate)]; +} BranchIsland; + +/************************** +* +* Funky Protos +* +**************************/ +#pragma mark - +#pragma mark (Funky Protos) + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ); + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); + int e = err_none; + e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); + e |= msync((void *)page, kPageSize, MS_INVALIDATE ); + if (e) { + err = err_cannot_override; + } + return err; +} +#endif + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionAddress ); + assert( overrideFunctionAddress ); + + // this addresses overriding such functions as AudioOutputUnitStart() + // test with modified DefaultOutputUnit project +#if defined(__x86_64__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] + originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); + else break; + } +#elif defined(__i386__) + for(;;){ + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? + originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); + else break; + } +#endif + + long *originalFunctionPtr = (long*) originalFunctionAddress; + mach_error_t err = err_none; + +#if defined(__ppc__) || defined(__POWERPC__) + // Ensure first instruction isn't 'mfctr'. + #define kMFCTRMask 0xfc1fffff + #define kMFCTRInstruction 0x7c0903a6 + + long originalInstruction = *originalFunctionPtr; + if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) + err = err_cannot_override; +#elif defined(__i386__) || defined(__x86_64__) + int eatenCount = 0; + int originalInstructionCount = 0; + char originalInstructions[kOriginalInstructionsSize]; + uint8_t originalInstructionSizes[kOriginalInstructionsSize]; + uint64_t jumpRelativeInstruction = 0; // JMP + + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, + &jumpRelativeInstruction, &eatenCount, + originalInstructions, &originalInstructionCount, + originalInstructionSizes ); + if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { + //printf ("Too many instructions eaten\n"); + overridePossible = false; + } + if (!overridePossible) err = err_cannot_override; + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); +#endif + + // Make the original function implementation writable. + if( !err ) { + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_ALL | VM_PROT_COPY) ); + if( err ) + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_DEFAULT | VM_PROT_COPY) ); + } + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + // Allocate and target the escape island to the overriding function. + BranchIsland *escapeIsland = NULL; + if( !err ) + err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + +#if defined(__ppc__) || defined(__POWERPC__) + if( !err ) + err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); + + // Build the branch absolute instruction to the escape island. + long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. + if( !err ) { + long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; + branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; + } +#elif defined(__i386__) || defined(__x86_64__) + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + + if( !err ) + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); + + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); + // Build the jump relative instruction to the escape island +#endif + + +#if defined(__i386__) || defined(__x86_64__) + if (!err) { + uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); + addressOffset = OSSwapInt32(addressOffset); + + jumpRelativeInstruction |= 0xE900000000000000LL; + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); + } +#endif + + // Optionally allocate & return the reentry island. This may contain relocated + // jmp instructions and so has all the same addressing reachability requirements + // the escape island has to the original function, except the escape island is + // technically our original function. + BranchIsland *reentryIsland = NULL; + if( !err && originalFunctionReentryIsland ) { + err = allocateBranchIsland( &reentryIsland, escapeIsland); + if( !err ) + *originalFunctionReentryIsland = reentryIsland; + } + +#if defined(__ppc__) || defined(__POWERPC__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instruction into the reentry island. + // o Target the reentry island at the 2nd instruction of the + // original function. + // o Replace the original instruction with the branch absolute. + if( !err ) { + int escapeIslandEngaged = false; + do { + if( reentryIsland ) + err = setBranchIslandTarget( reentryIsland, + (void*) (originalFunctionPtr+1), originalInstruction ); + if( !err ) { + escapeIslandEngaged = CompareAndSwap( originalInstruction, + branchAbsoluteInstruction, + (UInt32*)originalFunctionPtr ); + if( !escapeIslandEngaged ) { + // Someone replaced the instruction out from under us, + // re-read the instruction, make sure it's still not + // 'mfctr' and try again. + originalInstruction = *originalFunctionPtr; + if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) + err = err_cannot_override; + } + } + } while( !err && !escapeIslandEngaged ); + } +#elif defined(__i386__) || defined(__x86_64__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instructions into the reentry island. + // o Target the reentry island at the first non-replaced + // instruction of the original function. + // o Replace the original first instructions with the jump relative. + // + // Note that on i386, we do not support someone else changing the code under our feet + if ( !err ) { + uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; + fixupInstructions(offset, originalInstructions, + originalInstructionCount, originalInstructionSizes ); + + if( reentryIsland ) + err = setBranchIslandTarget_i386( reentryIsland, + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); + // try making islands executable before planting the jmp +#if defined(__x86_64__) || defined(__i386__) + if( !err ) + err = makeIslandExecutable(escapeIsland); + if( !err && reentryIsland ) + err = makeIslandExecutable(reentryIsland); +#endif + if ( !err ) + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + + return err; +} + +/******************************************************************************* +* +* Implementation +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Implementation) + +static bool jump_in_range(intptr_t from, intptr_t to) { + intptr_t field_value = to - from - 5; + int32_t field_value_32 = field_value; + return field_value == field_value_32; +} + +/******************************************************************************* + Implementation: Allocates memory for a branch island. + + @param island <- The allocated island. + @result <- mach_error_t + + ***************************************************************************/ + +static mach_error_t +allocateBranchIslandAux( + BranchIsland **island, + void *originalFunctionAddress, + bool forward) +{ + assert( island ); + assert( sizeof( BranchIsland ) <= kPageSize ); + + vm_map_t task_self = mach_task_self(); + vm_address_t original_address = (vm_address_t) originalFunctionAddress; + vm_address_t address = original_address; + + for (;;) { + vm_size_t vmsize = 0; + memory_object_name_t object = 0; + kern_return_t kr = 0; + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; + // Find the region the address is in. +#if __WORDSIZE == 32 + vm_region_basic_info_data_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + kr = vm_region(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#else + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + kr = vm_region_64(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); +#endif + if (kr != KERN_SUCCESS) + return kr; + assert((address & (kPageSize - 1)) == 0); + + // Go to the first page before or after this region + vm_address_t new_address = forward ? address + vmsize : address - kPageSize; +#if __WORDSIZE == 64 + if(!jump_in_range(original_address, new_address)) + break; +#endif + address = new_address; + + // Try to allocate this page. + kr = vm_allocate(task_self, &address, kPageSize, 0); + if (kr == KERN_SUCCESS) { + *island = (BranchIsland*) address; + return err_none; + } + if (kr != KERN_NO_SPACE) + return kr; + } + + return KERN_NO_SPACE; +} + +static mach_error_t +allocateBranchIsland( + BranchIsland **island, + void *originalFunctionAddress) +{ + mach_error_t err = + allocateBranchIslandAux(island, originalFunctionAddress, true); + if (!err) + return err; + return allocateBranchIslandAux(island, originalFunctionAddress, false); +} + + +/******************************************************************************* + Implementation: Deallocates memory for a branch island. + + @param island -> The island to deallocate. + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +freeBranchIsland( + BranchIsland *island ) +{ + assert( island ); + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); + assert( sizeof( BranchIsland ) <= kPageSize ); + return vm_deallocate( mach_task_self(), (vm_address_t) island, + kPageSize ); +} + +/******************************************************************************* + Implementation: Sets the branch island's target, with an optional + instruction. + + @param island -> The branch island to insert target into. + @param branchTo -> The address of the target. + @param instruction -> Optional instruction to execute prior to branch. Set + to zero for nop. + @result <- mach_error_t + + ***************************************************************************/ +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Fill in the address. + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; + ((short*)island->instructions)[kAddressHi] + = (((long) branchTo) >> 16) & 0x0000FFFF; + + // Fill in the (optional) instuction. + if( instruction != 0 ) { + ((short*)island->instructions)[kInstructionLo] + = instruction & 0x0000FFFF; + ((short*)island->instructions)[kInstructionHi] + = (instruction >> 16) & 0x0000FFFF; + } + + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + +#if defined(__i386__) + mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // copy original instructions + if (instructions) { + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); + } + + // Fill in the address. + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; + + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + return err_none; +} + +#elif defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Copy original instructions. + if (instructions) { + bcopy (instructions, island->instructions, kOriginalInstructionsSize); + } + + // Fill in the address. + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + + +#if defined(__i386__) || defined(__x86_64__) +// simplistic instruction matching +typedef struct { + unsigned int length; // max 15 + unsigned char mask[15]; // sequence of bytes in memory order + unsigned char constraint[15]; // sequence of bytes in memory order +} AsmInstructionMatch; + +#if defined(__i386__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xFF}, {0x55} }, // push %esp + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp + { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg + { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax + { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax + { 0x0 } +}; +#elif defined(__x86_64__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xF8}, {0x50} }, // push %rX + { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp + { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg + { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax + + //leaq offset(%rip),%rax + { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, + + { 0x0 } +}; +#endif + +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) +{ + Boolean match = true; + + size_t i; + for (i=0; i<instruction->length; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; + unsigned char codeValue = code[i]; + + match = ((codeValue & mask) == constraint); + if (!match) break; + } + + return match; +} + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + unsigned char* ptr = code; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + int instructionIndex = 0; + + if (howManyEaten) *howManyEaten = 0; + if (originalInstructionCount) *originalInstructionCount = 0; + while (remainsToEat > 0) { + Boolean curInstructionKnown = false; + + // See if instruction matches one we know + AsmInstructionMatch* curInstr = possibleInstructions; + do { + if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; + curInstr++; + } while (curInstr->length > 0); + + // if all instruction matches failed, we don't know current instruction then, stop here + if (!curInstructionKnown) { + allInstructionsKnown = false; + fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); + break; + } + + // At this point, we've matched curInstr + int eaten = curInstr->length; + ptr += eaten; + remainsToEat -= eaten; + totalEaten += eaten; + + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; + instructionIndex += 1; + if (originalInstructionCount) *originalInstructionCount = instructionIndex; + } + + + if (howManyEaten) *howManyEaten = totalEaten; + + if (originalInstructions) { + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); + + if (enoughSpaceForOriginalInstructions) { + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP + bcopy(code, originalInstructions, totalEaten); + } else { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + return false; + } + } + + if (allInstructionsKnown) { + // save last 3 bytes of first 64bits of codre we'll replace + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; + + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes + } + + return allInstructionsKnown; +} + + static void +fixupInstructions( + uint32_t offset, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ) +{ + // The start of "leaq offset(%rip),%rax" + static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; + + int index; + for (index = 0;index < instructionCount;index += 1) + { + if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative + { + uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); + *jumpOffsetPtr += offset; + } + + // leaq offset(%rip),%rax + if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { + uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); + *LeaqOffsetPtr += offset; + } + + // 32-bit call relative to the next addr; pop %eax + if (*(uint8_t*)instructionsToFix == 0xE8) + { + // Just this call is larger than the jump we use, so we + // know this is the last instruction. + assert(index == (instructionCount - 1)); + assert(instructionSizes[index] == 6); + + // Insert "addl $offset, %eax" in the end so that when + // we jump to the rest of the function %eax has the + // value it would have if eip had been pushed by the + // call in its original position. + uint8_t *op = (uint8_t*)instructionsToFix; + op += 6; + *op = 0x05; // addl + uint32_t *addImmPtr = (uint32_t*)(op + 1); + *addImmPtr = offset; + } + + instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); + } +} +#endif + +#if defined(__i386__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value) +{ + while (true) + { + uint64_t old_value = *targetAddress; + if (OSAtomicCompareAndSwap64(old_value, value, (int64_t*)targetAddress)) return; + } +} +#elif defined(__x86_64__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value ) +{ + *targetAddress = value; +} +#endif +#endif diff --git a/togl/linuxwin/mach_override.h b/togl/linuxwin/mach_override.h new file mode 100644 index 0000000..ecd319c --- /dev/null +++ b/togl/linuxwin/mach_override.h @@ -0,0 +1,76 @@ +// mach_override.h semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#ifndef _mach_override_ +#define _mach_override_ + +#include <sys/types.h> +#include <mach/error.h> + +#define err_cannot_override (err_local|1) + +__BEGIN_DECLS + +/**************************************************************************************** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. + Optionally returns a pointer to a "reentry island" which, if jumped to, will resume + the original implementation. + + @param originalFunctionAddress -> Required address of the function to + override (with overrideFunctionAddress). + @param overrideFunctionAddress -> Required address to the overriding + function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to the + reentry island. Can be NULL. + @result <- err_cannot_override if the original + function's implementation begins with + the 'mfctr' instruction. + + ************************************************************************************/ + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ); + +__END_DECLS + +/**************************************************************************************** + If you're using C++ this macro will ease the tedium of typedef'ing, naming, keeping + track of reentry islands and defining your override code. See test_mach_override.cp + for example usage. + + ************************************************************************************/ + +#ifdef __cplusplus +#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ +{ \ + static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ + static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ + class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ + public: \ + static kern_return_t override(void *originalFunctionPtr) { \ + kern_return_t result = err_none; \ + if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ + ORIGINAL_FUNCTION_NAME##_overriden = true; \ + result = mach_override_ptr( (void*)originalFunctionPtr, \ + (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ + (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ + } \ + return result; \ + } \ + static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { + +#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ + } \ + }; \ + \ + err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ +} +#endif + +#endif // _mach_override_ diff --git a/togl/togl.vpc b/togl/togl.vpc new file mode 100644 index 0000000..d120879 --- /dev/null +++ b/togl/togl.vpc @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// TOGL.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." [$WIN32] +$Macro SRCDIR ".." [!$WIN32] +$Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "togl" +$Macro TOGL_SRCDIR "$SRCDIR/togl/linuxwin" +$Macro TOGL_INCDIR "$SRCDIR/public/togl/linuxwin" + + +$include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +// Common Configuration +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\" + $PreprocessorDefinitions "$BASE;TOGL_DLL_EXPORT;PROTECTED_THINGS_ENABLE;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead" [!$OSXALL] + $PreprocessorDefinitions "$BASE;TOGL_DLL_EXPORT" [$OSXALL] + + } + + $Linker + { + $ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [!$X360 && !$OSXALL] + $ImportLibrary "$SRCDIR\lib\$PLATFORM\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [$OSXALL] + } + + $Linker [$OSXALL] + { + $SystemFrameworks "Carbon;OpenGL;Quartz;Cocoa;IOKit" + } + + // togl/tier0/vstdlib traditionally used "lib" prefix though nobody else seems to. + $Linker [$POSIX] + { + $OutputFile "$(OBJ_DIR)/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT" + } + + $General [$POSIX] + { + $GameOutputFile "$OUTBINDIR/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT" + } + + $PreLinkEvent [$WINDOWS] + { + $CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $LIBPUBLIC\$(TargetName).lib $SRCDIR" "\n" \ + "$BASE" + } +} + +$Project "togl" +{ + $Folder "Source Files" [$GL] + { + $File "$TOGL_SRCDIR/dx9asmtogl2.cpp" + $File "$TOGL_SRCDIR/dxabstract.cpp" + $File "$TOGL_SRCDIR/glentrypoints.cpp" + $File "$TOGL_SRCDIR/glmgr.cpp" + $File "$TOGL_SRCDIR/glmgrbasics.cpp" + $File "$TOGL_SRCDIR/glmgrcocoa.mm" [$OSXALL] + $File "$TOGL_SRCDIR/intelglmallocworkaround.cpp" [$OSXALL] + $File "$TOGL_SRCDIR/mach_override.c" [$OSXALL] + $File "$TOGL_SRCDIR/cglmtex.cpp" + $File "$TOGL_SRCDIR/cglmfbo.cpp" + $File "$TOGL_SRCDIR/cglmprogram.cpp" + $File "$TOGL_SRCDIR/cglmbuffer.cpp" + $File "$TOGL_SRCDIR/cglmquery.cpp" + } + + $Folder "DirectX Header Files" [$WIN32 && !$GL] + { + } + + $Folder "Header Files" [$GL] + { + $File "$TOGL_SRCDIR/dx9asmtogl2.h" + $File "$TOGL_SRCDIR/glmgr_flush.inl" + $File "$TOGL_SRCDIR/intelglmallocworkaround.h" [$OSXALL] + $File "$TOGL_SRCDIR/mach_override.h" [$OSXALL] + } + + $Folder "Public Header Files" [$GL] + { + $File "$SRCDIR/public/togl/rendermechanism.h" + $File "$TOGL_INCDIR/dxabstract.h" + $File "$TOGL_INCDIR/dxabstract_types.h" + $File "$TOGL_INCDIR/glbase.h" + $File "$TOGL_INCDIR/glentrypoints.h" + $File "$TOGL_INCDIR/glmgr.h" + $File "$TOGL_INCDIR/glmdebug.h" + $File "$TOGL_INCDIR/glmgrbasics.h" + $File "$TOGL_INCDIR/glmgrext.h" + $File "$TOGL_INCDIR/glmdisplay.h" + $File "$TOGL_INCDIR/glmdisplaydb.h" + $File "$TOGL_INCDIR/glfuncs.h" + $File "$TOGL_INCDIR/cglmtex.h" + $File "$TOGL_INCDIR/cglmfbo.h" + $File "$TOGL_INCDIR/cglmprogram.h" + $File "$TOGL_INCDIR/cglmbuffer.h" + $File "$TOGL_INCDIR/cglmquery.h" + } + + $Folder "Link Libraries" + { + $Lib tier2 + $Lib mathlib + } +} + |