RenderState.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*global define*/
  2. define([
  3. '../Core/BoundingRectangle',
  4. '../Core/Color',
  5. '../Core/defaultValue',
  6. '../Core/defined',
  7. '../Core/DeveloperError',
  8. '../Core/RuntimeError',
  9. '../Core/WindingOrder'
  10. ], function(
  11. BoundingRectangle,
  12. Color,
  13. defaultValue,
  14. defined,
  15. DeveloperError,
  16. RuntimeError,
  17. WindingOrder) {
  18. "use strict";
  19. /*global WebGLRenderingContext*/
  20. function validateBlendEquation(blendEquation) {
  21. return ((blendEquation === WebGLRenderingContext.FUNC_ADD) ||
  22. (blendEquation === WebGLRenderingContext.FUNC_SUBTRACT) ||
  23. (blendEquation === WebGLRenderingContext.FUNC_REVERSE_SUBTRACT));
  24. }
  25. function validateBlendFunction(blendFunction) {
  26. return ((blendFunction === WebGLRenderingContext.ZERO) ||
  27. (blendFunction === WebGLRenderingContext.ONE) ||
  28. (blendFunction === WebGLRenderingContext.SRC_COLOR) ||
  29. (blendFunction === WebGLRenderingContext.ONE_MINUS_SRC_COLOR) ||
  30. (blendFunction === WebGLRenderingContext.DST_COLOR) ||
  31. (blendFunction === WebGLRenderingContext.ONE_MINUS_DST_COLOR) ||
  32. (blendFunction === WebGLRenderingContext.SRC_ALPHA) ||
  33. (blendFunction === WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) ||
  34. (blendFunction === WebGLRenderingContext.DST_ALPHA) ||
  35. (blendFunction === WebGLRenderingContext.ONE_MINUS_DST_ALPHA) ||
  36. (blendFunction === WebGLRenderingContext.CONSTANT_COLOR) ||
  37. (blendFunction === WebGLRenderingContext.ONE_MINUS_CONSTANT_COLOR) ||
  38. (blendFunction === WebGLRenderingContext.CONSTANT_ALPHA) ||
  39. (blendFunction === WebGLRenderingContext.ONE_MINUS_CONSTANT_ALPHA) ||
  40. (blendFunction === WebGLRenderingContext.SRC_ALPHA_SATURATE));
  41. }
  42. function validateCullFace(cullFace) {
  43. return ((cullFace === WebGLRenderingContext.FRONT) ||
  44. (cullFace === WebGLRenderingContext.BACK) ||
  45. (cullFace === WebGLRenderingContext.FRONT_AND_BACK));
  46. }
  47. function validateDepthFunction(depthFunction) {
  48. return ((depthFunction === WebGLRenderingContext.NEVER) ||
  49. (depthFunction === WebGLRenderingContext.LESS) ||
  50. (depthFunction === WebGLRenderingContext.EQUAL) ||
  51. (depthFunction === WebGLRenderingContext.LEQUAL) ||
  52. (depthFunction === WebGLRenderingContext.GREATER) ||
  53. (depthFunction === WebGLRenderingContext.NOTEQUAL) ||
  54. (depthFunction === WebGLRenderingContext.GEQUAL) ||
  55. (depthFunction === WebGLRenderingContext.ALWAYS));
  56. }
  57. function validateStencilFunction (stencilFunction) {
  58. return ((stencilFunction === WebGLRenderingContext.NEVER) ||
  59. (stencilFunction === WebGLRenderingContext.LESS) ||
  60. (stencilFunction === WebGLRenderingContext.EQUAL) ||
  61. (stencilFunction === WebGLRenderingContext.LEQUAL) ||
  62. (stencilFunction === WebGLRenderingContext.GREATER) ||
  63. (stencilFunction === WebGLRenderingContext.NOTEQUAL) ||
  64. (stencilFunction === WebGLRenderingContext.GEQUAL) ||
  65. (stencilFunction === WebGLRenderingContext.ALWAYS));
  66. }
  67. function validateStencilOperation(stencilOperation) {
  68. return ((stencilOperation === WebGLRenderingContext.ZERO) ||
  69. (stencilOperation === WebGLRenderingContext.KEEP) ||
  70. (stencilOperation === WebGLRenderingContext.REPLACE) ||
  71. (stencilOperation === WebGLRenderingContext.INCR) ||
  72. (stencilOperation === WebGLRenderingContext.DECR) ||
  73. (stencilOperation === WebGLRenderingContext.INVERT) ||
  74. (stencilOperation === WebGLRenderingContext.INCREMENT_WRAP) ||
  75. (stencilOperation === WebGLRenderingContext.DECR_WRAP));
  76. }
  77. /**
  78. * @private
  79. */
  80. var RenderState = function(context, renderState) {
  81. var rs = defaultValue(renderState, {});
  82. var cull = defaultValue(rs.cull, {});
  83. var polygonOffset = defaultValue(rs.polygonOffset, {});
  84. var scissorTest = defaultValue(rs.scissorTest, {});
  85. var scissorTestRectangle = defaultValue(scissorTest.rectangle, {});
  86. var depthRange = defaultValue(rs.depthRange, {});
  87. var depthTest = defaultValue(rs.depthTest, {});
  88. var colorMask = defaultValue(rs.colorMask, {});
  89. var blending = defaultValue(rs.blending, {});
  90. var blendingColor = defaultValue(blending.color, {});
  91. var stencilTest = defaultValue(rs.stencilTest, {});
  92. var stencilTestFrontOperation = defaultValue(stencilTest.frontOperation, {});
  93. var stencilTestBackOperation = defaultValue(stencilTest.backOperation, {});
  94. var sampleCoverage = defaultValue(rs.sampleCoverage, {});
  95. var viewport = rs.viewport;
  96. this.frontFace = defaultValue(rs.frontFace, WindingOrder.COUNTER_CLOCKWISE);
  97. this.cull = {
  98. enabled : defaultValue(cull.enabled, false),
  99. face : defaultValue(cull.face, WebGLRenderingContext.BACK)
  100. };
  101. this.lineWidth = defaultValue(rs.lineWidth, 1.0);
  102. this.polygonOffset = {
  103. enabled : defaultValue(polygonOffset.enabled, false),
  104. factor : defaultValue(polygonOffset.factor, 0),
  105. units : defaultValue(polygonOffset.units, 0)
  106. };
  107. this.scissorTest = {
  108. enabled : defaultValue(scissorTest.enabled, false),
  109. rectangle : BoundingRectangle.clone(scissorTestRectangle)
  110. };
  111. this.depthRange = {
  112. near : defaultValue(depthRange.near, 0),
  113. far : defaultValue(depthRange.far, 1)
  114. };
  115. this.depthTest = {
  116. enabled : defaultValue(depthTest.enabled, false),
  117. func : defaultValue(depthTest.func, WebGLRenderingContext.LESS) // func, because function is a JavaScript keyword
  118. };
  119. this.colorMask = {
  120. red : defaultValue(colorMask.red, true),
  121. green : defaultValue(colorMask.green, true),
  122. blue : defaultValue(colorMask.blue, true),
  123. alpha : defaultValue(colorMask.alpha, true)
  124. };
  125. this.depthMask = defaultValue(rs.depthMask, true);
  126. this.stencilMask = defaultValue(rs.stencilMask, ~0);
  127. this.blending = {
  128. enabled : defaultValue(blending.enabled, false),
  129. color : new Color(
  130. defaultValue(blendingColor.red, 0.0),
  131. defaultValue(blendingColor.green, 0.0),
  132. defaultValue(blendingColor.blue, 0.0),
  133. defaultValue(blendingColor.alpha, 0.0)
  134. ),
  135. equationRgb : defaultValue(blending.equationRgb, WebGLRenderingContext.FUNC_ADD),
  136. equationAlpha : defaultValue(blending.equationAlpha, WebGLRenderingContext.FUNC_ADD),
  137. functionSourceRgb : defaultValue(blending.functionSourceRgb, WebGLRenderingContext.ONE),
  138. functionSourceAlpha : defaultValue(blending.functionSourceAlpha, WebGLRenderingContext.ONE),
  139. functionDestinationRgb : defaultValue(blending.functionDestinationRgb, WebGLRenderingContext.ZERO),
  140. functionDestinationAlpha : defaultValue(blending.functionDestinationAlpha, WebGLRenderingContext.ZERO)
  141. };
  142. this.stencilTest = {
  143. enabled : defaultValue(stencilTest.enabled, false),
  144. frontFunction : defaultValue(stencilTest.frontFunction, WebGLRenderingContext.ALWAYS),
  145. backFunction : defaultValue(stencilTest.backFunction, WebGLRenderingContext.ALWAYS),
  146. reference : defaultValue(stencilTest.reference, 0),
  147. mask : defaultValue(stencilTest.mask, ~0),
  148. frontOperation : {
  149. fail : defaultValue(stencilTestFrontOperation.fail, WebGLRenderingContext.KEEP),
  150. zFail : defaultValue(stencilTestFrontOperation.zFail, WebGLRenderingContext.KEEP),
  151. zPass : defaultValue(stencilTestFrontOperation.zPass, WebGLRenderingContext.KEEP)
  152. },
  153. backOperation : {
  154. fail : defaultValue(stencilTestBackOperation.fail, WebGLRenderingContext.KEEP),
  155. zFail : defaultValue(stencilTestBackOperation.zFail, WebGLRenderingContext.KEEP),
  156. zPass : defaultValue(stencilTestBackOperation.zPass, WebGLRenderingContext.KEEP)
  157. }
  158. };
  159. this.sampleCoverage = {
  160. enabled : defaultValue(sampleCoverage.enabled, false),
  161. value : defaultValue(sampleCoverage.value, 1.0),
  162. invert : defaultValue(sampleCoverage.invert, false)
  163. };
  164. this.viewport = (defined(viewport)) ? new BoundingRectangle(viewport.x, viewport.y,
  165. (!defined(viewport.width)) ? context.drawingBufferWidth : viewport.width,
  166. (!defined(viewport.height)) ? context.drawingBufferHeight : viewport.height) : undefined;
  167. if ((this.lineWidth < context.minimumAliasedLineWidth) ||
  168. (this.lineWidth > context.maximumAliasedLineWidth)) {
  169. throw new RuntimeError('renderState.lineWidth is out of range. Check minimumAliasedLineWidth and maximumAliasedLineWidth.');
  170. }
  171. //>>includeStart('debug', pragmas.debug);
  172. if (!WindingOrder.validate(this.frontFace)) {
  173. throw new DeveloperError('Invalid renderState.frontFace.');
  174. }
  175. if (!validateCullFace(this.cull.face)) {
  176. throw new DeveloperError('Invalid renderState.cull.face.');
  177. }
  178. if ((this.scissorTest.rectangle.width < 0) ||
  179. (this.scissorTest.rectangle.height < 0)) {
  180. throw new DeveloperError('renderState.scissorTest.rectangle.width and renderState.scissorTest.rectangle.height must be greater than or equal to zero.');
  181. }
  182. if (this.depthRange.near > this.depthRange.far) {
  183. // WebGL specific - not an error in GL ES
  184. throw new DeveloperError('renderState.depthRange.near can not be greater than renderState.depthRange.far.');
  185. }
  186. if (this.depthRange.near < 0) {
  187. // Would be clamped by GL
  188. throw new DeveloperError('renderState.depthRange.near must be greater than or equal to zero.');
  189. }
  190. if (this.depthRange.far > 1) {
  191. // Would be clamped by GL
  192. throw new DeveloperError('renderState.depthRange.far must be less than or equal to one.');
  193. }
  194. if (!validateDepthFunction(this.depthTest.func)) {
  195. throw new DeveloperError('Invalid renderState.depthTest.func.');
  196. }
  197. if ((this.blending.color.red < 0.0) || (this.blending.color.red > 1.0) ||
  198. (this.blending.color.green < 0.0) || (this.blending.color.green > 1.0) ||
  199. (this.blending.color.blue < 0.0) || (this.blending.color.blue > 1.0) ||
  200. (this.blending.color.alpha < 0.0) || (this.blending.color.alpha > 1.0)) {
  201. // Would be clamped by GL
  202. throw new DeveloperError('renderState.blending.color components must be greater than or equal to zero and less than or equal to one.');
  203. }
  204. if (!validateBlendEquation(this.blending.equationRgb)) {
  205. throw new DeveloperError('Invalid renderState.blending.equationRgb.');
  206. }
  207. if (!validateBlendEquation(this.blending.equationAlpha)) {
  208. throw new DeveloperError('Invalid renderState.blending.equationAlpha.');
  209. }
  210. if (!validateBlendFunction(this.blending.functionSourceRgb)) {
  211. throw new DeveloperError('Invalid renderState.blending.functionSourceRgb.');
  212. }
  213. if (!validateBlendFunction(this.blending.functionSourceAlpha)) {
  214. throw new DeveloperError('Invalid renderState.blending.functionSourceAlpha.');
  215. }
  216. if (!validateBlendFunction(this.blending.functionDestinationRgb)) {
  217. throw new DeveloperError('Invalid renderState.blending.functionDestinationRgb.');
  218. }
  219. if (!validateBlendFunction(this.blending.functionDestinationAlpha)) {
  220. throw new DeveloperError('Invalid renderState.blending.functionDestinationAlpha.');
  221. }
  222. if (!validateStencilFunction(this.stencilTest.frontFunction)) {
  223. throw new DeveloperError('Invalid renderState.stencilTest.frontFunction.');
  224. }
  225. if (!validateStencilFunction(this.stencilTest.backFunction)) {
  226. throw new DeveloperError('Invalid renderState.stencilTest.backFunction.');
  227. }
  228. if (!validateStencilOperation(this.stencilTest.frontOperation.fail)) {
  229. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.fail.');
  230. }
  231. if (!validateStencilOperation(this.stencilTest.frontOperation.zFail)) {
  232. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zFail.');
  233. }
  234. if (!validateStencilOperation(this.stencilTest.frontOperation.zPass)) {
  235. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zPass.');
  236. }
  237. if (!validateStencilOperation(this.stencilTest.backOperation.fail)) {
  238. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.fail.');
  239. }
  240. if (!validateStencilOperation(this.stencilTest.backOperation.zFail)) {
  241. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zFail.');
  242. }
  243. if (!validateStencilOperation(this.stencilTest.backOperation.zPass)) {
  244. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zPass.');
  245. }
  246. //>>includeEnd('debug');
  247. if (defined(this.viewport)) {
  248. //>>includeStart('debug', pragmas.debug);
  249. if (this.viewport.width < 0) {
  250. throw new DeveloperError('renderState.viewport.width must be greater than or equal to zero.');
  251. }
  252. if (this.viewport.height < 0) {
  253. throw new DeveloperError('renderState.viewport.height must be greater than or equal to zero.');
  254. }
  255. //>>includeEnd('debug');
  256. if (this.viewport.width > context.maximumViewportWidth) {
  257. throw new RuntimeError('renderState.viewport.width must be less than or equal to the maximum viewport width (' + this.maximumViewportWidth.toString() + '). Check maximumViewportWidth.');
  258. }
  259. if (this.viewport.height > context.maximumViewportHeight) {
  260. throw new RuntimeError('renderState.viewport.height must be less than or equal to the maximum viewport height (' + this.maximumViewportHeight.toString() + '). Check maximumViewportHeight.');
  261. }
  262. }
  263. this.id = 0;
  264. this._applyFunctions = [];
  265. };
  266. function enableOrDisable(gl, glEnum, enable) {
  267. if (enable) {
  268. gl.enable(glEnum);
  269. } else {
  270. gl.disable(glEnum);
  271. }
  272. }
  273. function applyFrontFace(gl, renderState) {
  274. gl.frontFace(renderState.frontFace);
  275. }
  276. function applyCull(gl, renderState) {
  277. var cull = renderState.cull;
  278. var enabled = cull.enabled;
  279. enableOrDisable(gl, gl.CULL_FACE, enabled);
  280. if (enabled) {
  281. gl.cullFace(cull.face);
  282. }
  283. }
  284. function applyLineWidth(gl, renderState) {
  285. gl.lineWidth(renderState.lineWidth);
  286. }
  287. function applyPolygonOffset(gl, renderState) {
  288. var polygonOffset = renderState.polygonOffset;
  289. var enabled = polygonOffset.enabled;
  290. enableOrDisable(gl, gl.POLYGON_OFFSET_FILL, enabled);
  291. if (enabled) {
  292. gl.polygonOffset(polygonOffset.factor, polygonOffset.units);
  293. }
  294. }
  295. function applyScissorTest(gl, renderState, passState) {
  296. var scissorTest = renderState.scissorTest;
  297. var enabled = (defined(passState.scissorTest)) ? passState.scissorTest.enabled : scissorTest.enabled;
  298. enableOrDisable(gl, gl.SCISSOR_TEST, enabled);
  299. if (enabled) {
  300. var rectangle = (defined(passState.scissorTest)) ? passState.scissorTest.rectangle : scissorTest.rectangle;
  301. gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  302. }
  303. }
  304. function applyDepthRange(gl, renderState) {
  305. var depthRange = renderState.depthRange;
  306. gl.depthRange(depthRange.near, depthRange.far);
  307. }
  308. function applyDepthTest(gl, renderState) {
  309. var depthTest = renderState.depthTest;
  310. var enabled = depthTest.enabled;
  311. enableOrDisable(gl, gl.DEPTH_TEST, enabled);
  312. if (enabled) {
  313. gl.depthFunc(depthTest.func);
  314. }
  315. }
  316. function applyColorMask(gl, renderState) {
  317. var colorMask = renderState.colorMask;
  318. gl.colorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
  319. }
  320. function applyDepthMask(gl, renderState) {
  321. gl.depthMask(renderState.depthMask);
  322. }
  323. function applyStencilMask(gl, renderState) {
  324. gl.stencilMask(renderState.stencilMask);
  325. }
  326. var applyBlendingColor = function(gl, color) {
  327. gl.blendColor(color.red, color.green, color.blue, color.alpha);
  328. };
  329. function applyBlending(gl, renderState, passState) {
  330. var blending = renderState.blending;
  331. var enabled = (defined(passState.blendingEnabled)) ? passState.blendingEnabled : blending.enabled;
  332. enableOrDisable(gl, gl.BLEND, enabled);
  333. if (enabled) {
  334. applyBlendingColor(gl, blending.color);
  335. gl.blendEquationSeparate(blending.equationRgb, blending.equationAlpha);
  336. gl.blendFuncSeparate(blending.functionSourceRgb, blending.functionDestinationRgb, blending.functionSourceAlpha, blending.functionDestinationAlpha);
  337. }
  338. }
  339. function applyStencilTest(gl, renderState) {
  340. var stencilTest = renderState.stencilTest;
  341. var enabled = stencilTest.enabled;
  342. enableOrDisable(gl, gl.STENCIL_TEST, enabled);
  343. if (enabled) {
  344. var frontFunction = stencilTest.frontFunction;
  345. var backFunction = stencilTest.backFunction;
  346. var reference = stencilTest.reference;
  347. var mask = stencilTest.mask;
  348. // Section 6.8 of the WebGL spec requires the reference and masks to be the same for
  349. // front- and back-face tests. This call prevents invalid operation errors when calling
  350. // stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this.
  351. gl.stencilFunc(stencilTest.frontFunction, stencilTest.reference, stencilTest.mask);
  352. gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask);
  353. gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask);
  354. var frontOperation = stencilTest.frontOperation;
  355. var frontOperationFail = frontOperation.fail;
  356. var frontOperationZFail = frontOperation.zFail;
  357. var frontOperationZPass = frontOperation.zPass;
  358. gl.stencilOpSeparate(gl.FRONT, frontOperationFail, frontOperationZFail, frontOperationZPass);
  359. var backOperation = stencilTest.backOperation;
  360. var backOperationFail = backOperation.fail;
  361. var backOperationZFail = backOperation.zFail;
  362. var backOperationZPass = backOperation.zPass;
  363. gl.stencilOpSeparate(gl.BACK, backOperationFail, backOperationZFail, backOperationZPass);
  364. }
  365. }
  366. var applySampleCoverage = function(gl, renderState) {
  367. var sampleCoverage = renderState.sampleCoverage;
  368. var enabled = sampleCoverage.enabled;
  369. enableOrDisable(gl, gl.SAMPLE_COVERAGE, enabled);
  370. if (enabled) {
  371. gl.sampleCoverage(sampleCoverage.value, sampleCoverage.invert);
  372. }
  373. };
  374. var scratchViewport = new BoundingRectangle();
  375. function applyViewport(gl, renderState, passState) {
  376. var viewport = renderState.viewport;
  377. if (!defined(viewport)) {
  378. viewport = scratchViewport;
  379. viewport.width = passState.context.drawingBufferWidth;
  380. viewport.height = passState.context.drawingBufferHeight;
  381. }
  382. passState.context.uniformState.viewport = viewport;
  383. gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
  384. }
  385. RenderState.apply = function(gl, renderState, passState) {
  386. applyFrontFace(gl, renderState);
  387. applyCull(gl, renderState);
  388. applyLineWidth(gl, renderState);
  389. applyPolygonOffset(gl, renderState);
  390. applyScissorTest(gl, renderState, passState);
  391. applyDepthRange(gl, renderState);
  392. applyDepthTest(gl, renderState);
  393. applyColorMask(gl, renderState);
  394. applyDepthMask(gl, renderState);
  395. applyStencilMask(gl, renderState);
  396. applyBlending(gl, renderState, passState);
  397. applyStencilTest(gl, renderState);
  398. applySampleCoverage(gl, renderState);
  399. applyViewport(gl, renderState, passState);
  400. };
  401. function createFuncs(previousState, nextState) {
  402. var funcs = [];
  403. if (previousState.frontFace !== nextState.frontFace) {
  404. funcs.push(applyFrontFace);
  405. }
  406. if ((previousState.cull.enabled !== nextState.cull.enabled) || (previousState.cull.face !== nextState.cull.face)) {
  407. funcs.push(applyCull);
  408. }
  409. if (previousState.lineWidth !== nextState.lineWidth) {
  410. funcs.push(applyLineWidth);
  411. }
  412. if ((previousState.polygonOffset.enabled !== nextState.polygonOffset.enabled) ||
  413. (previousState.polygonOffset.factor !== nextState.polygonOffset.factor) ||
  414. (previousState.polygonOffset.units !== nextState.polygonOffset.units)) {
  415. funcs.push(applyPolygonOffset);
  416. }
  417. // For now, always apply because of passState
  418. funcs.push(applyScissorTest);
  419. if ((previousState.depthRange.near !== nextState.depthRange.near) || (previousState.depthRange.far !== nextState.depthRange.far)) {
  420. funcs.push(applyDepthRange);
  421. }
  422. if ((previousState.depthTest.enabled !== nextState.depthTest.enabled) || (previousState.depthTest.func !== nextState.depthTest.func)) {
  423. funcs.push(applyDepthTest);
  424. }
  425. if ((previousState.colorMask.red !== nextState.colorMask.red) ||
  426. (previousState.colorMask.green !== nextState.colorMask.green) ||
  427. (previousState.colorMask.blue !== nextState.colorMask.blue) ||
  428. (previousState.colorMask.alpha !== nextState.colorMask.alpha)) {
  429. funcs.push(applyColorMask);
  430. }
  431. if (previousState.depthMask !== nextState.depthMask) {
  432. funcs.push(applyDepthMask);
  433. }
  434. // For now, always apply because of passState
  435. funcs.push(applyBlending);
  436. if (previousState.stencilMask !== nextState.stencilMask) {
  437. funcs.push(applyStencilMask);
  438. }
  439. if ((previousState.stencilTest.enabled !== nextState.stencilTest.enabled) ||
  440. (previousState.stencilTest.frontFunction !== nextState.stencilTest.frontFunction) ||
  441. (previousState.stencilTest.backFunction !== nextState.stencilTest.backFunction) ||
  442. (previousState.stencilTest.reference !== nextState.stencilTest.reference) ||
  443. (previousState.stencilTest.mask !== nextState.stencilTest.mask) ||
  444. (previousState.stencilTest.frontOperation.fail !== nextState.stencilTest.frontOperation.fail) ||
  445. (previousState.stencilTest.frontOperation.zFail !== nextState.stencilTest.frontOperation.zFail) ||
  446. (previousState.stencilTest.backOperation.fail !== nextState.stencilTest.backOperation.fail) ||
  447. (previousState.stencilTest.backOperation.zFail !== nextState.stencilTest.backOperation.zFail) ||
  448. (previousState.stencilTest.backOperation.zPass !== nextState.stencilTest.backOperation.zPass)) {
  449. funcs.push(applyStencilTest);
  450. }
  451. if ((previousState.sampleCoverage.enabled !== nextState.sampleCoverage.enabled) ||
  452. (previousState.sampleCoverage.value !== nextState.sampleCoverage.value) ||
  453. (previousState.sampleCoverage.invert !== nextState.sampleCoverage.invert)) {
  454. funcs.push(applySampleCoverage);
  455. }
  456. // For now, always apply because of passState
  457. funcs.push(applyViewport);
  458. return funcs;
  459. }
  460. RenderState.partialApply = function(gl, previousState, nextState, passState) {
  461. // When a new render state is applied, instead of making WebGL calls for all the states or first
  462. // comparing the states one-by-one with the previous state (basically a linear search), we take
  463. // advantage of RenderState's immutability, and store a dynamically populated sparse data structure
  464. // containing functions that make the minimum number of WebGL calls when transitioning from one state
  465. // to the other. In practice, this works well since state-to-state transitions generally only require a
  466. // few WebGL calls, especially if commands are stored by state.
  467. var funcs = nextState._applyFunctions[previousState.id];
  468. if (!defined(funcs)) {
  469. funcs = createFuncs(previousState, nextState);
  470. nextState._applyFunctions[previousState.id] = funcs;
  471. }
  472. var len = funcs.length;
  473. for (var i = 0; i < len; ++i) {
  474. funcs[i](gl, nextState, passState);
  475. }
  476. };
  477. /**
  478. * Duplicates a RenderState instance. The object returned must still be created with {@link Context#createRenderState}.
  479. *
  480. * @param renderState The render state to be cloned.
  481. * @returns {Object} The duplicated render state.
  482. */
  483. RenderState.clone = function(renderState) {
  484. //>>includeStart('debug', pragmas.debug);
  485. if (!defined(renderState)) {
  486. throw new DeveloperError('renderState is required.');
  487. }
  488. //>>includeEnd('debug');
  489. return {
  490. frontFace : renderState.frontFace,
  491. cull : {
  492. enabled : renderState.cull.enabled,
  493. face : renderState.cull.face
  494. },
  495. lineWidth : renderState.lineWidth,
  496. polygonOffset : {
  497. enabled : renderState.polygonOffset.enabled,
  498. factor : renderState.polygonOffset.factor,
  499. units : renderState.polygonOffset.units
  500. },
  501. scissorTest : {
  502. enabled : renderState.scissorTest.enabled,
  503. rectangle : BoundingRectangle.clone(renderState.scissorTest.rectangle)
  504. },
  505. depthRange : {
  506. near : renderState.depthRange.near,
  507. far : renderState.depthRange.far
  508. },
  509. depthTest : {
  510. enabled : renderState.depthTest.enabled,
  511. func : renderState.depthTest.func
  512. },
  513. colorMask : {
  514. red : renderState.colorMask.red,
  515. green : renderState.colorMask.green,
  516. blue : renderState.colorMask.blue,
  517. alpha : renderState.colorMask.alpha
  518. },
  519. depthMask : renderState.depthMask,
  520. stencilMask : renderState.stencilMask,
  521. blending : {
  522. enabled : renderState.blending.enabled,
  523. color : Color.clone(renderState.blending.color),
  524. equationRgb : renderState.blending.equationRgb,
  525. equationAlpha : renderState.blending.equationAlpha,
  526. functionSourceRgb : renderState.blending.functionSourceRgb,
  527. functionSourceAlpha : renderState.blending.functionSourceAlpha,
  528. functionDestinationRgb : renderState.blending.functionDestinationRgb,
  529. functionDestinationAlpha : renderState.blending.functionDestinationAlpha
  530. },
  531. stencilTest : {
  532. enabled : renderState.stencilTest.enabled,
  533. frontFunction : renderState.stencilTest.frontFunction,
  534. backFunction : renderState.stencilTest.backFunction,
  535. reference : renderState.stencilTest.reference,
  536. mask : renderState.stencilTest.mask,
  537. frontOperation : {
  538. fail : renderState.stencilTest.frontOperation.fail,
  539. zFail : renderState.stencilTest.frontOperation.zFail,
  540. zPass : renderState.stencilTest.frontOperation.zPass
  541. },
  542. backOperation : {
  543. fail : renderState.stencilTest.backOperation.fail,
  544. zFail : renderState.stencilTest.backOperation.zFail,
  545. zPass : renderState.stencilTest.backOperation.zPass
  546. }
  547. },
  548. sampleCoverage : {
  549. enabled : renderState.sampleCoverage.enabled,
  550. value : renderState.sampleCoverage.value,
  551. invert : renderState.sampleCoverage.invert
  552. },
  553. viewport : defined(renderState.viewport) ? BoundingRectangle.clone(renderState.viewport) : undefined
  554. };
  555. };
  556. return RenderState;
  557. });