]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Check for GL_EXT_timer_query and GL_ARB_occlusion_query.
[apitrace] / retrace / glretrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28
29 #include "retrace.hpp"
30 #include "glproc.hpp"
31 #include "glstate.hpp"
32 #include "glretrace.hpp"
33 #include "os_time.hpp"
34
35
36 namespace glretrace {
37
38 bool insideList = false;
39 bool insideGlBeginEnd = false;
40
41 struct CallQuery
42 {
43     GLuint ids[3];
44     unsigned call;
45     GLuint program;
46     const trace::FunctionSig *sig;
47     uint64_t start;
48     uint64_t duration;
49 };
50
51 static bool supportsElapsed = true;
52 static bool supportsTimestamp = true;
53 static bool supportsOcclusion = true;
54
55 static bool firstFrame = true;
56 static std::list<CallQuery> callQueries;
57 static std::map<glws::Context*, GLuint> activePrograms;
58
59
60 void
61 checkGlError(trace::Call &call) {
62     GLenum error = glGetError();
63     if (error == GL_NO_ERROR) {
64         return;
65     }
66
67     std::ostream & os = retrace::warning(call);
68
69     os << "glGetError(";
70     os << call.name();
71     os << ") = ";
72
73     switch (error) {
74     case GL_INVALID_ENUM:
75         os << "GL_INVALID_ENUM";
76         break;
77     case GL_INVALID_VALUE:
78         os << "GL_INVALID_VALUE";
79         break;
80     case GL_INVALID_OPERATION:
81         os << "GL_INVALID_OPERATION";
82         break;
83     case GL_STACK_OVERFLOW:
84         os << "GL_STACK_OVERFLOW";
85         break;
86     case GL_STACK_UNDERFLOW:
87         os << "GL_STACK_UNDERFLOW";
88         break;
89     case GL_OUT_OF_MEMORY:
90         os << "GL_OUT_OF_MEMORY";
91         break;
92     case GL_INVALID_FRAMEBUFFER_OPERATION:
93         os << "GL_INVALID_FRAMEBUFFER_OPERATION";
94         break;
95     case GL_TABLE_TOO_LARGE:
96         os << "GL_TABLE_TOO_LARGE";
97         break;
98     default:
99         os << error;
100         break;
101     }
102     os << "\n";
103 }
104
105 static GLuint64
106 getGpuTimestamp() {
107     GLuint query = 0;
108     GLuint64 timestamp = 0;
109
110     if (retrace::profilingGpuTimes && supportsTimestamp) {
111         glGenQueries(1, &query);
112         glQueryCounter(query, GL_TIMESTAMP);
113         glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
114         glDeleteQueries(1, &query);
115     }
116
117     return timestamp;
118 }
119
120 static GLuint64
121 getCpuTimestamp() {
122     if (retrace::profilingCpuTimes) {
123         return os::getTime() * (1.0E9 / os::timeFrequency);
124     } else {
125         return 0;
126     }
127 }
128
129 static void
130 completeCallQuery(CallQuery& query) {
131     /* Get call start and duration */
132     GLuint64 timestamp = 0, duration = 0, samples = 0;
133
134     if (retrace::profilingGpuTimes) {
135         if (supportsTimestamp) {
136             glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
137         }
138
139         if (supportsElapsed) {
140             glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
141         }
142     }
143
144     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
145         glGetQueryObjectui64vEXT(query.ids[2], GL_QUERY_RESULT, &samples);
146     }
147
148     glDeleteQueries(3, query.ids);
149
150     /* Add call to profile */
151     retrace::profiler.addCall(query.call, query.sig->name, query.program, samples, timestamp, duration, query.start, query.duration);
152 }
153
154 void
155 flushQueries() {
156     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
157         completeCallQuery(*itr);
158     }
159
160     callQueries.clear();
161 }
162
163 void setActiveProgram(GLuint program)
164 {
165     activePrograms[glretrace::currentContext] = program;
166 }
167
168 static GLuint
169 getActiveProgram()
170 {
171     std::map<glws::Context*, GLuint>::iterator it;
172     it = activePrograms.find(glretrace::currentContext);
173     if (it == activePrograms.end())
174         return 0;
175
176     return it->second;
177 }
178
179 void
180 beginProfile(trace::Call &call) {
181     if (firstFrame) {
182         /* Check for extension support */
183         const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
184
185         supportsTimestamp = glws::checkExtension("GL_ARB_timer_query", extensions);
186         supportsElapsed   = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp;
187         supportsOcclusion = glws::checkExtension("GL_ARB_occlusion_query", extensions);
188
189         if (retrace::profilingGpuTimes && !supportsTimestamp && !supportsElapsed) {
190             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
191             exit(-1);
192         }
193
194         if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
195             std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
196             exit(-1);
197         }
198
199         frame_start();
200     }
201
202     /* Create call query */
203     CallQuery query;
204     query.call = call.no;
205     query.sig = call.sig;
206     query.program = getActiveProgram();
207
208     glGenQueries(3, query.ids);
209
210     if (retrace::profilingGpuTimes) {
211         if (supportsTimestamp) {
212             glQueryCounter(query.ids[0], GL_TIMESTAMP);
213         }
214
215         if (supportsElapsed) {
216             glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
217         }
218     }
219
220     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
221         glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
222     }
223
224     if (retrace::profilingCpuTimes) {
225         query.start = os::getTime();
226     }
227
228     callQueries.push_back(query);
229 }
230
231 void
232 endProfile(trace::Call &call) {
233     if (retrace::profilingCpuTimes) {
234         CallQuery& query = callQueries.back();
235         query.duration = (os::getTime() - query.start) * (1.0E9 / os::timeFrequency);
236     }
237
238     if (retrace::profilingGpuTimes && supportsElapsed) {
239         glEndQuery(GL_TIME_ELAPSED);
240     }
241
242     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
243         glEndQuery(GL_SAMPLES_PASSED);
244     }
245 }
246
247 void
248 frame_start() {
249     firstFrame = false;
250
251     if (retrace::profiling) {
252         retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp());
253     }
254 }
255
256 void
257 frame_complete(trace::Call &call) {
258     if (retrace::profiling) {
259         /* Complete any remaining queries */
260         flushQueries();
261
262         /* Indicate end of current frame */
263         retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp());
264     }
265
266     retrace::frameComplete(call);
267
268     /* Indicate start of next frame */
269     frame_start();
270
271     if (!currentDrawable) {
272         return;
273     }
274
275     if (retrace::debug && !currentDrawable->visible) {
276         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
277     }
278 }
279
280 } /* namespace glretrace */
281
282
283 void
284 retrace::setUp(void) {
285     glws::init();
286 }
287
288
289 void
290 retrace::addCallbacks(retrace::Retracer &retracer)
291 {
292     retracer.addCallbacks(glretrace::gl_callbacks);
293     retracer.addCallbacks(glretrace::glx_callbacks);
294     retracer.addCallbacks(glretrace::wgl_callbacks);
295     retracer.addCallbacks(glretrace::cgl_callbacks);
296     retracer.addCallbacks(glretrace::egl_callbacks);
297 }
298
299
300 image::Image *
301 retrace::getSnapshot(void) {
302     if (!glretrace::currentDrawable) {
303         return NULL;
304     }
305
306     return glstate::getDrawBufferImage();
307 }
308
309
310 bool
311 retrace::dumpState(std::ostream &os)
312 {
313     if (glretrace::insideGlBeginEnd ||
314         !glretrace::currentDrawable ||
315         !glretrace::currentContext) {
316         return false;
317     }
318
319     glstate::dumpCurrentContext(os);
320
321     return true;
322 }
323
324 void
325 retrace::flushRendering(void) {
326     glretrace::flushQueries();
327     glFlush();
328 }
329
330 void
331 retrace::waitForInput(void) {
332     while (glws::processEvents()) {
333     }
334 }
335
336 void
337 retrace::cleanUp(void) {
338     glws::cleanup();
339 }