RenderState.js 33 KB

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