summaryrefslogtreecommitdiff
path: root/togl/linuxwin/cglmtex.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /togl/linuxwin/cglmtex.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'togl/linuxwin/cglmtex.cpp')
-rw-r--r--togl/linuxwin/cglmtex.cpp1990
1 files changed, 1990 insertions, 0 deletions
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;
+}