text: import Resource from './resource';
  import Texture from './texture';
  import Framebuffer from './framebuffer';
  import { parseUniformName, getUniformSetter } from './uniforms';
  import { VertexShader, FragmentShader } from './shader';
  import ProgramConfiguration from './program-configuration';
  import { copyUniform, checkUniformValues } from './uniforms';
  import { isWebGL2, withParameters, log } from '@luma.gl/gltools';
  import { assertWebGL2Context, getKey } from '../webgl-utils';
  import { getPrimitiveDrawMode } from '../webgl-utils/attribute-utils';
  import { uid, assert } from '../utils';
  const LOG_PROGRAM_PERF_PRIORITY = 4;
  const GL_SEPARATE_ATTRIBS = 0x8c8d;
  const V6_DEPRECATED_METHODS = ['setVertexArray', 'setAttributes', 'setBuffers', 'unsetBuffers', 'use', 'getUniformCount', 'getUniformInfo', 'getUniformLocation', 'getUniformValue', 'getVarying', 'getFragDataLocation', 'getAttachedShaders', 'getAttributeCount', 'getAttributeLocation', 'getAttributeInfo'];
  export default class Program extends Resource {
    constructor(gl, props = {}) {
      super(gl, props);
      this.stubRemovedMethods('Program', 'v6.0', V6_DEPRECATED_METHODS);
      this._isCached = false;
      this.initialize(props);
      Object.seal(this);
  
      this._setId(props.id);
    }
  
    initialize(props = {}) {
      const {
        hash,
        vs,
        fs,
        varyings,
        bufferMode = GL_SEPARATE_ATTRIBS
      } = props;
      this.hash = hash || '';
      this.vs = typeof vs === 'string' ? new VertexShader(this.gl, {
        id: "".concat(props.id, "-vs"),
        source: vs
      }) : vs;
      this.fs = typeof fs === 'string' ? new FragmentShader(this.gl, {
        id: "".concat(props.id, "-fs"),
        source: fs
      }) : fs;
      assert(this.vs instanceof VertexShader);
      assert(this.fs instanceof FragmentShader);
      this.uniforms = {};
      this._textureUniforms = {};
      this._texturesRenderable = true;
  
      if (varyings && varyings.length > 0) {
        assertWebGL2Context(this.gl);
        this.varyings = varyings;
        this.gl.transformFeedbackVaryings(this.handle, varyings, bufferMode);
      }
  
      this._compileAndLink();
  
      this._readUniformLocationsFromLinkedProgram();
  
      this.configuration = new ProgramConfiguration(this);
      return this.setProps(props);
    }
  
    delete(options = {}) {
      if (this._isCached) {
        return this;
      }
  
      return super.delete(options);
    }
  
    setProps(props) {
      if ('uniforms' in props) {
        this.setUniforms(props.uniforms);
      }
  
      return this;
    }
  
    draw({
      logPriority,
      drawMode = 4,
      vertexCount,
      offset = 0,
      start,
      end,
      isIndexed = false,
      indexType = 5123,
      instanceCount = 0,
      isInstanced = instanceCount > 0,
      vertexArray = null,
      transformFeedback,
      framebuffer,
      parameters = {},
      uniforms,
      samplers
    }) {
      if (uniforms || samplers) {
        log.deprecated('Program.draw({uniforms})', 'Program.setUniforms(uniforms)')();
        this.setUniforms(uniforms || {});
      }
  
      if (log.priority >= logPriority) {
        const fb = framebuffer ? framebuffer.id : 'default';
        const message = "mode=".concat(getKey(this.gl, drawMode), " verts=").concat(vertexCount, " ") + "instances=".concat(instanceCount, " indexType=").concat(getKey(this.gl, indexType), " ") + "isInstanced=".concat(isInstanced, " isIndexed=").concat(isIndexed, " ") + "Framebuffer=".concat(fb);
        log.log(logPriority, message)();
      }
  
      assert(vertexArray);
      this.gl.useProgram(this.handle);
  
      if (!this._areTexturesRenderable() || vertexCount === 0 || isInstanced && instanceCount === 0) {
        return false;
      }
  
      vertexArray.bindForDraw(vertexCount, instanceCount, () => {
        if (framebuffer !== undefined) {
          parameters = Object.assign({}, parameters, {
            framebuffer
          });
        }
  
        if (transformFeedback) {
          const primitiveMode = getPrimitiveDrawMode(drawMode);
          transformFeedback.begin(primitiveMode);
        }
  
        this._bindTextures();
  
        withParameters(this.gl, parameters, () => {
          if (isIndexed && isInstanced) {
            this.gl.drawElementsInstanced(drawMode, vertexCount, indexType, offset, instanceCount);
          } else if (isIndexed && isWebGL2(this.gl) && !isNaN(start) && !isNaN(end)) {
            this.gl.drawRangeElements(drawMode, start, end, vertexCount, indexType, offset);
          } else if (isIndexed) {
            this.gl.drawElements(drawMode, vertexCount, indexType, offset);
          } else if (isInstanced) {
            this.gl.drawArraysInstanced(drawMode, offset, vertexCount, instanceCount);
          } else {
            this.gl.drawArrays(drawMode, offset, vertexCount);
          }
        });
  
        if (transformFeedback) {
          transformFeedback.end();
        }
      });
      return true;
    }
  
    setUniforms(uniforms = {}) {
      if (log.priority >= 2) {
        checkUniformValues(uniforms, this.id, this._uniformSetters);
      }
  
      this.gl.useProgram(this.handle);
  
      for (const uniformName in uniforms) {
        const uniform = uniforms[uniformName];
        const uniformSetter = this._uniformSetters[uniformName];
  
        if (uniformSetter) {
          let value = uniform;
          let textureUpdate = false;
  
          if (value instanceof Framebuffer) {
            value = value.texture;
          }
  
          if (value instanceof Texture) {
            textureUpdate = this.uniforms[uniformName] !== uniform;
  
            if (textureUpdate) {
              if (uniformSetter.textureIndex === undefined) {
                uniformSetter.textureIndex = this._textureIndexCounter++;
              }
  
              const texture = value;
              const {
                textureIndex
              } = uniformSetter;
              texture.bind(textureIndex);
              value = textureIndex;
  
              if (!texture.loaded) {
                this._texturesRenderable = false;
              }
  
              this._textureUniforms[uniformName] = texture;
            } else {
              value = uniformSetter.textureIndex;
            }
          } else if (this._textureUniforms[uniformName]) {
            delete this._textureUniforms[uniformName];
          }
  
          if (uniformSetter(value) || textureUpdate) {
            copyUniform(this.uniforms, uniformName, uniform);
          }
        }
      }
  
      return this;
    }
  
    _areTexturesRenderable() {
      if (this._texturesRenderable) {
        return true;
      }
  
      this._texturesRenderable = true;
  
      for (const uniformName in this._textureUniforms) {
        const texture = this._textureUniforms[uniformName];
        this._texturesRenderable = this._texturesRenderable && texture.loaded;
      }
  
      return this._texturesRenderable;
    }
  
    _bindTextures() {
      for (const uniformName in this._textureUniforms) {
        const textureIndex = this._uniformSetters[uniformName].textureIndex;
  
        this._textureUniforms[uniformName].bind(textureIndex);
      }
    }
  
    _createHandle() {
      return this.gl.createProgram();
    }
  
    _deleteHandle() {
      this.gl.deleteProgram(this.handle);
    }
  
    _getOptionsFromHandle(handle) {
      const shaderHandles = this.gl.getAttachedShaders(handle);
      const opts = {};
  
      for (const shaderHandle of shaderHandles) {
        const type = this.gl.getShaderParameter(this.handle, 35663);
  
        switch (type) {
          case 35633:
            opts.vs = new VertexShader({
              handle: shaderHandle
            });
            break;
  
          case 35632:
            opts.fs = new FragmentShader({
              handle: shaderHandle
            });
            break;
  
          default:
        }
      }
  
      return opts;
    }
  
    _getParameter(pname) {
      return this.gl.getProgramParameter(this.handle, pname);
    }
  
    _setId(id) {
      if (!id) {
        const programName = this._getName();
  
        this.id = uid(programName);
      }
    }
  
    _getName() {
      let programName = this.vs.getName() || this.fs.getName();
      programName = programName.replace(/shader/i, '');
      programName = programName ? "".concat(programName, "-program") : 'program';
      return programName;
    }
  
    _compileAndLink() {
      const {
        gl
      } = this;
      gl.attachShader(this.handle, this.vs.handle);
      gl.attachShader(this.handle, this.fs.handle);
      log.time(LOG_PROGRAM_PERF_PRIORITY, "linkProgram for ".concat(this._getName()))();
      gl.linkProgram(this.handle);
      log.timeEnd(LOG_PROGRAM_PERF_PRIORITY, "linkProgram for ".concat(this._getName()))();
  
      if (gl.debug || log.level > 0) {
        gl.validateProgram(this.handle);
        const linked = gl.getProgramParameter(this.handle, 35714);
  
        if (!linked) {
          throw new Error("Error linking: ".concat(gl.getProgramInfoLog(this.handle)));
        }
      }
    }
  
    _readUniformLocationsFromLinkedProgram() {
      const {
        gl
      } = this;
      this._uniformSetters = {};
      this._uniformCount = this._getParameter(35718);
  
      for (let i = 0; i < this._uniformCount; i++) {
        const info = this.gl.getActiveUniform(this.handle, i);
        const {
          name,
          isArray
        } = parseUniformName(info.name);
        let location = gl.getUniformLocation(this.handle, name);
        this._uniformSetters[name] = getUniformSetter(gl, location, info, isArray);
  
        if (info.size > 1) {
          for (let l = 0; l < info.size; l++) {
            location = gl.getUniformLocation(this.handle, "".concat(name, "[").concat(l, "]"));
            this._uniformSetters["".concat(name, "[").concat(l, "]")] = getUniformSetter(gl, location, info, isArray);
          }
        }
      }
  
      this._textureIndexCounter = 0;
    }
  
    getActiveUniforms(uniformIndices, pname) {
      return this.gl.getActiveUniforms(this.handle, uniformIndices, pname);
    }
  
    getUniformBlockIndex(blockName) {
      return this.gl.getUniformBlockIndex(this.handle, blockName);
    }
  
    getActiveUniformBlockParameter(blockIndex, pname) {
      return this.gl.getActiveUniformBlockParameter(this.handle, blockIndex, pname);
    }
  
    uniformBlockBinding(blockIndex, blockBinding) {
      this.gl.uniformBlockBinding(this.handle, blockIndex, blockBinding);
    }
  
  }
  //# sourceMappingURL=program.js.map