Mir

Merge lp:~raof/mir/check-for-surfaceless into lp:mir

Proposed by Chris Halse Rogers
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1297
Proposed branch: lp:~raof/mir/check-for-surfaceless
Merge into: lp:mir
Diff against target: 928 lines (+406/-151)
21 files modified
include/platform/mir/graphics/gl_context.h (+2/-2)
include/server/mir/graphics/gl_extensions_base.h (+3/-6)
include/server/mir/graphics/surfaceless_egl_context.h (+11/-10)
include/test/mir_test_doubles/null_gl_context.h (+2/-2)
src/platform/graphics/android/gl_context.cpp (+2/-2)
src/platform/graphics/android/gl_context.h (+2/-2)
src/platform/graphics/mesa/display.cpp (+2/-2)
src/platform/graphics/mesa/display_helpers.cpp (+2/-2)
src/platform/graphics/mesa/display_helpers.h (+2/-2)
src/server/graphics/CMakeLists.txt (+2/-0)
src/server/graphics/gl_extensions_base.cpp (+4/-4)
src/server/graphics/nested/nested_display.cpp (+4/-45)
src/server/graphics/offscreen/CMakeLists.txt (+0/-2)
src/server/graphics/offscreen/display.cpp (+1/-29)
src/server/graphics/offscreen/display.h (+1/-1)
src/server/graphics/offscreen/display_buffer.cpp (+3/-3)
src/server/graphics/offscreen/display_buffer.h (+1/-1)
src/server/graphics/surfaceless_egl_context.cpp (+103/-32)
tests/unit-tests/graphics/CMakeLists.txt (+1/-0)
tests/unit-tests/graphics/test_surfaceless_egl_context.cpp (+254/-0)
tests/unit-tests/scene/test_gl_pixel_buffer.cpp (+4/-4)
To merge this branch: bzr merge lp:~raof/mir/check-for-surfaceless
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel van Vugt Approve
Alexandros Frantzis (community) Abstain
Chris Halse Rogers Approve
Andreas Pokorny (community) Approve
Review via email: mp+198510@code.launchpad.net

Commit message

nested: Check for, and prefer, EGL_KHR_surfaceless_context over dummy pbuffer surfaces

It seems that at least one of our Android stacks doesn't support
EGL_KHR_surfaceless_context, and Mesa doesn't support pbuffer surfaces.

Fixes nested on Mesa (LP: #1260635)

Description of the change

nested: Check for, and prefer, EGL_KHR_surfaceless_context over dummy pbuffer surfaces

It seems that at least one of our Android stacks doesn't support EGL_KHR_surfaceless_context,
and Mesa doesn't support pbuffer surfaces.

Fixes nested on Mesa

To post a comment you must log in.
Revision history for this message
Chris Halse Rogers (raof) wrote :

Note: I'm in the process of testing nested on Android. Although I don't see how this would fail on Android, it'd be courteous to check that nested still works before top approving this :)

review: Needs Information
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Looks good, if you ignore what C++ forces us to do with initializer lists :(

Nit: Missing an underscore:
31 + (khr_surfaceless_context ? " (using EGL_KHR_surfaceless context)\n" :

review: Approve
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

I think

16 + khr_surfaceless_context(strstr(eglQueryString(egl_display, EGL_EXTENSIONS), "EGL_KHR_surfaceless_context") != nullptr)
 wants to be a utility function for querying egl extension existance

You could also turn
20 + egl_surface( .... )

 into
  egl_surface( EGLSurfaceStore::create_dummy_surface(egl_display, egl_config, khr_surfaceless_context) )

and evade control flow in the initializer list.

-> yay and that would be my first launchpad review.

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

Ok, confirmed that this doesn't break android. Time to address some reviews!

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

You might also want to take a look at graphics/offscreen/surfaceless_egl_context.cpp and friends, which already provide the needed functionality.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

looks fine to me

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Now really tested on Android (I didn't know you could run two different mir_demo_server_shells against the hardware simultaneously!), and with some tests. That pass!

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Verified nesting now works on desktop (LP: #1260635)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Only minor issues...

1. Typo:
77 + /* We have to explicitly define this, as GLContext has a deleted copy construtor */

2. constness of make_current/release_current; I don't think this is necessary and doesn't look very natural to make such operations const. The ability to make them const is probably only because they're implemented using non-C++ objects (EGL). It seems likely they might have to lose the constness at some point in future following other changes.

3. while (parentheses_dont_match_mir_stye) {
    needs_fixing
   }
I prefer this style, but it's too much of an obvious deviation from the Mir project style.

4. Unreadably long identifiers:
768 +TEST_F(SurfacelessEGLContextTest, RequestsPBufferWhenNoSurfacelessAndAttribsDoesNotContainSurfaceSpecifier)
787 +TEST_F(SurfacelessEGLContextTest, RequestsPBufferWhenNoSurfacelessAndAttribsContainsNonPBufferSurfaceType)
On that theme, so are lines longer than 80 columns.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

578 +mg::SurfacelessEGLContext::SurfacelessEGLContext(

587 +mg::SurfacelessEGLContext::SurfacelessEGLContext(
588 + EGLDisplay egl_display,
589 + EGLint const* attribs,
590 + EGLContext shared_context)

It would be clearer to implement the SurfaceEGLContext(egl_display, shared_context) constructor in terms of the new SurfaceEGLContext(egl_display, attribs, shared_context) constructor, by passing our default config attribs:

SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context)
 : SurfacelessEGLContext(egl_display, default_attribs, shared_context)

429 +mgo::DisplayBuffer::DisplayBuffer(SurfacelessEGLContext &&egl_context,
430 geom::Rectangle const& area)
431 - : egl_context{std::move(egl_context)},
432 + : egl_context{std::forward<SurfacelessEGLContext>(egl_context)},

It's better to accept a move only type (such as SurfacelessEGLContext or std::unique_ptr<>) by value vs rvalue reference: see https://code.launchpad.net/~rocket-scientists/mir/buffer-swapper-multi/+merge/139127/comments/301713 .

Also, with this code, since the egl_context parameter is an rvalue reference, not a universal reference, using std::forward doesn't offer any benefits over using std::move.

613 + move.egl_display = EGL_NO_DISPLAY;

618 + release_current();

A invalidated (i.e. moved) SurfacelessEGLContext object may try to release the EGL context when being destroyed. Although this will have no effect since egl_display == EGL_NO_DISPLAY, I think it would be better to not try at all to avoid error/debug messages from EGL.

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

On Mon, 2013-12-16 at 11:40 +0000, Alexandros Frantzis wrote:
> Review: Needs Fixing
>
> 578 +mg::SurfacelessEGLContext::SurfacelessEGLContext(
>
> 587 +mg::SurfacelessEGLContext::SurfacelessEGLContext(
> 588 + EGLDisplay egl_display,
> 589 + EGLint const* attribs,
> 590 + EGLContext shared_context)
>
> It would be clearer to implement the SurfaceEGLContext(egl_display, shared_context) constructor in terms of the new SurfaceEGLContext(egl_display, attribs, shared_context) constructor, by passing our default config attribs:
>
> SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context)
> : SurfacelessEGLContext(egl_display, default_attribs, shared_context)
>

Quite true! Fixed.

> 429 +mgo::DisplayBuffer::DisplayBuffer(SurfacelessEGLContext &&egl_context,
> 430 geom::Rectangle const& area)
> 431 - : egl_context{std::move(egl_context)},
> 432 + : egl_context{std::forward<SurfacelessEGLContext>(egl_context)},
>
> It's better to accept a move only type (such as SurfacelessEGLContext or std::unique_ptr<>) by value vs rvalue reference: see https://code.launchpad.net/~rocket-scientists/mir/buffer-swapper-multi/+merge/139127/comments/301713 .

Done.
>
> 613 + move.egl_display = EGL_NO_DISPLAY;
>
> 618 + release_current();
>
> A invalidated (i.e. moved) SurfacelessEGLContext object may try to release the EGL context when being destroyed. Although this will have no effect since egl_display == EGL_NO_DISPLAY, I think it would be better to not try at all to avoid error/debug messages from EGL.

Oh, right. If there's no current context then it'll attempt to release.
Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good overall, with the exception of some brace placement style issues in the tests.

Abstaining in order not to block this MP.

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Still too many "minor issues". See my previous comment. This includes some braces still not fixed.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm not going to block on a couple of minor issues, but there are still more than a couple. Fix half-ish and I'm happy :)

3. Braces you missed - bzr diff | egrep '^\+.*[^ ]+ {$'
+EGLint const default_attr[] = {
+namespace {
+ if (found_surface_type) {
+ const EGLint attribs_with_pbuffer[] = {
+ const EGLint attribs_without_surface_type[] = {
+ const EGLint attribs_without_pbuffer[] = {
+ const EGLint attribs_without_surface_type[] = {
+ const EGLint attribs_with_surface_type[] = {

2. I would prefer they're not const.

4. Use your imagination. You know more about the context than I do.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

OK, enough back and forth.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/platform/mir/graphics/gl_context.h'
2--- include/platform/mir/graphics/gl_context.h 2013-12-17 18:24:51 +0000
3+++ include/platform/mir/graphics/gl_context.h 2013-12-19 09:13:25 +0000
4@@ -29,8 +29,8 @@
5 public:
6 virtual ~GLContext() = default;
7
8- virtual void make_current() = 0;
9- virtual void release_current() = 0;
10+ virtual void make_current() const = 0;
11+ virtual void release_current() const = 0;
12
13 protected:
14 GLContext() = default;
15
16=== renamed file 'src/server/graphics/offscreen/gl_extensions_base.h' => 'include/server/mir/graphics/gl_extensions_base.h'
17--- src/server/graphics/offscreen/gl_extensions_base.h 2013-11-12 17:23:53 +0000
18+++ include/server/mir/graphics/gl_extensions_base.h 2013-12-19 09:13:25 +0000
19@@ -16,15 +16,13 @@
20 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
21 */
22
23-#ifndef MIR_GRAPHICS_OFFSCREEN_GL_EXTENSIONS_BASE_H_
24-#define MIR_GRAPHICS_OFFSCREEN_GL_EXTENSIONS_BASE_H_
25+#ifndef MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_
26+#define MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_
27
28 namespace mir
29 {
30 namespace graphics
31 {
32-namespace offscreen
33-{
34
35 class GLExtensionsBase
36 {
37@@ -42,6 +40,5 @@
38
39 }
40 }
41-}
42
43-#endif /* MIR_GRAPHICS_OFFSCREEN_GL_EXTENSIONS_BASE_H_ */
44+#endif /* MIR_GRAPHICS_GL_EXTENSIONS_BASE_H_ */
45
46=== renamed file 'src/server/graphics/offscreen/surfaceless_egl_context.h' => 'include/server/mir/graphics/surfaceless_egl_context.h'
47--- src/server/graphics/offscreen/surfaceless_egl_context.h 2013-11-12 17:23:53 +0000
48+++ include/server/mir/graphics/surfaceless_egl_context.h 2013-12-19 09:13:25 +0000
49@@ -16,10 +16,11 @@
50 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
51 */
52
53-#ifndef MIR_GRAPHICS_OFFSCREEN_SURFACELESS_EGL_CONTEXT_H_
54-#define MIR_GRAPHICS_OFFSCREEN_SURFACELESS_EGL_CONTEXT_H_
55+#ifndef MIR_GRAPHICS_SURFACELESS_EGL_CONTEXT_H_
56+#define MIR_GRAPHICS_SURFACELESS_EGL_CONTEXT_H_
57
58 #include "mir/graphics/egl_resources.h"
59+#include "mir/graphics/gl_context.h"
60
61 #include <EGL/egl.h>
62
63@@ -27,17 +28,18 @@
64 {
65 namespace graphics
66 {
67-namespace offscreen
68-{
69
70-class SurfacelessEGLContext
71+class SurfacelessEGLContext : public GLContext
72 {
73 public:
74 SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context);
75- SurfacelessEGLContext(SurfacelessEGLContext&&) = default;
76+ SurfacelessEGLContext(EGLDisplay egl_display, EGLint const* attribs, EGLContext shared_context);
77+ /* We have to explicitly define this, as GLContext has a deleted copy constructor */
78+ SurfacelessEGLContext(SurfacelessEGLContext&& move);
79+ virtual ~SurfacelessEGLContext() noexcept;
80
81- void make_current() const;
82- void release_current() const;
83+ void make_current() const override;
84+ void release_current() const override;
85
86 operator EGLContext() const;
87
88@@ -54,6 +56,5 @@
89
90 }
91 }
92-}
93
94-#endif /* MIR_GRAPHICS_OFFSCREEN_SURFACELESS_EGL_SURFACE_H_ */
95+#endif /* MIR_GRAPHICS_SURFACELESS_EGL_SURFACE_H_ */
96
97=== modified file 'include/test/mir_test_doubles/null_gl_context.h'
98--- include/test/mir_test_doubles/null_gl_context.h 2013-06-12 10:27:50 +0000
99+++ include/test/mir_test_doubles/null_gl_context.h 2013-12-19 09:13:25 +0000
100@@ -31,8 +31,8 @@
101 class NullGLContext : public graphics::GLContext
102 {
103 public:
104- void make_current() {}
105- void release_current() {}
106+ void make_current() const {}
107+ void release_current() const {}
108 };
109
110 }
111
112=== modified file 'src/platform/graphics/android/gl_context.cpp'
113--- src/platform/graphics/android/gl_context.cpp 2013-12-17 18:24:51 +0000
114+++ src/platform/graphics/android/gl_context.cpp 2013-12-19 09:13:25 +0000
115@@ -131,7 +131,7 @@
116 {
117 }
118
119-void mga::GLContext::make_current()
120+void mga::GLContext::make_current() const
121 {
122 if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)
123 {
124@@ -140,7 +140,7 @@
125 }
126 }
127
128-void mga::GLContext::release_current()
129+void mga::GLContext::release_current() const
130 {
131 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
132 }
133
134=== modified file 'src/platform/graphics/android/gl_context.h'
135--- src/platform/graphics/android/gl_context.h 2013-12-17 18:24:51 +0000
136+++ src/platform/graphics/android/gl_context.h 2013-12-19 09:13:25 +0000
137@@ -48,9 +48,9 @@
138
139 ~GLContext();
140
141- void make_current();
142+ void make_current() const override;
143 void swap_buffers();
144- void release_current();
145+ void release_current() const override;
146
147 /* TODO: (kdub) remove these two functions once HWC1.0 construction is sorted out. */
148 EGLDisplay display()
149
150=== modified file 'src/platform/graphics/mesa/display.cpp'
151--- src/platform/graphics/mesa/display.cpp 2013-12-17 18:24:51 +0000
152+++ src/platform/graphics/mesa/display.cpp 2013-12-19 09:13:25 +0000
153@@ -58,12 +58,12 @@
154 egl.setup(gbm, shared_context);
155 }
156
157- void make_current()
158+ void make_current() const override
159 {
160 egl.make_current();
161 }
162
163- void release_current()
164+ void release_current() const override
165 {
166 egl.release_current();
167 }
168
169=== modified file 'src/platform/graphics/mesa/display_helpers.cpp'
170--- src/platform/graphics/mesa/display_helpers.cpp 2013-12-17 18:24:51 +0000
171+++ src/platform/graphics/mesa/display_helpers.cpp 2013-12-19 09:13:25 +0000
172@@ -357,13 +357,13 @@
173 return (ret == EGL_TRUE);
174 }
175
176-bool mgmh::EGLHelper::make_current()
177+bool mgmh::EGLHelper::make_current() const
178 {
179 auto ret = eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
180 return (ret == EGL_TRUE);
181 }
182
183-bool mgmh::EGLHelper::release_current()
184+bool mgmh::EGLHelper::release_current() const
185 {
186 auto ret = eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
187 return (ret == EGL_TRUE);
188
189=== modified file 'src/platform/graphics/mesa/display_helpers.h'
190--- src/platform/graphics/mesa/display_helpers.h 2013-12-17 18:24:51 +0000
191+++ src/platform/graphics/mesa/display_helpers.h 2013-12-19 09:13:25 +0000
192@@ -108,8 +108,8 @@
193 EGLContext shared_context);
194
195 bool swap_buffers();
196- bool make_current();
197- bool release_current();
198+ bool make_current() const;
199+ bool release_current() const;
200
201 EGLContext context() { return egl_context; }
202
203
204=== modified file 'src/server/graphics/CMakeLists.txt'
205--- src/server/graphics/CMakeLists.txt 2013-12-17 18:24:51 +0000
206+++ src/server/graphics/CMakeLists.txt 2013-12-19 09:13:25 +0000
207@@ -5,6 +5,8 @@
208
209 default_configuration.cpp
210 default_display_configuration_policy.cpp
211+ gl_extensions_base.cpp
212+ surfaceless_egl_context.cpp
213 )
214
215 add_subdirectory(nested/)
216
217=== renamed file 'src/server/graphics/offscreen/gl_extensions_base.cpp' => 'src/server/graphics/gl_extensions_base.cpp'
218--- src/server/graphics/offscreen/gl_extensions_base.cpp 2013-11-14 09:45:44 +0000
219+++ src/server/graphics/gl_extensions_base.cpp 2013-12-19 09:13:25 +0000
220@@ -16,16 +16,16 @@
221 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
222 */
223
224-#include "gl_extensions_base.h"
225+#include "mir/graphics/gl_extensions_base.h"
226
227 #include <boost/throw_exception.hpp>
228 #include <stdexcept>
229
230 #include <cstring>
231
232-namespace mgo = mir::graphics::offscreen;
233+namespace mg = mir::graphics;
234
235-mgo::GLExtensionsBase::GLExtensionsBase(char const* extensions)
236+mg::GLExtensionsBase::GLExtensionsBase(char const* extensions)
237 : extensions{extensions}
238 {
239 if (!extensions)
240@@ -35,7 +35,7 @@
241 }
242 }
243
244-bool mgo::GLExtensionsBase::support(char const* ext) const
245+bool mg::GLExtensionsBase::support(char const* ext) const
246 {
247 char const* ext_ptr = extensions;
248 size_t const len = strlen(ext);
249
250=== modified file 'src/server/graphics/nested/nested_display.cpp'
251--- src/server/graphics/nested/nested_display.cpp 2013-12-17 18:24:51 +0000
252+++ src/server/graphics/nested/nested_display.cpp 2013-12-19 09:13:25 +0000
253@@ -23,6 +23,7 @@
254
255 #include "mir/geometry/rectangle.h"
256 #include "mir/graphics/gl_context.h"
257+#include "mir/graphics/surfaceless_egl_context.h"
258 #include "host_connection.h"
259
260 #include <boost/throw_exception.hpp>
261@@ -275,53 +276,11 @@
262
263 namespace
264 {
265-EGLint const dummy_pbuffer_attribs[] =
266-{
267- EGL_WIDTH, 1,
268- EGL_HEIGHT, 1,
269- EGL_NONE
270-};
271 }
272
273 std::unique_ptr<mg::GLContext> mgn::NestedDisplay::create_gl_context()
274 {
275- class NestedGLContext : public mg::GLContext
276- {
277- public:
278- NestedGLContext(detail::EGLDisplayHandle const& egl_display) :
279- egl_display{egl_display},
280- egl_config{egl_display.choose_config(detail::nested_egl_config_attribs)},
281- egl_context{egl_display, eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, detail::nested_egl_context_attribs)},
282- egl_surface{egl_display, eglCreatePbufferSurface(egl_display, egl_config, dummy_pbuffer_attribs)}
283- {
284- }
285-
286- ~NestedGLContext() noexcept
287- {
288- if (eglGetCurrentContext() == egl_context)
289- release_current();
290- }
291-
292- void make_current() override
293- {
294- if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) == EGL_FALSE)
295- {
296- BOOST_THROW_EXCEPTION(
297- std::runtime_error("could not activate dummy surface with eglMakeCurrent\n"));
298- }
299- }
300-
301- void release_current() override
302- {
303- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
304- }
305-
306- private:
307- EGLDisplay const egl_display;
308- EGLConfig const egl_config;
309- EGLContextStore const egl_context;
310- EGLSurfaceStore const egl_surface;
311- };
312-
313- return std::unique_ptr<mg::GLContext>{new NestedGLContext(egl_display)};
314+ return std::unique_ptr<mg::GLContext>{new SurfacelessEGLContext(egl_display,
315+ detail::nested_egl_config_attribs,
316+ EGL_NO_CONTEXT)};
317 }
318
319=== modified file 'src/server/graphics/offscreen/CMakeLists.txt'
320--- src/server/graphics/offscreen/CMakeLists.txt 2013-11-12 17:23:53 +0000
321+++ src/server/graphics/offscreen/CMakeLists.txt 2013-12-19 09:13:25 +0000
322@@ -4,8 +4,6 @@
323 display.cpp
324 display_configuration.cpp
325 display_buffer.cpp
326- surfaceless_egl_context.cpp
327- gl_extensions_base.cpp
328 )
329
330 target_link_libraries(
331
332=== modified file 'src/server/graphics/offscreen/display.cpp'
333--- src/server/graphics/offscreen/display.cpp 2013-11-18 12:35:14 +0000
334+++ src/server/graphics/offscreen/display.cpp 2013-12-19 09:13:25 +0000
335@@ -20,7 +20,6 @@
336 #include "display_buffer.h"
337 #include "mir/graphics/basic_platform.h"
338 #include "mir/graphics/display_configuration_policy.h"
339-#include "mir/graphics/gl_context.h"
340 #include "mir/geometry/size.h"
341
342 #include <boost/throw_exception.hpp>
343@@ -33,33 +32,6 @@
344 namespace
345 {
346
347-class OffscreenGLContext : public mg::GLContext
348-{
349-public:
350- OffscreenGLContext(mgo::SurfacelessEGLContext surfaceless_egl_context)
351- : surfaceless_egl_context{std::move(surfaceless_egl_context)}
352- {
353- }
354-
355- ~OffscreenGLContext() noexcept
356- {
357- release_current();
358- }
359-
360- void make_current()
361- {
362- surfaceless_egl_context.make_current();
363- }
364-
365- void release_current()
366- {
367- surfaceless_egl_context.release_current();
368- }
369-
370-private:
371- mgo::SurfacelessEGLContext const surfaceless_egl_context;
372-};
373-
374 mgo::detail::EGLDisplayHandle
375 create_and_initialize_display(mg::BasicPlatform& basic_platform)
376 {
377@@ -193,5 +165,5 @@
378 std::unique_ptr<mg::GLContext> mgo::Display::create_gl_context()
379 {
380 return std::unique_ptr<GLContext>{
381- new OffscreenGLContext{SurfacelessEGLContext{egl_display, egl_context_shared}}};
382+ new SurfacelessEGLContext{egl_display, egl_context_shared}};
383 }
384
385=== modified file 'src/server/graphics/offscreen/display.h'
386--- src/server/graphics/offscreen/display.h 2013-11-18 17:08:15 +0000
387+++ src/server/graphics/offscreen/display.h 2013-12-19 09:13:25 +0000
388@@ -21,7 +21,7 @@
389
390 #include "mir/graphics/display.h"
391 #include "display_configuration.h"
392-#include "surfaceless_egl_context.h"
393+#include "mir/graphics/surfaceless_egl_context.h"
394
395 #include <mutex>
396 #include <vector>
397
398=== modified file 'src/server/graphics/offscreen/display_buffer.cpp'
399--- src/server/graphics/offscreen/display_buffer.cpp 2013-12-17 18:24:51 +0000
400+++ src/server/graphics/offscreen/display_buffer.cpp 2013-12-19 09:13:25 +0000
401@@ -17,7 +17,7 @@
402 */
403
404 #include "display_buffer.h"
405-#include "gl_extensions_base.h"
406+#include "mir/graphics/gl_extensions_base.h"
407 #include "mir/raii.h"
408
409 #include <boost/throw_exception.hpp>
410@@ -33,11 +33,11 @@
411 namespace
412 {
413
414-class GLExtensions : public mgo::GLExtensionsBase
415+class GLExtensions : public mg::GLExtensionsBase
416 {
417 public:
418 GLExtensions() :
419- mgo::GLExtensionsBase{
420+ mg::GLExtensionsBase{
421 reinterpret_cast<char const*>(glGetString(GL_EXTENSIONS))}
422 {
423 }
424
425=== modified file 'src/server/graphics/offscreen/display_buffer.h'
426--- src/server/graphics/offscreen/display_buffer.h 2013-11-14 09:46:02 +0000
427+++ src/server/graphics/offscreen/display_buffer.h 2013-12-19 09:13:25 +0000
428@@ -19,7 +19,7 @@
429 #ifndef MIR_GRAPHICS_OFFSCREEN_DISPLAY_BUFFER_H_
430 #define MIR_GRAPHICS_OFFSCREEN_DISPLAY_BUFFER_H_
431
432-#include "surfaceless_egl_context.h"
433+#include "mir/graphics/surfaceless_egl_context.h"
434
435 #include "mir/graphics/display_buffer.h"
436 #include "mir/geometry/size.h"
437
438=== renamed file 'src/server/graphics/offscreen/surfaceless_egl_context.cpp' => 'src/server/graphics/surfaceless_egl_context.cpp'
439--- src/server/graphics/offscreen/surfaceless_egl_context.cpp 2013-11-18 12:58:38 +0000
440+++ src/server/graphics/surfaceless_egl_context.cpp 2013-12-19 09:13:25 +0000
441@@ -16,22 +16,23 @@
442 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
443 */
444
445-#include "surfaceless_egl_context.h"
446-#include "gl_extensions_base.h"
447+#include "mir/graphics/surfaceless_egl_context.h"
448+#include "mir/graphics/gl_extensions_base.h"
449
450 #include <boost/throw_exception.hpp>
451 #include <stdexcept>
452+#include <vector>
453
454-namespace mgo = mir::graphics::offscreen;
455+namespace mg = mir::graphics;
456
457 namespace
458 {
459
460-class EGLExtensions : public mgo::GLExtensionsBase
461+class EGLExtensions : public mg::GLExtensionsBase
462 {
463 public:
464 EGLExtensions(EGLDisplay egl_display) :
465- mgo::GLExtensionsBase{
466+ mg::GLExtensionsBase{
467 reinterpret_cast<char const*>(eglQueryString(egl_display, EGL_EXTENSIONS))}
468 {
469 }
470@@ -44,27 +45,62 @@
471 return extensions.support("EGL_KHR_surfaceless_context");
472 }
473
474-EGLConfig choose_config(EGLDisplay egl_display, bool surfaceless)
475-{
476- EGLint const surface_type = surfaceless ? EGL_DONT_CARE : EGL_PBUFFER_BIT;
477-
478- EGLint const config_attr[] = {
479- EGL_SURFACE_TYPE, surface_type,
480- EGL_RED_SIZE, 8,
481- EGL_GREEN_SIZE, 8,
482- EGL_BLUE_SIZE, 8,
483- EGL_ALPHA_SIZE, 0,
484- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
485- EGL_NONE
486- };
487-
488+std::vector<EGLint> ensure_pbuffer_set(EGLint const* attribs)
489+{
490+ bool has_preferred_surface = false;
491+ std::vector<EGLint> attribs_with_surface_type;
492+ int i = 0;
493+
494+ while (attribs[i] != EGL_NONE)
495+ {
496+ attribs_with_surface_type.push_back(attribs[i]);
497+ if (attribs[i] == EGL_SURFACE_TYPE)
498+ {
499+ has_preferred_surface = true;
500+ if (attribs[i+1] == EGL_DONT_CARE)
501+ {
502+ /* Need to treat EGL_DONT_CARE specially, as it is defined as all-bits-set */
503+ attribs_with_surface_type.push_back(EGL_PBUFFER_BIT);
504+ }
505+ else
506+ {
507+ attribs_with_surface_type.push_back(attribs[i+1] | EGL_PBUFFER_BIT);
508+ }
509+ }
510+ else
511+ {
512+ attribs_with_surface_type.push_back(attribs[i+1]);
513+ }
514+ i += 2;
515+ }
516+
517+ if (!has_preferred_surface)
518+ {
519+ attribs_with_surface_type.push_back(EGL_SURFACE_TYPE);
520+ attribs_with_surface_type.push_back(EGL_PBUFFER_BIT);
521+ }
522+
523+ attribs_with_surface_type.push_back(EGL_NONE);
524+
525+ return attribs_with_surface_type;
526+}
527+
528+EGLConfig choose_config(EGLDisplay egl_display, EGLint const* attribs, bool surfaceless)
529+{
530 EGLConfig egl_config{0};
531 int num_egl_configs{0};
532-
533- if (eglChooseConfig(egl_display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE ||
534+ std::vector<EGLint> validated_attribs;
535+
536+ if (!surfaceless)
537+ {
538+ validated_attribs = ensure_pbuffer_set(attribs);
539+ attribs = validated_attribs.data();
540+ }
541+
542+ if (eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_egl_configs) == EGL_FALSE ||
543 num_egl_configs != 1)
544 {
545- BOOST_THROW_EXCEPTION(std::runtime_error("Failed to choose ARGB EGL config"));
546+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to choose EGL config"));
547 }
548
549 return egl_config;
550@@ -88,14 +124,32 @@
551 EGL_NONE
552 };
553
554-}
555-
556-mgo::SurfacelessEGLContext::SurfacelessEGLContext(
557- EGLDisplay egl_display,
558- EGLContext shared_context)
559+EGLint const default_attr[] =
560+{
561+ EGL_SURFACE_TYPE, EGL_DONT_CARE,
562+ EGL_RED_SIZE, 8,
563+ EGL_GREEN_SIZE, 8,
564+ EGL_BLUE_SIZE, 8,
565+ EGL_ALPHA_SIZE, 0,
566+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
567+ EGL_NONE
568+};
569+
570+
571+}
572+
573+mg::SurfacelessEGLContext::SurfacelessEGLContext(EGLDisplay egl_display, EGLContext shared_context)
574+ : SurfacelessEGLContext(egl_display, default_attr, shared_context)
575+{
576+}
577+
578+mg::SurfacelessEGLContext::SurfacelessEGLContext(
579+ EGLDisplay egl_display,
580+ EGLint const* attribs,
581+ EGLContext shared_context)
582 : egl_display{egl_display},
583 surfaceless{supports_surfaceless_context(egl_display)},
584- egl_config{choose_config(egl_display, surfaceless)},
585+ egl_config{choose_config(egl_display, attribs, surfaceless)},
586 egl_surface{egl_display,
587 surfaceless ? EGL_NO_SURFACE : create_surface(egl_display, egl_config),
588 surfaceless ? EGLSurfaceStore::AllowNoSurface :
589@@ -107,7 +161,24 @@
590 {
591 }
592
593-void mgo::SurfacelessEGLContext::make_current() const
594+
595+mg::SurfacelessEGLContext::SurfacelessEGLContext(SurfacelessEGLContext&& move)
596+ : egl_display(move.egl_display),
597+ surfaceless(move.surfaceless),
598+ egl_config(move.egl_config),
599+ egl_surface{std::move(move.egl_surface)},
600+ egl_context{std::move(move.egl_context)}
601+{
602+ move.egl_display = EGL_NO_DISPLAY;
603+}
604+
605+mg::SurfacelessEGLContext::~SurfacelessEGLContext() noexcept
606+{
607+ release_current();
608+}
609+
610+
611+void mg::SurfacelessEGLContext::make_current() const
612 {
613 if (eglGetCurrentContext() == egl_context)
614 return;
615@@ -120,13 +191,13 @@
616 }
617 }
618
619-void mgo::SurfacelessEGLContext::release_current() const
620+void mg::SurfacelessEGLContext::release_current() const
621 {
622- if (eglGetCurrentContext() == egl_context)
623+ if (egl_context != EGL_NO_CONTEXT && eglGetCurrentContext() == egl_context)
624 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
625 }
626
627-mgo::SurfacelessEGLContext::operator EGLContext() const
628+mg::SurfacelessEGLContext::operator EGLContext() const
629 {
630 return egl_context;
631 }
632
633=== modified file 'tests/unit-tests/graphics/CMakeLists.txt'
634--- tests/unit-tests/graphics/CMakeLists.txt 2013-12-17 18:24:51 +0000
635+++ tests/unit-tests/graphics/CMakeLists.txt 2013-12-19 09:13:25 +0000
636@@ -6,6 +6,7 @@
637 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_display_configuration_policy.cpp
638 ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_id.cpp
639 ${CMAKE_CURRENT_SOURCE_DIR}/test_buffer_properties.cpp
640+ ${CMAKE_CURRENT_SOURCE_DIR}/test_surfaceless_egl_context.cpp
641 )
642
643 add_subdirectory(nested/)
644
645=== added file 'tests/unit-tests/graphics/test_surfaceless_egl_context.cpp'
646--- tests/unit-tests/graphics/test_surfaceless_egl_context.cpp 1970-01-01 00:00:00 +0000
647+++ tests/unit-tests/graphics/test_surfaceless_egl_context.cpp 2013-12-19 09:13:25 +0000
648@@ -0,0 +1,254 @@
649+/*
650+ * Copyright © 2013 Canonical Ltd.
651+ *
652+ * This program is free software: you can redistribute it and/or modify it
653+ * under the terms of the GNU General Public License version 3,
654+ * as published by the Free Software Foundation.
655+ *
656+ * This program is distributed in the hope that it will be useful,
657+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
658+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
659+ * GNU General Public License for more details.
660+ *
661+ * You should have received a copy of the GNU General Public License
662+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
663+ *
664+ * Authored by:
665+ * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
666+ */
667+
668+#include "mir/graphics/surfaceless_egl_context.h"
669+#include "mir_test_doubles/mock_egl.h"
670+#include <stdexcept>
671+#include <list>
672+
673+#include <gtest/gtest.h>
674+#include <gmock/gmock.h>
675+
676+namespace mg = mir::graphics;
677+namespace mtd = mir::test::doubles;
678+
679+class SurfacelessEGLContextTest : public ::testing::Test
680+{
681+public:
682+ EGLDisplay const fake_display{reinterpret_cast<EGLDisplay>(0xfffaaafa)};
683+ EGLSurface const fake_surface{reinterpret_cast<EGLSurface>(0xdeadbeef)};
684+ EGLContext const fake_context{reinterpret_cast<EGLContext>(0xfaafbaaf)};
685+
686+protected:
687+ virtual void SetUp()
688+ {
689+ using namespace testing;
690+
691+ ON_CALL(mock_egl, eglCreatePbufferSurface(_,_,_))
692+ .WillByDefault(Return(fake_surface));
693+ ON_CALL(mock_egl, eglCreateContext(_,_,_,_))
694+ .WillByDefault(Return(fake_context));
695+ ON_CALL(mock_egl, eglGetCurrentContext())
696+ .WillByDefault(Return(EGL_NO_CONTEXT));
697+ }
698+
699+ testing::NiceMock<mtd::MockEGL> mock_egl;
700+};
701+
702+namespace
703+{
704+
705+MATCHER(ConfigAttribContainsPBufferFlag, "")
706+{
707+ EGLint surface_type = 0;
708+ bool found_surface_type = false;
709+ std::list<std::string> pretty_surface;
710+
711+ for (int i = 0; arg[i] != EGL_NONE; ++i)
712+ {
713+ if (arg[i] == EGL_SURFACE_TYPE)
714+ {
715+ surface_type = arg[i+1];
716+ found_surface_type = true;
717+ }
718+ }
719+
720+ if (found_surface_type)
721+ {
722+ if (surface_type == EGL_DONT_CARE)
723+ {
724+ pretty_surface.push_back("EGL_DONT_CARE");
725+ }
726+ else
727+ {
728+ if (surface_type & EGL_MULTISAMPLE_RESOLVE_BOX_BIT)
729+ {
730+ pretty_surface.push_back("EGL_MULTISAMPLE_RESOLVE_BOX_BIT");
731+ }
732+ if (surface_type & EGL_PBUFFER_BIT)
733+ {
734+ pretty_surface.push_back("EGL_PBUFFER_BIT");
735+ }
736+ if (surface_type & EGL_PIXMAP_BIT)
737+ {
738+ pretty_surface.push_back("EGL_PIXMAP_BIT");
739+ }
740+ if (surface_type & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)
741+ {
742+ pretty_surface.push_back("EGL_SWAP_BEHAVIOR_PRESERVED_BIT");
743+ }
744+ if (surface_type & EGL_VG_ALPHA_FORMAT_PRE_BIT)
745+ {
746+ pretty_surface.push_back("EGL_VG_ALPHA_FORMAT_PRE_BIT");
747+ }
748+ if (surface_type & EGL_VG_COLORSPACE_LINEAR_BIT)
749+ {
750+ pretty_surface.push_back("EGL_VG_COLORSPACE_LINEAR_BIT");
751+ }
752+ if (surface_type & EGL_WINDOW_BIT)
753+ {
754+ pretty_surface.push_back("EGL_WINDOW_BIT");
755+ }
756+ }
757+ std::string pretty_result = pretty_surface.back();
758+ pretty_surface.pop_back();
759+ for (auto& pretty : pretty_surface)
760+ {
761+ pretty_result += " | " + pretty;
762+ }
763+ *result_listener << "surface type is "<< pretty_result;
764+ }
765+ else
766+ {
767+ *result_listener << "no surface type set";
768+ }
769+
770+ return found_surface_type &&
771+ (surface_type != EGL_DONT_CARE) &&
772+ (surface_type & EGL_PBUFFER_BIT);
773+}
774+
775+}
776+
777+TEST_F(SurfacelessEGLContextTest, UsesPBufferContainingAttribsListByDefault)
778+{
779+ using namespace testing;
780+
781+ ON_CALL(mock_egl, eglQueryString(_,_))
782+ .WillByDefault(Return(""));
783+
784+ EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_))
785+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
786+
787+ mg::SurfacelessEGLContext ctx_noattrib(fake_display, EGL_NO_CONTEXT);
788+}
789+
790+TEST_F(SurfacelessEGLContextTest, KeepsPBufferInAttribsList)
791+{
792+ using namespace testing;
793+
794+ const EGLint attribs_with_pbuffer[] =
795+ {
796+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT,
797+ EGL_NONE
798+ };
799+
800+ ON_CALL(mock_egl, eglQueryString(_,_))
801+ .WillByDefault(Return(""));
802+
803+ EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_))
804+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
805+
806+ mg::SurfacelessEGLContext ctx_attribs_with_pbuffer(fake_display, attribs_with_pbuffer, EGL_NO_CONTEXT);
807+}
808+
809+TEST_F(SurfacelessEGLContextTest, AddsPBufferToAttribList)
810+{
811+ using namespace testing;
812+
813+ const EGLint attribs_without_surface_type[] =
814+ {
815+ EGL_ALPHA_SIZE, 8,
816+ EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT,
817+ EGL_NONE
818+ };
819+
820+ ON_CALL(mock_egl, eglQueryString(_,_))
821+ .WillByDefault(Return(""));
822+
823+ EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_))
824+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
825+
826+ mg::SurfacelessEGLContext ctx_attribs_without_surface_type(fake_display, attribs_without_surface_type, EGL_NO_CONTEXT);
827+}
828+
829+TEST_F(SurfacelessEGLContextTest, AddsPBufferWhenNonPBufferSurfaceTypeRequested)
830+{
831+ using namespace testing;
832+
833+ const EGLint attribs_without_pbuffer[] =
834+ {
835+ EGL_SURFACE_TYPE, EGL_DONT_CARE,
836+ EGL_ALPHA_SIZE, 8,
837+ EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT,
838+ EGL_NONE
839+ };
840+
841+ ON_CALL(mock_egl, eglQueryString(_,_))
842+ .WillByDefault(Return(""));
843+
844+ EXPECT_CALL(mock_egl, eglChooseConfig(_, ConfigAttribContainsPBufferFlag(), _,_,_))
845+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
846+
847+ mg::SurfacelessEGLContext ctx_attribs_without_pbuffer(fake_display, attribs_without_pbuffer, EGL_NO_CONTEXT);
848+}
849+
850+TEST_F(SurfacelessEGLContextTest, DoesNotRequestPBufferWithNoAttrib)
851+{
852+ using namespace testing;
853+
854+ ON_CALL(mock_egl, eglQueryString(_,_))
855+ .WillByDefault(Return("EGL_KHR_surfaceless_context"));
856+
857+ EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_))
858+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
859+
860+ mg::SurfacelessEGLContext ctx_noattrib(fake_display, EGL_NO_CONTEXT);
861+}
862+
863+TEST_F(SurfacelessEGLContextTest, DoesNotAddPBufferToAttribList)
864+{
865+ using namespace testing;
866+
867+ const EGLint attribs_without_surface_type[] =
868+ {
869+ EGL_ALPHA_SIZE, 8,
870+ EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT,
871+ EGL_NONE
872+ };
873+
874+ ON_CALL(mock_egl, eglQueryString(_,_))
875+ .WillByDefault(Return("EGL_KHR_surfaceless_context"));
876+
877+ EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_))
878+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
879+
880+ mg::SurfacelessEGLContext ctx_attribs_without_surface_type(fake_display, attribs_without_surface_type, EGL_NO_CONTEXT);
881+}
882+
883+TEST_F(SurfacelessEGLContextTest, DoesNotAddPBufferToSurfaceTypeRequest)
884+{
885+ using namespace testing;
886+
887+ const EGLint attribs_with_surface_type[] =
888+ {
889+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
890+ EGL_ALPHA_SIZE, 8,
891+ EGL_CLIENT_APIS, EGL_OPENGL_ES2_BIT,
892+ EGL_NONE
893+ };
894+
895+ ON_CALL(mock_egl, eglQueryString(_,_))
896+ .WillByDefault(Return("EGL_KHR_surfaceless_context"));
897+
898+ EXPECT_CALL(mock_egl, eglChooseConfig(_, Not(ConfigAttribContainsPBufferFlag()), _,_,_))
899+ .WillOnce(DoAll(SetArgPointee<4>(1), Return(EGL_TRUE)));
900+
901+ mg::SurfacelessEGLContext ctx_attribs_with_surface_type(fake_display, attribs_with_surface_type, EGL_NO_CONTEXT);
902+}
903
904=== modified file 'tests/unit-tests/scene/test_gl_pixel_buffer.cpp'
905--- tests/unit-tests/scene/test_gl_pixel_buffer.cpp 2013-12-17 18:24:51 +0000
906+++ tests/unit-tests/scene/test_gl_pixel_buffer.cpp 2013-12-19 09:13:25 +0000
907@@ -38,8 +38,8 @@
908 struct MockGLContext : public mg::GLContext
909 {
910 ~MockGLContext() noexcept {}
911- MOCK_METHOD0(make_current, void());
912- MOCK_METHOD0(release_current, void());
913+ MOCK_CONST_METHOD0(make_current, void());
914+ MOCK_CONST_METHOD0(release_current, void());
915 };
916
917 struct WrappingGLContext : public mg::GLContext
918@@ -49,8 +49,8 @@
919 {
920 }
921 ~WrappingGLContext() noexcept {}
922- void make_current() { wrapped.make_current(); }
923- void release_current() { wrapped.release_current(); }
924+ void make_current() const { wrapped.make_current(); }
925+ void release_current() const { wrapped.release_current(); }
926
927 mg::GLContext& wrapped;
928 };

Subscribers

People subscribed via source and target branches