//========= 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.h // singleton class, common basis for managing GL contexts // responsible for tracking adapters and contexts // //=============================================================================== #ifndef GLMGR_H #define GLMGR_H #pragma once #undef HAVE_GL_ARB_SYNC #ifndef OSX #define HAVE_GL_ARB_SYNC 1 #endif #include "glbase.h" #include "glentrypoints.h" #include "glmdebug.h" #include "glmdisplay.h" #include "glmgrext.h" #include "glmgrbasics.h" #include "cglmtex.h" #include "cglmfbo.h" #include "cglmprogram.h" #include "cglmbuffer.h" #include "cglmquery.h" #include "tier0/tslist.h" #include "tier0/vprof_telemetry.h" #include "materialsystem/IShader.h" #include "dxabstract_types.h" #include "tier0/icommandline.h" #undef FORCEINLINE #define FORCEINLINE inline //=============================================================================== #define GLM_OPENGL_VENDOR_ID 1 #define GLM_OPENGL_DEFAULT_DEVICE_ID 1 #define GLM_OPENGL_LOW_PERF_DEVICE_ID 2 #define GL_ALPHA_TEST_QCOM 0x0BC0 #define GL_ALPHA_TEST_FUNC_QCOM 0x0BC1 #define GL_ALPHA_TEST_REF_QCOM 0x0BC2 extern void GLMDebugPrintf( const char *pMsg, ... ); extern uint g_nTotalDrawsOrClears, g_nTotalVBLockBytes, g_nTotalIBLockBytes; #if GL_TELEMETRY_GPU_ZONES struct TelemetryGPUStats_t { uint m_nTotalBufferLocksAndUnlocks; uint m_nTotalTexLocksAndUnlocks; uint m_nTotalBlit2; uint m_nTotalResolveTex; uint m_nTotalPresent; inline void Clear() { memset( this, 0, sizeof( *this ) ); } inline uint GetTotal() const { return m_nTotalBufferLocksAndUnlocks + m_nTotalTexLocksAndUnlocks + m_nTotalBlit2 + m_nTotalResolveTex + m_nTotalPresent; } }; extern TelemetryGPUStats_t g_TelemetryGPUStats; #endif struct GLMRect; typedef void *PseudoGLContextPtr; // parrot the D3D present parameters, more or less... "adapter" translates into "active display index" per the m_activeDisplayCount below. class GLMDisplayParams { public: // presumption, these indices are in sync with the current display DB that GLMgr has handy //int m_rendererIndex; // index of renderer (-1 if root context) //int m_displayIndex; // index of display in renderer - for FS //int m_modeIndex; // index of mode in display - for FS void *m_focusWindow; // (VD3DHWND aka WindowRef) - what window does this context display into bool m_fsEnable; // fullscreen on or not bool m_vsyncEnable; // vsync on or not // height and width have to match the display mode info if full screen. uint m_backBufferWidth; // pixel width (aka screen h-resolution if full screen) uint m_backBufferHeight; // pixel height (aka screen v-resolution if full screen) D3DFORMAT m_backBufferFormat; // pixel format uint m_multiSampleCount; // 0 means no MSAA, 2 means 2x MSAA, etc // uint m_multiSampleQuality; // no MSAA quality control yet bool m_enableAutoDepthStencil; // generally set to 'TRUE' per CShaderDeviceDx8::SetPresentParameters D3DFORMAT m_autoDepthStencilFormat; uint m_fsRefreshHz; // if full screen, this refresh rate (likely 0 for LCD's) //uint m_rootRendererID; // only used if m_rendererIndex is -1. //uint m_rootDisplayMask; // only used if m_rendererIndex is -1. bool m_mtgl; // enable multi threaded GL driver }; //=============================================================================== class GLMgr { public: //=========================================================================== // class methods - singleton static void NewGLMgr( void ); // instantiate singleton.. static GLMgr *aGLMgr( void ); // return singleton.. static void DelGLMgr( void ); // tear down singleton.. //=========================================================================== // plain methods #if 0 // turned all these off while new approach is coded void RefreshDisplayDB( void ); // blow away old display DB, make a new one GLMDisplayDB *GetDisplayDB( void ); // get a ptr to the one GLMgr keeps. only valid til next refresh. // eligible renderers will be ranked by desirability starting at index 0 within the db // within each renderer, eligible displays will be ranked some kind of desirability (area? dist from menu bar?) // within each display, eligible modes will be ranked by descending areas // calls supplying indices are implicitly making reference to the current DB bool CaptureDisplay( int rendIndex, int displayIndex, bool captureAll ); // capture one display or all displays void ReleaseDisplays( void ); // release all captures int GetDisplayMode( int rendIndex, int displayIndex ); // retrieve current display res (returns modeIndex) void SetDisplayMode( GLMDisplayParams *params ); // set the display res (only useful for FS) #endif GLMContext *NewContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params ); // this will have to change void DelContext( GLMContext *context ); // with usage of CGLMacro.h we could dispense with the "current context" thing // and just declare a member variable of GLMContext, allowing each glXXX call to be routed directly // to the correct context void SetCurrentContext( GLMContext *context ); // make current in calling thread only GLMContext *GetCurrentContext( void ); protected: friend class GLMContext; GLMgr(); ~GLMgr(); }; //===========================================================================// // helper function to do enable or disable in one step FORCEINLINE void glSetEnable( GLenum which, bool enable ) { if (enable) gGL->glEnable(which); else gGL->glDisable(which); } // helper function for int vs enum clarity FORCEINLINE void glGetEnumv( GLenum which, GLenum *dst ) { gGL->glGetIntegerv( which, (int*)dst ); } //===========================================================================// // // types to support the GLMContext // //===========================================================================// // Each state set/get path we are providing caching for, needs its own struct and a comparison operator. // we also provide an enum of how many such types there are, handy for building dirty masks etc. // shorthand macros #define EQ(fff) ( (src.fff) == (fff) ) //rasterizer struct GLAlphaTestEnable_t { GLint enable; inline bool operator==(const GLAlphaTestEnable_t& src) const { return EQ(enable); } }; struct GLAlphaTestFunc_t { GLenum func; GLclampf ref; inline bool operator==(const GLAlphaTestFunc_t& src) const { return EQ(func) && EQ(ref); } }; struct GLAlphaTest_t { GLint enable; GLenum func; GLclampf ref; }; struct GLCullFaceEnable_t { GLint enable; inline bool operator==(const GLCullFaceEnable_t& src) const { return EQ(enable); } }; struct GLCullFrontFace_t { GLenum value; inline bool operator==(const GLCullFrontFace_t& src) const { return EQ(value); } }; struct GLPolygonMode_t { GLenum values[2]; inline bool operator==(const GLPolygonMode_t& src) const { return EQ(values[0]) && EQ(values[1]); } }; struct GLDepthBias_t { GLfloat factor; GLfloat units; inline bool operator==(const GLDepthBias_t& src) const { return EQ(factor) && EQ(units); } }; struct GLScissorEnable_t { GLint enable; inline bool operator==(const GLScissorEnable_t& src) const { return EQ(enable); } }; struct GLScissorBox_t { GLint x,y; GLsizei width, height; inline bool operator==(const GLScissorBox_t& src) const { return EQ(x) && EQ(y) && EQ(width) && EQ(height); } }; struct GLAlphaToCoverageEnable_t{ GLint enable; inline bool operator==(const GLAlphaToCoverageEnable_t& src) const { return EQ(enable); } }; struct GLViewportBox_t { GLint x,y; GLsizei width, height; uint widthheight; inline bool operator==(const GLViewportBox_t& src) const { return EQ(x) && EQ(y) && EQ(width) && EQ(height); } }; struct GLViewportDepthRange_t { GLfloat flNear,flFar; inline bool operator==(const GLViewportDepthRange_t& src) const { return EQ(flNear) && EQ(flFar); } }; struct GLClipPlaneEnable_t { GLint enable; inline bool operator==(const GLClipPlaneEnable_t& src) const { return EQ(enable); } }; struct GLClipPlaneEquation_t { GLfloat x,y,z,w; inline bool operator==(const GLClipPlaneEquation_t& src) const { return EQ(x) && EQ(y) && EQ(z) && EQ(w); } }; //blend struct GLColorMaskSingle_t { signed char r,g,b,a; inline bool operator==(const GLColorMaskSingle_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLColorMaskMultiple_t { signed char r,g,b,a; inline bool operator==(const GLColorMaskMultiple_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLBlendEnable_t { GLint enable; inline bool operator==(const GLBlendEnable_t& src) const { return EQ(enable); } }; struct GLBlendFactor_t { GLenum srcfactor,dstfactor; inline bool operator==(const GLBlendFactor_t& src) const { return EQ(srcfactor) && EQ(dstfactor); } }; struct GLBlendEquation_t { GLenum equation; inline bool operator==(const GLBlendEquation_t& src) const { return EQ(equation); } }; struct GLBlendColor_t { GLfloat r,g,b,a; inline bool operator==(const GLBlendColor_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLBlendEnableSRGB_t { GLint enable; inline bool operator==(const GLBlendEnableSRGB_t& src) const { return EQ(enable); } }; //depth struct GLDepthTestEnable_t { GLint enable; inline bool operator==(const GLDepthTestEnable_t& src) const { return EQ(enable); } }; struct GLDepthFunc_t { GLenum func; inline bool operator==(const GLDepthFunc_t& src) const { return EQ(func); } }; struct GLDepthMask_t { char mask; inline bool operator==(const GLDepthMask_t& src) const { return EQ(mask); } }; //stencil struct GLStencilTestEnable_t { GLint enable; inline bool operator==(const GLStencilTestEnable_t& src) const { return EQ(enable); } }; struct GLStencilFunc_t { GLenum frontfunc, backfunc; GLint ref; GLuint mask; inline bool operator==(const GLStencilFunc_t& src) const { return EQ(frontfunc) && EQ(backfunc) && EQ(ref) && EQ(mask); } }; struct GLStencilOp_t { GLenum sfail; GLenum dpfail; GLenum dppass; inline bool operator==(const GLStencilOp_t& src) const { return EQ(sfail) && EQ(dpfail) && EQ(dppass); } }; struct GLStencilWriteMask_t { GLint mask; inline bool operator==(const GLStencilWriteMask_t& src) const { return EQ(mask); } }; //clearing struct GLClearColor_t { GLfloat r,g,b,a; inline bool operator==(const GLClearColor_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLClearDepth_t { GLfloat d; inline bool operator==(const GLClearDepth_t& src) const { return EQ(d); } }; struct GLClearStencil_t { GLint s; inline bool operator==(const GLClearStencil_t& src) const { return EQ(s); } }; #undef EQ enum EGLMStateBlockType { kGLAlphaTestEnable, kGLAlphaTestFunc, kGLCullFaceEnable, kGLCullFrontFace, kGLPolygonMode, kGLDepthBias, kGLScissorEnable, kGLScissorBox, kGLViewportBox, kGLViewportDepthRange, kGLClipPlaneEnable, kGLClipPlaneEquation, kGLColorMaskSingle, kGLColorMaskMultiple, kGLBlendEnable, kGLBlendFactor, kGLBlendEquation, kGLBlendColor, kGLBlendEnableSRGB, kGLDepthTestEnable, kGLDepthFunc, kGLDepthMask, kGLStencilTestEnable, kGLStencilFunc, kGLStencilOp, kGLStencilWriteMask, kGLClearColor, kGLClearDepth, kGLClearStencil, kGLAlphaToCoverageEnable, kGLMStateBlockLimit }; //===========================================================================// // templated functions representing GL R/W bottlenecks // one set of set/get/getdefault is instantiated for each of the GL*** types above. // use these from the non array state objects template void GLContextSet( T *src ); template void GLContextGet( T *dst ); template void GLContextGetDefault( T *dst ); // use these from the array state objects template void GLContextSetIndexed( T *src, int index ); template void GLContextGetIndexed( T *dst, int index ); template void GLContextGetDefaultIndexed( T *dst, int index ); //=============================================================================== // template specializations for each type of state static GLAlphaTest_t g_alpha_test; // --- GLAlphaTestEnable --- FORCEINLINE void GLContextSet( GLAlphaTestEnable_t *src ) { if( gGL->m_bHave_GL_QCOM_alpha_test ) glSetEnable( GL_ALPHA_TEST_QCOM, src->enable != 0 ); else g_alpha_test.enable = src->enable; } FORCEINLINE void GLContextGet( GLAlphaTestEnable_t *dst ) { if( gGL->m_bHave_GL_QCOM_alpha_test ) dst->enable = gGL->glIsEnabled( GL_ALPHA_TEST_QCOM ); else dst->enable = g_alpha_test.enable; } FORCEINLINE void GLContextGetDefault( GLAlphaTestEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLAlphaTestFunc --- FORCEINLINE void GLContextSet( GLAlphaTestFunc_t *src ) { if( gGL->m_bHave_GL_QCOM_alpha_test ) gGL->glAlphaFuncQCOM( src->func, src->ref ); g_alpha_test.func = src->func; g_alpha_test.ref = src->ref; } FORCEINLINE void GLContextGet( GLAlphaTestFunc_t *dst ) { if( gGL->m_bHave_GL_QCOM_alpha_test ) { glGetEnumv( GL_ALPHA_TEST_FUNC_QCOM, &dst->func ); gGL->glGetFloatv( GL_ALPHA_TEST_REF_QCOM, &dst->ref ); } else { dst->func = g_alpha_test.func; dst->ref = g_alpha_test.ref; } } FORCEINLINE void GLContextGetDefault( GLAlphaTestFunc_t *dst ) { dst->func = GL_ALWAYS; dst->ref = 0.0f; } // --- GLAlphaToCoverageEnable --- FORCEINLINE void GLContextSet( GLAlphaToCoverageEnable_t *src ) { glSetEnable( GL_SAMPLE_ALPHA_TO_COVERAGE, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLAlphaToCoverageEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_SAMPLE_ALPHA_TO_COVERAGE ); } FORCEINLINE void GLContextGetDefault( GLAlphaToCoverageEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLCullFaceEnable --- FORCEINLINE void GLContextSet( GLCullFaceEnable_t *src ) { glSetEnable( GL_CULL_FACE, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLCullFaceEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_CULL_FACE ); } FORCEINLINE void GLContextGetDefault( GLCullFaceEnable_t *dst ) { dst->enable = GL_TRUE; } // --- GLCullFrontFace --- FORCEINLINE void GLContextSet( GLCullFrontFace_t *src ) { gGL->glFrontFace( src->value ); // legal values are GL_CW or GL_CCW } FORCEINLINE void GLContextGet( GLCullFrontFace_t *dst ) { glGetEnumv( GL_FRONT_FACE, &dst->value ); } FORCEINLINE void GLContextGetDefault( GLCullFrontFace_t *dst ) { dst->value = GL_CCW; } // --- GLPolygonMode --- FORCEINLINE void GLContextSet( GLPolygonMode_t *src ) { // gGL->glPolygonMode( GL_FRONT, src->values[0] ); // gGL->glPolygonMode( GL_BACK, src->values[1] ); } FORCEINLINE void GLContextGet( GLPolygonMode_t *dst ) { glGetEnumv( GL_POLYGON_MODE, &dst->values[0] ); } FORCEINLINE void GLContextGetDefault( GLPolygonMode_t *dst ) { dst->values[0] = dst->values[1] = GL_FILL; } // --- GLDepthBias --- // note the implicit enable / disable. // if you set non zero values, it is enabled, otherwise not. FORCEINLINE void GLContextSet( GLDepthBias_t *src ) { bool enable = (src->factor != 0.0f) || (src->units != 0.0f); glSetEnable( GL_POLYGON_OFFSET_FILL, enable ); gGL->glPolygonOffset( src->factor, src->units ); } FORCEINLINE void GLContextGet( GLDepthBias_t *dst ) { gGL->glGetFloatv ( GL_POLYGON_OFFSET_FACTOR, &dst->factor ); gGL->glGetFloatv ( GL_POLYGON_OFFSET_UNITS, &dst->units ); } FORCEINLINE void GLContextGetDefault( GLDepthBias_t *dst ) { dst->factor = 0.0; dst->units = 0.0; } // --- GLScissorEnable --- FORCEINLINE void GLContextSet( GLScissorEnable_t *src ) { glSetEnable( GL_SCISSOR_TEST, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLScissorEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_SCISSOR_TEST ); } FORCEINLINE void GLContextGetDefault( GLScissorEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLScissorBox --- FORCEINLINE void GLContextSet( GLScissorBox_t *src ) { gGL->glScissor ( src->x, src->y, src->width, src->height ); } FORCEINLINE void GLContextGet( GLScissorBox_t *dst ) { gGL->glGetIntegerv ( GL_SCISSOR_BOX, &dst->x ); } FORCEINLINE void GLContextGetDefault( GLScissorBox_t *dst ) { // hmmmm, good question? we can't really know a good answer so we pick a silly one // and the client better come back with a better answer later. dst->x = dst->y = 0; dst->width = dst->height = 16; } // --- GLViewportBox --- FORCEINLINE void GLContextSet( GLViewportBox_t *src ) { Assert( src->width == (int)( src->widthheight & 0xFFFF ) ); Assert( src->height == (int)( src->widthheight >> 16 ) ); gGL->glViewport (src->x, src->y, src->width, src->height ); } FORCEINLINE void GLContextGet( GLViewportBox_t *dst ) { gGL->glGetIntegerv ( GL_VIEWPORT, &dst->x ); dst->widthheight = dst->width | ( dst->height << 16 ); } FORCEINLINE void GLContextGetDefault( GLViewportBox_t *dst ) { // as with the scissor box, we don't know yet, so pick a silly one and change it later dst->x = dst->y = 0; dst->width = dst->height = 16; dst->widthheight = dst->width | ( dst->height << 16 ); } // --- GLViewportDepthRange --- FORCEINLINE void GLContextSet( GLViewportDepthRange_t *src ) { gGL->glDepthRangef ( src->flNear, src->flFar ); } FORCEINLINE void GLContextGet( GLViewportDepthRange_t *dst ) { gGL->glGetFloatv( GL_DEPTH_RANGE, &dst->flNear ); } FORCEINLINE void GLContextGetDefault( GLViewportDepthRange_t *dst ) { dst->flNear = 0.0; dst->flFar = 1.0; } // --- GLClipPlaneEnable --- FORCEINLINE void GLContextSetIndexed( GLClipPlaneEnable_t *src, int index ) { #if GLMDEBUG if (CommandLine()->FindParm("-caps_noclipplanes")) { if (GLMKnob("caps-key",NULL) > 0.0) { // caps ON means NO clipping src->enable = false; } } #endif glSetEnable( GL_CLIP_PLANE0 + index, src->enable != 0 ); } FORCEINLINE void GLContextGetIndexed( GLClipPlaneEnable_t *dst, int index ) { dst->enable = gGL->glIsEnabled( GL_CLIP_PLANE0 + index ); } FORCEINLINE void GLContextGetDefaultIndexed( GLClipPlaneEnable_t *dst, int index ) { dst->enable = 0; } // --- GLClipPlaneEquation --- FORCEINLINE void GLContextSetIndexed( GLClipPlaneEquation_t *src, int index ) { // shove into glGlipPlane // GLdouble coeffs[4] = { src->x, src->y, src->z, src->w }; // gGL->glClipPlane( GL_CLIP_PLANE0 + index, coeffs ); } FORCEINLINE void GLContextGetIndexed( GLClipPlaneEquation_t *dst, int index ) { DebuggerBreak(); // do this later // glClipPlane( GL_CLIP_PLANE0 + index, coeffs ); // GLdouble coeffs[4] = { src->x, src->y, src->z, src->w }; } FORCEINLINE void GLContextGetDefaultIndexed( GLClipPlaneEquation_t *dst, int index ) { dst->x = 1.0; dst->y = 0.0; dst->z = 0.0; dst->w = 0.0; } // --- GLColorMaskSingle --- FORCEINLINE void GLContextSet( GLColorMaskSingle_t *src ) { gGL->glColorMask( src->r, src->g, src->b, src->a ); } FORCEINLINE void GLContextGet( GLColorMaskSingle_t *dst ) { gGL->glGetBooleanv( GL_COLOR_WRITEMASK, (GLboolean*)&dst->r); } FORCEINLINE void GLContextGetDefault( GLColorMaskSingle_t *dst ) { dst->r = dst->g = dst->b = dst->a = 1; } // --- GLColorMaskMultiple --- FORCEINLINE void GLContextSetIndexed( GLColorMaskMultiple_t *src, int index ) { GLint Rfbo = 0, Dfbo = 0; gGL->glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &Dfbo ); gGL->glGetIntegerv( GL_READ_FRAMEBUFFER_BINDING, &Rfbo ); GLint target = Dfbo == Rfbo?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER; gGL->glBindFramebuffer( target, index ); gGL->glColorMask ( src->r, src->g, src->b, src->a ); gGL->glBindFramebuffer( target, Dfbo ); } FORCEINLINE void GLContextGetIndexed( GLColorMaskMultiple_t *dst, int index ) { GLint Rfbo = 0, Dfbo = 0; gGL->glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &Dfbo ); gGL->glGetIntegerv( GL_READ_FRAMEBUFFER_BINDING, &Rfbo ); GLint target = Dfbo == Rfbo?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER; gGL->glBindFramebuffer( target, index ); gGL->glGetBooleanv( GL_COLOR_WRITEMASK, (GLboolean*)&dst->r ); gGL->glBindFramebuffer( target, Dfbo ); } FORCEINLINE void GLContextGetDefaultIndexed( GLColorMaskMultiple_t *dst, int index ) { dst->r = dst->g = dst->b = dst->a = 1; } // --- GLBlendEnable --- FORCEINLINE void GLContextSet( GLBlendEnable_t *src ) { glSetEnable( GL_BLEND, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLBlendEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_BLEND ); } FORCEINLINE void GLContextGetDefault( GLBlendEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLBlendFactor --- FORCEINLINE void GLContextSet( GLBlendFactor_t *src ) { gGL->glBlendFunc ( src->srcfactor, src->dstfactor ); } FORCEINLINE void GLContextGet( GLBlendFactor_t *dst ) { glGetEnumv ( GL_BLEND_SRC, &dst->srcfactor ); glGetEnumv ( GL_BLEND_DST, &dst->dstfactor ); } FORCEINLINE void GLContextGetDefault( GLBlendFactor_t *dst ) { dst->srcfactor = GL_ONE; dst->dstfactor = GL_ZERO; } // --- GLBlendEquation --- FORCEINLINE void GLContextSet( GLBlendEquation_t *src ) { gGL->glBlendEquation ( src->equation ); } FORCEINLINE void GLContextGet( GLBlendEquation_t *dst ) { glGetEnumv ( GL_BLEND_EQUATION, &dst->equation ); } FORCEINLINE void GLContextGetDefault( GLBlendEquation_t *dst ) { dst->equation = GL_FUNC_ADD; } // --- GLBlendColor --- FORCEINLINE void GLContextSet( GLBlendColor_t *src ) { gGL->glBlendColor ( src->r, src->g, src->b, src->a ); } FORCEINLINE void GLContextGet( GLBlendColor_t *dst ) { gGL->glGetFloatv ( GL_BLEND_COLOR, &dst->r ); } FORCEINLINE void GLContextGetDefault( GLBlendColor_t *dst ) { //solid white dst->r = dst->g = dst->b = dst->a = 1.0; } // --- GLBlendEnableSRGB --- #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_COLOR_ATTACHMENT0 0x8CE0 FORCEINLINE void GLContextSet( GLBlendEnableSRGB_t *src ) { #if GLMDEBUG // just check in debug... this is too expensive to look at on MTGL if (src->enable) { GLboolean srgb_capable = false; gGL->glGetBooleanv( GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable); if (src->enable && !srgb_capable) { GLMPRINTF(("-Z- srgb-state-set FBO conflict: attempt to enable SRGB on non SRGB capable FBO config")); } } #endif // this query is not useful unless you have the ARB_framebuffer_srgb ext. //GLint encoding = 0; //pfnglGetFramebufferAttachmentParameteriv( GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &encoding ); glSetEnable( GL_FRAMEBUFFER_SRGB_EXT, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLBlendEnableSRGB_t *dst ) { dst->enable = gGL->glIsEnabled( GL_FRAMEBUFFER_SRGB_EXT ); // dst->enable = true; // wtf ? } FORCEINLINE void GLContextGetDefault( GLBlendEnableSRGB_t *dst ) { dst->enable = GL_FALSE; } // --- GLDepthTestEnable --- FORCEINLINE void GLContextSet( GLDepthTestEnable_t *src ) { glSetEnable( GL_DEPTH_TEST, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLDepthTestEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_DEPTH_TEST ); } FORCEINLINE void GLContextGetDefault( GLDepthTestEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLDepthFunc --- FORCEINLINE void GLContextSet( GLDepthFunc_t *src ) { gGL->glDepthFunc ( src->func ); } FORCEINLINE void GLContextGet( GLDepthFunc_t *dst ) { glGetEnumv ( GL_DEPTH_FUNC, &dst->func ); } FORCEINLINE void GLContextGetDefault( GLDepthFunc_t *dst ) { dst->func = GL_GEQUAL; } // --- GLDepthMask --- FORCEINLINE void GLContextSet( GLDepthMask_t *src ) { gGL->glDepthMask ( src->mask ); } FORCEINLINE void GLContextGet( GLDepthMask_t *dst ) { gGL->glGetBooleanv ( GL_DEPTH_WRITEMASK, (GLboolean*)&dst->mask ); } FORCEINLINE void GLContextGetDefault( GLDepthMask_t *dst ) { dst->mask = GL_TRUE; } // --- GLStencilTestEnable --- FORCEINLINE void GLContextSet( GLStencilTestEnable_t *src ) { glSetEnable( GL_STENCIL_TEST, src->enable != 0 ); } FORCEINLINE void GLContextGet( GLStencilTestEnable_t *dst ) { dst->enable = gGL->glIsEnabled( GL_STENCIL_TEST ); } FORCEINLINE void GLContextGetDefault( GLStencilTestEnable_t *dst ) { dst->enable = GL_FALSE; } // --- GLStencilFunc --- FORCEINLINE void GLContextSet( GLStencilFunc_t *src ) { if (src->frontfunc == src->backfunc) gGL->glStencilFuncSeparate( GL_FRONT_AND_BACK, src->frontfunc, src->ref, src->mask); else { gGL->glStencilFuncSeparate( GL_FRONT, src->frontfunc, src->ref, src->mask); gGL->glStencilFuncSeparate( GL_BACK, src->backfunc, src->ref, src->mask); } } FORCEINLINE void GLContextGet( GLStencilFunc_t *dst ) { glGetEnumv ( GL_STENCIL_FUNC, &dst->frontfunc ); glGetEnumv ( GL_STENCIL_BACK_FUNC, &dst->backfunc ); gGL->glGetIntegerv ( GL_STENCIL_REF, &dst->ref ); gGL->glGetIntegerv ( GL_STENCIL_VALUE_MASK, (GLint*)&dst->mask ); } FORCEINLINE void GLContextGetDefault( GLStencilFunc_t *dst ) { dst->frontfunc = GL_ALWAYS; dst->backfunc = GL_ALWAYS; dst->ref = 0; dst->mask = 0xFFFFFFFF; } // --- GLStencilOp --- indexed 0=front, 1=back FORCEINLINE void GLContextSetIndexed( GLStencilOp_t *src, int index ) { GLenum face = (index==0) ? GL_FRONT : GL_BACK; gGL->glStencilOpSeparate( face, src->sfail, src->dpfail, src->dppass ); } FORCEINLINE void GLContextGetIndexed( GLStencilOp_t *dst, int index ) { glGetEnumv ( (index==0) ? GL_STENCIL_FAIL : GL_STENCIL_BACK_FAIL, &dst->sfail ); glGetEnumv ( (index==0) ? GL_STENCIL_PASS_DEPTH_FAIL : GL_STENCIL_BACK_PASS_DEPTH_FAIL, &dst->dpfail ); glGetEnumv ( (index==0) ? GL_STENCIL_PASS_DEPTH_PASS : GL_STENCIL_BACK_PASS_DEPTH_PASS, &dst->dppass ); } FORCEINLINE void GLContextGetDefaultIndexed( GLStencilOp_t *dst, int index ) { dst->sfail = dst->dpfail = dst->dppass = GL_KEEP; } // --- GLStencilWriteMask --- FORCEINLINE void GLContextSet( GLStencilWriteMask_t *src ) { gGL->glStencilMask( src->mask ); } FORCEINLINE void GLContextGet( GLStencilWriteMask_t *dst ) { gGL->glGetIntegerv ( GL_STENCIL_WRITEMASK, &dst->mask ); } FORCEINLINE void GLContextGetDefault( GLStencilWriteMask_t *dst ) { dst->mask = 0xFFFFFFFF; } // --- GLClearColor --- FORCEINLINE void GLContextSet( GLClearColor_t *src ) { gGL->glClearColor( src->r, src->g, src->b, src->a ); } FORCEINLINE void GLContextGet( GLClearColor_t *dst ) { gGL->glGetFloatv ( GL_COLOR_CLEAR_VALUE, &dst->r ); } FORCEINLINE void GLContextGetDefault( GLClearColor_t *dst ) { dst->r = dst->g = dst->b = 0.5; dst->a = 1.0; } // --- GLClearDepth --- FORCEINLINE void GLContextSet( GLClearDepth_t *src ) { gGL->glClearDepthf( src->d ); } FORCEINLINE void GLContextGet( GLClearDepth_t *dst ) { gGL->glGetFloatv( GL_DEPTH_CLEAR_VALUE, &dst->d ); } FORCEINLINE void GLContextGetDefault( GLClearDepth_t *dst ) { dst->d = 1.0; } // --- GLClearStencil --- FORCEINLINE void GLContextSet( GLClearStencil_t *src ) { gGL->glClearStencil( src->s ); } FORCEINLINE void GLContextGet( GLClearStencil_t *dst ) { gGL->glGetIntegerv ( GL_STENCIL_CLEAR_VALUE, &dst->s ); } FORCEINLINE void GLContextGetDefault( GLClearStencil_t *dst ) { dst->s = 0; } //===========================================================================// // caching state object template. One of these is instantiated in the context per unique struct type above template class GLState { public: inline GLState() { memset( &data, 0, sizeof(data) ); Default(); } FORCEINLINE void Flush() { // immediately blast out the state - it makes no sense to delta it or do anything fancy because shaderapi, dxabstract, and OpenGL itself does this for us (and OpenGL calls with multithreaded drivers are very cheap) GLContextSet( &data ); } // write: client src into cache // common case is both false. dirty is calculated, context write is deferred. FORCEINLINE void Write( const T *src ) { data = *src; Flush(); } // default: write default value to cache, optionally write through inline void Default( bool noDefer=false ) { GLContextGetDefault( &data ); // read default values directly to our cache copy Flush(); } // read: sel = 0 for cache, 1 for context inline void Read( T *dst, int sel ) { if (sel==0) *dst = data; else GLContextGet( dst ); } // check: verify that context equals cache, return true if mismatched or if illegal values seen inline bool Check ( void ) { T temp; bool result; GLContextGet( &temp ); result = !(temp == data); return result; } FORCEINLINE const T &GetData() const { return data; } protected: T data; }; // caching state object template - with multiple values behind it that are indexed template class GLStateArray { public: inline GLStateArray() { memset( &data, 0, sizeof(data) ); Default(); } // write cache->context if dirty or forced. FORCEINLINE void FlushIndex( int index ) { // immediately blast out the state - it makes no sense to delta it or do anything fancy because shaderapi, dxabstract, and OpenGL itself does this for us (and OpenGL calls with multithreaded drivers are very cheap) GLContextSetIndexed( &data[index], index ); }; // write: client src into cache // common case is both false. dirty is calculated, context write is deferred. FORCEINLINE void WriteIndex( T *src, int index ) { data[index] = *src; FlushIndex( index ); // dirty becomes false }; // write all slots in the array FORCEINLINE void Flush() { for( int i=0; i < COUNT; i++) { FlushIndex( i ); } } // default: write default value to cache, optionally write through inline void DefaultIndex( int index ) { GLContextGetDefaultIndexed( &data[index], index ); // read default values directly to our cache copy Flush(); }; inline void Default( void ) { for( int i=0; ienable != 0; } // note however that we're still tracking what this mode should be, so FlushDrawStates can look at it and adjust the pixel shader // if fake SRGB mode is in place (m_caps.m_hasGammaWrites is false) } FORCEINLINE void WriteDepthTestEnable( GLDepthTestEnable_t *src ) { m_DepthTestEnable.Write( src ); } FORCEINLINE void WriteDepthFunc( GLDepthFunc_t *src ) { m_DepthFunc.Write( src ); } FORCEINLINE void WriteDepthMask( GLDepthMask_t *src ) { m_DepthMask.Write( src ); } FORCEINLINE void WriteStencilTestEnable( GLStencilTestEnable_t *src ) { m_StencilTestEnable.Write( src ); } FORCEINLINE void WriteStencilFunc( GLStencilFunc_t *src ) { m_StencilFunc.Write( src ); } FORCEINLINE void WriteStencilOp( GLStencilOp_t *src, int which ) { m_StencilOp.WriteIndex( src, which ); } FORCEINLINE void WriteStencilWriteMask( GLStencilWriteMask_t *src ) { m_StencilWriteMask.Write( src ); } FORCEINLINE void WriteClearColor( GLClearColor_t *src ) { m_ClearColor.Write( src ); } FORCEINLINE void WriteClearDepth( GLClearDepth_t *src ) { m_ClearDepth.Write( src ); } FORCEINLINE void WriteClearStencil( GLClearStencil_t *src ) { m_ClearStencil.Write( src ); } // debug stuff void BeginFrame( void ); void EndFrame( void ); // new interactive debug stuff #if GLMDEBUG void DebugDump( GLMDebugHookInfo *info, uint options, uint vertDumpMode ); void DebugHook( GLMDebugHookInfo *info ); void DebugPresent( void ); void DebugClear( void ); #endif FORCEINLINE void SetMaxUsedVertexShaderConstantsHint( uint nMaxConstants ); FORCEINLINE DWORD GetCurrentOwnerThreadId() const { return m_nCurOwnerThreadId; } protected: friend class GLMgr; // only GLMgr can make GLMContext objects friend class GLMRendererInfo; // only GLMgr can make GLMContext objects friend class CGLMTex; // tex needs to be able to do binds friend class CGLMFBO; // fbo needs to be able to do binds friend class CGLMProgram; friend class CGLMShaderPair; friend class CGLMShaderPairCache; friend class CGLMBuffer; friend class CGLMBufferSpanManager; friend class GLMTester; // tester class needs access back into GLMContext friend struct IDirect3D9; friend struct IDirect3DDevice9; friend struct IDirect3DQuery9; // methods------------------------------------------ // old GLMContext( GLint displayMask, GLint rendererID, PseudoNSGLContextPtr nsglShareCtx ); GLMContext( IDirect3DDevice9 *pDevice, GLMDisplayParams *params ); ~GLMContext(); #ifndef OSX FORCEINLINE GLuint FindSamplerObject( const GLMTexSamplingParams &desiredParams ); #endif FORCEINLINE void SetBufAndVertexAttribPointer( uint nIndex, GLuint nGLName, GLuint stride, GLuint datatype, GLboolean normalized, GLuint nCompCount, const void *pBuf, uint nRevision ) { VertexAttribs_t &curAttribs = m_boundVertexAttribs[nIndex]; if ( nGLName != m_nBoundGLBuffer[kGLMVertexBuffer] ) { m_nBoundGLBuffer[kGLMVertexBuffer] = nGLName; gGL->glBindBuffer( GL_ARRAY_BUFFER, nGLName ); } else if ( ( curAttribs.m_pPtr == pBuf ) && ( curAttribs.m_revision == nRevision ) && ( curAttribs.m_stride == stride ) && ( curAttribs.m_datatype == datatype ) && ( curAttribs.m_normalized == normalized ) && ( curAttribs.m_nCompCount == nCompCount ) ) { return; } curAttribs.m_nCompCount = nCompCount; curAttribs.m_datatype = datatype; curAttribs.m_normalized = normalized; curAttribs.m_stride = stride; curAttribs.m_pPtr = pBuf; curAttribs.m_revision = nRevision; gGL->glVertexAttribPointer( nIndex, nCompCount, datatype, normalized, stride, pBuf ); } struct CurAttribs_t { uint m_nTotalBufferRevision; IDirect3DVertexDeclaration9 *m_pVertDecl; D3DStreamDesc m_streams[ D3D_MAX_STREAMS ]; uint64 m_vtxAttribMap[2]; }; CurAttribs_t m_CurAttribs; FORCEINLINE void ClearCurAttribs() { m_CurAttribs.m_nTotalBufferRevision = 0; m_CurAttribs.m_pVertDecl = NULL; memset( m_CurAttribs.m_streams, 0, sizeof( m_CurAttribs.m_streams ) ); m_CurAttribs.m_vtxAttribMap[0] = 0xBBBBBBBBBBBBBBBBULL; m_CurAttribs.m_vtxAttribMap[1] = 0xBBBBBBBBBBBBBBBBULL; } FORCEINLINE void ReleasedShader() { NullProgram(); } // textures FORCEINLINE void SelectTMU( int tmu ) { if ( tmu != m_activeTexture ) { gGL->glActiveTexture( GL_TEXTURE0 + tmu ); m_activeTexture = tmu; } } void BindTexToTMU( CGLMTex *tex, int tmu ); // render targets / FBO's void BindFBOToCtx( CGLMFBO *fbo, GLenum bindPoint = GL_FRAMEBUFFER ); // you can also choose GL_READ_FRAMEBUFFER_EXT / GL_DRAW_FRAMEBUFFER_EXT // buffers FORCEINLINE void BindGLBufferToCtx( GLenum nGLBufType, GLuint nGLName, bool bForce = false ) { Assert( ( nGLBufType == GL_ARRAY_BUFFER ) || ( nGLBufType == GL_ELEMENT_ARRAY_BUFFER ) ); const uint nIndex = ( nGLBufType == GL_ARRAY_BUFFER ) ? kGLMVertexBuffer : kGLMIndexBuffer; if ( ( bForce ) || ( m_nBoundGLBuffer[nIndex] != nGLName ) ) { m_nBoundGLBuffer[nIndex] = nGLName; gGL->glBindBuffer( nGLBufType, nGLName ); } } void BindBufferToCtx( EGLMBufferType type, CGLMBuffer *buff, bool force = false ); // does not twiddle any enables. FORCEINLINE void BindIndexBufferToCtx( CGLMBuffer *buff ); FORCEINLINE void BindVertexBufferToCtx( CGLMBuffer *buff ); GLuint CreateTex( GLenum texBind, GLenum internalFormat ); void CleanupTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ); void DestroyTex( GLenum texBind, GLMTexLayout* pLayout, GLuint tex ); GLuint FillTexCache( bool holdOne, int newTextures ); void PurgeTexCache( ); // debug font void GenDebugFontTex( void ); void DrawDebugText( float x, float y, float z, float drawCharWidth, float drawCharHeight, char *string ); CPersistentBuffer* GetCurPersistentBuffer( EGLMBufferType type ) { return &( m_persistentBuffer[m_nCurPersistentBuffer][type] ); } // members------------------------------------------ // context DWORD m_nCurOwnerThreadId; uint m_nThreadOwnershipReleaseCounter; bool m_bUseSamplerObjects; bool m_bTexClientStorage; IDirect3DDevice9 *m_pDevice; GLMRendererInfoFields m_caps; bool m_displayParamsValid; // is there a param block copied in yet GLMDisplayParams m_displayParams; // last known display config, either via constructor, or by SetDisplayParams... #if defined( USE_SDL ) int m_pixelFormatAttribs[100]; // more than enough PseudoNSGLContextPtr m_nsctx; void * m_ctx; #endif bool m_bUseBoneUniformBuffers; // if true, we use two uniform buffers for vertex shader constants vs. one // texture form table CGLMTexLayoutTable *m_texLayoutTable; // context state mirrors GLState m_AlphaTestEnable; GLState m_AlphaTestFunc; GLState m_CullFaceEnable; GLState m_CullFrontFace; GLState m_PolygonMode; GLState m_DepthBias; GLStateArray m_ClipPlaneEnable; GLStateArray m_ClipPlaneEquation; // dxabstract puts them directly into param slot 253(0) and 254(1) GLState m_ScissorEnable; GLState m_ScissorBox; GLState m_AlphaToCoverageEnable; GLState m_ViewportBox; GLState m_ViewportDepthRange; GLState m_ColorMaskSingle; GLStateArray m_ColorMaskMultiple; // need an official constant for the color buffers limit GLState m_BlendEnable; GLState m_BlendFactor; GLState m_BlendEquation; GLState m_BlendColor; GLState m_BlendEnableSRGB; // write to this one to transmit intent to write SRGB encoded pixels to drawing FB bool m_FakeBlendEnableSRGB; // writes to above will be shunted here if fake SRGB is in effect. GLState m_DepthTestEnable; GLState m_DepthFunc; GLState m_DepthMask; GLState m_StencilTestEnable; // global stencil test enable GLState m_StencilFunc; // holds front and back stencil funcs GLStateArray m_StencilOp; // indexed: 0=front 1=back GLState m_StencilWriteMask; GLState m_ClearColor; GLState m_ClearDepth; GLState m_ClearStencil; // texture bindings and sampler setup int m_activeTexture; // mirror for glActiveTexture GLMTexSampler m_samplers[GLM_SAMPLER_COUNT]; uint8 m_nDirtySamplerFlags[GLM_SAMPLER_COUNT]; // 0 if the sampler is dirty, 1 if not uint32 m_nNumDirtySamplers; // # of unique dirty sampler indices in m_nDirtySamplers uint8 m_nDirtySamplers[GLM_SAMPLER_COUNT + 1]; // dirty sampler indices void MarkAllSamplersDirty(); struct SamplerHashEntry { GLuint m_samplerObject; GLMTexSamplingParams m_params; }; enum { cSamplerObjectHashBits = 9, cSamplerObjectHashSize = 1 << cSamplerObjectHashBits }; SamplerHashEntry m_samplerObjectHash[cSamplerObjectHashSize]; uint m_nSamplerObjectHashNumEntries; // texture lock tracking - CGLMTex objects share usage of this CUtlVector< GLMTexLockDesc > m_texLocks; // render target binding - check before draw // similar to tex sampler mechanism, we track "bound" from "chosen for drawing" separately, // so binding for creation/setup need not disrupt any notion of what will be used at draw time CGLMFBO *m_boundDrawFBO; // FBO on GL_DRAW_FRAMEBUFFER bind point CGLMFBO *m_boundReadFBO; // FBO on GL_READ_FRAMEBUFFER bind point // ^ both are set if you bind to GL_FRAMEBUFFER_EXT CGLMFBO *m_drawingFBO; // what FBO should be bound at draw time (to both read/draw bp's). CGLMFBO *m_blitReadFBO; CGLMFBO *m_blitDrawFBO; // scratch FBO's for framebuffer blit CGLMFBO *m_scratchFBO[ kGLMScratchFBOCount ]; // general purpose FBO's for internal use CUtlVector< CGLMFBO* > m_fboTable; // each live FBO goes in the table uint m_fragDataMask; // program bindings EGLMProgramLang m_drawingLangAtFrameStart; // selector for start of frame (spills into m_drawingLang) EGLMProgramLang m_drawingLang; // selector for which language we desire to draw with on the next batch CGLMProgram *m_drawingProgram[ kGLMNumProgramTypes ]; bool m_bDirtyPrograms; GLMProgramParamsF m_programParamsF[ kGLMNumProgramTypes ]; GLMProgramParamsB m_programParamsB[ kGLMNumProgramTypes ]; GLMProgramParamsI m_programParamsI[ kGLMNumProgramTypes ]; // two banks, but only the vertex one is used EGLMParamWriteMode m_paramWriteMode; CGLMProgram *m_pNullFragmentProgram; // write opaque black. Activate when caller asks for null FP CGLMProgram *m_preloadTexVertexProgram; // programs to help preload textures (dummies) CGLMProgram *m_preload2DTexFragmentProgram; CGLMProgram *m_preload3DTexFragmentProgram; CGLMProgram *m_preloadCubeTexFragmentProgram; #if defined( OSX ) && defined( GLMDEBUG ) CGLMProgram *m_boundProgram[ kGLMNumProgramTypes ]; #endif CGLMShaderPairCache *m_pairCache; // GLSL only CGLMShaderPair *m_pBoundPair; // GLSL only FORCEINLINE void NewLinkedProgram() { ClearCurAttribs(); } //uint m_boundPairRevision; // GLSL only //GLhandleARB m_boundPairProgram; // GLSL only // buffer bindings GLuint m_nBoundGLBuffer[kGLMNumBufferTypes]; struct VertexAttribs_t { GLuint m_nCompCount; GLenum m_datatype; GLboolean m_normalized; GLuint m_stride; const void *m_pPtr; uint m_revision; }; VertexAttribs_t m_boundVertexAttribs[ kGLMVertexAttributeIndexMax ]; // tracked per attrib for dupe-set-absorb uint m_lastKnownVertexAttribMask; // tracked for dupe-enable-absorb int m_nNumSetVertexAttributes; // FIXME: Remove this, it's no longer used GLMVertexSetup m_drawVertexSetup; EGLMAttribWriteMode m_attribWriteMode; bool m_slowCheckEnable; // turn this on or no native checking is done ("-glmassertslow" or "-glmsspewslow") bool m_slowAssertEnable; // turn this on to assert on a non-native batch "-glmassertslow" bool m_slowSpewEnable; // turn this on to log non-native batches to stdout "-glmspewslow" bool m_checkglErrorsAfterEveryBatch; // turn this on to check for GL errors after each batch (slow) ("-glcheckerrors") // debug font texture CGLMTex *m_debugFontTex; // might be NULL unless you call GenDebugFontTex CGLMBuffer *m_debugFontIndices; // up to 1024 indices (256 chars times 4) CGLMBuffer *m_debugFontVertices; // up to 1024 verts // batch/frame debugging support int m_debugFrameIndex; // init to -1. Increment at BeginFrame int m_nMaxUsedVertexProgramConstantsHint; uint32 m_dwRenderThreadId; volatile bool m_bIsThreading; uint m_nCurFrame; uint m_nBatchCounter; struct TextureEntry_t { GLenum m_nTexBind; GLenum m_nInternalFormat; GLuint m_nTexName; }; GLuint m_destroyPBO; CUtlVector< TextureEntry_t > m_availableTextures; enum { cNumPersistentBuffers = 3 }; CPersistentBuffer m_persistentBuffer[cNumPersistentBuffers][kGLMNumBufferTypes]; uint m_nCurPersistentBuffer; void SaveColorMaskAndSetToDefault(); void RestoreSavedColorMask(); GLColorMaskSingle_t m_SavedColorMask; #if GLMDEBUG // interactive (DebugHook) debug support // using these you can implement frame advance, batch single step, and batch rewind (let it run til next frame and hold on prev batch #) int m_holdFrameBegin; // -1 if no hold req'd, otherwise # of frame to hold at (at beginframe time) int m_holdFrameEnd; // -1 if no hold req'd, otherwise # of frame to hold at (at endframe time) int m_holdBatch,m_holdBatchFrame; // -1 if no hold, else # of batch&frame to hold at (both must be set) // these can be expired/cleared to -1 if the frame passes without a hit // may be desirable to re-pause in that event, as user was expecting a hold to occur bool m_debugDelayEnable; // allow sleep delay uint m_debugDelay; // sleep time per hook call in microseconds (for usleep()) // pre-draw global toggles / options bool m_autoClearColor,m_autoClearDepth,m_autoClearStencil; float m_autoClearColorValues[4]; // debug knobs int m_selKnobIndex; float m_selKnobMinValue,m_selKnobMaxValue,m_selKnobIncrement; #endif #if GL_BATCH_PERF_ANALYSIS uint m_nTotalVSUniformCalls; uint m_nTotalVSUniformBoneCalls; uint m_nTotalVSUniformsSet; uint m_nTotalVSUniformsBoneSet; uint m_nTotalPSUniformCalls; uint m_nTotalPSUniformsSet; CFlushDrawStatesStats m_FlushStats; #endif void ProcessTextureDeletes(); CTSQueue m_DeleteTextureQueue; }; #ifndef OSX FORCEINLINE void GLMContext::DrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf ) { #if GL_ENABLE_INDEX_VERIFICATION DrawRangeElementsNonInline( mode, start, end, count, type, indices, baseVertex, pIndexBuf ); #else #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 GLMDEBUG #if 0 bool hasVP = m_drawingProgram[ kGLMVertexProgram ] != NULL; bool hasFP = m_drawingProgram[ kGLMFragmentProgram ] != NULL; // 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 //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_drawingProgram[ kGLMVertexProgram ]) { m_drawingProgram[ kGLMVertexProgram ]->SyncWithEditable(); } else { AssertOnce(!"drawing with no vertex program bound"); } if (m_drawingProgram[ kGLMFragmentProgram ]) { m_drawingProgram[ kGLMFragmentProgram ]->SyncWithEditable(); } else { AssertOnce(!"drawing with no fragment program bound"); } #endif // do the drawing if (hasVP && hasFP) { gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex ); if ( m_slowCheckEnable ) { CheckNative(); } } this->DebugHook( &info ); } while ( info.m_loop ); #else Assert( m_drawingLang == kGLMGLSL ); if ( m_pBoundPair ) { gGL->glDrawRangeElementsBaseVertex( mode, start, end, count, type, indicesActual, baseVertex ); #if GLMDEBUG if ( m_slowCheckEnable ) { CheckNative(); } #endif } #endif #endif // GL_ENABLE_INDEX_VERIFICATION } #endif // #ifndef OSX FORCEINLINE void GLMContext::SetVertexProgram( CGLMProgram *pProg ) { m_drawingProgram[kGLMVertexProgram] = pProg; m_bDirtyPrograms = true; } FORCEINLINE void GLMContext::SetFragmentProgram( CGLMProgram *pProg ) { m_drawingProgram[kGLMFragmentProgram] = pProg ? pProg : m_pNullFragmentProgram; m_bDirtyPrograms = true; } // "slot" means a vec4-sized thing // these write into .env parameter space FORCEINLINE void GLMContext::SetProgramParametersF( EGLMProgramType type, uint baseSlot, float *slotData, uint slotCount ) { #if GLMDEBUG GLM_FUNC; #endif Assert( baseSlot < kGLMProgramParamFloat4Limit ); Assert( baseSlot+slotCount <= kGLMProgramParamFloat4Limit ); #if GLMDEBUG GLMPRINTF(("-S-GLMContext::SetProgramParametersF %s slots %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + slotCount - 1 )); for( uint i=0; i DXABSTRACT_VS_LAST_BONE_SLOT ) { m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty - maxBoneSlots ); m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots ); } else if ( firstDirty >= DXABSTRACT_VS_FIRST_BONE_SLOT ) { m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = DXABSTRACT_VS_LAST_BONE_SLOT + 1; m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, DXABSTRACT_VS_FIRST_BONE_SLOT ); m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots ); } else { int nNumActualBones = ( DXABSTRACT_VS_LAST_BONE_SLOT + 1 ) - DXABSTRACT_VS_FIRST_BONE_SLOT; m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone, nNumActualBones ); m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty ); m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater - maxBoneSlots ); } } } else { m_programParamsF[type].m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[type].m_dirtySlotHighWaterNonBone, (int)(baseSlot + slotCount) ); m_programParamsF[type].m_firstDirtySlotNonBone = MIN( m_programParamsF[type].m_firstDirtySlotNonBone, (int)baseSlot ); } } FORCEINLINE void GLMContext::SetProgramParametersB( EGLMProgramType type, uint baseSlot, int *slotData, uint boolCount ) { #if GLMDEBUG GLM_FUNC; #endif Assert( m_drawingLang == kGLMGLSL ); Assert( type==kGLMVertexProgram || type==kGLMFragmentProgram ); Assert( baseSlot < kGLMProgramParamBoolLimit ); Assert( baseSlot+boolCount <= kGLMProgramParamBoolLimit ); #if GLMDEBUG GLMPRINTF(("-S-GLMContext::SetProgramParametersB %s bools %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + boolCount - 1 )); for( uint i=0; i m_programParamsB[type].m_dirtySlotCount) m_programParamsB[type].m_dirtySlotCount = baseSlot+boolCount; } FORCEINLINE void GLMContext::SetProgramParametersI( EGLMProgramType type, uint baseSlot, int *slotData, uint slotCount ) // groups of 4 ints... { #if GLMDEBUG GLM_FUNC; #endif Assert( m_drawingLang == kGLMGLSL ); Assert( type==kGLMVertexProgram ); Assert( baseSlot < kGLMProgramParamInt4Limit ); Assert( baseSlot+slotCount <= kGLMProgramParamInt4Limit ); #if GLMDEBUG GLMPRINTF(("-S-GLMContext::SetProgramParametersI %s slots %d - %d: ", (type==kGLMVertexProgram) ? "VS" : "FS", baseSlot, baseSlot + slotCount - 1 )); for( uint i=0; i m_programParamsI[type].m_dirtySlotCount) { m_programParamsI[type].m_dirtySlotCount = baseSlot + slotCount; } } FORCEINLINE void GLMContext::SetSamplerDirty( int sampler ) { Assert( sampler < GLM_SAMPLER_COUNT ); m_nDirtySamplers[m_nNumDirtySamplers] = sampler; m_nNumDirtySamplers += m_nDirtySamplerFlags[sampler]; m_nDirtySamplerFlags[sampler] = 0; } FORCEINLINE void GLMContext::SetSamplerTex( int sampler, CGLMTex *tex ) { Assert( sampler < GLM_SAMPLER_COUNT ); m_samplers[sampler].m_pBoundTex = tex; if ( tex ) { if ( !gGL->m_bHave_GL_EXT_direct_state_access ) { if ( sampler != m_activeTexture ) { gGL->glActiveTexture( GL_TEXTURE0 + sampler ); m_activeTexture = sampler; } gGL->glBindTexture( tex->m_texGLTarget, tex->m_texName ); } else { gGL->glBindMultiTextureEXT( GL_TEXTURE0 + sampler, tex->m_texGLTarget, tex->m_texName ); } } if ( !m_bUseSamplerObjects ) { SetSamplerDirty( sampler ); } } FORCEINLINE void GLMContext::SetSamplerMinFilter( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_minFilter = Value; } FORCEINLINE void GLMContext::SetSamplerMagFilter( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_magFilter = Value; } FORCEINLINE void GLMContext::SetSamplerMipFilter( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_mipFilter = Value; } FORCEINLINE void GLMContext::SetSamplerAddressU( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); m_samplers[sampler].m_samp.m_packed.m_addressU = Value; } FORCEINLINE void GLMContext::SetSamplerAddressV( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); m_samplers[sampler].m_samp.m_packed.m_addressV = Value; } FORCEINLINE void GLMContext::SetSamplerAddressW( int sampler, GLenum Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); m_samplers[sampler].m_samp.m_packed.m_addressW = Value; } FORCEINLINE void GLMContext::SetSamplerStates( int sampler, GLenum AddressU, GLenum AddressV, GLenum AddressW, GLenum minFilter, GLenum magFilter, GLenum mipFilter, int minLod, float lodBias ) { Assert( AddressU < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); Assert( AddressV < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); Assert( AddressW < ( 1 << GLM_PACKED_SAMPLER_PARAMS_ADDRESS_BITS) ); Assert( minFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_FILTER_BITS ) ); Assert( magFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAG_FILTER_BITS ) ); Assert( mipFilter < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIP_FILTER_BITS ) ); Assert( minLod < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS ) ); GLMTexSamplingParams ¶ms = m_samplers[sampler].m_samp; params.m_packed.m_addressU = AddressU; params.m_packed.m_addressV = AddressV; params.m_packed.m_addressW = AddressW; params.m_packed.m_minFilter = minFilter; params.m_packed.m_magFilter = magFilter; params.m_packed.m_mipFilter = mipFilter; params.m_packed.m_minLOD = minLod; params.m_lodBias = lodBias; } FORCEINLINE void GLMContext::SetSamplerBorderColor( int sampler, DWORD Value ) { m_samplers[sampler].m_samp.m_borderColor = Value; } FORCEINLINE void GLMContext::SetSamplerMipMapLODBias( int sampler, DWORD Value ) { typedef union { DWORD asDword; float asFloat; } Convert_t; Convert_t c; c.asDword = Value; m_samplers[sampler].m_samp.m_lodBias = c.asFloat; } FORCEINLINE void GLMContext::SetSamplerMaxMipLevel( int sampler, DWORD Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MIN_LOD_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_minLOD = Value; } FORCEINLINE void GLMContext::SetSamplerMaxAnisotropy( int sampler, DWORD Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_MAX_ANISO_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_maxAniso = Value; } FORCEINLINE void GLMContext::SetSamplerSRGBTexture( int sampler, DWORD Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_SRGB_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_srgb = Value; } FORCEINLINE void GLMContext::SetShadowFilter( int sampler, DWORD Value ) { Assert( Value < ( 1 << GLM_PACKED_SAMPLER_PARAMS_COMPARE_MODE_BITS ) ); m_samplers[sampler].m_samp.m_packed.m_compareMode = Value; } FORCEINLINE void GLMContext::BindIndexBufferToCtx( CGLMBuffer *buff ) { GLMPRINTF(( "--- GLMContext::BindIndexBufferToCtx buff %p, GL name %d", buff, (buff) ? buff->m_nHandle : -1 )); Assert( !buff || ( buff->m_buffGLTarget == GL_ELEMENT_ARRAY_BUFFER ) ); GLuint nGLName = buff ? buff->GetHandle() : 0; if ( m_nBoundGLBuffer[ kGLMIndexBuffer] == nGLName ) return; m_nBoundGLBuffer[ kGLMIndexBuffer] = nGLName; gGL->glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, nGLName ); } FORCEINLINE void GLMContext::BindVertexBufferToCtx( CGLMBuffer *buff ) { GLMPRINTF(( "--- GLMContext::BindVertexBufferToCtx buff %p, GL name %d", buff, (buff) ? buff->m_nHandle : -1 )); Assert( !buff || ( buff->m_buffGLTarget == GL_ARRAY_BUFFER ) ); GLuint nGLName = buff ? buff->GetHandle() : 0; if ( m_nBoundGLBuffer[ kGLMVertexBuffer] == nGLName ) return; m_nBoundGLBuffer[ kGLMVertexBuffer] = nGLName; gGL->glBindBuffer( GL_ARRAY_BUFFER, nGLName ); } FORCEINLINE void GLMContext::SetMaxUsedVertexShaderConstantsHint( uint nMaxConstants ) { static bool bUseMaxVertexShadeConstantHints = !CommandLine()->CheckParm("-disablemaxvertexshaderconstanthints"); if ( bUseMaxVertexShadeConstantHints ) { m_nMaxUsedVertexProgramConstantsHint = nMaxConstants; } } struct GLMTestParams { GLMContext *m_ctx; int *m_testList; // -1 termed bool m_glErrToDebugger; bool m_glErrToConsole; bool m_intlErrToDebugger; bool m_intlErrToConsole; int m_frameCount; // how many frames to test. }; class GLMTester { public: GLMTester(GLMTestParams *params); ~GLMTester(); // optionally callable by test routines to get basic drawables wired up void StdSetup( void ); void StdCleanup( void ); // callable by test routines to clear the frame or present it void Clear( void ); void Present( int seed ); // error reporting void CheckGLError( const char *comment ); // obey m_params setting for console / debugger response void InternalError( int errcode, char *comment ); // if errcode!=0, obey m_params setting for console / debugger response void RunTests(); void RunOneTest( int testindex ); // test routines themselves void Test0(); void Test1(); void Test2(); void Test3(); GLMTestParams m_params; // copy of caller's params, do not mutate... // std-setup stuff int m_drawWidth, m_drawHeight; CGLMFBO *m_drawFBO; CGLMTex *m_drawColorTex; CGLMTex *m_drawDepthTex; }; class CShowPixelsParams { public: GLuint m_srcTexName; int m_width,m_height; bool m_vsyncEnable; bool m_fsEnable; // want receiving view to be full screen. for now, just target the main screen. extend later. bool m_useBlit; // use FBO blit - sending context says it is available. bool m_noBlit; // the back buffer has already been populated by the caller (perhaps via direct MSAA resolve from multisampled RT tex) bool m_onlySyncView; // react to full/windowed state change only, do not present bits }; #define kMaxCrawlFrames 100 #define kMaxCrawlText (kMaxCrawlFrames * 256) class CStackCrawlParams { public: uint m_frameLimit; // input: max frames to retrieve uint m_frameCount; // output: frames found void *m_crawl[kMaxCrawlFrames]; // call site addresses char *m_crawlNames[kMaxCrawlFrames]; // pointers into text following, one per decoded name char m_crawlText[kMaxCrawlText]; }; #endif // GLMGR_H