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
+358
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;
}
+355
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
+413
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
+80
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 ];
}
+104
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
}
*/