Home Reference Source

cables_dev/cables/src/core/cgp/cgp_state.js

  1. import { Logger } from "cables-shared-client";
  2. import { CG } from "../cg/cg_constants.js";
  3. import { CGState } from "../cg/cg_state.js";
  4. import Shader from "./cgp_shader.js";
  5. import defaultShaderSrcVert from "./cgl_shader_default.wgsl";
  6. import Texture from "./cgp_texture.js";
  7. import CgTexture from "../cg/cg_texture.js";
  8.  
  9. // https://github.com/greggman/webgpu-utils
  10. // https://developer.chrome.com/blog/from-webgl-to-webgpu/
  11. // https://gpuweb.github.io/gpuweb/explainer/
  12.  
  13.  
  14. /**
  15. * cables webgpu context/state manager
  16. * @class
  17. * @namespace external:CGP
  18. * @hideconstructor
  19. */
  20. // const Context = function (_patch)
  21. class WebGpuContext extends CGState
  22. {
  23. constructor(_patch)
  24. {
  25. super();
  26.  
  27. this.patch = _patch;
  28.  
  29. this.lastErrorMsg = "";
  30.  
  31. this._log = new Logger("WebGpuContext");
  32. this.gApi = CG.GAPI_WEBGPU;
  33. this._viewport = [0, 0, 256, 256];
  34. this._shaderStack = [];
  35. this._simpleShader = null;
  36. this.frame = 0;
  37. this.catchErrors = false;
  38.  
  39. this._stackCullFaceFacing = [];
  40. this._stackDepthTest = [];
  41. this._stackCullFace = [];
  42. this._stackDepthFunc = [];
  43. this._stackDepthWrite = [];
  44. this._stackErrorScope = [];
  45. this._stackBlend = [];
  46. this._stackErrorScopeLogs = [];
  47.  
  48. this._defaultBlend = {
  49. "color": {
  50. "operation": "add",
  51. "srcFactor": "one",
  52. "dstFactor": "zero",
  53. },
  54. "alpha": {
  55. "operation": "add",
  56. "srcFactor": "one",
  57. "dstFactor": "zero",
  58. },
  59. };
  60.  
  61. this.DEPTH_FUNCS = [
  62. "never",
  63. "always",
  64. "less",
  65. "less-equal",
  66. "greater",
  67. "greater-equal",
  68. "equal",
  69. "not-equal"
  70. ];
  71.  
  72. this.CULL_MODES = [
  73. "none",
  74. "back",
  75. "front",
  76. "none" // both does not exist in webgpu
  77. ];
  78. }
  79.  
  80.  
  81. /// ////////////////////
  82.  
  83. // getViewPort()
  84. // {
  85. // return [0, 0, this.canvasWidth, this.canvasHeight];
  86. // }
  87.  
  88. renderStart(cgp, identTranslate, identTranslateView)
  89. {
  90. this.frame++;
  91. this.pushErrorScope("cgpstate internal", "internal");
  92. this.pushErrorScope("cgpstate out-of-memory", "out-of-memory");
  93.  
  94. if (!this._simpleShader)
  95. {
  96. this._simpleShader = new Shader(this, "simple default shader");
  97. this._simpleShader.setSource(defaultShaderSrcVert);
  98. this._simpleShader.addUniformFrag("4f", "color", [1, 1, 0, 1]);
  99. }
  100.  
  101. this.fpsCounter.startFrame();
  102.  
  103. this._startMatrixStacks(identTranslate, identTranslateView);
  104. this.setViewPort(0, 0, this.canvasWidth, this.canvasHeight);
  105.  
  106. this.pushShader(this._simpleShader);
  107. this.pushDepthTest(true);
  108. this.pushDepthWrite(true);
  109. this.pushDepthFunc("less-equal");
  110.  
  111.  
  112. this.pushBlend(this._defaultBlend);
  113.  
  114. this.emitEvent("beginFrame");
  115. }
  116.  
  117. renderEnd()
  118. {
  119. this._endMatrixStacks();
  120.  
  121. this.popShader();
  122. this.popDepthFunc();
  123. this.popDepthWrite();
  124. this.popDepthTest();
  125.  
  126. this.popErrorScope();
  127. this.popErrorScope();
  128.  
  129. if (this._stackErrorScope.length > 0)console.log("scope stack length invalid...");
  130.  
  131. this.emitEvent("endFrame");
  132. this.fpsCounter.endFrame();
  133. }
  134.  
  135.  
  136. setViewPort(x, y, w, h)
  137. {
  138. this._viewport = [x, y, w, h];
  139. }
  140.  
  141. /**
  142. * @function getViewPort
  143. * @memberof Context
  144. * @instance
  145. * @description get current gl viewport
  146. * @returns {Array} array [x,y,w,h]
  147. */
  148. getViewPort()
  149. {
  150. return this._viewPort;
  151. }
  152.  
  153. createMesh(geom, glPrimitive)
  154. {
  155. return new CGP.Mesh(this, geom, glPrimitive);
  156. }
  157.  
  158. /**
  159. * push a shader to the shader stack
  160. * @function pushShader
  161. * @memberof Context
  162. * @instance
  163. * @param {Object} shader
  164. * @function
  165. */
  166. pushShader(shader)
  167. {
  168. this._shaderStack.push(shader);
  169. // currentShader = shader;
  170. }
  171.  
  172. /**
  173. * pop current used shader from shader stack
  174. * @function popShader
  175. * @memberof Context
  176. * @instance
  177. * @function
  178. */
  179. popShader()
  180. {
  181. if (this._shaderStack.length === 0) throw new Error("Invalid shader stack pop!");
  182. this._shaderStack.pop();
  183. // currentShader = this._shaderStack[this._shaderStack.length - 1];
  184. }
  185.  
  186. getShader()
  187. {
  188. return this._shaderStack[this._shaderStack.length - 1];
  189. // if (currentShader) if (!this.frameStore || ((this.frameStore.renderOffscreen === true) == currentShader.offScreenPass) === true) return currentShader;
  190. // for (let i = this._shaderStack.length - 1; i >= 0; i--) if (this._shaderStack[i]) if (this.frameStore.renderOffscreen == this._shaderStack[i].offScreenPass) return this._shaderStack[i];
  191. }
  192.  
  193. setDevice(device)
  194. {
  195. this.device = device;
  196.  
  197.  
  198. if (this._emptyTexture) this._emptyTexture = this._emptyTexture.dispose();
  199. if (this._defaultTexture) this._defaultTexture = this._defaultTexture.dispose();
  200. if (this._errorTexture) this._errorTexture = this._errorTexture.dispose();
  201.  
  202. this.emitEvent("deviceChange");
  203. }
  204.  
  205. pushErrorScope(name, options = {})
  206. {
  207. if (this.catchErrors)
  208. {
  209. this._stackErrorScope.push(name);
  210. this._stackErrorScopeLogs.push(options.logger || null);
  211. this.device.pushErrorScope(options.scope || "validation");
  212. }
  213. }
  214.  
  215. popErrorScope(cb)
  216. {
  217. if (this.catchErrors)
  218. {
  219. const name = this._stackErrorScope.pop();
  220. const logger = this._stackErrorScopeLogs.pop();
  221. this.device.popErrorScope().then((error) =>
  222. {
  223. if (error)
  224. {
  225. if (this.lastErrorMsg == error.message)
  226. {
  227. // this._log.warn("last error once more...");
  228. }
  229. else
  230. {
  231. (logger || this._log).error(error.constructor.name, "in", name);
  232. (logger || this._log).error(error.message);
  233. }
  234. this.lastErrorMsg = error.message;
  235.  
  236. if (cb)cb(error);
  237. }
  238. });
  239. }
  240. }
  241.  
  242. /**
  243. * push depth testing enabled state
  244. * @function pushDepthTest
  245. * @param {Boolean} b enabled
  246. * @memberof Context
  247. * @instance
  248. */
  249. pushDepthTest(b)
  250. {
  251. this._stackDepthTest.push(b);
  252. }
  253.  
  254. /**
  255. * current state of depth testing
  256. * @function stateDepthTest
  257. * @returns {Boolean} enabled
  258. * @memberof Context
  259. * @instance
  260. */
  261. stateDepthTest()
  262. {
  263. return this._stackDepthTest[this._stackDepthTest.length - 1];
  264. }
  265.  
  266. /**
  267. * pop depth testing state
  268. * @function popDepthTest
  269. * @memberof Context
  270. * @instance
  271. */
  272. popDepthTest()
  273. {
  274. this._stackDepthTest.pop();
  275. }
  276.  
  277. // --------------------------------------
  278. // state depthwrite
  279.  
  280. /**
  281. * push depth write enabled state
  282. * @function pushDepthWrite
  283. * @param {Boolean} b enabled
  284. * @memberof Context
  285. * @instance
  286. */
  287. pushDepthWrite(b)
  288. {
  289. b = b || false;
  290. this._stackDepthWrite.push(b);
  291. }
  292.  
  293. /**
  294. * current state of depth writing
  295. * @function stateCullFace
  296. * @returns {Boolean} enabled
  297. * @memberof Context
  298. * @instance
  299. */
  300. stateDepthWrite()
  301. {
  302. return this._stackDepthWrite[this._stackDepthWrite.length - 1];
  303. }
  304.  
  305. /**
  306. * pop depth writing state
  307. * @function popCullFace
  308. * @memberof Context
  309. * @instance
  310. */
  311. popDepthWrite()
  312. {
  313. this._stackDepthWrite.pop();
  314. }
  315.  
  316. // --------------------------------------
  317. // state depthfunc
  318.  
  319. /**
  320. * @function pushDepthFunc
  321. * @memberof Context
  322. * @instance
  323. * @param {string} f depth compare func
  324. */
  325. pushDepthFunc(f)
  326. {
  327. this._stackDepthFunc.push(f);
  328. }
  329.  
  330. /**
  331. * @function stateDepthFunc
  332. * @memberof Context
  333. * @instance
  334. * @returns {string}
  335. */
  336. stateDepthFunc()
  337. {
  338. if (this._stackDepthFunc.length > 0) return this._stackDepthFunc[this._stackDepthFunc.length - 1];
  339. return false;
  340. }
  341.  
  342. /**
  343. * pop depth compare func
  344. * @function popDepthFunc
  345. * @memberof Context
  346. * @instance
  347. */
  348. popDepthFunc()
  349. {
  350. this._stackDepthFunc.pop();
  351. }
  352.  
  353. // --------------------------------------
  354. // state CullFace
  355.  
  356. /**
  357. * push face culling face enabled state
  358. * @function pushCullFace
  359. * @param {Boolean} b enabled
  360. * @memberof Context
  361. * @instance
  362. */
  363. pushCullFace(b)
  364. {
  365. this._stackCullFace.push(b);
  366. }
  367.  
  368. /**
  369. * current state of face culling
  370. * @function stateCullFace
  371. * @returns {Boolean} enabled
  372. * @memberof Context
  373. * @instance
  374. */
  375. stateCullFace()
  376. {
  377. return this._stackCullFace[this._stackCullFace.length - 1];
  378. }
  379.  
  380. /**
  381. * pop face culling enabled state
  382. * @function popCullFace
  383. * @memberof Context
  384. * @instance
  385. */
  386. popCullFace()
  387. {
  388. this._stackCullFace.pop();
  389. }
  390.  
  391. // --------------------------------------
  392. // state CullFace Facing
  393.  
  394. /**
  395. * push face culling face side
  396. * @function pushCullFaceFacing
  397. * @memberof Context
  398. * @param b
  399. * @instance
  400. */
  401. pushCullFaceFacing(b)
  402. {
  403. this._stackCullFaceFacing.push(b);
  404. }
  405.  
  406. /**
  407. * current state of face culling side
  408. * @function stateCullFaceFacing
  409. * @returns {Boolean} enabled
  410. * @memberof Context
  411. * @instance
  412. */
  413. stateCullFaceFacing()
  414. {
  415. return this._stackCullFaceFacing[this._stackCullFaceFacing.length - 1];
  416. }
  417.  
  418. /**
  419. * pop face culling face side
  420. * @function popCullFaceFacing
  421. * @memberof Context
  422. * @instance
  423. */
  424. popCullFaceFacing()
  425. {
  426. this._stackCullFaceFacing.pop();
  427. }
  428.  
  429. pushBlend(b)
  430. {
  431. this._stackBlend.push(b);
  432. }
  433.  
  434. popBlend()
  435. {
  436. this._stackBlend.pop();
  437. }
  438.  
  439. stateBlend()
  440. {
  441. return this._stackBlend[this._stackBlend.length - 1];
  442. }
  443.  
  444. getEmptyTexture()
  445. {
  446. if (this._emptyTexture) return this._emptyTexture;
  447. const size = 8;
  448. this._emptyTexture = new Texture(this, {});
  449. this._emptyTexture.initFromData(CgTexture.getDefaultTextureData("empty", size), size, size);
  450. return this._emptyTexture;
  451. }
  452.  
  453. getErrorTexture()
  454. {
  455. // if (this._errorTexture) return this._errorTexture;
  456. const size = 256;
  457. this._errorTexture = new Texture(this, {});
  458. this._errorTexture.initFromData(CgTexture.getDefaultTextureData("stripes", size, { "r": 1, "g": 0, "b": 0 }), size, size);
  459. return this._errorTexture;
  460. }
  461.  
  462. getDefaultTexture()
  463. {
  464. if (this._defaultTexture) return this._defaultTexture;
  465. const size = 256;
  466. this._defaultTexture = new Texture(this, {});
  467. this._defaultTexture.initFromData(CgTexture.getDefaultTextureData("stripes", size), size, size);
  468. return this._defaultTexture;
  469. }
  470. }
  471. export { WebGpuContext };