1 /* Copyright (c) 2014 Wildfire Games
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 #include "precompiled.h"
25 #include "Profiler2GPU.h"
28 #include "lib/allocators/shared_ptr.h"
29 #include "ps/ConfigDB.h"
30 #include "ps/Profiler2.h"
34 class CProfiler2GPU_base
36 NONCOPYABLE(CProfiler2GPU_base
);
39 CProfiler2GPU_base(CProfiler2
& profiler
, const char* name
) :
40 m_Profiler(profiler
), m_Storage(profiler
, name
)
42 m_Storage
.RecordSyncMarker(m_Profiler
.GetTime());
43 m_Storage
.Record(CProfiler2::ITEM_EVENT
, m_Profiler
.GetTime(), "thread start");
45 m_Profiler
.AddThreadStorage(&m_Storage
);
50 m_Profiler
.RemoveThreadStorage(&m_Storage
);
53 CProfiler2
& m_Profiler
;
54 CProfiler2::ThreadStorage m_Storage
;
57 //////////////////////////////////////////////////////////////////////////
59 // Base class for ARB_timer_query, EXT_timer_query
60 class CProfiler2GPU_timer_query
: public CProfiler2GPU_base
63 CProfiler2GPU_timer_query(CProfiler2
& profiler
, const char* name
) :
64 CProfiler2GPU_base(profiler
, name
)
68 ~CProfiler2GPU_timer_query()
70 if (!m_FreeQueries
.empty())
71 pglDeleteQueriesARB(m_FreeQueries
.size(), &m_FreeQueries
[0]);
75 // Returns a new GL query object (or a recycled old one)
78 if (m_FreeQueries
.empty())
80 // Generate a batch of new queries
81 m_FreeQueries
.resize(8);
82 pglGenQueriesARB(m_FreeQueries
.size(), &m_FreeQueries
[0]);
86 GLuint query
= m_FreeQueries
.back();
87 m_FreeQueries
.pop_back();
91 std::vector
<GLuint
> m_FreeQueries
; // query objects that are allocated but not currently in used
94 //////////////////////////////////////////////////////////////////////////
97 * GL_ARB_timer_query supports sync and async queries for absolute GPU
98 * timestamps, which lets us time regions of code relative to the CPU.
99 * At the start of a frame, we record the CPU time and sync GPU timestamp,
100 * giving the time-vs-timestamp offset.
101 * At each enter/leave-region event, we do an async GPU timestamp query.
102 * When all the queries for a frame have their results available,
103 * we convert their GPU timestamps into CPU times and record the data.
105 class CProfiler2GPU_ARB_timer_query
: public CProfiler2GPU_timer_query
111 bool isEnter
; // true if entering region; false if leaving
118 double syncTimeStart
; // CPU time at start of maybe this frame or a recent one
119 GLint64 syncTimestampStart
; // GL timestamp corresponding to timeStart
121 std::vector
<SEvent
> events
;
124 std::deque
<SFrame
> m_Frames
;
127 static bool IsSupported()
129 return ogl_HaveExtension("GL_ARB_timer_query");
132 CProfiler2GPU_ARB_timer_query(CProfiler2
& profiler
) :
133 CProfiler2GPU_timer_query(profiler
, "gpu_arb")
135 // TODO: maybe we should check QUERY_COUNTER_BITS to ensure it's
136 // high enough (but apparently it might trigger GL errors on ATI)
139 ~CProfiler2GPU_ARB_timer_query()
141 // Pop frames to return queries to the free list
142 while (!m_Frames
.empty())
151 frame
.num
= m_Profiler
.GetFrameNumber();
153 // On (at least) some NVIDIA Windows drivers, when GPU-bound, or when
154 // vsync enabled and not CPU-bound, the first glGet* call at the start
155 // of a frame appears to trigger a wait (to stop the GPU getting too
156 // far behind, or to wait for the vsync period).
157 // That will be this GL_TIMESTAMP get, which potentially distorts the
158 // reported results. So we'll only do it fairly rarely, and for most
159 // frames we'll just assume the clocks don't drift much
161 const double RESYNC_PERIOD
= 1.0; // seconds
163 double now
= m_Profiler
.GetTime();
165 if (m_Frames
.empty() || now
> m_Frames
.back().syncTimeStart
+ RESYNC_PERIOD
)
167 PROFILE2("profile timestamp resync");
169 pglGetInteger64v(GL_TIMESTAMP
, &frame
.syncTimestampStart
);
172 frame
.syncTimeStart
= m_Profiler
.GetTime();
173 // (Have to do GetTime again after GL_TIMESTAMP, because GL_TIMESTAMP
174 // might wait a while before returning its now-current timestamp)
178 // Reuse the previous frame's sync data
179 frame
.syncTimeStart
= m_Frames
[m_Frames
.size()-1].syncTimeStart
;
180 frame
.syncTimestampStart
= m_Frames
[m_Frames
.size()-1].syncTimestampStart
;
183 m_Frames
.push_back(frame
);
185 RegionEnter("frame");
190 RegionLeave("frame");
193 void RecordRegion(const char* id
, bool isEnter
)
195 ENSURE(!m_Frames
.empty());
196 SFrame
& frame
= m_Frames
.back();
200 event
.query
= NewQuery();
201 event
.isEnter
= isEnter
;
203 pglQueryCounter(event
.query
, GL_TIMESTAMP
);
206 frame
.events
.push_back(event
);
209 void RegionEnter(const char* id
)
211 RecordRegion(id
, true);
214 void RegionLeave(const char* id
)
216 RecordRegion(id
, false);
223 while (!m_Frames
.empty())
225 SFrame
& frame
= m_Frames
.front();
227 // Queries become available in order so we only need to check the last one
229 pglGetQueryObjectivARB(frame
.events
.back().query
, GL_QUERY_RESULT_AVAILABLE
, &available
);
234 // The frame's queries are now available, so retrieve and record all their results:
236 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
238 GLuint64 queryTimestamp
= 0;
239 pglGetQueryObjectui64v(frame
.events
[i
].query
, GL_QUERY_RESULT
, &queryTimestamp
);
240 // (use the non-suffixed function here, as defined by GL_ARB_timer_query)
243 // Convert to absolute CPU-clock time
244 double t
= frame
.syncTimeStart
+ (double)(queryTimestamp
- frame
.syncTimestampStart
) / 1e9
;
246 // Record a frame-start for syncing
248 m_Storage
.RecordFrameStart(t
);
250 if (frame
.events
[i
].isEnter
)
251 m_Storage
.Record(CProfiler2::ITEM_ENTER
, t
, frame
.events
[i
].id
);
253 m_Storage
.RecordLeave(t
);
255 // Associate the frame number with the "frame" region
257 m_Storage
.RecordAttributePrintf("%u", frame
.num
);
266 ENSURE(!m_Frames
.empty());
267 SFrame
& frame
= m_Frames
.front();
268 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
269 m_FreeQueries
.push_back(frame
.events
[i
].query
);
270 m_Frames
.pop_front();
274 //////////////////////////////////////////////////////////////////////////
277 * GL_EXT_timer_query only supports async queries for elapsed time,
278 * and only a single simultaneous query.
279 * We can't correctly convert it to absolute time, so we just pretend
280 * each GPU frame starts the same time as the CPU for that frame.
281 * We do a query for elapsed time between every adjacent enter/leave-region event.
282 * When all the queries for a frame have their results available,
283 * we sum the elapsed times to calculate when each event occurs within the
284 * frame, and record the data.
286 class CProfiler2GPU_EXT_timer_query
: public CProfiler2GPU_timer_query
291 GLuint query
; // query for time elapsed from this event until the next, or 0 for final event
292 bool isEnter
; // true if entering region; false if leaving
298 double timeStart
; // CPU time at frame start
299 std::vector
<SEvent
> events
;
302 std::deque
<SFrame
> m_Frames
;
305 static bool IsSupported()
307 return ogl_HaveExtension("GL_EXT_timer_query");
310 CProfiler2GPU_EXT_timer_query(CProfiler2
& profiler
) :
311 CProfiler2GPU_timer_query(profiler
, "gpu_ext")
315 ~CProfiler2GPU_EXT_timer_query()
317 // Pop frames to return queries to the free list
318 while (!m_Frames
.empty())
327 frame
.num
= m_Profiler
.GetFrameNumber();
328 frame
.timeStart
= m_Profiler
.GetTime();
330 m_Frames
.push_back(frame
);
332 RegionEnter("frame");
337 RegionLeave("frame");
339 pglEndQueryARB(GL_TIME_ELAPSED
);
343 void RecordRegion(const char* id
, bool isEnter
)
345 ENSURE(!m_Frames
.empty());
346 SFrame
& frame
= m_Frames
.back();
348 // Must call glEndQuery before calling glGenQueries (via NewQuery),
349 // for compatibility with the GL_EXT_timer_query spec (which says
350 // GL_INVALID_OPERATION if a query of any target is active; the ARB
351 // spec and OpenGL specs don't appear to say that, but the AMD drivers
352 // implement that error (see Trac #1033))
354 if (!frame
.events
.empty())
356 pglEndQueryARB(GL_TIME_ELAPSED
);
362 event
.query
= NewQuery();
363 event
.isEnter
= isEnter
;
365 pglBeginQueryARB(GL_TIME_ELAPSED
, event
.query
);
368 frame
.events
.push_back(event
);
371 void RegionEnter(const char* id
)
373 RecordRegion(id
, true);
376 void RegionLeave(const char* id
)
378 RecordRegion(id
, false);
384 while (!m_Frames
.empty())
386 SFrame
& frame
= m_Frames
.front();
388 // Queries become available in order so we only need to check the last one
390 pglGetQueryObjectivARB(frame
.events
.back().query
, GL_QUERY_RESULT_AVAILABLE
, &available
);
395 // The frame's queries are now available, so retrieve and record all their results:
397 double t
= frame
.timeStart
;
398 m_Storage
.RecordFrameStart(t
);
400 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
402 if (frame
.events
[i
].isEnter
)
403 m_Storage
.Record(CProfiler2::ITEM_ENTER
, t
, frame
.events
[i
].id
);
405 m_Storage
.RecordLeave(t
);
407 // Associate the frame number with the "frame" region
409 m_Storage
.RecordAttributePrintf("%u", frame
.num
);
411 // Advance by the elapsed time to the next event
412 GLuint64 queryElapsed
= 0;
413 pglGetQueryObjectui64vEXT(frame
.events
[i
].query
, GL_QUERY_RESULT
, &queryElapsed
);
414 // (use the EXT-suffixed function here, as defined by GL_EXT_timer_query)
416 t
+= (double)queryElapsed
/ 1e9
;
425 ENSURE(!m_Frames
.empty());
426 SFrame
& frame
= m_Frames
.front();
427 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
428 m_FreeQueries
.push_back(frame
.events
[i
].query
);
429 m_Frames
.pop_front();
433 //////////////////////////////////////////////////////////////////////////
436 * GL_INTEL_performance_queries is not officially documented
437 * (see http://zaynar.co.uk/docs/gl-intel-performance-queries.html)
438 * but it's potentially useful so we'll support it anyway.
439 * It supports async queries giving elapsed time plus a load of other
440 * counters that we'd like to use, and supports many simultaneous queries
441 * (unlike GL_EXT_timer_query).
442 * There are multiple query types (typically 2), each with its own set of
444 * On each enter-region event, we start a new set of queries.
445 * On each leave-region event, we end the corresponding set of queries.
446 * We can't tell the offsets between the enter events of nested regions,
447 * so we pretend they all got entered at the same time.
449 class CProfiler2GPU_INTEL_performance_queries
: public CProfiler2GPU_base
455 std::vector
<GLuint
> queries
; // if isEnter, one per SPerfQueryType; else empty
461 double timeStart
; // CPU time at frame start
462 std::vector
<SEvent
> events
;
463 std::vector
<size_t> activeRegions
; // stack of indexes into events
466 std::deque
<SFrame
> m_Frames
;
468 // Counters listed by the graphics driver for a particular query type
478 // Query types listed by the graphics driver
479 struct SPerfQueryType
483 GLuint counterBufferSize
;
484 std::vector
<SPerfCounter
> counters
;
486 std::vector
<GLuint
> freeQueries
; // query objects that are allocated but not currently in use
489 std::vector
<SPerfQueryType
> m_QueryTypes
;
491 #define INTEL_PERFQUERIES_NONBLOCK 0x83FA
492 #define INTEL_PERFQUERIES_BLOCK 0x83FB
493 #define INTEL_PERFQUERIES_TYPE_UNSIGNED_INT 0x9402
494 #define INTEL_PERFQUERIES_TYPE_UNSIGNED_INT64 0x9403
495 #define INTEL_PERFQUERIES_TYPE_FLOAT 0x9404
496 #define INTEL_PERFQUERIES_TYPE_BOOL 0x9406
499 static bool IsSupported()
501 return ogl_HaveExtension("GL_INTEL_performance_queries");
504 CProfiler2GPU_INTEL_performance_queries(CProfiler2
& profiler
) :
505 CProfiler2GPU_base(profiler
, "gpu_intel")
510 ~CProfiler2GPU_INTEL_performance_queries()
512 // Pop frames to return queries to the free list
513 while (!m_Frames
.empty())
516 for (size_t i
= 0; i
< m_QueryTypes
.size(); ++i
)
517 for (size_t j
= 0; j
< m_QueryTypes
[i
].freeQueries
.size(); ++j
)
518 pglDeletePerfQueryINTEL(m_QueryTypes
[i
].freeQueries
[j
]);
528 frame
.num
= m_Profiler
.GetFrameNumber();
529 frame
.timeStart
= m_Profiler
.GetTime();
531 m_Frames
.push_back(frame
);
533 RegionEnter("frame");
538 RegionLeave("frame");
541 void RegionEnter(const char* id
)
543 ENSURE(!m_Frames
.empty());
544 SFrame
& frame
= m_Frames
.back();
548 event
.isEnter
= true;
550 for (size_t i
= 0; i
< m_QueryTypes
.size(); ++i
)
552 GLuint id
= NewQuery(i
);
553 pglBeginPerfQueryINTEL(id
);
555 event
.queries
.push_back(id
);
558 frame
.activeRegions
.push_back(frame
.events
.size());
560 frame
.events
.push_back(event
);
563 void RegionLeave(const char* id
)
565 ENSURE(!m_Frames
.empty());
566 SFrame
& frame
= m_Frames
.back();
568 ENSURE(!frame
.activeRegions
.empty());
569 SEvent
& activeEvent
= frame
.events
[frame
.activeRegions
.back()];
571 for (size_t i
= 0; i
< m_QueryTypes
.size(); ++i
)
573 pglEndPerfQueryINTEL(activeEvent
.queries
[i
]);
577 frame
.activeRegions
.pop_back();
581 event
.isEnter
= false;
582 frame
.events
.push_back(event
);
586 GLuint
NewQuery(size_t queryIdx
)
588 ENSURE(queryIdx
< m_QueryTypes
.size());
590 if (m_QueryTypes
[queryIdx
].freeQueries
.empty())
593 pglCreatePerfQueryINTEL(m_QueryTypes
[queryIdx
].queryTypeId
, &id
);
598 GLuint id
= m_QueryTypes
[queryIdx
].freeQueries
.back();
599 m_QueryTypes
[queryIdx
].freeQueries
.pop_back();
605 while (!m_Frames
.empty())
607 SFrame
& frame
= m_Frames
.front();
609 // Queries don't become available in order, so check them all before
610 // trying to read the results from any
611 for (size_t j
= 0; j
< m_QueryTypes
.size(); ++j
)
613 size_t size
= m_QueryTypes
[j
].counterBufferSize
;
614 shared_ptr
<char> buf(new char[size
], ArrayDeleter());
616 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
618 if (!frame
.events
[i
].isEnter
)
622 pglGetPerfQueryDataINTEL(frame
.events
[i
].queries
[j
], INTEL_PERFQUERIES_NONBLOCK
, size
, buf
.get(), &length
);
629 double lastTime
= frame
.timeStart
;
630 std::stack
<double> endTimes
;
632 m_Storage
.RecordFrameStart(frame
.timeStart
);
634 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
636 if (frame
.events
[i
].isEnter
)
638 m_Storage
.Record(CProfiler2::ITEM_ENTER
, lastTime
, frame
.events
[i
].id
);
641 m_Storage
.RecordAttributePrintf("%u", frame
.num
);
643 double elapsed
= 0.0;
645 for (size_t j
= 0; j
< m_QueryTypes
.size(); ++j
)
648 char* buf
= new char[m_QueryTypes
[j
].counterBufferSize
];
649 pglGetPerfQueryDataINTEL(frame
.events
[i
].queries
[j
], INTEL_PERFQUERIES_BLOCK
, m_QueryTypes
[j
].counterBufferSize
, buf
, &length
);
651 ENSURE(length
== m_QueryTypes
[j
].counterBufferSize
);
653 m_Storage
.RecordAttributePrintf("-- %s --", m_QueryTypes
[j
].name
.c_str());
655 for (size_t k
= 0; k
< m_QueryTypes
[j
].counters
.size(); ++k
)
657 SPerfCounter
& counter
= m_QueryTypes
[j
].counters
[k
];
659 if (counter
.type
== INTEL_PERFQUERIES_TYPE_UNSIGNED_INT
)
661 ENSURE(counter
.size
== 4);
663 memcpy(&value
, buf
+ counter
.offset
, counter
.size
);
664 m_Storage
.RecordAttributePrintf("%s: %u", counter
.name
.c_str(), value
);
666 else if (counter
.type
== INTEL_PERFQUERIES_TYPE_UNSIGNED_INT64
)
668 ENSURE(counter
.size
== 8);
670 memcpy(&value
, buf
+ counter
.offset
, counter
.size
);
671 m_Storage
.RecordAttributePrintf("%s: %.0f", counter
.name
.c_str(), (double)value
);
673 if (counter
.name
== "TotalTime")
674 elapsed
= (double)value
/ 1e6
;
676 else if (counter
.type
== INTEL_PERFQUERIES_TYPE_FLOAT
)
678 ENSURE(counter
.size
== 4);
680 memcpy(&value
, buf
+ counter
.offset
, counter
.size
);
681 m_Storage
.RecordAttributePrintf("%s: %f", counter
.name
.c_str(), value
);
683 else if (counter
.type
== INTEL_PERFQUERIES_TYPE_BOOL
)
685 ENSURE(counter
.size
== 4);
687 memcpy(&value
, buf
+ counter
.offset
, counter
.size
);
688 ENSURE(value
== 0 || value
== 1);
689 m_Storage
.RecordAttributePrintf("%s: %u", counter
.name
.c_str(), value
);
693 //debug_warn(L"unrecognised Intel performance counter type");
700 endTimes
.push(lastTime
+ elapsed
);
704 lastTime
= endTimes
.top();
706 m_Storage
.RecordLeave(lastTime
);
716 ENSURE(!m_Frames
.empty());
717 SFrame
& frame
= m_Frames
.front();
718 for (size_t i
= 0; i
< frame
.events
.size(); ++i
)
719 if (frame
.events
[i
].isEnter
)
720 for (size_t j
= 0; j
< m_QueryTypes
.size(); ++j
)
721 m_QueryTypes
[j
].freeQueries
.push_back(frame
.events
[i
].queries
[j
]);
722 m_Frames
.pop_front();
725 void LoadPerfCounters()
728 pglGetFirstPerfQueryIdINTEL(&queryTypeId
);
733 GLuint counterBufferSize
, numCounters
, maxQueries
, unknown
;
734 pglGetPerfQueryInfoINTEL(queryTypeId
, ARRAY_SIZE(queryName
), queryName
, &counterBufferSize
, &numCounters
, &maxQueries
, &unknown
);
736 ENSURE(unknown
== 1);
738 SPerfQueryType query
;
739 query
.queryTypeId
= queryTypeId
;
740 query
.name
= queryName
;
741 query
.counterBufferSize
= counterBufferSize
;
743 for (GLuint counterId
= 1; counterId
<= numCounters
; ++counterId
)
745 char counterName
[256];
746 char counterDesc
[2048];
747 GLuint counterOffset
, counterSize
, counterUsage
, counterType
;
749 pglGetPerfCounterInfoINTEL(queryTypeId
, counterId
, ARRAY_SIZE(counterName
), counterName
, ARRAY_SIZE(counterDesc
), counterDesc
, &counterOffset
, &counterSize
, &counterUsage
, &counterType
, &unknown2
);
751 ENSURE(unknown2
== 0 || unknown2
== 1);
753 SPerfCounter counter
;
754 counter
.name
= counterName
;
755 counter
.desc
= counterDesc
;
756 counter
.offset
= counterOffset
;
757 counter
.size
= counterSize
;
758 counter
.type
= counterType
;
759 query
.counters
.push_back(counter
);
762 m_QueryTypes
.push_back(query
);
764 pglGetNextPerfQueryIdINTEL(queryTypeId
, &queryTypeId
);
767 } while (queryTypeId
);
771 //////////////////////////////////////////////////////////////////////////
773 CProfiler2GPU::CProfiler2GPU(CProfiler2
& profiler
) :
774 m_Profiler(profiler
), m_ProfilerARB(NULL
), m_ProfilerEXT(NULL
), m_ProfilerINTEL(NULL
)
776 bool enabledARB
= false;
777 bool enabledEXT
= false;
778 bool enabledINTEL
= false;
779 CFG_GET_VAL("profiler2.gpu.arb.enable", enabledARB
);
780 CFG_GET_VAL("profiler2.gpu.ext.enable", enabledEXT
);
781 CFG_GET_VAL("profiler2.gpu.intel.enable", enabledINTEL
);
783 // Only enable either ARB or EXT, not both, because they are redundant
784 // (EXT is only needed for compatibility with older systems), and because
785 // using both triggers GL_INVALID_OPERATION on AMD drivers (see comment
786 // in CProfiler2GPU_EXT_timer_query::RecordRegion)
787 if (enabledARB
&& CProfiler2GPU_ARB_timer_query::IsSupported())
789 m_ProfilerARB
= new CProfiler2GPU_ARB_timer_query(m_Profiler
);
791 else if (enabledEXT
&& CProfiler2GPU_EXT_timer_query::IsSupported())
793 m_ProfilerEXT
= new CProfiler2GPU_EXT_timer_query(m_Profiler
);
796 // The INTEL mode should be compatible with ARB/EXT (though no current
797 // drivers support both), and provides complementary data, so enable it
799 if (enabledINTEL
&& CProfiler2GPU_INTEL_performance_queries::IsSupported())
801 m_ProfilerINTEL
= new CProfiler2GPU_INTEL_performance_queries(m_Profiler
);
805 CProfiler2GPU::~CProfiler2GPU()
807 SAFE_DELETE(m_ProfilerARB
);
808 SAFE_DELETE(m_ProfilerEXT
);
809 SAFE_DELETE(m_ProfilerINTEL
);
812 void CProfiler2GPU::FrameStart()
815 m_ProfilerARB
->FrameStart();
818 m_ProfilerEXT
->FrameStart();
821 m_ProfilerINTEL
->FrameStart();
824 void CProfiler2GPU::FrameEnd()
827 m_ProfilerARB
->FrameEnd();
830 m_ProfilerEXT
->FrameEnd();
833 m_ProfilerINTEL
->FrameEnd();
836 void CProfiler2GPU::RegionEnter(const char* id
)
839 m_ProfilerARB
->RegionEnter(id
);
842 m_ProfilerEXT
->RegionEnter(id
);
845 m_ProfilerINTEL
->RegionEnter(id
);
848 void CProfiler2GPU::RegionLeave(const char* id
)
851 m_ProfilerARB
->RegionLeave(id
);
854 m_ProfilerEXT
->RegionLeave(id
);
857 m_ProfilerINTEL
->RegionLeave(id
);
860 #else // CONFIG2_GLES
862 CProfiler2GPU::CProfiler2GPU(CProfiler2
& profiler
) :
863 m_Profiler(profiler
), m_ProfilerARB(NULL
), m_ProfilerEXT(NULL
), m_ProfilerINTEL(NULL
)
867 CProfiler2GPU::~CProfiler2GPU() { }
869 void CProfiler2GPU::FrameStart() { }
870 void CProfiler2GPU::FrameEnd() { }
871 void CProfiler2GPU::RegionEnter(const char* UNUSED(id
)) { }
872 void CProfiler2GPU::RegionLeave(const char* UNUSED(id
)) { }