This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

2711 lines
93 KiB
C++

//========= 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 <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);
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
};
#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 &params = 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