Merge lp:~glmark2-dev/glmark2/canvas-drm-rebranch into lp:glmark2/2011.11
- canvas-drm-rebranch
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 257 |
Proposed branch: | lp:~glmark2-dev/glmark2/canvas-drm-rebranch |
Merge into: | lp:glmark2/2011.11 |
Diff against target: |
907 lines (+798/-12) 6 files modified
INSTALL (+1/-1) src/canvas-drm.cpp (+587/-0) src/canvas-drm.h (+132/-0) src/main.cpp (+6/-2) src/wscript_build (+43/-0) wscript (+29/-9) |
To merge this branch: | bzr merge lp:~glmark2-dev/glmark2/canvas-drm-rebranch |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandros Frantzis | Approve | ||
Jesse Barker | Needs Resubmitting | ||
Review via email: mp+138605@code.launchpad.net |
Commit message
Description of the change
CanvasDRM: Add a new canvas type that renders directly to DRM-managed buffers rather than going through X11. The canvas uses GBM for surface management, DRM (KMS) for mode control and page flip, and EGL for rendering context and config management. Client API support is predicated upon the EGL, but both OpenGL and OpenGL ES 2.0 are supported.
This work was originally done on lp:~glmark2-dev/glmark2/canvas-drm, however, based upon review comments there, it was decided it would be cleaner (with respect to other development branches that have been merged in the interim) to rebranch and reintegrate the work. So, I rebranched from current trunk and then did a 'bzr merge --interactive' from canvas-drm into this branch and de-selected the changes relating to egl-state.{h,cpp}. I think we lose a bit of the revision history, but actually gain a bit of clarity.
Jesse Barker (jesse-barker) wrote : | # |
> Rendering works fine on the platforms I have tested. The only problems I see
> are during shutdown:
>
> 1. Pressing Ctrl+C occasionally doesn't seem to reset the crtc correctly, so I
> end up with a blank screen. It also always causes an error message to be
> displayed (Error in select: -1).
This obviously works better for you than it does for me (possibly a driver-specific thing?).
>
> 2. Pressing Enter (and therefore flushing stdin), causes a segfault. This
> happens because we are leaving DRMState::do_flip() with both surface buffers
> locked. Next time we call gbm_surface_
> a NULL value which we currently don't handle properly (i.e. we just use it).
> We get into the same situation if select fails etc
Are you saying that there's a segfault when you press <enter> after glmark2 exits normally? I'm not clear I've ever see this, but I think I'm misunderstanding the scenario.
>
> (1) is not critical, so I am ok with shipping this as part of an experimental
> drm backend release (perhaps we could add a warning about the experimental
> status when startinp up?).
Probably should declare this a bit experimental in any case.
>
> On the other hand, I think (2) should be fixed before this release, if
> possible, as it's relatively easy for a user to cause a segfault. For example,
> a user that wants to quit will probably try pressing ESC (which won't work),
> then some keys then Enter.
Agreed. I'll try to reproduce. If you can provide more info on how to reproduce, that would be very useful.
cheers,
Jesse
- 256. By Jesse Barker
-
CanvasDRM: Fix handling of Ctrl-C. Because the Ctrl-C was being handled by
CanvasDRM directly, DRMState had no knowledge of the event, and wasn't able
to detect it properly when select() came back with an error. Move the
quit handler into DRMState, and give it a public should_quit() method that
CanvasDRM can call. This is better encapsulation, and allows DRMState to
properly detect the user interrupt and release the GBM resources so the
console isn't hung when the application exits.
Jesse Barker (jesse-barker) wrote : | # |
OK, got it. I reproduced the behavior, and have pushed a fix at revision 256. This works fine for me from both a serial console, as well as the "main" console (Ctrl-Alt-F1). The signal handler is now better encapsulated within DRMState, and allows the resources to be released properly. I've squelched the "error in select" message by making it debug only, as the vast majority of the time we would see that is precisely the case where a user interrupt has broken us out. I also added a notice to CanvasDRM::init() to indicate that the DRM canvas is experimental and is subject to change with the evolution of buffer management and modesetting APIs.
Alexandros Frantzis (afrantzis) wrote : | # |
Even with the latest changes, I still get stuck with the glmark2 buffer on screen occasionally (~1 out of 5 times) after exiting with Ctrl-C. There are no errors or hangs, and if I switch to the X11 VT and back again, everything is displayed correctly.
> Are you saying that there's a segfault when you press <enter> after glmark2 exits normally?
> I'm not clear I've ever see this, but I think I'm misunderstanding the scenario.
To reproduce, start glmark2*-drm and press enter while a benchmark is running.
Alexandros Frantzis (afrantzis) wrote : | # |
> Even with the latest changes, I still get stuck with the glmark2 buffer on
> screen occasionally (~1 out of 5 times) after exiting with Ctrl-C. There are
> no errors or hangs, and if I switch to the X11 VT and back again, everything
> is displayed correctly.
Note that this happens for me only with radeon; I can't reproduce it at all with i915 (tried until my fingers got tired of pressing Ctrl-C ;)). This could well by some kind of driver issue, so I don't think it's worth pursuing further for now.
> > Are you saying that there's a segfault when you press <enter> after glmark2
> exits normally?
> > I'm not clear I've ever see this, but I think I'm misunderstanding the
> scenario.
>
> To reproduce, start glmark2*-drm and press enter while a benchmark is running.
This issue, however, is 100% reproducible on both platforms.
- 257. By Jesse Barker
-
CanvasDRM: Further simplify the select() logic. Firstly, as we have a NULL
pointer for the timeout parameter, select() won't time out and we don't need to
handle that return value. Secondly, unless we have a really great idea for
handling user input (apart from Ctrl-C), we don't need to listen on stdin.
Jesse Barker (jesse-barker) wrote : | # |
Got rid of the need (well, the need already wasn't there ;-) for listening on stdin for arbitrary user input. We also didn't need to watch for a time out. Things work fine for me, even with interspersed <Enter> and <Ctrl-C> presses.
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
Preview Diff
1 | === modified file 'INSTALL' |
2 | --- INSTALL 2011-01-25 15:07:17 +0000 |
3 | +++ INSTALL 2012-12-17 22:54:19 +0000 |
4 | @@ -2,7 +2,7 @@ |
5 | |
6 | To configure glmark2 use: |
7 | |
8 | -$ ./waf configure [--enable-gl --enable-glesv2 --data-path=DATA_PATH --prefix=PREFIX] |
9 | +$ ./waf configure [--enable-gl --enable-glesv2 --enable-gl-drm --enable-glesv2-drm --data-path=DATA_PATH --prefix=PREFIX] |
10 | |
11 | To build use: |
12 | |
13 | |
14 | === added file 'src/canvas-drm.cpp' |
15 | --- src/canvas-drm.cpp 1970-01-01 00:00:00 +0000 |
16 | +++ src/canvas-drm.cpp 2012-12-17 22:54:19 +0000 |
17 | @@ -0,0 +1,587 @@ |
18 | +// |
19 | +// Copyright © 2012 Linaro Limited |
20 | +// |
21 | +// This file is part of the glmark2 OpenGL (ES) 2.0 benchmark. |
22 | +// |
23 | +// glmark2 is free software: you can redistribute it and/or modify it under the |
24 | +// terms of the GNU General Public License as published by the Free Software |
25 | +// Foundation, either version 3 of the License, or (at your option) any later |
26 | +// version. |
27 | +// |
28 | +// glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY |
29 | +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
30 | +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
31 | +// details. |
32 | +// |
33 | +// You should have received a copy of the GNU General Public License along with |
34 | +// glmark2. If not, see <http://www.gnu.org/licenses/>. |
35 | +// |
36 | +// Authors: |
37 | +// Simon Que |
38 | +// Jesse Barker |
39 | +// |
40 | +#include "canvas-drm.h" |
41 | +#include "log.h" |
42 | +#include "options.h" |
43 | +#include "util.h" |
44 | + |
45 | +#include <fcntl.h> |
46 | +#include <poll.h> |
47 | +#include <signal.h> |
48 | +#include <string.h> |
49 | +#include <time.h> |
50 | + |
51 | +#include <fstream> |
52 | +#include <sstream> |
53 | +#include <list> |
54 | + |
55 | +/****************** |
56 | + * Public methods * |
57 | + ******************/ |
58 | + |
59 | +bool |
60 | +CanvasDRM::reset() |
61 | +{ |
62 | + if (!reset_context()) |
63 | + return false; |
64 | + |
65 | + if (!make_current()) |
66 | + return false; |
67 | + |
68 | + if (!supports_gl2()) { |
69 | + Log::error("Glmark2 needs OpenGL(ES) version >= 2.0 to run" |
70 | + " (but version string is: '%s')!\n", |
71 | + glGetString(GL_VERSION)); |
72 | + return false; |
73 | + } |
74 | + |
75 | + glViewport(0, 0, width_, height_); |
76 | + glEnable(GL_DEPTH_TEST); |
77 | + glDepthFunc(GL_LEQUAL); |
78 | + glEnable(GL_CULL_FACE); |
79 | + glCullFace(GL_BACK); |
80 | + |
81 | + clear(); |
82 | + egl_.swap(); |
83 | + |
84 | + if (!drm_.reset()) { |
85 | + return false; |
86 | + } |
87 | + |
88 | + return true; |
89 | +} |
90 | + |
91 | +void |
92 | +DRMState::fb_destroy_callback(gbm_bo* bo, void* data) |
93 | +{ |
94 | + DRMFBState* fb = reinterpret_cast<DRMFBState*>(data); |
95 | + if (fb && fb->fb_id) { |
96 | + drmModeRmFB(fb->fd, fb->fb_id); |
97 | + } |
98 | + delete fb; |
99 | + gbm_device* dev = gbm_bo_get_device(bo); |
100 | + Log::debug("Got GBM device handle %p from buffer object\n", dev); |
101 | +} |
102 | + |
103 | +DRMFBState* |
104 | +DRMState::fb_get_from_bo(gbm_bo* bo) |
105 | +{ |
106 | + DRMFBState* fb = reinterpret_cast<DRMFBState*>(gbm_bo_get_user_data(bo)); |
107 | + if (fb) { |
108 | + return fb; |
109 | + } |
110 | + |
111 | + unsigned int width = gbm_bo_get_width(bo); |
112 | + unsigned int height = gbm_bo_get_height(bo); |
113 | + unsigned int stride = gbm_bo_get_stride(bo); |
114 | + unsigned int handle = gbm_bo_get_handle(bo).u32; |
115 | + unsigned int fb_id(0); |
116 | + int status = drmModeAddFB(fd_, width, height, 24, 32, stride, handle, &fb_id); |
117 | + if (status < 0) { |
118 | + Log::error("Failed to create FB: %d\n", status); |
119 | + return 0; |
120 | + } |
121 | + |
122 | + fb = new DRMFBState(); |
123 | + fb->fd = fd_; |
124 | + fb->bo = bo; |
125 | + fb->fb_id = fb_id; |
126 | + |
127 | + gbm_bo_set_user_data(bo, fb, fb_destroy_callback); |
128 | + return fb; |
129 | +} |
130 | + |
131 | +bool |
132 | +DRMState::init_gbm() |
133 | +{ |
134 | + dev_ = gbm_create_device(fd_); |
135 | + if (!dev_) { |
136 | + Log::error("Failed to create GBM device\n"); |
137 | + return false; |
138 | + } |
139 | + |
140 | + surface_ = gbm_surface_create(dev_, mode_->hdisplay, mode_->vdisplay, |
141 | + GBM_FORMAT_XRGB8888, |
142 | + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); |
143 | + if (!surface_) { |
144 | + Log::error("Failed to create GBM surface\n"); |
145 | + return false; |
146 | + } |
147 | + |
148 | + return true; |
149 | +} |
150 | + |
151 | +bool |
152 | +DRMState::init() |
153 | +{ |
154 | + // TODO: Replace this with something that explicitly probes for the loaded |
155 | + // driver (udev?). |
156 | + static const char* drm_modules[] = { |
157 | + "i915", |
158 | + "nouveau", |
159 | + "radeon", |
160 | + "vmgfx", |
161 | + "omapdrm", |
162 | + "exynos" |
163 | + }; |
164 | + |
165 | + unsigned int num_modules(sizeof(drm_modules)/sizeof(drm_modules[0])); |
166 | + for (unsigned int m = 0; m < num_modules; m++) { |
167 | + fd_ = drmOpen(drm_modules[m], 0); |
168 | + if (fd_ < 0) { |
169 | + Log::debug("Failed to open DRM module '%s'\n", drm_modules[m]); |
170 | + continue; |
171 | + } |
172 | + Log::debug("Opened DRM module '%s'\n", drm_modules[m]); |
173 | + break; |
174 | + } |
175 | + |
176 | + if (fd_ < 0) { |
177 | + Log::error("Failed to find a suitable DRM device\n"); |
178 | + return false; |
179 | + } |
180 | + |
181 | + resources_ = drmModeGetResources(fd_); |
182 | + if (!resources_) { |
183 | + Log::error("drmModeGetResources failed\n"); |
184 | + return false; |
185 | + } |
186 | + |
187 | + // Find a connected connector |
188 | + for (int c = 0; c < resources_->count_connectors; c++) { |
189 | + connector_ = drmModeGetConnector(fd_, resources_->connectors[c]); |
190 | + if (DRM_MODE_CONNECTED == connector_->connection) { |
191 | + break; |
192 | + } |
193 | + drmModeFreeConnector(connector_); |
194 | + connector_ = 0; |
195 | + } |
196 | + |
197 | + if (!connector_) { |
198 | + Log::error("Failed to find a suitable connector\n"); |
199 | + return false; |
200 | + } |
201 | + |
202 | + // Find the best resolution (we will always operate full-screen). |
203 | + unsigned int bestArea(0); |
204 | + for (int m = 0; m < connector_->count_modes; m++) { |
205 | + drmModeModeInfo* curMode = &connector_->modes[m]; |
206 | + unsigned int curArea = curMode->hdisplay * curMode->vdisplay; |
207 | + if (curArea > bestArea) { |
208 | + mode_ = curMode; |
209 | + bestArea = curArea; |
210 | + } |
211 | + } |
212 | + |
213 | + if (!mode_) { |
214 | + Log::error("Failed to find a suitable mode\n"); |
215 | + return false; |
216 | + } |
217 | + |
218 | + // Find a suitable encoder |
219 | + for (int e = 0; e < resources_->count_encoders; e++) { |
220 | + encoder_ = drmModeGetEncoder(fd_, resources_->encoders[e]); |
221 | + if (encoder_ && encoder_->encoder_id == connector_->encoder_id) { |
222 | + break; |
223 | + } |
224 | + drmModeFreeEncoder(encoder_); |
225 | + encoder_ = 0; |
226 | + } |
227 | + |
228 | + if (!encoder_) { |
229 | + Log::error("Failed to find a suitable encoder\n"); |
230 | + return false; |
231 | + } |
232 | + |
233 | + if (!init_gbm()) { |
234 | + return false; |
235 | + } |
236 | + |
237 | + crtc_ = drmModeGetCrtc(fd_, encoder_->crtc_id); |
238 | + if (!crtc_) { |
239 | + Log::error("Failed to get current CRTC\n"); |
240 | + return false; |
241 | + } |
242 | + |
243 | + signal(SIGINT, &DRMState::quit_handler); |
244 | + |
245 | + return true; |
246 | +} |
247 | + |
248 | +bool |
249 | +DRMState::reset() |
250 | +{ |
251 | + if (bo_) { |
252 | + gbm_surface_release_buffer(surface_, bo_); |
253 | + } |
254 | + |
255 | + bo_ = gbm_surface_lock_front_buffer(surface_); |
256 | + fb_ = fb_get_from_bo(bo_); |
257 | + |
258 | + int status = drmModeSetCrtc(fd_, encoder_->crtc_id, fb_->fb_id, 0, 0, |
259 | + &connector_->connector_id, 1, mode_); |
260 | + if (status < 0) { |
261 | + Log::error("Failed to set CRTC: %d\n", status); |
262 | + return false; |
263 | + } |
264 | + |
265 | + return true; |
266 | +} |
267 | + |
268 | + |
269 | +bool DRMState::should_quit_ = false; |
270 | + |
271 | +void |
272 | +DRMState::quit_handler(int signo) |
273 | +{ |
274 | + Log::debug("Got SIGINT (%d).\n", signo); |
275 | + should_quit_ = true; |
276 | +} |
277 | +void |
278 | +DRMState::page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) |
279 | +{ |
280 | + unsigned int* waiting = reinterpret_cast<unsigned int*>(data); |
281 | + *waiting = 0; |
282 | + // Deal with unused parameters |
283 | + static_cast<void>(fd); |
284 | + static_cast<void>(frame); |
285 | + static_cast<void>(sec); |
286 | + static_cast<void>(usec); |
287 | +} |
288 | + |
289 | +void |
290 | +DRMState::do_flip() |
291 | +{ |
292 | + gbm_bo* next = gbm_surface_lock_front_buffer(surface_); |
293 | + fb_ = fb_get_from_bo(next); |
294 | + unsigned int waiting(1); |
295 | + int status = drmModePageFlip(fd_, encoder_->crtc_id, fb_->fb_id, |
296 | + DRM_MODE_PAGE_FLIP_EVENT, &waiting); |
297 | + if (status < 0) { |
298 | + Log::error("Failed to enqueue page flip: %d\n", status); |
299 | + return; |
300 | + } |
301 | + |
302 | + fd_set fds; |
303 | + FD_ZERO(&fds); |
304 | + FD_SET(fd_, &fds); |
305 | + drmEventContext evCtx; |
306 | + evCtx.version = DRM_EVENT_CONTEXT_VERSION; |
307 | + evCtx.page_flip_handler = page_flip_handler; |
308 | + |
309 | + while (waiting) { |
310 | + status = select(fd_ + 1, &fds, 0, 0, 0); |
311 | + if (status < 0) { |
312 | + // Most of the time, select() will return an error because the |
313 | + // user pressed Ctrl-C. So, only print out a message in debug |
314 | + // mode, and just check for the likely condition and release |
315 | + // the current buffer object before getting out. |
316 | + Log::debug("Error in select\n"); |
317 | + if (should_quit()) { |
318 | + gbm_surface_release_buffer(surface_, bo_); |
319 | + bo_ = next; |
320 | + } |
321 | + return; |
322 | + } |
323 | + drmHandleEvent(fd_, &evCtx); |
324 | + } |
325 | + |
326 | + gbm_surface_release_buffer(surface_, bo_); |
327 | + bo_ = next; |
328 | +} |
329 | + |
330 | +void |
331 | +DRMState::cleanup() |
332 | +{ |
333 | + // Restore CRTC state if necessary |
334 | + if (crtc_) { |
335 | + int status = drmModeSetCrtc(fd_, crtc_->crtc_id, crtc_->buffer_id, |
336 | + crtc_->x, crtc_->y, &connector_->connector_id, |
337 | + 1, &crtc_->mode); |
338 | + if (status < 0) { |
339 | + Log::error("Failed to restore original CRTC: %d\n", status); |
340 | + } |
341 | + drmModeFreeCrtc(crtc_); |
342 | + crtc_ = 0; |
343 | + } |
344 | + if (surface_) { |
345 | + gbm_surface_destroy(surface_); |
346 | + surface_ = 0; |
347 | + } |
348 | + if (dev_) { |
349 | + gbm_device_destroy(dev_); |
350 | + dev_ = 0; |
351 | + } |
352 | + if (connector_) { |
353 | + drmModeFreeConnector(connector_); |
354 | + connector_ = 0; |
355 | + } |
356 | + if (encoder_) { |
357 | + drmModeFreeEncoder(encoder_); |
358 | + encoder_ = 0; |
359 | + } |
360 | + if (resources_) { |
361 | + drmModeFreeResources(resources_); |
362 | + resources_ = 0; |
363 | + } |
364 | + if (fd_ > 0) { |
365 | + drmClose(fd_); |
366 | + } |
367 | + fd_ = 0; |
368 | + mode_ = 0; |
369 | +} |
370 | + |
371 | +bool |
372 | +CanvasDRM::init() |
373 | +{ |
374 | + Log::info("NOTE: The DRM canvas is still experimental and its behavior\n"); |
375 | + Log::info(" is subject to change as GBM and modesetting behavior\n"); |
376 | + Log::info(" evolves\n"); |
377 | + |
378 | + if (!drm_.init()) { |
379 | + Log::error("Failed to initialize the DRM canvas\n"); |
380 | + drm_.cleanup(); |
381 | + return false; |
382 | + } |
383 | + |
384 | + width_ = drm_.mode_width(); |
385 | + height_ = drm_.mode_height(); |
386 | + resize_no_viewport(width_, height_); |
387 | + |
388 | + if (!egl_.init_display(drm_.device(), visual_config_)) { |
389 | + return false; |
390 | + } |
391 | + if (!egl_.init_surface(drm_.surface())) { |
392 | + return false; |
393 | + } |
394 | + if (!egl_.valid()) { |
395 | + return false; |
396 | + } |
397 | + |
398 | + return reset(); |
399 | +} |
400 | + |
401 | + |
402 | +CanvasDRM::~CanvasDRM() |
403 | +{ |
404 | + drm_.cleanup(); |
405 | +} |
406 | + |
407 | +void |
408 | +CanvasDRM::visible(bool /* visible */) |
409 | +{ |
410 | +} |
411 | + |
412 | +void |
413 | +CanvasDRM::clear() |
414 | +{ |
415 | + glClearColor(0.0f, 0.0f, 0.0f, 0.5f); |
416 | +#if USE_GL |
417 | + glClearDepth(1.0f); |
418 | +#elif USE_GLESv2 |
419 | + glClearDepthf(1.0f); |
420 | +#endif |
421 | + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
422 | +} |
423 | + |
424 | +void |
425 | +CanvasDRM::update() |
426 | +{ |
427 | + Options::FrameEnd m = Options::frame_end; |
428 | + |
429 | + if (m == Options::FrameEndDefault) { |
430 | + if (offscreen_) |
431 | + m = Options::FrameEndFinish; |
432 | + else |
433 | + m = Options::FrameEndSwap; |
434 | + } |
435 | + |
436 | + switch(m) { |
437 | + case Options::FrameEndSwap: |
438 | + swap_buffers(); |
439 | + break; |
440 | + case Options::FrameEndFinish: |
441 | + glFinish(); |
442 | + break; |
443 | + case Options::FrameEndReadPixels: |
444 | + read_pixel(width_ / 2, height_ / 2); |
445 | + break; |
446 | + case Options::FrameEndNone: |
447 | + default: |
448 | + break; |
449 | + } |
450 | +} |
451 | + |
452 | +void |
453 | +CanvasDRM::print_info() |
454 | +{ |
455 | + make_current(); |
456 | + |
457 | + std::stringstream ss; |
458 | + ss << " OpenGL Information" << std::endl; |
459 | + ss << " GL_VENDOR: " << glGetString(GL_VENDOR) << std::endl; |
460 | + ss << " GL_RENDERER: " << glGetString(GL_RENDERER) << std::endl; |
461 | + ss << " GL_VERSION: " << glGetString(GL_VERSION) << std::endl; |
462 | + Log::info("%s", ss.str().c_str()); |
463 | +} |
464 | + |
465 | +Canvas::Pixel |
466 | +CanvasDRM::read_pixel(int x, int y) |
467 | +{ |
468 | + uint8_t pixel[4]; |
469 | + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); |
470 | + return Canvas::Pixel(pixel[0], pixel[1], pixel[2], pixel[3]); |
471 | +} |
472 | + |
473 | +void |
474 | +CanvasDRM::write_to_file(std::string &filename) |
475 | +{ |
476 | + char *pixels = new char[width_ * height_ * 4]; |
477 | + |
478 | + for (int i = 0; i < height_; i++) { |
479 | + glReadPixels(0, i, width_, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
480 | + &pixels[(height_ - i - 1) * width_ * 4]); |
481 | + } |
482 | + |
483 | + std::ofstream output (filename.c_str(), std::ios::out | std::ios::binary); |
484 | + output.write(pixels, 4 * width_ * height_); |
485 | + |
486 | + delete [] pixels; |
487 | +} |
488 | + |
489 | +bool |
490 | +CanvasDRM::should_quit() |
491 | +{ |
492 | + return drm_.should_quit(); |
493 | +} |
494 | + |
495 | +void |
496 | +CanvasDRM::resize(int width, int height) |
497 | +{ |
498 | + resize_no_viewport(width, height); |
499 | + glViewport(0, 0, width_, height_); |
500 | +} |
501 | + |
502 | +/********************* |
503 | + * Protected methods * |
504 | + *********************/ |
505 | + |
506 | +bool |
507 | +CanvasDRM::supports_gl2() |
508 | +{ |
509 | + std::string gl_version_str( |
510 | + reinterpret_cast<const char*>(glGetString(GL_VERSION))); |
511 | + int gl_major = 0; |
512 | + |
513 | + size_t point_pos(gl_version_str.find('.')); |
514 | + |
515 | + if (point_pos != std::string::npos) { |
516 | + point_pos--; |
517 | + |
518 | + size_t start_pos(gl_version_str.rfind(' ', point_pos)); |
519 | + if (start_pos == std::string::npos) |
520 | + start_pos = 0; |
521 | + else |
522 | + start_pos++; |
523 | + |
524 | + gl_major = Util::fromString<int>( |
525 | + gl_version_str.substr(start_pos, point_pos - start_pos + 1) |
526 | + ); |
527 | + } |
528 | + |
529 | + return gl_major >= 2; |
530 | +} |
531 | + |
532 | +bool |
533 | +CanvasDRM::make_current() |
534 | +{ |
535 | + if (!egl_.valid()) { |
536 | + Log::error("CanvasDRM: Invalid EGL state\n"); |
537 | + return false; |
538 | + } |
539 | + |
540 | + init_gl_extensions(); |
541 | + |
542 | + return true; |
543 | +} |
544 | + |
545 | +bool |
546 | +CanvasDRM::reset_context() |
547 | +{ |
548 | + return egl_.reset(); |
549 | +} |
550 | + |
551 | +void |
552 | +CanvasDRM::swap_buffers() |
553 | +{ |
554 | + egl_.swap(); |
555 | + drm_.do_flip(); |
556 | +} |
557 | + |
558 | +void |
559 | +CanvasDRM::init_gl_extensions() |
560 | +{ |
561 | +#if USE_GLESv2 |
562 | + /* |
563 | + * Parse the extensions we care about from the extension string. |
564 | + * Don't even bother to get function pointers until we know the |
565 | + * extension is present. |
566 | + */ |
567 | + std::string extString; |
568 | + const char* exts = |
569 | + reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); |
570 | + if (exts) |
571 | + extString = exts; |
572 | + |
573 | + if (extString.find("GL_OES_mapbuffer") != std::string::npos) { |
574 | + GLExtensions::MapBuffer = reinterpret_cast<PFNGLMAPBUFFEROESPROC>( |
575 | + eglGetProcAddress("glMapBufferOES")); |
576 | + GLExtensions::UnmapBuffer = reinterpret_cast<PFNGLUNMAPBUFFEROESPROC>( |
577 | + eglGetProcAddress("glUnmapBufferOES")); |
578 | + } |
579 | +#elif USE_GL |
580 | + GLExtensions::MapBuffer = glMapBuffer; |
581 | + GLExtensions::UnmapBuffer = glUnmapBuffer; |
582 | +#endif |
583 | +} |
584 | + |
585 | + |
586 | +/******************* |
587 | + * Private methods * |
588 | + *******************/ |
589 | + |
590 | +void |
591 | +CanvasDRM::resize_no_viewport(int width, int height) |
592 | +{ |
593 | + width_ = drm_.mode_width(); |
594 | + height_ = drm_.mode_height(); |
595 | + |
596 | + if (!width_) |
597 | + width_ = width; |
598 | + if (!height_) |
599 | + height_ = height; |
600 | + |
601 | + projection_ = |
602 | + LibMatrix::Mat4::perspective(60.0, width_ / static_cast<float>(height_), |
603 | + 1.0, 1024.0); |
604 | +} |
605 | |
606 | === added file 'src/canvas-drm.h' |
607 | --- src/canvas-drm.h 1970-01-01 00:00:00 +0000 |
608 | +++ src/canvas-drm.h 2012-12-17 22:54:19 +0000 |
609 | @@ -0,0 +1,132 @@ |
610 | +// |
611 | +// Copyright © 2012 Linaro Limited |
612 | +// |
613 | +// This file is part of the glmark2 OpenGL (ES) 2.0 benchmark. |
614 | +// |
615 | +// glmark2 is free software: you can redistribute it and/or modify it under the |
616 | +// terms of the GNU General Public License as published by the Free Software |
617 | +// Foundation, either version 3 of the License, or (at your option) any later |
618 | +// version. |
619 | +// |
620 | +// glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY |
621 | +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
622 | +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
623 | +// details. |
624 | +// |
625 | +// You should have received a copy of the GNU General Public License along with |
626 | +// glmark2. If not, see <http://www.gnu.org/licenses/>. |
627 | +// |
628 | +// Authors: |
629 | +// Simon Que |
630 | +// Jesse Barker |
631 | +// |
632 | +#ifndef GLMARK2_CANVAS_DRM_H_ |
633 | +#define GLMARK2_CANVAS_DRM_H_ |
634 | + |
635 | +#include <cstring> |
636 | +#include <gbm.h> |
637 | +#include <drm.h> |
638 | +#include <xf86drm.h> |
639 | +#include <xf86drmMode.h> |
640 | + |
641 | +#include "canvas.h" |
642 | +#include "egl-state.h" |
643 | + |
644 | +struct DRMFBState |
645 | +{ |
646 | + int fd; |
647 | + gbm_bo* bo; |
648 | + uint32_t fb_id; |
649 | +}; |
650 | + |
651 | +class DRMState |
652 | +{ |
653 | + static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, |
654 | + unsigned int usec, void* data); |
655 | + static void fb_destroy_callback(gbm_bo* bo, void* data); |
656 | + static void quit_handler(int signum); |
657 | + static bool should_quit_; |
658 | + DRMFBState* fb_get_from_bo(gbm_bo* bo); |
659 | + bool init_gbm(); |
660 | + int fd_; |
661 | + drmModeRes* resources_; |
662 | + drmModeConnector* connector_; |
663 | + drmModeEncoder* encoder_; |
664 | + drmModeCrtcPtr crtc_; |
665 | + drmModeModeInfo* mode_; |
666 | + gbm_device* dev_; |
667 | + gbm_surface* surface_; |
668 | + gbm_bo* bo_; |
669 | + DRMFBState* fb_; |
670 | + |
671 | +public: |
672 | + DRMState() : |
673 | + fd_(0), |
674 | + resources_(0), |
675 | + connector_(0), |
676 | + encoder_(0), |
677 | + mode_(0), |
678 | + dev_(0), |
679 | + surface_(0), |
680 | + bo_(0), |
681 | + fb_(0) {} |
682 | + ~DRMState() { cleanup(); } |
683 | + void cleanup(); |
684 | + bool init(); |
685 | + bool reset(); |
686 | + void do_flip(); |
687 | + bool should_quit() const { return should_quit_; } |
688 | + gbm_device* device() const { return dev_; } |
689 | + gbm_surface* surface() const { return surface_; } |
690 | + unsigned int mode_width() const |
691 | + { |
692 | + if (mode_) { |
693 | + return mode_->hdisplay; |
694 | + } |
695 | + return 0; |
696 | + } |
697 | + unsigned int mode_height() const |
698 | + { |
699 | + if (mode_) { |
700 | + return mode_->vdisplay; |
701 | + } |
702 | + return 0; |
703 | + } |
704 | +}; |
705 | + |
706 | +/** |
707 | + * Canvas for direct rendering with EGL. |
708 | + */ |
709 | +class CanvasDRM: public Canvas |
710 | +{ |
711 | +public: |
712 | + CanvasDRM(int width, int height) : |
713 | + Canvas(width, height) {} |
714 | + ~CanvasDRM(); |
715 | + |
716 | + virtual bool init(); |
717 | + virtual bool reset(); |
718 | + virtual void visible(bool visible); |
719 | + virtual void clear(); |
720 | + virtual void update(); |
721 | + virtual void print_info(); |
722 | + virtual Pixel read_pixel(int x, int y); |
723 | + virtual void write_to_file(std::string &filename); |
724 | + virtual bool should_quit(); |
725 | + virtual void resize(int width, int height); |
726 | + |
727 | +protected: |
728 | + virtual bool make_current(); |
729 | + virtual bool reset_context(); |
730 | + virtual void swap_buffers(); |
731 | + virtual bool supports_gl2(); |
732 | + |
733 | +private: |
734 | + DRMState drm_; |
735 | + EGLState egl_; |
736 | + |
737 | + void resize_no_viewport(int width, int height); |
738 | + void init_gl_extensions(); |
739 | +}; |
740 | + |
741 | +#endif |
742 | |
743 | === modified file 'src/main.cpp' |
744 | --- src/main.cpp 2012-12-07 19:57:44 +0000 |
745 | +++ src/main.cpp 2012-12-17 22:54:19 +0000 |
746 | @@ -35,7 +35,9 @@ |
747 | #include <iostream> |
748 | #include <fstream> |
749 | |
750 | -#if USE_GL |
751 | +#if USE_DRM |
752 | +#include "canvas-drm.h" |
753 | +#elif USE_GL |
754 | #include "canvas-x11-glx.h" |
755 | #elif USE_GLESv2 |
756 | #include "canvas-x11-egl.h" |
757 | @@ -180,7 +182,9 @@ |
758 | } |
759 | |
760 | // Create the canvas |
761 | -#if USE_GL |
762 | +#if USE_DRM |
763 | + CanvasDRM canvas(Options::size.first, Options::size.second); |
764 | +#elif USE_GL |
765 | CanvasX11GLX canvas(Options::size.first, Options::size.second); |
766 | #elif USE_GLESv2 |
767 | CanvasX11EGL canvas(Options::size.first, Options::size.second); |
768 | |
769 | === modified file 'src/wscript_build' |
770 | --- src/wscript_build 2012-11-13 16:53:41 +0000 |
771 | +++ src/wscript_build 2012-12-17 22:54:19 +0000 |
772 | @@ -4,6 +4,8 @@ |
773 | f.name.find('egl-') == -1] |
774 | gl_sources = ['canvas-x11.cpp', 'canvas-x11-glx.cpp'] |
775 | glesv2_sources = ['canvas-x11.cpp', 'canvas-x11-egl.cpp', 'egl-state.cpp'] |
776 | +gl_drm_sources = ['canvas-drm.cpp', 'egl-state.cpp'] |
777 | +glesv2_drm_sources = ['canvas-drm.cpp', 'egl-state.cpp'] |
778 | libmatrix_sources = [f for f in bld.path.ant_glob('libmatrix/*.cc') |
779 | if not f.name.endswith('test.cc')] |
780 | includes = ['.', 'scene-ideas', 'scene-terrain'] |
781 | @@ -47,3 +49,44 @@ |
782 | includes = includes, |
783 | defines = ['USE_GLESv2', 'USE_EXCEPTIONS'] |
784 | ) |
785 | + |
786 | +if bld.env.USE_GL_DRM: |
787 | + bld( |
788 | + features = ['cxx', 'cxxstlib'], |
789 | + source = libmatrix_sources, |
790 | + target = 'matrix-drm', |
791 | + lib = ['m'], |
792 | + includes = ['.'], |
793 | + export_includes = 'libmatrix', |
794 | + defines = ['USE_DRM', '__GBM__', 'USE_GL', 'USE_EXCEPTIONS'] |
795 | + ) |
796 | + bld( |
797 | + features = ['cxx', 'cprogram'], |
798 | + source = common_sources + gl_drm_sources, |
799 | + target = 'glmark2-drm', |
800 | + use = ['egl', 'gl', 'matrix-drm', 'libpng12', 'drm', 'gbm'], |
801 | + lib = ['m', 'jpeg', 'dl'], |
802 | + includes = includes, |
803 | + defines = ['USE_DRM', '__GBM__', 'USE_GL'] |
804 | + ) |
805 | + |
806 | +if bld.env.USE_GLESv2_DRM: |
807 | + bld( |
808 | + features = ['cxx', 'cxxstlib'], |
809 | + source = libmatrix_sources, |
810 | + target = 'matrix-es2-drm', |
811 | + lib = ['m'], |
812 | + includes = ['.'], |
813 | + export_includes = 'libmatrix', |
814 | + defines = ['USE_DRM', '__GBM__', 'USE_GLESv2', 'USE_EXCEPTIONS'] |
815 | + ) |
816 | + bld( |
817 | + features = ['cxx', 'cprogram'], |
818 | + source = common_sources + glesv2_drm_sources, |
819 | + target = 'glmark2-es2-drm', |
820 | + use = ['egl', 'glesv2', 'matrix-es2-drm', 'libpng12', 'drm', |
821 | + 'gbm'], |
822 | + lib = ['m', 'jpeg', 'dl'], |
823 | + includes = includes, |
824 | + defines = ['USE_DRM', '__GBM__', 'USE_GLESv2'] |
825 | + ) |
826 | |
827 | === modified file 'wscript' |
828 | --- wscript 2012-11-26 19:29:33 +0000 |
829 | +++ wscript 2012-12-17 22:54:19 +0000 |
830 | @@ -20,6 +20,11 @@ |
831 | default = False, help='build using OpenGL 2.0') |
832 | opt.add_option('--enable-glesv2', action='store_true', dest = 'glesv2', |
833 | default = False, help='build using OpenGL ES 2.0') |
834 | + opt.add_option('--enable-gl-drm', action='store_true', dest = 'gl_drm', |
835 | + default = False, help='build using OpenGL 2.0 without X') |
836 | + opt.add_option('--enable-glesv2-drm', action='store_true', |
837 | + dest = 'glesv2_drm', |
838 | + default = False, help='build using OpenGL ES 2.0 without X') |
839 | opt.add_option('--no-debug', action='store_false', dest = 'debug', |
840 | default = True, help='disable compiler debug information') |
841 | opt.add_option('--no-opt', action='store_false', dest = 'opt', |
842 | @@ -30,8 +35,10 @@ |
843 | help='path to additional data (models, shaders, textures)') |
844 | |
845 | def configure(ctx): |
846 | - if not Options.options.gl and not Options.options.glesv2: |
847 | - ctx.fatal("You must configure using at least one of --enable-gl, --enable-glesv2") |
848 | + if not Options.options.gl and not Options.options.glesv2 and \ |
849 | + not Options.options.gl_drm and not Options.options.glesv2_drm: |
850 | + ctx.fatal("You must configure using at least one of --enable-gl, " + |
851 | + "--enable-glesv2, --enable-gl-drm, --enable-glesv2-drm") |
852 | |
853 | ctx.check_tool('gnu_dirs') |
854 | ctx.check_tool('compiler_cc') |
855 | @@ -54,15 +61,22 @@ |
856 | uselib = uselib, mandatory = True) |
857 | |
858 | # Check required packages |
859 | - req_pkgs = [('x11', 'x11'), ('libpng12', 'libpng12')] |
860 | + req_pkgs = [('libpng12', 'libpng12')] |
861 | for (pkg, uselib) in req_pkgs: |
862 | ctx.check_cfg(package = pkg, uselib_store = uselib, |
863 | args = '--cflags --libs', mandatory = True) |
864 | |
865 | # Check optional packages |
866 | - opt_pkgs = [('gl', 'gl', Options.options.gl), |
867 | - ('egl', 'egl', Options.options.glesv2), |
868 | - ('glesv2', 'glesv2', Options.options.glesv2)] |
869 | + opt_pkgs = [('x11', 'x11', Options.options.gl or Options.options.glesv2), |
870 | + ('gl', 'gl', Options.options.gl or Options.options.gl_drm), |
871 | + ('egl', 'egl', Options.options.glesv2 or |
872 | + Options.options.glesv2_drm), |
873 | + ('glesv2', 'glesv2', Options.options.glesv2 or |
874 | + Options.options.glesv2_drm), |
875 | + ('libdrm','drm', Options.options.gl_drm or |
876 | + Options.options.glesv2_drm), |
877 | + ('gbm','gbm', Options.options.gl_drm or |
878 | + Options.options.glesv2_drm)] |
879 | for (pkg, uselib, mandatory) in opt_pkgs: |
880 | ctx.check_cfg(package = pkg, uselib_store = uselib, |
881 | args = '--cflags --libs', mandatory = mandatory) |
882 | @@ -94,6 +108,8 @@ |
883 | |
884 | ctx.env.USE_GL = Options.options.gl |
885 | ctx.env.USE_GLESv2 = Options.options.glesv2 |
886 | + ctx.env.USE_GL_DRM = Options.options.gl_drm |
887 | + ctx.env.USE_GLESv2_DRM = Options.options.glesv2_drm |
888 | |
889 | ctx.msg("Prefix", ctx.env.PREFIX, color = 'PINK') |
890 | ctx.msg("Data path", data_path, color = 'PINK') |
891 | @@ -101,9 +117,13 @@ |
892 | color = 'PINK'); |
893 | if ctx.env.HAVE_EXTRAS: |
894 | ctx.msg("Extras path", Options.options.extras_path, color = 'PINK') |
895 | - ctx.msg("Building GL2 version", "Yes" if ctx.env.USE_GL else "No", |
896 | - color = 'PINK') |
897 | - ctx.msg("Building GLESv2 version", "Yes" if ctx.env.USE_GLESv2 else "No", |
898 | + ctx.msg("Building X11 GL2 version", "Yes" if ctx.env.USE_GL else "No", |
899 | + color = 'PINK') |
900 | + ctx.msg("Building X11 GLESv2 version", "Yes" if ctx.env.USE_GLESv2 else "No", |
901 | + color = 'PINK') |
902 | + ctx.msg("Building DRM GL2 version", "Yes" if ctx.env.USE_GL_DRM else "No", |
903 | + color = 'PINK') |
904 | + ctx.msg("Building DRM GLESv2 version", "Yes" if ctx.env.USE_GLESv2_DRM else "No", |
905 | color = 'PINK') |
906 | |
907 | def build(ctx): |
Rendering works fine on the platforms I have tested. The only problems I see are during shutdown:
1. Pressing Ctrl+C occasionally doesn't seem to reset the crtc correctly, so I end up with a blank screen. It also always causes an error message to be displayed (Error in select: -1).
2. Pressing Enter (and therefore flushing stdin), causes a segfault. This happens because we are leaving DRMState::do_flip() with both surface buffers locked. Next time we call gbm_surface_ lock_front_ buffer( surface_ ) we get back a NULL value which we currently don't handle properly (i.e. we just use it). We get into the same situation if select fails etc
(1) is not critical, so I am ok with shipping this as part of an experimental drm backend release (perhaps we could add a warning about the experimental status when startinp up?).
On the other hand, I think (2) should be fixed before this release, if possible, as it's relatively easy for a user to cause a segfault. For example, a user that wants to quit will probably try pressing ESC (which won't work), then some keys then Enter.