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
=== modified file 'examples/demo-shell/demo_renderer.cpp'
--- examples/demo-shell/demo_renderer.cpp 2014-04-15 05:31:19 +0000
+++ examples/demo-shell/demo_renderer.cpp 2014-04-22 16:51:34 +0000
@@ -134,8 +134,10 @@
134134
135} // namespace135} // namespace
136136
137DemoRenderer::DemoRenderer(geometry::Rectangle const& display_area)137DemoRenderer::DemoRenderer(
138 : GLRenderer(display_area)138 graphics::GLProgramFactory const& program_factory,
139 geometry::Rectangle const& display_area)
140 : GLRenderer(program_factory, display_area)
139 , corner_radius(0.5f)141 , corner_radius(0.5f)
140{142{
141 shadow_corner_tex = generate_shadow_corner_texture(0.4f);143 shadow_corner_tex = generate_shadow_corner_texture(0.4f);
142144
=== modified file 'examples/demo-shell/demo_renderer.h'
--- examples/demo-shell/demo_renderer.h 2014-04-15 05:31:19 +0000
+++ examples/demo-shell/demo_renderer.h 2014-04-22 16:51:34 +0000
@@ -29,7 +29,7 @@
29class DemoRenderer : public compositor::GLRenderer29class DemoRenderer : public compositor::GLRenderer
30{30{
31public:31public:
32 DemoRenderer(geometry::Rectangle const& display_area);32 DemoRenderer(graphics::GLProgramFactory const& factory, geometry::Rectangle const& display_area);
33 ~DemoRenderer();33 ~DemoRenderer();
3434
35 void begin() const override;35 void begin() const override;
3636
=== modified file 'examples/demo-shell/demo_shell.cpp'
--- examples/demo-shell/demo_shell.cpp 2014-04-15 05:31:19 +0000
+++ examples/demo-shell/demo_shell.cpp 2014-04-22 16:51:34 +0000
@@ -47,11 +47,18 @@
47class DemoRendererFactory : public compositor::RendererFactory47class DemoRendererFactory : public compositor::RendererFactory
48{48{
49public:49public:
50 DemoRendererFactory(std::shared_ptr<graphics::GLProgramFactory> const& gl_program_factory) :
51 gl_program_factory(gl_program_factory)
52 {
53 }
54
50 std::unique_ptr<compositor::Renderer> create_renderer_for(55 std::unique_ptr<compositor::Renderer> create_renderer_for(
51 geometry::Rectangle const& rect) override56 geometry::Rectangle const& rect) override
52 {57 {
53 return std::unique_ptr<compositor::Renderer>(new DemoRenderer(rect));58 return std::unique_ptr<compositor::Renderer>(new DemoRenderer(*gl_program_factory, rect));
54 }59 }
60private:
61 std::shared_ptr<graphics::GLProgramFactory> const gl_program_factory;
55};62};
5663
57class DemoServerConfiguration : public mir::examples::ServerConfiguration64class DemoServerConfiguration : public mir::examples::ServerConfiguration
@@ -97,7 +104,7 @@
97104
98 std::shared_ptr<compositor::RendererFactory> the_renderer_factory() override105 std::shared_ptr<compositor::RendererFactory> the_renderer_factory() override
99 {106 {
100 return std::make_shared<DemoRendererFactory>();107 return std::make_shared<DemoRendererFactory>(the_gl_program_factory());
101 }108 }
102109
103private:110private:
104111
=== added file 'include/platform/mir/graphics/gl_program_factory.h'
--- include/platform/mir/graphics/gl_program_factory.h 1970-01-01 00:00:00 +0000
+++ include/platform/mir/graphics/gl_program_factory.h 2014-04-22 16:51:34 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */
18
19#ifndef MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_
20#define MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_
21
22#include "mir/graphics/gl_program.h"
23#include <memory>
24
25namespace mir
26{
27namespace graphics
28{
29
30class GLProgramFactory
31{
32public:
33 virtual ~GLProgramFactory() = default;
34
35 virtual std::unique_ptr<GLProgram>
36 create_gl_program(std::string const&, std::string const&) const = 0;
37
38protected:
39 GLProgramFactory() = default;
40
41private:
42 GLProgramFactory(GLProgramFactory const&) = delete;
43 GLProgramFactory& operator=(GLProgramFactory const&) = delete;
44
45};
46
47}
48}
49
50#endif /* MIR_COMPOSITOR_GL_PROGRAM_FACTORY_H_ */
051
=== modified file 'include/server/mir/compositor/gl_renderer.h'
--- include/server/mir/compositor/gl_renderer.h 2014-04-17 03:56:51 +0000
+++ include/server/mir/compositor/gl_renderer.h 2014-04-22 16:51:34 +0000
@@ -20,10 +20,11 @@
20#define MIR_COMPOSITOR_GL_RENDERER_H_20#define MIR_COMPOSITOR_GL_RENDERER_H_
2121
22#include <mir/compositor/renderer.h>22#include <mir/compositor/renderer.h>
23#include <mir/compositor/gl_program.h>23#include <mir/graphics/gl_program.h>
24#include <mir/geometry/rectangle.h>24#include <mir/geometry/rectangle.h>
25#include <mir/graphics/buffer_id.h>25#include <mir/graphics/buffer_id.h>
26#include <mir/graphics/renderable.h>26#include <mir/graphics/renderable.h>
27#include <mir/graphics/gl_program_factory.h>
27#include <GLES2/gl2.h>28#include <GLES2/gl2.h>
28#include <unordered_map>29#include <unordered_map>
29#include <unordered_set>30#include <unordered_set>
@@ -37,7 +38,9 @@
37class GLRenderer : public Renderer38class GLRenderer : public Renderer
38{39{
39public:40public:
40 GLRenderer(geometry::Rectangle const& display_area);41 GLRenderer(
42 graphics::GLProgramFactory const& program_factory,
43 geometry::Rectangle const& display_area);
41 virtual ~GLRenderer() noexcept;44 virtual ~GLRenderer() noexcept;
4245
43 // These are called with a valid GL context:46 // These are called with a valid GL context:
@@ -98,7 +101,7 @@
98 graphics::Buffer& buffer) const;101 graphics::Buffer& buffer) const;
99102
100private:103private:
101 GLProgram program;104 std::unique_ptr<graphics::GLProgram> program;
102 GLuint position_attr_loc;105 GLuint position_attr_loc;
103 GLuint texcoord_attr_loc;106 GLuint texcoord_attr_loc;
104 GLuint centre_uniform_loc;107 GLuint centre_uniform_loc;
105108
=== modified file 'include/server/mir/default_server_configuration.h'
--- include/server/mir/default_server_configuration.h 2014-04-15 05:31:19 +0000
+++ include/server/mir/default_server_configuration.h 2014-04-22 16:51:34 +0000
@@ -92,6 +92,7 @@
92class DisplayReport;92class DisplayReport;
93class GraphicBufferAllocator;93class GraphicBufferAllocator;
94class GLConfig;94class GLConfig;
95class GLProgramFactory;
95namespace nested { class HostConnection; }96namespace nested { class HostConnection; }
96}97}
97namespace input98namespace input
@@ -247,6 +248,7 @@
247protected:248protected:
248 std::shared_ptr<options::Option> the_options() const;249 std::shared_ptr<options::Option> the_options() const;
249250
251 virtual std::shared_ptr<graphics::GLProgramFactory> the_gl_program_factory();
250 virtual std::shared_ptr<input::InputChannelFactory> the_input_channel_factory();252 virtual std::shared_ptr<input::InputChannelFactory> the_input_channel_factory();
251 virtual std::shared_ptr<scene::MediatingDisplayChanger> the_mediating_display_changer();253 virtual std::shared_ptr<scene::MediatingDisplayChanger> the_mediating_display_changer();
252 virtual std::shared_ptr<frontend::ProtobufIpcFactory> the_ipc_factory(254 virtual std::shared_ptr<frontend::ProtobufIpcFactory> the_ipc_factory(
@@ -303,6 +305,7 @@
303 CachedPtr<graphics::DisplayConfigurationPolicy> display_configuration_policy;305 CachedPtr<graphics::DisplayConfigurationPolicy> display_configuration_policy;
304 CachedPtr<graphics::nested::HostConnection> host_connection;306 CachedPtr<graphics::nested::HostConnection> host_connection;
305 CachedPtr<scene::MediatingDisplayChanger> mediating_display_changer;307 CachedPtr<scene::MediatingDisplayChanger> mediating_display_changer;
308 CachedPtr<graphics::GLProgramFactory> gl_program_factory;
306 CachedPtr<graphics::GLConfig> gl_config;309 CachedPtr<graphics::GLConfig> gl_config;
307310
308private:311private:
309312
=== renamed file 'include/server/mir/compositor/gl_program.h' => 'include/server/mir/graphics/gl_program.h'
--- include/server/mir/compositor/gl_program.h 2014-04-07 23:05:24 +0000
+++ include/server/mir/graphics/gl_program.h 2014-04-22 16:51:34 +0000
@@ -16,14 +16,14 @@
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */17 */
1818
19#ifndef MIR_COMPOSITOR_GL_PROGRAM_H_19#ifndef MIR_GRAPHICS_GL_PROGRAM_H_
20#define MIR_COMPOSITOR_GL_PROGRAM_H_20#define MIR_GRAPHICS_GL_PROGRAM_H_
2121
22#include <GLES2/gl2.h>22#include <GLES2/gl2.h>
2323
24namespace mir24namespace mir
25{25{
26namespace compositor26namespace graphics
27{27{
2828
29class GLShader29class GLShader
@@ -62,4 +62,4 @@
62}62}
63}63}
6464
65#endif /* MIR_COMPOSITOR_GL_PROGRAM_H_ */65#endif /* MIR_GRAPHICS_GL_PROGRAM_H_ */
6666
=== modified file 'src/server/compositor/CMakeLists.txt'
--- src/server/compositor/CMakeLists.txt 2014-04-16 21:26:27 +0000
+++ src/server/compositor/CMakeLists.txt 2014-04-22 16:51:34 +0000
@@ -6,7 +6,6 @@
6 temporary_buffers.cpp6 temporary_buffers.cpp
7 buffer_stream_factory.cpp7 buffer_stream_factory.cpp
8 buffer_stream_surfaces.cpp8 buffer_stream_surfaces.cpp
9 gl_program.cpp
10 gl_renderer.cpp9 gl_renderer.cpp
11 gl_renderer_factory.cpp10 gl_renderer_factory.cpp
12 multi_threaded_compositor.cpp11 multi_threaded_compositor.cpp
1312
=== modified file 'src/server/compositor/default_configuration.cpp'
--- src/server/compositor/default_configuration.cpp 2014-03-07 03:15:55 +0000
+++ src/server/compositor/default_configuration.cpp 2014-04-22 16:51:34 +0000
@@ -72,9 +72,9 @@
72std::shared_ptr<mc::RendererFactory> mir::DefaultServerConfiguration::the_renderer_factory()72std::shared_ptr<mc::RendererFactory> mir::DefaultServerConfiguration::the_renderer_factory()
73{73{
74 return renderer_factory(74 return renderer_factory(
75 []()75 [this]()
76 {76 {
77 return std::make_shared<mc::GLRendererFactory>();77 return std::make_shared<mc::GLRendererFactory>(the_gl_program_factory());
78 });78 });
79}79}
8080
8181
=== modified file 'src/server/compositor/gl_renderer.cpp'
--- src/server/compositor/gl_renderer.cpp 2014-04-17 03:56:51 +0000
+++ src/server/compositor/gl_renderer.cpp 2014-04-22 16:51:34 +0000
@@ -65,8 +65,10 @@
65};65};
66}66}
6767
68mc::GLRenderer::GLRenderer(geom::Rectangle const& display_area)68mc::GLRenderer::GLRenderer(
69 : program(vertex_shader_src, fragment_shader_src),69 mg::GLProgramFactory const& program_factory,
70 geom::Rectangle const& display_area)
71 : program(program_factory.create_gl_program(vertex_shader_src, fragment_shader_src)),
70 position_attr_loc(0),72 position_attr_loc(0),
71 texcoord_attr_loc(0),73 texcoord_attr_loc(0),
72 centre_uniform_loc(0),74 centre_uniform_loc(0),
@@ -74,16 +76,16 @@
74 alpha_uniform_loc(0),76 alpha_uniform_loc(0),
75 rotation(NAN) // ensure the first set_rotation succeeds77 rotation(NAN) // ensure the first set_rotation succeeds
76{78{
77 glUseProgram(program);79 glUseProgram(*program);
7880
79 /* Set up program variables */81 /* Set up program variables */
80 GLint tex_loc = glGetUniformLocation(program, "tex");82 GLint tex_loc = glGetUniformLocation(*program, "tex");
81 display_transform_uniform_loc = glGetUniformLocation(program, "display_transform");83 display_transform_uniform_loc = glGetUniformLocation(*program, "display_transform");
82 transform_uniform_loc = glGetUniformLocation(program, "transform");84 transform_uniform_loc = glGetUniformLocation(*program, "transform");
83 alpha_uniform_loc = glGetUniformLocation(program, "alpha");85 alpha_uniform_loc = glGetUniformLocation(*program, "alpha");
84 position_attr_loc = glGetAttribLocation(program, "position");86 position_attr_loc = glGetAttribLocation(*program, "position");
85 texcoord_attr_loc = glGetAttribLocation(program, "texcoord");87 texcoord_attr_loc = glGetAttribLocation(*program, "texcoord");
86 centre_uniform_loc = glGetUniformLocation(program, "centre");88 centre_uniform_loc = glGetUniformLocation(*program, "centre");
8789
88 glUniform1i(tex_loc, 0);90 glUniform1i(tex_loc, 0);
8991
@@ -133,7 +135,7 @@
133 auto buffer = renderable.buffer(this);135 auto buffer = renderable.buffer(this);
134 saved_resources.insert(buffer);136 saved_resources.insert(buffer);
135137
136 glUseProgram(program);138 glUseProgram(*program);
137139
138 if (renderable.shaped() || renderable.alpha() < 1.0f)140 if (renderable.shaped() || renderable.alpha() < 1.0f)
139 {141 {
@@ -252,8 +254,8 @@
252 -rect.top_left.y.as_float(),254 -rect.top_left.y.as_float(),
253 0.0f});255 0.0f});
254256
255 glUseProgram(program);257 glUseProgram(*program);
256 GLint mat_loc = glGetUniformLocation(program, "screen_to_gl_coords");258 GLint mat_loc = glGetUniformLocation(*program, "screen_to_gl_coords");
257 glUniformMatrix4fv(mat_loc, 1, GL_FALSE, glm::value_ptr(screen_to_gl_coords));259 glUniformMatrix4fv(mat_loc, 1, GL_FALSE, glm::value_ptr(screen_to_gl_coords));
258 glUseProgram(0);260 glUseProgram(0);
259261
@@ -272,7 +274,7 @@
272 -sin, cos, 0.0f, 0.0f,274 -sin, cos, 0.0f, 0.0f,
273 0.0f, 0.0f, 1.0f, 0.0f,275 0.0f, 0.0f, 1.0f, 0.0f,
274 0.0f, 0.0f, 0.0f, 1.0f};276 0.0f, 0.0f, 0.0f, 1.0f};
275 glUseProgram(program);277 glUseProgram(*program);
276 glUniformMatrix4fv(display_transform_uniform_loc, 1, GL_FALSE, rot);278 glUniformMatrix4fv(display_transform_uniform_loc, 1, GL_FALSE, rot);
277 glUseProgram(0);279 glUseProgram(0);
278280
279281
=== modified file 'src/server/compositor/gl_renderer_factory.cpp'
--- src/server/compositor/gl_renderer_factory.cpp 2014-04-07 20:23:24 +0000
+++ src/server/compositor/gl_renderer_factory.cpp 2014-04-22 16:51:34 +0000
@@ -17,17 +17,22 @@
17 */17 */
1818
19#include "gl_renderer_factory.h"19#include "gl_renderer_factory.h"
20#include "mir/compositor/gl_renderer.h"
21#include "mir/graphics/gl_program.h"
20#include "mir/geometry/rectangle.h"22#include "mir/geometry/rectangle.h"
21#include "mir/compositor/gl_renderer.h"
2223
24namespace mg = mir::graphics;
23namespace mc = mir::compositor;25namespace mc = mir::compositor;
24namespace geom = mir::geometry;26namespace geom = mir::geometry;
2527
28mc::GLRendererFactory::GLRendererFactory(std::shared_ptr<mg::GLProgramFactory> const& factory) :
29 program_factory(factory)
30{
31}
32
26std::unique_ptr<mc::Renderer>33std::unique_ptr<mc::Renderer>
27mc::GLRendererFactory::create_renderer_for(geom::Rectangle const& rect)34mc::GLRendererFactory::create_renderer_for(geom::Rectangle const& rect)
28{35{
29 std::lock_guard<std::mutex> lock(mutex);36 auto raw = new GLRenderer(*program_factory, rect);
30
31 auto raw = new GLRenderer(rect);
32 return std::unique_ptr<mc::Renderer>(raw);37 return std::unique_ptr<mc::Renderer>(raw);
33}38}
3439
=== modified file 'src/server/compositor/gl_renderer_factory.h'
--- src/server/compositor/gl_renderer_factory.h 2014-04-07 20:23:24 +0000
+++ src/server/compositor/gl_renderer_factory.h 2014-04-22 16:51:34 +0000
@@ -20,28 +20,24 @@
20#define MIR_COMPOSITOR_GL_RENDERER_FACTORY_H_20#define MIR_COMPOSITOR_GL_RENDERER_FACTORY_H_
2121
22#include "mir/compositor/renderer_factory.h"22#include "mir/compositor/renderer_factory.h"
23#include <mutex>
2423
25namespace mir24namespace mir
26{25{
26namespace graphics
27{
28class GLProgramFactory;
29}
27namespace compositor30namespace compositor
28{31{
2932
30class GLRendererFactory : public RendererFactory33class GLRendererFactory : public RendererFactory
31{34{
32public:35public:
36 GLRendererFactory(std::shared_ptr<graphics::GLProgramFactory> const& factory);
33 std::unique_ptr<Renderer> create_renderer_for(geometry::Rectangle const& rect);37 std::unique_ptr<Renderer> create_renderer_for(geometry::Rectangle const& rect);
34
35private:38private:
36 /*39 std::shared_ptr<graphics::GLProgramFactory> const program_factory;
37 * We need to serialize renderer creation because some GL calls used
38 * during renderer construction that create unique resource ids
39 * (e.g. glCreateProgram) are not thread-safe when the threads are
40 * have the same or shared EGL contexts.
41 */
42 std::mutex mutex;
43};40};
44
45}41}
46}42}
4743
4844
=== modified file 'src/server/graphics/CMakeLists.txt'
--- src/server/graphics/CMakeLists.txt 2014-03-12 02:46:58 +0000
+++ src/server/graphics/CMakeLists.txt 2014-04-22 16:51:34 +0000
@@ -6,6 +6,8 @@
6 default_configuration.cpp6 default_configuration.cpp
7 default_display_configuration_policy.cpp7 default_display_configuration_policy.cpp
8 gl_extensions_base.cpp8 gl_extensions_base.cpp
9 gl_program.cpp
10 program_factory.cpp
9 surfaceless_egl_context.cpp11 surfaceless_egl_context.cpp
10)12)
1113
1214
=== modified file 'src/server/graphics/default_configuration.cpp'
--- src/server/graphics/default_configuration.cpp 2014-03-27 09:52:04 +0000
+++ src/server/graphics/default_configuration.cpp 2014-04-22 16:51:34 +0000
@@ -27,6 +27,7 @@
2727
28#include "mir/graphics/buffer_initializer.h"28#include "mir/graphics/buffer_initializer.h"
29#include "mir/graphics/gl_config.h"29#include "mir/graphics/gl_config.h"
30#include "program_factory.h"
3031
31#include "mir/shared_library.h"32#include "mir/shared_library.h"
32#include "mir/shared_library_loader.h"33#include "mir/shared_library_loader.h"
@@ -165,3 +166,13 @@
165 return std::make_shared<NoGLConfig>();166 return std::make_shared<NoGLConfig>();
166 });167 });
167}168}
169
170std::shared_ptr<mg::GLProgramFactory>
171mir::DefaultServerConfiguration::the_gl_program_factory()
172{
173 return gl_program_factory(
174 [this]
175 {
176 return std::make_shared<mg::ProgramFactory>();
177 });
178}
168179
=== renamed file 'src/server/compositor/gl_program.cpp' => 'src/server/graphics/gl_program.cpp'
--- src/server/compositor/gl_program.cpp 2014-04-07 21:21:09 +0000
+++ src/server/graphics/gl_program.cpp 2014-04-22 16:51:34 +0000
@@ -16,11 +16,11 @@
16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>16 * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
17 */17 */
1818
19#include "mir/compositor/gl_program.h"19#include "mir/graphics/gl_program.h"
20#include <boost/throw_exception.hpp>20#include <boost/throw_exception.hpp>
21#include <stdexcept>21#include <stdexcept>
2222
23namespace mc = mir::compositor;23namespace mg = mir::graphics;
2424
25namespace25namespace
26{26{
@@ -49,7 +49,7 @@
49}49}
50}50}
5151
52mc::GLShader::GLShader(GLchar const* shader_src, GLuint type)52mg::GLShader::GLShader(GLchar const* shader_src, GLuint type)
53 : shader(glCreateShader(type))53 : shader(glCreateShader(type))
54{54{
55 GLint param{0};55 GLint param{0};
@@ -66,17 +66,17 @@
66 }66 }
67}67}
6868
69mc::GLShader::~GLShader()69mg::GLShader::~GLShader()
70{70{
71 glDeleteShader(shader);71 glDeleteShader(shader);
72}72}
7373
74mc::GLShader::operator GLuint() const74mg::GLShader::operator GLuint() const
75{75{
76 return shader;76 return shader;
77}77}
7878
79mc::GLProgram::GLProgram(79mg::GLProgram::GLProgram(
80 GLchar const* vertex_shader_src,80 GLchar const* vertex_shader_src,
81 GLchar const* fragment_shader_src)81 GLchar const* fragment_shader_src)
82 : vertex_shader(vertex_shader_src, GL_VERTEX_SHADER),82 : vertex_shader(vertex_shader_src, GL_VERTEX_SHADER),
@@ -98,12 +98,12 @@
98 }98 }
99}99}
100100
101mc::GLProgram::~GLProgram()101mg::GLProgram::~GLProgram()
102{102{
103 glDeleteProgram(program);103 glDeleteProgram(program);
104}104}
105105
106mc::GLProgram::operator GLuint() const106mg::GLProgram::operator GLuint() const
107{107{
108 return program;108 return program;
109}109}
110110
=== added file 'src/server/graphics/program_factory.cpp'
--- src/server/graphics/program_factory.cpp 1970-01-01 00:00:00 +0000
+++ src/server/graphics/program_factory.cpp 2014-04-22 16:51:34 +0000
@@ -0,0 +1,32 @@
1
2/*
3 * Copyright © 2013 Canonical Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 3,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
18 */
19
20#include "program_factory.h"
21#include "mir/graphics/gl_program.h"
22
23namespace mg = mir::graphics;
24
25std::unique_ptr<mg::GLProgram>
26mg::ProgramFactory::create_gl_program(
27 std::string const& vertex_shader,
28 std::string const& fragment_shader) const
29{
30 std::lock_guard<decltype(mutex)> lock(mutex);
31 return std::unique_ptr<mg::GLProgram>(new GLProgram(vertex_shader.c_str(), fragment_shader.c_str()));
32}
033
=== added file 'src/server/graphics/program_factory.h'
--- src/server/graphics/program_factory.h 1970-01-01 00:00:00 +0000
+++ src/server/graphics/program_factory.h 2014-04-22 16:51:34 +0000
@@ -0,0 +1,47 @@
1/*
2 * Copyright © 2012 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
17 */
18
19#ifndef MIR_GRAPHICS_GL_RENDERER_FACTORY_H_
20#define MIR_GRAPHICS_GL_RENDERER_FACTORY_H_
21
22#include "mir/graphics/gl_program_factory.h"
23#include "mir/graphics/gl_program.h"
24#include <mutex>
25
26namespace mir
27{
28namespace graphics
29{
30class ProgramFactory : public GLProgramFactory
31{
32public:
33 std::unique_ptr<GLProgram> create_gl_program(std::string const&, std::string const&) const override;
34
35private:
36 /*
37 * We need to serialize renderer creation because some GL calls used
38 * during renderer construction that create unique resource ids
39 * (e.g. glCreateProgram) are not thread-safe when the threads are
40 * have the same or shared EGL contexts.
41 */
42 std::mutex mutable mutex;
43};
44}
45}
46
47#endif /* MIR_GRAPHICS_GL_RENDERER_FACTORY_H_ */
048
=== modified file 'tests/unit-tests/compositor/test_gl_renderer.cpp'
--- tests/unit-tests/compositor/test_gl_renderer.cpp 2014-04-22 03:40:44 +0000
+++ tests/unit-tests/compositor/test_gl_renderer.cpp 2014-04-22 16:51:34 +0000
@@ -25,6 +25,7 @@
25#include <mir/geometry/rectangle.h>25#include <mir/geometry/rectangle.h>
26#include "mir/compositor/renderer.h"26#include "mir/compositor/renderer.h"
27#include "src/server/compositor/gl_renderer_factory.h"27#include "src/server/compositor/gl_renderer_factory.h"
28#include "src/server/graphics/program_factory.h"
28#include <mir_test/fake_shared.h>29#include <mir_test/fake_shared.h>
29#include <mir_test_doubles/mock_buffer.h>30#include <mir_test_doubles/mock_buffer.h>
30#include <mir_test_doubles/mock_renderable.h>31#include <mir_test_doubles/mock_renderable.h>
@@ -64,60 +65,6 @@
64const std::string stub_info_log = "something failed!";65const std::string stub_info_log = "something failed!";
65const size_t stub_info_log_length = stub_info_log.size();66const size_t stub_info_log_length = stub_info_log.size();
6667
67void ExpectShaderCompileFailure(const GLint shader, mtd::MockGL &mock_gl)
68{
69 EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
70 .WillOnce(SetArgPointee<2>(GL_FALSE));
71}
72
73void ExpectShaderCompileSuccess(const GLint shader, mtd::MockGL &mock_gl)
74{
75 EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
76 .WillOnce(SetArgPointee<2>(GL_TRUE));
77}
78
79void SetUpMockVertexShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
80{
81 /* Vertex Shader */
82 EXPECT_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
83 .WillOnce(Return(stub_v_shader));
84 EXPECT_CALL(mock_gl, glShaderSource(stub_v_shader, 1, _, 0));
85 EXPECT_CALL(mock_gl, glCompileShader(stub_v_shader));
86 shader_compile_expectation(stub_v_shader, mock_gl);
87}
88
89void SetUpMockFragmentShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
90{
91 /* Fragment Shader */
92 EXPECT_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
93 .WillOnce(Return(stub_f_shader));
94 EXPECT_CALL(mock_gl, glShaderSource(stub_f_shader, 1, _, 0));
95 EXPECT_CALL(mock_gl, glCompileShader(stub_f_shader));
96 shader_compile_expectation(stub_f_shader, mock_gl);
97}
98
99void ExpectProgramLinkFailure(const GLint program, mtd::MockGL &mock_gl)
100{
101 EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
102 .WillOnce(SetArgPointee<2>(GL_FALSE));
103}
104
105void ExpectProgramLinkSuccess(const GLint program, mtd::MockGL &mock_gl)
106{
107 EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
108 .WillOnce(SetArgPointee<2>(GL_TRUE));
109}
110
111void SetUpMockGraphicsProgram(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &program_link_expectation)
112{
113 /* Graphics Program */
114 EXPECT_CALL(mock_gl, glCreateProgram())
115 .WillOnce(Return(stub_program));
116 EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_v_shader));
117 EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_f_shader));
118 EXPECT_CALL(mock_gl, glLinkProgram(stub_program));
119 program_link_expectation(stub_program, mock_gl);
120}
12168
122void SetUpMockProgramData(mtd::MockGL &mock_gl)69void SetUpMockProgramData(mtd::MockGL &mock_gl)
123{70{
@@ -140,95 +87,6 @@
140 .WillOnce(Return(centre_uniform_location));87 .WillOnce(Return(centre_uniform_location));
141}88}
14289
143class GLRendererSetupProcess :
144 public testing::Test
145{
146public:
147
148 mtd::MockGL mock_gl;
149 mc::GLRendererFactory gl_renderer_factory;
150 mir::geometry::Rectangle display_area;
151};
152
153ACTION_P2(CopyString, str, len)
154{
155 memcpy(arg3, str, len);
156 arg3[len] = '\0';
157}
158
159ACTION_P(ReturnByConstReference, cref)
160{
161 return cref;
162}
163
164MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte")
165{
166 return arg[n] == '\0';
167}
168
169TEST_F(GLRendererSetupProcess, vertex_shader_compiler_failure_recovers_and_throws)
170{
171 using namespace std::placeholders;
172
173 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
174
175 EXPECT_CALL(mock_gl, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _))
176 .WillOnce(SetArgPointee<2>(stub_info_log_length));
177 EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_v_shader,
178 stub_info_log_length,
179 _,
180 NthCharacterIsNul(stub_info_log_length)))
181 .WillOnce(CopyString(stub_info_log.c_str(),
182 stub_info_log.size()));
183
184 EXPECT_THROW({
185 auto r = gl_renderer_factory.create_renderer_for(display_area);
186 }, std::runtime_error);
187}
188
189TEST_F(GLRendererSetupProcess, fragment_shader_compiler_failure_recovers_and_throw)
190{
191 using namespace std::placeholders;
192
193 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
194 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
195
196 EXPECT_CALL(mock_gl, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _))
197 .WillOnce(SetArgPointee<2>(stub_info_log_length));
198 EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_f_shader,
199 stub_info_log_length,
200 _,
201 NthCharacterIsNul(stub_info_log_length)))
202 .WillOnce(CopyString(stub_info_log.c_str(),
203 stub_info_log.size()));
204
205 EXPECT_THROW({
206 auto r = gl_renderer_factory.create_renderer_for(display_area);
207 }, std::runtime_error);
208}
209
210TEST_F(GLRendererSetupProcess, graphics_program_linker_failure_recovers_and_throw)
211{
212 using namespace std::placeholders;
213
214 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
215 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
216 SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkFailure, _1, _2));
217
218 EXPECT_CALL(mock_gl, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _))
219 .WillOnce(SetArgPointee<2>(stub_info_log_length));
220 EXPECT_CALL(mock_gl, glGetProgramInfoLog(stub_program,
221 stub_info_log_length,
222 _,
223 NthCharacterIsNul(stub_info_log_length)))
224 .WillOnce(CopyString(stub_info_log.c_str(),
225 stub_info_log.size()));
226
227 EXPECT_THROW({
228 auto r = gl_renderer_factory.create_renderer_for(display_area);
229 }, std::runtime_error);
230}
231
232class GLRenderer :90class GLRenderer :
233 public testing::Test91 public testing::Test
234{92{
@@ -236,11 +94,19 @@
23694
237 GLRenderer()95 GLRenderer()
238 {96 {
239 using namespace std::placeholders;97 //Mock defaults
98 ON_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
99 .WillByDefault(Return(stub_v_shader));
100 ON_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
101 .WillByDefault(Return(stub_f_shader));
102 ON_CALL(mock_gl, glCreateProgram())
103 .WillByDefault(Return(stub_program));
104 ON_CALL(mock_gl, glGetProgramiv(_,_,_))
105 .WillByDefault(SetArgPointee<2>(GL_TRUE));
106 ON_CALL(mock_gl, glGetShaderiv(_,_,_))
107 .WillByDefault(SetArgPointee<2>(GL_TRUE));
240108
241 // Silence warnings about "uninteresting" calls that we truly aren't109 //A mix of defaults and silencing from here on out
242 // interested in (for most test cases)...
243 EXPECT_CALL(mock_gl, glClear(_)).Times(AnyNumber());
244 EXPECT_CALL(mock_gl, glUseProgram(_)).Times(AnyNumber());110 EXPECT_CALL(mock_gl, glUseProgram(_)).Times(AnyNumber());
245 EXPECT_CALL(mock_gl, glActiveTexture(_)).Times(AnyNumber());111 EXPECT_CALL(mock_gl, glActiveTexture(_)).Times(AnyNumber());
246 EXPECT_CALL(mock_gl, glUniformMatrix4fv(_, _, GL_FALSE, _))112 EXPECT_CALL(mock_gl, glUniformMatrix4fv(_, _, GL_FALSE, _))
@@ -269,29 +135,23 @@
269 EXPECT_CALL(mock_gl, glDisable(_)).Times(AnyNumber());135 EXPECT_CALL(mock_gl, glDisable(_)).Times(AnyNumber());
270136
271 InSequence s;137 InSequence s;
272
273 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
274 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
275 SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkSuccess, _1,_2));
276 SetUpMockProgramData(mock_gl);138 SetUpMockProgramData(mock_gl);
277 EXPECT_CALL(mock_gl, glUniform1i(tex_uniform_location, 0));139 EXPECT_CALL(mock_gl, glUniform1i(tex_uniform_location, 0));
278140
279 EXPECT_CALL(mock_gl, glGetUniformLocation(stub_program, _))141 EXPECT_CALL(mock_gl, glGetUniformLocation(stub_program, _))
280 .WillOnce(Return(screen_to_gl_coords_uniform_location));142 .WillOnce(Return(screen_to_gl_coords_uniform_location));
281143
144 mc::GLRendererFactory gl_renderer_factory{std::make_shared<mg::ProgramFactory>()};
282 display_area = {{1, 2}, {3, 4}};145 display_area = {{1, 2}, {3, 4}};
283146 renderer = gl_renderer_factory.create_renderer_for(display_area);
284 EXPECT_CALL(mock_gl, glDeleteProgram(stub_program));
285 EXPECT_CALL(mock_gl, glDeleteShader(stub_f_shader));
286 EXPECT_CALL(mock_gl, glDeleteShader(stub_v_shader));
287 }147 }
288148
289 testing::NiceMock<mtd::MockGL> mock_gl;149 testing::NiceMock<mtd::MockGL> mock_gl;
290 std::shared_ptr<mtd::MockBuffer> mock_buffer;150 std::shared_ptr<mtd::MockBuffer> mock_buffer;
291 mir::geometry::Rectangle display_area;151 mir::geometry::Rectangle display_area;
292 glm::mat4 trans;152 std::unique_ptr<mc::Renderer> renderer;
293 testing::NiceMock<mtd::MockRenderable> renderable;153 testing::NiceMock<mtd::MockRenderable> renderable;
294 mc::GLRendererFactory gl_renderer_factory;154 glm::mat4 trans;
295};155};
296156
297}157}
@@ -300,8 +160,6 @@
300{160{
301 using namespace std::placeholders;161 using namespace std::placeholders;
302162
303 auto renderer = gl_renderer_factory.create_renderer_for(display_area);
304
305 InSequence seq;163 InSequence seq;
306164
307 EXPECT_CALL(mock_gl, glClear(_));165 EXPECT_CALL(mock_gl, glClear(_));
@@ -353,8 +211,6 @@
353211
354TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces)212TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces)
355{213{
356 auto renderer = gl_renderer_factory.create_renderer_for(display_area);
357
358 InSequence seq;214 InSequence seq;
359 EXPECT_CALL(renderable, shaped())215 EXPECT_CALL(renderable, shaped())
360 .WillOnce(Return(false));216 .WillOnce(Return(false));
@@ -380,8 +236,6 @@
380236
381TEST_F(GLRenderer, caches_and_uploads_texture_only_on_buffer_changes)237TEST_F(GLRenderer, caches_and_uploads_texture_only_on_buffer_changes)
382{238{
383 auto renderer = gl_renderer_factory.create_renderer_for(display_area);
384
385 InSequence seq;239 InSequence seq;
386240
387 // First render() - texture generated and uploaded241 // First render() - texture generated and uploaded
@@ -459,8 +313,6 @@
459313
460TEST_F(GLRenderer, holds_buffers_till_the_end)314TEST_F(GLRenderer, holds_buffers_till_the_end)
461{315{
462 auto renderer = gl_renderer_factory.create_renderer_for(display_area);
463
464 InSequence seq;316 InSequence seq;
465317
466 EXPECT_CALL(*mock_buffer, id())318 EXPECT_CALL(*mock_buffer, id())
467319
=== modified file 'tests/unit-tests/graphics/CMakeLists.txt'
--- tests/unit-tests/graphics/CMakeLists.txt 2014-04-15 05:31:19 +0000
+++ tests/unit-tests/graphics/CMakeLists.txt 2014-04-22 16:51:34 +0000
@@ -9,6 +9,7 @@
9 ${CMAKE_CURRENT_SOURCE_DIR}/test_pixel_format_utils.cpp9 ${CMAKE_CURRENT_SOURCE_DIR}/test_pixel_format_utils.cpp
10 ${CMAKE_CURRENT_SOURCE_DIR}/test_surfaceless_egl_context.cpp10 ${CMAKE_CURRENT_SOURCE_DIR}/test_surfaceless_egl_context.cpp
11 ${CMAKE_CURRENT_SOURCE_DIR}/test_overlapping_output_grouping.cpp11 ${CMAKE_CURRENT_SOURCE_DIR}/test_overlapping_output_grouping.cpp
12 ${CMAKE_CURRENT_SOURCE_DIR}/test_program_factory.cpp
12)13)
1314
14add_subdirectory(nested/)15add_subdirectory(nested/)
1516
=== added file 'tests/unit-tests/graphics/test_program_factory.cpp'
--- tests/unit-tests/graphics/test_program_factory.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/graphics/test_program_factory.cpp 2014-04-22 16:51:34 +0000
@@ -0,0 +1,211 @@
1/*
2 * Copyright © 2012-2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Sam Spilsbury <sam.spilsbury@canonical.com>
17 * Kevin DuBois <kevin.dubois@canonical.com>
18 */
19
20#include <functional>
21#include <string>
22#include <cstring>
23#include <stdexcept>
24#include <gtest/gtest.h>
25#include <gmock/gmock.h>
26#include <mir/geometry/rectangle.h>
27#include "mir/compositor/renderer.h"
28#include "src/server/compositor/gl_renderer_factory.h"
29#include "src/server/graphics/program_factory.h"
30#include <mir_test/fake_shared.h>
31#include <mir_test_doubles/mock_buffer.h>
32#include <mir_test_doubles/mock_renderable.h>
33#include <mir_test_doubles/mock_buffer_stream.h>
34#include <mir/compositor/buffer_stream.h>
35#include <mir_test_doubles/mock_gl.h>
36
37using testing::SetArgPointee;
38using testing::InSequence;
39using testing::Return;
40using testing::ReturnRef;
41using testing::Pointee;
42using testing::AnyNumber;
43using testing::AtLeast;
44using testing::_;
45
46namespace mt=mir::test;
47namespace mtd=mir::test::doubles;
48namespace mc=mir::compositor;
49namespace mg=mir::graphics;
50
51namespace
52{
53
54const GLint stub_v_shader = 1;
55const GLint stub_f_shader = 2;
56const GLint stub_program = 1;
57const std::string stub_info_log = "something failed!";
58const size_t stub_info_log_length = stub_info_log.size();
59
60void ExpectShaderCompileFailure(const GLint shader, mtd::MockGL &mock_gl)
61{
62 EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
63 .WillOnce(SetArgPointee<2>(GL_FALSE));
64}
65
66void ExpectShaderCompileSuccess(const GLint shader, mtd::MockGL &mock_gl)
67{
68 EXPECT_CALL(mock_gl, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
69 .WillOnce(SetArgPointee<2>(GL_TRUE));
70}
71
72void SetUpMockVertexShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
73{
74 /* Vertex Shader */
75 EXPECT_CALL(mock_gl, glCreateShader(GL_VERTEX_SHADER))
76 .WillOnce(Return(stub_v_shader));
77 EXPECT_CALL(mock_gl, glShaderSource(stub_v_shader, 1, _, 0));
78 EXPECT_CALL(mock_gl, glCompileShader(stub_v_shader));
79 shader_compile_expectation(stub_v_shader, mock_gl);
80}
81
82void SetUpMockFragmentShader(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &shader_compile_expectation)
83{
84 /* Fragment Shader */
85 EXPECT_CALL(mock_gl, glCreateShader(GL_FRAGMENT_SHADER))
86 .WillOnce(Return(stub_f_shader));
87 EXPECT_CALL(mock_gl, glShaderSource(stub_f_shader, 1, _, 0));
88 EXPECT_CALL(mock_gl, glCompileShader(stub_f_shader));
89 shader_compile_expectation(stub_f_shader, mock_gl);
90}
91
92void ExpectProgramLinkFailure(const GLint program, mtd::MockGL &mock_gl)
93{
94 EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
95 .WillOnce(SetArgPointee<2>(GL_FALSE));
96}
97
98void ExpectProgramLinkSuccess(const GLint program, mtd::MockGL &mock_gl)
99{
100 EXPECT_CALL(mock_gl, glGetProgramiv(program, GL_LINK_STATUS, _))
101 .WillOnce(SetArgPointee<2>(GL_TRUE));
102}
103
104void SetUpMockGraphicsProgram(mtd::MockGL &mock_gl, const std::function<void(const GLint, mtd::MockGL &)> &program_link_expectation)
105{
106 /* Graphics Program */
107 EXPECT_CALL(mock_gl, glCreateProgram())
108 .WillOnce(Return(stub_program));
109 EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_v_shader));
110 EXPECT_CALL(mock_gl, glAttachShader(stub_program, stub_f_shader));
111 EXPECT_CALL(mock_gl, glLinkProgram(stub_program));
112 program_link_expectation(stub_program, mock_gl);
113}
114
115class ProgramFactory : public testing::Test
116{
117public:
118 ProgramFactory() :
119 gl_renderer_factory{std::make_shared<mg::ProgramFactory>()}
120 {
121 }
122 testing::NiceMock<mtd::MockGL> mock_gl;
123 mc::GLRendererFactory gl_renderer_factory;
124 mir::geometry::Rectangle display_area;
125};
126
127ACTION_P2(CopyString, str, len)
128{
129 memcpy(arg3, str, len);
130 arg3[len] = '\0';
131}
132
133MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte")
134{
135 return arg[n] == '\0';
136}
137
138TEST_F(ProgramFactory, vertex_shader_compiler_failure_recovers_and_throws)
139{
140 using namespace std::placeholders;
141
142 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
143
144 EXPECT_CALL(mock_gl, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _))
145 .WillOnce(SetArgPointee<2>(stub_info_log_length));
146 EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_v_shader,
147 stub_info_log_length,
148 _,
149 NthCharacterIsNul(stub_info_log_length)))
150 .WillOnce(CopyString(stub_info_log.c_str(),
151 stub_info_log.size()));
152
153 EXPECT_THROW({
154 auto r = gl_renderer_factory.create_renderer_for(display_area);
155 }, std::runtime_error);
156}
157
158TEST_F(ProgramFactory, fragment_shader_compiler_failure_recovers_and_throw)
159{
160 using namespace std::placeholders;
161
162 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
163 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileFailure, _1, _2));
164
165 EXPECT_CALL(mock_gl, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _))
166 .WillOnce(SetArgPointee<2>(stub_info_log_length));
167 EXPECT_CALL(mock_gl, glGetShaderInfoLog(stub_f_shader,
168 stub_info_log_length,
169 _,
170 NthCharacterIsNul(stub_info_log_length)))
171 .WillOnce(CopyString(stub_info_log.c_str(),
172 stub_info_log.size()));
173
174 EXPECT_THROW({
175 auto r = gl_renderer_factory.create_renderer_for(display_area);
176 }, std::runtime_error);
177}
178
179TEST_F(ProgramFactory, graphics_program_linker_failure_recovers_and_throw)
180{
181 using namespace std::placeholders;
182
183 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
184 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
185 SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkFailure, _1, _2));
186
187 EXPECT_CALL(mock_gl, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _))
188 .WillOnce(SetArgPointee<2>(stub_info_log_length));
189 EXPECT_CALL(mock_gl, glGetProgramInfoLog(stub_program,
190 stub_info_log_length,
191 _,
192 NthCharacterIsNul(stub_info_log_length)))
193 .WillOnce(CopyString(stub_info_log.c_str(),
194 stub_info_log.size()));
195
196 EXPECT_THROW({
197 auto r = gl_renderer_factory.create_renderer_for(display_area);
198 }, std::runtime_error);
199}
200
201TEST_F(ProgramFactory, graphics_program_creation_success)
202{
203 using namespace std::placeholders;
204
205 SetUpMockVertexShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
206 SetUpMockFragmentShader(mock_gl, std::bind(ExpectShaderCompileSuccess, _1, _2));
207 SetUpMockGraphicsProgram(mock_gl, std::bind(ExpectProgramLinkSuccess, _1, _2));
208
209 gl_renderer_factory.create_renderer_for(display_area);
210}
211}

Subscribers

People subscribed via source and target branches