//========= 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 "cglmbuffer.h" #include "cglmfbo.h" #include "cglmprogram.h" #include "cglmquery.h" #include "cglmtex.h" #include "glbase.h" #include "glentrypoints.h" #include "glmdebug.h" #include "glmdisplay.h" #include "glmgrbasics.h" #include "glmgrext.h" #include "dxabstract_types.h" #include "materialsystem/IShader.h" #include "tier0/icommandline.h" #include "tier0/vprof_telemetry.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 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 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 { GLdouble 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 { 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 { 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 { GLdouble 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 // --- // GLAlphaTestEnable // --- FORCEINLINE void GLContextSet(GLAlphaTestEnable_t *src) { glSetEnable(GL_ALPHA_TEST, src->enable != 0); } FORCEINLINE void GLContextGet(GLAlphaTestEnable_t *dst) { dst->enable = gGL->glIsEnabled(GL_ALPHA_TEST); } FORCEINLINE void GLContextGetDefault(GLAlphaTestEnable_t *dst) { dst->enable = GL_FALSE; } // --- // GLAlphaTestFunc // --- FORCEINLINE void GLContextSet(GLAlphaTestFunc_t *src) { gGL->glAlphaFunc(src->func, src->ref); } FORCEINLINE void GLContextGet(GLAlphaTestFunc_t *dst) { glGetEnumv(GL_ALPHA_TEST_FUNC, &dst->func); gGL->glGetFloatv(GL_ALPHA_TEST_REF, &dst->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_ARB, src->enable != 0); } FORCEINLINE void GLContextGet(GLAlphaToCoverageEnable_t *dst) { dst->enable = gGL->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); } 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->glDepthRange(src->flNear, src->flFar); } FORCEINLINE void GLContextGet(GLViewportDepthRange_t *dst) { gGL->glGetDoublev(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) { gGL->glColorMaskIndexedEXT(index, src->r, src->g, src->b, src->a); } FORCEINLINE void GLContextGetIndexed(GLColorMaskMultiple_t *dst, int index) { gGL->glGetBooleanIndexedvEXT(GL_COLOR_WRITEMASK, index, (GLboolean *)&dst->r); } 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 = 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->glClearDepth(src->d); } FORCEINLINE void GLContextGet(GLClearDepth_t *dst) { gGL->glGetDoublev(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; i < COUNT; i++) { DefaultIndex(i); } } // read: sel = 0 for cache, 1 for context inline void ReadIndex(T *dst, int index, int sel) { if (sel == 0) *dst = data[index]; else GLContextGetIndexed(dst, index); }; // check: verify that context equals cache, return true if mismatched or if // illegal values seen inline bool CheckIndex(int index) { T temp; bool result; GLContextGetIndexed(&temp, index); result = !(temp == data[index]); return result; }; inline bool Check(void) { // T temp; bool result = false; for (int i = 0; i < COUNT; i++) { result |= CheckIndex(i); } return result; }; protected: T data[COUNT]; }; //===========================================================================// struct GLMTexSampler { CGLMTex *m_pBoundTex; // tex which is actually bound now GLMTexSamplingParams m_samp; // current 2D sampler state }; // GLMContext will maintain one of these structures inside the context to // represent the current state. Client can supply a new one when it wants to // change the setup. // FIXME GLMContext can do the work to migrate from old setup to new setup as // efficiently as possible (but it doesn't yet) struct GLMVertexSetup { uint m_attrMask; // which attrs are enabled (1<( (reinterpret_cast(m_pRawBuf) + 65535) & (~65535)); m_nSize = nSize; m_nOfs = 0; gGL->glGenBuffersARB(1, &m_nBufferObj); gGL->glBindBufferARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nBufferObj); gGL->glBufferDataARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nSize, m_pBuf, GL_STREAM_COPY); return true; } void Deinit() { if (!m_pRawBuf) return; BlockUntilNotBusy(); gGL->glBindBufferARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_nBufferObj); gGL->glBufferDataARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0, (void *)NULL, GL_STREAM_COPY); gGL->glBindBufferARB(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); gGL->glDeleteBuffersARB(1, &m_nBufferObj); m_nBufferObj = 0; free(m_pRawBuf); m_pRawBuf = NULL; m_pBuf = NULL; m_nSize = 0; m_nOfs = 0; } inline uint GetSize() const { return m_nSize; } inline uint GetOfs() const { return m_nOfs; } inline uint GetBytesRemaining() const { return m_nSize - m_nOfs; } inline void *GetPtr() const { return m_pBuf; } inline GLuint GetHandle() const { return m_nBufferObj; } void InsertFence() { #ifdef HAVE_GL_ARB_SYNC if (m_nSyncObj) { gGL->glDeleteSync(m_nSyncObj); } m_nSyncObj = gGL->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); #endif } void BlockUntilNotBusy() { #ifdef HAVE_GL_ARB_SYNC if (m_nSyncObj) { gGL->glClientWaitSync(m_nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 3000000000000ULL); gGL->glDeleteSync(m_nSyncObj); m_nSyncObj = 0; } #endif m_nOfs = 0; } void Append(uint nSize) { m_nOfs += nSize; Assert(m_nOfs <= m_nSize); } private: void *m_pRawBuf; void *m_pBuf; uint m_nSize; uint m_nOfs; GLuint m_nBufferObj; #ifdef HAVE_GL_ARB_SYNC GLsync m_nSyncObj; #endif }; #endif // !OSX //===========================================================================// class GLMContext { public: // set/check current context (perq for many other calls) void MakeCurrent(bool bRenderThread = false); void ReleaseCurrent(bool bRenderThread = false); // CheckCurrent has been removed (it no longer compiled on Linux). To // minimize churn I'm leaving the inline NOP version. DO NOT change this to // non-inlined. It's called all over the place from very hot codepaths. FORCEINLINE void CheckCurrent(void) {} void PopulateCaps(void); // fill out later portions of renderer info record // which need context queries void DumpCaps( void); // printf all the caps info (you can call this in release too) const GLMRendererInfoFields &Caps(void); // peek at the caps record // state cache/mirror void SetDefaultStates(void); void ForceFlushStates(); void VerifyStates(void); // textures // Lock and Unlock reqs go directly to the tex object CGLMTex *NewTex(GLMTexLayoutKey *key, uint levels = 1, const char *debugLabel = NULL); void DelTex(CGLMTex *tex); // options for Blit (replacement for ResolveTex and BlitTex) // pass NULL for dstTex if you want to target GL_BACK with the blit. You // get y-flip with that, don't change the dstrect yourself. void Blit2(CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter); // tex blit (via FBO blit) void BlitTex(CGLMTex *srcTex, GLMRect *srcRect, int srcFace, int srcMip, CGLMTex *dstTex, GLMRect *dstRect, int dstFace, int dstMip, uint filter, bool useBlitFB = true); // MSAA resolve - we do this in GLMContext because it has to do a bunch of //FBO/blit gymnastics void ResolveTex(CGLMTex *tex, bool forceDirty = false); // texture pre-load (residency forcing) - normally done one-time but you can // force it void PreloadTex(CGLMTex *tex, bool force = false); // samplers FORCEINLINE void SetSamplerTex(int sampler, CGLMTex *tex); FORCEINLINE void SetSamplerDirty(int sampler); FORCEINLINE void SetSamplerMinFilter(int sampler, GLenum Value); FORCEINLINE void SetSamplerMagFilter(int sampler, GLenum Value); FORCEINLINE void SetSamplerMipFilter(int sampler, GLenum Value); FORCEINLINE void SetSamplerAddressU(int sampler, GLenum Value); FORCEINLINE void SetSamplerAddressV(int sampler, GLenum Value); FORCEINLINE void SetSamplerAddressW(int sampler, GLenum Value); FORCEINLINE void SetSamplerStates(int sampler, GLenum AddressU, GLenum AddressV, GLenum AddressW, GLenum minFilter, GLenum magFilter, GLenum mipFilter); FORCEINLINE void SetSamplerBorderColor(int sampler, DWORD Value); FORCEINLINE void SetSamplerMipMapLODBias(int sampler, DWORD Value); FORCEINLINE void SetSamplerMaxMipLevel(int sampler, DWORD Value); FORCEINLINE void SetSamplerMaxAnisotropy(int sampler, DWORD Value); FORCEINLINE void SetSamplerSRGBTexture(int sampler, DWORD Value); FORCEINLINE void SetShadowFilter(int sampler, DWORD Value); // render targets (FBO's) CGLMFBO *NewFBO(void); void DelFBO(CGLMFBO *fbo); // programs CGLMProgram *NewProgram(EGLMProgramType type, char *progString, const char *pShaderName); void DelProgram(CGLMProgram *pProg); void NullProgram(void); // de-ac all shader state FORCEINLINE void SetVertexProgram(CGLMProgram *pProg); FORCEINLINE void SetFragmentProgram(CGLMProgram *pProg); FORCEINLINE void SetProgram(EGLMProgramType nProgType, CGLMProgram *pProg) { m_drawingProgram[nProgType] = pProg; m_bDirtyPrograms = true; } void SetDrawingLang( EGLMProgramLang lang, bool immediate = false); // choose ARB or GLSL. immediate=false defers // lang change to top of frame void LinkShaderPair(CGLMProgram *vp, CGLMProgram *fp); // ensure this combo has been linked // and is in the GLSL pair cache void ValidateShaderPair(CGLMProgram *vp, CGLMProgram *fp); void ClearShaderPairCache( void); // call this to shoot down all the linked pairs void QueryShaderPair( int index, GLMShaderPairInfo *infoOut); // this lets you query the shader pair // cache for saving its state // buffers // Lock and Unlock reqs go directly to the buffer object CGLMBuffer *NewBuffer(EGLMBufferType type, uint size, uint options); void DelBuffer(CGLMBuffer *buff); FORCEINLINE void SetIndexBuffer(CGLMBuffer *buff) { BindIndexBufferToCtx(buff); } // FIXME: Remove this, it's no longer used FORCEINLINE void SetVertexAttributes(GLMVertexSetup *setup) { // we now just latch the vert setup and then execute on it at // flushdrawstatestime if shaders are enabled. if (setup) { m_drawVertexSetup = *setup; } else { memset(&m_drawVertexSetup, 0, sizeof(m_drawVertexSetup)); } } // note, no API is exposed for setting a single attribute source. // come prepared with a complete block of attributes to use. // Queries CGLMQuery *NewQuery(GLMQueryParams *params); void DelQuery(CGLMQuery *query); // "slot" means a vec4-sized thing // these write into .env parameter space FORCEINLINE void SetProgramParametersF(EGLMProgramType type, uint baseSlot, float *slotData, uint slotCount); FORCEINLINE void SetProgramParametersB( EGLMProgramType type, uint baseSlot, int *slotData, uint boolCount); // take "BOOL" aka int FORCEINLINE void SetProgramParametersI(EGLMProgramType type, uint baseSlot, int *slotData, uint slotCount); // take int4s // state sync // If lazyUnbinding is true, unbound samplers will not actually be unbound // to the GL device. FORCEINLINE void FlushDrawStates( uint nStartIndex, uint nEndIndex, uint nBaseVertex); // pushes all drawing state - samplers, tex, // programs, etc. void FlushDrawStatesNoShaders(); // drawing #ifndef OSX FORCEINLINE void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf); void DrawRangeElementsNonInline(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, uint baseVertex, CGLMBuffer *pIndexBuf); #else void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, CGLMBuffer *pIndexBuf); #endif void CheckNative(void); // clearing void Clear(bool color, unsigned long colorValue, bool depth, float depthValue, bool stencil, unsigned int stencilValue, GLScissorBox_t *rect = NULL); // display // void SetVSyncEnable( bool vsyncOn ); // void SetFullScreen( bool fsOn, int screenIndex ); // will be latched for // next BeginFrame // void ActivateFullScreen( bool fsOn, int screenIndex ); // will be called // by BeginFrame bool SetDisplayParams( GLMDisplayParams *params); // either the first time setup, or a change to new setup void Present(CGLMTex *tex); // somewhat hardwired for the time being // Called when IDirect3DDevice9::Reset() is called. void Reset(); // writers for the state block inputs FORCEINLINE void WriteAlphaTestEnable(GLAlphaTestEnable_t *src) { m_AlphaTestEnable.Write(src); } FORCEINLINE void WriteAlphaTestFunc(GLAlphaTestFunc_t *src) { m_AlphaTestFunc.Write(src); } FORCEINLINE void WriteAlphaToCoverageEnable( GLAlphaToCoverageEnable_t *src) { m_AlphaToCoverageEnable.Write(src); } FORCEINLINE void WriteCullFaceEnable(GLCullFaceEnable_t *src) { m_CullFaceEnable.Write(src); } FORCEINLINE void WriteCullFrontFace(GLCullFrontFace_t *src) { m_CullFrontFace.Write(src); } FORCEINLINE void WritePolygonMode(GLPolygonMode_t *src) { m_PolygonMode.Write(src); } FORCEINLINE void WriteDepthBias(GLDepthBias_t *src) { m_DepthBias.Write(src); } FORCEINLINE void WriteClipPlaneEnable(GLClipPlaneEnable_t *src, int which) { m_ClipPlaneEnable.WriteIndex(src, which); } FORCEINLINE void WriteClipPlaneEquation(GLClipPlaneEquation_t *src, int which) { m_ClipPlaneEquation.WriteIndex(src, which); } FORCEINLINE void WriteScissorEnable(GLScissorEnable_t *src) { m_ScissorEnable.Write(src); } FORCEINLINE void WriteScissorBox(GLScissorBox_t *src) { m_ScissorBox.Write(src); } FORCEINLINE void WriteViewportBox(GLViewportBox_t *src) { m_ViewportBox.Write(src); } FORCEINLINE void WriteViewportDepthRange(GLViewportDepthRange_t *src) { m_ViewportDepthRange.Write(src); } FORCEINLINE void WriteColorMaskSingle(GLColorMaskSingle_t *src) { m_ColorMaskSingle.Write(src); } FORCEINLINE void WriteColorMaskMultiple(GLColorMaskMultiple_t *src, int which) { m_ColorMaskMultiple.WriteIndex(src, which); } FORCEINLINE void WriteBlendEnable(GLBlendEnable_t *src) { m_BlendEnable.Write(src); } FORCEINLINE void WriteBlendFactor(GLBlendFactor_t *src) { m_BlendFactor.Write(src); } FORCEINLINE void WriteBlendEquation(GLBlendEquation_t *src) { m_BlendEquation.Write(src); } FORCEINLINE void WriteBlendColor(GLBlendColor_t *src) { m_BlendColor.Write(src); } FORCEINLINE void WriteBlendEnableSRGB(GLBlendEnableSRGB_t *src) { if (m_caps.m_hasGammaWrites) // only if caps allow do we actually push // it through to the extension { m_BlendEnableSRGB.Write(src); } else { m_FakeBlendEnableSRGB = src->enable != 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->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 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_EXT); // 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_ARB) || (nGLBufType == GL_ELEMENT_ARRAY_BUFFER_ARB)); const uint nIndex = (nGLBufType == GL_ARRAY_BUFFER_ARB) ? kGLMVertexBuffer : kGLMIndexBuffer; if ((bForce) || (m_nBoundGLBuffer[nIndex] != nGLName)) { m_nBoundGLBuffer[nIndex] = nGLName; gGL->glBindBufferARB(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); #ifndef OSX CPinnedMemoryBuffer *GetCurPinnedMemoryBuffer() { return &m_PinnedMemoryBuffers[m_nCurPinnedMemoryBuffer]; } #endif 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 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 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 m_availableTextures; #ifndef OSX enum { cNumPinnedMemoryBuffers = 4 }; CPinnedMemoryBuffer m_PinnedMemoryBuffers[cNumPinnedMemoryBuffers]; uint m_nCurPinnedMemoryBuffer; #endif 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 }; #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 < slotCount; i++) { GLMPRINTF(("-S- %03d: [ %7.4f %7.4f %7.4f %7.4f ]", baseSlot + i, slotData[i * 4], slotData[i * 4 + 1], slotData[i * 4 + 2], slotData[i * 4 + 3])); } #endif memcpy(&m_programParamsF[type].m_values[baseSlot][0], slotData, (4 * sizeof(float)) * slotCount); if ((type == kGLMVertexProgram) && (m_bUseBoneUniformBuffers)) { // changes here to handle vertex shaders which use constants before and // after the bone array i.e. before c58 and after c216 a better change // may be to modify the shaders and place the bone consts at either // start or end - would simplify this and the flush code the current // supporting code (shader translator(dx9asmtogl2), param setting(here) // and flushing(glmgr_flush.inl) should work unchanged, even if the // const mapping is changed. int firstDirty = (int)baseSlot; int highWater = (int)(baseSlot + slotCount); if (highWater <= DXABSTRACT_VS_FIRST_BONE_SLOT) { m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone = MIN(m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty); m_programParamsF[kGLMVertexProgram] .m_dirtySlotHighWaterNonBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterNonBone, highWater); } else if (highWater <= (DXABSTRACT_VS_LAST_BONE_SLOT + 1)) { if (firstDirty < DXABSTRACT_VS_FIRST_BONE_SLOT) { m_programParamsF[kGLMVertexProgram] .m_firstDirtySlotNonBone = MIN( m_programParamsF[kGLMVertexProgram].m_firstDirtySlotNonBone, firstDirty); m_programParamsF[kGLMVertexProgram] .m_dirtySlotHighWaterNonBone = MAX(m_programParamsF[kGLMVertexProgram] .m_dirtySlotHighWaterNonBone, MIN(DXABSTRACT_VS_FIRST_BONE_SLOT, highWater)); firstDirty = DXABSTRACT_VS_FIRST_BONE_SLOT; } int nNumActualBones = (firstDirty + slotCount) - DXABSTRACT_VS_FIRST_BONE_SLOT; m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone = MAX( m_programParamsF[kGLMVertexProgram].m_dirtySlotHighWaterBone, nNumActualBones); } else { const int maxBoneSlots = (DXABSTRACT_VS_LAST_BONE_SLOT + 1) - DXABSTRACT_VS_FIRST_BONE_SLOT; if (firstDirty > 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 < boolCount; i++) { GLMPRINTF(("-S- %03d: %d (bool)", baseSlot + i, slotData[i])); } #endif memcpy(&m_programParamsB[type].m_values[baseSlot], slotData, sizeof(int) * boolCount); if ((baseSlot + boolCount) > 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 < slotCount; i++) { GLMPRINTF(("-S- %03d: %d %d %d %d (int4)", baseSlot + i, slotData[i * 4], slotData[i * 4 + 1], slotData[i * 4 + 2], slotData[i * 4 + 3])); } #endif memcpy(&m_programParamsI[type].m_values[baseSlot][0], slotData, (4 * sizeof(int)) * slotCount); if ((baseSlot + slotCount) > 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) { 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)); 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; } FORCEINLINE void GLMContext::SetSamplerBorderColor(int sampler, DWORD Value) { m_samplers[sampler].m_samp.m_borderColor = Value; } FORCEINLINE void GLMContext::SetSamplerMipMapLODBias(int sampler, DWORD Value) { // not currently supported } 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_ARB)); GLuint nGLName = buff ? buff->GetHandle() : 0; if (m_nBoundGLBuffer[kGLMIndexBuffer] == nGLName) return; m_nBoundGLBuffer[kGLMIndexBuffer] = nGLName; gGL->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 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_ARB)); GLuint nGLName = buff ? buff->GetHandle() : 0; if (m_nBoundGLBuffer[kGLMVertexBuffer] == nGLName) return; m_nBoundGLBuffer[kGLMVertexBuffer] = nGLName; gGL->glBindBufferARB(GL_ARRAY_BUFFER_ARB, 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