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