text: "use strict";
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  };
  var __generator = (this && this.__generator) || function (thisArg, body) {
      var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
      return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
      function verb(n) { return function (v) { return step([n, v]); }; }
      function step(op) {
          if (f) throw new TypeError("Generator is already executing.");
          while (_) try {
              if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
              if (y = 0, t) op = [0, t.value];
              switch (op[0]) {
                  case 0: case 1: t = op; break;
                  case 4: _.label++; return { value: op[1], done: false };
                  case 5: _.label++; y = op[1]; op = [0]; continue;
                  case 7: op = _.ops.pop(); _.trys.pop(); continue;
                  default:
                      if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                      if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                      if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                      if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                      if (t[2]) _.ops.pop();
                      _.trys.pop(); continue;
              }
              op = body.call(thisArg, _);
          } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
          if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
      }
  };
  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;
          this.vertexAttrsAreBound = 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');
          if (environment_1.ENV.get('WEBGL_GET_BUFFER_SUB_DATA_ASYNC_EXTENSION_ENABLED')) {
              this.getBufferSubDataAsyncExtension =
                  this.gl.getExtension('WEBGL_get_buffer_sub_data_async');
          }
          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;
          if (this.disposed) {
              return;
          }
          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.downloadMatrixFromTextureAsync = function (texture, rows, columns) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              return __generator(this, function (_a) {
                  if (this.getBufferSubDataAsyncExtension == null) {
                      throw new Error("Cannot download matrix from output texture asynchronously, " +
                          "WEBGL_get_buffer_sub_data_async is not enabled.");
                  }
                  return [2, this.downloadMatrixDriverAsync(texture, function () { return gpgpu_util.downloadMatrixFromOutputTextureAsync(_this.gl, _this.getBufferSubDataAsyncExtension, 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);
          }
          if (!this.vertexAttrsAreBound) {
              this.setProgram(program);
              this.vertexAttrsAreBound = gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer);
          }
          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, shouldThrow) {
          if (shouldThrow === void 0) { shouldThrow = true; }
          this.throwIfDisposed();
          if (shouldThrow) {
              return webgl_util.getProgramUniformLocationOrThrow(this.gl, program, uniformName);
          }
          else {
              return webgl_util.getProgramUniformLocation(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 () {
          this.throwIfDisposed();
          this.throwIfNoProgram();
          var gl = this.gl;
          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.getQueryTimerExtension = function () {
          if (this.disjointQueryTimerExtension == null) {
              this.disjointQueryTimerExtension =
                  webgl_util.getExtensionOrThrow(this.gl, environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2 ?
                      'EXT_disjoint_timer_query_webgl2' :
                      'EXT_disjoint_timer_query');
          }
          return this.disjointQueryTimerExtension;
      };
      GPGPUContext.prototype.getQueryTimerExtensionWebGL2 = function () {
          return this.getQueryTimerExtension();
      };
      GPGPUContext.prototype.getQueryTimerExtensionWebGL1 = function () {
          return this.getQueryTimerExtension();
      };
      GPGPUContext.prototype.runQuery = function (queryFn) {
          var query = this.beginQuery();
          queryFn();
          this.endQuery();
          return this.pollQueryTime(query);
      };
      GPGPUContext.prototype.beginQuery = function () {
          if (environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) {
              var gl2 = this.gl;
              var ext_1 = this.getQueryTimerExtensionWebGL2();
              var query_1 = gl2.createQuery();
              gl2.beginQuery(ext_1.TIME_ELAPSED_EXT, query_1);
              return query_1;
          }
          var ext = this.getQueryTimerExtensionWebGL1();
          var query = ext.createQueryEXT();
          ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query);
          return query;
      };
      GPGPUContext.prototype.endQuery = function () {
          if (environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) {
              var gl2 = this.gl;
              var ext_2 = this.getQueryTimerExtensionWebGL2();
              gl2.endQuery(ext_2.TIME_ELAPSED_EXT);
              return;
          }
          var ext = this.getQueryTimerExtensionWebGL1();
          ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
      };
      GPGPUContext.prototype.isQueryAvailable = function (query, queryTimerVersion) {
          if (queryTimerVersion === 0) {
              return true;
          }
          if (queryTimerVersion === 2) {
              var gl2 = this.gl;
              var ext = this.getQueryTimerExtensionWebGL2();
              var available = gl2.getQueryParameter(query, gl2.QUERY_RESULT_AVAILABLE);
              var disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT);
              return available && !disjoint;
          }
          else {
              var ext = this.getQueryTimerExtensionWebGL1();
              var available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT);
              var disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT);
              return available && !disjoint;
          }
      };
      GPGPUContext.prototype.pollQueryTime = function (query) {
          var _this = this;
          return new Promise(function (resolve, reject) {
              var resolveWithWarning = function () {
                  console.warn('Disjoint query timer never available.');
                  resolve(-1);
              };
              var queryTimerVersion = environment_1.ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION');
              util.repeatedTry(function () { return _this.isQueryAvailable(query, queryTimerVersion); })
                  .then(function () { return resolve(_this.getQueryTime(query, queryTimerVersion)); })
                  .catch(resolveWithWarning);
          });
      };
      GPGPUContext.prototype.getQueryTime = function (query, queryTimerVersion) {
          if (queryTimerVersion === 0) {
              return null;
          }
          if (queryTimerVersion === 2) {
              var gl2 = this.gl;
              var timeElapsedNanos = gl2.getQueryParameter(query, gl2.QUERY_RESULT);
              return timeElapsedNanos / 1000000;
          }
          else {
              var ext = this.getQueryTimerExtensionWebGL1();
              var timeElapsedNanos = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT);
              return timeElapsedNanos / 1000000;
          }
      };
      GPGPUContext.prototype.downloadMatrixDriverSetup = function (texture) {
          this.throwIfDisposed();
          webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer);
          if (this.autoDebugValidate) {
              webgl_util.validateFramebuffer(this.gl);
          }
      };
      GPGPUContext.prototype.downloadMatrixDriverTeardown = function () {
          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);
          }
      };
      GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) {
          this.downloadMatrixDriverSetup(texture);
          var result = downloadAndDecode();
          this.downloadMatrixDriverTeardown();
          return result;
      };
      GPGPUContext.prototype.downloadMatrixDriverAsync = function (texture, downloadAndDecode) {
          return __awaiter(this, void 0, void 0, function () {
              var result;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          this.downloadMatrixDriverSetup(texture);
                          return [4, downloadAndDecode()];
                      case 1:
                          result = _a.sent();
                          this.downloadMatrixDriverTeardown();
                          return [2, 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