Merge lp:~kdub/mir/gl-program-creation-factory into lp:mir
- gl-program-creation-factory
- Merge into development-branch
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 |
Related bugs: |
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-
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-
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-
note 3: (future planning)
I plan on giving the GLProgramFactory down to the android platform so it can make its own HWC gl program.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1572
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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....
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://
"Applications are responsible for providing the synchronization across API calls when objects are accessed from different execution threads."
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:glCreateProg
Alexandros Frantzis (afrantzis) wrote : | # |
100 + GLProgramFactory() {};
= default;
Alexandros Frantzis (afrantzis) wrote : | # |
> 100 + GLProgramFactory() {};
> = default;
Looks good otherwise, so pre-approving to unblock.
Kevin DuBois (kdub) wrote : | # |
> > 100 + GLProgramFactory() {};
> > = default;
>
> Looks good otherwise, so pre-approving to unblock.
thanks! working on change now
Alberto Aguirre (albaguirre) wrote : | # |
489 + * Copyright © 2013 Canonical Ltd.
525 + * Copyright © 2012 Canonical Ltd.
Wrong year?
Kevin DuBois (kdub) wrote : | # |
> 489 + * Copyright © 2013 Canonical Ltd.
> 525 + * Copyright © 2012 Canonical Ltd.
>
> Wrong year?
Indeed, fixed!
Preview Diff
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 | +} |
FAILED: Continuous integration, rev:1571 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/1359/ jenkins. qa.ubuntu. com/job/ mir-android- trusty- i386-build/ 1651/console jenkins. qa.ubuntu. com/job/ mir-clang- trusty- amd64-build/ 1649/console jenkins. qa.ubuntu. com/job/ mir-mediumtests -trusty- touch/1224/ console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- amd64-ci/ 1091/console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 1096/console jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- trusty- armhf/1225/ console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/1359/ rebuild
http://