text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var environment_1 = require("../../environment");
  var util = require("../../util");
  var gpgpu_util = require("./gpgpu_util");
  var tex_util = require("./tex_util");
  var webgl_util = require("./webgl_util");
  var GPGPUContext = (function () {
      function GPGPUContext(gl) {
          this.outputTexture = null;
          this.program = null;
          this.disposed = false;
          this.autoDebugValidate = false;
          if (gl != null) {
              this.gl = gl;
          }
          else {
              this.gl = gpgpu_util.createWebGLContext();
          }
          if (environment_1.ENV.get('WEBGL_VERSION') === 1) {
              this.textureFloatExtension =
                  webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float');
              this.colorBufferFloatExtension =
                  this.gl.getExtension('WEBGL_color_buffer_float');
          }
          else {
              this.colorBufferFloatExtension =
                  webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float');
          }
          this.loseContextExtension =
              webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context');
          this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl);
          this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl);
          this.framebuffer = webgl_util.createFramebuffer(this.gl);
      }
      GPGPUContext.prototype.dispose = function () {
          var _this = this;
          this.throwIfDisposed();
          if (this.program != null) {
              console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' +
                  ' This is probably a resource leak, delete the program with ' +
                  'GPGPUContext.deleteProgram before disposing.');
          }
          if (this.outputTexture != null) {
              console.warn('Disposing a GPGPUContext that still has a bound output matrix ' +
                  'texture.  This is probably a resource leak, delete the output ' +
                  'matrix texture with GPGPUContext.deleteMatrixTexture before ' +
                  'disposing.');
          }
          var gl = this.gl;
          webgl_util.callAndCheck(gl, function () { return gl.finish(); });
          webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); });
          webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); });
          webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); });
          webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); });
          webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); });
          webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); });
          this.loseContextExtension.loseContext();
          this.disposed = true;
      };
      GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) {
          this.autoDebugValidate = enabled;
          webgl_util.enableDebugWebGLErrorChecking(enabled);
      };
      GPGPUContext.prototype.createMatrixTexture = function (rows, columns) {
          this.throwIfDisposed();
          return gpgpu_util.createMatrixTexture(this.gl, rows, columns);
      };
      GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) {
          this.throwIfDisposed();
          gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels);
      };
      GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) {
          this.throwIfDisposed();
          return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns);
      };
      GPGPUContext.prototype.deleteMatrixTexture = function (texture) {
          var _this = this;
          this.throwIfDisposed();
          if (this.outputTexture === texture) {
              webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer);
              this.outputTexture = null;
          }
          webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); });
      };
      GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) {
          this.throwIfDisposed();
          var numChannels = 1;
          return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels);
      };
      GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) {
          this.throwIfDisposed();
          return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix);
      };
      GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) {
          var _this = this;
          return this.downloadMatrixDriver(texture, function () {
              return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns);
          });
      };
      GPGPUContext.prototype.downloadMatrixFromRGBAColorTexture = function (texture, rows, columns, channels) {
          var _this = this;
          return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromRGBAColorTexture(_this.gl, rows, columns, channels); });
      };
      GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) {
          var _this = this;
          return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); });
      };
      GPGPUContext.prototype.createProgram = function (fragmentShaderSource) {
          this.throwIfDisposed();
          var gl = this.gl;
          var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource);
          var vertexShader = gpgpu_util.createVertexShader(gl);
          var program = webgl_util.createProgram(gl);
          webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); });
          webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); });
          webgl_util.linkProgram(gl, program);
          if (this.autoDebugValidate) {
              webgl_util.validateProgram(gl, program);
          }
          return program;
      };
      GPGPUContext.prototype.deleteProgram = function (program) {
          var _this = this;
          this.throwIfDisposed();
          if (program === this.program) {
              this.program = null;
          }
          if (program != null) {
              webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); });
          }
      };
      GPGPUContext.prototype.setProgram = function (program) {
          var _this = this;
          this.throwIfDisposed();
          this.program = program;
          if ((this.program != null) && this.autoDebugValidate) {
              webgl_util.validateProgram(this.gl, this.program);
          }
          webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); });
      };
      GPGPUContext.prototype.getUniformLocation = function (program, uniformName) {
          this.throwIfDisposed();
          return webgl_util.getProgramUniformLocationOrThrow(this.gl, program, uniformName);
      };
      GPGPUContext.prototype.getAttributeLocation = function (program, attribute) {
          var _this = this;
          this.throwIfDisposed();
          return webgl_util.callAndCheck(this.gl, function () { return _this.gl.getAttribLocation(program, attribute); });
      };
      GPGPUContext.prototype.getUniformLocationNoThrow = function (program, uniformName) {
          this.throwIfDisposed();
          return this.gl.getUniformLocation(program, uniformName);
      };
      GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformLocation, textureUnit) {
          this.throwIfDisposed();
          this.throwIfNoProgram();
          webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformLocation, textureUnit);
      };
      GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) {
          this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows);
      };
      GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) {
          this.throwIfDisposed();
          var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1];
          this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height);
      };
      GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) {
          this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows);
      };
      GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) {
          throw new Error('setOutputPackedMatrixWriteRegion not implemented.');
      };
      GPGPUContext.prototype.debugValidate = function () {
          if (this.program != null) {
              webgl_util.validateProgram(this.gl, this.program);
          }
          webgl_util.validateFramebuffer(this.gl);
      };
      GPGPUContext.prototype.executeProgram = function (attribLocations) {
          this.throwIfDisposed();
          this.throwIfNoProgram();
          var gl = this.gl;
          gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer, attribLocations);
          if (this.autoDebugValidate) {
              this.debugValidate();
          }
          webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); });
      };
      GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () {
          var _this = this;
          this.throwIfDisposed();
          webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); });
      };
      GPGPUContext.prototype.runQuery = function (queryFn) {
          if (environment_1.ENV.get('WEBGL_VERSION') === 2) {
              return this.runQueryWebGL2(queryFn);
          }
          return this.runQueryWebGL1(queryFn);
      };
      GPGPUContext.prototype.runQueryWebGL2 = function (benchmark) {
          var _this = this;
          var ext = webgl_util.getExtensionOrThrow(this.gl, 'EXT_disjoint_timer_query_webgl2');
          var query = this.gl.createQuery();
          this.gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
          benchmark();
          this.gl.endQuery(ext.TIME_ELAPSED_EXT);
          return new Promise(function (resolve, reject) {
              var queryGPU = function () {
                  var available = _this.gl
                      .getQueryParameter(query, _this.gl.QUERY_RESULT_AVAILABLE);
                  var disjoint = _this.gl.getParameter(ext.GPU_DISJOINT_EXT);
                  return available && !disjoint;
              };
              var getTimeElapsed = function () {
                  var timeElapsedNanos = _this.gl
                      .getQueryParameter(query, _this.gl.QUERY_RESULT);
                  resolve(timeElapsedNanos / 1000000);
              };
              var resolveWithWarning = function () {
                  console.warn('Disjoint query timer never available.');
                  resolve(-1);
              };
              util.repeatedTry(queryGPU).then(getTimeElapsed).catch(resolveWithWarning);
          });
      };
      GPGPUContext.prototype.runQueryWebGL1 = function (benchmark) {
          var _this = this;
          var ext = webgl_util.getExtensionOrThrow(this.gl, 'EXT_disjoint_timer_query');
          var query = ext.createQueryEXT();
          ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
          benchmark();
          ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
          return new Promise(function (resolve, reject) {
              var queryGPU = function () {
                  var available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT);
                  var disjoint = _this.gl.getParameter(ext.GPU_DISJOINT_EXT);
                  return available && !disjoint;
              };
              var getTimeElapsed = function () {
                  var timeElapsedNanos = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT);
                  resolve(timeElapsedNanos / 1000000);
              };
              var resolveWithWarning = function () {
                  console.warn('Disjoint query timer never available.');
                  resolve(-1);
              };
              util.repeatedTry(queryGPU).then(getTimeElapsed).catch(resolveWithWarning);
          });
      };
      GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) {
          this.throwIfDisposed();
          webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer);
          if (this.autoDebugValidate) {
              webgl_util.validateFramebuffer(this.gl);
          }
          var result = downloadAndDecode();
          if (this.outputTexture != null) {
              webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer);
              if (this.autoDebugValidate) {
                  webgl_util.validateFramebuffer(this.gl);
              }
          }
          else {
              webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer);
          }
          return result;
      };
      GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) {
          this.throwIfDisposed();
          var gl = this.gl;
          webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer);
          if (this.autoDebugValidate) {
              webgl_util.validateFramebuffer(gl);
          }
          this.outputTexture = outputMatrixTextureMaybePacked;
          webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); });
          webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); });
      };
      GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) {
          var _this = this;
          this.throwIfDisposed();
          webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); });
      };
      GPGPUContext.prototype.throwIfDisposed = function () {
          if (this.disposed) {
              throw new Error('Attempted to use disposed GPGPUContext.');
          }
      };
      GPGPUContext.prototype.throwIfNoProgram = function () {
          if (this.program == null) {
              throw new Error('No GPU program is currently set.');
          }
      };
      return GPGPUContext;
  }());
  exports.GPGPUContext = GPGPUContext;
  //# sourceMappingURL=gpgpu_context.js.map