Mir

Merge lp:~kdub/mir/gl-program-creation-to-common-place into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Kevin DuBois
Approved revision: no longer in the source branch.
Merged at revision: 1559
Proposed branch: lp:~kdub/mir/gl-program-creation-to-common-place
Merge into: lp:mir
Diff against target: 447 lines (+209/-106)
8 files modified
include/server/mir/compositor/gl_program.h (+65/-0)
include/server/mir/compositor/gl_renderer.h (+2/-3)
src/server/compositor/CMakeLists.txt (+1/-0)
src/server/compositor/gl_program.cpp (+109/-0)
src/server/compositor/gl_renderer.cpp (+11/-94)
src/server/compositor/gl_renderer_factory.cpp (+2/-0)
src/server/compositor/gl_renderer_factory.h (+10/-0)
tests/unit-tests/compositor/test_gl_renderer.cpp (+9/-9)
To merge this branch: bzr merge lp:~kdub/mir/gl-program-creation-to-common-place
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) Approve
Robert Carr (community) Approve
Alberto Aguirre (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+214609@code.launchpad.net

Commit message

graphics: move gl program creation code to a common place. Ensure gl program creation is serialized in the GLRenderer factory (as opposed to the constructor of the GLRenderer). Address some exception safety issues in making gl programs/shaders.

Description of the change

graphics: move gl program creation code to a common place. Ensure gl program creation is serialized in the GLRenderer factory (as opposed to the constructor of the GLRenderer). Address some exception safety issues in making gl programs/shaders.

I intend to have a non-overridable gl program to handle the android surface fallback. The GLRenderer and the android fallback renderer are different enough that two programs are warranted (one can do arbitrary transforms, can inject different behaviors like drop shadows, etc the other just should be the bare minimum shader, maybe with some helpful debug features)

I put this common code in include/server/mir/compositor as GLRenderer is exposed publicly, and that class needs the definitions. I hope to avoid having libmirplatformgraphics.so link to OpenGL. The exception safety issue was that we would leak GL resources if we threw at lines [285,297,310]. These are fatal errors anyways as the code currently stands.

To post a comment you must log in.
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: Approve (continuous-integration)
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

~~~
+mc::GLShader::operator GLuint() const
184+{
185+ return shader;
186+}

215+mc::GLProgram::operator GLuint() const
216+{
217+ return program;
218+}
~~~

Sneaky :)

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

380 + /*
381 + * We need to serialize renderer creation because some GL calls used
382 + * during renderer construction that create unique resource ids
383 + * (e.g. glCreateProgram) are not thread-safe when the threads are
384 + * have the same or shared EGL contexts.
385 + */
386 + std::mutex mutex;

By moving the mutex to the renderer factory, we protect renderer creation from races. However, since we wan to use the GLProgram/Shader classes independently, what's the plan for dealing with races there (and between the independent cases and renderer creation cases)? Perhaps we should move the mutex inside the the GL* classes?

review: Needs Information
Revision history for this message
Robert Carr (robertcarr) wrote :

I like this. I was thinking about this in the Zoom branch when daniel references attempting to do it in the renderer and encountering issues...clearly the renderer could be split in to multiple useful useful primitives and this is one of them.

Not my most expert area of code so not sure if this is a great suggestion but...

Maybe test_gl_renderer should now be testing interactions with GLProgram and test_gl_program should come in to existence and test against the mock_gl. On one hand its frustrating because you need an interface whereas its hard to imagine production alternative implementations of GLProgram...(though actually now that I think about it, could perhaps come in to play for Desktop GL v. GLES). On the other hand, I think it could make test_gl_renderer more clear. You know this code and tests better so I will let you decide!

review: Needs Information
Revision history for this message
Kevin DuBois (kdub) wrote :

> By moving the mutex to the renderer factory, we protect renderer creation from
> races. However, since we wan to use the GLProgram/Shader classes
> independently, what's the plan for dealing with races there (and between the
> independent cases and renderer creation cases)? Perhaps we should move the
> mutex inside the the GL* classes?

Still have to poke around different options to sort out which one feels the best. My instinct says there's some resources (RAII) that we have to tease out of the implementation, so different gl programs can use them cooperatively.

Revision history for this message
Kevin DuBois (kdub) wrote :

> I like this. I was thinking about this in the Zoom branch when daniel
> references attempting to do it in the renderer and encountering
> issues...clearly the renderer could be split in to multiple useful useful
> primitives and this is one of them.
>
> Not my most expert area of code so not sure if this is a great suggestion
> but...
>
> Maybe test_gl_renderer should now be testing interactions with GLProgram and
> test_gl_program should come in to existence and test against the mock_gl. On
> one hand its frustrating because you need an interface whereas its hard to
> imagine production alternative implementations of GLProgram...(though actually
> now that I think about it, could perhaps come in to play for Desktop GL v.
> GLES). On the other hand, I think it could make test_gl_renderer more clear.
> You know this code and tests better so I will let you decide!

The GLProgram/GLshader code is still under test in GLRenderer.*, although the test is one of the oldest and could use an overhaul. It'll probably be necessary to do that at some point, but I judged it wasn't necessary right now.

Revision history for this message
Robert Carr (robertcarr) wrote :

>> The GLProgram/GLshader code is still under test in GLRenderer.*, although the test is one of the oldest and
>> could use an overhaul. It'll probably be necessary to do that at some point, but I judged it wasn't
>> necessary right now.

Sounds good!

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

OK.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'include/server/mir/compositor/gl_program.h'
2--- include/server/mir/compositor/gl_program.h 1970-01-01 00:00:00 +0000
3+++ include/server/mir/compositor/gl_program.h 2014-04-07 23:06:24 +0000
4@@ -0,0 +1,65 @@
5+/*
6+ * Copyright © 2013-2014 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU General Public License version 3,
10+ * as published by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
21+ */
22+
23+#ifndef MIR_COMPOSITOR_GL_PROGRAM_H_
24+#define MIR_COMPOSITOR_GL_PROGRAM_H_
25+
26+#include <GLES2/gl2.h>
27+
28+namespace mir
29+{
30+namespace compositor
31+{
32+
33+class GLShader
34+{
35+public:
36+ GLShader(GLchar const* shader_src, GLuint type);
37+ ~GLShader();
38+ operator GLuint() const;
39+
40+private:
41+ GLShader(GLShader const&) = delete;
42+ GLShader& operator=(GLShader const&) = delete;
43+
44+ GLuint const shader;
45+};
46+
47+class GLProgram
48+{
49+public:
50+ GLProgram(
51+ GLchar const* vertex_shader_src,
52+ GLchar const* fragment_shader_src);
53+ ~GLProgram();
54+
55+ operator GLuint() const;
56+
57+private:
58+ GLProgram(GLProgram const&) = delete;
59+ GLProgram& operator=(GLProgram const&) = delete;
60+
61+ GLShader const vertex_shader;
62+ GLShader const fragment_shader;
63+ GLuint const program;
64+};
65+
66+}
67+}
68+
69+#endif /* MIR_COMPOSITOR_GL_PROGRAM_H_ */
70
71=== modified file 'include/server/mir/compositor/gl_renderer.h'
72--- include/server/mir/compositor/gl_renderer.h 2014-04-03 07:16:36 +0000
73+++ include/server/mir/compositor/gl_renderer.h 2014-04-07 23:06:24 +0000
74@@ -20,6 +20,7 @@
75 #define MIR_COMPOSITOR_GL_RENDERER_H_
76
77 #include <mir/compositor/renderer.h>
78+#include <mir/compositor/gl_program.h>
79 #include <mir/geometry/rectangle.h>
80 #include <mir/graphics/buffer_id.h>
81 #include <mir/graphics/renderable.h>
82@@ -97,9 +98,7 @@
83 graphics::Buffer& buffer) const;
84
85 private:
86- GLuint vertex_shader;
87- GLuint fragment_shader;
88- GLuint program;
89+ GLProgram program;
90 GLuint position_attr_loc;
91 GLuint texcoord_attr_loc;
92 GLuint centre_uniform_loc;
93
94=== modified file 'src/server/compositor/CMakeLists.txt'
95--- src/server/compositor/CMakeLists.txt 2014-04-02 22:23:14 +0000
96+++ src/server/compositor/CMakeLists.txt 2014-04-07 23:06:24 +0000
97@@ -6,6 +6,7 @@
98 temporary_buffers.cpp
99 buffer_stream_factory.cpp
100 buffer_stream_surfaces.cpp
101+ gl_program.cpp
102 gl_renderer.cpp
103 gl_renderer_factory.cpp
104 multi_threaded_compositor.cpp
105
106=== added file 'src/server/compositor/gl_program.cpp'
107--- src/server/compositor/gl_program.cpp 1970-01-01 00:00:00 +0000
108+++ src/server/compositor/gl_program.cpp 2014-04-07 23:06:24 +0000
109@@ -0,0 +1,109 @@
110+/*
111+ * Copyright © 2013-2014 Canonical Ltd.
112+ *
113+ * This program is free software: you can redistribute it and/or modify it
114+ * under the terms of the GNU General Public License version 3,
115+ * as published by the Free Software Foundation.
116+ *
117+ * This program is distributed in the hope that it will be useful,
118+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
119+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
120+ * GNU General Public License for more details.
121+ *
122+ * You should have received a copy of the GNU General Public License
123+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
124+ *
125+ * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
126+ */
127+
128+#include "mir/compositor/gl_program.h"
129+#include <boost/throw_exception.hpp>
130+#include <stdexcept>
131+
132+namespace mc = mir::compositor;
133+
134+namespace
135+{
136+typedef void(*MirGLGetObjectInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
137+typedef void(*MirGLGetObjectiv)(GLuint, GLenum, GLint *);
138+
139+void GetObjectLogAndThrow(MirGLGetObjectInfoLog getObjectInfoLog,
140+ MirGLGetObjectiv getObjectiv,
141+ std::string const & msg,
142+ GLuint object)
143+{
144+ GLint object_log_length = 0;
145+ (*getObjectiv)(object, GL_INFO_LOG_LENGTH, &object_log_length);
146+
147+ const GLuint object_log_buffer_length = object_log_length + 1;
148+ std::string object_info_log;
149+
150+ object_info_log.resize(object_log_buffer_length);
151+ (*getObjectInfoLog)(object, object_log_length, NULL,
152+ const_cast<GLchar *>(object_info_log.data()));
153+
154+ std::string object_info_err(msg + "\n");
155+ object_info_err += object_info_log;
156+
157+ BOOST_THROW_EXCEPTION(std::runtime_error(object_info_err));
158+}
159+}
160+
161+mc::GLShader::GLShader(GLchar const* shader_src, GLuint type)
162+ : shader(glCreateShader(type))
163+{
164+ GLint param{0};
165+ glShaderSource(shader, 1, &shader_src, 0);
166+ glCompileShader(shader);
167+ glGetShaderiv(shader, GL_COMPILE_STATUS, &param);
168+ if (param == GL_FALSE)
169+ {
170+ glDeleteShader(shader);
171+ GetObjectLogAndThrow(glGetShaderInfoLog,
172+ glGetShaderiv,
173+ "Failed to compile shader:",
174+ shader);
175+ }
176+}
177+
178+mc::GLShader::~GLShader()
179+{
180+ glDeleteShader(shader);
181+}
182+
183+mc::GLShader::operator GLuint() const
184+{
185+ return shader;
186+}
187+
188+mc::GLProgram::GLProgram(
189+ GLchar const* vertex_shader_src,
190+ GLchar const* fragment_shader_src)
191+ : vertex_shader(vertex_shader_src, GL_VERTEX_SHADER),
192+ fragment_shader(fragment_shader_src, GL_FRAGMENT_SHADER),
193+ program(glCreateProgram())
194+{
195+ glAttachShader(program, vertex_shader);
196+ glAttachShader(program, fragment_shader);
197+ glLinkProgram(program);
198+ GLint param = 0;
199+ glGetProgramiv(program, GL_LINK_STATUS, &param);
200+ if (param == GL_FALSE)
201+ {
202+ glDeleteProgram(program);
203+ GetObjectLogAndThrow(glGetProgramInfoLog,
204+ glGetProgramiv,
205+ "Failed to link program:",
206+ program);
207+ }
208+}
209+
210+mc::GLProgram::~GLProgram()
211+{
212+ glDeleteProgram(program);
213+}
214+
215+mc::GLProgram::operator GLuint() const
216+{
217+ return program;
218+}
219
220=== modified file 'src/server/compositor/gl_renderer.cpp'
221--- src/server/compositor/gl_renderer.cpp 2014-04-03 07:16:36 +0000
222+++ src/server/compositor/gl_renderer.cpp 2014-04-07 23:06:24 +0000
223@@ -27,7 +27,6 @@
224 #include <boost/throw_exception.hpp>
225 #include <stdexcept>
226 #include <cmath>
227-#include <mutex>
228
229 namespace mg = mir::graphics;
230 namespace mc = mir::compositor;
231@@ -64,93 +63,17 @@
232 " gl_FragColor = vec4(frag.xyz, frag.a * alpha);\n"
233 "}\n"
234 };
235-
236-typedef void(*MirGLGetObjectInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
237-typedef void(*MirGLGetObjectiv)(GLuint, GLenum, GLint *);
238-
239-void GetObjectLogAndThrow(MirGLGetObjectInfoLog getObjectInfoLog,
240- MirGLGetObjectiv getObjectiv,
241- std::string const & msg,
242- GLuint object)
243-{
244- GLint object_log_length = 0;
245- (*getObjectiv)(object, GL_INFO_LOG_LENGTH, &object_log_length);
246-
247- const GLuint object_log_buffer_length = object_log_length + 1;
248- std::string object_info_log;
249-
250- object_info_log.resize(object_log_buffer_length);
251- (*getObjectInfoLog)(object, object_log_length, NULL,
252- const_cast<GLchar *>(object_info_log.data()));
253-
254- std::string object_info_err(msg + "\n");
255- object_info_err += object_info_log;
256-
257- BOOST_THROW_EXCEPTION(std::runtime_error(object_info_err));
258-}
259-
260-}
261-
262-mc::GLRenderer::GLRenderer(geom::Rectangle const& display_area) :
263- vertex_shader(0),
264- fragment_shader(0),
265- program(0),
266- position_attr_loc(0),
267- texcoord_attr_loc(0),
268- centre_uniform_loc(0),
269- transform_uniform_loc(0),
270- alpha_uniform_loc(0),
271- rotation(NAN) // ensure the first set_rotation succeeds
272-{
273- /*
274- * We need to serialize renderer creation because some GL calls used
275- * during renderer construction that create unique resource ids
276- * (e.g. glCreateProgram) are not thread-safe when the threads are
277- * have the same or shared EGL contexts.
278- */
279- static std::mutex mutex;
280- std::lock_guard<std::mutex> lock(mutex);
281-
282- GLint param = 0;
283-
284- /* Create shaders and program */
285- vertex_shader = glCreateShader(GL_VERTEX_SHADER);
286- glShaderSource(vertex_shader, 1, &vertex_shader_src, 0);
287- glCompileShader(vertex_shader);
288- glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &param);
289- if (param == GL_FALSE)
290- {
291- GetObjectLogAndThrow(glGetShaderInfoLog,
292- glGetShaderiv,
293- "Failed to compile vertex shader:",
294- vertex_shader);
295- }
296-
297- fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
298- glShaderSource(fragment_shader, 1, &fragment_shader_src, 0);
299- glCompileShader(fragment_shader);
300- glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &param);
301- if (param == GL_FALSE)
302- {
303- GetObjectLogAndThrow(glGetShaderInfoLog,
304- glGetShaderiv,
305- "Failed to compile fragment shader:",
306- fragment_shader);
307- }
308-
309- program = glCreateProgram();
310- glAttachShader(program, vertex_shader);
311- glAttachShader(program, fragment_shader);
312- glLinkProgram(program);
313- glGetProgramiv(program, GL_LINK_STATUS, &param);
314- if (param == GL_FALSE)
315- {
316- GetObjectLogAndThrow(glGetProgramInfoLog,
317- glGetProgramiv,
318- "Failed to link program:",
319- program);
320- }
321-
322+}
323+
324+mc::GLRenderer::GLRenderer(geom::Rectangle const& display_area)
325+ : program(vertex_shader_src, fragment_shader_src),
326+ position_attr_loc(0),
327+ texcoord_attr_loc(0),
328+ centre_uniform_loc(0),
329+ transform_uniform_loc(0),
330+ alpha_uniform_loc(0),
331+ rotation(NAN) // ensure the first set_rotation succeeds
332+{
333 glUseProgram(program);
334
335 /* Set up program variables */
336@@ -173,12 +96,6 @@
337
338 mc::GLRenderer::~GLRenderer() noexcept
339 {
340- if (vertex_shader)
341- glDeleteShader(vertex_shader);
342- if (fragment_shader)
343- glDeleteShader(fragment_shader);
344- if (program)
345- glDeleteProgram(program);
346 for (auto& t : textures)
347 glDeleteTextures(1, &t.second.id);
348 }
349
350=== modified file 'src/server/compositor/gl_renderer_factory.cpp'
351--- src/server/compositor/gl_renderer_factory.cpp 2014-03-06 06:05:17 +0000
352+++ src/server/compositor/gl_renderer_factory.cpp 2014-04-07 23:06:24 +0000
353@@ -26,6 +26,8 @@
354 std::unique_ptr<mc::Renderer>
355 mc::GLRendererFactory::create_renderer_for(geom::Rectangle const& rect)
356 {
357+ std::lock_guard<std::mutex> lock(mutex);
358+
359 auto raw = new GLRenderer(rect);
360 return std::unique_ptr<mc::Renderer>(raw);
361 }
362
363=== modified file 'src/server/compositor/gl_renderer_factory.h'
364--- src/server/compositor/gl_renderer_factory.h 2014-03-06 06:05:17 +0000
365+++ src/server/compositor/gl_renderer_factory.h 2014-04-07 23:06:24 +0000
366@@ -20,6 +20,7 @@
367 #define MIR_COMPOSITOR_GL_RENDERER_FACTORY_H_
368
369 #include "mir/compositor/renderer_factory.h"
370+#include <mutex>
371
372 namespace mir
373 {
374@@ -30,6 +31,15 @@
375 {
376 public:
377 std::unique_ptr<Renderer> create_renderer_for(geometry::Rectangle const& rect);
378+
379+private:
380+ /*
381+ * We need to serialize renderer creation because some GL calls used
382+ * during renderer construction that create unique resource ids
383+ * (e.g. glCreateProgram) are not thread-safe when the threads are
384+ * have the same or shared EGL contexts.
385+ */
386+ std::mutex mutex;
387 };
388
389 }
390
391=== modified file 'tests/unit-tests/compositor/test_gl_renderer.cpp'
392--- tests/unit-tests/compositor/test_gl_renderer.cpp 2014-03-28 20:12:32 +0000
393+++ tests/unit-tests/compositor/test_gl_renderer.cpp 2014-04-07 23:06:24 +0000
394@@ -272,20 +272,20 @@
395 EXPECT_CALL(mock_gl, glGetUniformLocation(stub_program, _))
396 .WillOnce(Return(screen_to_gl_coords_uniform_location));
397
398- mc::GLRendererFactory gl_renderer_factory;
399 display_area = {{1, 2}, {3, 4}};
400- renderer = gl_renderer_factory.create_renderer_for(display_area);
401
402+ EXPECT_CALL(mock_gl, glDeleteProgram(stub_program));
403+ EXPECT_CALL(mock_gl, glDeleteShader(stub_f_shader));
404 EXPECT_CALL(mock_gl, glDeleteShader(stub_v_shader));
405- EXPECT_CALL(mock_gl, glDeleteShader(stub_f_shader));
406- EXPECT_CALL(mock_gl, glDeleteProgram(stub_program));
407 }
408
409- mtd::MockGL mock_gl;
410+ testing::NiceMock<mtd::MockGL> mock_gl;
411+ testing::NiceMock<mtd::MockBuffer> mock_buffer;
412 mir::geometry::Rectangle display_area;
413 std::unique_ptr<mc::Renderer> renderer;
414 glm::mat4 trans;
415- mtd::MockRenderable renderable;
416+ testing::NiceMock<mtd::MockRenderable> renderable;
417+ mc::GLRendererFactory gl_renderer_factory;
418 };
419
420 }
421@@ -294,7 +294,7 @@
422 {
423 using namespace std::placeholders;
424
425- mtd::MockBuffer mock_buffer;
426+ auto renderer = gl_renderer_factory.create_renderer_for(display_area);
427
428 InSequence seq;
429
430@@ -357,7 +357,7 @@
431
432 TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces)
433 {
434- mtd::MockBuffer mock_buffer;
435+ auto renderer = gl_renderer_factory.create_renderer_for(display_area);
436
437 InSequence seq;
438 EXPECT_CALL(renderable, shaped())
439@@ -392,7 +392,7 @@
440
441 TEST_F(GLRenderer, caches_and_uploads_texture_only_on_buffer_changes)
442 {
443- mtd::MockBuffer mock_buffer;
444+ auto renderer = gl_renderer_factory.create_renderer_for(display_area);
445
446 EXPECT_CALL(mock_buffer, size())
447 .WillRepeatedly(Return(mir::geometry::Size{123, 456}));

Subscribers

People subscribed via source and target branches