Context.js 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. import Check from "../Core/Check.js";
  2. import Color from "../Core/Color.js";
  3. import ComponentDatatype from "../Core/ComponentDatatype.js";
  4. import createGuid from "../Core/createGuid.js";
  5. import defaultValue from "../Core/defaultValue.js";
  6. import defined from "../Core/defined.js";
  7. import deprecationWarning from "../Core/deprecationWarning.js";
  8. import destroyObject from "../Core/destroyObject.js";
  9. import DeveloperError from "../Core/DeveloperError.js";
  10. import Geometry from "../Core/Geometry.js";
  11. import GeometryAttribute from "../Core/GeometryAttribute.js";
  12. import loadKTX2 from "../Core/loadKTX2.js";
  13. import Matrix4 from "../Core/Matrix4.js";
  14. import PixelFormat from "../Core/PixelFormat.js";
  15. import PrimitiveType from "../Core/PrimitiveType.js";
  16. import RuntimeError from "../Core/RuntimeError.js";
  17. import WebGLConstants from "../Core/WebGLConstants.js";
  18. import ViewportQuadVS from "../Shaders/ViewportQuadVS.js";
  19. import BufferUsage from "./BufferUsage.js";
  20. import ClearCommand from "./ClearCommand.js";
  21. import ContextLimits from "./ContextLimits.js";
  22. import CubeMap from "./CubeMap.js";
  23. import DrawCommand from "./DrawCommand.js";
  24. import PassState from "./PassState.js";
  25. import PixelDatatype from "./PixelDatatype.js";
  26. import RenderState from "./RenderState.js";
  27. import ShaderCache from "./ShaderCache.js";
  28. import ShaderProgram from "./ShaderProgram.js";
  29. import Texture from "./Texture.js";
  30. import TextureCache from "./TextureCache.js";
  31. import UniformState from "./UniformState.js";
  32. import VertexArray from "./VertexArray.js";
  33. /**
  34. * @private
  35. * @constructor
  36. *
  37. * @param {HTMLCanvasElement} canvas The canvas element to which the context will be associated
  38. * @param {ContextOptions} [options] Options to control WebGL settings for the context
  39. */
  40. function Context(canvas, options) {
  41. //>>includeStart('debug', pragmas.debug);
  42. Check.defined("canvas", canvas);
  43. //>>includeEnd('debug');
  44. const {
  45. getWebGLStub,
  46. requestWebgl1,
  47. webgl: webglOptions = {},
  48. allowTextureFilterAnisotropic = true,
  49. } = defaultValue(options, {});
  50. // Override select WebGL defaults
  51. webglOptions.alpha = defaultValue(webglOptions.alpha, false); // WebGL default is true
  52. webglOptions.stencil = defaultValue(webglOptions.stencil, true); // WebGL default is false
  53. webglOptions.powerPreference = defaultValue(
  54. webglOptions.powerPreference,
  55. "high-performance"
  56. ); // WebGL default is "default"
  57. const glContext = defined(getWebGLStub)
  58. ? getWebGLStub(canvas, webglOptions)
  59. : getWebGLContext(canvas, webglOptions, requestWebgl1);
  60. // Get context type. instanceof will throw if WebGL2 is not supported
  61. const webgl2Supported = typeof WebGL2RenderingContext !== "undefined";
  62. const webgl2 = webgl2Supported && glContext instanceof WebGL2RenderingContext;
  63. this._canvas = canvas;
  64. this._originalGLContext = glContext;
  65. this._gl = glContext;
  66. this._webgl2 = webgl2;
  67. this._id = createGuid();
  68. // Validation and logging disabled by default for speed.
  69. this.validateFramebuffer = false;
  70. this.validateShaderProgram = false;
  71. this.logShaderCompilation = false;
  72. this._throwOnWebGLError = false;
  73. this._shaderCache = new ShaderCache(this);
  74. this._textureCache = new TextureCache();
  75. const gl = glContext;
  76. this._stencilBits = gl.getParameter(gl.STENCIL_BITS);
  77. ContextLimits._maximumCombinedTextureImageUnits = gl.getParameter(
  78. gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS
  79. ); // min: 8
  80. ContextLimits._maximumCubeMapSize = gl.getParameter(
  81. gl.MAX_CUBE_MAP_TEXTURE_SIZE
  82. ); // min: 16
  83. ContextLimits._maximumFragmentUniformVectors = gl.getParameter(
  84. gl.MAX_FRAGMENT_UNIFORM_VECTORS
  85. ); // min: 16
  86. ContextLimits._maximumTextureImageUnits = gl.getParameter(
  87. gl.MAX_TEXTURE_IMAGE_UNITS
  88. ); // min: 8
  89. ContextLimits._maximumRenderbufferSize = gl.getParameter(
  90. gl.MAX_RENDERBUFFER_SIZE
  91. ); // min: 1
  92. ContextLimits._maximumTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); // min: 64
  93. ContextLimits._maximumVaryingVectors = gl.getParameter(
  94. gl.MAX_VARYING_VECTORS
  95. ); // min: 8
  96. ContextLimits._maximumVertexAttributes = gl.getParameter(
  97. gl.MAX_VERTEX_ATTRIBS
  98. ); // min: 8
  99. ContextLimits._maximumVertexTextureImageUnits = gl.getParameter(
  100. gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS
  101. ); // min: 0
  102. ContextLimits._maximumVertexUniformVectors = gl.getParameter(
  103. gl.MAX_VERTEX_UNIFORM_VECTORS
  104. ); // min: 128
  105. ContextLimits._maximumSamples = this._webgl2
  106. ? gl.getParameter(gl.MAX_SAMPLES)
  107. : 0;
  108. const aliasedLineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE); // must include 1
  109. ContextLimits._minimumAliasedLineWidth = aliasedLineWidthRange[0];
  110. ContextLimits._maximumAliasedLineWidth = aliasedLineWidthRange[1];
  111. const aliasedPointSizeRange = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); // must include 1
  112. ContextLimits._minimumAliasedPointSize = aliasedPointSizeRange[0];
  113. ContextLimits._maximumAliasedPointSize = aliasedPointSizeRange[1];
  114. const maximumViewportDimensions = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
  115. ContextLimits._maximumViewportWidth = maximumViewportDimensions[0];
  116. ContextLimits._maximumViewportHeight = maximumViewportDimensions[1];
  117. const highpFloat = gl.getShaderPrecisionFormat(
  118. gl.FRAGMENT_SHADER,
  119. gl.HIGH_FLOAT
  120. );
  121. ContextLimits._highpFloatSupported = highpFloat.precision !== 0;
  122. const highpInt = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT);
  123. ContextLimits._highpIntSupported = highpInt.rangeMax !== 0;
  124. this._antialias = gl.getContextAttributes().antialias;
  125. // Query and initialize extensions
  126. this._standardDerivatives = !!getExtension(gl, ["OES_standard_derivatives"]);
  127. this._blendMinmax = !!getExtension(gl, ["EXT_blend_minmax"]);
  128. this._elementIndexUint = !!getExtension(gl, ["OES_element_index_uint"]);
  129. this._depthTexture = !!getExtension(gl, [
  130. "WEBGL_depth_texture",
  131. "WEBKIT_WEBGL_depth_texture",
  132. ]);
  133. this._fragDepth = !!getExtension(gl, ["EXT_frag_depth"]);
  134. this._debugShaders = getExtension(gl, ["WEBGL_debug_shaders"]);
  135. this._textureFloat = !!getExtension(gl, ["OES_texture_float"]);
  136. this._textureHalfFloat = !!getExtension(gl, ["OES_texture_half_float"]);
  137. this._textureFloatLinear = !!getExtension(gl, ["OES_texture_float_linear"]);
  138. this._textureHalfFloatLinear = !!getExtension(gl, [
  139. "OES_texture_half_float_linear",
  140. ]);
  141. this._colorBufferFloat = !!getExtension(gl, [
  142. "EXT_color_buffer_float",
  143. "WEBGL_color_buffer_float",
  144. ]);
  145. this._floatBlend = !!getExtension(gl, ["EXT_float_blend"]);
  146. this._colorBufferHalfFloat = !!getExtension(gl, [
  147. "EXT_color_buffer_half_float",
  148. ]);
  149. this._s3tc = !!getExtension(gl, [
  150. "WEBGL_compressed_texture_s3tc",
  151. "MOZ_WEBGL_compressed_texture_s3tc",
  152. "WEBKIT_WEBGL_compressed_texture_s3tc",
  153. ]);
  154. this._pvrtc = !!getExtension(gl, [
  155. "WEBGL_compressed_texture_pvrtc",
  156. "WEBKIT_WEBGL_compressed_texture_pvrtc",
  157. ]);
  158. this._astc = !!getExtension(gl, ["WEBGL_compressed_texture_astc"]);
  159. this._etc = !!getExtension(gl, ["WEBG_compressed_texture_etc"]);
  160. this._etc1 = !!getExtension(gl, ["WEBGL_compressed_texture_etc1"]);
  161. this._bc7 = !!getExtension(gl, ["EXT_texture_compression_bptc"]);
  162. // It is necessary to pass supported formats to loadKTX2
  163. // because imagery layers don't have access to the context.
  164. loadKTX2.setKTX2SupportedFormats(
  165. this._s3tc,
  166. this._pvrtc,
  167. this._astc,
  168. this._etc,
  169. this._etc1,
  170. this._bc7
  171. );
  172. const textureFilterAnisotropic = allowTextureFilterAnisotropic
  173. ? getExtension(gl, [
  174. "EXT_texture_filter_anisotropic",
  175. "WEBKIT_EXT_texture_filter_anisotropic",
  176. ])
  177. : undefined;
  178. this._textureFilterAnisotropic = textureFilterAnisotropic;
  179. ContextLimits._maximumTextureFilterAnisotropy = defined(
  180. textureFilterAnisotropic
  181. )
  182. ? gl.getParameter(textureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)
  183. : 1.0;
  184. let glCreateVertexArray;
  185. let glBindVertexArray;
  186. let glDeleteVertexArray;
  187. let glDrawElementsInstanced;
  188. let glDrawArraysInstanced;
  189. let glVertexAttribDivisor;
  190. let glDrawBuffers;
  191. let vertexArrayObject;
  192. let instancedArrays;
  193. let drawBuffers;
  194. if (webgl2) {
  195. const that = this;
  196. glCreateVertexArray = function () {
  197. return that._gl.createVertexArray();
  198. };
  199. glBindVertexArray = function (vao) {
  200. that._gl.bindVertexArray(vao);
  201. };
  202. glDeleteVertexArray = function (vao) {
  203. that._gl.deleteVertexArray(vao);
  204. };
  205. glDrawElementsInstanced = function (
  206. mode,
  207. count,
  208. type,
  209. offset,
  210. instanceCount
  211. ) {
  212. gl.drawElementsInstanced(mode, count, type, offset, instanceCount);
  213. };
  214. glDrawArraysInstanced = function (mode, first, count, instanceCount) {
  215. gl.drawArraysInstanced(mode, first, count, instanceCount);
  216. };
  217. glVertexAttribDivisor = function (index, divisor) {
  218. gl.vertexAttribDivisor(index, divisor);
  219. };
  220. glDrawBuffers = function (buffers) {
  221. gl.drawBuffers(buffers);
  222. };
  223. } else {
  224. vertexArrayObject = getExtension(gl, ["OES_vertex_array_object"]);
  225. if (defined(vertexArrayObject)) {
  226. glCreateVertexArray = function () {
  227. return vertexArrayObject.createVertexArrayOES();
  228. };
  229. glBindVertexArray = function (vertexArray) {
  230. vertexArrayObject.bindVertexArrayOES(vertexArray);
  231. };
  232. glDeleteVertexArray = function (vertexArray) {
  233. vertexArrayObject.deleteVertexArrayOES(vertexArray);
  234. };
  235. }
  236. instancedArrays = getExtension(gl, ["ANGLE_instanced_arrays"]);
  237. if (defined(instancedArrays)) {
  238. glDrawElementsInstanced = function (
  239. mode,
  240. count,
  241. type,
  242. offset,
  243. instanceCount
  244. ) {
  245. instancedArrays.drawElementsInstancedANGLE(
  246. mode,
  247. count,
  248. type,
  249. offset,
  250. instanceCount
  251. );
  252. };
  253. glDrawArraysInstanced = function (mode, first, count, instanceCount) {
  254. instancedArrays.drawArraysInstancedANGLE(
  255. mode,
  256. first,
  257. count,
  258. instanceCount
  259. );
  260. };
  261. glVertexAttribDivisor = function (index, divisor) {
  262. instancedArrays.vertexAttribDivisorANGLE(index, divisor);
  263. };
  264. }
  265. drawBuffers = getExtension(gl, ["WEBGL_draw_buffers"]);
  266. if (defined(drawBuffers)) {
  267. glDrawBuffers = function (buffers) {
  268. drawBuffers.drawBuffersWEBGL(buffers);
  269. };
  270. }
  271. }
  272. this.glCreateVertexArray = glCreateVertexArray;
  273. this.glBindVertexArray = glBindVertexArray;
  274. this.glDeleteVertexArray = glDeleteVertexArray;
  275. this.glDrawElementsInstanced = glDrawElementsInstanced;
  276. this.glDrawArraysInstanced = glDrawArraysInstanced;
  277. this.glVertexAttribDivisor = glVertexAttribDivisor;
  278. this.glDrawBuffers = glDrawBuffers;
  279. this._vertexArrayObject = !!vertexArrayObject;
  280. this._instancedArrays = !!instancedArrays;
  281. this._drawBuffers = !!drawBuffers;
  282. ContextLimits._maximumDrawBuffers = this.drawBuffers
  283. ? gl.getParameter(WebGLConstants.MAX_DRAW_BUFFERS)
  284. : 1;
  285. ContextLimits._maximumColorAttachments = this.drawBuffers
  286. ? gl.getParameter(WebGLConstants.MAX_COLOR_ATTACHMENTS)
  287. : 1;
  288. this._clearColor = new Color(0.0, 0.0, 0.0, 0.0);
  289. this._clearDepth = 1.0;
  290. this._clearStencil = 0;
  291. const us = new UniformState();
  292. const ps = new PassState(this);
  293. const rs = RenderState.fromCache();
  294. this._defaultPassState = ps;
  295. this._defaultRenderState = rs;
  296. // default texture has a value of (1, 1, 1)
  297. // default emissive texture has a value of (0, 0, 0)
  298. // default normal texture is +z which is encoded as (0.5, 0.5, 1)
  299. this._defaultTexture = undefined;
  300. this._defaultEmissiveTexture = undefined;
  301. this._defaultNormalTexture = undefined;
  302. this._defaultCubeMap = undefined;
  303. this._us = us;
  304. this._currentRenderState = rs;
  305. this._currentPassState = ps;
  306. this._currentFramebuffer = undefined;
  307. this._maxFrameTextureUnitIndex = 0;
  308. // Vertex attribute divisor state cache. Workaround for ANGLE (also look at VertexArray.setVertexAttribDivisor)
  309. this._vertexAttribDivisors = [];
  310. this._previousDrawInstanced = false;
  311. for (let i = 0; i < ContextLimits._maximumVertexAttributes; i++) {
  312. this._vertexAttribDivisors.push(0);
  313. }
  314. this._pickObjects = {};
  315. this._nextPickColor = new Uint32Array(1);
  316. /**
  317. * The options used to construct this context
  318. *
  319. * @type {ContextOptions}
  320. */
  321. this.options = {
  322. getWebGLStub: getWebGLStub,
  323. requestWebgl1: requestWebgl1,
  324. webgl: webglOptions,
  325. allowTextureFilterAnisotropic: allowTextureFilterAnisotropic,
  326. };
  327. /**
  328. * A cache of objects tied to this context. Just before the Context is destroyed,
  329. * <code>destroy</code> will be invoked on each object in this object literal that has
  330. * such a method. This is useful for caching any objects that might otherwise
  331. * be stored globally, except they're tied to a particular context, and to manage
  332. * their lifetime.
  333. *
  334. * @type {object}
  335. */
  336. this.cache = {};
  337. RenderState.apply(gl, rs, ps);
  338. }
  339. /**
  340. * @typedef {object} ContextOptions
  341. *
  342. * Options to control the setting up of a WebGL Context.
  343. * <p>
  344. * <code>allowTextureFilterAnisotropic</code> defaults to true, which enables
  345. * anisotropic texture filtering when the WebGL extension is supported.
  346. * Setting this to false will improve performance, but hurt visual quality,
  347. * especially for horizon views.
  348. * </p>
  349. *
  350. * @property {boolean} [requestWebgl1=false] If true and the browser supports it, use a WebGL 1 rendering context
  351. * @property {boolean} [allowTextureFilterAnisotropic=true] If true, use anisotropic filtering during texture sampling
  352. * @property {WebGLOptions} [webgl] WebGL options to be passed on to canvas.getContext
  353. * @property {Function} [getWebGLStub] A function to create a WebGL stub for testing
  354. */
  355. /**
  356. * @private
  357. * @param {HTMLCanvasElement} canvas The canvas element to which the context will be associated
  358. * @param {WebGLOptions} webglOptions WebGL options to be passed on to HTMLCanvasElement.getContext()
  359. * @param {boolean} requestWebgl1 Whether to request a WebGLRenderingContext or a WebGL2RenderingContext.
  360. * @returns {WebGLRenderingContext|WebGL2RenderingContext}
  361. */
  362. function getWebGLContext(canvas, webglOptions, requestWebgl1) {
  363. if (typeof WebGLRenderingContext === "undefined") {
  364. throw new RuntimeError(
  365. "The browser does not support WebGL. Visit http://get.webgl.org."
  366. );
  367. }
  368. // Ensure that WebGL 2 is supported when it is requested. Otherwise, fall back to WebGL 1.
  369. const webgl2Supported = typeof WebGL2RenderingContext !== "undefined";
  370. if (!requestWebgl1 && !webgl2Supported) {
  371. requestWebgl1 = true;
  372. }
  373. const contextType = requestWebgl1 ? "webgl" : "webgl2";
  374. const glContext = canvas.getContext(contextType, webglOptions);
  375. if (!defined(glContext)) {
  376. throw new RuntimeError(
  377. "The browser supports WebGL, but initialization failed."
  378. );
  379. }
  380. return glContext;
  381. }
  382. /**
  383. * @typedef {object} WebGLOptions
  384. *
  385. * WebGL options to be passed on to HTMLCanvasElement.getContext().
  386. * See {@link https://registry.khronos.org/webgl/specs/latest/1.0/#5.2|WebGLContextAttributes}
  387. * but note the modified defaults for 'alpha', 'stencil', and 'powerPreference'
  388. *
  389. * <p>
  390. * <code>alpha</code> defaults to false, which can improve performance
  391. * compared to the standard WebGL default of true. If an application needs
  392. * to composite Cesium above other HTML elements using alpha-blending, set
  393. * <code>alpha</code> to true.
  394. * </p>
  395. *
  396. * @property {boolean} [alpha=false]
  397. * @property {boolean} [depth=true]
  398. * @property {boolean} [stencil=false]
  399. * @property {boolean} [antialias=true]
  400. * @property {boolean} [premultipliedAlpha=true]
  401. * @property {boolean} [preserveDrawingBuffer=false]
  402. * @property {("default"|"low-power"|"high-performance")} [powerPreference="high-performance"]
  403. * @property {boolean} [failIfMajorPerformanceCaveat=false]
  404. */
  405. function errorToString(gl, error) {
  406. let message = "WebGL Error: ";
  407. switch (error) {
  408. case gl.INVALID_ENUM:
  409. message += "INVALID_ENUM";
  410. break;
  411. case gl.INVALID_VALUE:
  412. message += "INVALID_VALUE";
  413. break;
  414. case gl.INVALID_OPERATION:
  415. message += "INVALID_OPERATION";
  416. break;
  417. case gl.OUT_OF_MEMORY:
  418. message += "OUT_OF_MEMORY";
  419. break;
  420. case gl.CONTEXT_LOST_WEBGL:
  421. message += "CONTEXT_LOST_WEBGL lost";
  422. break;
  423. default:
  424. message += `Unknown (${error})`;
  425. }
  426. return message;
  427. }
  428. function createErrorMessage(gl, glFunc, glFuncArguments, error) {
  429. let message = `${errorToString(gl, error)}: ${glFunc.name}(`;
  430. for (let i = 0; i < glFuncArguments.length; ++i) {
  431. if (i !== 0) {
  432. message += ", ";
  433. }
  434. message += glFuncArguments[i];
  435. }
  436. message += ");";
  437. return message;
  438. }
  439. function throwOnError(gl, glFunc, glFuncArguments) {
  440. const error = gl.getError();
  441. if (error !== gl.NO_ERROR) {
  442. throw new RuntimeError(
  443. createErrorMessage(gl, glFunc, glFuncArguments, error)
  444. );
  445. }
  446. }
  447. function makeGetterSetter(gl, propertyName, logFunction) {
  448. return {
  449. get: function () {
  450. const value = gl[propertyName];
  451. logFunction(gl, `get: ${propertyName}`, value);
  452. return gl[propertyName];
  453. },
  454. set: function (value) {
  455. gl[propertyName] = value;
  456. logFunction(gl, `set: ${propertyName}`, value);
  457. },
  458. };
  459. }
  460. function wrapGL(gl, logFunction) {
  461. if (!defined(logFunction)) {
  462. return gl;
  463. }
  464. function wrapFunction(property) {
  465. return function () {
  466. const result = property.apply(gl, arguments);
  467. logFunction(gl, property, arguments);
  468. return result;
  469. };
  470. }
  471. const glWrapper = {};
  472. // JavaScript linters normally demand that a for..in loop must directly contain an if,
  473. // but in our loop below, we actually intend to iterate all properties, including
  474. // those in the prototype.
  475. /*eslint-disable guard-for-in*/
  476. for (const propertyName in gl) {
  477. const property = gl[propertyName];
  478. // wrap any functions we encounter, otherwise just copy the property to the wrapper.
  479. if (property instanceof Function) {
  480. glWrapper[propertyName] = wrapFunction(property);
  481. } else {
  482. Object.defineProperty(
  483. glWrapper,
  484. propertyName,
  485. makeGetterSetter(gl, propertyName, logFunction)
  486. );
  487. }
  488. }
  489. /*eslint-enable guard-for-in*/
  490. return glWrapper;
  491. }
  492. function getExtension(gl, names) {
  493. const length = names.length;
  494. for (let i = 0; i < length; ++i) {
  495. const extension = gl.getExtension(names[i]);
  496. if (extension) {
  497. return extension;
  498. }
  499. }
  500. return undefined;
  501. }
  502. const defaultFramebufferMarker = {};
  503. Object.defineProperties(Context.prototype, {
  504. id: {
  505. get: function () {
  506. return this._id;
  507. },
  508. },
  509. webgl2: {
  510. get: function () {
  511. return this._webgl2;
  512. },
  513. },
  514. canvas: {
  515. get: function () {
  516. return this._canvas;
  517. },
  518. },
  519. shaderCache: {
  520. get: function () {
  521. return this._shaderCache;
  522. },
  523. },
  524. textureCache: {
  525. get: function () {
  526. return this._textureCache;
  527. },
  528. },
  529. uniformState: {
  530. get: function () {
  531. return this._us;
  532. },
  533. },
  534. /**
  535. * The number of stencil bits per pixel in the default bound framebuffer. The minimum is eight bits.
  536. * @memberof Context.prototype
  537. * @type {number}
  538. * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with <code>STENCIL_BITS</code>.
  539. */
  540. stencilBits: {
  541. get: function () {
  542. return this._stencilBits;
  543. },
  544. },
  545. /**
  546. * <code>true</code> if the WebGL context supports stencil buffers.
  547. * Stencil buffers are not supported by all systems.
  548. * @memberof Context.prototype
  549. * @type {boolean}
  550. */
  551. stencilBuffer: {
  552. get: function () {
  553. return this._stencilBits >= 8;
  554. },
  555. },
  556. /**
  557. * <code>true</code> if the WebGL context supports antialiasing. By default
  558. * antialiasing is requested, but it is not supported by all systems.
  559. * @memberof Context.prototype
  560. * @type {boolean}
  561. */
  562. antialias: {
  563. get: function () {
  564. return this._antialias;
  565. },
  566. },
  567. /**
  568. * <code>true</code> if the WebGL context supports multisample antialiasing. Requires
  569. * WebGL2.
  570. * @memberof Context.prototype
  571. * @type {boolean}
  572. */
  573. msaa: {
  574. get: function () {
  575. return this._webgl2;
  576. },
  577. },
  578. /**
  579. * <code>true</code> if the OES_standard_derivatives extension is supported. This
  580. * extension provides access to <code>dFdx</code>, <code>dFdy</code>, and <code>fwidth</code>
  581. * functions from GLSL. A shader using these functions still needs to explicitly enable the
  582. * extension with <code>#extension GL_OES_standard_derivatives : enable</code>.
  583. * @memberof Context.prototype
  584. * @type {boolean}
  585. * @see {@link http://www.khronos.org/registry/gles/extensions/OES/OES_standard_derivatives.txt|OES_standard_derivatives}
  586. */
  587. standardDerivatives: {
  588. get: function () {
  589. return this._standardDerivatives || this._webgl2;
  590. },
  591. },
  592. /**
  593. * <code>true</code> if the EXT_float_blend extension is supported. This
  594. * extension enables blending with 32-bit float values.
  595. * @memberof Context.prototype
  596. * @type {boolean}
  597. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_float_blend/}
  598. */
  599. floatBlend: {
  600. get: function () {
  601. return this._floatBlend;
  602. },
  603. },
  604. /**
  605. * <code>true</code> if the EXT_blend_minmax extension is supported. This
  606. * extension extends blending capabilities by adding two new blend equations:
  607. * the minimum or maximum color components of the source and destination colors.
  608. * @memberof Context.prototype
  609. * @type {boolean}
  610. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_blend_minmax/}
  611. */
  612. blendMinmax: {
  613. get: function () {
  614. return this._blendMinmax || this._webgl2;
  615. },
  616. },
  617. /**
  618. * <code>true</code> if the OES_element_index_uint extension is supported. This
  619. * extension allows the use of unsigned int indices, which can improve performance by
  620. * eliminating batch breaking caused by unsigned short indices.
  621. * @memberof Context.prototype
  622. * @type {boolean}
  623. * @see {@link http://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/|OES_element_index_uint}
  624. */
  625. elementIndexUint: {
  626. get: function () {
  627. return this._elementIndexUint || this._webgl2;
  628. },
  629. },
  630. /**
  631. * <code>true</code> if WEBGL_depth_texture is supported. This extension provides
  632. * access to depth textures that, for example, can be attached to framebuffers for shadow mapping.
  633. * @memberof Context.prototype
  634. * @type {boolean}
  635. * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture}
  636. */
  637. depthTexture: {
  638. get: function () {
  639. return this._depthTexture || this._webgl2;
  640. },
  641. },
  642. /**
  643. * <code>true</code> if OES_texture_float is supported. This extension provides
  644. * access to floating point textures that, for example, can be attached to framebuffers for high dynamic range.
  645. * @memberof Context.prototype
  646. * @type {boolean}
  647. * @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_float/}
  648. */
  649. floatingPointTexture: {
  650. get: function () {
  651. return this._webgl2 || this._textureFloat;
  652. },
  653. },
  654. /**
  655. * <code>true</code> if OES_texture_half_float is supported. This extension provides
  656. * access to floating point textures that, for example, can be attached to framebuffers for high dynamic range.
  657. * @memberof Context.prototype
  658. * @type {boolean}
  659. * @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/}
  660. */
  661. halfFloatingPointTexture: {
  662. get: function () {
  663. return this._webgl2 || this._textureHalfFloat;
  664. },
  665. },
  666. /**
  667. * <code>true</code> if OES_texture_float_linear is supported. This extension provides
  668. * access to linear sampling methods for minification and magnification filters of floating-point textures.
  669. * @memberof Context.prototype
  670. * @type {boolean}
  671. * @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/}
  672. */
  673. textureFloatLinear: {
  674. get: function () {
  675. return this._textureFloatLinear;
  676. },
  677. },
  678. /**
  679. * <code>true</code> if OES_texture_half_float_linear is supported. This extension provides
  680. * access to linear sampling methods for minification and magnification filters of half floating-point textures.
  681. * @memberof Context.prototype
  682. * @type {boolean}
  683. * @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float_linear/}
  684. */
  685. textureHalfFloatLinear: {
  686. get: function () {
  687. return (
  688. (this._webgl2 && this._textureFloatLinear) ||
  689. (!this._webgl2 && this._textureHalfFloatLinear)
  690. );
  691. },
  692. },
  693. /**
  694. * <code>true</code> if EXT_texture_filter_anisotropic is supported. This extension provides
  695. * access to anisotropic filtering for textured surfaces at an oblique angle from the viewer.
  696. * @memberof Context.prototype
  697. * @type {boolean}
  698. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/}
  699. */
  700. textureFilterAnisotropic: {
  701. get: function () {
  702. return !!this._textureFilterAnisotropic;
  703. },
  704. },
  705. /**
  706. * <code>true</code> if WEBGL_compressed_texture_s3tc is supported. This extension provides
  707. * access to DXT compressed textures.
  708. * @memberof Context.prototype
  709. * @type {boolean}
  710. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/}
  711. */
  712. s3tc: {
  713. get: function () {
  714. return this._s3tc;
  715. },
  716. },
  717. /**
  718. * <code>true</code> if WEBGL_compressed_texture_pvrtc is supported. This extension provides
  719. * access to PVR compressed textures.
  720. * @memberof Context.prototype
  721. * @type {boolean}
  722. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/}
  723. */
  724. pvrtc: {
  725. get: function () {
  726. return this._pvrtc;
  727. },
  728. },
  729. /**
  730. * <code>true</code> if WEBGL_compressed_texture_astc is supported. This extension provides
  731. * access to ASTC compressed textures.
  732. * @memberof Context.prototype
  733. * @type {boolean}
  734. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/}
  735. */
  736. astc: {
  737. get: function () {
  738. return this._astc;
  739. },
  740. },
  741. /**
  742. * <code>true</code> if WEBGL_compressed_texture_etc is supported. This extension provides
  743. * access to ETC compressed textures.
  744. * @memberof Context.prototype
  745. * @type {boolean}
  746. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/}
  747. */
  748. etc: {
  749. get: function () {
  750. return this._etc;
  751. },
  752. },
  753. /**
  754. * <code>true</code> if WEBGL_compressed_texture_etc1 is supported. This extension provides
  755. * access to ETC1 compressed textures.
  756. * @memberof Context.prototype
  757. * @type {boolean}
  758. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/}
  759. */
  760. etc1: {
  761. get: function () {
  762. return this._etc1;
  763. },
  764. },
  765. /**
  766. * <code>true</code> if EXT_texture_compression_bptc is supported. This extension provides
  767. * access to BC7 compressed textures.
  768. * @memberof Context.prototype
  769. * @type {boolean}
  770. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_texture_compression_bptc/}
  771. */
  772. bc7: {
  773. get: function () {
  774. return this._bc7;
  775. },
  776. },
  777. /**
  778. * <code>true</code> if S3TC, PVRTC, ASTC, ETC, ETC1, or BC7 compression is supported.
  779. * @memberof Context.prototype
  780. * @type {boolean}
  781. */
  782. supportsBasis: {
  783. get: function () {
  784. return (
  785. this._s3tc ||
  786. this._pvrtc ||
  787. this._astc ||
  788. this._etc ||
  789. this._etc1 ||
  790. this._bc7
  791. );
  792. },
  793. },
  794. /**
  795. * <code>true</code> if the OES_vertex_array_object extension is supported. This
  796. * extension can improve performance by reducing the overhead of switching vertex arrays.
  797. * When enabled, this extension is automatically used by {@link VertexArray}.
  798. * @memberof Context.prototype
  799. * @type {boolean}
  800. * @see {@link http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/|OES_vertex_array_object}
  801. */
  802. vertexArrayObject: {
  803. get: function () {
  804. return this._vertexArrayObject || this._webgl2;
  805. },
  806. },
  807. /**
  808. * <code>true</code> if the EXT_frag_depth extension is supported. This
  809. * extension provides access to the <code>gl_FragDepthEXT</code> built-in output variable
  810. * from GLSL fragment shaders. A shader using these functions still needs to explicitly enable the
  811. * extension with <code>#extension GL_EXT_frag_depth : enable</code>.
  812. * @memberof Context.prototype
  813. * @type {boolean}
  814. * @see {@link http://www.khronos.org/registry/webgl/extensions/EXT_frag_depth/|EXT_frag_depth}
  815. */
  816. fragmentDepth: {
  817. get: function () {
  818. return this._fragDepth || this._webgl2;
  819. },
  820. },
  821. /**
  822. * <code>true</code> if the ANGLE_instanced_arrays extension is supported. This
  823. * extension provides access to instanced rendering.
  824. * @memberof Context.prototype
  825. * @type {boolean}
  826. * @see {@link https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays}
  827. */
  828. instancedArrays: {
  829. get: function () {
  830. return this._instancedArrays || this._webgl2;
  831. },
  832. },
  833. /**
  834. * <code>true</code> if the EXT_color_buffer_float extension is supported. This
  835. * extension makes the gl.RGBA32F format color renderable.
  836. * @memberof Context.prototype
  837. * @type {boolean}
  838. * @see {@link https://www.khronos.org/registry/webgl/extensions/WEBGL_color_buffer_float/}
  839. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_float/}
  840. */
  841. colorBufferFloat: {
  842. get: function () {
  843. return this._colorBufferFloat;
  844. },
  845. },
  846. /**
  847. * <code>true</code> if the EXT_color_buffer_half_float extension is supported. This
  848. * extension makes the format gl.RGBA16F format color renderable.
  849. * @memberof Context.prototype
  850. * @type {boolean}
  851. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_half_float/}
  852. * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_float/}
  853. */
  854. colorBufferHalfFloat: {
  855. get: function () {
  856. return (
  857. (this._webgl2 && this._colorBufferFloat) ||
  858. (!this._webgl2 && this._colorBufferHalfFloat)
  859. );
  860. },
  861. },
  862. /**
  863. * <code>true</code> if the WEBGL_draw_buffers extension is supported. This
  864. * extensions provides support for multiple render targets. The framebuffer object can have mutiple
  865. * color attachments and the GLSL fragment shader can write to the built-in output array <code>gl_FragData</code>.
  866. * A shader using this feature needs to explicitly enable the extension with
  867. * <code>#extension GL_EXT_draw_buffers : enable</code>.
  868. * @memberof Context.prototype
  869. * @type {boolean}
  870. * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/|WEBGL_draw_buffers}
  871. */
  872. drawBuffers: {
  873. get: function () {
  874. return this._drawBuffers || this._webgl2;
  875. },
  876. },
  877. debugShaders: {
  878. get: function () {
  879. return this._debugShaders;
  880. },
  881. },
  882. throwOnWebGLError: {
  883. get: function () {
  884. return this._throwOnWebGLError;
  885. },
  886. set: function (value) {
  887. this._throwOnWebGLError = value;
  888. this._gl = wrapGL(
  889. this._originalGLContext,
  890. value ? throwOnError : undefined
  891. );
  892. },
  893. },
  894. /**
  895. * A 1x1 RGBA texture initialized to [255, 255, 255, 255]. This can
  896. * be used as a placeholder texture while other textures are downloaded.
  897. * @memberof Context.prototype
  898. * @type {Texture}
  899. */
  900. defaultTexture: {
  901. get: function () {
  902. if (this._defaultTexture === undefined) {
  903. this._defaultTexture = new Texture({
  904. context: this,
  905. source: {
  906. width: 1,
  907. height: 1,
  908. arrayBufferView: new Uint8Array([255, 255, 255, 255]),
  909. },
  910. flipY: false,
  911. });
  912. }
  913. return this._defaultTexture;
  914. },
  915. },
  916. /**
  917. * A 1x1 RGB texture initialized to [0, 0, 0] representing a material that is
  918. * not emissive. This can be used as a placeholder texture for emissive
  919. * textures while other textures are downloaded.
  920. * @memberof Context.prototype
  921. * @type {Texture}
  922. */
  923. defaultEmissiveTexture: {
  924. get: function () {
  925. if (this._defaultEmissiveTexture === undefined) {
  926. this._defaultEmissiveTexture = new Texture({
  927. context: this,
  928. pixelFormat: PixelFormat.RGB,
  929. source: {
  930. width: 1,
  931. height: 1,
  932. arrayBufferView: new Uint8Array([0, 0, 0]),
  933. },
  934. flipY: false,
  935. });
  936. }
  937. return this._defaultEmissiveTexture;
  938. },
  939. },
  940. /**
  941. * A 1x1 RGBA texture initialized to [128, 128, 255] to encode a tangent
  942. * space normal pointing in the +z direction, i.e. (0, 0, 1). This can
  943. * be used as a placeholder normal texture while other textures are
  944. * downloaded.
  945. * @memberof Context.prototype
  946. * @type {Texture}
  947. */
  948. defaultNormalTexture: {
  949. get: function () {
  950. if (this._defaultNormalTexture === undefined) {
  951. this._defaultNormalTexture = new Texture({
  952. context: this,
  953. pixelFormat: PixelFormat.RGB,
  954. source: {
  955. width: 1,
  956. height: 1,
  957. arrayBufferView: new Uint8Array([128, 128, 255]),
  958. },
  959. flipY: false,
  960. });
  961. }
  962. return this._defaultNormalTexture;
  963. },
  964. },
  965. /**
  966. * A cube map, where each face is a 1x1 RGBA texture initialized to
  967. * [255, 255, 255, 255]. This can be used as a placeholder cube map while
  968. * other cube maps are downloaded.
  969. * @memberof Context.prototype
  970. * @type {CubeMap}
  971. */
  972. defaultCubeMap: {
  973. get: function () {
  974. if (this._defaultCubeMap === undefined) {
  975. const face = {
  976. width: 1,
  977. height: 1,
  978. arrayBufferView: new Uint8Array([255, 255, 255, 255]),
  979. };
  980. this._defaultCubeMap = new CubeMap({
  981. context: this,
  982. source: {
  983. positiveX: face,
  984. negativeX: face,
  985. positiveY: face,
  986. negativeY: face,
  987. positiveZ: face,
  988. negativeZ: face,
  989. },
  990. flipY: false,
  991. });
  992. }
  993. return this._defaultCubeMap;
  994. },
  995. },
  996. /**
  997. * The drawingBufferHeight of the underlying GL context.
  998. * @memberof Context.prototype
  999. * @type {number}
  1000. * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferHeight|drawingBufferHeight}
  1001. */
  1002. drawingBufferHeight: {
  1003. get: function () {
  1004. return this._gl.drawingBufferHeight;
  1005. },
  1006. },
  1007. /**
  1008. * The drawingBufferWidth of the underlying GL context.
  1009. * @memberof Context.prototype
  1010. * @type {number}
  1011. * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferWidth|drawingBufferWidth}
  1012. */
  1013. drawingBufferWidth: {
  1014. get: function () {
  1015. return this._gl.drawingBufferWidth;
  1016. },
  1017. },
  1018. /**
  1019. * Gets an object representing the currently bound framebuffer. While this instance is not an actual
  1020. * {@link Framebuffer}, it is used to represent the default framebuffer in calls to
  1021. * {@link Texture.fromFramebuffer}.
  1022. * @memberof Context.prototype
  1023. * @type {object}
  1024. */
  1025. defaultFramebuffer: {
  1026. get: function () {
  1027. return defaultFramebufferMarker;
  1028. },
  1029. },
  1030. });
  1031. /**
  1032. * Validates a framebuffer.
  1033. * Available in debug builds only.
  1034. * @private
  1035. */
  1036. function validateFramebuffer(context) {
  1037. //>>includeStart('debug', pragmas.debug);
  1038. if (context.validateFramebuffer) {
  1039. const gl = context._gl;
  1040. const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  1041. if (status !== gl.FRAMEBUFFER_COMPLETE) {
  1042. let message;
  1043. switch (status) {
  1044. case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
  1045. message =
  1046. "Framebuffer is not complete. Incomplete attachment: at least one attachment point with a renderbuffer or texture attached has its attached object no longer in existence or has an attached image with a width or height of zero, or the color attachment point has a non-color-renderable image attached, or the depth attachment point has a non-depth-renderable image attached, or the stencil attachment point has a non-stencil-renderable image attached. Color-renderable formats include GL_RGBA4, GL_RGB5_A1, and GL_RGB565. GL_DEPTH_COMPONENT16 is the only depth-renderable format. GL_STENCIL_INDEX8 is the only stencil-renderable format.";
  1047. break;
  1048. case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
  1049. message =
  1050. "Framebuffer is not complete. Incomplete dimensions: not all attached images have the same width and height.";
  1051. break;
  1052. case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  1053. message =
  1054. "Framebuffer is not complete. Missing attachment: no images are attached to the framebuffer.";
  1055. break;
  1056. case gl.FRAMEBUFFER_UNSUPPORTED:
  1057. message =
  1058. "Framebuffer is not complete. Unsupported: the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.";
  1059. break;
  1060. }
  1061. throw new DeveloperError(message);
  1062. }
  1063. }
  1064. //>>includeEnd('debug');
  1065. }
  1066. function applyRenderState(context, renderState, passState, clear) {
  1067. const previousRenderState = context._currentRenderState;
  1068. const previousPassState = context._currentPassState;
  1069. context._currentRenderState = renderState;
  1070. context._currentPassState = passState;
  1071. RenderState.partialApply(
  1072. context._gl,
  1073. previousRenderState,
  1074. renderState,
  1075. previousPassState,
  1076. passState,
  1077. clear
  1078. );
  1079. }
  1080. let scratchBackBufferArray;
  1081. // this check must use typeof, not defined, because defined doesn't work with undeclared variables.
  1082. if (typeof WebGLRenderingContext !== "undefined") {
  1083. scratchBackBufferArray = [WebGLConstants.BACK];
  1084. }
  1085. function bindFramebuffer(context, framebuffer) {
  1086. if (framebuffer !== context._currentFramebuffer) {
  1087. context._currentFramebuffer = framebuffer;
  1088. let buffers = scratchBackBufferArray;
  1089. if (defined(framebuffer)) {
  1090. framebuffer._bind();
  1091. validateFramebuffer(context);
  1092. // TODO: Need a way for a command to give what draw buffers are active.
  1093. buffers = framebuffer._getActiveColorAttachments();
  1094. } else {
  1095. const gl = context._gl;
  1096. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  1097. }
  1098. if (context.drawBuffers) {
  1099. context.glDrawBuffers(buffers);
  1100. }
  1101. }
  1102. }
  1103. const defaultClearCommand = new ClearCommand();
  1104. Context.prototype.clear = function (clearCommand, passState) {
  1105. clearCommand = defaultValue(clearCommand, defaultClearCommand);
  1106. passState = defaultValue(passState, this._defaultPassState);
  1107. const gl = this._gl;
  1108. let bitmask = 0;
  1109. const c = clearCommand.color;
  1110. const d = clearCommand.depth;
  1111. const s = clearCommand.stencil;
  1112. if (defined(c)) {
  1113. if (!Color.equals(this._clearColor, c)) {
  1114. Color.clone(c, this._clearColor);
  1115. gl.clearColor(c.red, c.green, c.blue, c.alpha);
  1116. }
  1117. bitmask |= gl.COLOR_BUFFER_BIT;
  1118. }
  1119. if (defined(d)) {
  1120. if (d !== this._clearDepth) {
  1121. this._clearDepth = d;
  1122. gl.clearDepth(d);
  1123. }
  1124. bitmask |= gl.DEPTH_BUFFER_BIT;
  1125. }
  1126. if (defined(s)) {
  1127. if (s !== this._clearStencil) {
  1128. this._clearStencil = s;
  1129. gl.clearStencil(s);
  1130. }
  1131. bitmask |= gl.STENCIL_BUFFER_BIT;
  1132. }
  1133. const rs = defaultValue(clearCommand.renderState, this._defaultRenderState);
  1134. applyRenderState(this, rs, passState, true);
  1135. // The command's framebuffer takes presidence over the pass' framebuffer, e.g., for off-screen rendering.
  1136. const framebuffer = defaultValue(
  1137. clearCommand.framebuffer,
  1138. passState.framebuffer
  1139. );
  1140. bindFramebuffer(this, framebuffer);
  1141. gl.clear(bitmask);
  1142. };
  1143. function beginDraw(
  1144. context,
  1145. framebuffer,
  1146. passState,
  1147. shaderProgram,
  1148. renderState
  1149. ) {
  1150. //>>includeStart('debug', pragmas.debug);
  1151. if (defined(framebuffer) && renderState.depthTest) {
  1152. if (renderState.depthTest.enabled && !framebuffer.hasDepthAttachment) {
  1153. throw new DeveloperError(
  1154. "The depth test can not be enabled (drawCommand.renderState.depthTest.enabled) because the framebuffer (drawCommand.framebuffer) does not have a depth or depth-stencil renderbuffer."
  1155. );
  1156. }
  1157. }
  1158. //>>includeEnd('debug');
  1159. bindFramebuffer(context, framebuffer);
  1160. applyRenderState(context, renderState, passState, false);
  1161. shaderProgram._bind();
  1162. context._maxFrameTextureUnitIndex = Math.max(
  1163. context._maxFrameTextureUnitIndex,
  1164. shaderProgram.maximumTextureUnitIndex
  1165. );
  1166. }
  1167. function continueDraw(context, drawCommand, shaderProgram, uniformMap) {
  1168. const primitiveType = drawCommand._primitiveType;
  1169. const va = drawCommand._vertexArray;
  1170. let offset = drawCommand._offset;
  1171. let count = drawCommand._count;
  1172. const instanceCount = drawCommand.instanceCount;
  1173. //>>includeStart('debug', pragmas.debug);
  1174. if (!PrimitiveType.validate(primitiveType)) {
  1175. throw new DeveloperError(
  1176. "drawCommand.primitiveType is required and must be valid."
  1177. );
  1178. }
  1179. Check.defined("drawCommand.vertexArray", va);
  1180. Check.typeOf.number.greaterThanOrEquals("drawCommand.offset", offset, 0);
  1181. if (defined(count)) {
  1182. Check.typeOf.number.greaterThanOrEquals("drawCommand.count", count, 0);
  1183. }
  1184. Check.typeOf.number.greaterThanOrEquals(
  1185. "drawCommand.instanceCount",
  1186. instanceCount,
  1187. 0
  1188. );
  1189. if (instanceCount > 0 && !context.instancedArrays) {
  1190. throw new DeveloperError("Instanced arrays extension is not supported");
  1191. }
  1192. //>>includeEnd('debug');
  1193. context._us.model = defaultValue(drawCommand._modelMatrix, Matrix4.IDENTITY);
  1194. shaderProgram._setUniforms(
  1195. uniformMap,
  1196. context._us,
  1197. context.validateShaderProgram
  1198. );
  1199. va._bind();
  1200. const indexBuffer = va.indexBuffer;
  1201. if (defined(indexBuffer)) {
  1202. offset = offset * indexBuffer.bytesPerIndex; // offset in vertices to offset in bytes
  1203. count = defaultValue(count, indexBuffer.numberOfIndices);
  1204. if (instanceCount === 0) {
  1205. context._gl.drawElements(
  1206. primitiveType,
  1207. count,
  1208. indexBuffer.indexDatatype,
  1209. offset
  1210. );
  1211. } else {
  1212. context.glDrawElementsInstanced(
  1213. primitiveType,
  1214. count,
  1215. indexBuffer.indexDatatype,
  1216. offset,
  1217. instanceCount
  1218. );
  1219. }
  1220. } else {
  1221. count = defaultValue(count, va.numberOfVertices);
  1222. if (instanceCount === 0) {
  1223. context._gl.drawArrays(primitiveType, offset, count);
  1224. } else {
  1225. context.glDrawArraysInstanced(
  1226. primitiveType,
  1227. offset,
  1228. count,
  1229. instanceCount
  1230. );
  1231. }
  1232. }
  1233. va._unBind();
  1234. }
  1235. Context.prototype.draw = function (
  1236. drawCommand,
  1237. passState,
  1238. shaderProgram,
  1239. uniformMap
  1240. ) {
  1241. //>>includeStart('debug', pragmas.debug);
  1242. Check.defined("drawCommand", drawCommand);
  1243. Check.defined("drawCommand.shaderProgram", drawCommand._shaderProgram);
  1244. //>>includeEnd('debug');
  1245. passState = defaultValue(passState, this._defaultPassState);
  1246. // The command's framebuffer takes presidence over the pass' framebuffer, e.g., for off-screen rendering.
  1247. const framebuffer = defaultValue(
  1248. drawCommand._framebuffer,
  1249. passState.framebuffer
  1250. );
  1251. const renderState = defaultValue(
  1252. drawCommand._renderState,
  1253. this._defaultRenderState
  1254. );
  1255. shaderProgram = defaultValue(shaderProgram, drawCommand._shaderProgram);
  1256. uniformMap = defaultValue(uniformMap, drawCommand._uniformMap);
  1257. beginDraw(this, framebuffer, passState, shaderProgram, renderState);
  1258. continueDraw(this, drawCommand, shaderProgram, uniformMap);
  1259. };
  1260. Context.prototype.endFrame = function () {
  1261. const gl = this._gl;
  1262. gl.useProgram(null);
  1263. this._currentFramebuffer = undefined;
  1264. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  1265. const buffers = scratchBackBufferArray;
  1266. if (this.drawBuffers) {
  1267. this.glDrawBuffers(buffers);
  1268. }
  1269. const length = this._maxFrameTextureUnitIndex;
  1270. this._maxFrameTextureUnitIndex = 0;
  1271. for (let i = 0; i < length; ++i) {
  1272. gl.activeTexture(gl.TEXTURE0 + i);
  1273. gl.bindTexture(gl.TEXTURE_2D, null);
  1274. gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
  1275. }
  1276. };
  1277. Context.prototype.readPixels = function (readState) {
  1278. const gl = this._gl;
  1279. readState = defaultValue(readState, defaultValue.EMPTY_OBJECT);
  1280. const x = Math.max(defaultValue(readState.x, 0), 0);
  1281. const y = Math.max(defaultValue(readState.y, 0), 0);
  1282. const width = defaultValue(readState.width, gl.drawingBufferWidth);
  1283. const height = defaultValue(readState.height, gl.drawingBufferHeight);
  1284. const framebuffer = readState.framebuffer;
  1285. //>>includeStart('debug', pragmas.debug);
  1286. Check.typeOf.number.greaterThan("readState.width", width, 0);
  1287. Check.typeOf.number.greaterThan("readState.height", height, 0);
  1288. //>>includeEnd('debug');
  1289. let pixelDatatype = PixelDatatype.UNSIGNED_BYTE;
  1290. if (defined(framebuffer) && framebuffer.numberOfColorAttachments > 0) {
  1291. pixelDatatype = framebuffer.getColorTexture(0).pixelDatatype;
  1292. }
  1293. const pixels = PixelFormat.createTypedArray(
  1294. PixelFormat.RGBA,
  1295. pixelDatatype,
  1296. width,
  1297. height
  1298. );
  1299. bindFramebuffer(this, framebuffer);
  1300. gl.readPixels(
  1301. x,
  1302. y,
  1303. width,
  1304. height,
  1305. PixelFormat.RGBA,
  1306. PixelDatatype.toWebGLConstant(pixelDatatype, this),
  1307. pixels
  1308. );
  1309. return pixels;
  1310. };
  1311. const viewportQuadAttributeLocations = {
  1312. position: 0,
  1313. textureCoordinates: 1,
  1314. };
  1315. Context.prototype.getViewportQuadVertexArray = function () {
  1316. // Per-context cache for viewport quads
  1317. let vertexArray = this.cache.viewportQuad_vertexArray;
  1318. if (!defined(vertexArray)) {
  1319. const geometry = new Geometry({
  1320. attributes: {
  1321. position: new GeometryAttribute({
  1322. componentDatatype: ComponentDatatype.FLOAT,
  1323. componentsPerAttribute: 2,
  1324. values: [-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0],
  1325. }),
  1326. textureCoordinates: new GeometryAttribute({
  1327. componentDatatype: ComponentDatatype.FLOAT,
  1328. componentsPerAttribute: 2,
  1329. values: [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0],
  1330. }),
  1331. },
  1332. // Workaround Internet Explorer 11.0.8 lack of TRIANGLE_FAN
  1333. indices: new Uint16Array([0, 1, 2, 0, 2, 3]),
  1334. primitiveType: PrimitiveType.TRIANGLES,
  1335. });
  1336. vertexArray = VertexArray.fromGeometry({
  1337. context: this,
  1338. geometry: geometry,
  1339. attributeLocations: viewportQuadAttributeLocations,
  1340. bufferUsage: BufferUsage.STATIC_DRAW,
  1341. interleave: true,
  1342. });
  1343. this.cache.viewportQuad_vertexArray = vertexArray;
  1344. }
  1345. return vertexArray;
  1346. };
  1347. Context.prototype.createViewportQuadCommand = function (
  1348. fragmentShaderSource,
  1349. overrides
  1350. ) {
  1351. overrides = defaultValue(overrides, defaultValue.EMPTY_OBJECT);
  1352. return new DrawCommand({
  1353. vertexArray: this.getViewportQuadVertexArray(),
  1354. primitiveType: PrimitiveType.TRIANGLES,
  1355. renderState: overrides.renderState,
  1356. shaderProgram: ShaderProgram.fromCache({
  1357. context: this,
  1358. vertexShaderSource: ViewportQuadVS,
  1359. fragmentShaderSource: fragmentShaderSource,
  1360. attributeLocations: viewportQuadAttributeLocations,
  1361. }),
  1362. uniformMap: overrides.uniformMap,
  1363. owner: overrides.owner,
  1364. framebuffer: overrides.framebuffer,
  1365. pass: overrides.pass,
  1366. });
  1367. };
  1368. /**
  1369. * Gets the object associated with a pick color.
  1370. *
  1371. * @param {Color} pickColor The pick color.
  1372. * @returns {object} The object associated with the pick color, or undefined if no object is associated with that color.
  1373. *
  1374. * @example
  1375. * const object = context.getObjectByPickColor(pickColor);
  1376. *
  1377. * @see Context#createPickId
  1378. */
  1379. Context.prototype.getObjectByPickColor = function (pickColor) {
  1380. //>>includeStart('debug', pragmas.debug);
  1381. Check.defined("pickColor", pickColor);
  1382. //>>includeEnd('debug');
  1383. return this._pickObjects[pickColor.toRgba()];
  1384. };
  1385. function PickId(pickObjects, key, color) {
  1386. this._pickObjects = pickObjects;
  1387. this.key = key;
  1388. this.color = color;
  1389. }
  1390. Object.defineProperties(PickId.prototype, {
  1391. object: {
  1392. get: function () {
  1393. return this._pickObjects[this.key];
  1394. },
  1395. set: function (value) {
  1396. this._pickObjects[this.key] = value;
  1397. },
  1398. },
  1399. });
  1400. PickId.prototype.destroy = function () {
  1401. delete this._pickObjects[this.key];
  1402. return undefined;
  1403. };
  1404. /**
  1405. * Creates a unique ID associated with the input object for use with color-buffer picking.
  1406. * The ID has an RGBA color value unique to this context. You must call destroy()
  1407. * on the pick ID when destroying the input object.
  1408. *
  1409. * @param {object} object The object to associate with the pick ID.
  1410. * @returns {object} A PickId object with a <code>color</code> property.
  1411. *
  1412. * @exception {RuntimeError} Out of unique Pick IDs.
  1413. *
  1414. *
  1415. * @example
  1416. * this._pickId = context.createPickId({
  1417. * primitive : this,
  1418. * id : this.id
  1419. * });
  1420. *
  1421. * @see Context#getObjectByPickColor
  1422. */
  1423. Context.prototype.createPickId = function (object) {
  1424. //>>includeStart('debug', pragmas.debug);
  1425. Check.defined("object", object);
  1426. //>>includeEnd('debug');
  1427. // the increment and assignment have to be separate statements to
  1428. // actually detect overflow in the Uint32 value
  1429. ++this._nextPickColor[0];
  1430. const key = this._nextPickColor[0];
  1431. if (key === 0) {
  1432. // In case of overflow
  1433. throw new RuntimeError("Out of unique Pick IDs.");
  1434. }
  1435. this._pickObjects[key] = object;
  1436. return new PickId(this._pickObjects, key, Color.fromRgba(key));
  1437. };
  1438. Context.prototype.isDestroyed = function () {
  1439. return false;
  1440. };
  1441. Context.prototype.destroy = function () {
  1442. // Destroy all objects in the cache that have a destroy method.
  1443. const cache = this.cache;
  1444. for (const property in cache) {
  1445. if (cache.hasOwnProperty(property)) {
  1446. const propertyValue = cache[property];
  1447. if (defined(propertyValue.destroy)) {
  1448. propertyValue.destroy();
  1449. }
  1450. }
  1451. }
  1452. this._shaderCache = this._shaderCache.destroy();
  1453. this._textureCache = this._textureCache.destroy();
  1454. this._defaultTexture = this._defaultTexture && this._defaultTexture.destroy();
  1455. this._defaultEmissiveTexture =
  1456. this._defaultEmissiveTexture && this._defaultEmissiveTexture.destroy();
  1457. this._defaultNormalTexture =
  1458. this._defaultNormalTexture && this._defaultNormalTexture.destroy();
  1459. this._defaultCubeMap = this._defaultCubeMap && this._defaultCubeMap.destroy();
  1460. return destroyObject(this);
  1461. };
  1462. // Used for specs.
  1463. Context._deprecationWarning = deprecationWarning;
  1464. export default Context;