智慧水务管理系统 - 精河县供水工程综合管理平台

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