RenderState.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. import BoundingRectangle from '../Core/BoundingRectangle.js';
  2. import Color from '../Core/Color.js';
  3. import defaultValue from '../Core/defaultValue.js';
  4. import defined from '../Core/defined.js';
  5. import DeveloperError from '../Core/DeveloperError.js';
  6. import WebGLConstants from '../Core/WebGLConstants.js';
  7. import WindingOrder from '../Core/WindingOrder.js';
  8. import ContextLimits from './ContextLimits.js';
  9. import freezeRenderState from './freezeRenderState.js';
  10. function validateBlendEquation(blendEquation) {
  11. return ((blendEquation === WebGLConstants.FUNC_ADD) ||
  12. (blendEquation === WebGLConstants.FUNC_SUBTRACT) ||
  13. (blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT) ||
  14. (blendEquation === WebGLConstants.MIN) ||
  15. (blendEquation === WebGLConstants.MAX));
  16. }
  17. function validateBlendFunction(blendFunction) {
  18. return ((blendFunction === WebGLConstants.ZERO) ||
  19. (blendFunction === WebGLConstants.ONE) ||
  20. (blendFunction === WebGLConstants.SRC_COLOR) ||
  21. (blendFunction === WebGLConstants.ONE_MINUS_SRC_COLOR) ||
  22. (blendFunction === WebGLConstants.DST_COLOR) ||
  23. (blendFunction === WebGLConstants.ONE_MINUS_DST_COLOR) ||
  24. (blendFunction === WebGLConstants.SRC_ALPHA) ||
  25. (blendFunction === WebGLConstants.ONE_MINUS_SRC_ALPHA) ||
  26. (blendFunction === WebGLConstants.DST_ALPHA) ||
  27. (blendFunction === WebGLConstants.ONE_MINUS_DST_ALPHA) ||
  28. (blendFunction === WebGLConstants.CONSTANT_COLOR) ||
  29. (blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_COLOR) ||
  30. (blendFunction === WebGLConstants.CONSTANT_ALPHA) ||
  31. (blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_ALPHA) ||
  32. (blendFunction === WebGLConstants.SRC_ALPHA_SATURATE));
  33. }
  34. function validateCullFace(cullFace) {
  35. return ((cullFace === WebGLConstants.FRONT) ||
  36. (cullFace === WebGLConstants.BACK) ||
  37. (cullFace === WebGLConstants.FRONT_AND_BACK));
  38. }
  39. function validateDepthFunction(depthFunction) {
  40. return ((depthFunction === WebGLConstants.NEVER) ||
  41. (depthFunction === WebGLConstants.LESS) ||
  42. (depthFunction === WebGLConstants.EQUAL) ||
  43. (depthFunction === WebGLConstants.LEQUAL) ||
  44. (depthFunction === WebGLConstants.GREATER) ||
  45. (depthFunction === WebGLConstants.NOTEQUAL) ||
  46. (depthFunction === WebGLConstants.GEQUAL) ||
  47. (depthFunction === WebGLConstants.ALWAYS));
  48. }
  49. function validateStencilFunction(stencilFunction) {
  50. return ((stencilFunction === WebGLConstants.NEVER) ||
  51. (stencilFunction === WebGLConstants.LESS) ||
  52. (stencilFunction === WebGLConstants.EQUAL) ||
  53. (stencilFunction === WebGLConstants.LEQUAL) ||
  54. (stencilFunction === WebGLConstants.GREATER) ||
  55. (stencilFunction === WebGLConstants.NOTEQUAL) ||
  56. (stencilFunction === WebGLConstants.GEQUAL) ||
  57. (stencilFunction === WebGLConstants.ALWAYS));
  58. }
  59. function validateStencilOperation(stencilOperation) {
  60. return ((stencilOperation === WebGLConstants.ZERO) ||
  61. (stencilOperation === WebGLConstants.KEEP) ||
  62. (stencilOperation === WebGLConstants.REPLACE) ||
  63. (stencilOperation === WebGLConstants.INCR) ||
  64. (stencilOperation === WebGLConstants.DECR) ||
  65. (stencilOperation === WebGLConstants.INVERT) ||
  66. (stencilOperation === WebGLConstants.INCR_WRAP) ||
  67. (stencilOperation === WebGLConstants.DECR_WRAP));
  68. }
  69. /**
  70. * @private
  71. */
  72. function RenderState(renderState) {
  73. var rs = defaultValue(renderState, defaultValue.EMPTY_OBJECT);
  74. var cull = defaultValue(rs.cull, defaultValue.EMPTY_OBJECT);
  75. var polygonOffset = defaultValue(rs.polygonOffset, defaultValue.EMPTY_OBJECT);
  76. var scissorTest = defaultValue(rs.scissorTest, defaultValue.EMPTY_OBJECT);
  77. var scissorTestRectangle = defaultValue(scissorTest.rectangle, defaultValue.EMPTY_OBJECT);
  78. var depthRange = defaultValue(rs.depthRange, defaultValue.EMPTY_OBJECT);
  79. var depthTest = defaultValue(rs.depthTest, defaultValue.EMPTY_OBJECT);
  80. var colorMask = defaultValue(rs.colorMask, defaultValue.EMPTY_OBJECT);
  81. var blending = defaultValue(rs.blending, defaultValue.EMPTY_OBJECT);
  82. var blendingColor = defaultValue(blending.color, defaultValue.EMPTY_OBJECT);
  83. var stencilTest = defaultValue(rs.stencilTest, defaultValue.EMPTY_OBJECT);
  84. var stencilTestFrontOperation = defaultValue(stencilTest.frontOperation, defaultValue.EMPTY_OBJECT);
  85. var stencilTestBackOperation = defaultValue(stencilTest.backOperation, defaultValue.EMPTY_OBJECT);
  86. var sampleCoverage = defaultValue(rs.sampleCoverage, defaultValue.EMPTY_OBJECT);
  87. var viewport = rs.viewport;
  88. this.frontFace = defaultValue(rs.frontFace, WindingOrder.COUNTER_CLOCKWISE);
  89. this.cull = {
  90. enabled : defaultValue(cull.enabled, false),
  91. face : defaultValue(cull.face, WebGLConstants.BACK)
  92. };
  93. this.lineWidth = defaultValue(rs.lineWidth, 1.0);
  94. this.polygonOffset = {
  95. enabled : defaultValue(polygonOffset.enabled, false),
  96. factor : defaultValue(polygonOffset.factor, 0),
  97. units : defaultValue(polygonOffset.units, 0)
  98. };
  99. this.scissorTest = {
  100. enabled : defaultValue(scissorTest.enabled, false),
  101. rectangle : BoundingRectangle.clone(scissorTestRectangle)
  102. };
  103. this.depthRange = {
  104. near : defaultValue(depthRange.near, 0),
  105. far : defaultValue(depthRange.far, 1)
  106. };
  107. this.depthTest = {
  108. enabled : defaultValue(depthTest.enabled, false),
  109. func : defaultValue(depthTest.func, WebGLConstants.LESS) // func, because function is a JavaScript keyword
  110. };
  111. this.colorMask = {
  112. red : defaultValue(colorMask.red, true),
  113. green : defaultValue(colorMask.green, true),
  114. blue : defaultValue(colorMask.blue, true),
  115. alpha : defaultValue(colorMask.alpha, true)
  116. };
  117. this.depthMask = defaultValue(rs.depthMask, true);
  118. this.stencilMask = defaultValue(rs.stencilMask, ~0);
  119. this.blending = {
  120. enabled : defaultValue(blending.enabled, false),
  121. color : new Color(
  122. defaultValue(blendingColor.red, 0.0),
  123. defaultValue(blendingColor.green, 0.0),
  124. defaultValue(blendingColor.blue, 0.0),
  125. defaultValue(blendingColor.alpha, 0.0)
  126. ),
  127. equationRgb : defaultValue(blending.equationRgb, WebGLConstants.FUNC_ADD),
  128. equationAlpha : defaultValue(blending.equationAlpha, WebGLConstants.FUNC_ADD),
  129. functionSourceRgb : defaultValue(blending.functionSourceRgb, WebGLConstants.ONE),
  130. functionSourceAlpha : defaultValue(blending.functionSourceAlpha, WebGLConstants.ONE),
  131. functionDestinationRgb : defaultValue(blending.functionDestinationRgb, WebGLConstants.ZERO),
  132. functionDestinationAlpha : defaultValue(blending.functionDestinationAlpha, WebGLConstants.ZERO)
  133. };
  134. this.stencilTest = {
  135. enabled : defaultValue(stencilTest.enabled, false),
  136. frontFunction : defaultValue(stencilTest.frontFunction, WebGLConstants.ALWAYS),
  137. backFunction : defaultValue(stencilTest.backFunction, WebGLConstants.ALWAYS),
  138. reference : defaultValue(stencilTest.reference, 0),
  139. mask : defaultValue(stencilTest.mask, ~0),
  140. frontOperation : {
  141. fail : defaultValue(stencilTestFrontOperation.fail, WebGLConstants.KEEP),
  142. zFail : defaultValue(stencilTestFrontOperation.zFail, WebGLConstants.KEEP),
  143. zPass : defaultValue(stencilTestFrontOperation.zPass, WebGLConstants.KEEP)
  144. },
  145. backOperation : {
  146. fail : defaultValue(stencilTestBackOperation.fail, WebGLConstants.KEEP),
  147. zFail : defaultValue(stencilTestBackOperation.zFail, WebGLConstants.KEEP),
  148. zPass : defaultValue(stencilTestBackOperation.zPass, WebGLConstants.KEEP)
  149. }
  150. };
  151. this.sampleCoverage = {
  152. enabled : defaultValue(sampleCoverage.enabled, false),
  153. value : defaultValue(sampleCoverage.value, 1.0),
  154. invert : defaultValue(sampleCoverage.invert, false)
  155. };
  156. this.viewport = (defined(viewport)) ? new BoundingRectangle(viewport.x, viewport.y, viewport.width, viewport.height) : undefined;
  157. //>>includeStart('debug', pragmas.debug);
  158. if ((this.lineWidth < ContextLimits.minimumAliasedLineWidth) ||
  159. (this.lineWidth > ContextLimits.maximumAliasedLineWidth)) {
  160. throw new DeveloperError('renderState.lineWidth is out of range. Check minimumAliasedLineWidth and maximumAliasedLineWidth.');
  161. }
  162. if (!WindingOrder.validate(this.frontFace)) {
  163. throw new DeveloperError('Invalid renderState.frontFace.');
  164. }
  165. if (!validateCullFace(this.cull.face)) {
  166. throw new DeveloperError('Invalid renderState.cull.face.');
  167. }
  168. if ((this.scissorTest.rectangle.width < 0) ||
  169. (this.scissorTest.rectangle.height < 0)) {
  170. throw new DeveloperError('renderState.scissorTest.rectangle.width and renderState.scissorTest.rectangle.height must be greater than or equal to zero.');
  171. }
  172. if (this.depthRange.near > this.depthRange.far) {
  173. // WebGL specific - not an error in GL ES
  174. throw new DeveloperError('renderState.depthRange.near can not be greater than renderState.depthRange.far.');
  175. }
  176. if (this.depthRange.near < 0) {
  177. // Would be clamped by GL
  178. throw new DeveloperError('renderState.depthRange.near must be greater than or equal to zero.');
  179. }
  180. if (this.depthRange.far > 1) {
  181. // Would be clamped by GL
  182. throw new DeveloperError('renderState.depthRange.far must be less than or equal to one.');
  183. }
  184. if (!validateDepthFunction(this.depthTest.func)) {
  185. throw new DeveloperError('Invalid renderState.depthTest.func.');
  186. }
  187. if ((this.blending.color.red < 0.0) || (this.blending.color.red > 1.0) ||
  188. (this.blending.color.green < 0.0) || (this.blending.color.green > 1.0) ||
  189. (this.blending.color.blue < 0.0) || (this.blending.color.blue > 1.0) ||
  190. (this.blending.color.alpha < 0.0) || (this.blending.color.alpha > 1.0)) {
  191. // Would be clamped by GL
  192. throw new DeveloperError('renderState.blending.color components must be greater than or equal to zero and less than or equal to one.');
  193. }
  194. if (!validateBlendEquation(this.blending.equationRgb)) {
  195. throw new DeveloperError('Invalid renderState.blending.equationRgb.');
  196. }
  197. if (!validateBlendEquation(this.blending.equationAlpha)) {
  198. throw new DeveloperError('Invalid renderState.blending.equationAlpha.');
  199. }
  200. if (!validateBlendFunction(this.blending.functionSourceRgb)) {
  201. throw new DeveloperError('Invalid renderState.blending.functionSourceRgb.');
  202. }
  203. if (!validateBlendFunction(this.blending.functionSourceAlpha)) {
  204. throw new DeveloperError('Invalid renderState.blending.functionSourceAlpha.');
  205. }
  206. if (!validateBlendFunction(this.blending.functionDestinationRgb)) {
  207. throw new DeveloperError('Invalid renderState.blending.functionDestinationRgb.');
  208. }
  209. if (!validateBlendFunction(this.blending.functionDestinationAlpha)) {
  210. throw new DeveloperError('Invalid renderState.blending.functionDestinationAlpha.');
  211. }
  212. if (!validateStencilFunction(this.stencilTest.frontFunction)) {
  213. throw new DeveloperError('Invalid renderState.stencilTest.frontFunction.');
  214. }
  215. if (!validateStencilFunction(this.stencilTest.backFunction)) {
  216. throw new DeveloperError('Invalid renderState.stencilTest.backFunction.');
  217. }
  218. if (!validateStencilOperation(this.stencilTest.frontOperation.fail)) {
  219. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.fail.');
  220. }
  221. if (!validateStencilOperation(this.stencilTest.frontOperation.zFail)) {
  222. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zFail.');
  223. }
  224. if (!validateStencilOperation(this.stencilTest.frontOperation.zPass)) {
  225. throw new DeveloperError('Invalid renderState.stencilTest.frontOperation.zPass.');
  226. }
  227. if (!validateStencilOperation(this.stencilTest.backOperation.fail)) {
  228. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.fail.');
  229. }
  230. if (!validateStencilOperation(this.stencilTest.backOperation.zFail)) {
  231. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zFail.');
  232. }
  233. if (!validateStencilOperation(this.stencilTest.backOperation.zPass)) {
  234. throw new DeveloperError('Invalid renderState.stencilTest.backOperation.zPass.');
  235. }
  236. if (defined(this.viewport)) {
  237. if (this.viewport.width < 0) {
  238. throw new DeveloperError('renderState.viewport.width must be greater than or equal to zero.');
  239. }
  240. if (this.viewport.height < 0) {
  241. throw new DeveloperError('renderState.viewport.height must be greater than or equal to zero.');
  242. }
  243. if (this.viewport.width > ContextLimits.maximumViewportWidth) {
  244. throw new DeveloperError('renderState.viewport.width must be less than or equal to the maximum viewport width (' + ContextLimits.maximumViewportWidth.toString() + '). Check maximumViewportWidth.');
  245. }
  246. if (this.viewport.height > ContextLimits.maximumViewportHeight) {
  247. throw new DeveloperError('renderState.viewport.height must be less than or equal to the maximum viewport height (' + ContextLimits.maximumViewportHeight.toString() + '). Check maximumViewportHeight.');
  248. }
  249. }
  250. //>>includeEnd('debug');
  251. this.id = 0;
  252. this._applyFunctions = [];
  253. }
  254. var nextRenderStateId = 0;
  255. var renderStateCache = {};
  256. /**
  257. * Validates and then finds or creates an immutable render state, which defines the pipeline
  258. * state for a {@link DrawCommand} or {@link ClearCommand}. All inputs states are optional. Omitted states
  259. * use the defaults shown in the example below.
  260. *
  261. * @param {Object} [renderState] The states defining the render state as shown in the example below.
  262. *
  263. * @exception {RuntimeError} renderState.lineWidth is out of range.
  264. * @exception {DeveloperError} Invalid renderState.frontFace.
  265. * @exception {DeveloperError} Invalid renderState.cull.face.
  266. * @exception {DeveloperError} scissorTest.rectangle.width and scissorTest.rectangle.height must be greater than or equal to zero.
  267. * @exception {DeveloperError} renderState.depthRange.near can't be greater than renderState.depthRange.far.
  268. * @exception {DeveloperError} renderState.depthRange.near must be greater than or equal to zero.
  269. * @exception {DeveloperError} renderState.depthRange.far must be less than or equal to zero.
  270. * @exception {DeveloperError} Invalid renderState.depthTest.func.
  271. * @exception {DeveloperError} renderState.blending.color components must be greater than or equal to zero and less than or equal to one
  272. * @exception {DeveloperError} Invalid renderState.blending.equationRgb.
  273. * @exception {DeveloperError} Invalid renderState.blending.equationAlpha.
  274. * @exception {DeveloperError} Invalid renderState.blending.functionSourceRgb.
  275. * @exception {DeveloperError} Invalid renderState.blending.functionSourceAlpha.
  276. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationRgb.
  277. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationAlpha.
  278. * @exception {DeveloperError} Invalid renderState.stencilTest.frontFunction.
  279. * @exception {DeveloperError} Invalid renderState.stencilTest.backFunction.
  280. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.fail.
  281. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zFail.
  282. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zPass.
  283. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.fail.
  284. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zFail.
  285. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zPass.
  286. * @exception {DeveloperError} renderState.viewport.width must be greater than or equal to zero.
  287. * @exception {DeveloperError} renderState.viewport.width must be less than or equal to the maximum viewport width.
  288. * @exception {DeveloperError} renderState.viewport.height must be greater than or equal to zero.
  289. * @exception {DeveloperError} renderState.viewport.height must be less than or equal to the maximum viewport height.
  290. *
  291. *
  292. * @example
  293. * var defaults = {
  294. * frontFace : WindingOrder.COUNTER_CLOCKWISE,
  295. * cull : {
  296. * enabled : false,
  297. * face : CullFace.BACK
  298. * },
  299. * lineWidth : 1,
  300. * polygonOffset : {
  301. * enabled : false,
  302. * factor : 0,
  303. * units : 0
  304. * },
  305. * scissorTest : {
  306. * enabled : false,
  307. * rectangle : {
  308. * x : 0,
  309. * y : 0,
  310. * width : 0,
  311. * height : 0
  312. * }
  313. * },
  314. * depthRange : {
  315. * near : 0,
  316. * far : 1
  317. * },
  318. * depthTest : {
  319. * enabled : false,
  320. * func : DepthFunction.LESS
  321. * },
  322. * colorMask : {
  323. * red : true,
  324. * green : true,
  325. * blue : true,
  326. * alpha : true
  327. * },
  328. * depthMask : true,
  329. * stencilMask : ~0,
  330. * blending : {
  331. * enabled : false,
  332. * color : {
  333. * red : 0.0,
  334. * green : 0.0,
  335. * blue : 0.0,
  336. * alpha : 0.0
  337. * },
  338. * equationRgb : BlendEquation.ADD,
  339. * equationAlpha : BlendEquation.ADD,
  340. * functionSourceRgb : BlendFunction.ONE,
  341. * functionSourceAlpha : BlendFunction.ONE,
  342. * functionDestinationRgb : BlendFunction.ZERO,
  343. * functionDestinationAlpha : BlendFunction.ZERO
  344. * },
  345. * stencilTest : {
  346. * enabled : false,
  347. * frontFunction : StencilFunction.ALWAYS,
  348. * backFunction : StencilFunction.ALWAYS,
  349. * reference : 0,
  350. * mask : ~0,
  351. * frontOperation : {
  352. * fail : StencilOperation.KEEP,
  353. * zFail : StencilOperation.KEEP,
  354. * zPass : StencilOperation.KEEP
  355. * },
  356. * backOperation : {
  357. * fail : StencilOperation.KEEP,
  358. * zFail : StencilOperation.KEEP,
  359. * zPass : StencilOperation.KEEP
  360. * }
  361. * },
  362. * sampleCoverage : {
  363. * enabled : false,
  364. * value : 1.0,
  365. * invert : false
  366. * }
  367. * };
  368. *
  369. * var rs = RenderState.fromCache(defaults);
  370. *
  371. * @see DrawCommand
  372. * @see ClearCommand
  373. *
  374. * @private
  375. */
  376. RenderState.fromCache = function(renderState) {
  377. var partialKey = JSON.stringify(renderState);
  378. var cachedState = renderStateCache[partialKey];
  379. if (defined(cachedState)) {
  380. ++cachedState.referenceCount;
  381. return cachedState.state;
  382. }
  383. // Cache miss. Fully define render state and try again.
  384. var states = new RenderState(renderState);
  385. var fullKey = JSON.stringify(states);
  386. cachedState = renderStateCache[fullKey];
  387. if (!defined(cachedState)) {
  388. states.id = nextRenderStateId++;
  389. //>>includeStart('debug', pragmas.debug);
  390. states = freezeRenderState(states);
  391. //>>includeEnd('debug');
  392. cachedState = {
  393. referenceCount : 0,
  394. state : states
  395. };
  396. // Cache full render state. Multiple partially defined render states may map to this.
  397. renderStateCache[fullKey] = cachedState;
  398. }
  399. ++cachedState.referenceCount;
  400. // Cache partial render state so we can skip validation on a cache hit for a partially defined render state
  401. renderStateCache[partialKey] = {
  402. referenceCount : 1,
  403. state : cachedState.state
  404. };
  405. return cachedState.state;
  406. };
  407. /**
  408. * @private
  409. */
  410. RenderState.removeFromCache = function(renderState) {
  411. var states = new RenderState(renderState);
  412. var fullKey = JSON.stringify(states);
  413. var fullCachedState = renderStateCache[fullKey];
  414. // decrement partial key reference count
  415. var partialKey = JSON.stringify(renderState);
  416. var cachedState = renderStateCache[partialKey];
  417. if (defined(cachedState)) {
  418. --cachedState.referenceCount;
  419. if (cachedState.referenceCount === 0) {
  420. // remove partial key
  421. delete renderStateCache[partialKey];
  422. // decrement full key reference count
  423. if (defined(fullCachedState)) {
  424. --fullCachedState.referenceCount;
  425. }
  426. }
  427. }
  428. // remove full key if reference count is zero
  429. if (defined(fullCachedState) && (fullCachedState.referenceCount === 0)) {
  430. delete renderStateCache[fullKey];
  431. }
  432. };
  433. /**
  434. * This function is for testing purposes only.
  435. * @private
  436. */
  437. RenderState.getCache = function() {
  438. return renderStateCache;
  439. };
  440. /**
  441. * This function is for testing purposes only.
  442. * @private
  443. */
  444. RenderState.clearCache = function() {
  445. renderStateCache = {};
  446. };
  447. function enableOrDisable(gl, glEnum, enable) {
  448. if (enable) {
  449. gl.enable(glEnum);
  450. } else {
  451. gl.disable(glEnum);
  452. }
  453. }
  454. function applyFrontFace(gl, renderState) {
  455. gl.frontFace(renderState.frontFace);
  456. }
  457. function applyCull(gl, renderState) {
  458. var cull = renderState.cull;
  459. var enabled = cull.enabled;
  460. enableOrDisable(gl, gl.CULL_FACE, enabled);
  461. if (enabled) {
  462. gl.cullFace(cull.face);
  463. }
  464. }
  465. function applyLineWidth(gl, renderState) {
  466. gl.lineWidth(renderState.lineWidth);
  467. }
  468. function applyPolygonOffset(gl, renderState) {
  469. var polygonOffset = renderState.polygonOffset;
  470. var enabled = polygonOffset.enabled;
  471. enableOrDisable(gl, gl.POLYGON_OFFSET_FILL, enabled);
  472. if (enabled) {
  473. gl.polygonOffset(polygonOffset.factor, polygonOffset.units);
  474. }
  475. }
  476. function applyScissorTest(gl, renderState, passState) {
  477. var scissorTest = renderState.scissorTest;
  478. var enabled = (defined(passState.scissorTest)) ? passState.scissorTest.enabled : scissorTest.enabled;
  479. enableOrDisable(gl, gl.SCISSOR_TEST, enabled);
  480. if (enabled) {
  481. var rectangle = (defined(passState.scissorTest)) ? passState.scissorTest.rectangle : scissorTest.rectangle;
  482. gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  483. }
  484. }
  485. function applyDepthRange(gl, renderState) {
  486. var depthRange = renderState.depthRange;
  487. gl.depthRange(depthRange.near, depthRange.far);
  488. }
  489. function applyDepthTest(gl, renderState) {
  490. var depthTest = renderState.depthTest;
  491. var enabled = depthTest.enabled;
  492. enableOrDisable(gl, gl.DEPTH_TEST, enabled);
  493. if (enabled) {
  494. gl.depthFunc(depthTest.func);
  495. }
  496. }
  497. function applyColorMask(gl, renderState) {
  498. var colorMask = renderState.colorMask;
  499. gl.colorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
  500. }
  501. function applyDepthMask(gl, renderState) {
  502. gl.depthMask(renderState.depthMask);
  503. }
  504. function applyStencilMask(gl, renderState) {
  505. gl.stencilMask(renderState.stencilMask);
  506. }
  507. function applyBlendingColor(gl, color) {
  508. gl.blendColor(color.red, color.green, color.blue, color.alpha);
  509. }
  510. function applyBlending(gl, renderState, passState) {
  511. var blending = renderState.blending;
  512. var enabled = (defined(passState.blendingEnabled)) ? passState.blendingEnabled : blending.enabled;
  513. enableOrDisable(gl, gl.BLEND, enabled);
  514. if (enabled) {
  515. applyBlendingColor(gl, blending.color);
  516. gl.blendEquationSeparate(blending.equationRgb, blending.equationAlpha);
  517. gl.blendFuncSeparate(blending.functionSourceRgb, blending.functionDestinationRgb, blending.functionSourceAlpha, blending.functionDestinationAlpha);
  518. }
  519. }
  520. function applyStencilTest(gl, renderState) {
  521. var stencilTest = renderState.stencilTest;
  522. var enabled = stencilTest.enabled;
  523. enableOrDisable(gl, gl.STENCIL_TEST, enabled);
  524. if (enabled) {
  525. var frontFunction = stencilTest.frontFunction;
  526. var backFunction = stencilTest.backFunction;
  527. var reference = stencilTest.reference;
  528. var mask = stencilTest.mask;
  529. // Section 6.8 of the WebGL spec requires the reference and masks to be the same for
  530. // front- and back-face tests. This call prevents invalid operation errors when calling
  531. // stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this.
  532. gl.stencilFunc(frontFunction, reference, mask);
  533. gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask);
  534. gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask);
  535. var frontOperation = stencilTest.frontOperation;
  536. var frontOperationFail = frontOperation.fail;
  537. var frontOperationZFail = frontOperation.zFail;
  538. var frontOperationZPass = frontOperation.zPass;
  539. gl.stencilOpSeparate(gl.FRONT, frontOperationFail, frontOperationZFail, frontOperationZPass);
  540. var backOperation = stencilTest.backOperation;
  541. var backOperationFail = backOperation.fail;
  542. var backOperationZFail = backOperation.zFail;
  543. var backOperationZPass = backOperation.zPass;
  544. gl.stencilOpSeparate(gl.BACK, backOperationFail, backOperationZFail, backOperationZPass);
  545. }
  546. }
  547. function applySampleCoverage(gl, renderState) {
  548. var sampleCoverage = renderState.sampleCoverage;
  549. var enabled = sampleCoverage.enabled;
  550. enableOrDisable(gl, gl.SAMPLE_COVERAGE, enabled);
  551. if (enabled) {
  552. gl.sampleCoverage(sampleCoverage.value, sampleCoverage.invert);
  553. }
  554. }
  555. var scratchViewport = new BoundingRectangle();
  556. function applyViewport(gl, renderState, passState) {
  557. var viewport = defaultValue(renderState.viewport, passState.viewport);
  558. if (!defined(viewport)) {
  559. viewport = scratchViewport;
  560. viewport.width = passState.context.drawingBufferWidth;
  561. viewport.height = passState.context.drawingBufferHeight;
  562. }
  563. passState.context.uniformState.viewport = viewport;
  564. gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
  565. }
  566. RenderState.apply = function(gl, renderState, passState) {
  567. applyFrontFace(gl, renderState);
  568. applyCull(gl, renderState);
  569. applyLineWidth(gl, renderState);
  570. applyPolygonOffset(gl, renderState);
  571. applyDepthRange(gl, renderState);
  572. applyDepthTest(gl, renderState);
  573. applyColorMask(gl, renderState);
  574. applyDepthMask(gl, renderState);
  575. applyStencilMask(gl, renderState);
  576. applyStencilTest(gl, renderState);
  577. applySampleCoverage(gl, renderState);
  578. applyScissorTest(gl, renderState, passState);
  579. applyBlending(gl, renderState, passState);
  580. applyViewport(gl, renderState, passState);
  581. };
  582. function createFuncs(previousState, nextState) {
  583. var funcs = [];
  584. if (previousState.frontFace !== nextState.frontFace) {
  585. funcs.push(applyFrontFace);
  586. }
  587. if ((previousState.cull.enabled !== nextState.cull.enabled) || (previousState.cull.face !== nextState.cull.face)) {
  588. funcs.push(applyCull);
  589. }
  590. if (previousState.lineWidth !== nextState.lineWidth) {
  591. funcs.push(applyLineWidth);
  592. }
  593. if ((previousState.polygonOffset.enabled !== nextState.polygonOffset.enabled) ||
  594. (previousState.polygonOffset.factor !== nextState.polygonOffset.factor) ||
  595. (previousState.polygonOffset.units !== nextState.polygonOffset.units)) {
  596. funcs.push(applyPolygonOffset);
  597. }
  598. if ((previousState.depthRange.near !== nextState.depthRange.near) || (previousState.depthRange.far !== nextState.depthRange.far)) {
  599. funcs.push(applyDepthRange);
  600. }
  601. if ((previousState.depthTest.enabled !== nextState.depthTest.enabled) || (previousState.depthTest.func !== nextState.depthTest.func)) {
  602. funcs.push(applyDepthTest);
  603. }
  604. if ((previousState.colorMask.red !== nextState.colorMask.red) ||
  605. (previousState.colorMask.green !== nextState.colorMask.green) ||
  606. (previousState.colorMask.blue !== nextState.colorMask.blue) ||
  607. (previousState.colorMask.alpha !== nextState.colorMask.alpha)) {
  608. funcs.push(applyColorMask);
  609. }
  610. if (previousState.depthMask !== nextState.depthMask) {
  611. funcs.push(applyDepthMask);
  612. }
  613. if (previousState.stencilMask !== nextState.stencilMask) {
  614. funcs.push(applyStencilMask);
  615. }
  616. if ((previousState.stencilTest.enabled !== nextState.stencilTest.enabled) ||
  617. (previousState.stencilTest.frontFunction !== nextState.stencilTest.frontFunction) ||
  618. (previousState.stencilTest.backFunction !== nextState.stencilTest.backFunction) ||
  619. (previousState.stencilTest.reference !== nextState.stencilTest.reference) ||
  620. (previousState.stencilTest.mask !== nextState.stencilTest.mask) ||
  621. (previousState.stencilTest.frontOperation.fail !== nextState.stencilTest.frontOperation.fail) ||
  622. (previousState.stencilTest.frontOperation.zFail !== nextState.stencilTest.frontOperation.zFail) ||
  623. (previousState.stencilTest.backOperation.fail !== nextState.stencilTest.backOperation.fail) ||
  624. (previousState.stencilTest.backOperation.zFail !== nextState.stencilTest.backOperation.zFail) ||
  625. (previousState.stencilTest.backOperation.zPass !== nextState.stencilTest.backOperation.zPass)) {
  626. funcs.push(applyStencilTest);
  627. }
  628. if ((previousState.sampleCoverage.enabled !== nextState.sampleCoverage.enabled) ||
  629. (previousState.sampleCoverage.value !== nextState.sampleCoverage.value) ||
  630. (previousState.sampleCoverage.invert !== nextState.sampleCoverage.invert)) {
  631. funcs.push(applySampleCoverage);
  632. }
  633. return funcs;
  634. }
  635. RenderState.partialApply = function(gl, previousRenderState, renderState, previousPassState, passState, clear) {
  636. if (previousRenderState !== renderState) {
  637. // When a new render state is applied, instead of making WebGL calls for all the states or first
  638. // comparing the states one-by-one with the previous state (basically a linear search), we take
  639. // advantage of RenderState's immutability, and store a dynamically populated sparse data structure
  640. // containing functions that make the minimum number of WebGL calls when transitioning from one state
  641. // to the other. In practice, this works well since state-to-state transitions generally only require a
  642. // few WebGL calls, especially if commands are stored by state.
  643. var funcs = renderState._applyFunctions[previousRenderState.id];
  644. if (!defined(funcs)) {
  645. funcs = createFuncs(previousRenderState, renderState);
  646. renderState._applyFunctions[previousRenderState.id] = funcs;
  647. }
  648. var len = funcs.length;
  649. for (var i = 0; i < len; ++i) {
  650. funcs[i](gl, renderState);
  651. }
  652. }
  653. var previousScissorTest = (defined(previousPassState.scissorTest)) ? previousPassState.scissorTest : previousRenderState.scissorTest;
  654. var scissorTest = (defined(passState.scissorTest)) ? passState.scissorTest : renderState.scissorTest;
  655. // Our scissor rectangle can get out of sync with the GL scissor rectangle on clears.
  656. // Seems to be a problem only on ANGLE. See https://github.com/AnalyticalGraphicsInc/cesium/issues/2994
  657. if ((previousScissorTest !== scissorTest) || clear) {
  658. applyScissorTest(gl, renderState, passState);
  659. }
  660. var previousBlendingEnabled = (defined(previousPassState.blendingEnabled)) ? previousPassState.blendingEnabled : previousRenderState.blending.enabled;
  661. var blendingEnabled = (defined(passState.blendingEnabled)) ? passState.blendingEnabled : renderState.blending.enabled;
  662. if ((previousBlendingEnabled !== blendingEnabled) ||
  663. (blendingEnabled && (previousRenderState.blending !== renderState.blending))) {
  664. applyBlending(gl, renderState, passState);
  665. }
  666. if (previousRenderState !== renderState || previousPassState !== passState || previousPassState.context !== passState.context) {
  667. applyViewport(gl, renderState, passState);
  668. }
  669. };
  670. RenderState.getState = function(renderState) {
  671. //>>includeStart('debug', pragmas.debug);
  672. if (!defined(renderState)) {
  673. throw new DeveloperError('renderState is required.');
  674. }
  675. //>>includeEnd('debug');
  676. return {
  677. frontFace : renderState.frontFace,
  678. cull : {
  679. enabled : renderState.cull.enabled,
  680. face : renderState.cull.face
  681. },
  682. lineWidth : renderState.lineWidth,
  683. polygonOffset : {
  684. enabled : renderState.polygonOffset.enabled,
  685. factor : renderState.polygonOffset.factor,
  686. units : renderState.polygonOffset.units
  687. },
  688. scissorTest : {
  689. enabled : renderState.scissorTest.enabled,
  690. rectangle : BoundingRectangle.clone(renderState.scissorTest.rectangle)
  691. },
  692. depthRange : {
  693. near : renderState.depthRange.near,
  694. far : renderState.depthRange.far
  695. },
  696. depthTest : {
  697. enabled : renderState.depthTest.enabled,
  698. func : renderState.depthTest.func
  699. },
  700. colorMask : {
  701. red : renderState.colorMask.red,
  702. green : renderState.colorMask.green,
  703. blue : renderState.colorMask.blue,
  704. alpha : renderState.colorMask.alpha
  705. },
  706. depthMask : renderState.depthMask,
  707. stencilMask : renderState.stencilMask,
  708. blending : {
  709. enabled : renderState.blending.enabled,
  710. color : Color.clone(renderState.blending.color),
  711. equationRgb : renderState.blending.equationRgb,
  712. equationAlpha : renderState.blending.equationAlpha,
  713. functionSourceRgb : renderState.blending.functionSourceRgb,
  714. functionSourceAlpha : renderState.blending.functionSourceAlpha,
  715. functionDestinationRgb : renderState.blending.functionDestinationRgb,
  716. functionDestinationAlpha : renderState.blending.functionDestinationAlpha
  717. },
  718. stencilTest : {
  719. enabled : renderState.stencilTest.enabled,
  720. frontFunction : renderState.stencilTest.frontFunction,
  721. backFunction : renderState.stencilTest.backFunction,
  722. reference : renderState.stencilTest.reference,
  723. mask : renderState.stencilTest.mask,
  724. frontOperation : {
  725. fail : renderState.stencilTest.frontOperation.fail,
  726. zFail : renderState.stencilTest.frontOperation.zFail,
  727. zPass : renderState.stencilTest.frontOperation.zPass
  728. },
  729. backOperation : {
  730. fail : renderState.stencilTest.backOperation.fail,
  731. zFail : renderState.stencilTest.backOperation.zFail,
  732. zPass : renderState.stencilTest.backOperation.zPass
  733. }
  734. },
  735. sampleCoverage : {
  736. enabled : renderState.sampleCoverage.enabled,
  737. value : renderState.sampleCoverage.value,
  738. invert : renderState.sampleCoverage.invert
  739. },
  740. viewport : defined(renderState.viewport) ? BoundingRectangle.clone(renderState.viewport) : undefined
  741. };
  742. };
  743. export default RenderState;