452 lines
17 KiB
C++
452 lines
17 KiB
C++
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES 3.1 Module
|
|
* -------------------------------------------------
|
|
*
|
|
* Copyright 2016 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 Negative Tessellation tests.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es31fNegativeTessellationTests.hpp"
|
|
#include "gluContextInfo.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "glwDefs.hpp"
|
|
#include "glwEnums.hpp"
|
|
#include "tcuStringTemplate.hpp"
|
|
|
|
namespace deqp
|
|
{
|
|
|
|
using std::string;
|
|
using std::map;
|
|
|
|
namespace gles31
|
|
{
|
|
namespace Functional
|
|
{
|
|
namespace NegativeTestShared
|
|
{
|
|
|
|
using tcu::TestLog;
|
|
using namespace glw;
|
|
|
|
static const char* vertexShaderSource = "${GLSL_VERSION_STRING}\n"
|
|
"${GLSL_PER_VERTEX_OUT}\n"
|
|
"void main (void)\n"
|
|
"{\n"
|
|
" gl_Position = vec4(0.0);\n"
|
|
"}\n";
|
|
|
|
static const char* fragmentShaderSource = "${GLSL_VERSION_STRING}\n"
|
|
"precision mediump float;\n"
|
|
"layout(location = 0) out mediump vec4 fragColor;\n"
|
|
"\n"
|
|
"void main (void)\n"
|
|
"{\n"
|
|
" fragColor = vec4(1.0);\n"
|
|
"}\n";
|
|
|
|
static const char* tessControlShaderSource = "${GLSL_VERSION_STRING}\n"
|
|
"${GLSL_TESS_EXTENSION_STRING}\n"
|
|
"${GLSL_PER_VERTEX_IN_ARR}\n"
|
|
"${GLSL_PER_VERTEX_OUT_ARR}\n"
|
|
"layout (vertices=3) out;\n"
|
|
"\n"
|
|
"void main()\n"
|
|
"{\n"
|
|
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
|
|
"}\n";
|
|
|
|
static const char* tessEvalShaderSource = "${GLSL_VERSION_STRING}\n"
|
|
"${GLSL_TESS_EXTENSION_STRING}\n"
|
|
"${GLSL_PER_VERTEX_IN_ARR}\n"
|
|
"${GLSL_PER_VERTEX_OUT}\n"
|
|
"layout(triangles) in;\n"
|
|
"\n"
|
|
"void main()\n"
|
|
"{\n"
|
|
" gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position;\n"
|
|
"}\n";
|
|
|
|
static void checkExtensionSupport (NegativeTestContext& ctx, const char* extName)
|
|
{
|
|
if (!ctx.getContextInfo().isExtensionSupported(extName))
|
|
throw tcu::NotSupportedError(string(extName) + " not supported");
|
|
}
|
|
|
|
static void checkTessellationSupport (NegativeTestContext& ctx)
|
|
{
|
|
if (glu::isContextTypeES(ctx.getRenderContext().getType()))
|
|
checkExtensionSupport(ctx, "GL_EXT_tessellation_shader");
|
|
}
|
|
|
|
// Helper for constructing tessellation pipeline sources.
|
|
static glu::ProgramSources makeTessPipelineSources (const std::string& vertexSrc, const std::string& fragmentSrc, const std::string& tessCtrlSrc, const std::string& tessEvalSrc)
|
|
{
|
|
glu::ProgramSources sources;
|
|
sources.sources[glu::SHADERTYPE_VERTEX].push_back(vertexSrc);
|
|
sources.sources[glu::SHADERTYPE_FRAGMENT].push_back(fragmentSrc);
|
|
|
|
if (!tessCtrlSrc.empty())
|
|
sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].push_back(tessCtrlSrc);
|
|
|
|
if (!tessEvalSrc.empty())
|
|
sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].push_back(tessEvalSrc);
|
|
|
|
return sources;
|
|
}
|
|
|
|
map<string, string> constructSpecializationMap(NegativeTestContext& ctx)
|
|
{
|
|
glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(ctx.getRenderContext().getType());
|
|
bool isES31 = (glslVersion == glu::GLSL_VERSION_310_ES);
|
|
string ext = isES31 ? "#extension GL_EXT_tessellation_shader : require" : "";
|
|
return
|
|
{
|
|
{ "GLSL_VERSION_STRING", getGLSLVersionDeclaration(glslVersion) },
|
|
{ "GLSL_TESS_EXTENSION_STRING", ext },
|
|
{ "GLSL_PER_VERTEX_OUT", "" }, // needed for GL4.5
|
|
{ "GLSL_PER_VERTEX_IN_ARR", "" },
|
|
{ "GLSL_PER_VERTEX_OUT_ARR", "" }
|
|
};
|
|
}
|
|
|
|
// Incomplete active tess shaders
|
|
void single_tessellation_stage (NegativeTestContext& ctx)
|
|
{
|
|
// this case does not apply to GL4.5
|
|
if (!glu::isContextTypeES(ctx.getRenderContext().getType()))
|
|
return;
|
|
|
|
const bool requireTES = !ctx.getContextInfo().isExtensionSupported("GL_NV_gpu_shader5");
|
|
map<string, string> args = constructSpecializationMap(ctx);
|
|
|
|
checkTessellationSupport(ctx);
|
|
|
|
{
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
tcu::StringTemplate(tessControlShaderSource).specialize(args),
|
|
"")); // missing tessEvalShaderSource
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
ctx.beginSection("A link error is generated if a non-separable program has a tessellation control shader but no tessellation evaluation shader, unless GL_NV_gpu_shader5 is supported.");
|
|
|
|
if (requireTES && program.isOk())
|
|
ctx.fail("Program was not expected to link");
|
|
else if (!requireTES && !program.isOk())
|
|
ctx.fail("Program was expected to link");
|
|
|
|
ctx.endSection();
|
|
}
|
|
|
|
{
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
tcu::StringTemplate(tessControlShaderSource).specialize(args),
|
|
"") // missing tessEvalShaderSource
|
|
<< glu::ProgramSeparable(true));
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
if (!program.isOk())
|
|
TCU_THROW(TestError, "failed to build program");
|
|
|
|
ctx.glUseProgram(program.getProgram());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if current program state has tessellation control shader but no tessellation evaluation shader, unless GL_NV_gpu_shader5 is supported.");
|
|
ctx.glDrawArrays(GL_PATCHES, 0, 3);
|
|
ctx.expectError(requireTES ? GL_INVALID_OPERATION : GL_NO_ERROR);
|
|
ctx.endSection();
|
|
|
|
ctx.glUseProgram(0);
|
|
}
|
|
|
|
{
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
"", // missing tessControlShaderSource
|
|
tcu::StringTemplate(tessEvalShaderSource).specialize(args)));
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
ctx.beginSection("A link error is generated if a non-separable program has a tessellation evaluation shader but no tessellation control shader.");
|
|
|
|
if (program.isOk())
|
|
ctx.fail("Program was not expected to link");
|
|
|
|
ctx.endSection();
|
|
}
|
|
|
|
{
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
"", // missing tessControlShaderSource
|
|
tcu::StringTemplate(tessEvalShaderSource).specialize(args))
|
|
<< glu::ProgramSeparable(true));
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
if (!program.isOk())
|
|
TCU_THROW(TestError, "failed to build program");
|
|
|
|
ctx.glUseProgram(program.getProgram());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if current program state has tessellation evaluation shader but no tessellation control shader.");
|
|
ctx.glDrawArrays(GL_PATCHES, 0, 3);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.glUseProgram(0);
|
|
}
|
|
}
|
|
|
|
// Complete active tess shaders invalid primitive mode
|
|
void invalid_primitive_mode (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
map<string, string> args = constructSpecializationMap(ctx);
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
tcu::StringTemplate(tessControlShaderSource).specialize(args),
|
|
tcu::StringTemplate(tessEvalShaderSource).specialize(args)));
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
ctx.glUseProgram(program.getProgram());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if tessellation is active and primitive mode is not GL_PATCHES.");
|
|
ctx.glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.glUseProgram(0);
|
|
}
|
|
|
|
void tessellation_not_active (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
const glw::GLenum tessErr = ctx.getContextInfo().isExtensionSupported("GL_NV_gpu_shader5") ? GL_NO_ERROR : GL_INVALID_OPERATION;
|
|
map<string, string> args = constructSpecializationMap(ctx);
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
"", // missing tessControlShaderSource
|
|
"")); // missing tessEvalShaderSource
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
GLuint vao = 0;
|
|
if (!glu::isContextTypeES(ctx.getRenderContext().getType()))
|
|
{
|
|
ctx.glGenVertexArrays(1, &vao);
|
|
ctx.glBindVertexArray(vao);
|
|
}
|
|
|
|
ctx.glUseProgram(program.getProgram());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if tessellation is not active and primitive mode is GL_PATCHES, unless GL_NV_gpu_shader5 is supported.");
|
|
ctx.glDrawArrays(GL_PATCHES, 0, 3);
|
|
ctx.expectError(tessErr);
|
|
ctx.endSection();
|
|
|
|
ctx.glUseProgram(0);
|
|
|
|
if (vao)
|
|
ctx.glDeleteVertexArrays(1, &vao);
|
|
}
|
|
|
|
void invalid_program_state (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
const glu::RenderContext& rc = ctx.getRenderContext();
|
|
map<string, string> args = constructSpecializationMap(ctx);
|
|
|
|
// for gl4.5 we need to add per vertex sections
|
|
if (!glu::isContextTypeES(rc.getType()))
|
|
{
|
|
args["GLSL_PER_VERTEX_OUT"] = "out gl_PerVertex { vec4 gl_Position; };\n";
|
|
args["GLSL_PER_VERTEX_IN_ARR"] = "in gl_PerVertex { vec4 gl_Position; } gl_in[];\n";
|
|
args["GLSL_PER_VERTEX_OUT_ARR"] = "out gl_PerVertex { vec4 gl_Position; } gl_out[];\n";
|
|
}
|
|
|
|
glu::FragmentSource frgSource(tcu::StringTemplate(fragmentShaderSource).specialize(args));
|
|
glu::TessellationControlSource tessCtrlSource(tcu::StringTemplate(tessControlShaderSource).specialize(args));
|
|
glu::TessellationEvaluationSource tessEvalSource(tcu::StringTemplate(tessEvalShaderSource).specialize(args));
|
|
|
|
glu::ProgramPipeline pipeline(rc);
|
|
|
|
glu::ShaderProgram fragProgram (rc, glu::ProgramSources() << glu::ProgramSeparable(true) << frgSource);
|
|
glu::ShaderProgram tessCtrlProgram (rc, glu::ProgramSources() << glu::ProgramSeparable(true) << tessCtrlSource);
|
|
glu::ShaderProgram tessEvalProgram (rc, glu::ProgramSources() << glu::ProgramSeparable(true) << tessEvalSource);
|
|
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << fragProgram << tessCtrlProgram << tessEvalProgram;
|
|
|
|
if (!fragProgram.isOk() || !tessCtrlProgram.isOk() || !tessEvalProgram.isOk())
|
|
throw tcu::TestError("failed to build program");
|
|
|
|
ctx.glBindProgramPipeline(pipeline.getPipeline());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.glUseProgramStages(pipeline.getPipeline(), GL_FRAGMENT_SHADER_BIT, fragProgram.getProgram());
|
|
ctx.glUseProgramStages(pipeline.getPipeline(), GL_TESS_CONTROL_SHADER_BIT, tessCtrlProgram.getProgram());
|
|
ctx.glUseProgramStages(pipeline.getPipeline(), GL_TESS_EVALUATION_SHADER_BIT, tessEvalProgram.getProgram());
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if tessellation is active and vertex shader is missing.");
|
|
ctx.glDrawArrays(GL_PATCHES, 0, 3);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.glBindProgramPipeline(0);
|
|
ctx.expectError(GL_NO_ERROR);
|
|
}
|
|
|
|
void tessellation_control_invalid_vertex_count (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
const char* const tessControlVertLimitSource = "${GLSL_VERSION_STRING}\n"
|
|
"${GLSL_TESS_EXTENSION_STRING}\n"
|
|
"layout (vertices=${GL_MAX_PATCH_LIMIT}) out;\n"
|
|
"void main()\n"
|
|
"{\n"
|
|
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
|
|
"}\n";
|
|
|
|
map<string, string> args = constructSpecializationMap(ctx);
|
|
int maxPatchVertices= 0;
|
|
|
|
ctx.beginSection("Output vertex count exceeds GL_MAX_PATCH_VERTICES.");
|
|
ctx.glGetIntegerv(GL_MAX_PATCH_VERTICES, &maxPatchVertices);
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
std::ostringstream oss;
|
|
oss << (maxPatchVertices + 1);
|
|
args["GL_MAX_PATCH_LIMIT"] = oss.str();
|
|
|
|
|
|
glu::ShaderProgram program(ctx.getRenderContext(),
|
|
makeTessPipelineSources(tcu::StringTemplate(vertexShaderSource).specialize(args),
|
|
tcu::StringTemplate(fragmentShaderSource).specialize(args),
|
|
tcu::StringTemplate(tessControlVertLimitSource).specialize(args),
|
|
tcu::StringTemplate(tessEvalShaderSource).specialize(args)));
|
|
tcu::TestLog& log = ctx.getLog();
|
|
log << program;
|
|
|
|
bool testFailed = program.getProgramInfo().linkOk;
|
|
|
|
if (testFailed)
|
|
ctx.fail("Program was not expected to link");
|
|
|
|
ctx.endSection();
|
|
}
|
|
|
|
void invalid_get_programiv (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
GLuint program = ctx.glCreateProgram();
|
|
GLint params[1] = { 0 };
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if GL_TESS_CONTROL_OUTPUT_VERTICES is queried for a program which has not been linked properly.");
|
|
ctx.glGetProgramiv(program, GL_TESS_CONTROL_OUTPUT_VERTICES, ¶ms[0]);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if GL_TESS_GEN_MODE is queried for a program which has not been linked properly.");
|
|
ctx.glGetProgramiv(program, GL_TESS_GEN_MODE, ¶ms[0]);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if GL_TESS_GEN_SPACING is queried for a program which has not been linked properly.");
|
|
ctx.glGetProgramiv(program, GL_TESS_GEN_SPACING, ¶ms[0]);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if GL_TESS_GEN_VERTEX_ORDER is queried for a program which has not been linked properly.");
|
|
ctx.glGetProgramiv(program, GL_TESS_GEN_VERTEX_ORDER, ¶ms[0]);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.beginSection("GL_INVALID_OPERATION is generated if GL_TESS_GEN_POINT_MODE is queried for a program which has not been linked properly.");
|
|
ctx.glGetProgramiv(program, GL_TESS_GEN_POINT_MODE, ¶ms[0]);
|
|
ctx.expectError(GL_INVALID_OPERATION);
|
|
ctx.endSection();
|
|
|
|
ctx.glDeleteProgram(program);
|
|
}
|
|
|
|
void invalid_patch_parameteri (NegativeTestContext& ctx)
|
|
{
|
|
checkTessellationSupport(ctx);
|
|
|
|
ctx.beginSection("GL_INVALID_ENUM is generated if pname is not GL_PATCH_VERTICES.");
|
|
ctx.glPatchParameteri(-1, 1);
|
|
ctx.expectError(GL_INVALID_ENUM);
|
|
ctx.endSection();
|
|
|
|
ctx.beginSection("GL_INVALID_VALUE is generated if value is less than or equal to zero.");
|
|
ctx.glPatchParameteri(GL_PATCH_VERTICES, 0);
|
|
ctx.expectError(GL_INVALID_VALUE);
|
|
ctx.endSection();
|
|
|
|
int maxPatchVertices= 0;
|
|
ctx.glGetIntegerv(GL_MAX_PATCH_VERTICES, &maxPatchVertices);
|
|
ctx.expectError(GL_NO_ERROR);
|
|
|
|
ctx.beginSection("GL_INVALID_VALUE is generated if value is greater than GL_MAX_PATCH_VERTICES.");
|
|
ctx.glPatchParameteri(GL_PATCH_VERTICES, maxPatchVertices + 1);
|
|
ctx.expectError(GL_INVALID_VALUE);
|
|
ctx.endSection();
|
|
}
|
|
|
|
std::vector<FunctionContainer> getNegativeTessellationTestFunctions (void)
|
|
{
|
|
const FunctionContainer funcs[] =
|
|
{
|
|
{ single_tessellation_stage, "single_tessellation_stage", "Invalid program state with single tessellation stage" },
|
|
{ invalid_primitive_mode, "invalid_primitive_mode", "Invalid primitive mode when tessellation is active" },
|
|
{ tessellation_not_active, "tessellation_not_active", "Use of GL_PATCHES when tessellation is not active" },
|
|
{ invalid_program_state, "invalid_program_state", "Invalid program state when tessellation active but no vertex shader present" },
|
|
{ invalid_get_programiv, "get_programiv", "Invalid glGetProgramiv() usage" },
|
|
{ invalid_patch_parameteri, "invalid_program_queries", "Invalid glPatchParameteri() usage" },
|
|
{ tessellation_control_invalid_vertex_count, "tessellation_control_invalid_vertex_count", "Exceed vertex count limit in tessellation control shader" },
|
|
};
|
|
|
|
return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
|
|
}
|
|
|
|
} // NegativeTestShared
|
|
} // Functional
|
|
} // gles31
|
|
} // deqp
|