mesa: move vbo_count_tessellated_primitives() to api_validate.c
[mesa.git] / src / mesa / main / api_validate.c
index 8f834324ad44cc88eb109bc5205e267d5502eedb..7b91fdff55fc18e17254aabf40374ac4315a30ee 100644 (file)
@@ -35,7 +35,6 @@
 #include "state.h"
 #include "transformfeedback.h"
 #include "uniforms.h"
-#include "vbo/vbo.h"
 #include "program/prog_print.h"
 
 
@@ -99,7 +98,7 @@ check_blend_func_error(struct gl_context *ctx)
        *     the blend equation or "blend_support_all_equations", the error
        *     INVALID_OPERATION is generated [...]"
        */
-      const struct gl_program *prog = ctx->_Shader->_CurrentFragmentProgram;
+      const struct gl_program *prog = ctx->FragmentProgram._Current;
       const GLbitfield blend_support = !prog ? 0 : prog->sh.fs.BlendSupport;
 
       if ((blend_support & ctx->Color._AdvancedBlendMode) == 0) {
@@ -133,15 +132,17 @@ _mesa_valid_to_render(struct gl_context *ctx, const char *where)
       /* Any shader stages that are not supplied by the GLSL shader and have
        * assembly shaders enabled must now be validated.
        */
-      if (!ctx->_Shader->CurrentProgram[MESA_SHADER_VERTEX]
-          && ctx->VertexProgram.Enabled && !ctx->VertexProgram._Enabled) {
+      if (!ctx->_Shader->CurrentProgram[MESA_SHADER_VERTEX] &&
+          ctx->VertexProgram.Enabled &&
+          !_mesa_arb_vertex_program_enabled(ctx)) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
                      "%s(vertex program not valid)", where);
          return GL_FALSE;
       }
 
       if (!ctx->_Shader->CurrentProgram[MESA_SHADER_FRAGMENT]) {
-         if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
+         if (ctx->FragmentProgram.Enabled &&
+             !_mesa_arb_fragment_program_enabled(ctx)) {
             _mesa_error(ctx, GL_INVALID_OPERATION,
                         "%s(fragment program not valid)", where);
             return GL_FALSE;
@@ -243,7 +244,20 @@ check_valid_to_render(struct gl_context *ctx, const char *function)
       return false;
    }
 
-   if (!_mesa_all_buffers_are_unmapped(ctx->Array.VAO)) {
+   /* Section 6.3.2 from the GL 4.5:
+    * "Any GL command which attempts to read from, write to, or change
+    *  the state of a buffer object may generate an INVALID_OPERATION error if
+    *  all or part of the buffer object is mapped ... However, only commands
+    *  which explicitly describe this error are required to do so. If an error
+    *  is not generated, such commands will have undefined results and may
+    *  result in GL interruption or termination."
+    *
+    * Only some buffer API functions require INVALID_OPERATION with mapped
+    * buffers. No other functions list such an error, thus it's not required
+    * to report INVALID_OPERATION for draw calls with mapped buffers.
+    */
+   if (!ctx->Const.AllowMappedBuffersDuringExecution &&
+       !_mesa_all_buffers_are_unmapped(ctx->Array.VAO)) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "%s(vertex buffers are mapped)", function);
       return false;
@@ -290,15 +304,6 @@ check_valid_to_render(struct gl_context *ctx, const char *function)
                      "%s(tess ctrl shader is missing)", function);
          return false;
       }
-
-      /* For ES2, we can draw if we have a vertex program/shader). */
-      return ctx->VertexProgram._Current != NULL;
-
-   case API_OPENGLES:
-      /* For OpenGL ES, only draw if we have vertex positions
-       */
-      if (!ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_POS].Enabled)
-         return false;
       break;
 
    case API_OPENGL_CORE:
@@ -312,32 +317,10 @@ check_valid_to_render(struct gl_context *ctx, const char *function)
          _mesa_error(ctx, GL_INVALID_OPERATION, "%s(no VAO bound)", function);
          return false;
       }
+      break;
 
-      /* Section 7.3 (Program Objects) of the OpenGL 4.5 Core Profile spec
-       * says:
-       *
-       *     "If there is no active program for the vertex or fragment shader
-       *     stages, the results of vertex and/or fragment processing will be
-       *     undefined. However, this is not an error."
-       *
-       * The fragment shader is not tested here because other state (e.g.,
-       * GL_RASTERIZER_DISCARD) affects whether or not we actually care.
-       */
-      return ctx->VertexProgram._Current != NULL;
-
+   case API_OPENGLES:
    case API_OPENGL_COMPAT:
-      if (ctx->VertexProgram._Current != NULL) {
-         /* Draw regardless of whether or not we have any vertex arrays.
-          * (Ex: could draw a point using a constant vertex pos)
-          */
-         return true;
-      } else {
-         /* Draw if we have vertex positions (GL_VERTEX_ARRAY or generic
-          * array [0]).
-          */
-         return (ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_POS].Enabled ||
-                 ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_GENERIC0].Enabled);
-      }
       break;
 
    default:
@@ -355,7 +338,7 @@ check_valid_to_render(struct gl_context *ctx, const char *function)
  * Note: This may be called during display list compilation.
  */
 bool
-_mesa_is_valid_prim_mode(struct gl_context *ctx, GLenum mode)
+_mesa_is_valid_prim_mode(const struct gl_context *ctx, GLenum mode)
 {
    /* The overwhelmingly common case is (mode <= GL_TRIANGLE_FAN).  Test that
     * first and exit.  You would think that a switch-statement would be the
@@ -700,14 +683,6 @@ validate_DrawElements_common(struct gl_context *ctx,
    if (!check_valid_to_render(ctx, caller))
       return false;
 
-   /* Not using a VBO for indices, so avoid NULL pointer derefs later.
-    */
-   if (!_mesa_is_bufferobj(ctx->Array.VAO->IndexBufferObj) && indices == NULL)
-      return false;
-
-   if (count == 0)
-      return false;
-
    return true;
 }
 
@@ -816,25 +791,10 @@ _mesa_validate_DrawRangeElements(struct gl_context *ctx, GLenum mode,
                                        "glDrawRangeElements");
 }
 
+
 static bool
-validate_draw_arrays(struct gl_context *ctx, const char *func,
-                     GLenum mode, GLsizei count, GLsizei numInstances)
+need_xfb_remaining_prims_check(const struct gl_context *ctx)
 {
-   struct gl_transform_feedback_object *xfb_obj
-      = ctx->TransformFeedback.CurrentObject;
-   FLUSH_CURRENT(ctx, 0);
-
-   if (count < 0) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "%s(count)", func);
-      return false;
-   }
-
-   if (!_mesa_valid_prim_mode(ctx, mode, func))
-      return false;
-
-   if (!check_valid_to_render(ctx, func))
-      return false;
-
    /* From the GLES3 specification, section 2.14.2 (Transform Feedback
     * Primitive Capture):
     *
@@ -862,10 +822,96 @@ validate_draw_arrays(struct gl_context *ctx, const char *func,
     *     is removed and replaced with the GL behavior (primitives are not
     *     written and the corresponding counter is not updated)..."
     */
-   if (_mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx) &&
-       !_mesa_has_OES_geometry_shader(ctx) &&
-       !_mesa_has_OES_tessellation_shader(ctx)) {
-      size_t prim_count = vbo_count_tessellated_primitives(mode, count, numInstances);
+   return _mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx) &&
+          !_mesa_has_OES_geometry_shader(ctx) &&
+          !_mesa_has_OES_tessellation_shader(ctx);
+}
+
+
+/**
+ * Figure out the number of transform feedback primitives that will be output
+ * considering the drawing mode, number of vertices, and instance count,
+ * assuming that no geometry shading is done and primitive restart is not
+ * used.
+ *
+ * This is used by driver back-ends in implementing the PRIMITIVES_GENERATED
+ * and TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries.  It is also used to
+ * pre-validate draw calls in GLES3 (where draw calls only succeed if there is
+ * enough room in the transform feedback buffer for the result).
+ */
+static size_t
+count_tessellated_primitives(GLenum mode, GLuint count, GLuint num_instances)
+{
+   size_t num_primitives;
+   switch (mode) {
+   case GL_POINTS:
+      num_primitives = count;
+      break;
+   case GL_LINE_STRIP:
+      num_primitives = count >= 2 ? count - 1 : 0;
+      break;
+   case GL_LINE_LOOP:
+      num_primitives = count >= 2 ? count : 0;
+      break;
+   case GL_LINES:
+      num_primitives = count / 2;
+      break;
+   case GL_TRIANGLE_STRIP:
+   case GL_TRIANGLE_FAN:
+   case GL_POLYGON:
+      num_primitives = count >= 3 ? count - 2 : 0;
+      break;
+   case GL_TRIANGLES:
+      num_primitives = count / 3;
+      break;
+   case GL_QUAD_STRIP:
+      num_primitives = count >= 4 ? ((count / 2) - 1) * 2 : 0;
+      break;
+   case GL_QUADS:
+      num_primitives = (count / 4) * 2;
+      break;
+   case GL_LINES_ADJACENCY:
+      num_primitives = count / 4;
+      break;
+   case GL_LINE_STRIP_ADJACENCY:
+      num_primitives = count >= 4 ? count - 3 : 0;
+      break;
+   case GL_TRIANGLES_ADJACENCY:
+      num_primitives = count / 6;
+      break;
+   case GL_TRIANGLE_STRIP_ADJACENCY:
+      num_primitives = count >= 6 ? (count - 4) / 2 : 0;
+      break;
+   default:
+      assert(!"Unexpected primitive type in count_tessellated_primitives");
+      num_primitives = 0;
+      break;
+   }
+   return num_primitives * num_instances;
+}
+
+
+static bool
+validate_draw_arrays(struct gl_context *ctx, const char *func,
+                     GLenum mode, GLsizei count, GLsizei numInstances)
+{
+   FLUSH_CURRENT(ctx, 0);
+
+   if (count < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(count)", func);
+      return false;
+   }
+
+   if (!_mesa_valid_prim_mode(ctx, mode, func))
+      return false;
+
+   if (!check_valid_to_render(ctx, func))
+      return false;
+
+   if (need_xfb_remaining_prims_check(ctx)) {
+      struct gl_transform_feedback_object *xfb_obj
+         = ctx->TransformFeedback.CurrentObject;
+      size_t prim_count = count_tessellated_primitives(mode, count, numInstances);
       if (xfb_obj->GlesRemainingPrims < prim_count) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
                      "%s(exceeds transform feedback size)", func);
@@ -913,6 +959,60 @@ _mesa_validate_DrawArraysInstanced(struct gl_context *ctx, GLenum mode, GLint fi
 }
 
 
+/**
+ * Called to error check the function parameters.
+ *
+ * Note that glMultiDrawArrays is not part of GLES, so there's limited scope
+ * for sharing code with the validation of glDrawArrays.
+ */
+bool
+_mesa_validate_MultiDrawArrays(struct gl_context *ctx, GLenum mode,
+                               const GLsizei *count, GLsizei primcount)
+{
+   int i;
+
+   FLUSH_CURRENT(ctx, 0);
+
+   if (!_mesa_valid_prim_mode(ctx, mode, "glMultiDrawArrays"))
+      return false;
+
+   if (!check_valid_to_render(ctx, "glMultiDrawArrays"))
+      return false;
+
+   if (primcount < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glMultiDrawArrays(primcount=%d)",
+                  primcount);
+      return false;
+   }
+
+   for (i = 0; i < primcount; ++i) {
+      if (count[i] < 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glMultiDrawArrays(count[%d]=%d)",
+                     i, count[i]);
+         return false;
+      }
+   }
+
+   if (need_xfb_remaining_prims_check(ctx)) {
+      struct gl_transform_feedback_object *xfb_obj
+         = ctx->TransformFeedback.CurrentObject;
+      size_t xfb_prim_count = 0;
+
+      for (i = 0; i < primcount; ++i)
+         xfb_prim_count += count_tessellated_primitives(mode, count[i], 1);
+
+      if (xfb_obj->GlesRemainingPrims < xfb_prim_count) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glMultiDrawArrays(exceeds transform feedback size)");
+         return false;
+      }
+      xfb_obj->GlesRemainingPrims -= xfb_prim_count;
+   }
+
+   return true;
+}
+
+
 GLboolean
 _mesa_validate_DrawElementsInstanced(struct gl_context *ctx,
                                      GLenum mode, GLsizei count, GLenum type,
@@ -1340,237 +1440,3 @@ _mesa_validate_MultiDrawElementsIndirectCount(struct gl_context *ctx,
    return valid_draw_indirect_parameters(
          ctx, "glMultiDrawElementsIndirectCountARB", drawcount);
 }
-
-static bool
-check_valid_to_compute(struct gl_context *ctx, const char *function)
-{
-   if (!_mesa_has_compute_shaders(ctx)) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "unsupported function (%s) called",
-                  function);
-      return false;
-   }
-
-   /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
-    *
-    * "An INVALID_OPERATION error is generated if there is no active program
-    *  for the compute shader stage."
-    */
-   if (ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE] == NULL) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "%s(no active compute shader)",
-                  function);
-      return false;
-   }
-
-   return true;
-}
-
-GLboolean
-_mesa_validate_DispatchCompute(struct gl_context *ctx,
-                               const GLuint *num_groups)
-{
-   int i;
-   FLUSH_CURRENT(ctx, 0);
-
-   if (!check_valid_to_compute(ctx, "glDispatchCompute"))
-      return GL_FALSE;
-
-   for (i = 0; i < 3; i++) {
-      /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
-       *
-       * "An INVALID_VALUE error is generated if any of num_groups_x,
-       *  num_groups_y and num_groups_z are greater than or equal to the
-       *  maximum work group count for the corresponding dimension."
-       *
-       * However, the "or equal to" portions appears to be a specification
-       * bug. In all other areas, the specification appears to indicate that
-       * the number of workgroups can match the MAX_COMPUTE_WORK_GROUP_COUNT
-       * value. For example, under DispatchComputeIndirect:
-       *
-       * "If any of num_groups_x, num_groups_y or num_groups_z is greater than
-       *  the value of MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding
-       *  dimension then the results are undefined."
-       *
-       * Additionally, the OpenGLES 3.1 specification does not contain "or
-       * equal to" as an error condition.
-       */
-      if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glDispatchCompute(num_groups_%c)", 'x' + i);
-         return GL_FALSE;
-      }
-   }
-
-   /* The ARB_compute_variable_group_size spec says:
-    *
-    * "An INVALID_OPERATION error is generated by DispatchCompute if the active
-    *  program for the compute shader stage has a variable work group size."
-    */
-   struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
-   if (prog->info.cs.local_size_variable) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glDispatchCompute(variable work group size forbidden)");
-      return GL_FALSE;
-   }
-
-   return GL_TRUE;
-}
-
-GLboolean
-_mesa_validate_DispatchComputeGroupSizeARB(struct gl_context *ctx,
-                                           const GLuint *num_groups,
-                                           const GLuint *group_size)
-{
-   GLuint total_invocations = 1;
-   int i;
-
-   FLUSH_CURRENT(ctx, 0);
-
-   if (!check_valid_to_compute(ctx, "glDispatchComputeGroupSizeARB"))
-      return GL_FALSE;
-
-   /* The ARB_compute_variable_group_size spec says:
-    *
-    * "An INVALID_OPERATION error is generated by
-    *  DispatchComputeGroupSizeARB if the active program for the compute
-    *  shader stage has a fixed work group size."
-    */
-   struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
-   if (!prog->info.cs.local_size_variable) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glDispatchComputeGroupSizeARB(fixed work group size "
-                  "forbidden)");
-      return GL_FALSE;
-   }
-
-   for (i = 0; i < 3; i++) {
-      /* The ARB_compute_variable_group_size spec says:
-       *
-       * "An INVALID_VALUE error is generated if any of num_groups_x,
-       *  num_groups_y and num_groups_z are greater than or equal to the
-       *  maximum work group count for the corresponding dimension."
-       */
-      if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glDispatchComputeGroupSizeARB(num_groups_%c)", 'x' + i);
-         return GL_FALSE;
-      }
-
-      /* The ARB_compute_variable_group_size spec says:
-       *
-       * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if
-       *  any of <group_size_x>, <group_size_y>, or <group_size_z> is less than
-       *  or equal to zero or greater than the maximum local work group size
-       *  for compute shaders with variable group size
-       *  (MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB) in the corresponding
-       *  dimension."
-       *
-       * However, the "less than" is a spec bug because they are declared as
-       * unsigned integers.
-       */
-      if (group_size[i] == 0 ||
-          group_size[i] > ctx->Const.MaxComputeVariableGroupSize[i]) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glDispatchComputeGroupSizeARB(group_size_%c)", 'x' + i);
-         return GL_FALSE;
-      }
-
-      total_invocations *= group_size[i];
-   }
-
-   /* The ARB_compute_variable_group_size spec says:
-    *
-    * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if
-    *  the product of <group_size_x>, <group_size_y>, and <group_size_z> exceeds
-    *  the implementation-dependent maximum local work group invocation count
-    *  for compute shaders with variable group size
-    *  (MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB)."
-    */
-   if (total_invocations > ctx->Const.MaxComputeVariableGroupInvocations) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glDispatchComputeGroupSizeARB(product of local_sizes "
-                  "exceeds MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB "
-                  "(%d > %d))", total_invocations,
-                  ctx->Const.MaxComputeVariableGroupInvocations);
-      return GL_FALSE;
-   }
-
-   return GL_TRUE;
-}
-
-static GLboolean
-valid_dispatch_indirect(struct gl_context *ctx,
-                        GLintptr indirect,
-                        GLsizei size, const char *name)
-{
-   const uint64_t end = (uint64_t) indirect + size;
-
-   if (!check_valid_to_compute(ctx, name))
-      return GL_FALSE;
-
-   /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
-    *
-    * "An INVALID_VALUE error is generated if indirect is negative or is not a
-    *  multiple of four."
-    */
-   if (indirect & (sizeof(GLuint) - 1)) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "%s(indirect is not aligned)", name);
-      return GL_FALSE;
-   }
-
-   if (indirect < 0) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "%s(indirect is less than zero)", name);
-      return GL_FALSE;
-   }
-
-   /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
-    *
-    * "An INVALID_OPERATION error is generated if no buffer is bound to the
-    *  DRAW_INDIRECT_BUFFER binding, or if the command would source data
-    *  beyond the end of the buffer object."
-    */
-   if (!_mesa_is_bufferobj(ctx->DispatchIndirectBuffer)) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "%s: no buffer bound to DISPATCH_INDIRECT_BUFFER", name);
-      return GL_FALSE;
-   }
-
-   if (_mesa_check_disallowed_mapping(ctx->DispatchIndirectBuffer)) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "%s(DISPATCH_INDIRECT_BUFFER is mapped)", name);
-      return GL_FALSE;
-   }
-
-   if (ctx->DispatchIndirectBuffer->Size < end) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "%s(DISPATCH_INDIRECT_BUFFER too small)", name);
-      return GL_FALSE;
-   }
-
-   /* The ARB_compute_variable_group_size spec says:
-    *
-    * "An INVALID_OPERATION error is generated if the active program for the
-    *  compute shader stage has a variable work group size."
-    */
-   struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
-   if (prog->info.cs.local_size_variable) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "%s(variable work group size forbidden)", name);
-      return GL_FALSE;
-   }
-
-   return GL_TRUE;
-}
-
-GLboolean
-_mesa_validate_DispatchComputeIndirect(struct gl_context *ctx,
-                                       GLintptr indirect)
-{
-   FLUSH_CURRENT(ctx, 0);
-
-   return valid_dispatch_indirect(ctx, indirect, 3 * sizeof(GLuint),
-                                  "glDispatchComputeIndirect");
-}