]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Merge remote-tracking branch 'jbenton/master'
[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         GLint bits;
185
186         supportsTimestamp = glws::checkExtension("GL_ARB_timer_query", extensions);
187         supportsElapsed   = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp;
188         supportsOcclusion = glws::checkExtension("GL_ARB_occlusion_query", extensions);
189
190         if (retrace::profilingGpuTimes) {
191             if (!supportsTimestamp && !supportsElapsed) {
192                 std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
193                 exit(-1);
194             }
195
196             glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
197
198             if (!bits) {
199                 std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
200                 exit(-1);
201             }
202         }
203
204         if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
205             std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
206             exit(-1);
207         }
208
209         frame_start();
210     }
211
212     /* Create call query */
213     CallQuery query;
214     query.call = call.no;
215     query.sig = call.sig;
216     query.program = getActiveProgram();
217
218     glGenQueries(3, query.ids);
219
220     if (retrace::profilingGpuTimes) {
221         if (supportsTimestamp) {
222             glQueryCounter(query.ids[0], GL_TIMESTAMP);
223         }
224
225         if (supportsElapsed) {
226             glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
227         }
228     }
229
230     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
231         glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
232     }
233
234     if (retrace::profilingCpuTimes) {
235         query.start = os::getTime();
236     }
237
238     callQueries.push_back(query);
239 }
240
241 void
242 endProfile(trace::Call &call) {
243     if (retrace::profilingCpuTimes) {
244         CallQuery& query = callQueries.back();
245         query.duration = (os::getTime() - query.start) * (1.0E9 / os::timeFrequency);
246     }
247
248     if (retrace::profilingGpuTimes && supportsElapsed) {
249         glEndQuery(GL_TIME_ELAPSED);
250     }
251
252     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
253         glEndQuery(GL_SAMPLES_PASSED);
254     }
255 }
256
257 void
258 frame_start() {
259     firstFrame = false;
260
261     if (retrace::profiling) {
262         retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp());
263     }
264 }
265
266 void
267 frame_complete(trace::Call &call) {
268     if (retrace::profiling) {
269         /* Complete any remaining queries */
270         flushQueries();
271
272         /* Indicate end of current frame */
273         retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp());
274     }
275
276     retrace::frameComplete(call);
277
278     /* Indicate start of next frame */
279     frame_start();
280
281     if (!currentDrawable) {
282         return;
283     }
284
285     if (retrace::debug && !currentDrawable->visible) {
286         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
287     }
288 }
289
290 } /* namespace glretrace */
291
292
293 void
294 retrace::setUp(void) {
295     glws::init();
296 }
297
298
299 void
300 retrace::addCallbacks(retrace::Retracer &retracer)
301 {
302     retracer.addCallbacks(glretrace::gl_callbacks);
303     retracer.addCallbacks(glretrace::glx_callbacks);
304     retracer.addCallbacks(glretrace::wgl_callbacks);
305     retracer.addCallbacks(glretrace::cgl_callbacks);
306     retracer.addCallbacks(glretrace::egl_callbacks);
307 }
308
309
310 image::Image *
311 retrace::getSnapshot(void) {
312     if (!glretrace::currentDrawable) {
313         return NULL;
314     }
315
316     return glstate::getDrawBufferImage();
317 }
318
319
320 bool
321 retrace::dumpState(std::ostream &os)
322 {
323     if (glretrace::insideGlBeginEnd ||
324         !glretrace::currentDrawable ||
325         !glretrace::currentContext) {
326         return false;
327     }
328
329     glstate::dumpCurrentContext(os);
330
331     return true;
332 }
333
334 void
335 retrace::flushRendering(void) {
336     glretrace::flushQueries();
337     glFlush();
338 }
339
340 void
341 retrace::waitForInput(void) {
342     while (glws::processEvents()) {
343     }
344 }
345
346 void
347 retrace::cleanUp(void) {
348     glws::cleanup();
349 }