//============ Copyright (c) Valve Corporation, All rights reserved. ============ // // glmgr.h // singleton class, common basis for managing GL contexts // responsible for tracking adapters and contexts // //=============================================================================== #ifndef GLMGR_H #define GLMGR_H #pragma once #include "glmgr/glmdebug.h" #include "glmgr/glmdisplay.h" #include "glmgr/glmgrext.h" #include "glmgr/glmgrbasics.h" #include "glmgr/cglmtex.h" #include "glmgr/cglmfbo.h" #include "glmgr/cglmprogram.h" #include "glmgr/cglmbuffer.h" #include "glmgr/cglmquery.h" //=============================================================================== // glue to call out to Obj-C land (these are in glmgrcocoa.mm) bool NewNSGLContext( unsigned long *attribs, PseudoNSGLContextPtr nsglShareCtx, PseudoNSGLContextPtr *nsglCtxOut, CGLContextObj *cglCtxOut ); CGLContextObj GetCGLContextFromNSGL( PseudoNSGLContextPtr nsglCtx ); void DelNSGLContext( PseudoNSGLContextPtr nsglCtx ); //=============================================================================== // 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( 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 inline void glSetEnable( GLenum which, bool enable ) { if (enable) glEnable(which); else glDisable(which); } // helper function for int vs enum clarity inline void glGetEnumv( GLenum which, GLenum *dst ) { 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; bool operator==(const GLAlphaTestEnable_t& src) const { return EQ(enable); } }; struct GLAlphaTestFunc_t { GLenum func; GLclampf ref; bool operator==(const GLAlphaTestFunc_t& src) const { return EQ(func) && EQ(ref); } }; struct GLCullFaceEnable_t { GLint enable; bool operator==(const GLCullFaceEnable_t& src) const { return EQ(enable); } }; struct GLCullFrontFace_t { GLenum value; bool operator==(const GLCullFrontFace_t& src) const { return EQ(value); } }; struct GLPolygonMode_t { GLenum values[2]; bool operator==(const GLPolygonMode_t& src) const { return EQ(values[0]) && EQ(values[1]); } }; struct GLDepthBias_t { GLfloat factor; GLfloat units; bool operator==(const GLDepthBias_t& src) const { return EQ(factor) && EQ(units); } }; struct GLScissorEnable_t { GLint enable; bool operator==(const GLScissorEnable_t& src) const { return EQ(enable); } }; struct GLScissorBox_t { GLint x,y; GLsizei width, height; bool operator==(const GLScissorBox_t& src) const { return EQ(x) && EQ(y) && EQ(width) && EQ(height); } }; struct GLAlphaToCoverageEnable_t{ GLint enable; bool operator==(const GLAlphaToCoverageEnable_t& src) const { return EQ(enable); } }; struct GLViewportBox_t { GLint x,y; GLsizei width, height; bool operator==(const GLViewportBox_t& src) const { return EQ(x) && EQ(y) && EQ(width) && EQ(height); } }; struct GLViewportDepthRange_t { GLdouble near,far; bool operator==(const GLViewportDepthRange_t& src) const { return EQ(near) && EQ(far); } }; struct GLClipPlaneEnable_t { GLint enable; bool operator==(const GLClipPlaneEnable_t& src) const { return EQ(enable); } }; struct GLClipPlaneEquation_t { GLfloat x,y,z,w; bool operator==(const GLClipPlaneEquation_t& src) const { return EQ(x) && EQ(y) && EQ(z) && EQ(w); } }; //blend struct GLColorMaskSingle_t { char r,g,b,a; bool operator==(const GLColorMaskSingle_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLColorMaskMultiple_t { char r,g,b,a; bool operator==(const GLColorMaskMultiple_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLBlendEnable_t { GLint enable; bool operator==(const GLBlendEnable_t& src) const { return EQ(enable); } }; struct GLBlendFactor_t { GLenum srcfactor,dstfactor; bool operator==(const GLBlendFactor_t& src) const { return EQ(srcfactor) && EQ(dstfactor); } }; struct GLBlendEquation_t { GLenum equation; bool operator==(const GLBlendEquation_t& src) const { return EQ(equation); } }; struct GLBlendColor_t { GLfloat r,g,b,a; bool operator==(const GLBlendColor_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLBlendEnableSRGB_t { GLint enable; bool operator==(const GLBlendEnableSRGB_t& src) const { return EQ(enable); } }; //depth struct GLDepthTestEnable_t { GLint enable; bool operator==(const GLDepthTestEnable_t& src) const { return EQ(enable); } }; struct GLDepthFunc_t { GLenum func; bool operator==(const GLDepthFunc_t& src) const { return EQ(func); } }; struct GLDepthMask_t { char mask; bool operator==(const GLDepthMask_t& src) const { return EQ(mask); } }; //stencil struct GLStencilTestEnable_t { GLint enable; bool operator==(const GLStencilTestEnable_t& src) const { return EQ(enable); } }; struct GLStencilFunc_t { GLenum frontfunc, backfunc; GLint ref; GLuint mask; 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; bool operator==(const GLStencilOp_t& src) const { return EQ(sfail) && EQ(dpfail) && EQ(dppass); } }; struct GLStencilWriteMask_t { GLint mask; bool operator==(const GLStencilWriteMask_t& src) const { return EQ(mask); } }; //clearing struct GLClearColor_t { GLfloat r,g,b,a; bool operator==(const GLClearColor_t& src) const { return EQ(r) && EQ(g) && EQ(b) && EQ(a); } }; struct GLClearDepth_t { GLdouble d; bool operator==(const GLClearDepth_t& src) const { return EQ(d); } }; struct GLClearStencil_t { GLint s; 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 ); //===========================================================================// // caching state object template. One of these is instantiated in the context per unique struct type above template class GLState { public: GLState() { dirty = false; memset( &data, 0, sizeof(data) ); }; // write: client src into cache // common case is both false. dirty is calculated, context write is deferred. void Write( T *src, bool noCompare=false, bool noDefer=false ) { if (noCompare) { dirty = true; } else { // only == is implemented, so test for equal and negate // note, you only set dirty if mismatch, you never clear it until flush if ( !(data == *src) ) { dirty = true; } } data = *src; if (noDefer) { Flush( true ); // dirty becomes false } }; // write cache->context if dirty or forced. void Flush( bool noDefer=false ) { if (dirty || noDefer) { GLContextSet( &data ); GLMCheckError(); // good place for some error checking here dirty = false; } }; // default: write default value to cache, optionally write through void Default( bool noDefer=false ) { GLContextGetDefault( &data ); // read default values directly to our cache copy dirty = true; Flush(noDefer); }; // read: sel = 0 for cache, 1 for context void Read( T *dst, int sel ) { if (sel==0) { *dst = data; } else { GLContextGet( dst ); GLMCheckError(); } }; // check: verify that context equals cache, return true if mismatched or if illegal values seen bool Check ( void ) { T temp; bool result; GLContextGet( &temp ); GLMCheckError(); result = !(temp == data); return result; }; protected: T data; bool dirty; }; // caching state object template - with multiple values behind it that are indexed template class GLStateArray { public: GLStateArray() { memset( &dirty, 0, sizeof(dirty) ); memset( &data, 0, sizeof(data) ); }; // write: client src into cache // common case is both false. dirty is calculated, context write is deferred. void WriteIndex( T *src, int index, bool noCompare=false, bool noDefer=false ) { if (noCompare) { dirty[index] = true; } else { // only == is implemented, so test for equal and negate // note, you only set dirty if mismatch, you never clear it until flush if (! (data[index] == *src) ) { dirty[index] = true; } } data[index] = *src; if (noDefer) { FlushIndex( index, true ); // dirty becomes false } }; // write cache->context if dirty or forced. void FlushIndex( int index, bool noDefer=false ) { if (dirty[index] || noDefer) { GLContextSetIndexed( &data[index], index ); GLMCheckError(); dirty[index] = false; } }; // write all slots in the array void Flush( bool noDefer=false ) { for( int i=0; i 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]; // 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 // 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 ]; GLMProgramParamsF m_programParamsF[ kGLMNumProgramTypes ]; GLMProgramParamsB m_programParamsB[ kGLMNumProgramTypes ]; // two banks, but only the vertex one is used GLMProgramParamsI m_programParamsI[ kGLMNumProgramTypes ]; // two banks, but only the vertex one is used EGLMParamWriteMode m_paramWriteMode; CGLMProgram *m_nullFragmentProgram; // 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; CGLMProgram *m_boundProgram[ kGLMNumProgramTypes ]; CGLMShaderPairCache *m_pairCache; // GLSL only CGLMShaderPair *m_boundPair; // GLSL only uint m_boundPairRevision; // GLSL only GLhandleARB m_boundPairProgram; // GLSL only // buffer bindings CGLMBuffer *m_lastKnownBufferBinds[ kGLMNumBufferTypes ]; // tracked per bind point for dupe-bind-absorb GLMVertexAttributeDesc m_lastKnownVertexAttribs[ kGLMVertexAttributeIndexMax ]; // tracked per attrib for dupe-set-absorb uint m_lastKnownVertexAttribMask; // tracked for dupe-enable-absorb CGLMBuffer *m_drawIndexBuffer; // ... ? do we need dupe tracking for index buffer setup? ? 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" // 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_debugBatchIndex; // init to -1. Increment at any draw call #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 }; 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; }; #endif