class_docstr: Shader program object
  
  A Program is an object to which shaders can be attached and linked to
  create the final program.
  
  Uniforms and attributes can be set using indexing: e.g.
  ``program['a_pos'] = pos_data`` and ``program['u_color'] = (1, 0, 0)``.
  
  Parameters
  ----------
  vert : str
      The vertex shader to be used by this program
  frag : str
      The fragment shader to be used by this program
  count : int (optional)
      The program will prepare a structured vertex buffer of count
      vertices. All attributes set using ``prog['attr'] = X`` will
      be combined into a structured vbo with interleaved elements, which
      is more efficient than having one vbo per attribute.
  
  Notes
  -----
  If several shaders are specified, only one can contain the main
  function. OpenGL ES 2.0 does not support a list of shaders.
class_docstr_tok: ["Shader","program","object","A","Program","is","an","object","to","which","shaders","can","be","attached","and","linked","to","create","the","final","program",".","Uniforms","and","attributes","can","be","set","using","indexing",":","e.g",".","``","program","[","'a_pos","'","]","=","pos_data","``","and","``","program","[","'u_color","'","]","=","(","1",",","0",",","0",")","``",".","Parameters","--","--","--","--","--","vert",":","str","The","vertex","shader","to","be","used","by","this","program","frag",":","str","The","fragment","shader","to","be","used","by","this","program","count",":","int","(","optional",")","The","program","will","prepare","a","structured","vertex","buffer","of","count","vertices",".","All","attributes","set","using","``","prog","[","'attr","'","]","=","X","``","will","be","combined","into","a","structured","vbo","with","interleaved","elements",",","which","is","more","efficient","than","having","one","vbo","per","attribute",".","Notes","--","--","-","If","several","shaders","are","specified",",","only","one","can","contain","the","main","function",".","OpenGL","ES","2.0","does","not","support","a","list","of","shaders","."]
docstr: Draw the attribute arrays in the specified mode.
  
  Parameters
  ----------
  mode : str | GL_ENUM
      'points', 'lines', 'line_strip', 'line_loop', 'lines_adjacency',
      'line_strip_adjacency', 'triangles', 'triangle_strip', or
      'triangle_fan'.
  indices : array
      Array of indices to draw.
  check_error:
      Check error after draw.
docstr_tok: ["Draw","the","attribute","arrays","in","the","specified","mode",".","Parameters","--","--","--","--","--","mode",":","str","|","GL_ENUM","'points","'",",","'lines","'",",","'line_strip","'",",","'line_loop","'",",","'lines_adjacency","'",",","'line_strip_adjacency","'",",","'triangles","'",",","'triangle_strip","'",",","or","'triangle_fan","'",".","indices",":","array","Array","of","indices","to","draw",".","check_error",":","Check","error","after","draw","."]
code: def draw(self, mode="triangles", indices=None, check_error=True):
      """Draw the attribute arrays in the specified mode.
  
      Parameters
      ----------
      mode : str | GL_ENUM
          'points', 'lines', 'line_strip', 'line_loop', 'lines_adjacency',
          'line_strip_adjacency', 'triangles', 'triangle_strip', or
          'triangle_fan'.
      indices : array
          Array of indices to draw.
      check_error:
          Check error after draw.
      """
      # Invalidate buffer (data has already been sent)
      self._buffer = None
  
      # Check if mode is valid
      mode = check_enum(mode)
      if mode not in [
          "points",
          "lines",
          "line_strip",
          "line_loop",
          "lines_adjacency",
          "line_strip_adjacency",
          "triangles",
          "triangle_strip",
          "triangle_fan",
      ]:
          raise ValueError("Invalid draw mode: %r" % mode)
  
      # Check leftover variables, warn, discard them
      # In GLIR we check whether all attributes are indeed set
      for name in self._pending_variables:
          logger.warn(
              "Value provided for %r, but this variable was not "
              "found in the shader program." % name
          )
      self._pending_variables = {}
  
      # Check attribute sizes
      attributes = [
          vbo
          for vbo in self._user_variables.values()
          if isinstance(vbo, DataBuffer)
      ]
  
      attrs = [
          a for a in attributes if getattr(a, "divisor", None) is None
      ]
      if len(attrs) < 1:
          raise RuntimeError("Must have at least one attribute")
      sizes = [a.size for a in attrs]
      if not all(s == sizes[0] for s in sizes[1:]):
          msg = "\n".join([f"{str(a)}: {a.size}" for a in attrs])
          raise RuntimeError(
              f"All attributes must have the same size, got:\n{msg}"
          )
  
      attrs_with_div = [a for a in attributes if a not in attrs]
      if attrs_with_div:
          sizes = [a.size for a in attrs_with_div]
          divs = [a.divisor for a in attrs_with_div]
          instances = sizes[0] * divs[0]
          if not all(s * d == instances for s, d in zip(sizes, divs)):
              msg = "\n".join(
                  [
                      f"{str(a)}: {a.size} * {a.divisor} = {a.size * a.divisor}"
                      for a in attrs_with_div
                  ]
              )
              raise RuntimeError(
                  f"All attributes with divisors must have the same size as the number of instances, got:\n{msg}"
              )
      else:
          instances = 1
  
      # Get the glir queue that we need now
      canvas = get_current_canvas()
      assert canvas is not None
  
      # Associate canvas
      canvas.context.glir.associate(self.glir)
  
      # Indexbuffer
      if isinstance(indices, IndexBuffer):
          canvas.context.glir.associate(indices.glir)
          logger.debug("Program drawing %r with index buffer" % mode)
          gltypes = {
              np.dtype(np.uint8): "UNSIGNED_BYTE",
              np.dtype(np.uint16): "UNSIGNED_SHORT",
              np.dtype(np.uint32): "UNSIGNED_INT",
          }
          selection = indices.id, gltypes[indices.dtype], indices.size
          canvas.context.glir.command(
              "DRAW", self._id, mode, selection, instances
          )
      elif indices is None:
          selection = 0, attributes[0].size
          logger.debug("Program drawing %r with %r" % (mode, selection))
          canvas.context.glir.command(
              "DRAW", self._id, mode, selection, instances
          )
      else:
          raise TypeError(
              "Invalid index: %r (must be IndexBuffer)" % indices
          )
  
      # Process GLIR commands
      canvas.context.flush_commands()
  
imports: import re
  import numpy
  from .globject import GLObject
  from .buffer import VertexBuffer, IndexBuffer, DataBuffer
  from .texture import BaseTexture, Texture2D, Texture3D, Texture1D, TextureCube
  from ..util import logger
  from .util import check_enum
  from .context import get_current_canvas
  from .preprocessor import preprocess
label_desc: Use image node_id 11 for calling the Program obj's underlying member method code with example usage: obj.draw(mode, indices, check_error) without return types