-
Notifications
You must be signed in to change notification settings - Fork 270
/
Copy pathocclusionquerymgr.cpp
258 lines (226 loc) · 9.07 KB
/
occlusionquerymgr.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "pch_materialsystem.h"
#define MATSYS_INTERNAL
#include "occlusionquerymgr.h"
#include "imaterialsysteminternal.h"
#include "imatrendercontextinternal.h"
// NOTE: This must be the last file included!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
static COcclusionQueryMgr s_OcclusionQueryMgr;
COcclusionQueryMgr *g_pOcclusionQueryMgr = &s_OcclusionQueryMgr;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
COcclusionQueryMgr::COcclusionQueryMgr()
{
m_nFrameCount = 0;
}
//-----------------------------------------------------------------------------
// Allocate and delete query objects.
//-----------------------------------------------------------------------------
OcclusionQueryObjectHandle_t COcclusionQueryMgr::CreateOcclusionQueryObject( )
{
m_Mutex.Lock();
intp h = m_OcclusionQueryObjects.AddToTail();
m_Mutex.Unlock();
return (OcclusionQueryObjectHandle_t)h;
}
void COcclusionQueryMgr::OnCreateOcclusionQueryObject( OcclusionQueryObjectHandle_t h )
{
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
{
m_OcclusionQueryObjects[(intp)h].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject( );
}
}
// Flushes an outstanding query
// HEY - Be very careful using this method - it causes a full pipeline flush/stall!
void COcclusionQueryMgr::FlushQuery( OcclusionQueryObjectHandle_t hOcclusionQuery, int nIndex )
{
// Flush out any previous queries
intp h = (intp)hOcclusionQuery;
if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] )
{
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex];
while ( OCCLUSION_QUERY_RESULT_PENDING == g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true ) )
continue;
}
}
void COcclusionQueryMgr::DestroyOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery )
{
intp h = (intp)hOcclusionQuery;
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
{
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
{
if ( m_OcclusionQueryObjects[h].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
{
g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[h].m_QueryHandle[i] );
}
}
m_Mutex.Lock();
m_OcclusionQueryObjects.Remove( h );
m_Mutex.Unlock();
}
}
//-----------------------------------------------------------------------------
// Advance frame
//-----------------------------------------------------------------------------
void COcclusionQueryMgr::AdvanceFrame()
{
++m_nFrameCount;
}
//-----------------------------------------------------------------------------
// Alt-tab support
// NOTE: This doesn't queue anything up
//-----------------------------------------------------------------------------
void COcclusionQueryMgr::AllocOcclusionQueryObjects( void )
{
FOR_EACH_LL( m_OcclusionQueryObjects, iterator )
{
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
{
m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = g_pShaderAPI->CreateOcclusionQueryObject();
m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false; // any in-flight queries are never returning
}
}
}
void COcclusionQueryMgr::FreeOcclusionQueryObjects( void )
{
FOR_EACH_LL( m_OcclusionQueryObjects, iterator )
{
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
{
if ( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
{
g_pShaderAPI->DestroyOcclusionQueryObject( m_OcclusionQueryObjects[iterator].m_QueryHandle[i] );
m_OcclusionQueryObjects[iterator].m_QueryHandle[i] = INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE;
m_OcclusionQueryObjects[iterator].m_bHasBeenIssued[i] = false;
}
}
}
}
//-----------------------------------------------------------------------------
// Used to make the handle think it's never had a successful query before
//-----------------------------------------------------------------------------
void COcclusionQueryMgr::ResetOcclusionQueryObject( OcclusionQueryObjectHandle_t hOcclusionQuery )
{
intp h = (intp)hOcclusionQuery;
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
{
// Forget we've issued any previous queries - there's no need to flush them.
for ( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++)
{
m_OcclusionQueryObjects[h].m_bHasBeenIssued[i] = false;
}
m_OcclusionQueryObjects[h].m_LastResult = -1;
m_OcclusionQueryObjects[h].m_nFrameIssued = -1;
}
}
//-----------------------------------------------------------------------------
// Bracket drawing with begin and end so that we can get counts next frame.
//-----------------------------------------------------------------------------
void COcclusionQueryMgr::BeginOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery )
{
intp h = (intp)hOcclusionQuery;
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
{
int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue;
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent];
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
{
// If it's been issued, but we haven't gotten a result when we polled last time,
// try polling one last time, since we can't poll again after we issue again.
if ( m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] )
{
int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, false );
if ( ( nPixels == OCCLUSION_QUERY_RESULT_PENDING ) && ( m_OcclusionQueryObjects[h].m_nFrameIssued == m_nFrameCount ) )
{
static int s_nWarnCount = 0;
if ( s_nWarnCount++ < 5 )
{
DevWarning( "blocking issue in occlusion queries! Grab brian!\n" );
}
}
while( !OCCLUSION_QUERY_FINISHED( nPixels ) )
{
// We're going to reuse this query, so issue a flush to force the query results to come back.
nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery, true );
}
if ( nPixels >= 0 )
{
m_OcclusionQueryObjects[h].m_LastResult = nPixels;
}
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = false;
}
g_pShaderAPI->BeginOcclusionQueryDrawing( hQuery );
}
}
}
void COcclusionQueryMgr::EndOcclusionQueryDrawing( OcclusionQueryObjectHandle_t hOcclusionQuery )
{
intp h = (intp)hOcclusionQuery;
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
{
int nCurrent = m_OcclusionQueryObjects[h].m_nCurrentIssue;
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nCurrent];
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE )
{
g_pShaderAPI->EndOcclusionQueryDrawing( hQuery );
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nCurrent] = true;
m_OcclusionQueryObjects[h].m_nFrameIssued = m_nFrameCount;
nCurrent = ( nCurrent + 1 ) % COUNT_OCCLUSION_QUERY_STACK;
m_OcclusionQueryObjects[h].m_nCurrentIssue = nCurrent;
}
}
}
//-----------------------------------------------------------------------------
// Get the number of pixels rendered between begin and end on an earlier frame.
// Calling this in the same frame is a huge perf hit!
//-----------------------------------------------------------------------------
void COcclusionQueryMgr::OcclusionQuery_IssueNumPixelsRenderedQuery( OcclusionQueryObjectHandle_t hOcclusionQuery )
{
intp h = (intp)hOcclusionQuery;
Assert( m_OcclusionQueryObjects.IsValidIndex( h ) );
if ( m_OcclusionQueryObjects.IsValidIndex( h ) )
{
for( int i = 0; i < COUNT_OCCLUSION_QUERY_STACK; i++ )
{
int nIndex = ( m_OcclusionQueryObjects[h].m_nCurrentIssue + i ) % COUNT_OCCLUSION_QUERY_STACK;
ShaderAPIOcclusionQuery_t hQuery = m_OcclusionQueryObjects[h].m_QueryHandle[nIndex];
if ( hQuery != INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE && m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] )
{
int nPixels = g_pShaderAPI->OcclusionQuery_GetNumPixelsRendered( hQuery );
if ( nPixels == OCCLUSION_QUERY_RESULT_ERROR )
{
// In GL mode, it's possible for queries to fail (say when mat_queue_mode is toggled). In this case, just clear m_bHasBeenIssued and forget we ever issued this query.
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false;
}
else if ( nPixels >= 0 )
{
m_OcclusionQueryObjects[h].m_LastResult = nPixels;
m_OcclusionQueryObjects[h].m_bHasBeenIssued[nIndex] = false;
}
}
}
}
}
int COcclusionQueryMgr::OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t h, bool bDoQuery )
{
if ( bDoQuery )
{
OcclusionQuery_IssueNumPixelsRenderedQuery( h );
}
int nPixels = m_OcclusionQueryObjects[(intp)h].m_LastResult;
return nPixels;
}