RenderState.js 33 KB

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