You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2435 lines
81 KiB
2435 lines
81 KiB
//========= 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 |
|
|
|
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<typename T> void GLContextSet( T *src ); |
|
template<typename T> void GLContextGet( T *dst ); |
|
template<typename T> void GLContextGetDefault( T *dst ); |
|
|
|
// use these from the array state objects |
|
template<typename T> void GLContextSetIndexed( T *src, int index ); |
|
template<typename T> void GLContextGetIndexed( T *dst, int index ); |
|
template<typename T> 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<typename T> 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<typename T, int COUNT> 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<<n) mask where n is a GLMVertexAttributeIndex. |
|
|
|
GLMVertexAttributeDesc m_attrs[ kGLMVertexAttributeIndexMax ]; |
|
|
|
// copied in from dxabstract, not strictly needed for operation, helps debugging |
|
unsigned char m_vtxAttribMap[16]; |
|
|
|
/* high nibble is usage per _D3DDECLUSAGE |
|
typedef enum _D3DDECLUSAGE |
|
{ |
|
D3DDECLUSAGE_POSITION = 0, |
|
D3DDECLUSAGE_BLENDWEIGHT = 1, |
|
D3DDECLUSAGE_BLENDINDICES = 2, |
|
D3DDECLUSAGE_NORMAL = 3, |
|
D3DDECLUSAGE_PSIZE = 4, |
|
D3DDECLUSAGE_TEXCOORD = 5, |
|
D3DDECLUSAGE_TANGENT = 6, |
|
D3DDECLUSAGE_BINORMAL = 7, |
|
D3DDECLUSAGE_TESSFACTOR = 8, |
|
D3DDECLUSAGE_PLUGH = 9, // mystery value |
|
D3DDECLUSAGE_COLOR = 10, |
|
D3DDECLUSAGE_FOG = 11, |
|
D3DDECLUSAGE_DEPTH = 12, |
|
D3DDECLUSAGE_SAMPLE = 13, |
|
} D3DDECLUSAGE; |
|
|
|
low nibble is usageindex (i.e. POSITION0, POSITION1, etc) |
|
array position is attrib number. |
|
*/ |
|
}; |
|
|
|
//===========================================================================// |
|
|
|
//FIXME magic numbers here |
|
|
|
#define kGLMProgramParamFloat4Limit 256 |
|
#define kGLMProgramParamBoolLimit 16 |
|
#define kGLMProgramParamInt4Limit 16 |
|
|
|
#define kGLMVertexProgramParamFloat4Limit 256 |
|
#define kGLMFragmentProgramParamFloat4Limit 256 |
|
|
|
struct GLMProgramParamsF |
|
{ |
|
float m_values[kGLMProgramParamFloat4Limit][4]; // float4's 256 of them |
|
|
|
int m_firstDirtySlotNonBone; |
|
int m_dirtySlotHighWaterNonBone; // index of slot past highest dirty non-bone register (assume 0 for base of range) |
|
|
|
int m_dirtySlotHighWaterBone; // index of slot past highest dirty bone register (0=first bone reg, which is DXABSTRACT_VS_FIRST_BONE_SLOT) |
|
}; |
|
|
|
struct GLMProgramParamsB |
|
{ |
|
int m_values[kGLMProgramParamBoolLimit]; // bools, 4 of them |
|
uint m_dirtySlotCount; |
|
}; |
|
|
|
struct GLMProgramParamsI |
|
{ |
|
int m_values[kGLMProgramParamInt4Limit][4]; // int4s, 16 of them |
|
uint m_dirtySlotCount; |
|
}; |
|
|
|
enum EGLMParamWriteMode |
|
{ |
|
eParamWriteAllSlots, // glUniform4fv of the maximum size (not recommended if shader is down-sizing the decl) |
|
eParamWriteShaderSlots, // glUniform4fv of the active slot count ("highwater") |
|
eParamWriteShaderSlotsOptional, // glUniform4fv of the active slot count ("highwater") - but only if at least one has been written - it's optional |
|
eParamWriteDirtySlotRange // glUniform4fv of the 0-N range where N is highest dirty slot |
|
}; |
|
|
|
enum EGLMAttribWriteMode |
|
{ |
|
eAttribWriteAll, |
|
eAttribWriteDirty |
|
}; |
|
|
|
//===========================================================================// |
|
|
|
#if GLMDEBUG |
|
enum EGLMDebugCallSite |
|
{ |
|
eBeginFrame, // inside begin frame func - frame number has been inc'd, batch number should be -1 |
|
eClear, // inside clear func |
|
eDrawElements, // inside repeat loop, prior to draw call - batch numberhas been inc'd |
|
eEndFrame, // end frame |
|
ePresent // before showing pixels |
|
}; |
|
|
|
// caller should zero one of these out and fill in the m_caller before invoking the hook |
|
struct GLMDebugHookInfo |
|
{ |
|
// info from the caller to the debug hook |
|
EGLMDebugCallSite m_caller; |
|
|
|
|
|
// state the hook uses to keep track of progress within a single run of the caller |
|
int m_iteration; // which call to the hook is this. if it's zero, it precedes any action in the caller. |
|
|
|
|
|
// bools used to communicate between caller and hook |
|
bool m_loop; // hook tells caller to loop around again (don't exit) |
|
bool m_holding; // current mood of hook, are we holding on this batch (i.e. rerun) |
|
|
|
// specific info for a draw call |
|
GLenum m_drawMode; |
|
GLuint m_drawStart; |
|
GLuint m_drawEnd; |
|
GLsizei m_drawCount; |
|
GLenum m_drawType; |
|
const GLvoid *m_drawIndices; |
|
}; |
|
#endif |
|
|
|
//===========================================================================// |
|
|
|
class CFlushDrawStatesStats |
|
{ |
|
public: |
|
CFlushDrawStatesStats() |
|
{ |
|
Clear(); |
|
} |
|
|
|
void Clear() |
|
{ |
|
memset(this, 0, sizeof(*this)); |
|
} |
|
|
|
uint m_nTotalBatchFlushes; |
|
uint m_nTotalProgramPairChanges; |
|
|
|
uint m_nNumChangedSamplers; |
|
uint m_nNumSamplingParamsChanged; |
|
uint m_nIndexBufferChanged; |
|
uint m_nVertexBufferChanged; |
|
|
|
uint m_nFirstVSConstant; |
|
uint m_nNumVSConstants; |
|
uint m_nNumVSBoneConstants; |
|
uint m_nFirstPSConstant; |
|
uint m_nNumPSConstants; |
|
uint m_nNewPS; |
|
uint m_nNewVS; |
|
}; |
|
|
|
//===========================================================================// |
|
#ifndef OSX |
|
|
|
#ifndef GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD |
|
#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 |
|
#endif |
|
|
|
#define GLMGR_PINNED_MEMORY_BUFFER_SIZE ( 6 * 1024 * 1024 ) |
|
|
|
class CPinnedMemoryBuffer |
|
{ |
|
CPinnedMemoryBuffer( const CPinnedMemoryBuffer & ); |
|
CPinnedMemoryBuffer & operator= ( const CPinnedMemoryBuffer & ); |
|
|
|
public: |
|
CPinnedMemoryBuffer() |
|
: |
|
m_pRawBuf( NULL ) |
|
, m_pBuf( NULL ) |
|
, m_nSize( 0 ) |
|
, m_nOfs( 0 ) |
|
, m_nBufferObj( 0 ) |
|
#ifdef HAVE_GL_ARB_SYNC |
|
, m_nSyncObj( 0 ) |
|
#endif |
|
{ |
|
} |
|
|
|
~CPinnedMemoryBuffer() |
|
{ |
|
Deinit(); |
|
} |
|
|
|
bool Init( uint nSize ) |
|
{ |
|
Deinit(); |
|
|
|
// Guarantee 64KB alignment |
|
m_pRawBuf = malloc( nSize + 65535 ); |
|
m_pBuf = reinterpret_cast<void *>((reinterpret_cast<uint64>(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, int minLod, float lodBias ); |
|
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<GLAlphaTestEnable_t> m_AlphaTestEnable; |
|
|
|
GLState<GLAlphaTestFunc_t> m_AlphaTestFunc; |
|
|
|
GLState<GLCullFaceEnable_t> m_CullFaceEnable; |
|
GLState<GLCullFrontFace_t> m_CullFrontFace; |
|
GLState<GLPolygonMode_t> m_PolygonMode; |
|
|
|
GLState<GLDepthBias_t> m_DepthBias; |
|
|
|
GLStateArray<GLClipPlaneEnable_t,kGLMUserClipPlanes> m_ClipPlaneEnable; |
|
GLStateArray<GLClipPlaneEquation_t,kGLMUserClipPlanes> m_ClipPlaneEquation; // dxabstract puts them directly into param slot 253(0) and 254(1) |
|
|
|
GLState<GLScissorEnable_t> m_ScissorEnable; |
|
GLState<GLScissorBox_t> m_ScissorBox; |
|
|
|
GLState<GLAlphaToCoverageEnable_t> m_AlphaToCoverageEnable; |
|
|
|
GLState<GLViewportBox_t> m_ViewportBox; |
|
GLState<GLViewportDepthRange_t> m_ViewportDepthRange; |
|
|
|
GLState<GLColorMaskSingle_t> m_ColorMaskSingle; |
|
GLStateArray<GLColorMaskMultiple_t,8> m_ColorMaskMultiple; // need an official constant for the color buffers limit |
|
|
|
GLState<GLBlendEnable_t> m_BlendEnable; |
|
GLState<GLBlendFactor_t> m_BlendFactor; |
|
GLState<GLBlendEquation_t> m_BlendEquation; |
|
GLState<GLBlendColor_t> m_BlendColor; |
|
GLState<GLBlendEnableSRGB_t> 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<GLDepthTestEnable_t> m_DepthTestEnable; |
|
GLState<GLDepthFunc_t> m_DepthFunc; |
|
GLState<GLDepthMask_t> m_DepthMask; |
|
|
|
GLState<GLStencilTestEnable_t> m_StencilTestEnable; // global stencil test enable |
|
GLState<GLStencilFunc_t> m_StencilFunc; // holds front and back stencil funcs |
|
GLStateArray<GLStencilOp_t,2> m_StencilOp; // indexed: 0=front 1=back |
|
GLState<GLStencilWriteMask_t> m_StencilWriteMask; |
|
|
|
GLState<GLClearColor_t> m_ClearColor; |
|
GLState<GLClearDepth_t> m_ClearDepth; |
|
GLState<GLClearStencil_t> 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; |
|
|
|
#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 |
|
|
|
void ProcessTextureDeletes(); |
|
CTSQueue<CGLMTex*> 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<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, 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_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
|
|
|