You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
8.9 KiB
363 lines
8.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// TOGL CODE LICENSE |
|
// |
|
// Copyright 2011-2014 Valve Corporation |
|
// All Rights Reserved. |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|
// of this software and associated documentation files (the "Software"), to deal |
|
// in the Software without restriction, including without limitation the rights |
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
// copies of the Software, and to permit persons to whom the Software is |
|
// furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in |
|
// all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
// THE SOFTWARE. |
|
// |
|
// cglmquery.cpp |
|
// |
|
//=============================================================================== |
|
|
|
#include "togles/rendermechanism.h" |
|
|
|
#ifndef _WIN32 |
|
#include <unistd.h> |
|
#endif |
|
|
|
// memdbgon -must- be the last include file in a .cpp file. |
|
#include "tier0/memdbgon.h" |
|
|
|
//=============================================================================== |
|
|
|
// http://www.opengl.org/registry/specs/ARB/occlusion_query.txt |
|
// Workaround for "Calling either GenQueriesARB or DeleteQueriesARB while any query of any target is active causes an INVALID_OPERATION error to be generated." |
|
uint CGLMQuery::s_nTotalOcclusionQueryCreatesOrDeletes; |
|
|
|
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" ); |
|
|
|
|
|
//=============================================================================== |
|
|
|
CGLMQuery::CGLMQuery( GLMContext *ctx, GLMQueryParams *params ) |
|
{ |
|
// get the type of query requested |
|
// generate name(s) needed |
|
// set initial state appropriately |
|
|
|
m_ctx = ctx; |
|
m_params = *params; |
|
|
|
m_name = 0; |
|
m_syncobj = 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) |
|
gGL->glGenQueries( 1, &m_name ); |
|
s_nTotalOcclusionQueryCreatesOrDeletes++; |
|
GLMPRINTF(("-A- CGLMQuery(OQ) created name %d", m_name)); |
|
} |
|
break; |
|
|
|
case EFence: |
|
//make a fence - no aux fence needed |
|
|
|
m_syncobj = 0; |
|
|
|
if (gGL->m_bHave_GL_ARB_sync) |
|
{ /* GL_ARB_sync doesn't separate gen and set, so we do glFenceSync() later. */ } |
|
else if (gGL->m_bHave_GL_NV_fence) |
|
gGL->glGenFencesNV(1, &m_name ); |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
gGL->glGenFencesAPPLE(1, &m_name ); |
|
|
|
GLMPRINTF(("-A- CGLMQuery(fence) created name %d", m_name)); |
|
break; |
|
} |
|
|
|
} |
|
|
|
CGLMQuery::~CGLMQuery() |
|
{ |
|
GLMPRINTF(("-A-> ~CGLMQuery")); |
|
|
|
// make sure query has completed (might not be necessary) |
|
// delete the name(s) |
|
|
|
switch(m_params.m_type) |
|
{ |
|
case EOcclusion: |
|
{ |
|
// do a finish occlusion query ? |
|
GLMPRINTF(("-A- ~CGLMQuery(OQ) deleting name %d", m_name)); |
|
gGL->glDeleteQueries(1, &m_name ); |
|
s_nTotalOcclusionQueryCreatesOrDeletes++; |
|
} |
|
break; |
|
|
|
case EFence: |
|
{ |
|
// do a finish fence ? |
|
GLMPRINTF(("-A- ~CGLMQuery(fence) deleting name %llu", gGL->m_bHave_GL_ARB_sync ? (unsigned long long) m_syncobj : (unsigned long long) m_name)); |
|
#ifdef HAVE_GL_ARB_SYNC |
|
if (gGL->m_bHave_GL_ARB_sync) |
|
gGL->glDeleteSync( m_syncobj ); |
|
else |
|
#endif |
|
if (gGL->m_bHave_GL_NV_fence) |
|
gGL->glDeleteFencesNV(1, &m_name ); |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
gGL->glDeleteFencesAPPLE(1, &m_name ); |
|
} |
|
break; |
|
} |
|
|
|
m_name = 0; |
|
m_syncobj = 0; |
|
|
|
GLMPRINTF(("-A-< ~CGLMQuery")); |
|
} |
|
|
|
|
|
|
|
|
|
void CGLMQuery::Start( void ) // "start counting" |
|
{ |
|
m_nullQuery = (gl_nullqueries.GetInt() != 0); // latch value for remainder of query life |
|
|
|
m_started = true; |
|
m_stopped = false; |
|
m_done = false; |
|
|
|
switch(m_params.m_type) |
|
{ |
|
case EOcclusion: |
|
{ |
|
if (m_nullQuery) |
|
{ |
|
// do nothing.. |
|
} |
|
else |
|
{ |
|
gGL->glBeginQuery( GL_ANY_SAMPLES_PASSED, m_name ); |
|
} |
|
} |
|
break; |
|
|
|
case EFence: |
|
#ifdef HAVE_GL_ARB_SYNC |
|
if (gGL->m_bHave_GL_ARB_sync) |
|
{ |
|
if (m_syncobj != 0) gGL->glDeleteSync(m_syncobj); |
|
m_syncobj = gGL->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
|
} |
|
else |
|
#endif |
|
if (gGL->m_bHave_GL_NV_fence) |
|
gGL->glSetFenceNV( m_name, GL_ALL_COMPLETED_NV ); |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
gGL->glSetFenceAPPLE( m_name ); |
|
|
|
m_stopped = true; // caller should not call Stop on a fence, it self-stops |
|
break; |
|
} |
|
} |
|
|
|
void CGLMQuery::Stop( void ) // "stop counting" |
|
{ |
|
Assert(m_started); |
|
|
|
if ( m_stopped ) |
|
return; |
|
|
|
switch(m_params.m_type) |
|
{ |
|
case EOcclusion: |
|
{ |
|
if (m_nullQuery) |
|
{ |
|
// do nothing.. |
|
} |
|
else |
|
{ |
|
gGL->glEndQuery( GL_ANY_SAMPLES_PASSED ); // we are only putting the request-to-stop-counting into the cmd stream. |
|
} |
|
} |
|
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 ) |
|
{ |
|
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: glTestFence* 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 |
|
|
|
GLuint available = 0; |
|
gGL->glGetQueryObjectuiv(m_name, GL_QUERY_RESULT_AVAILABLE, &available ); |
|
|
|
m_done = (available != 0); |
|
} |
|
} |
|
break; |
|
|
|
case EFence: |
|
{ |
|
#ifdef HAVE_GL_ARB_SYNC |
|
if (gGL->m_bHave_GL_ARB_sync) |
|
m_done = (gGL->glClientWaitSync( m_syncobj, 0, 0 ) == GL_ALREADY_SIGNALED); |
|
else |
|
#endif |
|
if ( m_name == 0 ) |
|
m_done = true; |
|
else if (gGL->m_bHave_GL_NV_fence) |
|
m_done = gGL->glTestFenceNV( m_name ) != 0; |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
m_done = gGL->glTestFenceAPPLE( m_name ) != 0; |
|
|
|
if (m_done) |
|
{ |
|
if (gGL->m_bHave_GL_ARB_sync) |
|
{ /* no-op; we already know it's set to GL_ALREADY_SIGNALED. */ } |
|
else |
|
{ |
|
if (gGL->m_bHave_GL_NV_fence) |
|
gGL->glFinishFenceNV( m_name ); // no set fence goes un-finished |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
gGL->glFinishFenceAPPLE( m_name ); // no set fence goes un-finished |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return m_done; |
|
} |
|
|
|
void CGLMQuery::Complete( uint *result ) |
|
{ |
|
uint resultval = 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 |
|
{ |
|
gGL->glGetQueryObjectuiv( m_name, GL_QUERY_RESULT, &resultval); |
|
m_done = true; |
|
} |
|
} |
|
break; |
|
|
|
case EFence: |
|
{ |
|
if(!m_done) |
|
{ |
|
#ifdef HAVE_GL_ARB_SYNC |
|
if (gGL->m_bHave_GL_ARB_sync) |
|
{ |
|
if (gGL->glClientWaitSync( m_syncobj, 0, 0 ) != GL_ALREADY_SIGNALED) |
|
{ |
|
GLenum syncstate; |
|
do { |
|
const GLuint64 timeout = 10 * ((GLuint64)1000 * 1000 * 1000); // 10 seconds in nanoseconds. |
|
(void)timeout; |
|
syncstate = gGL->glClientWaitSync( m_syncobj, GL_SYNC_FLUSH_COMMANDS_BIT, 0 ); |
|
} while (syncstate == GL_TIMEOUT_EXPIRED); // any errors or success break out of this loop. |
|
} |
|
} |
|
else |
|
#endif |
|
if (gGL->m_bHave_GL_NV_fence) |
|
gGL->glFinishFenceNV( m_name ); |
|
else if (gGL->m_bHave_GL_APPLE_fence) |
|
gGL->glFinishFenceAPPLE( 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; |
|
} |
|
|
|
|