]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Create glretrace specific context.
[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
58
59 void
60 checkGlError(trace::Call &call) {
61     GLenum error = glGetError();
62     if (error == GL_NO_ERROR) {
63         return;
64     }
65
66     std::ostream & os = retrace::warning(call);
67
68     os << "glGetError(";
69     os << call.name();
70     os << ") = ";
71
72     switch (error) {
73     case GL_INVALID_ENUM:
74         os << "GL_INVALID_ENUM";
75         break;
76     case GL_INVALID_VALUE:
77         os << "GL_INVALID_VALUE";
78         break;
79     case GL_INVALID_OPERATION:
80         os << "GL_INVALID_OPERATION";
81         break;
82     case GL_STACK_OVERFLOW:
83         os << "GL_STACK_OVERFLOW";
84         break;
85     case GL_STACK_UNDERFLOW:
86         os << "GL_STACK_UNDERFLOW";
87         break;
88     case GL_OUT_OF_MEMORY:
89         os << "GL_OUT_OF_MEMORY";
90         break;
91     case GL_INVALID_FRAMEBUFFER_OPERATION:
92         os << "GL_INVALID_FRAMEBUFFER_OPERATION";
93         break;
94     case GL_TABLE_TOO_LARGE:
95         os << "GL_TABLE_TOO_LARGE";
96         break;
97     default:
98         os << error;
99         break;
100     }
101     os << "\n";
102 }
103
104 static GLuint64
105 getGpuTimestamp() {
106     GLuint query = 0;
107     GLuint64 timestamp = 0;
108
109     if (retrace::profilingGpuTimes && supportsTimestamp) {
110         glGenQueries(1, &query);
111         glQueryCounter(query, GL_TIMESTAMP);
112         glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
113         glDeleteQueries(1, &query);
114     }
115
116     return timestamp;
117 }
118
119 static GLuint64
120 getCpuTimestamp() {
121     if (retrace::profilingCpuTimes) {
122         return os::getTime() * (1.0E9 / os::timeFrequency);
123     } else {
124         return 0;
125     }
126 }
127
128 static void
129 completeCallQuery(CallQuery& query) {
130     /* Get call start and duration */
131     GLuint64 timestamp = 0, duration = 0, samples = 0;
132
133     if (retrace::profilingGpuTimes) {
134         if (supportsTimestamp) {
135             glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
136         }
137
138         if (supportsElapsed) {
139             glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
140         }
141     }
142
143     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
144         glGetQueryObjectui64vEXT(query.ids[2], GL_QUERY_RESULT, &samples);
145     }
146
147     glDeleteQueries(3, query.ids);
148
149     /* Add call to profile */
150     retrace::profiler.addCall(query.call, query.sig->name, query.program, samples, timestamp, duration, query.start, query.duration);
151 }
152
153 void
154 flushQueries() {
155     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
156         completeCallQuery(*itr);
157     }
158
159     callQueries.clear();
160 }
161
162 void
163 beginProfile(trace::Call &call) {
164     if (firstFrame) {
165         /* Check for extension support */
166         const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
167         GLint bits;
168
169         supportsTimestamp = glws::checkExtension("GL_ARB_timer_query", extensions);
170         supportsElapsed   = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp;
171         supportsOcclusion = glws::checkExtension("GL_ARB_occlusion_query", extensions);
172
173         if (retrace::profilingGpuTimes) {
174             if (!supportsTimestamp && !supportsElapsed) {
175                 std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
176                 exit(-1);
177             }
178
179             glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
180
181             if (!bits) {
182                 std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
183                 exit(-1);
184             }
185         }
186
187         if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
188             std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
189             exit(-1);
190         }
191
192         frame_start();
193     }
194
195     /* Create call query */
196     CallQuery query;
197     query.call = call.no;
198     query.sig = call.sig;
199     query.program = glretrace::currentContext ? glretrace::currentContext->activeProgram : 0;
200
201     glGenQueries(3, query.ids);
202
203     if (retrace::profilingGpuTimes) {
204         if (supportsTimestamp) {
205             glQueryCounter(query.ids[0], GL_TIMESTAMP);
206         }
207
208         if (supportsElapsed) {
209             glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
210         }
211     }
212
213     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
214         glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
215     }
216
217     if (retrace::profilingCpuTimes) {
218         query.start = os::getTime();
219     }
220
221     callQueries.push_back(query);
222 }
223
224 void
225 endProfile(trace::Call &call) {
226     if (retrace::profilingCpuTimes) {
227         CallQuery& query = callQueries.back();
228         query.duration = (os::getTime() - query.start) * (1.0E9 / os::timeFrequency);
229     }
230
231     if (retrace::profilingGpuTimes && supportsElapsed) {
232         glEndQuery(GL_TIME_ELAPSED);
233     }
234
235     if (retrace::profilingPixelsDrawn && supportsOcclusion) {
236         glEndQuery(GL_SAMPLES_PASSED);
237     }
238 }
239
240 void
241 frame_start() {
242     firstFrame = false;
243
244     if (retrace::profiling) {
245         retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp());
246     }
247 }
248
249 void
250 frame_complete(trace::Call &call) {
251     if (retrace::profiling) {
252         /* Complete any remaining queries */
253         flushQueries();
254
255         /* Indicate end of current frame */
256         retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp());
257     }
258
259     retrace::frameComplete(call);
260
261     /* Indicate start of next frame */
262     frame_start();
263
264     if (!currentDrawable) {
265         return;
266     }
267
268     if (retrace::debug && !currentDrawable->visible) {
269         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
270     }
271 }
272
273 } /* namespace glretrace */
274
275
276 void
277 retrace::setUp(void) {
278     glws::init();
279 }
280
281
282 void
283 retrace::addCallbacks(retrace::Retracer &retracer)
284 {
285     retracer.addCallbacks(glretrace::gl_callbacks);
286     retracer.addCallbacks(glretrace::glx_callbacks);
287     retracer.addCallbacks(glretrace::wgl_callbacks);
288     retracer.addCallbacks(glretrace::cgl_callbacks);
289     retracer.addCallbacks(glretrace::egl_callbacks);
290 }
291
292
293 image::Image *
294 retrace::getSnapshot(void) {
295     if (!glretrace::currentDrawable) {
296         return NULL;
297     }
298
299     return glstate::getDrawBufferImage();
300 }
301
302
303 bool
304 retrace::dumpState(std::ostream &os)
305 {
306     if (glretrace::insideGlBeginEnd ||
307         !glretrace::currentDrawable ||
308         !glretrace::currentContext) {
309         return false;
310     }
311
312     glstate::dumpCurrentContext(os);
313
314     return true;
315 }
316
317 void
318 retrace::flushRendering(void) {
319     glretrace::flushQueries();
320     glFlush();
321 }
322
323 void
324 retrace::waitForInput(void) {
325     while (glws::processEvents()) {
326     }
327 }
328
329 void
330 retrace::cleanUp(void) {
331     glws::cleanup();
332 }