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