diff --git a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp index 572b8f6d..2f91ea24 100644 --- a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp +++ b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp @@ -162,6 +162,43 @@ namespace MobileGL::MG_Impl::GLImpl { return false; } + // A geometry stage only accepts the primitive types that decompose into its declared + // input primitive (GL 4.6 core 11.3.1); anything else is INVALID_OPERATION. GL_PATCHES + // is the tessellation pipeline's input and reaches the geometry stage already + // converted, so it is not constrained here. + const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); + const GLenum gsInput = currentProgram ? currentProgram->GetGeometryInputType() : GL_NONE; + if (gsInput != GL_NONE && mode != GL_PATCHES) { + Bool compatible = false; + switch (gsInput) { + case GL_POINTS: + compatible = mode == GL_POINTS; + break; + case GL_LINES: + compatible = mode == GL_LINES || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP; + break; + case GL_LINES_ADJACENCY: + compatible = mode == GL_LINES_ADJACENCY || mode == GL_LINE_STRIP_ADJACENCY; + break; + case GL_TRIANGLES: + compatible = mode == GL_TRIANGLES || mode == GL_TRIANGLE_STRIP || mode == GL_TRIANGLE_FAN; + break; + case GL_TRIANGLES_ADJACENCY: + compatible = mode == GL_TRIANGLES_ADJACENCY || mode == GL_TRIANGLE_STRIP_ADJACENCY; + break; + default: + break; + } + if (!compatible) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", functionName, + "Primitive mode is incompatible with the geometry shader's input primitive type.")); + return false; + } + } + // While transform feedback is active the draw's primitive type must match // the feedback primitive mode (GL 3.3 core 13.2.2). With a geometry shader // the constraint moves to the shader's output primitive type instead, so diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 222c8f41..11db3fa0 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -174,6 +174,7 @@ namespace MobileGL::MG_State::GLState { m_xfbStrides.clear(); m_xfbBufferMode = GL_INTERLEAVED_ATTRIBS; m_xfbVaryingNameMaxLength = 0; + m_gsInputPrimitive = GL_NONE; m_linkStatus = false; } @@ -542,6 +543,20 @@ namespace MobileGL::MG_State::GLState { return; } + // GL_GEOMETRY_INPUT_TYPE. A draw's primitive type has to be compatible with it + // (GL 4.6 core 11.3.1), so it is resolved for every link, not only a capturing one. + m_gsInputPrimitive = GL_NONE; + if (const glslang::TIntermediate* gs = m_program->getIntermediate(EShLangGeometry)) { + switch (gs->getInputPrimitive()) { + case glslang::ElgPoints: m_gsInputPrimitive = GL_POINTS; break; + case glslang::ElgLines: m_gsInputPrimitive = GL_LINES; break; + case glslang::ElgLinesAdjacency: m_gsInputPrimitive = GL_LINES_ADJACENCY; break; + case glslang::ElgTriangles: m_gsInputPrimitive = GL_TRIANGLES; break; + case glslang::ElgTrianglesAdjacency: m_gsInputPrimitive = GL_TRIANGLES_ADJACENCY; break; + default: break; + } + } + MGLOG_D("ProgramObject %u: Starting reflection", m_externalIndex); DoReflection(); MGLOG_D("ProgramObject %u: Reflection done (linkStatus=%d)", m_externalIndex, (int)m_linkStatus); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index e9518edc..e8dadf3c 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -497,6 +497,10 @@ namespace MobileGL::MG_State::GLState { Bool HasGsTriangleStripCaptureFixup() const { return m_gsStripCaptureFixup; } // Triangles per strip, in emission order, for ONE geometry invocation. const Vector& GetGsStripTriangles() const { return m_gsStripTriangles; } + // GL_GEOMETRY_INPUT_TYPE of the linked geometry stage (GL_POINTS, GL_LINES, + // 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. + GLenum GetGeometryInputType() const { return m_gsInputPrimitive; } Uint GetExternalIndex() const { return m_externalIndex; } // Globally-unique, never-reused id for this program object's lifetime. Unlike the GL @@ -604,6 +608,7 @@ namespace MobileGL::MG_State::GLState { Vector m_xfbStrides; Vector m_gsStripTriangles; Bool m_gsStripCaptureFixup = false; + GLenum m_gsInputPrimitive = GL_NONE; GLenum m_xfbBufferMode = GL_INTERLEAVED_ATTRIBS; Int m_xfbVaryingNameMaxLength = 0; };