• 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 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