2
* Copyright © 2012 Canonical Ltd.
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.
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.
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/>.
16
* Authored by: Sam Spilsbury <sam.spilsbury@canonical.com>
23
#include <gtest/gtest.h>
24
#include <gmock/gmock.h>
25
#include <mir/geometry/size.h>
26
#include <mir/graphics/gl_renderer.h>
27
#include <mir/graphics/renderable.h>
28
#include <mir/compositor/graphic_region.h>
29
#include <mir_test/gl_mock.h>
31
using testing::SetArgPointee;
32
using testing::InSequence;
33
using testing::Return;
36
namespace mc=mir::compositor;
37
namespace mg=mir::graphics;
42
const GLint stub_bad_shader = 0;
43
const GLint stub_bad_program = 0;
44
const GLint stub_v_shader = 1;
45
const GLint stub_f_shader = 2;
46
const GLint stub_program = 1;
47
const GLint stub_vbo = 1;
48
const GLint stub_texture = 1;
49
const GLint transform_uniform_location = 1;
50
const GLint alpha_uniform_location = 2;
51
const GLint position_attr_location = 3;
52
const GLint texcoord_attr_location = 4;
53
const GLint screen_to_gl_coords_uniform_location = 5;
54
const GLint tex_uniform_location = 6;
55
const std::string stub_info_log = "something failed!";
56
const size_t stub_info_log_length = stub_info_log.size();
57
const size_t stub_info_log_buffer_length = stub_info_log_length + 1;
59
void ExpectShaderCompileFailure(const GLint shader, mir::GLMock &gl_mock)
61
EXPECT_CALL(gl_mock, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
62
.WillOnce(SetArgPointee<2>(GL_FALSE));
65
void ExpectShaderCompileSuccess(const GLint shader, mir::GLMock &gl_mock)
67
EXPECT_CALL(gl_mock, glGetShaderiv(shader, GL_COMPILE_STATUS, _))
68
.WillOnce(SetArgPointee<2>(GL_TRUE));
71
void SetUpMockVertexShader(mir::GLMock &gl_mock, const std::function<void(const GLint, mir::GLMock &)> &shader_compile_expectation)
74
EXPECT_CALL(gl_mock, glCreateShader(GL_VERTEX_SHADER))
75
.WillOnce(Return(stub_v_shader));
76
EXPECT_CALL(gl_mock, glShaderSource(stub_v_shader, 1, _, 0));
77
EXPECT_CALL(gl_mock, glCompileShader(stub_v_shader));
78
shader_compile_expectation(stub_v_shader, gl_mock);
81
void SetUpMockFragmentShader(mir::GLMock &gl_mock, const std::function<void(const GLint, mir::GLMock &)> &shader_compile_expectation)
84
EXPECT_CALL(gl_mock, glCreateShader(GL_FRAGMENT_SHADER))
85
.WillOnce(Return(stub_f_shader));
86
EXPECT_CALL(gl_mock, glShaderSource(stub_f_shader, 1, _, 0));
87
EXPECT_CALL(gl_mock, glCompileShader(stub_f_shader));
88
shader_compile_expectation(stub_f_shader, gl_mock);
91
void ExpectProgramLinkFailure(const GLint program, mir::GLMock &gl_mock)
93
EXPECT_CALL(gl_mock, glGetProgramiv(program, GL_LINK_STATUS, _))
94
.WillOnce(SetArgPointee<2>(GL_FALSE));
97
void ExpectProgramLinkSuccess(const GLint program, mir::GLMock &gl_mock)
99
EXPECT_CALL(gl_mock, glGetProgramiv(program, GL_LINK_STATUS, _))
100
.WillOnce(SetArgPointee<2>(GL_TRUE));
103
void SetUpMockGraphicsProgram(mir::GLMock &gl_mock, const std::function<void(const GLint, mir::GLMock &)> &program_link_expectation)
105
/* Graphics Program */
106
EXPECT_CALL(gl_mock, glCreateProgram())
107
.WillOnce(Return(stub_program));
108
EXPECT_CALL(gl_mock, glAttachShader(stub_program, stub_v_shader));
109
EXPECT_CALL(gl_mock, glAttachShader(stub_program, stub_f_shader));
110
EXPECT_CALL(gl_mock, glLinkProgram(stub_program));
111
program_link_expectation(stub_program, gl_mock);
114
void SetUpMockProgramData(mir::GLMock &gl_mock)
116
/* Uniforms and Attributes */
117
EXPECT_CALL(gl_mock, glUseProgram(stub_program));
119
EXPECT_CALL(gl_mock, glGetUniformLocation(stub_program, _))
120
.WillOnce(Return(screen_to_gl_coords_uniform_location));
121
EXPECT_CALL(gl_mock, glGetUniformLocation(stub_program, _))
122
.WillOnce(Return(tex_uniform_location));
123
EXPECT_CALL(gl_mock, glGetUniformLocation(stub_program, _))
124
.WillOnce(Return(transform_uniform_location));
125
EXPECT_CALL(gl_mock, glGetUniformLocation(stub_program, _))
126
.WillOnce(Return(alpha_uniform_location));
127
EXPECT_CALL(gl_mock, glGetAttribLocation(stub_program, _))
128
.WillOnce(Return(position_attr_location));
129
EXPECT_CALL(gl_mock, glGetAttribLocation(stub_program, _))
130
.WillOnce(Return(texcoord_attr_location));
132
EXPECT_CALL(gl_mock, glUniformMatrix4fv(screen_to_gl_coords_uniform_location, 1, GL_FALSE, _));
135
void SetUpMockRenderTexture(mir::GLMock &gl_mock)
137
/* Set up the render texture */
138
EXPECT_CALL(gl_mock, glGenTextures(1, _))
139
.WillOnce(SetArgPointee<1>(stub_texture));
140
EXPECT_CALL(gl_mock, glBindTexture(GL_TEXTURE_2D, stub_texture));
141
EXPECT_CALL(gl_mock, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
142
EXPECT_CALL(gl_mock, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
143
EXPECT_CALL(gl_mock, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
144
EXPECT_CALL(gl_mock, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
147
void FillMockVertexBuffer(mir::GLMock &gl_mock)
150
EXPECT_CALL(gl_mock, glGenBuffers(1, _))
151
.WillOnce(SetArgPointee<1>(stub_vbo));
152
EXPECT_CALL(gl_mock, glBindBuffer(GL_ARRAY_BUFFER, stub_vbo));
153
EXPECT_CALL(gl_mock, glBufferData(GL_ARRAY_BUFFER, _, _, GL_STATIC_DRAW));
155
/* These should go away */
156
EXPECT_CALL(gl_mock, glBindBuffer(GL_ARRAY_BUFFER, 0));
157
EXPECT_CALL(gl_mock, glUseProgram(0));
160
class GLRendererSetupProcess :
166
mir::geometry::Size display_size;
169
ACTION_P2(CopyString, str, len)
171
memcpy(arg3, str, len);
175
ACTION_P(ReturnByConstReference, cref)
180
MATCHER_P(NthCharacterIsNul, n, "specified character is the nul-byte")
182
return arg[n] == '\0';
185
TEST_F(GLRendererSetupProcess, vertex_shader_compiler_failure_recovers_and_throws)
187
using namespace std::placeholders;
189
SetUpMockVertexShader(gl_mock, std::bind(ExpectShaderCompileFailure, _1, _2));
191
EXPECT_CALL(gl_mock, glGetShaderiv(stub_v_shader, GL_INFO_LOG_LENGTH, _))
192
.WillOnce(SetArgPointee<2>(stub_info_log_length));
193
EXPECT_CALL(gl_mock, glGetShaderInfoLog(stub_v_shader,
194
stub_info_log_length,
196
NthCharacterIsNul(stub_info_log_length)))
197
.WillOnce(CopyString(stub_info_log.c_str(),
198
stub_info_log.size()));
201
std::unique_ptr<mg::GLRenderer> r;
202
r.reset(new mg::GLRenderer(display_size));
203
}, std::runtime_error);
206
TEST_F(GLRendererSetupProcess, fragment_shader_compiler_failure_recovers_and_throw)
208
using namespace std::placeholders;
210
SetUpMockVertexShader(gl_mock, std::bind(ExpectShaderCompileSuccess, _1, _2));
211
SetUpMockFragmentShader(gl_mock, std::bind(ExpectShaderCompileFailure, _1, _2));
213
EXPECT_CALL(gl_mock, glGetShaderiv(stub_f_shader, GL_INFO_LOG_LENGTH, _))
214
.WillOnce(SetArgPointee<2>(stub_info_log_length));
215
EXPECT_CALL(gl_mock, glGetShaderInfoLog(stub_f_shader,
216
stub_info_log_length,
218
NthCharacterIsNul(stub_info_log_length)))
219
.WillOnce(CopyString(stub_info_log.c_str(),
220
stub_info_log.size()));
223
std::unique_ptr<mg::GLRenderer> r;
224
r.reset(new mg::GLRenderer(display_size));
225
}, std::runtime_error);
228
TEST_F(GLRendererSetupProcess, graphics_program_linker_failure_recovers_and_throw)
230
using namespace std::placeholders;
232
SetUpMockVertexShader(gl_mock, std::bind(ExpectShaderCompileSuccess, _1, _2));
233
SetUpMockFragmentShader(gl_mock, std::bind(ExpectShaderCompileSuccess, _1, _2));
234
SetUpMockGraphicsProgram(gl_mock, std::bind(ExpectProgramLinkFailure, _1, _2));
236
EXPECT_CALL(gl_mock, glGetProgramiv(stub_program, GL_INFO_LOG_LENGTH, _))
237
.WillOnce(SetArgPointee<2>(stub_info_log_length));
238
EXPECT_CALL(gl_mock, glGetProgramInfoLog(stub_program,
239
stub_info_log_length,
241
NthCharacterIsNul(stub_info_log_length)))
242
.WillOnce(CopyString(stub_info_log.c_str(),
243
stub_info_log.size()));
246
std::unique_ptr<mg::GLRenderer> r;
247
r.reset(new mg::GLRenderer(display_size));
248
}, std::runtime_error);
258
using namespace std::placeholders;
262
SetUpMockVertexShader(gl_mock, std::bind(ExpectShaderCompileSuccess, _1, _2));
263
SetUpMockFragmentShader(gl_mock, std::bind(ExpectShaderCompileSuccess, _1, _2));
264
SetUpMockGraphicsProgram(gl_mock, std::bind(ExpectProgramLinkSuccess, _1,_2));
265
SetUpMockProgramData(gl_mock);
266
SetUpMockRenderTexture(gl_mock);
267
EXPECT_CALL(gl_mock, glUniform1i(tex_uniform_location, 0));
268
FillMockVertexBuffer(gl_mock);
270
renderer.reset(new mg::GLRenderer(display_size));
274
mir::geometry::Size display_size;
275
std::unique_ptr<mg::GLRenderer> renderer;
278
class MockRenderable :
279
public mg::Renderable
283
MOCK_CONST_METHOD0(top_left, mir::geometry::Point());
284
MOCK_CONST_METHOD0(size, mir::geometry::Size());
285
MOCK_CONST_METHOD0(texture, std::shared_ptr<mc::GraphicRegion>());
286
MOCK_CONST_METHOD0(transformation, glm::mat4());
287
MOCK_CONST_METHOD0(alpha, float());
288
MOCK_CONST_METHOD0(hidden, bool());
291
class MockGraphicRegion :
292
public mc::GraphicRegion
296
MOCK_CONST_METHOD0(size, mir::geometry::Size());
297
MOCK_CONST_METHOD0(stride, mir::geometry::Stride());
298
MOCK_CONST_METHOD0(pixel_format, mir::geometry::PixelFormat());
299
MOCK_METHOD0(bind_to_texture, void());
302
void NullGraphicRegionDeleter(MockGraphicRegion * /* gr */)
308
TEST_F(GLRenderer, TestSetUpRenderContextBeforeRenderingRenderable)
310
using namespace std::placeholders;
313
MockGraphicRegion gr;
314
std::shared_ptr<MockGraphicRegion> gr_ptr(&gr, std::bind(NullGraphicRegionDeleter, _1));
315
mir::geometry::Point tl;
316
mir::geometry::Size s;
317
glm::mat4 transformation;
319
tl.x = mir::geometry::X(1);
320
tl.y = mir::geometry::Y(2);
322
s.width = mir::geometry::Width(10);
323
s.height = mir::geometry::Height(20);
327
EXPECT_CALL(rd, top_left()).WillOnce(Return(tl));
328
EXPECT_CALL(rd, size()).WillOnce(Return(s));
330
EXPECT_CALL(gl_mock, glUseProgram(stub_program));
331
EXPECT_CALL(gl_mock, glEnable(GL_BLEND));
332
EXPECT_CALL(gl_mock, glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
333
EXPECT_CALL(gl_mock, glActiveTexture(GL_TEXTURE0));
335
EXPECT_CALL(rd, transformation())
336
.WillOnce(Return(transformation));
337
EXPECT_CALL(gl_mock, glUniformMatrix4fv(transform_uniform_location, 1, GL_FALSE, _));
338
EXPECT_CALL(rd, alpha())
339
.WillOnce(Return(0));
340
EXPECT_CALL(gl_mock, glUniform1f(alpha_uniform_location, _));
341
EXPECT_CALL(gl_mock, glBindBuffer(GL_ARRAY_BUFFER, stub_vbo));
342
EXPECT_CALL(gl_mock, glVertexAttribPointer(position_attr_location, 3, GL_FLOAT, GL_FALSE, _, _));
343
EXPECT_CALL(gl_mock, glVertexAttribPointer(texcoord_attr_location, 2, GL_FLOAT, GL_FALSE, _, _));
345
EXPECT_CALL(gl_mock, glBindTexture(GL_TEXTURE_2D, stub_texture));
347
EXPECT_CALL(rd, texture())
348
.WillOnce(Return(gr_ptr));
349
EXPECT_CALL(gr, bind_to_texture());
351
EXPECT_CALL(gl_mock, glEnableVertexAttribArray(position_attr_location));
352
EXPECT_CALL(gl_mock, glEnableVertexAttribArray(texcoord_attr_location));
354
EXPECT_CALL(gl_mock, glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
356
EXPECT_CALL(gl_mock, glDisableVertexAttribArray(texcoord_attr_location));
357
EXPECT_CALL(gl_mock, glDisableVertexAttribArray(position_attr_location));
359
renderer->render(rd);