//========= 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 "togles/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 [ ] =
{
" #version 300 es \n "
" precision mediump float; \n "
" out vec4 _gl_FragColor; \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 [ ] = // Гроб гроб кладбище пидор
{
" #version 300 es \n "
" precision mediump float; \n "
" out vec4 otex; \n "
" void main() \n "
" { \n "
" vec4 pos = 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 [ ] =
{
" #version 300 es \n "
" precision mediump float; \n "
" out vec4 _gl_FragColor; \n "
" in 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 [ ] =
{
" #version 300 es \n "
" precision mediump float; \n "
" out vec4 _gl_FragColor; \n "
" in vec4 otex; \n "
" //SAMPLERMASK-8000 // may not be needed \n "
" //HIGHWATER-30 // may not be needed \n "
" \n "
" precision mediump sampler3D; \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 = vec4(0,0,0,0); //discard; \n "
" } \n "
} ;
char g_preloadCubeTexFragmentProgramText [ ] =
{
" #version 300 es \n "
" precision mediump float; \n "
" in vec4 otex; \n "
" out vec4 _gl_FragColor; \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 ) ;
BindFBOToCtx ( m_boundDrawFBO , GL_DRAW_FRAMEBUFFER ) ;
// Current VB/IB/pinned memory buffers
gGL - > glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , m_nBoundGLBuffer [ kGLMIndexBuffer ] ) ;
gGL - > glBindBuffer ( GL_ARRAY_BUFFER , m_nBoundGLBuffer [ kGLMVertexBuffer ] ) ;
}
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 , GL_DEPTH_ATTACHMENT , GL_DEPTH_STENCIL_ATTACHMENT } ;
void glScrubFBO ( GLenum target )
{
gGL - > glFramebufferRenderbuffer ( target , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , 0 ) ;
gGL - > glFramebufferRenderbuffer ( target , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
gGL - > glFramebufferRenderbuffer ( target , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
gGL - > glFramebufferTexture2D ( target , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , 0 , 0 ) ;
gGL - > glFramebufferTexture2D ( target , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , 0 , 0 ) ;
gGL - > glFramebufferTexture2D ( target , GL_STENCIL_ATTACHMENT , GL_TEXTURE_2D , 0 , 0 ) ;
}
void glAttachRBOtoFBO ( GLenum target , eBlitFormatClass formatClass , uint rboName )
{
switch ( formatClass )
{
case eColor :
gGL - > glFramebufferRenderbuffer ( target , GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER , rboName ) ;
break ;
case eDepth :
gGL - > glFramebufferRenderbuffer ( target , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , rboName ) ;
break ;
case eDepthStencil :
gGL - > glFramebufferRenderbuffer ( target , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , rboName ) ;
gGL - > glFramebufferRenderbuffer ( target , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , rboName ) ;
break ;
}
}
void glAttachTex2DtoFBO ( GLenum target , eBlitFormatClass formatClass , uint texName , uint texMip )
{
switch ( formatClass )
{
case eColor :
gGL - > glFramebufferTexture2D ( target , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , texName , texMip ) ;
break ;
case eDepth :
gGL - > glFramebufferTexture2D ( target , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , texName , texMip ) ;
break ;
case eDepthStencil :
gGL - > glFramebufferTexture2D ( target , GL_DEPTH_STENCIL_ATTACHMENT , 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 :
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 ) ;
glScrubFBO ( GL_READ_FRAMEBUFFER ) ;
glAttachRBOtoFBO ( GL_READ_FRAMEBUFFER , formatClass , srcTex - > m_rboName ) ;
// bind scratch FBO1 to write, scrub it, attach scratch tex
BindFBOToCtx ( m_scratchFBO [ 1 ] , GL_DRAW_FRAMEBUFFER ) ;
glScrubFBO ( GL_DRAW_FRAMEBUFFER ) ;
glAttachTex2DtoFBO ( GL_DRAW_FRAMEBUFFER , 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 - > glBlitFramebuffer ( 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 ) ; // zap FBO0
BindFBOToCtx ( m_scratchFBO [ 1 ] , GL_READ_FRAMEBUFFER ) ;
srcTex - > ForceRBONonDirty ( ) ;
}
else
{
# if 1
if ( srcTex - > m_pBlitSrcFBO = = NULL )
{
srcTex - > m_pBlitSrcFBO = NewFBO ( ) ;
BindFBOToCtx ( srcTex - > m_pBlitSrcFBO , GL_READ_FRAMEBUFFER ) ;
if ( blitResolves )
{
glAttachRBOtoFBO ( GL_READ_FRAMEBUFFER , formatClass , srcTex - > m_rboName ) ;
}
else
{
glAttachTex2DtoFBO ( GL_READ_FRAMEBUFFER , formatClass , srcTex - > m_texName , srcMip ) ;
}
}
else
{
BindFBOToCtx ( srcTex - > m_pBlitSrcFBO , GL_READ_FRAMEBUFFER ) ;
// 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 ) ;
glScrubFBO ( GL_READ_FRAMEBUFFER ) ;
GLMCheckError ( ) ;
if ( blitResolves )
{
glAttachRBOtoFBO ( GL_READ_FRAMEBUFFER , formatClass , srcTex - > m_rboName ) ;
}
else
{
glAttachTex2DtoFBO ( GL_READ_FRAMEBUFFER , 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 ) ;
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 ) ;
if ( dstTex - > m_rboName )
{
glAttachRBOtoFBO ( GL_DRAW_FRAMEBUFFER , formatClass , dstTex - > m_rboName ) ;
}
else
{
glAttachTex2DtoFBO ( GL_DRAW_FRAMEBUFFER , formatClass , dstTex - > m_texName , dstMip ) ;
}
}
else
{
BindFBOToCtx ( dstTex - > m_pBlitDstFBO , GL_DRAW_FRAMEBUFFER ) ;
}
# else
BindFBOToCtx ( m_scratchFBO [ 0 ] , GL_DRAW_FRAMEBUFFER ) ; GLMCheckError ( ) ;
glScrubFBO ( GL_DRAW_FRAMEBUFFER ) ;
if ( dstTex - > m_rboName )
{
glAttachRBOtoFBO ( GL_DRAW_FRAMEBUFFER , formatClass , dstTex - > m_rboName ) ;
}
else
{
glAttachTex2DtoFBO ( GL_DRAW_FRAMEBUFFER , 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 - > glBlitFramebuffer ( 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 - > glBlitFramebuffer ( 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 );
BindFBOToCtx ( NULL , GL_READ_FRAMEBUFFER ) ;
if ( ! blitToBack )
{
// glScrubFBO ( GL_DRAW_FRAMEBUFFER );
BindFBOToCtx ( NULL , GL_DRAW_FRAMEBUFFER ) ;
}
//----------------------------------------------------------------- restore GLM's drawing FBO
// restore GLM drawing FBO
BindFBOToCtx ( m_drawingFBO , GL_FRAMEBUFFER ) ;
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 ;
blitMask = GL_COLOR_BUFFER_BIT ;
break ;
case GL_DEPTH_COMPONENT :
attachIndex = kAttDepth ;
attachIndexGL = GL_DEPTH_ATTACHMENT ;
blitMask = GL_DEPTH_BUFFER_BIT ;
break ;
case GL_DEPTH_STENCIL :
attachIndex = kAttDepthStencil ;
attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT ;
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 ) ;
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 ) ;
gGL - > glReadBuffer ( attachIndexGL ) ;
// set the write fb and buffer, and attach write tex
BindFBOToCtx ( m_blitDrawFBO , GL_DRAW_FRAMEBUFFER ) ;
attparams . m_tex = dstTex ;
attparams . m_face = dstFace ;
attparams . m_mip = dstMip ;
attparams . m_zslice = 0 ;
m_blitDrawFBO - > TexAttach ( & attparams , attachIndex , GL_DRAW_FRAMEBUFFER ) ;
gGL - > glDrawBuffer ( attachIndexGL ) ;
// do the blit
gGL - > glBlitFramebuffer ( 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 ) ;
m_blitDrawFBO - > TexDetach ( attachIndex , GL_DRAW_FRAMEBUFFER ) ;
// 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 ) ;
// 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 ;
break ;
default :
Assert ( ! " Can't blit that format " ) ;
break ;
}
BindFBOToCtx ( m_blitDrawFBO , GL_DRAW_FRAMEBUFFER ) ;
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 ) ;
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
const float topv = 1.0 ;
const float botv = 0.0 ;
const float verts [ ] = { - 1.f , - 1.f , 1.f , - 1.f , 1.f , 1.f , - 1.f , 1.f } ;
const float verts_tex [ ] = { 0.f , botv , 1.f , botv , 1.f , topv , 0.f , topv } ;
gGL - > glEnableClientState ( GL_VERTEX_ARRAY ) ;
gGL - > glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
gGL - > glVertexPointer ( 2 , GL_FLOAT , 0 , verts ) ;
gGL - > glTexCoordPointer ( 2 , GL_FLOAT , 0 , verts_tex ) ;
glDrawArrays ( GL_TRIANGLE_FAN , 0 , 4 ) ;
gGL - > glDisableClientState ( GL_VERTEX_ARRAY ) ;
gGL - > glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
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 ) ;
// put the original FB back in place (both read and draw)
BindFBOToCtx ( m_drawingFBO , GL_FRAMEBUFFER ) ;
}
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 ;
blitMask = GL_COLOR_BUFFER_BIT ;
break ;
// case GL_DEPTH_COMPONENT:
// attachIndex = kAttDepth;
// attachIndexGL = GL_DEPTH_ATTACHMENT;
// blitMask = GL_DEPTH_BUFFER_BIT;
// break;
case GL_DEPTH_STENCIL :
attachIndex = kAttDepthStencil ;
attachIndexGL = GL_DEPTH_STENCIL_ATTACHMENT ;
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 ) ;
// 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 )
{
// you have to attach it both places...
// http://www.opengl.org/wiki/GL_EXT_framebuffer_object
// bind the RBO to the GL_RENDERBUFFER target - is this extraneous ?
//glBindRenderbufferEXT( GL_RENDERBUFFER, tex->m_rboName );
// attach the GL_RENDERBUFFER target to the depth and stencil attach points
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , tex - > m_rboName ) ;
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , tex - > m_rboName ) ;
// no need to leave the RBO hanging on
//glBindRenderbufferEXT( GL_RENDERBUFFER, 0 );
}
else
{
//glBindRenderbufferEXT( GL_RENDERBUFFER, tex->m_rboName );
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , attachIndexGL , GL_RENDERBUFFER , tex - > m_rboName ) ;
//glBindRenderbufferEXT( GL_RENDERBUFFER, 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 ) ;
// regular path - attaching a texture2d
if ( attachIndexGL = = GL_DEPTH_STENCIL_ATTACHMENT )
{
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , tex - > m_texName , 0 ) ;
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_TEXTURE_2D , tex - > m_texName , 0 ) ;
}
else
{
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , attachIndexGL , GL_TEXTURE_2D , tex - > m_texName , 0 ) ;
}
gGL - > glDrawBuffer ( attachIndexGL ) ;
//-----------------------------------------------------------------------------------
// blit
gGL - > glBlitFramebuffer ( 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, 0 );
if ( attachIndexGL = = GL_DEPTH_STENCIL_ATTACHMENT )
{
// detach the GL_RENDERBUFFER target from the depth and stencil attach points
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_RENDERBUFFER , 0 ) ;
}
else
{
gGL - > glFramebufferRenderbuffer ( GL_READ_FRAMEBUFFER , attachIndexGL , GL_RENDERBUFFER , 0 ) ;
}
//-----------------------------------------------------------------------------------
// unset the write fb and buffer, detach write tex
if ( attachIndexGL = = GL_DEPTH_STENCIL_ATTACHMENT )
{
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , 0 , 0 ) ;
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_STENCIL_ATTACHMENT , GL_TEXTURE_2D , 0 , 0 ) ;
}
else
{
gGL - > glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , 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 ) ;
// 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 , 2 , 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 ) ;
m_boundReadFBO = NULL ;
}
if ( m_boundDrawFBO = = fbo )
{
BindFBOToCtx ( NULL , GL_DRAW_FRAMEBUFFER ) ;
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 ( ) ;
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 ) ;
}
else
{
ResolveTex ( tex , true ) ; // dxabstract used to do this unconditionally.we still do if new refresh mode doesn't engage.
BindFBOToCtx ( NULL , GL_FRAMEBUFFER ) ;
// 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 ) ;
// 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 ( ) ;
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 ( 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
// TODO(nillerusr): rewrite me!!!
// This should be there if we get in here--make sure.
gGL - > glDebugMessageControl ( GL_DONT_CARE , GL_DONT_CARE , GL_DONT_CARE , 0 , ( const GLuint * ) NULL , GL_TRUE ) ;
// Gonna filter these out, they're "chatty".
gGL - > glDebugMessageControl ( GL_DEBUG_SOURCE_API_ARB , GL_DEBUG_TYPE_OTHER_ARB , GL_DEBUG_SEVERITY_LOW_ARB , 0 , ( const GLuint * ) NULL , GL_FALSE ) ;
gGL - > glDebugMessageCallback ( GL_Debug_Output_Callback , ( void * ) NULL ) ;
GLMDebugPrintf ( " GLMContext::GLMContext: Debug output (gl_arb_debug_output) enabled! \n " ) ;
//#endif
}
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 & params = 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 - > glGenBuffers ( 1 , & m_destroyPBO ) ;
gGL - > glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , m_destroyPBO ) ;
gGL - > glBufferData ( GL_PIXEL_UNPACK_BUFFER , sizeof ( g_garbageTextureBits ) , g_garbageTextureBits , GL_STATIC_DRAW ) ;
gGL - > glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 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 ( " \n MTGL detected. \n " ) ;
}
else
{
printf ( " \n MTGL *not* detected, falling back. \n " ) ;
}
}
if ( ! ready )
{
// try old MTGL
result = CGLEnable ( context , kCGLCEMPEngine ) ;
if ( ! result )
{
printf ( " \n MTGL 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 ( " \n Enabling 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 ( )
{
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 - > glDeleteBuffers ( 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 )
{
gGL - > glBindFramebuffer ( GL_FRAMEBUFFER , fbo ? fbo - > m_name : 0 ) ;
m_boundReadFBO = fbo ;
m_boundDrawFBO = fbo ;
return ;
}
bool targetRead = ( bindPoint = = GL_READ_FRAMEBUFFER ) ;
bool targetDraw = ( bindPoint = = GL_DRAW_FRAMEBUFFER ) ;
if ( targetRead )
{
if ( fbo ) // you can pass NULL to go back to no-FBO
{
gGL - > glBindFramebuffer ( GL_READ_FRAMEBUFFER , fbo - > m_name ) ;
m_boundReadFBO = fbo ;
//dontcare fbo->m_bound = true;
}
else
{
gGL - > glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
m_boundReadFBO = NULL ;
}
}
if ( targetDraw )
{
if ( fbo ) // you can pass NULL to go back to no-FBO
{
gGL - > glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , fbo - > m_name ) ;
m_boundDrawFBO = fbo ;
//dontcare fbo->m_bound = true;
}
else
{
gGL - > glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , 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 ; break ;
case kGLMIndexBuffer : target = GL_ELEMENT_ARRAY_BUFFER ; break ;
case kGLMUniformBuffer : target = GL_UNIFORM_BUFFER ; break ;
case kGLMPixelBuffer : target = GL_PIXEL_UNPACK_BUFFER ; break ;
default : Assert ( ! " Unknown buffer type " ) ;
}
Assert ( ! pBuff | | ( pBuff - > m_buffGLTarget = = target ) ) ;
m_nBoundGLBuffer [ type ] = nGLName ;
gGL - > glBindBuffer ( 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 - > glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 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 - > glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 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 ) , " \n GL 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 ( & params , 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 ( & params ) ;
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 ) ;
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 ) ;
// 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 )
{
// dig up the more detailed FBO status
errorcode2 = gGL - > glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
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 ( & gtp ) ;
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 ) ;
// 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