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