]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
ee19a69bb8ee3eeb6874492662a87182a62c4b5d
[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
34
35 namespace glretrace {
36
37 bool insideList = false;
38 bool insideGlBeginEnd = false;
39
40 struct CallQuery
41 {
42     GLuint ids[2];
43     unsigned call;
44     const trace::FunctionSig *sig;
45 };
46
47 static bool firstFrame = true;
48 static std::list<CallQuery> callQueries;
49 static const int maxActiveCallQueries = 100;
50
51
52 void
53 checkGlError(trace::Call &call) {
54     GLenum error = glGetError();
55     if (error == GL_NO_ERROR) {
56         return;
57     }
58
59     std::ostream & os = retrace::warning(call);
60
61     os << "glGetError(";
62     os << call.name();
63     os << ") = ";
64
65     switch (error) {
66     case GL_INVALID_ENUM:
67         os << "GL_INVALID_ENUM";
68         break;
69     case GL_INVALID_VALUE:
70         os << "GL_INVALID_VALUE";
71         break;
72     case GL_INVALID_OPERATION:
73         os << "GL_INVALID_OPERATION";
74         break;
75     case GL_STACK_OVERFLOW:
76         os << "GL_STACK_OVERFLOW";
77         break;
78     case GL_STACK_UNDERFLOW:
79         os << "GL_STACK_UNDERFLOW";
80         break;
81     case GL_OUT_OF_MEMORY:
82         os << "GL_OUT_OF_MEMORY";
83         break;
84     case GL_INVALID_FRAMEBUFFER_OPERATION:
85         os << "GL_INVALID_FRAMEBUFFER_OPERATION";
86         break;
87     case GL_TABLE_TOO_LARGE:
88         os << "GL_TABLE_TOO_LARGE";
89         break;
90     default:
91         os << error;
92         break;
93     }
94     os << "\n";
95 }
96
97 static GLuint64
98 getTimestamp() {
99     GLuint query = 0;
100     GLuint64 timestamp = 0;
101
102     glGenQueries(1, &query);
103     glQueryCounter(query, GL_TIMESTAMP);
104     glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
105     glDeleteQueries(1, &query);
106
107     return timestamp;
108 }
109
110 static void
111 completeCallQuery(CallQuery& query) {
112     /* Get call start and duration */
113     GLuint64 timestamp = 0, duration = 0;
114     glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &timestamp);
115     glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration);
116     glDeleteQueries(2, query.ids);
117
118     /* Add call to profile */
119     retrace::profiler.addCall(query.call, query.sig->name, timestamp, duration);
120 }
121
122 void
123 flushQueries() {
124     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
125         completeCallQuery(*itr);
126     }
127
128     callQueries.clear();
129 }
130
131 void
132 beginProfileGPU(trace::Call &call) {
133     if (firstFrame) {
134         frame_start();
135     }
136
137     /* Ensure we don't have TOO many queries waiting for results */
138     if (callQueries.size() >= maxActiveCallQueries) {
139         completeCallQuery(callQueries.front());
140         callQueries.pop_front();
141     }
142
143     /* Create call query */
144     CallQuery query;
145     query.call = call.no;
146     query.sig = call.sig;
147
148     glGenQueries(2, query.ids);
149     glQueryCounter(query.ids[0], GL_TIMESTAMP);
150     glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
151
152     callQueries.push_back(query);
153 }
154
155 void
156 endProfileGPU(trace::Call &call) {
157     glEndQuery(GL_TIME_ELAPSED);
158 }
159
160 void
161 frame_start() {
162     firstFrame = false;
163
164     if (retrace::profileGPU) {
165         retrace::profiler.addFrameStart(retrace::frameNo, getTimestamp());
166     }
167 }
168
169 void
170 frame_complete(trace::Call &call) {
171     if (retrace::profileGPU) {
172         /* Complete any remaining queries */
173         flushQueries();
174
175         /* Indicate end of current frame */
176         retrace::profiler.addFrameEnd(getTimestamp());
177     }
178
179     retrace::frameComplete(call);
180
181     /* Indicate start of next frame */
182     frame_start();
183
184     if (!currentDrawable) {
185         return;
186     }
187
188     if (retrace::debug && !currentDrawable->visible) {
189         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
190     }
191 }
192
193 } /* namespace glretrace */
194
195
196 void
197 retrace::setUp(void) {
198     glws::init();
199 }
200
201
202 void
203 retrace::addCallbacks(retrace::Retracer &retracer)
204 {
205     retracer.addCallbacks(glretrace::gl_callbacks);
206     retracer.addCallbacks(glretrace::glx_callbacks);
207     retracer.addCallbacks(glretrace::wgl_callbacks);
208     retracer.addCallbacks(glretrace::cgl_callbacks);
209     retracer.addCallbacks(glretrace::egl_callbacks);
210 }
211
212
213 image::Image *
214 retrace::getSnapshot(void) {
215     if (!glretrace::currentDrawable) {
216         return NULL;
217     }
218
219     return glstate::getDrawBufferImage();
220 }
221
222
223 bool
224 retrace::dumpState(std::ostream &os)
225 {
226     if (glretrace::insideGlBeginEnd ||
227         !glretrace::currentDrawable ||
228         !glretrace::currentContext) {
229         return false;
230     }
231
232     glstate::dumpCurrentContext(os);
233
234     return true;
235 }
236
237 void
238 retrace::flushRendering(void) {
239     glretrace::flushQueries();
240     glFlush();
241 }
242
243 void
244 retrace::waitForInput(void) {
245     while (glws::processEvents()) {
246     }
247 }
248
249 void
250 retrace::cleanUp(void) {
251     glws::cleanup();
252 }