This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,358 @@
//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// cglmbuffer.cpp
//
//===============================================================================
#include "glmgr/glmgr.h"
#include "glmgr/cglmbuffer.h"
// memdbgon -must- be the last include file in a .cpp file.
#include "tier0/memdbgon.h"
// void BindBufferARB(enum target, uint buffer);
// void DeleteBuffersARB(sizei n, const uint *buffers);
// void GenBuffersARB(sizei n, uint *buffers);
// boolean IsBufferARB(uint buffer);
//
// void BufferDataARB(enum target, sizeiptrARB size, const void *data,
// enum usage);
// void BufferSubDataARB(enum target, intptrARB offset, sizeiptrARB size,
// const void *data);
// void GetBufferSubDataARB(enum target, intptrARB offset,
// sizeiptrARB size, void *data);
//
// void *MapBufferARB(enum target, enum access);
// boolean UnmapBufferARB(enum target);
//
// void GetBufferParameterivARB(enum target, enum pname, int *params);
// void GetBufferPointervARB(enum target, enum pname, void **params);
//
//New Tokens
//
// Accepted by the <target> parameters of BindBufferARB, BufferDataARB,
// BufferSubDataARB, MapBufferARB, UnmapBufferARB,
// GetBufferSubDataARB, GetBufferParameterivARB, and
// GetBufferPointervARB:
//
// ARRAY_BUFFER_ARB 0x8892
// ELEMENT_ARRAY_BUFFER_ARB 0x8893
//
// Accepted by the <pname> parameter of GetBooleanv, GetIntegerv,
// GetFloatv, and GetDoublev:
//
// ARRAY_BUFFER_BINDING_ARB 0x8894
// ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895
// VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896
// NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897
// COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898
// INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899
// TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A
// EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B
// SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C
// FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D
// WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E
//
// Accepted by the <pname> parameter of GetVertexAttribivARB:
//
// VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F
//
// Accepted by the <usage> parameter of BufferDataARB:
//
// STREAM_DRAW_ARB 0x88E0
// STREAM_READ_ARB 0x88E1
// STREAM_COPY_ARB 0x88E2
// STATIC_DRAW_ARB 0x88E4
// STATIC_READ_ARB 0x88E5
// STATIC_COPY_ARB 0x88E6
// DYNAMIC_DRAW_ARB 0x88E8
// DYNAMIC_READ_ARB 0x88E9
// DYNAMIC_COPY_ARB 0x88EA
//
// Accepted by the <access> parameter of MapBufferARB:
//
// READ_ONLY_ARB 0x88B8
// WRITE_ONLY_ARB 0x88B9
// READ_WRITE_ARB 0x88BA
//
// Accepted by the <pname> parameter of GetBufferParameterivARB:
//
// BUFFER_SIZE_ARB 0x8764
// BUFFER_USAGE_ARB 0x8765
// BUFFER_ACCESS_ARB 0x88BB
// BUFFER_MAPPED_ARB 0x88BC
//
// Accepted by the <pname> parameter of GetBufferPointervARB:
//
// BUFFER_MAP_POINTER_ARB 0x88BD
// http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt
// Accepted by the <target> parameters of BindBuffer, BufferData,
// BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData,
// GetBufferParameteriv, and GetBufferPointerv:
// PIXEL_PACK_BUFFER_ARB 0x88EB
// PIXEL_UNPACK_BUFFER_ARB 0x88EC
// gl_bufmode: zero means we mark all vertex/index buffers static
// non zero means buffers are initially marked static..
// ->but can shift to dynamic upon first 'discard' (orphaning)
ConVar gl_bufmode( "gl_bufmode", "1" );
CGLMBuffer::CGLMBuffer( GLMContext *ctx, EGLMBufferType type, uint size, uint options )
{
m_ctx = ctx;
m_type = type;
switch(m_type)
{
case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break;
case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break;
case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break;
case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break;
default: Assert(!"Unknown buffer type" );
}
m_size = size;
m_bound = false;
m_mapped = false;
m_lastMappedAddress = NULL;
m_enableAsyncMap = false;
m_enableExplicitFlush = false;
m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
m_ctx->CheckCurrent();
m_revision = rand();
// make a decision about pseudo mode
// this looked like it didn't help much or was actually slower, so leave it available but only as opt-in.
// a more clever implementation would be able to select pseudo buf storage for small batches only..
m_pseudo = (m_type==kGLMIndexBuffer) && (CommandLine()->FindParm("-gl_enable_pseudobufs"));
if (m_pseudo)
{
m_name = 0;
m_pseudoBuf = (char*)malloc( size );
m_ctx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound
}
else
{
glGenBuffersARB( 1, &m_name );
GLMCheckError();
m_ctx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB
// buffers start out static, but if they get orphaned and gl_bufmode is non zero,
// then they will get flipped to dynamic.
GLenum hint = GL_STATIC_DRAW_ARB;
switch(m_type)
{
case kGLMVertexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
case kGLMIndexBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break; // "fwiw" - shrug
case kGLMPixelBuffer: hint = (options & GLMBufferOptionDynamic) ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
default: Assert(!"Unknown buffer type" );
}
glBufferDataARB( m_buffGLTarget, m_size, NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming)
this->SetModes( true, true, true );
m_ctx->BindBufferToCtx( m_type, NULL ); // unbind me
}
}
CGLMBuffer::~CGLMBuffer( )
{
m_ctx->CheckCurrent();
if (m_pseudo)
{
free (m_pseudoBuf);
m_pseudoBuf = NULL;
}
else
{
glDeleteBuffersARB( 1, &m_name );
GLMCheckError();
}
m_ctx = NULL;
m_name = 0;
m_bound = 0;
m_lastMappedAddress = NULL;
}
void CGLMBuffer::SetModes ( bool asyncMap, bool explicitFlush, bool force )
{
// assumes buffer is bound. called by constructor and by Lock.
if (m_pseudo)
{
// ignore it...
}
else
{
if (force || (m_enableAsyncMap != asyncMap) )
{
// note the sense of the parameter, it's TRUE if you *want* serialization, so for async you turn it to false.
glBufferParameteriAPPLE( this->m_buffGLTarget, GL_BUFFER_SERIALIZED_MODIFY_APPLE, asyncMap==false );
m_enableAsyncMap = asyncMap;
}
if (force || (m_enableExplicitFlush != explicitFlush) )
{
// note the sense of the parameter, it's TRUE if you *want* auto-flush-on-unmap, so for explicit-flush, you turn it to false.
glBufferParameteriAPPLE( this->m_buffGLTarget, GL_BUFFER_FLUSHING_UNMAP_APPLE, explicitFlush==false );
m_enableExplicitFlush = explicitFlush;
}
}
}
void CGLMBuffer::FlushRange ( uint offset, uint size )
{
if (m_pseudo)
{
// nothing to do
}
else
{
// assumes buffer is bound.
glFlushMappedBufferRangeAPPLE(this->m_buffGLTarget, (GLintptr)offset, (GLsizeiptr)size);
}
}
ConVar gl_buffer_alignment_quantum ( "gl_buffer_alignment_quantum", "32" ); // the alignment we use pre-SLGU
ConVar gl_buffer_alignment_quantum_slgu( "gl_buffer_alignment_quantum_slgu", "2" ); // alignment used post-SLGU
void CGLMBuffer::Lock( GLMBuffLockParams *params, char **addressOut )
{
char *resultPtr = NULL;
Assert( !m_mapped);
m_ctx->CheckCurrent();
GLMCheckError();
if (params->m_offset >= m_size)
Debugger();
if (params->m_offset + params->m_size > m_size)
Debugger();
// bind (yes, even for pseudo - this binds name 0)
m_ctx->BindBufferToCtx( this->m_type, this );
if (m_pseudo)
{
// discard is a no-op
// async map modes are a no-op
// latch last mapped address (silly..)
m_lastMappedAddress = (float*)m_pseudoBuf;
// calc lock address
resultPtr = m_pseudoBuf + params->m_offset;
// dirty range is a no-op
}
else
{
// perform discard if requested
if (params->m_discard)
{
// observe gl_bufmode on any orphan event.
// if orphaned and bufmode is nonzero, flip it to dynamic.
GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB;
glBufferDataARB( m_buffGLTarget, m_size, NULL, hint );
m_lastMappedAddress = NULL;
m_revision++; // revision grows on orphan event
}
// adjust async map option appropriately, leave explicit flush unchanged
this->SetModes( params->m_nonblocking, m_enableExplicitFlush );
// map
char *mapPtr = (char*)glMapBufferARB( this->m_buffGLTarget, GL_READ_WRITE_ARB );
if (!mapPtr)
{
Debugger();
}
if (m_lastMappedAddress)
{
// just check if it moved
Assert (m_lastMappedAddress == (float*)mapPtr);
}
m_lastMappedAddress = (float*)mapPtr;
// calculate offset location
resultPtr = mapPtr + params->m_offset;
// adjust dirty range
if (m_dirtyMinOffset != m_dirtyMaxOffset)
{
// grow range
m_dirtyMinOffset = MIN( m_dirtyMinOffset, params->m_offset );
m_dirtyMaxOffset = MIN( m_dirtyMaxOffset, params->m_offset+params->m_size );
}
else
{
// set range
m_dirtyMinOffset = params->m_offset;
m_dirtyMaxOffset = params->m_offset+params->m_size;
}
// pad and clamp dirty range to choice of boundary
uint quantum = (m_ctx->Caps().m_hasPerfPackage1) ? gl_buffer_alignment_quantum_slgu.GetInt() : gl_buffer_alignment_quantum.GetInt();
uint quantum_mask = quantum - 1;
m_dirtyMinOffset = m_dirtyMinOffset & (~quantum_mask);
m_dirtyMaxOffset = (m_dirtyMaxOffset + quantum_mask) & (~quantum_mask);
m_dirtyMaxOffset = MIN( m_dirtyMaxOffset, m_size );
}
m_mapped = true;
*addressOut = resultPtr;
}
void CGLMBuffer::Unlock( void )
{
m_ctx->CheckCurrent();
Assert (m_mapped);
if (m_pseudo)
{
// nothing to do actually
}
else
{
m_ctx->BindBufferToCtx( this->m_type, this );
// time to do explicit flush
if (m_enableExplicitFlush)
{
this->FlushRange( m_dirtyMinOffset, m_dirtyMaxOffset - m_dirtyMinOffset );
}
// clear dirty range no matter what
m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
glUnmapBuffer( this->m_buffGLTarget );
}
m_mapped = false;
}

View File

@@ -0,0 +1,355 @@
//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// cglmfbo.cpp
//
//===============================================================================
#include "glmgr/glmgr.h"
#include "glmgr/cglmfbo.h"
// #include "../shaderapidx9/dxabstract.h"
// memdbgon -must- be the last include file in a .cpp file.
#include "tier0/memdbgon.h"
CGLMFBO::CGLMFBO( GLMContext *ctx )
{
m_ctx = ctx;
m_ctx->CheckCurrent();
glGenFramebuffersEXT( 1, &m_name );
GLMCheckError();
memset( m_attach, 0, sizeof( m_attach ) );
m_sizeX = m_sizeY = 0;
}
CGLMFBO::~CGLMFBO( )
{
m_ctx->CheckCurrent();
// detach all known attached textures first... necessary ?
for( int index = 0; index < kAttCount; index++)
{
if (m_attach[ index ].m_tex)
{
TexDetach( (EGLMFBOAttachment)index );
GLMCheckError();
}
}
glDeleteFramebuffersEXT( 1, &m_name );
GLMCheckError();
m_name = 0;
m_ctx = NULL;
}
// the tex attach path should also select a specific slice of the texture...
// and we need a way to make renderbuffers..
static GLenum EncodeAttachmentFBO( EGLMFBOAttachment index )
{
if (index < kAttDepth)
{
return GL_COLOR_ATTACHMENT0_EXT + (int) index;
}
else
{
switch( index )
{
case kAttDepth:
return GL_DEPTH_ATTACHMENT_EXT;
break;
case kAttStencil:
return GL_STENCIL_ATTACHMENT_EXT;
break;
case kAttDepthStencil:
return GL_DEPTH_STENCIL_ATTACHMENT_EXT;
break;
default:
GLMStop(); // bad news
break;
}
}
}
void CGLMFBO::TexAttach( GLMFBOTexAttachParams *params, EGLMFBOAttachment attachIndex, GLenum fboBindPoint )
{
// force our parent context to be current
m_ctx->MakeCurrent();
// bind to context (will cause FBO object creation on first use)
m_ctx->BindFBOToCtx( this, fboBindPoint );
// it's either a plain 2D, a 2D face of a cube map, or a slice of a 3D.
CGLMTex *tex = params->m_tex;
// always detach what is currently there, if anything
this->TexDetach( attachIndex, fboBindPoint );
if (!tex)
{
// andif they pass NULL to us, then we are done.
return;
}
GLMTexLayout *layout = tex->m_layout;
GLenum target = tex->m_layout->m_key.m_texGLTarget;
GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex );
switch( target )
{
case GL_TEXTURE_2D:
{
// we will attach the underlying RBO on a multisampled tex, iff the tex hasone, **and** we're not being asked to attach it to the read buffer.
// if we get a req to attach an MSAA tex to the read buffer, chances are it's BlitTex calling, andit has already resolved the tex, so in those
// cases you really do want to attach the texture and not the RBO to the FBO in question.
bool useRBO = false; // initial state
if (layout->m_key.m_texFlags & kGLMTexMultisampled)
{
// it is an MSAA tex
if (fboBindPoint == GL_READ_FRAMEBUFFER_EXT)
{
// I think you just want to read a resolved tex.
// But I will check that it is resolved first..
Assert( tex->m_rboDirty == false );
}
else
{
// you want to draw into it. You get the RBO bound instead of the tex.
useRBO = true;
}
}
if (useRBO)
{
// MSAA path - attach the RBO, not the texture, and mark the RBO dirty
if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT)
{
// you have to attach it both places...
// http://www.opengl.org/wiki/GL_EXT_framebuffer_object
// bind the RBO to the GL_RENDERBUFFER_EXT target
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName );
GLMCheckError();
// attach the GL_RENDERBUFFER_EXT target to the depth and stencil attach points
glFramebufferRenderbufferEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName);
GLMCheckError();
glFramebufferRenderbufferEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, tex->m_rboName);
GLMCheckError();
// no need to leave the RBO hanging on
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 );
GLMCheckError();
}
else
{
// color attachment (likely 0)
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, tex->m_rboName );
GLMCheckError();
glFramebufferRenderbufferEXT( fboBindPoint, attachIndexGL, GL_RENDERBUFFER_EXT, tex->m_rboName);
GLMCheckError();
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 );
GLMCheckError();
}
tex->m_rboDirty = true;
}
else
{
// regular path - attaching a texture2d
if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT)
{
// you have to attach it both places...
// http://www.opengl.org/wiki/GL_EXT_framebuffer_object
glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip );
GLMCheckError();
glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, target, tex->m_texName, params->m_mip );
GLMCheckError();
}
else
{
glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip );
GLMCheckError();
}
}
}
break;
case GL_TEXTURE_3D:
{
glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip, params->m_zslice );
GLMCheckError();
}
break;
case GL_TEXTURE_CUBE_MAP:
{
// adjust target to steer to the proper face of the cube map
target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + params->m_face;
glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, target, tex->m_texName, params->m_mip );
GLMCheckError();
}
break;
}
// log the attached tex
m_attach[ attachIndex ] = *params;
// indicate that the tex has been bound to an RT
tex->m_rtAttachCount++;
}
void CGLMFBO::TexDetach( EGLMFBOAttachment attachIndex, GLenum fboBindPoint )
{
// force our parent context to be current
m_ctx->MakeCurrent();
// bind to context (will cause FBO object creation on first use)
m_ctx->BindFBOToCtx( this, fboBindPoint );
if (m_attach[ attachIndex ].m_tex)
{
CGLMTex *tex = m_attach[ attachIndex ].m_tex;
GLMTexLayout *layout = tex->m_layout;
GLenum target = tex->m_layout->m_key.m_texGLTarget;
GLenum attachIndexGL = EncodeAttachmentFBO( attachIndex );
switch( target )
{
case GL_TEXTURE_2D:
{
if (layout->m_key.m_texFlags & kGLMTexMultisampled)
{
// MSAA path - detach the RBO, not the texture
// (is this the right time to resolve? probably better to wait until someone tries to sample the texture)
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 );
GLMCheckError();
if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT)
{
// detach the GL_RENDERBUFFER_EXT target at depth and stencil attach points
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
GLMCheckError();
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
GLMCheckError();
}
else
{
// color attachment (likely 0)
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, attachIndexGL, GL_RENDERBUFFER_EXT, 0);
GLMCheckError();
}
}
else
{
// plain tex detach
if (attachIndexGL==GL_DEPTH_STENCIL_ATTACHMENT_EXT)
{
// you have to detach it both places...
// http://www.opengl.org/wiki/GL_EXT_framebuffer_object
glFramebufferTexture2DEXT( fboBindPoint, GL_DEPTH_ATTACHMENT_EXT, 0,0,0 );
glFramebufferTexture2DEXT( fboBindPoint, GL_STENCIL_ATTACHMENT_EXT, 0,0,0 );
}
else
{
glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, 0, 0, 0 );
}
}
}
break;
case GL_TEXTURE_3D:
{
glFramebufferTexture3DEXT( fboBindPoint, attachIndexGL, 0, 0, 0, 0 );
GLMCheckError();
}
break;
case GL_TEXTURE_CUBE_MAP:
{
glFramebufferTexture2DEXT( fboBindPoint, attachIndexGL, 0, 0, 0 );
GLMCheckError();
}
break;
}
// un-log the attached tex
memset( &m_attach[ attachIndex ], 0, sizeof( m_attach[0] ) );
// drop the RT attach count
tex->m_rtAttachCount--;
}
else
{
//Debugger(); // odd, but not harmful - typ comes from D3D code passing NULL into SetRenderTarget
}
}
void CGLMFBO::TexScrub( CGLMTex *tex )
{
// see if it's attached anywhere
for( int attachIndex = 0; attachIndex < kAttCount; attachIndex++ )
{
if (m_attach[ attachIndex ].m_tex == tex)
{
// blammo
TexDetach( (EGLMFBOAttachment)attachIndex, GL_DRAW_FRAMEBUFFER_EXT );
}
}
}
bool CGLMFBO::IsReady( void )
{
bool result = false;
// ensure our parent context is current
m_ctx->CheckCurrent();
// bind to context (will cause FBO object creation on first use)
m_ctx->BindFBOToCtx( this );
GLenum status;
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch(status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
result = true;
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
result = false;
Debugger();
/* choose different formats */
break;
default:
result = false;
Debugger();
/* programming error; will fail on all hardware */
break;
}
return result;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// cglmquery.cpp
//
//===============================================================================
#include "glmgr/glmgr.h"
#include "glmgr/cglmquery.h"
#include "../shaderapidx9/dxabstract.h"
#include <unistd.h>
// memdbgon -must- be the last include file in a .cpp file.
#include "tier0/memdbgon.h"
//===============================================================================
extern ConVar gl_errorcheckall;
extern ConVar gl_errorcheckqueries;
extern ConVar gl_errorchecknone;
// how many microseconds to wait after a failed query-available test
// presently on MTGL this doesn't happen, but it could change, keep this handy
ConVar gl_nullqueries( "gl_nullqueries", "0" );
GLenum GetQueryError( void )
{
if ( ( GLMDEBUG || (gl_errorcheckall.GetInt() != 0) || (gl_errorcheckqueries.GetInt() != 0) ) && (gl_errorchecknone.GetInt() == 0) )
{
return glGetError();
}
else
{
return (GLenum) 0; // whistle past graveyard
}
}
//===============================================================================
CGLMQuery::CGLMQuery( GLMContext *ctx, GLMQueryParams *params )
{
// make sure context is current
// get the type of query requested
// generate name(s) needed
// set initial state appropriately
ctx->MakeCurrent();
m_ctx = ctx;
m_params = *params;
m_name = 0;
m_started = m_stopped = m_done = false;
m_nullQuery = false;
// assume value of convar at start time
// does not change during individual query lifetime
// started null = stays null
// started live = stays live
switch(m_params.m_type)
{
case EOcclusion:
{
//make an occlusion query (and a fence to go with it)
glGenQueriesARB( 1, &m_name );
GLMPRINTF(("-A- CGLMQuery(OQ) created name %d", m_name));
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::CGLMQuery (OQ) saw %s error (%d) from glGenQueriesARB", decodedStr, errorcode );
m_name = 0;
}
}
break;
case EFence:
//make a fence - no aux fence needed
glGenFencesAPPLE(1, &m_name );
GLMPRINTF(("-A- CGLMQuery(fence) created name %d", m_name));
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::CGLMQuery (fence) saw %s error (%d) from glGenFencesAPPLE", decodedStr, errorcode );
m_name = 0;
}
break;
}
}
CGLMQuery::~CGLMQuery()
{
GLMPRINTF(("-A-> ~CGLMQuery"));
// make sure query has completed (might not be necessary)
// delete the name(s)
m_ctx->MakeCurrent();
switch(m_params.m_type)
{
case EOcclusion:
{
// do a finish occlusion query ?
GLMPRINTF(("-A- ~CGLMQuery(OQ) deleting name %d", m_name));
glDeleteQueries(1, &m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::~CGLMQuery (OQ) saw %s error (%d) from glDeleteQueries", decodedStr, errorcode );
}
}
break;
case EFence:
{
// do a finish fence ?
GLMPRINTF(("-A- ~CGLMQuery(fence) deleting name %d", m_name));
glDeleteFencesAPPLE(1, &m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::~CGLMQuery (fence) saw %s error (%d) from glDeleteFencesAPPLE", decodedStr, errorcode );
}
}
break;
}
m_name = 0;
GLMPRINTF(("-A-< ~CGLMQuery"));
}
void CGLMQuery::Start( void ) // "start counting"
{
m_ctx->MakeCurrent();
// on occlusion query:
// glBeginQueryARB on the OQ name. counting starts.
// on fence: glSetFence on m_name.
// note, fences finish themselves via command progress - OQ's do not.
Assert(!m_started);
Assert(!m_stopped);
Assert(!m_done);
m_nullQuery = (gl_nullqueries.GetInt() != 0); // latch value for remainder of query life
switch(m_params.m_type)
{
case EOcclusion:
{
if (m_nullQuery)
{
// do nothing..
}
else
{
glBeginQueryARB( GL_SAMPLES_PASSED_ARB, m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Start(OQ) saw %s error (%d) from glBeginQueryARB (GL_SAMPLES_PASSED_ARB) name=%d", decodedStr, errorcode, m_name );
}
}
}
break;
case EFence:
glSetFenceAPPLE( m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Start(fence) saw %s error (%d) from glSetFenceAPPLE name=%d", decodedStr, errorcode, m_name );
}
m_stopped = true; // caller should not call Stop on a fence, it self-stops
break;
}
m_started = true;
}
void CGLMQuery::Stop( void ) // "stop counting"
{
m_ctx->MakeCurrent();
Assert(m_started);
Assert(!m_stopped); // this will assert if you try to call Stop on a fence that is started
Assert(!m_done);
switch(m_params.m_type)
{
case EOcclusion:
{
if (m_nullQuery)
{
// do nothing..
}
else
{
glEndQueryARB( GL_SAMPLES_PASSED_ARB ); // we are only putting the request-to-stop-counting into the cmd stream.
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Stop(OQ) saw %s error (%d) from glEndQueryARB( GL_SAMPLES_PASSED_ARB ) name=%d", decodedStr, errorcode, m_name );
}
}
}
break;
case EFence:
// nop - you don't "end" a fence, you just test it and/or finish it out in Complete
break;
}
m_stopped = true;
}
bool CGLMQuery::IsDone( void )
{
m_ctx->MakeCurrent();
Assert(m_started);
Assert(m_stopped);
if(!m_done) // you can ask more than once, but we only check until it comes back as done.
{
// on occlusion: glGetQueryObjectivARB - large cost on pre SLGU, cheap after
// on fence: glTestFenceAPPLE on the fence
switch(m_params.m_type)
{
case EOcclusion: // just test the fence that was set after the query begin
{
if (m_nullQuery)
{
// do almost nothing.. but claim work is complete
m_done = true;
}
else
{
// prepare to pay a big price on drivers prior to 10.6.4+SLGU
GLint available = 0;
glGetQueryObjectivARB(m_name, GL_QUERY_RESULT_AVAILABLE_ARB, &available );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::IsDone saw %s error (%d) from glGetQueryObjectivARB(a2) name=%d", decodedStr, errorcode, m_name );
}
m_done = (available != 0);
}
}
break;
case EFence:
{
m_done = glTestFenceAPPLE( m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::IsDone saw %s error (%d) from glTestFenceAPPLE(b) name=%d", decodedStr, errorcode, m_name );
}
if (m_done)
{
glFinishFenceAPPLE( m_name ); // no set fence goes un-finished
errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::IsDone saw %s error (%d) from glFinishFenceAPPLE(b) name=%d", decodedStr, errorcode, m_name );
}
}
}
break;
}
}
return m_done;
}
void CGLMQuery::Complete( uint *result )
{
m_ctx->MakeCurrent();
uint resultval = 0;
GLint available = 0;
bool bogus_available = false;
// blocking call if not done
Assert(m_started);
Assert(m_stopped);
switch(m_params.m_type)
{
case EOcclusion:
{
if (m_nullQuery)
{
m_done = true;
resultval = 0; // we did say "null queries..."
}
else
{
// accept that the query is going to drain pipe in 10.6.4 and prior.
// check the error on the spot.
glGetQueryObjectivARB(m_name, GL_QUERY_RESULT_AVAILABLE_ARB, &available );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Complete saw %s error (%d) from glGetQueryObjectivARB GL_QUERY_RESULT_AVAILABLE_ARB name=%d", decodedStr, errorcode, m_name );
resultval=0;
}
else
{
if (!available)
{
// this does happen with some very modest frequency.
if (!m_ctx->Caps().m_hasPerfPackage1)
{
glFlush(); // ISTR some deadlock cases on pre-SLGU drivers if you didn't do this to kick the queue along..
}
}
glGetQueryObjectuivARB( m_name, GL_QUERY_RESULT_ARB, &resultval);
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Complete saw %s error (%d) from glGetQueryObjectivARB GL_QUERY_RESULT_ARB name=%d", decodedStr, errorcode, m_name );
resultval=0;
}
else
{
// resultval is legit
}
}
m_done = true;
}
}
break;
case EFence:
{
if(!m_done)
{
glFinishFenceAPPLE( m_name );
GLenum errorcode = GetQueryError();
if (errorcode)
{
const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
printf( "\nCGLMQuery::Complete saw %s error (%d) from glFinishFenceAPPLE (EFence) name=%d", decodedStr, errorcode, m_name );
}
m_done = true; // for clarity or if they try to Complete twice
}
}
break;
}
Assert( m_done );
// reset state for re-use - i.e. you have to call Complete if you want to re-use the object
m_started = m_stopped = m_done = false;
if (result) // caller may pass NULL if not interested in result, for example to clear a fence
{
*result = resultval;
}
}
// accessors for the started/stopped state
bool CGLMQuery::IsStarted ( void )
{
return m_started;
}
bool CGLMQuery::IsStopped ( void )
{
return m_stopped;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
//========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============//
//
// Purpose: provide some call-out glue to ObjC from the C++ GLMgr code
//
// $Revision: $
// $NoKeywords: $
//=============================================================================//
#include <Cocoa/Cocoa.h>
#undef MIN
#undef MAX
#define DONT_DEFINE_BOOL // Don't define BOOL!
#include "tier0/threadtools.h"
#include "tier1/interface.h"
#include "tier1/strtools.h"
#include "tier1/utllinkedlist.h"
#include "glmgr/glmgr.h"
// ------------------------------------------------------------------------------------ //
// some glue to let GLMgr call into NS/ObjC classes.
// ------------------------------------------------------------------------------------ //
bool NewNSGLContext( unsigned long *attribs, PseudoNSGLContextPtr nsglShareCtx, PseudoNSGLContextPtr *nsglCtxOut, CGLContextObj *cglCtxOut )
{
NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init ];
NSOpenGLPixelFormat *pixFmt = NULL;
NSOpenGLContext *nsglCtx = NULL;
bool result = true; // optimism
if (result)
{
pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs];
if (!pixFmt)
{
Debugger(); // bad news
result = false;
}
}
if (result)
{
nsglCtx = [[NSOpenGLContext alloc] initWithFormat: pixFmt shareContext: (NSOpenGLContext*) nsglShareCtx ];
if (!nsglCtx)
{
Debugger();
result = false;
}
}
if (result)
{
[nsglCtx makeCurrentContext];
*nsglCtxOut = nsglCtx;
*cglCtxOut = (CGLContextObj)[ (NSOpenGLContext*)nsglCtx CGLContextObj ];
}
else
{
*nsglCtxOut = NULL;
*cglCtxOut = NULL;
}
[tempPool release];
return result;
}
CGLContextObj GetCGLContextFromNSGL( PseudoNSGLContextPtr nsglCtx )
{
return (CGLContextObj)[ (NSOpenGLContext*)nsglCtx CGLContextObj];
}
void DelNSGLContext( PseudoNSGLContextPtr nsglCtx )
{
[ (NSOpenGLContext*)nsglCtx release ];
}

View File

@@ -0,0 +1,104 @@
//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// glmgrext.h
// helper file for extension testing and runtime importing of entry points
//
//===============================================================================
#pragma once
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <mach-o/dyld.h>
#include <stdlib.h>
#include <string.h>
#include "glmgr/glmgr.h"
PFNglColorMaskIndexedEXT pfnglColorMaskIndexedEXT;
PFNglEnableIndexedEXT pfnglEnableIndexedEXT;
PFNglDisableIndexedEXT pfnglDisableIndexedEXT;
PFNglGetFramebufferAttachmentParameteriv pfnglGetFramebufferAttachmentParameteriv;
PFNglUniformBufferEXT pfnglUniformBufferEXT;
void * NSGLGetProcAddress (const char *name)
{
NSSymbol symbol;
char *symbolName = (char *)malloc (strlen (name) + 2);
strcpy(symbolName + 1, name);
symbolName[0] = '_';
symbol = NULL;
if (NSIsSymbolNameDefined (symbolName))
symbol = NSLookupAndBindSymbol (symbolName);
free (symbolName);
return symbol ? NSAddressOfSymbol (symbol) : NULL;
}
void GLMSetupExtensions( void )
{
pfnglColorMaskIndexedEXT = (PFNglColorMaskIndexedEXT) NSGLGetProcAddress( "glColorMaskIndexedEXT" );
pfnglEnableIndexedEXT = (PFNglEnableIndexedEXT) NSGLGetProcAddress( "glEnableIndexedEXT" );
pfnglDisableIndexedEXT = (PFNglDisableIndexedEXT) NSGLGetProcAddress( "glDisableIndexedEXT" );
pfnglGetFramebufferAttachmentParameteriv = (PFNglGetFramebufferAttachmentParameteriv) NSGLGetProcAddress( "glGetFramebufferAttachmentParameteriv" );
pfnglUniformBufferEXT = (PFNglUniformBufferEXT) NSGLGetProcAddress( "glUniformBufferEXT" );
}
/*
#define INSTANTIATE_GL_IMPORTS
#include "glmgr/glmgr.h" // will include glmgrext.h
#undef INSTANTIATE_GL_IMPORTS
// helper class for looking up function names
// see http://andrewtolbert.com/svn/personal/OpenGLSuperBible/shared/gltools.cpp
// also http://developer.apple.com/mac/library/DOCUMENTATION/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_entrypts/opengl_entrypts.html
class CFunctionImporter
{
public:
CFBundleRef m_bundle;
CFunctionImporter( CFStringRef bundleID ) // for example CFSTR("com.apple.OpenGL")
{
m_bundle = CFBundleGetBundleWithIdentifier( bundleID );
if ( m_bundle )
CFRetain( m_bundle );
}
~CFunctionImporter()
{
if( m_bundle )
{
CFRelease(m_bundle);
m_bundle = NULL;
}
}
void *FindFunctionByName(CFStringRef name) // ex CFSTR("glColorMaskedIndexedEXT")
{
void *result = NULL;
if (m_bundle)
{
result = CFBundleGetFunctionPointerForName(m_bundle, name);
}
return result;
}
};
void GLMSetupExtensions( void )
{
CFunctionImporter importer( CFSTR("com.apple.OpenGL") );
#define DO_IMPORT(name) name = (name##FuncPtr)importer.FindFunctionByName( CFSTR(#name) );
#ifndef GL_EXT_draw_buffers2
// FIXME we're not checking for the extension string yet, we're just grabbing func ptrs
DO_IMPORT(glColorMaskIndexedEXT);
DO_IMPORT(glEnableIndexedEXT);
DO_IMPORT(glDisableIndexedEXT);
#endif
}
*/