Home Reference Source

cables_dev/cables/src/libs/cgl/pixelreader/pixelreader.js

  1. import { Logger } from "cables-shared-client";
  2.  
  3. class PixelReader
  4. {
  5. constructor()
  6. {
  7. this._log = new Logger("LoadingStatus");
  8.  
  9. this.pixelData = null;
  10. this._finishedFence = true;
  11. this._size = 0;
  12. this._pbo = null;
  13. }
  14.  
  15. _fence(cgl)
  16. {
  17. const gl = cgl.gl;
  18. this._finishedFence = false;
  19. return new Promise(function (resolve, reject)
  20. {
  21. if (cgl.aborted) return;
  22. let sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
  23. if (!sync) return;
  24. gl.flush(); // Ensure the fence is submitted.
  25.  
  26. function check()
  27. {
  28. if (cgl.aborted) return;
  29. const status = gl.clientWaitSync(sync, 0, 0);
  30.  
  31. if (status == gl.WAIT_FAILED)
  32. {
  33. console.error("fence wait failed");
  34. if (reject) reject();
  35. }
  36. else
  37. if (status == gl.TIMEOUT_EXPIRED)
  38. {
  39. // this._log.log("TIMEOUT_EXPIRED");
  40. return setTimeout(check, 0);
  41. }
  42. else
  43. if (status == gl.CONDITION_SATISFIED)
  44. {
  45. // this._log.log("CONDITION_SATISFIED");
  46. resolve();
  47. gl.deleteSync(sync);
  48. }
  49. else if (status == gl.ALREADY_SIGNALED)
  50. {
  51. // this._log.log("already signaled");
  52. resolve();
  53. gl.deleteSync(sync);
  54. }
  55. else
  56. {
  57. this._log.log("unknown fence status", status);
  58. }
  59. }
  60.  
  61. // setTimeout(check, 3);
  62. check();
  63. });
  64. }
  65.  
  66.  
  67. read(cgl, fb, pixelFormat, x, y, w, h, finishedcb)
  68. {
  69. if (CABLES.UI)
  70. if (!CABLES.UI.loaded || performance.now() - CABLES.UI.loadedTime < 1000) return;
  71.  
  72. if (!this._finishedFence) return;
  73.  
  74. const gl = cgl.gl;
  75. let bytesPerItem = 1;
  76.  
  77. if (cgl.aborted) return;
  78. if (!fb) return;
  79.  
  80. if (pixelFormat === CGL.Texture.TYPE_FLOAT) pixelFormat = CGL.Texture.PFORMATSTR_RGBA32F;
  81. // let isFloatingPoint = pixelFormat == CGL.Texture.TYPE_FLOAT; // old parameter was "textureType", now it is pixelformat, keeping this for compatibility...
  82.  
  83. let isFloatingPoint = CGL.Texture.isPixelFormatFloat(pixelFormat);
  84.  
  85. if (isFloatingPoint)bytesPerItem = 4;
  86. if (CGL.Texture.isPixelFormatHalfFloat(pixelFormat)) bytesPerItem = 2;
  87.  
  88.  
  89. const pixelInfo = CGL.Texture.setUpGlPixelFormat(cgl, pixelFormat);
  90. const numItems = pixelInfo.numColorChannels * w * h;
  91.  
  92. if (w == 0 || h == 0 || numItems == 0) return;
  93.  
  94. if (!this._pixelData || this._size != numItems * bytesPerItem)
  95. {
  96. if (bytesPerItem > 1) this._pixelData = new Float32Array(numItems);
  97. else this._pixelData = new Uint8Array(numItems);
  98.  
  99. this._size = numItems * bytesPerItem;
  100. }
  101.  
  102. let channelType = gl.UNSIGNED_BYTE;
  103. if (bytesPerItem > 1)channelType = gl.FLOAT;
  104.  
  105. if (this._size == 0 || !this._pixelData)
  106. {
  107. this._log.error("readpixel size 0", this._size, w, h);
  108. return;
  109. }
  110.  
  111. if (this._finishedFence)
  112. {
  113. this._pbo = gl.createBuffer();
  114. gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
  115. gl.bufferData(gl.PIXEL_PACK_BUFFER, this._pixelData.byteLength, gl.DYNAMIC_READ);
  116. gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  117. gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
  118. cgl.profileData.profileFencedPixelRead++;
  119.  
  120. if (this._size != numItems * bytesPerItem)
  121. this._log.error("buffer size invalid", numItems, w, h, bytesPerItem);
  122.  
  123. let dataType = pixelInfo.glDataType;
  124. if (bytesPerItem > 1)dataType = cgl.gl.FLOAT;
  125.  
  126. let format = pixelInfo.glDataFormat;
  127. gl.readPixels(x, y, w, h, format, dataType, 0);
  128.  
  129. gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
  130. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  131. }
  132. let startLength = this._pixelData.byteLength;
  133.  
  134. if (this._finishedFence && this._pbo)
  135. this._fence(cgl).then((error) =>
  136. {
  137. this._wasTriggered = false;
  138. this._finishedFence = true;
  139.  
  140. if (!error && this._pixelData && this._pixelData.byteLength == startLength)
  141. {
  142. gl.bindBuffer(gl.PIXEL_PACK_BUFFER, this._pbo);
  143. gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, this._pixelData);
  144. gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
  145.  
  146. if (finishedcb) finishedcb(this._pixelData);
  147. }
  148. gl.deleteBuffer(this._pbo);
  149. this._pbo = null;
  150. });
  151.  
  152. return true;
  153. }
  154. }
  155.  
  156. export { PixelReader };