• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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