1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "GLSnapshotTestStateUtils.h"
16 #include "GLSnapshotTesting.h"
17 #include "apigen-codec-common/glUtils.h"
18
19 #include <gtest/gtest.h>
20 #include <map>
21 #include <string>
22
23 namespace emugl {
24
25 static const char kTestVertexShader[] = R"(
26 attribute vec4 position;
27 uniform mat4 testFloatMat;
28 uniform mat4 transform;
29 uniform mat4 screenSpace;
30 uniform ivec3 testInts[2];
31 varying float linear;
32 void main(void) {
33 gl_Position = testFloatMat * transform * position;
34 linear = (screenSpace * position).x;
35 gl_PointSize = linear * 0.5 + float(testInts[1].x);
36 }
37 )";
38
39 static const char kTestFragmentShader[] = R"(
40 precision mediump float;
41 void main() {
42 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
43 }
44 )";
45
46 struct GlShaderVariable {
47 GLint size;
48 GLenum type;
49 std::vector<GLchar> name;
50 GLint location;
51
52 std::vector<GlValues> values;
53 };
54
55 struct GlProgramState {
56 GLboolean deleteStatus;
57 GLboolean linkStatus;
58 GLboolean validateStatus;
59
60 std::vector<GLchar> infoLog;
61
62 std::vector<GLuint> shaders;
63
64 GLint activeAttributes;
65 GLint maxAttributeName;
66 std::vector<GlShaderVariable> attributes;
67
68 GLint activeUniforms;
69 GLint maxUniformName;
70 std::vector<GlShaderVariable> uniforms;
71 };
72
73 // SnapshotGlProgramTest - A helper class for testing the snapshot preservation
74 // of program objects' state.
75 //
76 // This holds state information of a particular single program object whose
77 // state is mutated in order to set up tests.
78 // Provide a lambda via setStateChanger to set up the state which will be
79 // checked for preservation.
80 // A test can also verify that the snapshot keeps the correct program in use by
81 // calling useProgram during state setup.
82 //
83 class SnapshotGlProgramTest : public SnapshotPreserveTest {
84 public:
defaultStateCheck()85 void defaultStateCheck() override {
86 EXPECT_EQ(GL_FALSE, gl->glIsProgram(m_program_name));
87 EXPECT_TRUE(compareGlobalGlInt(gl, GL_CURRENT_PROGRAM, 0));
88 }
89
changedStateCheck()90 void changedStateCheck() override {
91 SCOPED_TRACE("test program name = " + std::to_string(m_program_name));
92 EXPECT_EQ(GL_TRUE, gl->glIsProgram(m_program_name));
93 EXPECT_TRUE(
94 compareGlobalGlInt(gl, GL_CURRENT_PROGRAM, m_current_program));
95
96 GlProgramState currentState = getProgramState();
97
98 EXPECT_STREQ(m_program_state.infoLog.data(),
99 currentState.infoLog.data());
100
101 EXPECT_EQ(m_program_state.deleteStatus, currentState.deleteStatus);
102 EXPECT_EQ(m_program_state.linkStatus, currentState.linkStatus);
103 EXPECT_EQ(m_program_state.validateStatus, currentState.validateStatus);
104
105 // TODO(benzene): allow test to pass even if these are out of order
106 EXPECT_EQ(m_program_state.shaders, currentState.shaders);
107
108 EXPECT_EQ(m_program_state.activeAttributes,
109 currentState.activeAttributes);
110 EXPECT_EQ(m_program_state.maxAttributeName,
111 currentState.maxAttributeName);
112 ASSERT_EQ(m_program_state.attributes.size(),
113 currentState.attributes.size());
114 for (int i = 0; i < currentState.attributes.size(); i++) {
115 SCOPED_TRACE("active attribute i = " + std::to_string(i));
116 EXPECT_EQ(m_program_state.attributes[i].size,
117 currentState.attributes[i].size);
118 EXPECT_EQ(m_program_state.attributes[i].type,
119 currentState.attributes[i].type);
120 EXPECT_STREQ(m_program_state.attributes[i].name.data(),
121 currentState.attributes[i].name.data());
122 EXPECT_EQ(m_program_state.attributes[i].location,
123 currentState.attributes[i].location);
124
125 // TODO(benzene): check attribute values?
126 }
127
128 EXPECT_EQ(m_program_state.activeUniforms, currentState.activeUniforms);
129 EXPECT_EQ(m_program_state.maxUniformName, currentState.maxUniformName);
130 ASSERT_EQ(m_program_state.uniforms.size(),
131 currentState.uniforms.size());
132 for (int i = 0; i < currentState.uniforms.size(); i++) {
133 SCOPED_TRACE("active uniform i = " + std::to_string(i));
134 EXPECT_EQ(m_program_state.uniforms[i].size,
135 currentState.uniforms[i].size);
136 EXPECT_EQ(m_program_state.uniforms[i].type,
137 currentState.uniforms[i].type);
138 EXPECT_STREQ(m_program_state.uniforms[i].name.data(),
139 currentState.uniforms[i].name.data());
140 EXPECT_EQ(m_program_state.uniforms[i].location,
141 currentState.uniforms[i].location);
142
143 for (int j = 0; j < currentState.uniforms[i].size; j++) {
144 SCOPED_TRACE("value j = " + std::to_string(j));
145 GlValues& expectedVal = m_program_state.uniforms[i].values[j];
146 GlValues& currentVal = currentState.uniforms[i].values[j];
147 if (currentVal.floats.size() > 0 &&
148 currentVal.ints.size() > 0) {
149 ADD_FAILURE() << "Uniform "
150 << currentState.uniforms[i].name.data()
151 << " had both ints and floats at index " << j;
152 }
153 if (currentVal.floats.size() > 0) {
154 EXPECT_EQ(currentVal.floats, expectedVal.floats)
155 << currentState.uniforms[i].name.data();
156 } else {
157 EXPECT_EQ(currentVal.ints, expectedVal.ints)
158 << currentState.uniforms[i].name.data();
159 }
160 }
161 }
162 }
163
stateChange()164 void stateChange() override {
165 m_program_name = gl->glCreateProgram();
166 m_program_state = getProgramState();
167 m_state_changer();
168 }
169
setStateChanger(std::function<void ()> changer)170 void setStateChanger(std::function<void()> changer) {
171 m_state_changer = changer;
172 }
173
174 protected:
175 // As part of state change, have the GL use the test program.
useProgram()176 void useProgram() {
177 gl->glUseProgram(m_program_name);
178 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
179 m_current_program = m_program_name;
180 }
181
182 // Collects information about the test program object's current state.
getProgramState()183 GlProgramState getProgramState() {
184 GlProgramState ret = {};
185 if (GL_FALSE == gl->glIsProgram(m_program_name)) {
186 ADD_FAILURE() << "cannot get program state: was not a program";
187 return ret;
188 }
189
190 // Info log
191 GLsizei logLength;
192 gl->glGetProgramiv(m_program_name, GL_INFO_LOG_LENGTH, &logLength);
193 ret.infoLog.resize(logLength);
194 GLsizei actualLength;
195 gl->glGetProgramInfoLog(m_program_name, logLength, &actualLength,
196 &ret.infoLog[0]);
197
198 // Boolean statuses
199 GLint val;
200 gl->glGetProgramiv(m_program_name, GL_DELETE_STATUS, &val);
201 ret.deleteStatus = val;
202 gl->glGetProgramiv(m_program_name, GL_LINK_STATUS, &val);
203 ret.linkStatus = val;
204 gl->glGetProgramiv(m_program_name, GL_VALIDATE_STATUS, &val);
205 ret.validateStatus = val;
206
207 // Attached shaders
208 GLint attachedShaders;
209 gl->glGetProgramiv(m_program_name, GL_ATTACHED_SHADERS,
210 &attachedShaders);
211 ret.shaders.resize(attachedShaders);
212 GLsizei shaderCount;
213 gl->glGetAttachedShaders(m_program_name, attachedShaders, &shaderCount,
214 &ret.shaders[0]);
215
216 // Uniforms
217 gl->glGetProgramiv(m_program_name, GL_ACTIVE_UNIFORM_MAX_LENGTH,
218 &ret.maxUniformName);
219 gl->glGetProgramiv(m_program_name, GL_ACTIVE_UNIFORMS,
220 &ret.activeUniforms);
221 for (GLuint i = 0; i < ret.activeUniforms; i++) {
222 GlShaderVariable unif = {};
223 unif.name.resize(ret.maxUniformName);
224 GLsizei unifLen;
225 gl->glGetActiveUniform(m_program_name, i, ret.maxUniformName,
226 &unifLen, &unif.size, &unif.type,
227 &unif.name[0]);
228 unif.location =
229 gl->glGetUniformLocation(m_program_name, unif.name.data());
230
231 if (unif.size > 1) {
232 // uniform array; get values from each index
233 std::string baseName =
234 getUniformBaseName(std::string(unif.name.data()));
235 for (int uniformValueIndex = 0; uniformValueIndex < unif.size;
236 uniformValueIndex++) {
237 std::string indexedName =
238 baseName + '[' + std::to_string(uniformValueIndex) +
239 ']';
240 GLuint indexedLocation = gl->glGetUniformLocation(
241 m_program_name, indexedName.c_str());
242 getUniformValues(indexedLocation, unif.type, unif.values);
243 }
244 } else {
245 getUniformValues(unif.location, unif.type, unif.values);
246 }
247 ret.uniforms.push_back(unif);
248 }
249
250 // Attributes
251 gl->glGetProgramiv(m_program_name, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
252 &ret.maxAttributeName);
253 gl->glGetProgramiv(m_program_name, GL_ACTIVE_ATTRIBUTES,
254 &ret.activeAttributes);
255 for (GLuint i = 0; i < ret.activeAttributes; i++) {
256 GlShaderVariable attr = {};
257 attr.name.resize(ret.maxAttributeName);
258 GLsizei attrLen;
259 gl->glGetActiveAttrib(m_program_name, i, ret.maxAttributeName,
260 &attrLen, &attr.size, &attr.type,
261 &attr.name[0]);
262 attr.location =
263 gl->glGetAttribLocation(m_program_name, &attr.name[0]);
264
265 // TODO(benzene): get attribute values?
266
267 ret.attributes.push_back(attr);
268 }
269
270 return ret;
271 }
272
273 // Retrieves the values of the uniform at |location| for the test program.
274 // Returns them into |values|.
getUniformValues(GLuint location,GLenum type,std::vector<GlValues> & values)275 void getUniformValues(GLuint location,
276 GLenum type,
277 std::vector<GlValues>& values) {
278 GlValues val = {};
279 switch (type) {
280 case GL_FLOAT:
281 case GL_FLOAT_VEC2:
282 case GL_FLOAT_VEC3:
283 case GL_FLOAT_VEC4:
284 case GL_FLOAT_MAT2:
285 case GL_FLOAT_MAT3:
286 case GL_FLOAT_MAT4:
287 val.floats.resize(glSizeof(type) / sizeof(GLfloat));
288 gl->glGetUniformfv(m_program_name, location, val.floats.data());
289 values.push_back(std::move(val));
290 return;
291 case GL_INT:
292 case GL_INT_VEC2:
293 case GL_INT_VEC3:
294 case GL_INT_VEC4:
295 case GL_BOOL:
296 case GL_BOOL_VEC2:
297 case GL_BOOL_VEC3:
298 case GL_BOOL_VEC4:
299 case GL_SAMPLER_2D:
300 case GL_SAMPLER_CUBE:
301 val.ints.resize(glSizeof(type) / sizeof(GLint));
302 gl->glGetUniformiv(m_program_name, location, val.ints.data());
303 values.push_back(std::move(val));
304 break;
305 default:
306 ADD_FAILURE() << "unsupported uniform type " << type;
307 return;
308 }
309 }
310
311 // If string |name| ends with a subscript ([]) operator, return a substring
312 // with the subscript removed.
getUniformBaseName(const std::string & name)313 std::string getUniformBaseName(const std::string& name) {
314 std::string baseName;
315 int length = name.length();
316 if (length < 3)
317 return name;
318 size_t lastBracket = name.find_last_of('[');
319 if (lastBracket != std::string::npos) {
320 baseName = name.substr(0, lastBracket);
321 } else {
322 baseName = name;
323 }
324 return baseName;
325 }
326
327 GLuint m_program_name = 0;
328 GlProgramState m_program_state = {};
329 GLuint m_current_program = 0;
330
__anon266cd63c0102null331 std::function<void()> m_state_changer = [] {};
332 };
333
TEST_F(SnapshotGlProgramTest,CreateProgram)334 TEST_F(SnapshotGlProgramTest, CreateProgram) {
335 doCheckedSnapshot();
336 }
337
TEST_F(SnapshotGlProgramTest,AttachDetachShader)338 TEST_F(SnapshotGlProgramTest, AttachDetachShader) {
339 setStateChanger([this] {
340 GLuint vshader =
341 loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
342 GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
343 kTestFragmentShader);
344 gl->glAttachShader(m_program_name, vshader);
345 gl->glAttachShader(m_program_name, fshader);
346 gl->glDetachShader(m_program_name, vshader);
347 m_program_state.shaders.push_back(fshader);
348 });
349 doCheckedSnapshot();
350 }
351
TEST_F(SnapshotGlProgramTest,LinkAndValidate)352 TEST_F(SnapshotGlProgramTest, LinkAndValidate) {
353 setStateChanger([this] {
354 GLuint vshader =
355 loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
356 GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
357 kTestFragmentShader);
358
359 gl->glAttachShader(m_program_name, vshader);
360 gl->glAttachShader(m_program_name, fshader);
361
362 gl->glLinkProgram(m_program_name);
363 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
364
365 gl->glValidateProgram(m_program_name);
366 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
367
368 m_program_state = getProgramState();
369
370 EXPECT_EQ(1, m_program_state.activeAttributes);
371 EXPECT_EQ(4, m_program_state.activeUniforms);
372 EXPECT_EQ(GL_TRUE, m_program_state.linkStatus);
373 EXPECT_EQ(GL_TRUE, m_program_state.validateStatus);
374 });
375 doCheckedSnapshot();
376 }
377
TEST_F(SnapshotGlProgramTest,UseProgramAndUniforms)378 TEST_F(SnapshotGlProgramTest, UseProgramAndUniforms) {
379 setStateChanger([this] {
380 GLuint vshader =
381 loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
382 GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
383 kTestFragmentShader);
384
385 gl->glAttachShader(m_program_name, vshader);
386 gl->glAttachShader(m_program_name, fshader);
387
388 gl->glLinkProgram(m_program_name);
389 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
390
391 gl->glValidateProgram(m_program_name);
392 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
393
394 useProgram();
395
396 GLuint floatMatUnifLocation =
397 gl->glGetUniformLocation(m_program_name, "testFloatMat");
398 const GLfloat testFloatMatrix[16] = {
399 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3,
400 0.2, 0.1, 0.0, -0.1, -0.1, -0.3, -0.4, -0.5,
401 };
402 gl->glUniformMatrix4fv(floatMatUnifLocation, 1, GL_FALSE,
403 testFloatMatrix);
404
405 GLuint intVecUnifLocation =
406 gl->glGetUniformLocation(m_program_name, "testInts");
407 const GLint testIntVec[6] = {
408 10, 11, 12, 20, 21, 22,
409 };
410 gl->glUniform3iv(intVecUnifLocation, 2, testIntVec);
411
412 m_program_state = getProgramState();
413 });
414 doCheckedSnapshot();
415 }
416
417 } // namespace emugl
418