state.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /**
  2. * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { GLRenderingContext } from './compat';
  7. export type WebGLState = {
  8. currentProgramId: number
  9. currentMaterialId: number
  10. currentRenderItemId: number
  11. /**
  12. * specifies which WebGL capability to enable
  13. * - `gl.BLEND`: blending of the computed fragment color values
  14. * - `gl.CULL_FACE`: culling of polygons
  15. * - `gl.DEPTH_TEST`: depth comparisons and updates to the depth buffer
  16. * - `gl.DITHER`: dithering of color components before they get written to the color buffer
  17. * - `gl.POLYGON_OFFSET_FILL`: adding an offset to depth values of polygon's fragments
  18. * - `gl.SAMPLE_ALPHA_TO_COVERAGE`: computation of a temporary coverage value determined by the alpha value
  19. * - `gl.SAMPLE_COVERAGE`: ANDing the fragment's coverage with the temporary coverage value
  20. * - `gl.SCISSOR_TEST`: scissor test that discards fragments that are outside of the scissor rectangle
  21. * - `gl.STENCIL_TEST`: stencil testing and updates to the stencil buffer
  22. * - `ext.CLIP_DISTANCE[0-7]`: clip distance 0 to 7 (with `ext` being `WEBGL_clip_cull_distance`)
  23. */
  24. enable: (cap: number) => void
  25. /**
  26. * specifies which WebGL capability to disable
  27. * - `gl.BLEND`: blending of the computed fragment color values
  28. * - `gl.CULL_FACE`: culling of polygons
  29. * - `gl.DEPTH_TEST`: depth comparisons and updates to the depth buffer
  30. * - `gl.DITHER`: dithering of color components before they get written to the color buffer
  31. * - `gl.POLYGON_OFFSET_FILL`: adding an offset to depth values of polygon's fragments
  32. * - `gl.SAMPLE_ALPHA_TO_COVERAGE`: computation of a temporary coverage value determined by the alpha value
  33. * - `gl.SAMPLE_COVERAGE`: ANDing the fragment's coverage with the temporary coverage value
  34. * - `gl.SCISSOR_TEST`: scissor test that discards fragments that are outside of the scissor rectangle
  35. * - `gl.STENCIL_TEST`: stencil testing and updates to the stencil buffer
  36. * - `ext.CLIP_DISTANCE[0-7]`: clip distance 0 to 7 (with `ext` being `WEBGL_clip_cull_distance`)
  37. */
  38. disable: (cap: number) => void
  39. /** specifies whether polygons are front- or back-facing by setting a winding orientation */
  40. frontFace: (mode: number) => void
  41. /** specifies whether or not front- and/or back-facing polygons can be culled */
  42. cullFace: (mode: number) => void
  43. /** sets whether writing into the depth buffer is enabled or disabled */
  44. depthMask: (flag: boolean) => void
  45. /** specifies the depth value used when clearing depth buffer, used when calling `gl.clear` */
  46. clearDepth: (depth: number) => void
  47. /** sets the depth comparison function */
  48. depthFunc: (func: number) => void
  49. /** sets which color components to enable or to disable */
  50. colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => void
  51. /** specifies the color values used when clearing color buffers, used when calling `gl.clear`, clamped to [0, 1] */
  52. clearColor: (red: number, green: number, blue: number, alpha: number) => void
  53. /** defines which function is used for blending pixel arithmetic */
  54. blendFunc: (src: number, dst: number) => void
  55. /** defines which function is used for blending pixel arithmetic for RGB and alpha components separately */
  56. blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => void
  57. /** set both the RGB blend equation and alpha blend equation to a single equation, determines how a new pixel is combined with an existing */
  58. blendEquation: (mode: number) => void
  59. /** set the RGB blend equation and alpha blend equation separately, determines how a new pixel is combined with an existing */
  60. blendEquationSeparate: (modeRGB: number, modeAlpha: number) => void
  61. /** specifies the source and destination blending factors, clamped to [0, 1] */
  62. blendColor: (red: number, green: number, blue: number, alpha: number) => void
  63. /** sets the front and back function and reference value for stencil testing */
  64. stencilFunc: (func: number, ref: number, mask: number) => void
  65. /** sets the front and/or back function and reference value for stencil testing */
  66. stencilFuncSeparate: (face: number, func: number, ref: number, mask: number) => void
  67. /** controls enabling and disabling of both the front and back writing of individual bits in the stencil planes */
  68. stencilMask: (mask: number) => void
  69. /** controls enabling and disabling of both the front and back writing of individual bits in the stencil planes */
  70. stencilMaskSeparate: (face: number, mask: number) => void
  71. /** sets both the front and back-facing stencil test actions */
  72. stencilOp: (fail: number, zfail: number, zpass: number) => void
  73. /** sets the front and/or back-facing stencil test actions */
  74. stencilOpSeparate: (face: number, fail: number, zfail: number, zpass: number) => void
  75. enableVertexAttrib: (index: number) => void
  76. clearVertexAttribsState: () => void
  77. disableUnusedVertexAttribs: () => void
  78. viewport: (x: number, y: number, width: number, height: number) => void
  79. scissor: (x: number, y: number, width: number, height: number) => void
  80. reset: () => void
  81. }
  82. export function createState(gl: GLRenderingContext): WebGLState {
  83. let enabledCapabilities: Record<number, boolean> = {};
  84. let currentFrontFace = gl.getParameter(gl.FRONT_FACE);
  85. let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE);
  86. let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK);
  87. let currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE);
  88. let currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC);
  89. let currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK);
  90. let currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
  91. let currentBlendSrcRGB = gl.getParameter(gl.BLEND_SRC_RGB);
  92. let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB);
  93. let currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA);
  94. let currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA);
  95. let currentBlendColor = gl.getParameter(gl.BLEND_COLOR);
  96. let currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
  97. let currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);
  98. let currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
  99. let currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK);
  100. let currentStencilRef = gl.getParameter(gl.STENCIL_REF);
  101. let currentStencilBackFunc = gl.getParameter(gl.STENCIL_BACK_FUNC);
  102. let currentStencilBackValueMask = gl.getParameter(gl.STENCIL_BACK_VALUE_MASK);
  103. let currentStencilBackRef = gl.getParameter(gl.STENCIL_BACK_REF);
  104. let currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK);
  105. let currentStencilBackWriteMask = gl.getParameter(gl.STENCIL_BACK_WRITEMASK);
  106. let currentStencilFail = gl.getParameter(gl.STENCIL_FAIL);
  107. let currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
  108. let currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
  109. let currentStencilBackFail = gl.getParameter(gl.STENCIL_BACK_FAIL);
  110. let currentStencilBackPassDepthPass = gl.getParameter(gl.STENCIL_BACK_PASS_DEPTH_PASS);
  111. let currentStencilBackPassDepthFail = gl.getParameter(gl.STENCIL_BACK_PASS_DEPTH_FAIL);
  112. let maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
  113. const vertexAttribsState: number[] = [];
  114. let currentViewport: [number, number, number, number] = gl.getParameter(gl.VIEWPORT);
  115. let currentScissor: [number, number, number, number] = gl.getParameter(gl.SCISSOR_BOX);
  116. const clearVertexAttribsState = () => {
  117. for (let i = 0; i < maxVertexAttribs; ++i) {
  118. vertexAttribsState[i] = 0;
  119. }
  120. };
  121. clearVertexAttribsState();
  122. return {
  123. currentProgramId: -1,
  124. currentMaterialId: -1,
  125. currentRenderItemId: -1,
  126. enable: (cap: number) => {
  127. if (enabledCapabilities[cap] !== true) {
  128. gl.enable(cap);
  129. enabledCapabilities[cap] = true;
  130. }
  131. },
  132. disable: (cap: number) => {
  133. if (enabledCapabilities[cap] !== false) {
  134. gl.disable(cap);
  135. enabledCapabilities[cap] = false;
  136. }
  137. },
  138. frontFace: (mode: number) => {
  139. if (mode !== currentFrontFace) {
  140. gl.frontFace(mode);
  141. currentFrontFace = mode;
  142. }
  143. },
  144. cullFace: (mode: number) => {
  145. if (mode !== currentCullFace) {
  146. gl.cullFace(mode);
  147. currentCullFace = mode;
  148. }
  149. },
  150. depthMask: (flag: boolean) => {
  151. if (flag !== currentDepthMask) {
  152. gl.depthMask(flag);
  153. currentDepthMask = flag;
  154. }
  155. },
  156. clearDepth: (depth: number) => {
  157. if (depth !== currentClearDepth) {
  158. gl.clearDepth(depth);
  159. currentClearDepth = depth;
  160. }
  161. },
  162. depthFunc: (func: number) => {
  163. if (func !== currentDepthFunc) {
  164. gl.depthFunc(func);
  165. currentDepthFunc = func;
  166. }
  167. },
  168. colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => {
  169. if (red !== currentColorMask[0] || green !== currentColorMask[1] || blue !== currentColorMask[2] || alpha !== currentColorMask[3]) {
  170. gl.colorMask(red, green, blue, alpha);
  171. currentColorMask[0] = red;
  172. currentColorMask[1] = green;
  173. currentColorMask[2] = blue;
  174. currentColorMask[3] = alpha;
  175. }
  176. },
  177. clearColor: (red: number, green: number, blue: number, alpha: number) => {
  178. if (red !== currentClearColor[0] || green !== currentClearColor[1] || blue !== currentClearColor[2] || alpha !== currentClearColor[3]) {
  179. gl.clearColor(red, green, blue, alpha);
  180. currentClearColor[0] = red;
  181. currentClearColor[1] = green;
  182. currentClearColor[2] = blue;
  183. currentClearColor[3] = alpha;
  184. }
  185. },
  186. blendFunc: (src: number, dst: number) => {
  187. if (src !== currentBlendSrcRGB || dst !== currentBlendDstRGB || src !== currentBlendSrcAlpha || dst !== currentBlendDstAlpha) {
  188. gl.blendFunc(src, dst);
  189. currentBlendSrcRGB = src;
  190. currentBlendDstRGB = dst;
  191. currentBlendSrcAlpha = src;
  192. currentBlendDstAlpha = dst;
  193. }
  194. },
  195. blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => {
  196. if (srcRGB !== currentBlendSrcRGB || dstRGB !== currentBlendDstRGB || srcAlpha !== currentBlendSrcAlpha || dstAlpha !== currentBlendDstAlpha) {
  197. gl.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
  198. currentBlendSrcRGB = srcRGB;
  199. currentBlendDstRGB = dstRGB;
  200. currentBlendSrcAlpha = srcAlpha;
  201. currentBlendDstAlpha = dstAlpha;
  202. }
  203. },
  204. blendEquation: (mode: number) => {
  205. if (mode !== currentBlendEqRGB || mode !== currentBlendEqAlpha) {
  206. gl.blendEquation(mode);
  207. currentBlendEqRGB = mode;
  208. currentBlendEqAlpha = mode;
  209. }
  210. },
  211. blendEquationSeparate: (modeRGB: number, modeAlpha: number) => {
  212. if (modeRGB !== currentBlendEqRGB || modeAlpha !== currentBlendEqAlpha) {
  213. gl.blendEquationSeparate(modeRGB, modeAlpha);
  214. currentBlendEqRGB = modeRGB;
  215. currentBlendEqAlpha = modeAlpha;
  216. }
  217. },
  218. blendColor: (red: number, green: number, blue: number, alpha: number) => {
  219. if (red !== currentBlendColor[0] || green !== currentBlendColor[1] || blue !== currentBlendColor[2] || alpha !== currentBlendColor[3]) {
  220. gl.blendColor(red, green, blue, alpha);
  221. currentBlendColor[0] = red;
  222. currentBlendColor[1] = green;
  223. currentBlendColor[2] = blue;
  224. currentBlendColor[3] = alpha;
  225. }
  226. },
  227. stencilFunc: (func: number, ref: number, mask: number) => {
  228. if (func !== currentStencilFunc || ref !== currentStencilRef || mask !== currentStencilValueMask || func !== currentStencilBackFunc || ref !== currentStencilBackRef || mask !== currentStencilBackValueMask) {
  229. gl.stencilFunc(func, ref, mask);
  230. currentStencilFunc = func;
  231. currentStencilRef = ref;
  232. currentStencilValueMask = mask;
  233. currentStencilBackFunc = func;
  234. currentStencilBackRef = ref;
  235. currentStencilBackValueMask = mask;
  236. }
  237. },
  238. stencilFuncSeparate: (face: number, func: number, ref: number, mask: number) => {
  239. if (face === gl.FRONT) {
  240. if (func !== currentStencilFunc || ref !== currentStencilRef || mask !== currentStencilValueMask) {
  241. gl.stencilFuncSeparate(face, func, ref, mask);
  242. currentStencilFunc = func;
  243. currentStencilRef = ref;
  244. currentStencilValueMask = mask;
  245. }
  246. } else if (face === gl.BACK) {
  247. if (func !== currentStencilBackFunc || ref !== currentStencilBackRef || mask !== currentStencilBackValueMask) {
  248. gl.stencilFuncSeparate(face, func, ref, mask);
  249. currentStencilBackFunc = func;
  250. currentStencilBackRef = ref;
  251. currentStencilBackValueMask = mask;
  252. }
  253. } else if (face === gl.FRONT_AND_BACK) {
  254. if (func !== currentStencilFunc || ref !== currentStencilRef || mask !== currentStencilValueMask || func !== currentStencilBackFunc || ref !== currentStencilBackRef || mask !== currentStencilBackValueMask) {
  255. gl.stencilFuncSeparate(face, func, ref, mask);
  256. currentStencilFunc = func;
  257. currentStencilRef = ref;
  258. currentStencilValueMask = mask;
  259. currentStencilBackFunc = func;
  260. currentStencilBackRef = ref;
  261. currentStencilBackValueMask = mask;
  262. }
  263. }
  264. },
  265. stencilMask: (mask: number) => {
  266. if (mask !== currentStencilWriteMask || mask !== currentStencilBackWriteMask) {
  267. gl.stencilMask(mask);
  268. currentStencilWriteMask = mask;
  269. currentStencilBackWriteMask = mask;
  270. }
  271. },
  272. stencilMaskSeparate: (face: number, mask: number) => {
  273. if (face === gl.FRONT) {
  274. if (mask !== currentStencilWriteMask) {
  275. gl.stencilMaskSeparate(face, mask);
  276. currentStencilWriteMask = mask;
  277. }
  278. } else if (face === gl.BACK) {
  279. if (mask !== currentStencilBackWriteMask) {
  280. gl.stencilMaskSeparate(face, mask);
  281. currentStencilBackWriteMask = mask;
  282. }
  283. } else if (face === gl.FRONT_AND_BACK) {
  284. if (mask !== currentStencilWriteMask || mask !== currentStencilBackWriteMask) {
  285. gl.stencilMaskSeparate(face, mask);
  286. currentStencilWriteMask = mask;
  287. currentStencilBackWriteMask = mask;
  288. }
  289. }
  290. },
  291. stencilOp: (fail: number, zfail: number, zpass: number) => {
  292. if (fail !== currentStencilFail || zfail !== currentStencilPassDepthFail || zpass !== currentStencilPassDepthPass || fail !== currentStencilBackFail || zfail !== currentStencilBackPassDepthFail || zpass !== currentStencilBackPassDepthPass) {
  293. gl.stencilOp(fail, zfail, zpass);
  294. currentStencilFail = fail;
  295. currentStencilPassDepthFail = zfail;
  296. currentStencilPassDepthPass = zpass;
  297. currentStencilBackFail = fail;
  298. currentStencilBackPassDepthFail = zfail;
  299. currentStencilBackPassDepthPass = zpass;
  300. }
  301. },
  302. stencilOpSeparate: (face: number, fail: number, zfail: number, zpass: number) => {
  303. if (face === gl.FRONT) {
  304. if (fail !== currentStencilFail || zfail !== currentStencilPassDepthFail || zpass !== currentStencilPassDepthPass) {
  305. gl.stencilOpSeparate(face, fail, zfail, zpass);
  306. currentStencilFail = fail;
  307. currentStencilPassDepthFail = zfail;
  308. currentStencilPassDepthPass = zpass;
  309. }
  310. } else if (face === gl.BACK) {
  311. if (fail !== currentStencilBackFail || zfail !== currentStencilBackPassDepthFail || zpass !== currentStencilBackPassDepthPass) {
  312. gl.stencilOpSeparate(face, fail, zfail, zpass);
  313. currentStencilBackFail = fail;
  314. currentStencilBackPassDepthFail = zfail;
  315. currentStencilBackPassDepthPass = zpass;
  316. }
  317. } else if (face === gl.FRONT_AND_BACK) {
  318. if (fail !== currentStencilFail || zfail !== currentStencilPassDepthFail || zpass !== currentStencilPassDepthPass || fail !== currentStencilBackFail || zfail !== currentStencilBackPassDepthFail || zpass !== currentStencilBackPassDepthPass) {
  319. gl.stencilOpSeparate(face, fail, zfail, zpass);
  320. currentStencilFail = fail;
  321. currentStencilPassDepthFail = zfail;
  322. currentStencilPassDepthPass = zpass;
  323. currentStencilBackFail = fail;
  324. currentStencilBackPassDepthFail = zfail;
  325. currentStencilBackPassDepthPass = zpass;
  326. }
  327. }
  328. },
  329. enableVertexAttrib: (index: number) => {
  330. gl.enableVertexAttribArray(index);
  331. vertexAttribsState[index] = 1;
  332. },
  333. clearVertexAttribsState,
  334. disableUnusedVertexAttribs: () => {
  335. for (let i = 0; i < maxVertexAttribs; ++i) {
  336. if (vertexAttribsState[i] === 0) gl.disableVertexAttribArray(i);
  337. }
  338. },
  339. viewport: (x: number, y: number, width: number, height: number) => {
  340. if (x !== currentViewport[0] || y !== currentViewport[1] || width !== currentViewport[2] || height !== currentViewport[3]) {
  341. gl.viewport(x, y, width, height);
  342. currentViewport[0] = x;
  343. currentViewport[1] = y;
  344. currentViewport[2] = width;
  345. currentViewport[3] = height;
  346. }
  347. },
  348. scissor: (x: number, y: number, width: number, height: number) => {
  349. if (x !== currentScissor[0] || y !== currentScissor[1] || width !== currentScissor[2] || height !== currentScissor[3]) {
  350. gl.scissor(x, y, width, height);
  351. currentScissor[0] = x;
  352. currentScissor[1] = y;
  353. currentScissor[2] = width;
  354. currentScissor[3] = height;
  355. }
  356. },
  357. reset: () => {
  358. enabledCapabilities = {};
  359. currentFrontFace = gl.getParameter(gl.FRONT_FACE);
  360. currentCullFace = gl.getParameter(gl.CULL_FACE_MODE);
  361. currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK);
  362. currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE);
  363. currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC);
  364. currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK);
  365. currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
  366. currentBlendSrcRGB = gl.getParameter(gl.BLEND_SRC_RGB);
  367. currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB);
  368. currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA);
  369. currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA);
  370. currentBlendColor = gl.getParameter(gl.BLEND_COLOR);
  371. currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
  372. currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);
  373. currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
  374. currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK);
  375. currentStencilRef = gl.getParameter(gl.STENCIL_REF);
  376. currentStencilBackFunc = gl.getParameter(gl.STENCIL_BACK_FUNC);
  377. currentStencilBackValueMask = gl.getParameter(gl.STENCIL_BACK_VALUE_MASK);
  378. currentStencilBackRef = gl.getParameter(gl.STENCIL_BACK_REF);
  379. currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK);
  380. currentStencilBackWriteMask = gl.getParameter(gl.STENCIL_BACK_WRITEMASK);
  381. currentStencilFail = gl.getParameter(gl.STENCIL_FAIL);
  382. currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
  383. currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
  384. currentStencilBackFail = gl.getParameter(gl.STENCIL_BACK_FAIL);
  385. currentStencilBackPassDepthPass = gl.getParameter(gl.STENCIL_BACK_PASS_DEPTH_PASS);
  386. currentStencilBackPassDepthFail = gl.getParameter(gl.STENCIL_BACK_PASS_DEPTH_FAIL);
  387. maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
  388. vertexAttribsState.length = 0;
  389. for (let i = 0; i < maxVertexAttribs; ++i) {
  390. vertexAttribsState[i] = 0;
  391. }
  392. currentViewport = gl.getParameter(gl.VIEWPORT);
  393. currentScissor = gl.getParameter(gl.SCISSOR_BOX);
  394. }
  395. };
  396. }