mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Feat] (Program): report the geometry and tessellation link properties glGetProgramiv had no source for
This commit is contained in:
@@ -747,9 +747,71 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = programObject->GetSeparable() ? GL_TRUE : GL_FALSE;
|
*params = programObject->GetSeparable() ? GL_TRUE : GL_FALSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// The geometry and tessellation link properties (GL 4.6 core table 23.35). Same shape as
|
||||||
|
// GL_COMPUTE_WORK_GROUP_SIZE above, and for the same reason: "a linked program object
|
||||||
|
// with a geometry shader" is one whose EXECUTABLE has the stage, so an
|
||||||
|
// attached-but-not-yet-linked shader must give INVALID_OPERATION rather than the previous
|
||||||
|
// link's value. The geometry three used to be listed here only to fall through into the
|
||||||
|
// INVALID_ENUM default, and the tessellation five were not listed at all.
|
||||||
case GL_GEOMETRY_VERTICES_OUT:
|
case GL_GEOMETRY_VERTICES_OUT:
|
||||||
case GL_GEOMETRY_INPUT_TYPE:
|
case GL_GEOMETRY_INPUT_TYPE:
|
||||||
case GL_GEOMETRY_OUTPUT_TYPE:
|
case GL_GEOMETRY_OUTPUT_TYPE:
|
||||||
|
case GL_GEOMETRY_SHADER_INVOCATIONS: {
|
||||||
|
if (!programObject->GetLinkStatus() || !programObject->HasLinkedShaderStage(ShaderStage::Geometry)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(program) +
|
||||||
|
" is not a linked program object with a geometry shader."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (pname) {
|
||||||
|
case GL_GEOMETRY_VERTICES_OUT: *params = programObject->GetGeometryVerticesOut(); break;
|
||||||
|
case GL_GEOMETRY_INPUT_TYPE: *params = static_cast<GLint>(programObject->GetGeometryInputType()); break;
|
||||||
|
case GL_GEOMETRY_OUTPUT_TYPE: *params = static_cast<GLint>(programObject->GetGeometryOutputType()); break;
|
||||||
|
default: *params = programObject->GetGeometryShaderInvocations(); break;
|
||||||
|
}
|
||||||
|
MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_TESS_CONTROL_OUTPUT_VERTICES: {
|
||||||
|
if (!programObject->GetLinkStatus() || !programObject->HasLinkedShaderStage(ShaderStage::TessControl)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(program) +
|
||||||
|
" is not a linked program object with a tessellation control shader."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*params = programObject->GetTessControlOutputVertices();
|
||||||
|
MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_TESS_GEN_MODE:
|
||||||
|
case GL_TESS_GEN_SPACING:
|
||||||
|
case GL_TESS_GEN_VERTEX_ORDER:
|
||||||
|
case GL_TESS_GEN_POINT_MODE: {
|
||||||
|
if (!programObject->GetLinkStatus() || !programObject->HasLinkedShaderStage(ShaderStage::TessEval)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(program) +
|
||||||
|
" is not a linked program object with a tessellation evaluation shader."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (pname) {
|
||||||
|
case GL_TESS_GEN_MODE: *params = static_cast<GLint>(programObject->GetTessGenMode()); break;
|
||||||
|
case GL_TESS_GEN_SPACING: *params = static_cast<GLint>(programObject->GetTessGenSpacing()); break;
|
||||||
|
case GL_TESS_GEN_VERTEX_ORDER:
|
||||||
|
*params = static_cast<GLint>(programObject->GetTessGenVertexOrder());
|
||||||
|
break;
|
||||||
|
default: *params = programObject->GetTessGenPointMode() ? GL_TRUE : GL_FALSE; break;
|
||||||
|
}
|
||||||
|
MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
MGLOG_D("%s: %s", __func__, MG_Util::ConvertGLEnumToString(pname).c_str());
|
MGLOG_D("%s: %s", __func__, MG_Util::ConvertGLEnumToString(pname).c_str());
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
|
|||||||
@@ -670,9 +670,14 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL_GEOMETRY_INPUT_TYPE. A draw's primitive type has to be compatible with it
|
// The geometry stage's link properties. GL_GEOMETRY_INPUT_TYPE is load-bearing beyond the
|
||||||
// (GL 4.6 core 11.3.1), so it is resolved for every link, not only a capturing one.
|
// query surface - a draw's primitive type has to be compatible with it (GL 4.6 core
|
||||||
|
// 11.3.1) - so this block runs for every link, not only a capturing one. The other three
|
||||||
|
// are pure glGetProgramiv answers that previously had no source at all.
|
||||||
artifacts.gsInputPrimitive = GL_NONE;
|
artifacts.gsInputPrimitive = GL_NONE;
|
||||||
|
artifacts.gsOutputPrimitive = GL_NONE;
|
||||||
|
artifacts.gsMaxVertices = 0;
|
||||||
|
artifacts.gsInvocations = 0;
|
||||||
if (const glslang::TIntermediate* gs = artifacts.program->getIntermediate(EShLangGeometry)) {
|
if (const glslang::TIntermediate* gs = artifacts.program->getIntermediate(EShLangGeometry)) {
|
||||||
switch (gs->getInputPrimitive()) {
|
switch (gs->getInputPrimitive()) {
|
||||||
case glslang::ElgPoints: artifacts.gsInputPrimitive = GL_POINTS; break;
|
case glslang::ElgPoints: artifacts.gsInputPrimitive = GL_POINTS; break;
|
||||||
@@ -682,6 +687,45 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
case glslang::ElgTrianglesAdjacency: artifacts.gsInputPrimitive = GL_TRIANGLES_ADJACENCY; break;
|
case glslang::ElgTrianglesAdjacency: artifacts.gsInputPrimitive = GL_TRIANGLES_ADJACENCY; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
switch (gs->getOutputPrimitive()) {
|
||||||
|
case glslang::ElgPoints: artifacts.gsOutputPrimitive = GL_POINTS; break;
|
||||||
|
case glslang::ElgLineStrip: artifacts.gsOutputPrimitive = GL_LINE_STRIP; break;
|
||||||
|
case glslang::ElgTriangleStrip: artifacts.gsOutputPrimitive = GL_TRIANGLE_STRIP; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
// glslang leaves both at TQualifier::layoutNotSet (-1) when the shader declared no
|
||||||
|
// such layout, and `invocations` defaults to one per GLSL 4.60 4.4.2.2 - so clamp
|
||||||
|
// rather than forward, or GL_GEOMETRY_SHADER_INVOCATIONS reports the sentinel.
|
||||||
|
artifacts.gsMaxVertices = std::max(gs->getVertices(), 0);
|
||||||
|
artifacts.gsInvocations = std::max(gs->getInvocations(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tessellation evaluation stage's link properties, GL 4.6 core table 23.35: the
|
||||||
|
// primitive generator's mode, spacing, winding and point mode. (The control stage's
|
||||||
|
// output patch size is captured below, together with the limit check that goes with it.)
|
||||||
|
artifacts.tessGenMode = GL_NONE;
|
||||||
|
artifacts.tessGenSpacing = GL_NONE;
|
||||||
|
artifacts.tessGenVertexOrder = GL_NONE;
|
||||||
|
artifacts.tessGenPointMode = false;
|
||||||
|
if (const glslang::TIntermediate* tes = artifacts.program->getIntermediate(EShLangTessEvaluation)) {
|
||||||
|
switch (tes->getInputPrimitive()) {
|
||||||
|
case glslang::ElgTriangles: artifacts.tessGenMode = GL_TRIANGLES; break;
|
||||||
|
case glslang::ElgQuads: artifacts.tessGenMode = GL_QUADS; break;
|
||||||
|
case glslang::ElgIsolines: artifacts.tessGenMode = GL_ISOLINES; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
// GLSL 4.60 4.4.2.3: equal_spacing and ccw are the defaults, which is what an unset
|
||||||
|
// qualifier means here.
|
||||||
|
switch (tes->getVertexSpacing()) {
|
||||||
|
case glslang::EvsFractionalEven: artifacts.tessGenSpacing = GL_FRACTIONAL_EVEN; break;
|
||||||
|
case glslang::EvsFractionalOdd: artifacts.tessGenSpacing = GL_FRACTIONAL_ODD; break;
|
||||||
|
default: artifacts.tessGenSpacing = GL_EQUAL; break;
|
||||||
|
}
|
||||||
|
switch (tes->getVertexOrder()) {
|
||||||
|
case glslang::EvoCw: artifacts.tessGenVertexOrder = GL_CW; break;
|
||||||
|
default: artifacts.tessGenVertexOrder = GL_CCW; break;
|
||||||
|
}
|
||||||
|
artifacts.tessGenPointMode = tes->getPointMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL_TESS_CONTROL_OUTPUT_VERTICES, i.e. the `layout(vertices = N) out` the control stage
|
// GL_TESS_CONTROL_OUTPUT_VERTICES, i.e. the `layout(vertices = N) out` the control stage
|
||||||
|
|||||||
@@ -1356,6 +1356,23 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// tessellation control stage, or 0 when the program has none. Checked against
|
// tessellation control stage, or 0 when the program has none. Checked against
|
||||||
// GL_MAX_PATCH_VERTICES at link (GL 4.6 core 11.2.1.1).
|
// GL_MAX_PATCH_VERTICES at link (GL 4.6 core 11.2.1.1).
|
||||||
Int tcsOutputVertices = 0;
|
Int tcsOutputVertices = 0;
|
||||||
|
// The rest of the geometry stage's link properties, and the tessellation evaluation
|
||||||
|
// stage's. Every one of these is a glGetProgramiv answer that had no source at all:
|
||||||
|
// the query surface listed the geometry pnames only to fall through to
|
||||||
|
// GL_INVALID_ENUM, and the GL_TESS_GEN_* pnames were not mentioned anywhere. They
|
||||||
|
// come from the linked intermediates for the same reason gsInputPrimitive and
|
||||||
|
// tcsOutputVertices do - glslang has already merged the compilation units' layout
|
||||||
|
// qualifiers and diagnosed contradictions, so the linked program is the thing that
|
||||||
|
// knows.
|
||||||
|
GLenum gsOutputPrimitive = GL_NONE;
|
||||||
|
Int gsMaxVertices = 0;
|
||||||
|
Int gsInvocations = 0;
|
||||||
|
// The tessellation evaluation stage's layout: GL_QUADS / GL_TRIANGLES / GL_ISOLINES,
|
||||||
|
// GL_EQUAL / GL_FRACTIONAL_EVEN / GL_FRACTIONAL_ODD, GL_CW / GL_CCW, and point mode.
|
||||||
|
GLenum tessGenMode = GL_NONE;
|
||||||
|
GLenum tessGenSpacing = GL_NONE;
|
||||||
|
GLenum tessGenVertexOrder = GL_NONE;
|
||||||
|
Bool tessGenPointMode = false;
|
||||||
GLenum xfbBufferMode = GL_INTERLEAVED_ATTRIBS;
|
GLenum xfbBufferMode = GL_INTERLEAVED_ATTRIBS;
|
||||||
Int xfbVaryingNameMaxLength = 0;
|
Int xfbVaryingNameMaxLength = 0;
|
||||||
Bool xfbNeedsScatteredCapture = false;
|
Bool xfbNeedsScatteredCapture = false;
|
||||||
@@ -1545,10 +1562,22 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// GL_LINES_ADJACENCY, GL_TRIANGLES or GL_TRIANGLES_ADJACENCY), or GL_NONE when the
|
// GL_LINES_ADJACENCY, GL_TRIANGLES or GL_TRIANGLES_ADJACENCY), or GL_NONE when the
|
||||||
// program has no geometry stage. Draws must present a compatible primitive type.
|
// program has no geometry stage. Draws must present a compatible primitive type.
|
||||||
GLenum GetGeometryInputType() const { return Artifacts().gsInputPrimitive; }
|
GLenum GetGeometryInputType() const { return Artifacts().gsInputPrimitive; }
|
||||||
|
// GL_GEOMETRY_OUTPUT_TYPE (GL_POINTS, GL_LINE_STRIP or GL_TRIANGLE_STRIP),
|
||||||
|
// GL_GEOMETRY_VERTICES_OUT and GL_GEOMETRY_SHADER_INVOCATIONS of the linked geometry
|
||||||
|
// stage. Meaningless without one - glGetProgramiv raises INVALID_OPERATION there.
|
||||||
|
GLenum GetGeometryOutputType() const { return Artifacts().gsOutputPrimitive; }
|
||||||
|
Int GetGeometryVerticesOut() const { return Artifacts().gsMaxVertices; }
|
||||||
|
Int GetGeometryShaderInvocations() const { return Artifacts().gsInvocations; }
|
||||||
// GL_TESS_CONTROL_OUTPUT_VERTICES of the linked tessellation control stage, or 0 when
|
// GL_TESS_CONTROL_OUTPUT_VERTICES of the linked tessellation control stage, or 0 when
|
||||||
// the program has no such stage. Never greater than GL_MAX_PATCH_VERTICES: a program
|
// the program has no such stage. Never greater than GL_MAX_PATCH_VERTICES: a program
|
||||||
// that declared more does not link at all (GL 4.6 core 11.2.1.1).
|
// that declared more does not link at all (GL 4.6 core 11.2.1.1).
|
||||||
Int GetTessControlOutputVertices() const { return Artifacts().tcsOutputVertices; }
|
Int GetTessControlOutputVertices() const { return Artifacts().tcsOutputVertices; }
|
||||||
|
// GL_TESS_GEN_MODE / _SPACING / _VERTEX_ORDER / _POINT_MODE of the linked tessellation
|
||||||
|
// evaluation stage.
|
||||||
|
GLenum GetTessGenMode() const { return Artifacts().tessGenMode; }
|
||||||
|
GLenum GetTessGenSpacing() const { return Artifacts().tessGenSpacing; }
|
||||||
|
GLenum GetTessGenVertexOrder() const { return Artifacts().tessGenVertexOrder; }
|
||||||
|
Bool GetTessGenPointMode() const { return Artifacts().tessGenPointMode; }
|
||||||
|
|
||||||
Uint GetExternalIndex() const { return m_externalIndex; }
|
Uint GetExternalIndex() const { return m_externalIndex; }
|
||||||
// Globally-unique, never-reused id for this program object's lifetime. Unlike the GL
|
// Globally-unique, never-reused id for this program object's lifetime. Unlike the GL
|
||||||
|
|||||||
@@ -3973,3 +3973,166 @@ void main() { o_color = vec4(1.0); }
|
|||||||
EXPECT_FALSE(programObject->WriteReservedNumSamples(4));
|
EXPECT_FALSE(programObject->WriteReservedNumSamples(4));
|
||||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// glGetProgramiv's geometry and tessellation link properties (GL 4.6 core table 23.35). None of
|
||||||
|
// these had a source: GL_GEOMETRY_VERTICES_OUT / _INPUT_TYPE / _OUTPUT_TYPE were listed in the
|
||||||
|
// switch only to fall through into the GL_INVALID_ENUM default, GL_GEOMETRY_SHADER_INVOCATIONS
|
||||||
|
// and the five GL_TESS_* pnames were not listed at all, and the link recorded nothing but the
|
||||||
|
// geometry INPUT primitive. 72 of the tessellation family's 116 failing conformance bodies died
|
||||||
|
// on the first of these queries, before touching a single tessellation feature.
|
||||||
|
namespace {
|
||||||
|
GLuint CompileStage(GLenum type, const char* source) {
|
||||||
|
const GLuint shader = CreateShader(type);
|
||||||
|
ShaderSource(shader, 1, &source, nullptr);
|
||||||
|
CompileShader(shader);
|
||||||
|
GLint status = GL_FALSE;
|
||||||
|
GetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status != GL_TRUE) {
|
||||||
|
char infoLog[2048] = "";
|
||||||
|
GetShaderInfoLog(shader, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
ADD_FAILURE() << "stage " << type << " failed to compile: " << infoLog;
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint LinkStages(const std::vector<std::pair<GLenum, const char*>>& stages) {
|
||||||
|
const GLuint program = CreateProgram();
|
||||||
|
for (const auto& [type, source] : stages) {
|
||||||
|
const GLuint shader = CompileStage(type, source);
|
||||||
|
AttachShader(program, shader);
|
||||||
|
DeleteShader(shader);
|
||||||
|
}
|
||||||
|
LinkProgram(program);
|
||||||
|
GLint linkStatus = GL_FALSE;
|
||||||
|
GetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||||
|
if (linkStatus != GL_TRUE) {
|
||||||
|
char infoLog[2048] = "";
|
||||||
|
GetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
ADD_FAILURE() << "link failed: " << infoLog;
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* kPassthroughVs = R"(#version 460 core
|
||||||
|
void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); }
|
||||||
|
)";
|
||||||
|
constexpr const char* kPassthroughFs = R"(#version 460 core
|
||||||
|
out vec4 mgColor;
|
||||||
|
void main() { mgColor = vec4(1.0); }
|
||||||
|
)";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, GetProgramivReportsTheGeometryStageLinkProperties) {
|
||||||
|
constexpr const char* gs = R"(#version 460 core
|
||||||
|
layout(triangles, invocations = 3) in;
|
||||||
|
layout(line_strip, max_vertices = 7) out;
|
||||||
|
void main() {
|
||||||
|
for (int i = 0; i < 3; ++i) { gl_Position = gl_in[i].gl_Position; EmitVertex(); }
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
const GLuint program =
|
||||||
|
LinkStages({{GL_VERTEX_SHADER, kPassthroughVs}, {GL_GEOMETRY_SHADER, gs}, {GL_FRAGMENT_SHADER, kPassthroughFs}});
|
||||||
|
|
||||||
|
GLint value = -1;
|
||||||
|
GetProgramiv(program, GL_GEOMETRY_INPUT_TYPE, &value);
|
||||||
|
EXPECT_EQ(value, GL_TRIANGLES);
|
||||||
|
GetProgramiv(program, GL_GEOMETRY_OUTPUT_TYPE, &value);
|
||||||
|
EXPECT_EQ(value, GL_LINE_STRIP);
|
||||||
|
GetProgramiv(program, GL_GEOMETRY_VERTICES_OUT, &value);
|
||||||
|
EXPECT_EQ(value, 7);
|
||||||
|
GetProgramiv(program, GL_GEOMETRY_SHADER_INVOCATIONS, &value);
|
||||||
|
EXPECT_EQ(value, 3);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
// ...and INVALID_OPERATION, not INVALID_ENUM, on a program that has no geometry stage: GL
|
||||||
|
// says "a linked program object with a geometry shader", which the conformance suite checks
|
||||||
|
// from both sides.
|
||||||
|
const GLuint noGeometry = LinkStages({{GL_VERTEX_SHADER, kPassthroughVs}, {GL_FRAGMENT_SHADER, kPassthroughFs}});
|
||||||
|
for (const GLenum pname : {GL_GEOMETRY_INPUT_TYPE, GL_GEOMETRY_OUTPUT_TYPE, GL_GEOMETRY_VERTICES_OUT,
|
||||||
|
GL_GEOMETRY_SHADER_INVOCATIONS}) {
|
||||||
|
GetProgramiv(noGeometry, pname, &value);
|
||||||
|
EXPECT_EQ(GetError(), static_cast<GLenum>(GL_INVALID_OPERATION)) << "pname " << pname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProgramTest, GetProgramivReportsTheTessellationStageLinkProperties) {
|
||||||
|
constexpr const char* tcs = R"(#version 460 core
|
||||||
|
layout(vertices = 3) out;
|
||||||
|
void main() {
|
||||||
|
gl_TessLevelOuter[0] = 1.0; gl_TessLevelOuter[1] = 1.0; gl_TessLevelOuter[2] = 1.0;
|
||||||
|
gl_TessLevelInner[0] = 1.0;
|
||||||
|
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
constexpr const char* tes = R"(#version 460 core
|
||||||
|
layout(quads, fractional_odd_spacing, cw, point_mode) in;
|
||||||
|
void main() { gl_Position = gl_in[0].gl_Position; }
|
||||||
|
)";
|
||||||
|
const GLuint program = LinkStages({{GL_VERTEX_SHADER, kPassthroughVs},
|
||||||
|
{GL_TESS_CONTROL_SHADER, tcs},
|
||||||
|
{GL_TESS_EVALUATION_SHADER, tes},
|
||||||
|
{GL_FRAGMENT_SHADER, kPassthroughFs}});
|
||||||
|
|
||||||
|
GLint value = -1;
|
||||||
|
GetProgramiv(program, GL_TESS_CONTROL_OUTPUT_VERTICES, &value);
|
||||||
|
EXPECT_EQ(value, 3);
|
||||||
|
GetProgramiv(program, GL_TESS_GEN_MODE, &value);
|
||||||
|
EXPECT_EQ(value, GL_QUADS);
|
||||||
|
GetProgramiv(program, GL_TESS_GEN_SPACING, &value);
|
||||||
|
EXPECT_EQ(value, GL_FRACTIONAL_ODD);
|
||||||
|
GetProgramiv(program, GL_TESS_GEN_VERTEX_ORDER, &value);
|
||||||
|
EXPECT_EQ(value, GL_CW);
|
||||||
|
GetProgramiv(program, GL_TESS_GEN_POINT_MODE, &value);
|
||||||
|
EXPECT_EQ(value, GL_TRUE);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
// GLSL 4.60 4.4.2.3 defaults: equal_spacing, ccw, no point mode.
|
||||||
|
constexpr const char* defaultTes = R"(#version 460 core
|
||||||
|
layout(triangles) in;
|
||||||
|
void main() { gl_Position = gl_in[0].gl_Position; }
|
||||||
|
)";
|
||||||
|
const GLuint defaults = LinkStages({{GL_VERTEX_SHADER, kPassthroughVs},
|
||||||
|
{GL_TESS_CONTROL_SHADER, tcs},
|
||||||
|
{GL_TESS_EVALUATION_SHADER, defaultTes},
|
||||||
|
{GL_FRAGMENT_SHADER, kPassthroughFs}});
|
||||||
|
GetProgramiv(defaults, GL_TESS_GEN_MODE, &value);
|
||||||
|
EXPECT_EQ(value, GL_TRIANGLES);
|
||||||
|
GetProgramiv(defaults, GL_TESS_GEN_SPACING, &value);
|
||||||
|
EXPECT_EQ(value, GL_EQUAL);
|
||||||
|
GetProgramiv(defaults, GL_TESS_GEN_VERTEX_ORDER, &value);
|
||||||
|
EXPECT_EQ(value, GL_CCW);
|
||||||
|
GetProgramiv(defaults, GL_TESS_GEN_POINT_MODE, &value);
|
||||||
|
EXPECT_EQ(value, GL_FALSE);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
const GLuint noTess = LinkStages({{GL_VERTEX_SHADER, kPassthroughVs}, {GL_FRAGMENT_SHADER, kPassthroughFs}});
|
||||||
|
for (const GLenum pname : {GL_TESS_CONTROL_OUTPUT_VERTICES, GL_TESS_GEN_MODE, GL_TESS_GEN_SPACING,
|
||||||
|
GL_TESS_GEN_VERTEX_ORDER, GL_TESS_GEN_POINT_MODE}) {
|
||||||
|
GetProgramiv(noTess, pname, &value);
|
||||||
|
EXPECT_EQ(GetError(), static_cast<GLenum>(GL_INVALID_OPERATION)) << "pname " << pname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The context-wide tessellation state the same conformance group reads before it links anything.
|
||||||
|
// glGetBooleanv and glGetFloatv both have to answer GL_PATCH_DEFAULT_OUTER_LEVEL, which is
|
||||||
|
// FLOAT state - a delegation that writes element 0 only would leave the other three components
|
||||||
|
// as whatever was in the caller's stack.
|
||||||
|
TEST_F(ProgramTest, ContextWideTessellationPropertiesAnswerEveryWidth) {
|
||||||
|
GLfloat outer[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
|
||||||
|
GetFloatv(GL_PATCH_DEFAULT_OUTER_LEVEL, outer);
|
||||||
|
for (const GLfloat level : outer) EXPECT_FLOAT_EQ(level, 1.0f);
|
||||||
|
|
||||||
|
GLfloat inner[2] = {-1.0f, -1.0f};
|
||||||
|
GetFloatv(GL_PATCH_DEFAULT_INNER_LEVEL, inner);
|
||||||
|
for (const GLfloat level : inner) EXPECT_FLOAT_EQ(level, 1.0f);
|
||||||
|
|
||||||
|
GLboolean outerBools[4] = {GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE};
|
||||||
|
GetBooleanv(GL_PATCH_DEFAULT_OUTER_LEVEL, outerBools);
|
||||||
|
for (const GLboolean level : outerBools) EXPECT_EQ(level, GL_TRUE);
|
||||||
|
|
||||||
|
GLint restart = -1;
|
||||||
|
GetIntegerv(GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, &restart);
|
||||||
|
EXPECT_EQ(restart, GL_FALSE);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user