You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
15 KiB
418 lines
15 KiB
// Copyright (C) 2018 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "GLSnapshotTestStateUtils.h"
|
|
#include "GLSnapshotTesting.h"
|
|
#include "apigen-codec-common/glUtils.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace emugl {
|
|
|
|
static const char kTestVertexShader[] = R"(
|
|
attribute vec4 position;
|
|
uniform mat4 testFloatMat;
|
|
uniform mat4 transform;
|
|
uniform mat4 screenSpace;
|
|
uniform ivec3 testInts[2];
|
|
varying float linear;
|
|
void main(void) {
|
|
gl_Position = testFloatMat * transform * position;
|
|
linear = (screenSpace * position).x;
|
|
gl_PointSize = linear * 0.5 + float(testInts[1].x);
|
|
}
|
|
)";
|
|
|
|
static const char kTestFragmentShader[] = R"(
|
|
precision mediump float;
|
|
void main() {
|
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
|
|
}
|
|
)";
|
|
|
|
struct GlShaderVariable {
|
|
GLint size;
|
|
GLenum type;
|
|
std::vector<GLchar> name;
|
|
GLint location;
|
|
|
|
std::vector<GlValues> values;
|
|
};
|
|
|
|
struct GlProgramState {
|
|
GLboolean deleteStatus;
|
|
GLboolean linkStatus;
|
|
GLboolean validateStatus;
|
|
|
|
std::vector<GLchar> infoLog;
|
|
|
|
std::vector<GLuint> shaders;
|
|
|
|
GLint activeAttributes;
|
|
GLint maxAttributeName;
|
|
std::vector<GlShaderVariable> attributes;
|
|
|
|
GLint activeUniforms;
|
|
GLint maxUniformName;
|
|
std::vector<GlShaderVariable> uniforms;
|
|
};
|
|
|
|
// SnapshotGlProgramTest - A helper class for testing the snapshot preservation
|
|
// of program objects' state.
|
|
//
|
|
// This holds state information of a particular single program object whose
|
|
// state is mutated in order to set up tests.
|
|
// Provide a lambda via setStateChanger to set up the state which will be
|
|
// checked for preservation.
|
|
// A test can also verify that the snapshot keeps the correct program in use by
|
|
// calling useProgram during state setup.
|
|
//
|
|
class SnapshotGlProgramTest : public SnapshotPreserveTest {
|
|
public:
|
|
void defaultStateCheck() override {
|
|
EXPECT_EQ(GL_FALSE, gl->glIsProgram(m_program_name));
|
|
EXPECT_TRUE(compareGlobalGlInt(gl, GL_CURRENT_PROGRAM, 0));
|
|
}
|
|
|
|
void changedStateCheck() override {
|
|
SCOPED_TRACE("test program name = " + std::to_string(m_program_name));
|
|
EXPECT_EQ(GL_TRUE, gl->glIsProgram(m_program_name));
|
|
EXPECT_TRUE(
|
|
compareGlobalGlInt(gl, GL_CURRENT_PROGRAM, m_current_program));
|
|
|
|
GlProgramState currentState = getProgramState();
|
|
|
|
EXPECT_STREQ(m_program_state.infoLog.data(),
|
|
currentState.infoLog.data());
|
|
|
|
EXPECT_EQ(m_program_state.deleteStatus, currentState.deleteStatus);
|
|
EXPECT_EQ(m_program_state.linkStatus, currentState.linkStatus);
|
|
EXPECT_EQ(m_program_state.validateStatus, currentState.validateStatus);
|
|
|
|
// TODO(benzene): allow test to pass even if these are out of order
|
|
EXPECT_EQ(m_program_state.shaders, currentState.shaders);
|
|
|
|
EXPECT_EQ(m_program_state.activeAttributes,
|
|
currentState.activeAttributes);
|
|
EXPECT_EQ(m_program_state.maxAttributeName,
|
|
currentState.maxAttributeName);
|
|
ASSERT_EQ(m_program_state.attributes.size(),
|
|
currentState.attributes.size());
|
|
for (int i = 0; i < currentState.attributes.size(); i++) {
|
|
SCOPED_TRACE("active attribute i = " + std::to_string(i));
|
|
EXPECT_EQ(m_program_state.attributes[i].size,
|
|
currentState.attributes[i].size);
|
|
EXPECT_EQ(m_program_state.attributes[i].type,
|
|
currentState.attributes[i].type);
|
|
EXPECT_STREQ(m_program_state.attributes[i].name.data(),
|
|
currentState.attributes[i].name.data());
|
|
EXPECT_EQ(m_program_state.attributes[i].location,
|
|
currentState.attributes[i].location);
|
|
|
|
// TODO(benzene): check attribute values?
|
|
}
|
|
|
|
EXPECT_EQ(m_program_state.activeUniforms, currentState.activeUniforms);
|
|
EXPECT_EQ(m_program_state.maxUniformName, currentState.maxUniformName);
|
|
ASSERT_EQ(m_program_state.uniforms.size(),
|
|
currentState.uniforms.size());
|
|
for (int i = 0; i < currentState.uniforms.size(); i++) {
|
|
SCOPED_TRACE("active uniform i = " + std::to_string(i));
|
|
EXPECT_EQ(m_program_state.uniforms[i].size,
|
|
currentState.uniforms[i].size);
|
|
EXPECT_EQ(m_program_state.uniforms[i].type,
|
|
currentState.uniforms[i].type);
|
|
EXPECT_STREQ(m_program_state.uniforms[i].name.data(),
|
|
currentState.uniforms[i].name.data());
|
|
EXPECT_EQ(m_program_state.uniforms[i].location,
|
|
currentState.uniforms[i].location);
|
|
|
|
for (int j = 0; j < currentState.uniforms[i].size; j++) {
|
|
SCOPED_TRACE("value j = " + std::to_string(j));
|
|
GlValues& expectedVal = m_program_state.uniforms[i].values[j];
|
|
GlValues& currentVal = currentState.uniforms[i].values[j];
|
|
if (currentVal.floats.size() > 0 &&
|
|
currentVal.ints.size() > 0) {
|
|
ADD_FAILURE() << "Uniform "
|
|
<< currentState.uniforms[i].name.data()
|
|
<< " had both ints and floats at index " << j;
|
|
}
|
|
if (currentVal.floats.size() > 0) {
|
|
EXPECT_EQ(currentVal.floats, expectedVal.floats)
|
|
<< currentState.uniforms[i].name.data();
|
|
} else {
|
|
EXPECT_EQ(currentVal.ints, expectedVal.ints)
|
|
<< currentState.uniforms[i].name.data();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stateChange() override {
|
|
m_program_name = gl->glCreateProgram();
|
|
m_program_state = getProgramState();
|
|
m_state_changer();
|
|
}
|
|
|
|
void setStateChanger(std::function<void()> changer) {
|
|
m_state_changer = changer;
|
|
}
|
|
|
|
protected:
|
|
// As part of state change, have the GL use the test program.
|
|
void useProgram() {
|
|
gl->glUseProgram(m_program_name);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
m_current_program = m_program_name;
|
|
}
|
|
|
|
// Collects information about the test program object's current state.
|
|
GlProgramState getProgramState() {
|
|
GlProgramState ret = {};
|
|
if (GL_FALSE == gl->glIsProgram(m_program_name)) {
|
|
ADD_FAILURE() << "cannot get program state: was not a program";
|
|
return ret;
|
|
}
|
|
|
|
// Info log
|
|
GLsizei logLength;
|
|
gl->glGetProgramiv(m_program_name, GL_INFO_LOG_LENGTH, &logLength);
|
|
ret.infoLog.resize(logLength);
|
|
GLsizei actualLength;
|
|
gl->glGetProgramInfoLog(m_program_name, logLength, &actualLength,
|
|
&ret.infoLog[0]);
|
|
|
|
// Boolean statuses
|
|
GLint val;
|
|
gl->glGetProgramiv(m_program_name, GL_DELETE_STATUS, &val);
|
|
ret.deleteStatus = val;
|
|
gl->glGetProgramiv(m_program_name, GL_LINK_STATUS, &val);
|
|
ret.linkStatus = val;
|
|
gl->glGetProgramiv(m_program_name, GL_VALIDATE_STATUS, &val);
|
|
ret.validateStatus = val;
|
|
|
|
// Attached shaders
|
|
GLint attachedShaders;
|
|
gl->glGetProgramiv(m_program_name, GL_ATTACHED_SHADERS,
|
|
&attachedShaders);
|
|
ret.shaders.resize(attachedShaders);
|
|
GLsizei shaderCount;
|
|
gl->glGetAttachedShaders(m_program_name, attachedShaders, &shaderCount,
|
|
&ret.shaders[0]);
|
|
|
|
// Uniforms
|
|
gl->glGetProgramiv(m_program_name, GL_ACTIVE_UNIFORM_MAX_LENGTH,
|
|
&ret.maxUniformName);
|
|
gl->glGetProgramiv(m_program_name, GL_ACTIVE_UNIFORMS,
|
|
&ret.activeUniforms);
|
|
for (GLuint i = 0; i < ret.activeUniforms; i++) {
|
|
GlShaderVariable unif = {};
|
|
unif.name.resize(ret.maxUniformName);
|
|
GLsizei unifLen;
|
|
gl->glGetActiveUniform(m_program_name, i, ret.maxUniformName,
|
|
&unifLen, &unif.size, &unif.type,
|
|
&unif.name[0]);
|
|
unif.location =
|
|
gl->glGetUniformLocation(m_program_name, unif.name.data());
|
|
|
|
if (unif.size > 1) {
|
|
// uniform array; get values from each index
|
|
std::string baseName =
|
|
getUniformBaseName(std::string(unif.name.data()));
|
|
for (int uniformValueIndex = 0; uniformValueIndex < unif.size;
|
|
uniformValueIndex++) {
|
|
std::string indexedName =
|
|
baseName + '[' + std::to_string(uniformValueIndex) +
|
|
']';
|
|
GLuint indexedLocation = gl->glGetUniformLocation(
|
|
m_program_name, indexedName.c_str());
|
|
getUniformValues(indexedLocation, unif.type, unif.values);
|
|
}
|
|
} else {
|
|
getUniformValues(unif.location, unif.type, unif.values);
|
|
}
|
|
ret.uniforms.push_back(unif);
|
|
}
|
|
|
|
// Attributes
|
|
gl->glGetProgramiv(m_program_name, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
|
|
&ret.maxAttributeName);
|
|
gl->glGetProgramiv(m_program_name, GL_ACTIVE_ATTRIBUTES,
|
|
&ret.activeAttributes);
|
|
for (GLuint i = 0; i < ret.activeAttributes; i++) {
|
|
GlShaderVariable attr = {};
|
|
attr.name.resize(ret.maxAttributeName);
|
|
GLsizei attrLen;
|
|
gl->glGetActiveAttrib(m_program_name, i, ret.maxAttributeName,
|
|
&attrLen, &attr.size, &attr.type,
|
|
&attr.name[0]);
|
|
attr.location =
|
|
gl->glGetAttribLocation(m_program_name, &attr.name[0]);
|
|
|
|
// TODO(benzene): get attribute values?
|
|
|
|
ret.attributes.push_back(attr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Retrieves the values of the uniform at |location| for the test program.
|
|
// Returns them into |values|.
|
|
void getUniformValues(GLuint location,
|
|
GLenum type,
|
|
std::vector<GlValues>& values) {
|
|
GlValues val = {};
|
|
switch (type) {
|
|
case GL_FLOAT:
|
|
case GL_FLOAT_VEC2:
|
|
case GL_FLOAT_VEC3:
|
|
case GL_FLOAT_VEC4:
|
|
case GL_FLOAT_MAT2:
|
|
case GL_FLOAT_MAT3:
|
|
case GL_FLOAT_MAT4:
|
|
val.floats.resize(glSizeof(type) / sizeof(GLfloat));
|
|
gl->glGetUniformfv(m_program_name, location, val.floats.data());
|
|
values.push_back(std::move(val));
|
|
return;
|
|
case GL_INT:
|
|
case GL_INT_VEC2:
|
|
case GL_INT_VEC3:
|
|
case GL_INT_VEC4:
|
|
case GL_BOOL:
|
|
case GL_BOOL_VEC2:
|
|
case GL_BOOL_VEC3:
|
|
case GL_BOOL_VEC4:
|
|
case GL_SAMPLER_2D:
|
|
case GL_SAMPLER_CUBE:
|
|
val.ints.resize(glSizeof(type) / sizeof(GLint));
|
|
gl->glGetUniformiv(m_program_name, location, val.ints.data());
|
|
values.push_back(std::move(val));
|
|
break;
|
|
default:
|
|
ADD_FAILURE() << "unsupported uniform type " << type;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If string |name| ends with a subscript ([]) operator, return a substring
|
|
// with the subscript removed.
|
|
std::string getUniformBaseName(const std::string& name) {
|
|
std::string baseName;
|
|
int length = name.length();
|
|
if (length < 3)
|
|
return name;
|
|
size_t lastBracket = name.find_last_of('[');
|
|
if (lastBracket != std::string::npos) {
|
|
baseName = name.substr(0, lastBracket);
|
|
} else {
|
|
baseName = name;
|
|
}
|
|
return baseName;
|
|
}
|
|
|
|
GLuint m_program_name = 0;
|
|
GlProgramState m_program_state = {};
|
|
GLuint m_current_program = 0;
|
|
|
|
std::function<void()> m_state_changer = [] {};
|
|
};
|
|
|
|
TEST_F(SnapshotGlProgramTest, CreateProgram) {
|
|
doCheckedSnapshot();
|
|
}
|
|
|
|
TEST_F(SnapshotGlProgramTest, AttachDetachShader) {
|
|
setStateChanger([this] {
|
|
GLuint vshader =
|
|
loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
|
|
GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
|
|
kTestFragmentShader);
|
|
gl->glAttachShader(m_program_name, vshader);
|
|
gl->glAttachShader(m_program_name, fshader);
|
|
gl->glDetachShader(m_program_name, vshader);
|
|
m_program_state.shaders.push_back(fshader);
|
|
});
|
|
doCheckedSnapshot();
|
|
}
|
|
|
|
TEST_F(SnapshotGlProgramTest, LinkAndValidate) {
|
|
setStateChanger([this] {
|
|
GLuint vshader =
|
|
loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
|
|
GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
|
|
kTestFragmentShader);
|
|
|
|
gl->glAttachShader(m_program_name, vshader);
|
|
gl->glAttachShader(m_program_name, fshader);
|
|
|
|
gl->glLinkProgram(m_program_name);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
|
|
gl->glValidateProgram(m_program_name);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
|
|
m_program_state = getProgramState();
|
|
|
|
EXPECT_EQ(1, m_program_state.activeAttributes);
|
|
EXPECT_EQ(4, m_program_state.activeUniforms);
|
|
EXPECT_EQ(GL_TRUE, m_program_state.linkStatus);
|
|
EXPECT_EQ(GL_TRUE, m_program_state.validateStatus);
|
|
});
|
|
doCheckedSnapshot();
|
|
}
|
|
|
|
TEST_F(SnapshotGlProgramTest, UseProgramAndUniforms) {
|
|
setStateChanger([this] {
|
|
GLuint vshader =
|
|
loadAndCompileShader(gl, GL_VERTEX_SHADER, kTestVertexShader);
|
|
GLuint fshader = loadAndCompileShader(gl, GL_FRAGMENT_SHADER,
|
|
kTestFragmentShader);
|
|
|
|
gl->glAttachShader(m_program_name, vshader);
|
|
gl->glAttachShader(m_program_name, fshader);
|
|
|
|
gl->glLinkProgram(m_program_name);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
|
|
gl->glValidateProgram(m_program_name);
|
|
EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
|
|
|
|
useProgram();
|
|
|
|
GLuint floatMatUnifLocation =
|
|
gl->glGetUniformLocation(m_program_name, "testFloatMat");
|
|
const GLfloat testFloatMatrix[16] = {
|
|
1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3,
|
|
0.2, 0.1, 0.0, -0.1, -0.1, -0.3, -0.4, -0.5,
|
|
};
|
|
gl->glUniformMatrix4fv(floatMatUnifLocation, 1, GL_FALSE,
|
|
testFloatMatrix);
|
|
|
|
GLuint intVecUnifLocation =
|
|
gl->glGetUniformLocation(m_program_name, "testInts");
|
|
const GLint testIntVec[6] = {
|
|
10, 11, 12, 20, 21, 22,
|
|
};
|
|
gl->glUniform3iv(intVecUnifLocation, 2, testIntVec);
|
|
|
|
m_program_state = getProgramState();
|
|
});
|
|
doCheckedSnapshot();
|
|
}
|
|
|
|
} // namespace emugl
|