Mir

Merge lp:~kdub/mir/gl-program-creation-factory into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Kevin DuBois
Approved revision: no longer in the source branch.
Merged at revision: 1569
Proposed branch: lp:~kdub/mir/gl-program-creation-factory
Merge into: lp:mir
Prerequisite: lp:~kdub/mir/gl-program-creation-to-common-place
Diff against target: 1061 lines (+439/-216)
20 files modified
examples/demo-shell/demo_renderer.cpp (+4/-2)
examples/demo-shell/demo_renderer.h (+1/-1)
examples/demo-shell/demo_shell.cpp (+9/-2)
include/platform/mir/graphics/gl_program_factory.h (+50/-0)
include/server/mir/compositor/gl_renderer.h (+6/-3)
include/server/mir/default_server_configuration.h (+3/-0)
include/server/mir/graphics/gl_program.h (+4/-4)
src/server/compositor/CMakeLists.txt (+0/-1)
src/server/compositor/default_configuration.cpp (+2/-2)
src/server/compositor/gl_renderer.cpp (+16/-14)
src/server/compositor/gl_renderer_factory.cpp (+9/-4)
src/server/compositor/gl_renderer_factory.h (+6/-10)
src/server/graphics/CMakeLists.txt (+2/-0)
src/server/graphics/default_configuration.cpp (+11/-0)
src/server/graphics/gl_program.cpp (+8/-8)
src/server/graphics/program_factory.cpp (+32/-0)
src/server/graphics/program_factory.h (+47/-0)
tests/unit-tests/compositor/test_gl_renderer.cpp (+17/-165)
tests/unit-tests/graphics/CMakeLists.txt (+1/-0)
tests/unit-tests/graphics/test_program_factory.cpp (+211/-0)
To merge this branch: bzr merge lp:~kdub/mir/gl-program-creation-factory
Reviewer Review Type Date Requested Status
Alberto Aguirre (community) Approve
Alexandros Frantzis (community) Approve
Robert Carr (community) Needs Information
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+216218@code.launchpad.net

Commit message

Add a factory that is capable of compiling a vertex shader and a fragment shader into a gl program.

I am doing this so that the android platform can create a gl program that it can use when the overlays fail. I don't intend to expose this program in the platform-independent code, that will continue to use GLRenderer.

A few things were weighed (do we /really/ need another factory?)...

1) calls relating to gl program/shader creation should be serialized which points to a common factory.
2) the GLRendererFactory is a class that is overridable by the user. (see the demo-shell). I didn't want to extend the GLRendererFactory to have a method for creating a separate android-only specific renderer. It seemed better to have a generic factory for making any gl program, and have GLRendererFactory and GLRenderer use that generic factory to accomplish its goals

Description of the change

Add a factory that is capable of compiling a vertex shader and a fragment shader into a gl program.

I am doing this so that the android platform can create a gl program that it can use when the overlays fail. I don't intend to expose this program in the platform-independent code, that will continue to use GLRenderer.

A few things were weighed (do we /really/ need another factory?)...

1) calls relating to gl program/shader creation should be serialized which points to a common factory.
2) the GLRendererFactory is a class that is overridable by the user. (see the demo-shell). I didn't want to extend the GLRendererFactory to have a method for creating a separate android-only specific renderer. It seemed better to have a generic factory for making any gl program, and have GLRendererFactory and GLRenderer use that generic factory to accomplish its goals

note 1:
I also made an attempt to split out testing the gl program setup from the GLRenderer use cases. This one of the oldest tests (and... a mess by 'modern' mir test standards), so it has some cruft that could still use some attention. We'll probably have to continue to improve this test in smaller steps in subsequent MPs.

note 2:
If HWC rejects a surface, we are expected to draw that surface with OpenGLES. We should be doing this in the android platform, so the platform-independent code doesn't have to deal with this.

note 3: (future planning)
I plan on giving the GLProgramFactory down to the android platform so it can make its own HWC gl program.

To post a comment you must log in.
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
Robert Carr (robertcarr) wrote :

>> 557 + /*
>> 558 + * We need to serialize renderer creation because some GL calls used
>> 559 + * during renderer construction that create unique resource ids
>> 560 + * (e.g. glCreateProgram) are not thread-safe when the threads are
>> 561 + * have the same or shared EGL contexts.
>> 562 + */

Can you explain the synchronization requirements a little here? I got a little confused...it seems like this isn't sufficient.

If this can be called from multiple threads then something has to ensure the eglMakeCurrent is called from the right threads (and held for the duration of GL usage), right? So if something is doing this then the lock guard is not necessary, if nothing else is handling it then its not quite enough....

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

> >> 557 + /*
> >> 558 + * We need to serialize renderer creation because some GL calls used
> >> 559 + * during renderer construction that create unique resource ids
> >> 560 + * (e.g. glCreateProgram) are not thread-safe when the threads are
> >> 561 + * have the same or shared EGL contexts.
> >> 562 + */
>
> Can you explain the synchronization requirements a little here? I got a little
> confused...it seems like this isn't sufficient.
>
> If this can be called from multiple threads then something has to ensure the
> eglMakeCurrent is called from the right threads (and held for the duration of
> GL usage), right? So if something is doing this then the lock guard is not
> necessary, if nothing else is handling it then its not quite enough....

I would agree that our handling of the context doesn't really model the requirements of using gl contexts and could be improved. (but I'm not looking to improve that in this MP) I'm trying to preserve the guards that are in place so android can make a shader program in addition to the default compositor.

I suppose this guard is fulfilling:
http://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateShader.xml
"Applications are responsible for providing the synchronization across API calls when objects are accessed from different execution threads."

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

> If this can be called from multiple threads then something has to ensure the eglMakeCurrent
> is called from the right threads (and held for the duration of GL usage), right? So if
> something is doing this then the lock guard is not necessary, if nothing else is handling
> it then its not quite enough....

Not sure I follow, but let me try to explain the reason we need this. If two GL contexts are shared then objects in them live in the same "namespace". Creation of objects in this shared namespace is not thread-safe. So, imagine:

C1: EGL context 1
C2: EGL context 2 shared with context 1

T1: MakeCurrent(C1)
T2: MakeCurrent(C2)

Now if T1 and T2 run concurrently and call glCreateProgram() we have a potential race. What happened in our case was that both T1:glCreateProgram() and T2:glCreateProgram() occasionally returned the same program id, messing things up.

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

100 + GLProgramFactory() {};

= default;

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

> 100 + GLProgramFactory() {};
> = default;

Looks good otherwise, so pre-approving to unblock.

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

> > 100 + GLProgramFactory() {};
> > = default;
>
> Looks good otherwise, so pre-approving to unblock.

thanks! working on change now

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

489 + * Copyright © 2013 Canonical Ltd.
525 + * Copyright © 2012 Canonical Ltd.

Wrong year?

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

> 489 + * Copyright © 2013 Canonical Ltd.
> 525 + * Copyright © 2012 Canonical Ltd.
>
> Wrong year?

Indeed, fixed!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/demo-shell/demo_renderer.cpp'
2--- examples/demo-shell/demo_renderer.cpp 2014-04-15 05:31:19 +0000
3+++ examples/demo-shell/demo_renderer.cpp 2014-04-22 16:51:34 +0000
4@@ -134,8 +134,10 @@
5
6 } // namespace
7
8-DemoRenderer::DemoRenderer(geometry::Rectangle const& display_area)
9- : GLRenderer(display_area)
10+DemoRenderer::DemoRenderer(
11+ graphics::GLProgramFactory const& program_factory,
12+ geometry::Rectangle const& display_area)
13+ : GLRenderer(program_factory, display_area)
14 , corner_radius(0.5f)
15 {
16 shadow_corner_tex = generate_shadow_corner_texture(0.4f);
17
18=== modified file 'examples/demo-shell/demo_renderer.h'
19--- examples/demo-shell/demo_renderer.h 2014-04-15 05:31:19 +0000
20+++ examples/demo-shell/demo_renderer.h 2014-04-22 16:51:34 +0000
21@@ -29,7 +29,7 @@
22 class DemoRenderer : public compositor::GLRenderer
23 {
24 public:
25- DemoRenderer(geometry::Rectangle const& display_area);
26+ DemoRenderer(graphics::GLProgramFactory const& factory, geometry::Rectangle const& display_area);
27 ~DemoRenderer();
28
29 void begin() const override;
30
31=== modified file 'examples/demo-shell/demo_shell.cpp'
32--- examples/demo-shell/demo_shell.cpp 2014-04-15 05:31:19 +0000
33+++ examples/demo-shell/demo_shell.cpp 2014-04-22 16:51:34 +0000
34@@ -47,11 +47,18 @@
35 class DemoRendererFactory : public compositor::RendererFactory
36 {
37 public:
38+ DemoRendererFactory(std::shared_ptr<graphics::GLProgramFactory> const& gl_program_factory) :
39+ gl_program_factory(gl_program_factory)
40+ {
41+ }
42+
43 std::unique_ptr<compositor::Renderer> create_renderer_for(
44 geometry::Rectangle const& rect) override
45 {
46- return std::unique_ptr<compositor::Renderer>(new DemoRenderer(rect));
47+ return std::unique_ptr<compositor::Renderer>(new DemoRenderer(*gl_program_factory, rect));
48 }
49+private:
50+ std::shared_ptr<graphics::GLProgramFactory> const gl_program_factory;
51 };
52
53 class DemoServerConfiguration : public mir::examples::ServerConfiguration
54@@ -97,7 +104,7 @@
55
56 std::shared_ptr<compositor::RendererFactory> the_renderer_factory() override
57 {
58- return std::make_shared<DemoRendererFactory>();
59+ return std::make_shared<DemoRendererFactory>(the_gl_program_factory());
60 }
61
62 private:
63
64=== added file 'include/platform/mir/graphics/gl_program_factory.h'
65--- include/platform/mir/graphics/gl_program_factory.h 1970-01-01 00:00:00 +0000
66+++ include/platform/mir/graphics/gl_program_factory.h 2014-04-22 16:51:34 +0000
67@@ -0,0 +1,50 @@
68+/*
69+ * Copyright © 2014 Canonical Ltd.
70+ *
71+ * This program is free software: you can redistribute it and/or modify it
72+ * under the terms of the GNU Lesser General Public License version 3,
73+ * as published by the Free Software Foundation.
74+ *
75+ * This program is distributed in the hope that it will be useful,
76+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
77+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
78+ * GNU Lesser General Public License for more details.
79+ *
80+ * You should have received a copy of the GNU Lesser General Public License
81+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
82+ *
83+ * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
84+ */
85+
86+#ifndef MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_
87+#define MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_
88+
89+#include "mir/graphics/gl_program.h"
90+#include <memory>
91+
92+namespace mir
93+{
94+namespace graphics
95+{
96+
97+class GLProgramFactory
98+{
99+public:
100+ virtual ~GLProgramFactory() = default;
101+
102+ virtual std::unique_ptr<GLProgram>
103+ create_gl_program(std::string const&, std::string const&) const = 0;
104+
105+protected:
106+ GLProgramFactory() = default;
107+
108+private:
109+ GLProgramFactory(GLProgramFactory const&) = delete;
110+ GLProgramFactory& operator=(GLProgramFactory const&) = delete;
111+
112+};
113+
114+}
115+}
116+
117+#endif /* MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_ */
118
119=== modified file 'include/server/mir/compositor/gl_renderer.h'
120--- include/server/mir/compositor/gl_renderer.h 2014-04-17 03:56:51 +0000
121+++ include/server/mir/compositor/gl_renderer.h 2014-04-22 16:51:34 +0000
122@@ -20,10 +20,11 @@
123 #define MIR_COMPOSITOR_GL_RENDERER_H_
124
125 #include <mir/compositor/renderer.h>
126-#include <mir/compositor/gl_program.h>
127+#include <mir/graphics/gl_program.h>
128 #include <mir/geometry/rectangle.h>
129 #include <mir/graphics/buffer_id.h>
130 #include <mir/graphics/renderable.h>
131+#include <mir/graphics/gl_program_factory.h>
132 #include <GLES2/gl2.h>
133 #include <unordered_map>
134 #include <unordered_set>
135@@ -37,7 +38,9 @@
136 class GLRenderer : public Renderer
137 {
138 public:
139- GLRenderer(geometry::Rectangle const& display_area);
140+ GLRenderer(
141+ graphics::GLProgramFactory const& program_factory,
142+ geometry::Rectangle const& display_area);
143 virtual ~GLRenderer() noexcept;
144
145 // These are called with a valid GL context:
146@@ -98,7 +101,7 @@
147 graphics::Buffer& buffer) const;
148
149 private:
150- GLProgram program;
151+ std::unique_ptr<graphics::GLProgram> program;
152 GLuint position_attr_loc;
153 GLuint texcoord_attr_loc;
154 GLuint centre_uniform_loc;
155
156=== modified file 'include/server/mir/default_server_configuration.h'
157--- include/server/mir/default_server_configuration.h 2014-04-15 05:31:19 +0000
158+++ include/server/mir/default_server_configuration.h 2014-04-22 16:51:34 +0000
159@@ -92,6 +92,7 @@
160 class DisplayReport;
161 class GraphicBufferAllocator;
162 class GLConfig;
163+class GLProgramFactory;
164 namespace nested { class HostConnection; }
165 }
166 namespace input
167@@ -247,6 +248,7 @@
168 protected:
169 std::shared_ptr<options::Option> the_options() const;
170
171+ virtual std::shared_ptr<graphics::GLProgramFactory> the_gl_program_factory();
172 virtual std::shared_ptr<input::InputChannelFactory> the_input_channel_factory();
173 virtual std::shared_ptr<scene::MediatingDisplayChanger> the_mediating_display_changer();
174 virtual std::shared_ptr<frontend::ProtobufIpcFactory> the_ipc_factory(
175@@ -303,6 +305,7 @@
176 CachedPtr<graphics::DisplayConfigurationPolicy> display_configuration_policy;
177 CachedPtr<graphics::nested::HostConnection> host_connection;
178 CachedPtr<scene::MediatingDisplayChanger> mediating_display_changer;
179+ CachedPtr<graphics::GLProgramFactory> gl_program_factory;
180 CachedPtr<graphics::GLConfig> gl_config;
181
182 private:
183
184=== renamed file 'include/server/mir/compositor/gl_program.h' => 'include/server/mir/graphics/gl_program.h'
185--- include/server/mir/compositor/gl_program.h 2014-04-07 23:05:24 +0000
186+++ include/server/mir/graphics/gl_program.h 2014-04-22 16:51:34 +0000
187@@ -16,14 +16,14 @@
188 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
189 */
190
191-#ifndef MIR_COMPOSITOR_GL_PROGRAM_H_
192-#define MIR_COMPOSITOR_GL_PROGRAM_H_
193+#ifndef MIR_GRAPHICS_GL_PROGRAM_H_
194+#define MIR_GRAPHICS_GL_PROGRAM_H_
195
196 #include <GLES2/gl2.h>
197
198 namespace mir
199 {
200-namespace compositor
201+namespace graphics
202 {
203
204 class GLShader
205@@ -62,4 +62,4 @@
206 }
207 }
208
209-#endif /* MIR_COMPOSITOR_GL_PROGRAM_H_ */
210+#endif /* MIR_GRAPHICS_GL_PROGRAM_H_ */
211
212=== modified file 'src/server/compositor/CMakeLists.txt'
213--- src/server/compositor/CMakeLists.txt 2014-04-16 21:26:27 +0000
214+++ src/server/compositor/CMakeLists.txt 2014-04-22 16:51:34 +0000
215@@ -6,7 +6,6 @@
216 temporary_buffers.cpp
217 buffer_stream_factory.cpp
218 buffer_stream_surfaces.cpp
219- gl_program.cpp
220 gl_renderer.cpp
221 gl_renderer_factory.cpp
222 multi_threaded_compositor.cpp
223
224=== modified file 'src/server/compositor/default_configuration.cpp'
225--- src/server/compositor/default_configuration.cpp 2014-03-07 03:15:55 +0000
226+++ src/server/compositor/default_configuration.cpp 2014-04-22 16:51:34 +0000
227@@ -72,9 +72,9 @@
228 std::shared_ptr<mc::RendererFactory> mir::DefaultServerConfiguration::the_renderer_factory()
229 {
230 return renderer_factory(
231- []()
232+ [this]()
233 {
234- return std::make_shared<mc::GLRendererFactory>();
235+ return std::make_shared<mc::GLRendererFactory>(the_gl_program_factory());
236 });
237 }
238
239
240=== modified file 'src/server/compositor/gl_renderer.cpp'
241--- src/server/compositor/gl_renderer.cpp 2014-04-17 03:56:51 +0000
242+++ src/server/compositor/gl_renderer.cpp 2014-04-22 16:51:34 +0000
243@@ -65,8 +65,10 @@
244 };
245 }
246
247-mc::GLRenderer::GLRenderer(geom::Rectangle const& display_area)
248- : program(vertex_shader_src, fragment_shader_src),
249+mc::GLRenderer::GLRenderer(
250+ mg::GLProgramFactory const& program_factory,
251+ geom::Rectangle const& display_area)
252+ : program(program_factory.create_gl_program(vertex_shader_src, fragment_shader_src)),
253 position_attr_loc(0),
254 texcoord_attr_loc(0),
255 centre_uniform_loc(0),
256@@ -74,16 +76,16 @@
257 alpha_uniform_loc(0),
258 rotation(NAN) // ensure the first set_rotation succeeds
259 {
260- glUseProgram(program);
261+ glUseProgram(*program);
262
263 /* Set up program variables */
264- GLint tex_loc = glGetUniformLocation(program, "tex");
265- display_transform_uniform_loc = glGetUniformLocation(program, "display_transform");
266- transform_uniform_loc = glGetUniformLocation(program, "transform");
267- alpha_uniform_loc = glGetUniformLocation(program, "alpha");
268- position_attr_loc = glGetAttribLocation(program, "position");
269- texcoord_attr_loc = glGetAttribLocation(program, "texcoord");
270- centre_uniform_loc = glGetUniformLocation(program, "centre");
271+ GLint tex_loc = glGetUniformLocation(*program, "tex");
272+ display_transform_uniform_loc = glGetUniformLocation(*program, "display_transform");
273+ transform_uniform_loc = glGetUniformLocation(*program, "transform");
274+ alpha_uniform_loc = glGetUniformLocation(*program, "alpha");
275+ position_attr_loc = glGetAttribLocation(*program, "position");
276+ texcoord_attr_loc = glGetAttribLocation(*program, "texcoord");
277+ centre_uniform_loc = glGetUniformLocation(*program, "centre");
278
279 glUniform1i(tex_loc, 0);
280
281@@ -133,7 +135,7 @@
282 auto buffer = renderable.buffer(this);
283 saved_resources.insert(buffer);
284
285- glUseProgram(program);
286+ glUseProgram(*program);
287
288 if (renderable.shaped() || renderable.alpha() < 1.0f)
289 {
290@@ -252,8 +254,8 @@
291 -rect.top_left.y.as_float(),
292 0.0f});
293
294- glUseProgram(program);
295- GLint mat_loc = glGetUniformLocation(program, "screen_to_gl_coords");
296+ glUseProgram(*program);
297+ GLint mat_loc = glGetUniformLocation(*program, "screen_to_gl_coords");
298 glUniformMatrix4fv(mat_loc, 1, GL_FALSE, glm::value_ptr(screen_to_gl_coords));
299 glUseProgram(0);
300
301@@ -272,7 +274,7 @@
302 -sin, cos, 0.0f, 0.0f,
303 0.0f, 0.0f, 1.0f, 0.0f,
304 0.0f, 0.0f, 0.0f, 1.0f};
305- glUseProgram(program);
306+ glUseProgram(*program);
307 glUniformMatrix4fv(display_transform_uniform_loc, 1, GL_FALSE, rot);
308 glUseProgram(0);
309
310
311=== modified file 'src/server/compositor/gl_renderer_factory.cpp'
312--- src/server/compositor/gl_renderer_factory.cpp 2014-04-07 20:23:24 +0000
313+++ src/server/compositor/gl_renderer_factory.cpp 2014-04-22 16:51:34 +0000
314@@ -17,17 +17,22 @@
315 */
316
317 #include "gl_renderer_factory.h"
318+#include "mir/compositor/gl_renderer.h"
319+#include "mir/graphics/gl_program.h"
320 #include "mir/geometry/rectangle.h"
321-#include "mir/compositor/gl_renderer.h"
322
323+namespace mg = mir::graphics;
324 namespace mc = mir::compositor;
325 namespace geom = mir::geometry;
326
327+mc::GLRendererFactory::GLRendererFactory(std::shared_ptr<mg::GLProgramFactory> const& factory) :
328+ program_factory(factory)
329+{
330+}
331+
332 std::unique_ptr<mc::Renderer>
333 mc::GLRendererFactory::create_renderer_for(geom::Rectangle const& rect)
334 {
335- std::lock_guard<std::mutex> lock(mutex);
336-
337- auto raw = new GLRenderer(rect);
338+ auto raw = new GLRenderer(*program_factory, rect);
339 return std::unique_ptr<mc::Renderer>(raw);
340 }
341
342=== modified file 'src/server/compositor/gl_renderer_factory.h'
343--- src/server/compositor/gl_renderer_factory.h 2014-04-07 20:23:24 +0000
344+++ src/server/compositor/gl_renderer_factory.h 2014-04-22 16:51:34 +0000
345@@ -20,28 +20,24 @@
346 #define MIR_COMPOSITOR_GL_RENDERER_FACTORY_H_
347
348 #include "mir/compositor/renderer_factory.h"
349-#include <mutex>
350
351 namespace mir
352 {
353+namespace graphics
354+{
355+class GLProgramFactory;
356+}
357 namespace compositor
358 {
359
360 class GLRendererFactory : public RendererFactory
361 {
362 public:
363+ GLRendererFactory(std::shared_ptr<graphics::GLProgramFactory> const& factory);
364 std::unique_ptr<Renderer> create_renderer_for(geometry::Rectangle const& rect);
365-
366 private:
367- /*
368- * We need to serialize renderer creation because some GL calls used
369- * during renderer construction that create unique resource ids
370- * (e.g. glCreateProgram) are not thread-safe when the threads are
371- * have the same or shared EGL contexts.
372- */
373- std::mutex mutex;
374+ std::shared_ptr<graphics::GLProgramFactory> const program_factory;
375 };
376-
377 }
378 }
379
380
381=== modified file 'src/server/graphics/CMakeLists.txt'
382--- src/server/graphics/CMakeLists.txt 2014-03-12 02:46:58 +0000
383+++ src/server/graphics/CMakeLists.txt 2014-04-22 16:51:34 +0000
384@@ -6,6 +6,8 @@
385 default_configuration.cpp
386 default_display_configuration_policy.cpp
387 gl_extensions_base.cpp
388+ gl_program.cpp
389+ program_factory.cpp
390 surfaceless_egl_context.cpp
391 )
392
393
394=== modified file 'src/server/graphics/default_configuration.cpp'
395--- src/server/graphics/default_configuration.cpp 2014-03-27 09:52:04 +0000
396+++ src/server/graphics/default_configuration.cpp 2014-04-22 16:51:34 +0000
397@@ -27,6 +27,7 @@
398
399 #include "mir/graphics/buffer_initializer.h"
400 #include "mir/graphics/gl_config.h"
401+#include "program_factory.h"
402
403 #include "mir/shared_library.h"
404 #include "mir/shared_library_loader.h"
405@@ -165,3 +166,13 @@
406 return std::make_shared<NoGLConfig>();
407 });
408 }
409+
410+std::shared_ptr<mg::GLProgramFactory>
411+mir::DefaultServerConfiguration::the_gl_program_factory()
412+{
413+ return gl_program_factory(
414+ [this]
415+ {
416+ return std::make_shared<mg::ProgramFactory>();
417+ });
418+}
419
420=== renamed file 'src/server/compositor/gl_program.cpp' => 'src/server/graphics/gl_program.cpp'
421--- src/server/compositor/gl_program.cpp 2014-04-07 21:21:09 +0000
422+++ src/server/graphics/gl_program.cpp 2014-04-22 16:51:34 +0000
423@@ -16,11 +16,11 @@
424 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
425 */
426
427-#include "mir/compositor/gl_program.h"
428+#include "mir/graphics/gl_program.h"
429 #include <boost/throw_exception.hpp>
430 #include <stdexcept>
431
432-namespace mc = mir::compositor;
433+namespace mg = mir::graphics;
434
435 namespace
436 {
437@@ -49,7 +49,7 @@
438 }
439 }
440
441-mc::GLShader::GLShader(GLchar const* shader_src, GLuint type)
442+mg::GLShader::GLShader(GLchar const* shader_src, GLuint type)
443 : shader(glCreateShader(type))
444 {
445 GLint param{0};
446@@ -66,17 +66,17 @@
447 }
448 }
449
450-mc::GLShader::~GLShader()
451+mg::GLShader::~GLShader()
452 {
453 glDeleteShader(shader);
454 }
455
456-mc::GLShader::operator GLuint() const
457+mg::GLShader::operator GLuint() const
458 {
459 return shader;
460 }
461
462-mc::GLProgram::GLProgram(
463+mg::GLProgram::GLProgram(
464 GLchar const* vertex_shader_src,
465 GLchar const* fragment_shader_src)
466 : vertex_shader(vertex_shader_src, GL_VERTEX_SHADER),
467@@ -98,12 +98,12 @@
468 }
469 }
470
471-mc::GLProgram::~GLProgram()
472+mg::GLProgram::~GLProgram()
473 {
474 glDeleteProgram(program);
475 }
476
477-mc::GLProgram::operator GLuint() const
478+mg::GLProgram::operator GLuint() const
479 {
480 return program;
481 }
482
483=== added file 'src/server/graphics/program_factory.cpp'
484--- src/server/graphics/program_factory.cpp 1970-01-01 00:00:00 +0000
485+++ src/server/graphics/program_factory.cpp 2014-04-22 16:51:34 +0000
486@@ -0,0 +1,32 @@
487+
488+/*
489+ * Copyright © 2013 Canonical Ltd.
490+ *
491+ * This program is free software: you can redistribute it and/or modify it
492+ * under the terms of the GNU General Public License version 3,
493+ * as published by the Free Software Foundation.
494+ *
495+ * This program is distributed in the hope that it will be useful,
496+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
497+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
498+ * GNU General Public License for more details.
499+ *
500+ * You should have received a copy of the GNU General Public License
501+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
502+ *
503+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
504+ */
505+
506+#include "program_factory.h"
507+#include "mir/graphics/gl_program.h"
508+
509+namespace mg = mir::graphics;
510+
511+std::unique_ptr<mg::GLProgram>
512+mg::ProgramFactory::create_gl_program(
513+ std::string const& vertex_shader,
514+ std::string const& fragment_shader) const
515+{
516+ std::lock_guard<decltype(mutex)> lock(mutex);
517+ return std::unique_ptr<mg::GLProgram>(new GLProgram(vertex_shader.c_str(), fragment_shader.c_str()));
518+}
519
520=== added file 'src/server/graphics/program_factory.h'
521--- src/server/graphics/program_factory.h 1970-01-01 00:00:00 +0000
522+++ src/server/graphics/program_factory.h 2014-04-22 16:51:34 +0000
523@@ -0,0 +1,47 @@
524+/*
525+ * Copyright © 2012 Canonical Ltd.
526+ *
527+ * This program is free software: you can redistribute it and/or modify it
528+ * under the terms of the GNU General Public License version 3,
529+ * as published by the Free Software Foundation.
530+ *
531+ * This program is distributed in the hope that it will be useful,
532+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
533+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
534+ * GNU General Public License for more details.
535+ *
536+ * You should have received a copy of the GNU General Public License
537+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
538+ *
539+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
540+ */
541+
542+#ifndef MIR_GRAPHICS_GL_RENDERER_FACTORY_H_
543+#define MIR_GRAPHICS_GL_RENDERER_FACTORY_H_
544+
545+#include "mir/graphics/gl_program_factory.h"
546+#include "mir/graphics/gl_program.h"
547+#include <mutex>
548+
549+namespace mir
550+{
551+namespace graphics
552+{
553+class ProgramFactory : public GLProgramFactory
554+{
555+public:
556+ std::unique_ptr<GLProgram> create_gl_program(std::string const&, std::string const&) const override;
557+
558+private:
559+ /*
560+ * We need to serialize renderer creation because some GL calls used
561+ * during renderer construction that create unique resource ids
562+ * (e.g. glCreateProgram) are not thread-safe when the threads are
563+ * have the same or shared EGL contexts.
564+ */
565+ std::mutex mutable mutex;
566+};
567+}
568+}
569+
570+#endif /* MIR_GRAPHICS_GL_RENDERER_FACTORY_H_ */
571
572=== modified file 'tests/unit-tests/compositor/test_gl_renderer.cpp'
573--- tests/unit-tests/compositor/test_gl_renderer.cpp 2014-04-22 03:40:44 +0000
574+++ tests/unit-tests/compositor/test_gl_renderer.cpp 2014-04-22 16:51:34 +0000
575@@ -25,6 +25,7 @@
576 #include <mir/geometry/rectangle.h>
577 #include "mir/compositor/renderer.h"
578 #include "src/server/compositor/gl_renderer_factory.h"
579+#include "src/server/graphics/program_factory.h"
580 #include <mir_test/fake_shared.h>
581 #include <mir_test_doubles/mock_buffer.h>
582 #include <mir_test_doubles/mock_renderable.h>
583@@ -64,60 +65,6 @@
584 const std::string stub_info_log = "something failed!";
585 const size_t stub_info_log_length = stub_info_log.size();
586
587-void ExpectShaderCompileFailure(const GLint shader, mtd::MockGL &mock_gl)
588-{
589- EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
590- .WillOnce(SetArgPointee<2>(GL_FALSE));
591-}
592-
593-void ExpectShaderCompileSuccess(const GLint shader, mtd::MockGL &mock_gl)
594-{
595- EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
596- .WillOnce(SetArgPointee<2>(GL_TRUE));
597-}
598-
599-void SetUpMockVertexShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
600-{
601- /* Vertex Shader */
602- EXPECT_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
603- .WillOnce(Return(stub_v_shader));
604- EXPECT_CALL(mock_gl, glShaderSource(stub_v_shader, 1, _, 0));
605- EXPECT_CALL(mock_gl, glCompileShader(stub_v_shader));
606- shader_compile_expectation(stub_v_shader, mock_gl);
607-}
608-
609-void SetUpMockFragmentShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
610-{
611- /* Fragment Shader */
612- EXPECT_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
613- .WillOnce(Return(stub_f_shader));
614- EXPECT_CALL(mock_gl, glShaderSource(stub_f_shader, 1, _, 0));
615- EXPECT_CALL(mock_gl, glCompileShader(stub_f_shader));
616- shader_compile_expectation(stub_f_shader, mock_gl);
617-}
618-
619-void ExpectProgramLinkFailure(const GLint program, mtd::MockGL &mock_gl)
620-{
621- EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
622- .WillOnce(SetArgPointee<2>(GL_FALSE));
623-}
624-
625-void ExpectProgramLinkSuccess(const GLint program, mtd::MockGL &mock_gl)
626-{
627- EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
628- .WillOnce(SetArgPointee<2>(GL_TRUE));
629-}
630-
631-void SetUpMockGraphicsProgram(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &program_link_expectation)
632-{
633- /* Graphics Program */
634- EXPECT_CALL(mock_gl, glCreateProgram())
635- .WillOnce(Return(stub_program));
636- EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_v_shader));
637- EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_f_shader));
638- EXPECT_CALL(mock_gl, glLinkProgram(stub_program));
639- program_link_expectation(stub_program, mock_gl);
640-}
641
642 void SetUpMockProgramData(mtd::MockGL &mock_gl)
643 {
644@@ -140,95 +87,6 @@
645 .WillOnce(Return(centre_uniform_location));
646 }
647
648-class GLRendererSetupProcess :
649- public testing::Test
650-{
651-public:
652-
653- mtd::MockGL mock_gl;
654- mc::GLRendererFactory gl_renderer_factory;
655- mir::geometry::Rectangle display_area;
656-};
657-
658-ACTION_P2(CopyString, str, len)
659-{
660- memcpy(arg3, str, len);
661- arg3[len] = '\0';
662-}
663-
664-ACTION_P(ReturnByConstReference, cref)
665-{
666- return cref;
667-}
668-
669-MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte")
670-{
671- return arg[n] == '\0';
672-}
673-
674-TEST_F(GLRendererSetupProcess, vertex_shader_compiler_failure_recovers_and_throws)
675-{
676- using namespace std::placeholders;
677-
678- SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
679-
680- EXPECT_CALL(mock_gl, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _))
681- .WillOnce(SetArgPointee<2>(stub_info_log_length));
682- EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_v_shader,
683- stub_info_log_length,
684- _,
685- NthCharacterIsNul(stub_info_log_length)))
686- .WillOnce(CopyString(stub_info_log.c_str(),
687- stub_info_log.size()));
688-
689- EXPECT_THROW({
690- auto r = gl_renderer_factory.create_renderer_for(display_area);
691- }, std::runtime_error);
692-}
693-
694-TEST_F(GLRendererSetupProcess, fragment_shader_compiler_failure_recovers_and_throw)
695-{
696- using namespace std::placeholders;
697-
698- SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
699- SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
700-
701- EXPECT_CALL(mock_gl, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _))
702- .WillOnce(SetArgPointee<2>(stub_info_log_length));
703- EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_f_shader,
704- stub_info_log_length,
705- _,
706- NthCharacterIsNul(stub_info_log_length)))
707- .WillOnce(CopyString(stub_info_log.c_str(),
708- stub_info_log.size()));
709-
710- EXPECT_THROW({
711- auto r = gl_renderer_factory.create_renderer_for(display_area);
712- }, std::runtime_error);
713-}
714-
715-TEST_F(GLRendererSetupProcess, graphics_program_linker_failure_recovers_and_throw)
716-{
717- using namespace std::placeholders;
718-
719- SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
720- SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
721- SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkFailure, _1, _2));
722-
723- EXPECT_CALL(mock_gl, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _))
724- .WillOnce(SetArgPointee<2>(stub_info_log_length));
725- EXPECT_CALL(mock_gl, glGetProgramInfoLog(stub_program,
726- stub_info_log_length,
727- _,
728- NthCharacterIsNul(stub_info_log_length)))
729- .WillOnce(CopyString(stub_info_log.c_str(),
730- stub_info_log.size()));
731-
732- EXPECT_THROW({
733- auto r = gl_renderer_factory.create_renderer_for(display_area);
734- }, std::runtime_error);
735-}
736-
737 class GLRenderer :
738 public testing::Test
739 {
740@@ -236,11 +94,19 @@
741
742 GLRenderer()
743 {
744- using namespace std::placeholders;
745+ //Mock defaults
746+ ON_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
747+ .WillByDefault(Return(stub_v_shader));
748+ ON_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
749+ .WillByDefault(Return(stub_f_shader));
750+ ON_CALL(mock_gl, glCreateProgram())
751+ .WillByDefault(Return(stub_program));
752+ ON_CALL(mock_gl, glGetProgramiv(_,_,_))
753+ .WillByDefault(SetArgPointee<2>(GL_TRUE));
754+ ON_CALL(mock_gl, glGetShaderiv(_,_,_))
755+ .WillByDefault(SetArgPointee<2>(GL_TRUE));
756
757- // Silence warnings about "uninteresting" calls that we truly aren't
758- // interested in (for most test cases)...
759- EXPECT_CALL(mock_gl, glClear(_)).Times(AnyNumber());
760+ //A mix of defaults and silencing from here on out
761 EXPECT_CALL(mock_gl, glUseProgram(_)).Times(AnyNumber());
762 EXPECT_CALL(mock_gl, glActiveTexture(_)).Times(AnyNumber());
763 EXPECT_CALL(mock_gl, glUniformMatrix4fv(_, _, GL_FALSE, _))
764@@ -269,29 +135,23 @@
765 EXPECT_CALL(mock_gl, glDisable(_)).Times(AnyNumber());
766
767 InSequence s;
768-
769- SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
770- SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
771- SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkSuccess, _1,_2));
772 SetUpMockProgramData(mock_gl);
773 EXPECT_CALL(mock_gl, glUniform1i(tex_uniform_location, 0));
774
775 EXPECT_CALL(mock_gl, glGetUniformLocation(stub_program, _))
776 .WillOnce(Return(screen_to_gl_coords_uniform_location));
777
778+ mc::GLRendererFactory gl_renderer_factory{std::make_shared<mg::ProgramFactory>()};
779 display_area = {{1, 2}, {3, 4}};
780-
781- EXPECT_CALL(mock_gl, glDeleteProgram(stub_program));
782- EXPECT_CALL(mock_gl, glDeleteShader(stub_f_shader));
783- EXPECT_CALL(mock_gl, glDeleteShader(stub_v_shader));
784+ renderer = gl_renderer_factory.create_renderer_for(display_area);
785 }
786
787 testing::NiceMock<mtd::MockGL> mock_gl;
788 std::shared_ptr<mtd::MockBuffer> mock_buffer;
789 mir::geometry::Rectangle display_area;
790- glm::mat4 trans;
791+ std::unique_ptr<mc::Renderer> renderer;
792 testing::NiceMock<mtd::MockRenderable> renderable;
793- mc::GLRendererFactory gl_renderer_factory;
794+ glm::mat4 trans;
795 };
796
797 }
798@@ -300,8 +160,6 @@
799 {
800 using namespace std::placeholders;
801
802- auto renderer = gl_renderer_factory.create_renderer_for(display_area);
803-
804 InSequence seq;
805
806 EXPECT_CALL(mock_gl, glClear(_));
807@@ -353,8 +211,6 @@
808
809 TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces)
810 {
811- auto renderer = gl_renderer_factory.create_renderer_for(display_area);
812-
813 InSequence seq;
814 EXPECT_CALL(renderable, shaped())
815 .WillOnce(Return(false));
816@@ -380,8 +236,6 @@
817
818 TEST_F(GLRenderer, caches_and_uploads_texture_only_on_buffer_changes)
819 {
820- auto renderer = gl_renderer_factory.create_renderer_for(display_area);
821-
822 InSequence seq;
823
824 // First render() - texture generated and uploaded
825@@ -459,8 +313,6 @@
826
827 TEST_F(GLRenderer, holds_buffers_till_the_end)
828 {
829- auto renderer = gl_renderer_factory.create_renderer_for(display_area);
830-
831 InSequence seq;
832
833 EXPECT_CALL(*mock_buffer, id())
834
835=== modified file 'tests/unit-tests/graphics/CMakeLists.txt'
836--- tests/unit-tests/graphics/CMakeLists.txt 2014-04-15 05:31:19 +0000
837+++ tests/unit-tests/graphics/CMakeLists.txt 2014-04-22 16:51:34 +0000
838@@ -9,6 +9,7 @@
839 ${CMAKE_CURRENT_SOURCE_DIR}/test_pixel_format_utils.cpp
840 ${CMAKE_CURRENT_SOURCE_DIR}/test_surfaceless_egl_context.cpp
841 ${CMAKE_CURRENT_SOURCE_DIR}/test_overlapping_output_grouping.cpp
842+ ${CMAKE_CURRENT_SOURCE_DIR}/test_program_factory.cpp
843 )
844
845 add_subdirectory(nested/)
846
847=== added file 'tests/unit-tests/graphics/test_program_factory.cpp'
848--- tests/unit-tests/graphics/test_program_factory.cpp 1970-01-01 00:00:00 +0000
849+++ tests/unit-tests/graphics/test_program_factory.cpp 2014-04-22 16:51:34 +0000
850@@ -0,0 +1,211 @@
851+/*
852+ * Copyright © 2012-2014 Canonical Ltd.
853+ *
854+ * This program is free software: you can redistribute it and/or modify
855+ * it under the terms of the GNU General Public License version 3 as
856+ * published by the Free Software Foundation.
857+ *
858+ * This program is distributed in the hope that it will be useful,
859+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
860+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
861+ * GNU General Public License for more details.
862+ *
863+ * You should have received a copy of the GNU General Public License
864+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
865+ *
866+ * Authored by: Sam Spilsbury <sam.spilsbury@canonical.com>
867+ * Kevin DuBois <kevin.dubois@canonical.com>
868+ */
869+
870+#include <functional>
871+#include <string>
872+#include <cstring>
873+#include <stdexcept>
874+#include <gtest/gtest.h>
875+#include <gmock/gmock.h>
876+#include <mir/geometry/rectangle.h>
877+#include "mir/compositor/renderer.h"
878+#include "src/server/compositor/gl_renderer_factory.h"
879+#include "src/server/graphics/program_factory.h"
880+#include <mir_test/fake_shared.h>
881+#include <mir_test_doubles/mock_buffer.h>
882+#include <mir_test_doubles/mock_renderable.h>
883+#include <mir_test_doubles/mock_buffer_stream.h>
884+#include <mir/compositor/buffer_stream.h>
885+#include <mir_test_doubles/mock_gl.h>
886+
887+using testing::SetArgPointee;
888+using testing::InSequence;
889+using testing::Return;
890+using testing::ReturnRef;
891+using testing::Pointee;
892+using testing::AnyNumber;
893+using testing::AtLeast;
894+using testing::_;
895+
896+namespace mt=mir::test;
897+namespace mtd=mir::test::doubles;
898+namespace mc=mir::compositor;
899+namespace mg=mir::graphics;
900+
901+namespace
902+{
903+
904+const GLint stub_v_shader = 1;
905+const GLint stub_f_shader = 2;
906+const GLint stub_program = 1;
907+const std::string stub_info_log = "something failed!";
908+const size_t stub_info_log_length = stub_info_log.size();
909+
910+void ExpectShaderCompileFailure(const GLint shader, mtd::MockGL &mock_gl)
911+{
912+ EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
913+ .WillOnce(SetArgPointee<2>(GL_FALSE));
914+}
915+
916+void ExpectShaderCompileSuccess(const GLint shader, mtd::MockGL &mock_gl)
917+{
918+ EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
919+ .WillOnce(SetArgPointee<2>(GL_TRUE));
920+}
921+
922+void SetUpMockVertexShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
923+{
924+ /* Vertex Shader */
925+ EXPECT_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
926+ .WillOnce(Return(stub_v_shader));
927+ EXPECT_CALL(mock_gl, glShaderSource(stub_v_shader, 1, _, 0));
928+ EXPECT_CALL(mock_gl, glCompileShader(stub_v_shader));
929+ shader_compile_expectation(stub_v_shader, mock_gl);
930+}
931+
932+void SetUpMockFragmentShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
933+{
934+ /* Fragment Shader */
935+ EXPECT_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
936+ .WillOnce(Return(stub_f_shader));
937+ EXPECT_CALL(mock_gl, glShaderSource(stub_f_shader, 1, _, 0));
938+ EXPECT_CALL(mock_gl, glCompileShader(stub_f_shader));
939+ shader_compile_expectation(stub_f_shader, mock_gl);
940+}
941+
942+void ExpectProgramLinkFailure(const GLint program, mtd::MockGL &mock_gl)
943+{
944+ EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
945+ .WillOnce(SetArgPointee<2>(GL_FALSE));
946+}
947+
948+void ExpectProgramLinkSuccess(const GLint program, mtd::MockGL &mock_gl)
949+{
950+ EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
951+ .WillOnce(SetArgPointee<2>(GL_TRUE));
952+}
953+
954+void SetUpMockGraphicsProgram(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &program_link_expectation)
955+{
956+ /* Graphics Program */
957+ EXPECT_CALL(mock_gl, glCreateProgram())
958+ .WillOnce(Return(stub_program));
959+ EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_v_shader));
960+ EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_f_shader));
961+ EXPECT_CALL(mock_gl, glLinkProgram(stub_program));
962+ program_link_expectation(stub_program, mock_gl);
963+}
964+
965+class ProgramFactory : public testing::Test
966+{
967+public:
968+ ProgramFactory() :
969+ gl_renderer_factory{std::make_shared<mg::ProgramFactory>()}
970+ {
971+ }
972+ testing::NiceMock<mtd::MockGL> mock_gl;
973+ mc::GLRendererFactory gl_renderer_factory;
974+ mir::geometry::Rectangle display_area;
975+};
976+
977+ACTION_P2(CopyString, str, len)
978+{
979+ memcpy(arg3, str, len);
980+ arg3[len] = '\0';
981+}
982+
983+MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte")
984+{
985+ return arg[n] == '\0';
986+}
987+
988+TEST_F(ProgramFactory, vertex_shader_compiler_failure_recovers_and_throws)
989+{
990+ using namespace std::placeholders;
991+
992+ SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
993+
994+ EXPECT_CALL(mock_gl, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _))
995+ .WillOnce(SetArgPointee<2>(stub_info_log_length));
996+ EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_v_shader,
997+ stub_info_log_length,
998+ _,
999+ NthCharacterIsNul(stub_info_log_length)))
1000+ .WillOnce(CopyString(stub_info_log.c_str(),
1001+ stub_info_log.size()));
1002+
1003+ EXPECT_THROW({
1004+ auto r = gl_renderer_factory.create_renderer_for(display_area);
1005+ }, std::runtime_error);
1006+}
1007+
1008+TEST_F(ProgramFactory, fragment_shader_compiler_failure_recovers_and_throw)
1009+{
1010+ using namespace std::placeholders;
1011+
1012+ SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
1013+ SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
1014+
1015+ EXPECT_CALL(mock_gl, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _))
1016+ .WillOnce(SetArgPointee<2>(stub_info_log_length));
1017+ EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_f_shader,
1018+ stub_info_log_length,
1019+ _,
1020+ NthCharacterIsNul(stub_info_log_length)))
1021+ .WillOnce(CopyString(stub_info_log.c_str(),
1022+ stub_info_log.size()));
1023+
1024+ EXPECT_THROW({
1025+ auto r = gl_renderer_factory.create_renderer_for(display_area);
1026+ }, std::runtime_error);
1027+}
1028+
1029+TEST_F(ProgramFactory, graphics_program_linker_failure_recovers_and_throw)
1030+{
1031+ using namespace std::placeholders;
1032+
1033+ SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
1034+ SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
1035+ SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkFailure, _1, _2));
1036+
1037+ EXPECT_CALL(mock_gl, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _))
1038+ .WillOnce(SetArgPointee<2>(stub_info_log_length));
1039+ EXPECT_CALL(mock_gl, glGetProgramInfoLog(stub_program,
1040+ stub_info_log_length,
1041+ _,
1042+ NthCharacterIsNul(stub_info_log_length)))
1043+ .WillOnce(CopyString(stub_info_log.c_str(),
1044+ stub_info_log.size()));
1045+
1046+ EXPECT_THROW({
1047+ auto r = gl_renderer_factory.create_renderer_for(display_area);
1048+ }, std::runtime_error);
1049+}
1050+
1051+TEST_F(ProgramFactory, graphics_program_creation_success)
1052+{
1053+ using namespace std::placeholders;
1054+
1055+ SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
1056+ SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
1057+ SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkSuccess, _1, _2));
1058+
1059+ gl_renderer_factory.create_renderer_for(display_area);
1060+}
1061+}

Subscribers

People subscribed via source and target branches