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.
1410 lines
49 KiB
1410 lines
49 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL (ES) Module
|
|
* -----------------------------------------------
|
|
*
|
|
* Copyright 2014 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.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Compiler test case.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "glsShaderLibraryCase.hpp"
|
|
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuRenderTarget.hpp"
|
|
#include "tcuTextureUtil.hpp"
|
|
#include "tcuSurface.hpp"
|
|
|
|
#include "tcuStringTemplate.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "gluPixelTransfer.hpp"
|
|
#include "gluDrawUtil.hpp"
|
|
#include "gluContextInfo.hpp"
|
|
#include "gluStrUtil.hpp"
|
|
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
|
|
#include "deRandom.hpp"
|
|
#include "deInt32.h"
|
|
#include "deMath.h"
|
|
#include "deString.h"
|
|
#include "deStringUtil.hpp"
|
|
#include "deSharedPtr.hpp"
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <sstream>
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gls
|
|
{
|
|
|
|
using namespace tcu;
|
|
using namespace glu;
|
|
using namespace glu::sl;
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
using std::ostringstream;
|
|
using std::map;
|
|
using std::pair;
|
|
|
|
using de::SharedPtr;
|
|
|
|
// OpenGL-specific specialization utils
|
|
|
|
static vector<RequiredExtension> checkAndSpecializeExtensions (const vector<RequiredExtension>& src,
|
|
const ContextInfo& ctxInfo)
|
|
{
|
|
vector<RequiredExtension> specialized;
|
|
|
|
for (size_t extNdx = 0; extNdx < src.size(); ++extNdx)
|
|
{
|
|
const RequiredExtension& extension = src[extNdx];
|
|
int supportedAltNdx = -1;
|
|
|
|
for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx)
|
|
{
|
|
if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str()))
|
|
{
|
|
supportedAltNdx = (int)alternativeNdx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (supportedAltNdx >= 0)
|
|
{
|
|
specialized.push_back(RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages));
|
|
}
|
|
else
|
|
{
|
|
// no extension(s). Make a nice output
|
|
std::ostringstream extensionList;
|
|
|
|
for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx)
|
|
{
|
|
if (!extensionList.str().empty())
|
|
extensionList << ", ";
|
|
extensionList << extension.alternatives[ndx];
|
|
}
|
|
|
|
if (extension.alternatives.size() == 1)
|
|
throw tcu::NotSupportedError("Test requires extension " + extensionList.str());
|
|
else
|
|
throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str());
|
|
}
|
|
}
|
|
|
|
return specialized;
|
|
}
|
|
|
|
static void checkImplementationLimits (const vector<RequiredCapability>& requiredCaps,
|
|
const ContextInfo& ctxInfo)
|
|
{
|
|
for (size_t capNdx = 0; capNdx < requiredCaps.size(); ++capNdx)
|
|
{
|
|
const RequiredCapability& capability = requiredCaps[capNdx];
|
|
if (capability.type != CAPABILITY_LIMIT)
|
|
continue;
|
|
|
|
const deUint32 pname = capability.enumName;
|
|
const int requiredValue = capability.referenceValue;
|
|
const int supportedValue = ctxInfo.getInt((int)pname);
|
|
|
|
if (supportedValue <= requiredValue)
|
|
throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + de::toString(supportedValue) + ") >= " + de::toString(requiredValue));
|
|
}
|
|
}
|
|
|
|
// Shader source specialization
|
|
|
|
// This functions builds a matching vertex shader for a 'both' case, when
|
|
// the fragment shader is being tested.
|
|
// We need to build attributes and varyings for each 'input'.
|
|
static string genVertexShader (const ShaderCaseSpecification& spec)
|
|
{
|
|
ostringstream res;
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion);
|
|
const char* const vtxIn = usesInout ? "in" : "attribute";
|
|
const char* const vtxOut = usesInout ? "out" : "varying";
|
|
|
|
res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
|
|
|
|
// Declarations (position + attribute/varying for each input).
|
|
res << "precision highp float;\n";
|
|
res << "precision highp int;\n";
|
|
res << "\n";
|
|
res << vtxIn << " highp vec4 dEQP_Position;\n";
|
|
|
|
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.inputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const typeStr = getDataTypeName(floatType);
|
|
|
|
res << vtxIn << " " << typeStr << " a_" << val.name << ";\n";
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
res << vtxOut << " " << typeStr << " " << val.name << ";\n";
|
|
else
|
|
res << vtxOut << " " << typeStr << " v_" << val.name << ";\n";
|
|
}
|
|
res << "\n";
|
|
|
|
// Main function.
|
|
// - gl_Position = dEQP_Position;
|
|
// - for each input: write attribute directly to varying
|
|
res << "void main()\n";
|
|
res << "{\n";
|
|
res << " gl_Position = dEQP_Position;\n";
|
|
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.inputs[ndx];
|
|
const string& name = val.name;
|
|
|
|
if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT)
|
|
res << " " << name << " = a_" << name << ";\n";
|
|
else
|
|
res << " v_" << name << " = a_" << name << ";\n";
|
|
}
|
|
|
|
res << "}\n";
|
|
return res.str();
|
|
}
|
|
|
|
static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName)
|
|
{
|
|
bool isFirstOutput = true;
|
|
|
|
for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++)
|
|
{
|
|
const Value& val = valueBlock.outputs[ndx];
|
|
|
|
// Check if we're only interested in one variable (then skip if not the right one).
|
|
if (checkVarName && val.name != checkVarName)
|
|
continue;
|
|
|
|
// Prefix.
|
|
if (isFirstOutput)
|
|
{
|
|
output << "bool RES = ";
|
|
isFirstOutput = false;
|
|
}
|
|
else
|
|
output << "RES = RES && ";
|
|
|
|
// Generate actual comparison.
|
|
if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT)
|
|
output << "isOk(" << val.name << ", ref_" << val.name << ", 0.05);\n";
|
|
else
|
|
output << "isOk(" << nonFloatNamePrefix << val.name << ", ref_" << val.name << ");\n";
|
|
}
|
|
|
|
if (isFirstOutput)
|
|
output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case?
|
|
else
|
|
output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
|
|
}
|
|
|
|
static inline bool supportsFragmentHighp (glu::GLSLVersion version)
|
|
{
|
|
return version != glu::GLSL_VERSION_100_ES;
|
|
}
|
|
|
|
static string genFragmentShader (const ShaderCaseSpecification& spec)
|
|
{
|
|
ostringstream shader;
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion);
|
|
const bool customColorOut = usesInout;
|
|
const char* const fragIn = usesInout ? "in" : "varying";
|
|
const char* const prec = supportsFragmentHighp(spec.targetVersion) ? "highp" : "mediump";
|
|
|
|
shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
|
|
|
|
shader << "precision " << prec << " float;\n";
|
|
shader << "precision " << prec << " int;\n";
|
|
shader << "\n";
|
|
|
|
if (customColorOut)
|
|
{
|
|
shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
|
|
shader << "\n";
|
|
}
|
|
|
|
genCompareFunctions(shader, spec.values, true);
|
|
shader << "\n";
|
|
|
|
// Declarations (varying, reference for each output).
|
|
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.outputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const floatTypeStr = getDataTypeName(floatType);
|
|
const char* const refTypeStr = getDataTypeName(basicType);
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
shader << fragIn << " " << floatTypeStr << " " << val.name << ";\n";
|
|
else
|
|
shader << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n";
|
|
|
|
shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
|
|
}
|
|
|
|
shader << "\n";
|
|
shader << "void main()\n";
|
|
shader << "{\n";
|
|
|
|
shader << " ";
|
|
genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL);
|
|
|
|
shader << "}\n";
|
|
return shader.str();
|
|
}
|
|
|
|
// Specialize a shader for the vertex shader test case.
|
|
static string specializeVertexShader (const ShaderCaseSpecification& spec, const std::string& src, const vector<RequiredExtension>& extensions)
|
|
{
|
|
ostringstream decl;
|
|
ostringstream setup;
|
|
ostringstream output;
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion);
|
|
const char* const vtxIn = usesInout ? "in" : "attribute";
|
|
const char* const vtxOut = usesInout ? "out" : "varying";
|
|
|
|
// generated from "both" case
|
|
DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY);
|
|
|
|
// Output (write out position).
|
|
output << "gl_Position = dEQP_Position;\n";
|
|
|
|
// Declarations (position + attribute for each input, varying for each output).
|
|
decl << vtxIn << " highp vec4 dEQP_Position;\n";
|
|
|
|
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.inputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const floatTypeStr = getDataTypeName(floatType);
|
|
const char* const refTypeStr = getDataTypeName(basicType);
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
{
|
|
decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n";
|
|
}
|
|
else
|
|
{
|
|
decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
|
|
setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n";
|
|
}
|
|
}
|
|
|
|
// \todo [2015-07-24 pyry] Why are uniforms missing?
|
|
|
|
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.outputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const floatTypeStr = getDataTypeName(floatType);
|
|
const char* const refTypeStr = getDataTypeName(basicType);
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
decl << vtxOut << " " << floatTypeStr << " " << val.name << ";\n";
|
|
else
|
|
{
|
|
decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n";
|
|
decl << refTypeStr << " " << val.name << ";\n";
|
|
|
|
output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n";
|
|
}
|
|
}
|
|
|
|
// Shader specialization.
|
|
map<string, string> params;
|
|
params.insert(pair<string, string>("DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("SETUP", setup.str()));
|
|
params.insert(pair<string, string>("OUTPUT", output.str()));
|
|
params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position"));
|
|
|
|
StringTemplate tmpl (src);
|
|
const string baseSrc = tmpl.specialize(params);
|
|
const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_VERTEX);
|
|
|
|
return withExt;
|
|
}
|
|
|
|
// Specialize a shader for the fragment shader test case.
|
|
static string specializeFragmentShader (const ShaderCaseSpecification& spec, const std::string& src, const vector<RequiredExtension>& extensions)
|
|
{
|
|
ostringstream decl;
|
|
ostringstream setup;
|
|
ostringstream output;
|
|
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion);
|
|
const bool customColorOut = usesInout;
|
|
const char* const fragIn = usesInout ? "in" : "varying";
|
|
const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor";
|
|
|
|
// generated from "both" case
|
|
DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY);
|
|
|
|
genCompareFunctions(decl, spec.values, false);
|
|
genCompareOp(output, fragColor, spec.values, "", DE_NULL);
|
|
|
|
if (customColorOut)
|
|
decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
|
|
|
|
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.inputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const floatTypeStr = getDataTypeName(floatType);
|
|
const char* const refTypeStr = getDataTypeName(basicType);
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
decl << fragIn << " " << floatTypeStr << " " << val.name << ";\n";
|
|
else
|
|
{
|
|
decl << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n";
|
|
std::string offset = isDataTypeIntOrIVec(basicType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation
|
|
setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(v_" << val.name << offset << ");\n";
|
|
}
|
|
}
|
|
|
|
// \todo [2015-07-24 pyry] Why are uniforms missing?
|
|
|
|
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
|
|
{
|
|
const Value& val = spec.values.outputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const char* const refTypeStr = getDataTypeName(basicType);
|
|
|
|
decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
|
|
decl << refTypeStr << " " << val.name << ";\n";
|
|
}
|
|
|
|
/* \todo [2010-04-01 petri] Check all outputs. */
|
|
|
|
// Shader specialization.
|
|
map<string, string> params;
|
|
params.insert(pair<string, string>("DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("SETUP", setup.str()));
|
|
params.insert(pair<string, string>("OUTPUT", output.str()));
|
|
params.insert(pair<string, string>("POSITION_FRAG_COLOR", fragColor));
|
|
|
|
StringTemplate tmpl (src);
|
|
const string baseSrc = tmpl.specialize(params);
|
|
const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_FRAGMENT);
|
|
|
|
return withExt;
|
|
}
|
|
|
|
static void generateUniformDeclarations (std::ostream& dst, const ValueBlock& valueBlock)
|
|
{
|
|
for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++)
|
|
{
|
|
const Value& val = valueBlock.uniforms[ndx];
|
|
const char* const typeStr = getDataTypeName(val.type.getBasicType());
|
|
|
|
if (val.name.find('.') == string::npos)
|
|
dst << "uniform " << typeStr << " " << val.name << ";\n";
|
|
}
|
|
}
|
|
|
|
static map<string, string> generateVertexSpecialization (const ProgramSpecializationParams& specParams)
|
|
{
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion);
|
|
const char* vtxIn = usesInout ? "in" : "attribute";
|
|
ostringstream decl;
|
|
ostringstream setup;
|
|
map<string, string> params;
|
|
|
|
decl << vtxIn << " highp vec4 dEQP_Position;\n";
|
|
|
|
for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++)
|
|
{
|
|
const Value& val = specParams.caseSpec.values.inputs[ndx];
|
|
const DataType basicType = val.type.getBasicType();
|
|
const char* const typeStr = getDataTypeName(val.type.getBasicType());
|
|
|
|
if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
|
|
{
|
|
decl << vtxIn << " " << typeStr << " " << val.name << ";\n";
|
|
}
|
|
else
|
|
{
|
|
const DataType floatType = getDataTypeFloatScalars(basicType);
|
|
const char* const floatTypeStr = getDataTypeName(floatType);
|
|
|
|
decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
|
|
setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n";
|
|
}
|
|
}
|
|
|
|
generateUniformDeclarations(decl, specParams.caseSpec.values);
|
|
|
|
params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("VERTEX_SETUP", setup.str()));
|
|
params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n")));
|
|
|
|
return params;
|
|
}
|
|
|
|
static map<string, string> generateFragmentSpecialization (const ProgramSpecializationParams& specParams)
|
|
{
|
|
const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion);
|
|
const bool customColorOut = usesInout;
|
|
const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor";
|
|
ostringstream decl;
|
|
ostringstream output;
|
|
map<string, string> params;
|
|
|
|
genCompareFunctions(decl, specParams.caseSpec.values, false);
|
|
genCompareOp(output, fragColor, specParams.caseSpec.values, "", DE_NULL);
|
|
|
|
if (customColorOut)
|
|
decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
|
|
|
|
for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++)
|
|
{
|
|
const Value& val = specParams.caseSpec.values.outputs[ndx];
|
|
const char* const refTypeStr = getDataTypeName(val.type.getBasicType());
|
|
|
|
decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
|
|
decl << refTypeStr << " " << val.name << ";\n";
|
|
}
|
|
|
|
generateUniformDeclarations(decl, specParams.caseSpec.values);
|
|
|
|
params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str()));
|
|
params.insert(pair<string, string>("FRAG_COLOR", fragColor));
|
|
|
|
return params;
|
|
}
|
|
|
|
static map<string, string> generateGeometrySpecialization (const ProgramSpecializationParams& specParams)
|
|
{
|
|
ostringstream decl;
|
|
map<string, string> params;
|
|
|
|
decl << "layout (triangles) in;\n";
|
|
decl << "layout (triangle_strip, max_vertices=3) out;\n";
|
|
decl << "\n";
|
|
|
|
generateUniformDeclarations(decl, specParams.caseSpec.values);
|
|
|
|
params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str()));
|
|
|
|
return params;
|
|
}
|
|
|
|
static map<string, string> generateTessControlSpecialization (const ProgramSpecializationParams& specParams)
|
|
{
|
|
ostringstream decl;
|
|
ostringstream output;
|
|
map<string, string> params;
|
|
|
|
decl << "layout (vertices=3) out;\n";
|
|
decl << "\n";
|
|
|
|
generateUniformDeclarations(decl, specParams.caseSpec.values);
|
|
|
|
output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
|
|
"gl_TessLevelInner[0] = 2.0;\n"
|
|
"gl_TessLevelInner[1] = 2.0;\n"
|
|
"gl_TessLevelOuter[0] = 2.0;\n"
|
|
"gl_TessLevelOuter[1] = 2.0;\n"
|
|
"gl_TessLevelOuter[2] = 2.0;\n"
|
|
"gl_TessLevelOuter[3] = 2.0;";
|
|
|
|
params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str()));
|
|
params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
|
|
|
|
return params;
|
|
}
|
|
|
|
static map<string, string> generateTessEvalSpecialization (const ProgramSpecializationParams& specParams)
|
|
{
|
|
ostringstream decl;
|
|
ostringstream output;
|
|
map<string, string> params;
|
|
|
|
decl << "layout (triangles) in;\n";
|
|
decl << "\n";
|
|
|
|
generateUniformDeclarations(decl, specParams.caseSpec.values);
|
|
|
|
output << "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n";
|
|
|
|
params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str()));
|
|
params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str()));
|
|
params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
|
|
|
|
return params;
|
|
}
|
|
|
|
static void specializeShaderSources (ProgramSources& dst,
|
|
const ProgramSources& src,
|
|
const ProgramSpecializationParams& specParams,
|
|
glu::ShaderType shaderType,
|
|
map<string, string> (*specializationGenerator) (const ProgramSpecializationParams& specParams))
|
|
{
|
|
if (!src.sources[shaderType].empty())
|
|
{
|
|
const map<string, string> tmplParams = specializationGenerator(specParams);
|
|
|
|
for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx)
|
|
{
|
|
const StringTemplate tmpl (src.sources[shaderType][ndx]);
|
|
const std::string baseGLSLCode = tmpl.specialize(tmplParams);
|
|
const std::string sourceWithExts = injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType);
|
|
|
|
dst << glu::ShaderSource(shaderType, sourceWithExts);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void specializeProgramSources (glu::ProgramSources& dst,
|
|
const glu::ProgramSources& src,
|
|
const ProgramSpecializationParams& specParams)
|
|
{
|
|
specializeShaderSources(dst, src, specParams, SHADERTYPE_VERTEX, generateVertexSpecialization);
|
|
specializeShaderSources(dst, src, specParams, SHADERTYPE_FRAGMENT, generateFragmentSpecialization);
|
|
specializeShaderSources(dst, src, specParams, SHADERTYPE_GEOMETRY, generateGeometrySpecialization);
|
|
specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_CONTROL, generateTessControlSpecialization);
|
|
specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_EVALUATION, generateTessEvalSpecialization);
|
|
|
|
dst << ProgramSeparable(src.separable);
|
|
}
|
|
|
|
enum
|
|
{
|
|
VIEWPORT_WIDTH = 128,
|
|
VIEWPORT_HEIGHT = 128
|
|
};
|
|
|
|
class BeforeDrawValidator : public glu::DrawUtilCallback
|
|
{
|
|
public:
|
|
enum TargetType
|
|
{
|
|
TARGETTYPE_PROGRAM = 0,
|
|
TARGETTYPE_PIPELINE,
|
|
|
|
TARGETTYPE_LAST
|
|
};
|
|
|
|
BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType);
|
|
|
|
void beforeDrawCall (void);
|
|
|
|
const std::string& getInfoLog (void) const;
|
|
glw::GLint getValidateStatus (void) const;
|
|
|
|
private:
|
|
const glw::Functions& m_gl;
|
|
const glw::GLuint m_target;
|
|
const TargetType m_targetType;
|
|
|
|
glw::GLint m_validateStatus;
|
|
std::string m_logMessage;
|
|
};
|
|
|
|
BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType)
|
|
: m_gl (gl)
|
|
, m_target (target)
|
|
, m_targetType (targetType)
|
|
, m_validateStatus (-1)
|
|
{
|
|
DE_ASSERT(targetType < TARGETTYPE_LAST);
|
|
}
|
|
|
|
void BeforeDrawValidator::beforeDrawCall (void)
|
|
{
|
|
glw::GLint bytesWritten = 0;
|
|
glw::GLint infoLogLength;
|
|
std::vector<glw::GLchar> logBuffer;
|
|
int stringLength;
|
|
|
|
// validate
|
|
if (m_targetType == TARGETTYPE_PROGRAM)
|
|
m_gl.validateProgram(m_target);
|
|
else if (m_targetType == TARGETTYPE_PIPELINE)
|
|
m_gl.validateProgramPipeline(m_target);
|
|
else
|
|
DE_ASSERT(false);
|
|
|
|
GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate");
|
|
|
|
// check status
|
|
m_validateStatus = -1;
|
|
|
|
if (m_targetType == TARGETTYPE_PROGRAM)
|
|
m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus);
|
|
else if (m_targetType == TARGETTYPE_PIPELINE)
|
|
m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus);
|
|
else
|
|
DE_ASSERT(false);
|
|
|
|
GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status");
|
|
TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE);
|
|
|
|
// read log
|
|
|
|
infoLogLength = 0;
|
|
|
|
if (m_targetType == TARGETTYPE_PROGRAM)
|
|
m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
|
|
else if (m_targetType == TARGETTYPE_PIPELINE)
|
|
m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
|
|
else
|
|
DE_ASSERT(false);
|
|
|
|
GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length");
|
|
|
|
if (infoLogLength <= 0)
|
|
{
|
|
m_logMessage.clear();
|
|
return;
|
|
}
|
|
|
|
logBuffer.resize(infoLogLength + 2, '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger
|
|
|
|
if (m_targetType == TARGETTYPE_PROGRAM)
|
|
m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
|
|
else if (m_targetType == TARGETTYPE_PIPELINE)
|
|
m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
|
|
else
|
|
DE_ASSERT(false);
|
|
|
|
// just ignore bytesWritten to be safe, find the null terminator
|
|
stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin());
|
|
m_logMessage.assign(&logBuffer[0], stringLength);
|
|
}
|
|
|
|
const std::string& BeforeDrawValidator::getInfoLog (void) const
|
|
{
|
|
return m_logMessage;
|
|
}
|
|
|
|
glw::GLint BeforeDrawValidator::getValidateStatus (void) const
|
|
{
|
|
return m_validateStatus;
|
|
}
|
|
|
|
// ShaderCase.
|
|
|
|
ShaderLibraryCase::ShaderLibraryCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification)
|
|
: tcu::TestCase (testCtx, name, description)
|
|
, m_renderCtx (renderCtx)
|
|
, m_contextInfo (contextInfo)
|
|
, m_spec (specification)
|
|
{
|
|
}
|
|
|
|
ShaderLibraryCase::~ShaderLibraryCase (void)
|
|
{
|
|
}
|
|
|
|
void ShaderLibraryCase::init (void)
|
|
{
|
|
DE_ASSERT(isValid(m_spec));
|
|
|
|
if (!isGLSLVersionSupported(m_renderCtx.getType(), m_spec.targetVersion))
|
|
TCU_THROW(NotSupportedError, (string(getGLSLVersionName(m_spec.targetVersion)) + " is not supported").c_str());
|
|
|
|
checkImplementationLimits(m_spec.requiredCaps, m_contextInfo);
|
|
|
|
// log the expected result
|
|
switch (m_spec.expectResult)
|
|
{
|
|
case EXPECT_PASS:
|
|
// Don't write anything
|
|
break;
|
|
|
|
case EXPECT_COMPILE_FAIL:
|
|
m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." << tcu::TestLog::EndMessage;
|
|
break;
|
|
|
|
case EXPECT_LINK_FAIL:
|
|
m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage;
|
|
break;
|
|
|
|
case EXPECT_COMPILE_LINK_FAIL:
|
|
m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." << tcu::TestLog::EndMessage;
|
|
break;
|
|
|
|
case EXPECT_VALIDATION_FAIL:
|
|
m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." << tcu::TestLog::EndMessage;
|
|
break;
|
|
|
|
case EXPECT_BUILD_SUCCESSFUL:
|
|
m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation and program linking to succeed. Resulting program will not be executed." << tcu::TestLog::EndMessage;
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void setUniformValue (const glw::Functions& gl, const std::vector<deUint32>& pipelinePrograms, const std::string& name, const Value& val, int arrayNdx, tcu::TestLog& log)
|
|
{
|
|
bool foundAnyMatch = false;
|
|
|
|
for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx)
|
|
{
|
|
const DataType dataType = val.type.getBasicType();
|
|
const int scalarSize = getDataTypeScalarSize(dataType);
|
|
const int loc = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str());
|
|
const int elemNdx = arrayNdx * scalarSize;
|
|
|
|
DE_ASSERT(elemNdx+scalarSize <= (int)val.elements.size());
|
|
|
|
if (loc == -1)
|
|
continue;
|
|
|
|
foundAnyMatch = true;
|
|
|
|
DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat));
|
|
DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint));
|
|
|
|
gl.useProgram(pipelinePrograms[programNdx]);
|
|
|
|
switch (dataType)
|
|
{
|
|
case TYPE_FLOAT: gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_VEC2: gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_VEC3: gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_VEC4: gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT2: gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT3: gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT4: gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_INT: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_INT_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_INT_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_INT_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_BOOL: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_BOOL_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_BOOL_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_BOOL_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
|
|
case TYPE_UINT: gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
|
|
case TYPE_UINT_VEC2: gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
|
|
case TYPE_UINT_VEC3: gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
|
|
case TYPE_UINT_VEC4: gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
|
|
case TYPE_FLOAT_MAT2X3: gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT2X4: gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT3X2: gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT3X4: gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT4X2: gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
case TYPE_FLOAT_MAT4X3: gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
|
|
|
|
case TYPE_SAMPLER_2D:
|
|
case TYPE_SAMPLER_CUBE:
|
|
DE_FATAL("implement!");
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
if (!foundAnyMatch)
|
|
log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage;
|
|
}
|
|
|
|
static bool isTessellationPresent (const ShaderCaseSpecification& spec)
|
|
{
|
|
if (spec.programs[0].sources.separable)
|
|
{
|
|
const deUint32 tessellationBits = (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) |
|
|
(1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
|
|
|
|
for (int programNdx = 0; programNdx < (int)spec.programs.size(); ++programNdx)
|
|
if (spec.programs[programNdx].activeStages & tessellationBits)
|
|
return true;
|
|
return false;
|
|
}
|
|
else
|
|
return !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() ||
|
|
!spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty();
|
|
}
|
|
|
|
static bool isTessellationSupported (const glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo)
|
|
{
|
|
if (renderCtx.getType().getProfile() == PROFILE_ES)
|
|
{
|
|
const int majorVer = renderCtx.getType().getMajorVersion();
|
|
const int minorVer = renderCtx.getType().getMinorVersion();
|
|
|
|
return (majorVer > 3) || (majorVer == 3 && minorVer >= 2) ||
|
|
ctxInfo.isExtensionSupported("GL_EXT_tessellation_shader");
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& surface)
|
|
{
|
|
bool allWhite = true;
|
|
bool allBlack = true;
|
|
bool anyUnexpected = false;
|
|
|
|
for (int y = 0; y < surface.getHeight(); y++)
|
|
{
|
|
for (int x = 0; x < surface.getWidth(); x++)
|
|
{
|
|
const tcu::IVec4 pixel = surface.getPixelInt(x, y);
|
|
// Note: we really do not want to involve alpha in the check comparison
|
|
// \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black.
|
|
const bool isWhite = (pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255);
|
|
const bool isBlack = (pixel[0] == 0) && (pixel[1] == 0) && (pixel[2] == 0);
|
|
|
|
allWhite = allWhite && isWhite;
|
|
allBlack = allBlack && isBlack;
|
|
anyUnexpected = anyUnexpected || (!isWhite && !isBlack);
|
|
}
|
|
}
|
|
|
|
if (!allWhite)
|
|
{
|
|
if (anyUnexpected)
|
|
log << TestLog::Message << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" << TestLog::EndMessage;
|
|
else if (!allBlack)
|
|
log << TestLog::Message << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" << TestLog::EndMessage;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ShaderLibraryCase::execute (void)
|
|
{
|
|
const float quadSize = 1.0f;
|
|
static const float s_positions[4*4] =
|
|
{
|
|
-quadSize, -quadSize, 0.0f, 1.0f,
|
|
-quadSize, +quadSize, 0.0f, 1.0f,
|
|
+quadSize, -quadSize, 0.0f, 1.0f,
|
|
+quadSize, +quadSize, 0.0f, 1.0f
|
|
};
|
|
|
|
static const deUint16 s_indices[2*3] =
|
|
{
|
|
0, 1, 2,
|
|
1, 3, 2
|
|
};
|
|
|
|
TestLog& log = m_testCtx.getLog();
|
|
const glw::Functions& gl = m_renderCtx.getFunctions();
|
|
|
|
// Compute viewport.
|
|
const tcu::RenderTarget& renderTarget = m_renderCtx.getRenderTarget();
|
|
de::Random rnd (deStringHash(getName()));
|
|
const int width = deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH);
|
|
const int height = deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT);
|
|
const int viewportX = rnd.getInt(0, renderTarget.getWidth() - width);
|
|
const int viewportY = rnd.getInt(0, renderTarget.getHeight() - height);
|
|
const int numVerticesPerDraw = 4;
|
|
const bool tessellationPresent = isTessellationPresent(m_spec);
|
|
const bool separablePrograms = m_spec.programs[0].sources.separable;
|
|
|
|
bool allCompilesOk = true;
|
|
bool allLinksOk = true;
|
|
const char* failReason = DE_NULL;
|
|
|
|
vector<ProgramSources> specializedSources (m_spec.programs.size());
|
|
|
|
deUint32 vertexProgramID = -1;
|
|
vector<deUint32> pipelineProgramIDs;
|
|
vector<SharedPtr<ShaderProgram> > programs;
|
|
SharedPtr<ProgramPipeline> programPipeline;
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start");
|
|
|
|
if(isCapabilityRequired(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT, m_spec) && glu::IsES3Compatible(gl))
|
|
return true;
|
|
|
|
if(isCapabilityRequired(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER, m_spec))
|
|
{
|
|
// on unextended ES2 there is only one draw buffer
|
|
// and there is no GL_MAX_DRAW_BUFFERS query
|
|
glw::GLint maxDrawBuffers = 0;
|
|
gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
|
if ((gl.getError() == GL_NO_ERROR) && (maxDrawBuffers > 1))
|
|
throw tcu::NotSupportedError("Test requires exactly one draw buffer");
|
|
}
|
|
|
|
// Specialize shaders
|
|
if (m_spec.caseType == CASETYPE_VERTEX_ONLY)
|
|
{
|
|
const vector<RequiredExtension> reqExt = checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo);
|
|
|
|
DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX].size() == 1);
|
|
specializedSources[0] << glu::VertexSource(specializeVertexShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX][0], reqExt))
|
|
<< glu::FragmentSource(genFragmentShader(m_spec));
|
|
}
|
|
else if (m_spec.caseType == CASETYPE_FRAGMENT_ONLY)
|
|
{
|
|
const vector<RequiredExtension> reqExt = checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo);
|
|
|
|
DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].size() == 1);
|
|
specializedSources[0] << glu::VertexSource(genVertexShader(m_spec))
|
|
<< glu::FragmentSource(specializeFragmentShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT][0], reqExt));
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE);
|
|
|
|
const int maxPatchVertices = isTessellationPresent(m_spec) && isTessellationSupported(m_renderCtx, m_contextInfo)
|
|
? m_contextInfo.getInt(GL_MAX_PATCH_VERTICES) : 0;
|
|
|
|
for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++)
|
|
{
|
|
const ProgramSpecializationParams progSpecParams (m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), maxPatchVertices);
|
|
|
|
specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams);
|
|
}
|
|
}
|
|
|
|
if (!separablePrograms)
|
|
{
|
|
de::SharedPtr<glu::ShaderProgram> program (new glu::ShaderProgram(m_renderCtx, specializedSources[0]));
|
|
|
|
vertexProgramID = program->getProgram();
|
|
pipelineProgramIDs.push_back(program->getProgram());
|
|
programs.push_back(program);
|
|
|
|
// Check that compile/link results are what we expect.
|
|
|
|
DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
|
|
for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
|
|
if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
|
|
allCompilesOk = false;
|
|
|
|
if (!program->getProgramInfo().linkOk)
|
|
allLinksOk = false;
|
|
|
|
log << *program;
|
|
}
|
|
else
|
|
{
|
|
// Separate programs
|
|
for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx)
|
|
{
|
|
de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx]));
|
|
|
|
if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX))
|
|
vertexProgramID = program->getProgram();
|
|
|
|
pipelineProgramIDs.push_back(program->getProgram());
|
|
programs.push_back(program);
|
|
|
|
// Check that compile/link results are what we expect.
|
|
|
|
DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
|
|
for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
|
|
if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
|
|
allCompilesOk = false;
|
|
|
|
if (!program->getProgramInfo().linkOk)
|
|
allLinksOk = false;
|
|
|
|
// Log program and active stages
|
|
{
|
|
const tcu::ScopedLogSection section (log, "Program", "Program " + de::toString(programNdx+1));
|
|
tcu::MessageBuilder builder (&log);
|
|
bool firstStage = true;
|
|
|
|
builder << "Pipeline uses stages: ";
|
|
for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
|
|
{
|
|
if (m_spec.programs[programNdx].activeStages & (1u << stage))
|
|
{
|
|
if (!firstStage)
|
|
builder << ", ";
|
|
builder << glu::getShaderTypeName((glu::ShaderType)stage);
|
|
firstStage = true;
|
|
}
|
|
}
|
|
builder << tcu::TestLog::EndMessage;
|
|
|
|
log << *program;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (m_spec.expectResult)
|
|
{
|
|
case EXPECT_PASS:
|
|
case EXPECT_VALIDATION_FAIL:
|
|
case EXPECT_BUILD_SUCCESSFUL:
|
|
if (!allCompilesOk)
|
|
failReason = "expected shaders to compile and link properly, but failed to compile.";
|
|
else if (!allLinksOk)
|
|
failReason = "expected shaders to compile and link properly, but failed to link.";
|
|
break;
|
|
|
|
case EXPECT_COMPILE_FAIL:
|
|
if (allCompilesOk && !allLinksOk)
|
|
failReason = "expected compilation to fail, but shaders compiled and link failed.";
|
|
else if (allCompilesOk)
|
|
failReason = "expected compilation to fail, but shaders compiled correctly.";
|
|
break;
|
|
|
|
case EXPECT_LINK_FAIL:
|
|
if (!allCompilesOk)
|
|
failReason = "expected linking to fail, but unable to compile.";
|
|
else if (allLinksOk)
|
|
failReason = "expected linking to fail, but passed.";
|
|
break;
|
|
|
|
case EXPECT_COMPILE_LINK_FAIL:
|
|
if (allCompilesOk && allLinksOk)
|
|
failReason = "expected compile or link to fail, but passed.";
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
if (failReason != DE_NULL)
|
|
{
|
|
// \todo [2010-06-07 petri] These should be handled in the test case?
|
|
log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
|
|
|
|
if (isCapabilityRequired(CAPABILITY_FULL_GLSL_ES_100_SUPPORT, m_spec))
|
|
{
|
|
log << TestLog::Message
|
|
<< "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required."
|
|
<< TestLog::EndMessage;
|
|
|
|
if (allCompilesOk && !allLinksOk)
|
|
{
|
|
// Used features are detectable at compile time. If implementation parses shader
|
|
// at link time, report it as quality warning.
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
|
|
}
|
|
else
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported");
|
|
}
|
|
else if (m_spec.expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk)
|
|
{
|
|
// If implementation parses shader at link time, report it as quality warning.
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
|
|
}
|
|
else
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason);
|
|
return false;
|
|
}
|
|
|
|
// Return if shader is not intended to be run
|
|
if (m_spec.expectResult == EXPECT_COMPILE_FAIL ||
|
|
m_spec.expectResult == EXPECT_COMPILE_LINK_FAIL ||
|
|
m_spec.expectResult == EXPECT_LINK_FAIL ||
|
|
m_spec.expectResult == EXPECT_BUILD_SUCCESSFUL)
|
|
return true;
|
|
|
|
// Setup viewport.
|
|
gl.viewport(viewportX, viewportY, width, height);
|
|
|
|
if (separablePrograms)
|
|
{
|
|
programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx));
|
|
|
|
// Setup pipeline
|
|
gl.bindProgramPipeline(programPipeline->getPipeline());
|
|
for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx)
|
|
{
|
|
deUint32 shaderFlags = 0;
|
|
for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
|
|
if (m_spec.programs[programNdx].activeStages & (1u << stage))
|
|
shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage);
|
|
|
|
programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]);
|
|
}
|
|
|
|
programPipeline->activeShaderProgram(vertexProgramID);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline");
|
|
}
|
|
else
|
|
{
|
|
// Start using program
|
|
gl.useProgram(vertexProgramID);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
|
|
}
|
|
|
|
// Fetch location for positions positions.
|
|
int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position");
|
|
if (positionLoc == -1)
|
|
{
|
|
string errStr = string("no location found for attribute 'dEQP_Position'");
|
|
TCU_FAIL(errStr.c_str());
|
|
}
|
|
|
|
// Iterate all value blocks.
|
|
{
|
|
const ValueBlock& valueBlock = m_spec.values;
|
|
|
|
// always render at least one pass even if there is no input/output data
|
|
const int numRenderPasses = valueBlock.outputs.empty() ? 1 : (int)valueBlock.outputs[0].elements.size() / valueBlock.outputs[0].type.getScalarSize();
|
|
|
|
// Iterate all array sub-cases.
|
|
for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++)
|
|
{
|
|
vector<VertexArrayBinding> vertexArrays;
|
|
int attribValueNdx = 0;
|
|
vector<vector<float> > attribValues (valueBlock.inputs.size());
|
|
glw::GLenum postDrawError;
|
|
BeforeDrawValidator beforeDrawValidator (gl,
|
|
(separablePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID),
|
|
(separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM));
|
|
|
|
vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0]));
|
|
|
|
// Collect VA pointer for inputs
|
|
for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); valNdx++)
|
|
{
|
|
const Value& val = valueBlock.inputs[valNdx];
|
|
const char* const valueName = val.name.c_str();
|
|
const DataType dataType = val.type.getBasicType();
|
|
const int scalarSize = getDataTypeScalarSize(dataType);
|
|
|
|
// Replicate values four times.
|
|
std::vector<float>& scalars = attribValues[attribValueNdx++];
|
|
scalars.resize(numVerticesPerDraw * scalarSize);
|
|
if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType))
|
|
{
|
|
for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
|
|
for (int ndx = 0; ndx < scalarSize; ndx++)
|
|
scalars[repNdx*scalarSize + ndx] = val.elements[arrayNdx*scalarSize + ndx].float32;
|
|
}
|
|
else
|
|
{
|
|
// convert to floats.
|
|
for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
|
|
{
|
|
for (int ndx = 0; ndx < scalarSize; ndx++)
|
|
{
|
|
float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32;
|
|
DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v);
|
|
scalars[repNdx*scalarSize + ndx] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attribute name prefix.
|
|
string attribPrefix = "";
|
|
// \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)?
|
|
if ((m_spec.caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT))
|
|
attribPrefix = "a_";
|
|
|
|
// Input always given as attribute.
|
|
string attribName = attribPrefix + valueName;
|
|
int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str());
|
|
if (attribLoc == -1)
|
|
{
|
|
log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage;
|
|
continue;
|
|
}
|
|
|
|
if (isDataTypeMatrix(dataType))
|
|
{
|
|
int numCols = getDataTypeMatrixNumColumns(dataType);
|
|
int numRows = getDataTypeMatrixNumRows(dataType);
|
|
DE_ASSERT(scalarSize == numCols*numRows);
|
|
|
|
for (int i = 0; i < numCols; i++)
|
|
vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows]));
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType));
|
|
vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0]));
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array");
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms");
|
|
|
|
// set reference values for outputs.
|
|
for (size_t valNdx = 0; valNdx < valueBlock.outputs.size(); valNdx++)
|
|
{
|
|
const Value& val = valueBlock.outputs[valNdx];
|
|
const char* const valueName = val.name.c_str();
|
|
|
|
// Set reference value.
|
|
string refName = string("ref_") + valueName;
|
|
setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog());
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms");
|
|
}
|
|
|
|
// set uniform values
|
|
for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++)
|
|
{
|
|
const Value& val = valueBlock.uniforms[valNdx];
|
|
const char* const valueName = val.name.c_str();
|
|
|
|
setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog());
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
|
|
}
|
|
|
|
// Clear.
|
|
gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
|
|
gl.clear(GL_COLOR_BUFFER_BIT);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
|
|
|
|
// Use program or pipeline
|
|
if (separablePrograms)
|
|
gl.useProgram(0);
|
|
else
|
|
gl.useProgram(vertexProgramID);
|
|
|
|
// Draw.
|
|
if (tessellationPresent)
|
|
{
|
|
gl.patchParameteri(GL_PATCH_VERTICES, 3);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)");
|
|
}
|
|
|
|
draw(m_renderCtx,
|
|
vertexProgramID,
|
|
(int)vertexArrays.size(),
|
|
&vertexArrays[0],
|
|
(tessellationPresent) ?
|
|
(pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) :
|
|
(pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])),
|
|
(m_spec.expectResult == EXPECT_VALIDATION_FAIL) ?
|
|
(&beforeDrawValidator) :
|
|
(DE_NULL));
|
|
|
|
postDrawError = gl.getError();
|
|
|
|
if (m_spec.expectResult == EXPECT_PASS)
|
|
{
|
|
// Read back results.
|
|
Surface surface (width, height);
|
|
const float w = s_positions[3];
|
|
const int minY = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f);
|
|
const int maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f);
|
|
const int minX = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f);
|
|
const int maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f);
|
|
|
|
GLU_EXPECT_NO_ERROR(postDrawError, "draw");
|
|
|
|
glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
|
|
|
|
if (!checkPixels(log, tcu::getSubregion(surface.getAccess(), minX, minY, maxX-minX+1, maxY-minY+1)))
|
|
{
|
|
log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx+1 << " of " << numRenderPasses << "):"
|
|
<< TestLog::EndMessage;
|
|
|
|
log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
|
|
dumpValues(log, valueBlock, arrayNdx);
|
|
|
|
// Dump image on failure.
|
|
log << TestLog::Image("Result", "Rendered result image", surface);
|
|
|
|
gl.useProgram(0);
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
|
|
return false;
|
|
}
|
|
}
|
|
else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL)
|
|
{
|
|
log << TestLog::Message
|
|
<< "Draw call generated error: "
|
|
<< glu::getErrorStr(postDrawError) << " "
|
|
<< ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n"
|
|
<< "Validate status: "
|
|
<< glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " "
|
|
<< ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) << "\n"
|
|
<< "Info log: "
|
|
<< ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : (beforeDrawValidator.getInfoLog())) << "\n"
|
|
<< TestLog::EndMessage;
|
|
|
|
// test result
|
|
|
|
if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str());
|
|
return false;
|
|
}
|
|
|
|
if (beforeDrawValidator.getValidateStatus() == GL_TRUE)
|
|
{
|
|
if (postDrawError == GL_NO_ERROR)
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation and rendering succeeded");
|
|
else if (postDrawError == GL_INVALID_OPERATION)
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation succeeded (rendering failed as expected)");
|
|
else
|
|
DE_ASSERT(false);
|
|
return false;
|
|
}
|
|
else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but rendering succeeded (validation failed as expected)");
|
|
return false;
|
|
}
|
|
else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION)
|
|
{
|
|
// Validation does not depend on input values, no need to test all values
|
|
return true;
|
|
}
|
|
else
|
|
DE_ASSERT(false);
|
|
}
|
|
else
|
|
DE_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
gl.useProgram(0);
|
|
if (separablePrograms)
|
|
gl.bindProgramPipeline(0);
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
|
|
return true;
|
|
}
|
|
|
|
TestCase::IterateResult ShaderLibraryCase::iterate (void)
|
|
{
|
|
// Initialize state to pass.
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
|
|
|
bool executeOk = execute();
|
|
|
|
DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : m_testCtx.getTestResult() != QP_TEST_RESULT_PASS);
|
|
DE_UNREF(executeOk);
|
|
return TestCase::STOP;
|
|
}
|
|
|
|
} // gls
|
|
} // deqp
|