compat.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /**
  2. * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { isDebugMode } from '../../mol-util/debug';
  7. import { getErrorDescription } from './context';
  8. import { getProgram } from './program';
  9. import { getShader } from './shader';
  10. export type GLRenderingContext = WebGLRenderingContext | WebGL2RenderingContext
  11. export function isWebGL(gl: any): gl is WebGLRenderingContext {
  12. return typeof WebGLRenderingContext !== 'undefined' && gl instanceof WebGLRenderingContext;
  13. }
  14. export function isWebGL2(gl: any): gl is WebGL2RenderingContext {
  15. return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
  16. }
  17. export interface COMPAT_instanced_arrays {
  18. drawArraysInstanced(mode: number, first: number, count: number, primcount: number): void;
  19. drawElementsInstanced(mode: number, count: number, type: number, offset: number, primcount: number): void;
  20. vertexAttribDivisor(index: number, divisor: number): void;
  21. readonly VERTEX_ATTRIB_ARRAY_DIVISOR: number;
  22. }
  23. export function getInstancedArrays(gl: GLRenderingContext): COMPAT_instanced_arrays | null {
  24. if (isWebGL2(gl)) {
  25. return {
  26. drawArraysInstanced: gl.drawArraysInstanced.bind(gl),
  27. drawElementsInstanced: gl.drawElementsInstanced.bind(gl),
  28. vertexAttribDivisor: gl.vertexAttribDivisor.bind(gl),
  29. VERTEX_ATTRIB_ARRAY_DIVISOR: gl.VERTEX_ATTRIB_ARRAY_DIVISOR
  30. };
  31. } else {
  32. const ext = gl.getExtension('ANGLE_instanced_arrays');
  33. if (ext === null) return null;
  34. return {
  35. drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
  36. drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
  37. vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),
  38. VERTEX_ATTRIB_ARRAY_DIVISOR: ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
  39. };
  40. }
  41. }
  42. export interface COMPAT_standard_derivatives {
  43. readonly FRAGMENT_SHADER_DERIVATIVE_HINT: number;
  44. }
  45. export function getStandardDerivatives(gl: GLRenderingContext): COMPAT_standard_derivatives | null {
  46. if (isWebGL2(gl)) {
  47. return { FRAGMENT_SHADER_DERIVATIVE_HINT: gl.FRAGMENT_SHADER_DERIVATIVE_HINT };
  48. } else {
  49. const ext = gl.getExtension('OES_standard_derivatives');
  50. if (ext === null) return null;
  51. return { FRAGMENT_SHADER_DERIVATIVE_HINT: ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES };
  52. }
  53. }
  54. export interface COMPAT_element_index_uint {
  55. }
  56. export function getElementIndexUint(gl: GLRenderingContext): COMPAT_element_index_uint | null {
  57. return isWebGL2(gl) ? {} : gl.getExtension('OES_element_index_uint');
  58. }
  59. export interface COMPAT_vertex_array_object {
  60. readonly VERTEX_ARRAY_BINDING: number;
  61. bindVertexArray(arrayObject: WebGLVertexArrayObject | null): void;
  62. createVertexArray(): WebGLVertexArrayObject | null;
  63. deleteVertexArray(arrayObject: WebGLVertexArrayObject): void;
  64. isVertexArray(value: any): value is WebGLVertexArrayObject;
  65. }
  66. export function getVertexArrayObject(gl: GLRenderingContext): COMPAT_vertex_array_object | null {
  67. if (isWebGL2(gl)) {
  68. return {
  69. VERTEX_ARRAY_BINDING: gl.VERTEX_ARRAY_BINDING,
  70. bindVertexArray: gl.bindVertexArray.bind(gl),
  71. createVertexArray: gl.createVertexArray.bind(gl),
  72. deleteVertexArray: gl.deleteVertexArray.bind(gl),
  73. isVertexArray: gl.isVertexArray.bind(gl)
  74. };
  75. } else {
  76. const ext = gl.getExtension('OES_vertex_array_object');
  77. if (ext === null) return null;
  78. return {
  79. VERTEX_ARRAY_BINDING: ext.VERTEX_ARRAY_BINDING_OES,
  80. bindVertexArray: ext.bindVertexArrayOES.bind(ext),
  81. createVertexArray: ext.createVertexArrayOES.bind(ext),
  82. deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),
  83. isVertexArray: ext.isVertexArrayOES.bind(ext)
  84. };
  85. }
  86. }
  87. export interface COMPAT_texture_float {
  88. }
  89. export function getTextureFloat(gl: GLRenderingContext): COMPAT_texture_float | null {
  90. return isWebGL2(gl) ? {} : gl.getExtension('OES_texture_float');
  91. }
  92. export interface COMPAT_texture_float_linear {
  93. }
  94. export function getTextureFloatLinear(gl: GLRenderingContext): COMPAT_texture_float_linear | null {
  95. return gl.getExtension('OES_texture_float_linear');
  96. }
  97. export interface COMPAT_texture_half_float {
  98. readonly HALF_FLOAT: number
  99. }
  100. export function getTextureHalfFloat(gl: GLRenderingContext): COMPAT_texture_half_float | null {
  101. if (isWebGL2(gl)) {
  102. return { HALF_FLOAT: gl.HALF_FLOAT };
  103. } else {
  104. const ext = gl.getExtension('OES_texture_half_float');
  105. if (ext === null) return null;
  106. return { HALF_FLOAT: ext.HALF_FLOAT_OES };
  107. }
  108. }
  109. export interface COMPAT_texture_half_float_linear {
  110. }
  111. export function getTextureHalfFloatLinear(gl: GLRenderingContext): COMPAT_texture_half_float_linear | null {
  112. return gl.getExtension('OES_texture_half_float_linear');
  113. }
  114. export interface COMPAT_blend_minmax {
  115. readonly MIN: number
  116. readonly MAX: number
  117. }
  118. export function getBlendMinMax(gl: GLRenderingContext): COMPAT_blend_minmax | null {
  119. if (isWebGL2(gl)) {
  120. return { MIN: gl.MIN, MAX: gl.MAX };
  121. } else {
  122. const ext = gl.getExtension('EXT_blend_minmax');
  123. if (ext === null) return null;
  124. return { MIN: ext.MIN_EXT, MAX: ext.MAX_EXT };
  125. }
  126. }
  127. export interface COMPAT_frag_depth {
  128. }
  129. export function getFragDepth(gl: GLRenderingContext): COMPAT_frag_depth | null {
  130. return isWebGL2(gl) ? {} : gl.getExtension('EXT_frag_depth');
  131. }
  132. export interface COMPAT_color_buffer_float {
  133. readonly RGBA32F: number;
  134. }
  135. export function getColorBufferFloat(gl: GLRenderingContext): COMPAT_color_buffer_float | null {
  136. if (isWebGL2(gl)) {
  137. if (gl.getExtension('EXT_color_buffer_float') === null) return null;
  138. gl.getExtension('EXT_float_blend');
  139. return { RGBA32F: gl.RGBA32F };
  140. } else {
  141. const ext = gl.getExtension('WEBGL_color_buffer_float');
  142. if (ext === null) {
  143. // test as support may not be advertised by browsers
  144. gl.getExtension('OES_texture_float');
  145. return testColorBuffer(gl, gl.FLOAT) ? { RGBA32F: 0x8814 } : null;
  146. }
  147. gl.getExtension('EXT_float_blend');
  148. return { RGBA32F: ext.RGBA32F_EXT };
  149. }
  150. }
  151. export interface COMPAT_color_buffer_half_float {
  152. readonly RGBA16F: number;
  153. }
  154. export function getColorBufferHalfFloat(gl: GLRenderingContext): COMPAT_color_buffer_half_float | null {
  155. if (isWebGL2(gl)) {
  156. if (gl.getExtension('EXT_color_buffer_half_float') === null) return null;
  157. gl.getExtension('EXT_float_blend');
  158. return { RGBA16F: gl.RGBA16F };
  159. } else {
  160. const ext = gl.getExtension('EXT_color_buffer_half_float');
  161. if (ext === null) {
  162. // test as support may not be advertised by browsers
  163. gl.getExtension('OES_texture_half_float');
  164. return testColorBuffer(gl, 0x8D61) ? { RGBA16F: 0x881A } : null;
  165. }
  166. gl.getExtension('EXT_float_blend');
  167. return { RGBA16F: ext.RGBA16F_EXT };
  168. }
  169. }
  170. export interface COMPAT_draw_buffers {
  171. drawBuffers(buffers: number[]): void;
  172. readonly COLOR_ATTACHMENT0: number;
  173. readonly COLOR_ATTACHMENT1: number;
  174. readonly COLOR_ATTACHMENT2: number;
  175. readonly COLOR_ATTACHMENT3: number;
  176. readonly COLOR_ATTACHMENT4: number;
  177. readonly COLOR_ATTACHMENT5: number;
  178. readonly COLOR_ATTACHMENT6: number;
  179. readonly COLOR_ATTACHMENT7: number;
  180. readonly DRAW_BUFFER0: number;
  181. readonly DRAW_BUFFER1: number;
  182. readonly DRAW_BUFFER2: number;
  183. readonly DRAW_BUFFER3: number;
  184. readonly DRAW_BUFFER4: number;
  185. readonly DRAW_BUFFER5: number;
  186. readonly DRAW_BUFFER6: number;
  187. readonly DRAW_BUFFER7: number;
  188. readonly MAX_COLOR_ATTACHMENTS: number;
  189. readonly MAX_DRAW_BUFFERS: number;
  190. }
  191. export function getDrawBuffers(gl: GLRenderingContext): COMPAT_draw_buffers | null {
  192. if (isWebGL2(gl)) {
  193. return {
  194. drawBuffers: gl.drawBuffers.bind(gl),
  195. COLOR_ATTACHMENT0: gl.COLOR_ATTACHMENT0,
  196. COLOR_ATTACHMENT1: gl.COLOR_ATTACHMENT1,
  197. COLOR_ATTACHMENT2: gl.COLOR_ATTACHMENT2,
  198. COLOR_ATTACHMENT3: gl.COLOR_ATTACHMENT3,
  199. COLOR_ATTACHMENT4: gl.COLOR_ATTACHMENT4,
  200. COLOR_ATTACHMENT5: gl.COLOR_ATTACHMENT5,
  201. COLOR_ATTACHMENT6: gl.COLOR_ATTACHMENT6,
  202. COLOR_ATTACHMENT7: gl.COLOR_ATTACHMENT7,
  203. DRAW_BUFFER0: gl.DRAW_BUFFER0,
  204. DRAW_BUFFER1: gl.DRAW_BUFFER1,
  205. DRAW_BUFFER2: gl.DRAW_BUFFER2,
  206. DRAW_BUFFER3: gl.DRAW_BUFFER3,
  207. DRAW_BUFFER4: gl.DRAW_BUFFER4,
  208. DRAW_BUFFER5: gl.DRAW_BUFFER5,
  209. DRAW_BUFFER6: gl.DRAW_BUFFER6,
  210. DRAW_BUFFER7: gl.DRAW_BUFFER7,
  211. MAX_COLOR_ATTACHMENTS: gl.MAX_COLOR_ATTACHMENTS,
  212. MAX_DRAW_BUFFERS: gl.MAX_DRAW_BUFFERS,
  213. };
  214. } else {
  215. const ext = gl.getExtension('WEBGL_draw_buffers');
  216. if (ext === null) return null;
  217. return {
  218. drawBuffers: ext.drawBuffersWEBGL.bind(ext),
  219. COLOR_ATTACHMENT0: ext.COLOR_ATTACHMENT0_WEBGL,
  220. COLOR_ATTACHMENT1: ext.COLOR_ATTACHMENT1_WEBGL,
  221. COLOR_ATTACHMENT2: ext.COLOR_ATTACHMENT2_WEBGL,
  222. COLOR_ATTACHMENT3: ext.COLOR_ATTACHMENT3_WEBGL,
  223. COLOR_ATTACHMENT4: ext.COLOR_ATTACHMENT4_WEBGL,
  224. COLOR_ATTACHMENT5: ext.COLOR_ATTACHMENT5_WEBGL,
  225. COLOR_ATTACHMENT6: ext.COLOR_ATTACHMENT6_WEBGL,
  226. COLOR_ATTACHMENT7: ext.COLOR_ATTACHMENT7_WEBGL,
  227. DRAW_BUFFER0: ext.DRAW_BUFFER0_WEBGL,
  228. DRAW_BUFFER1: ext.DRAW_BUFFER1_WEBGL,
  229. DRAW_BUFFER2: ext.DRAW_BUFFER2_WEBGL,
  230. DRAW_BUFFER3: ext.DRAW_BUFFER3_WEBGL,
  231. DRAW_BUFFER4: ext.DRAW_BUFFER4_WEBGL,
  232. DRAW_BUFFER5: ext.DRAW_BUFFER5_WEBGL,
  233. DRAW_BUFFER6: ext.DRAW_BUFFER6_WEBGL,
  234. DRAW_BUFFER7: ext.DRAW_BUFFER7_WEBGL,
  235. MAX_COLOR_ATTACHMENTS: ext.MAX_COLOR_ATTACHMENTS_WEBGL,
  236. MAX_DRAW_BUFFERS: ext.MAX_DRAW_BUFFERS_WEBGL,
  237. };
  238. }
  239. }
  240. export interface COMPAT_shader_texture_lod {
  241. }
  242. export function getShaderTextureLod(gl: GLRenderingContext): COMPAT_shader_texture_lod | null {
  243. return isWebGL2(gl) ? {} : gl.getExtension('EXT_shader_texture_lod');
  244. }
  245. export interface COMPAT_depth_texture {
  246. readonly UNSIGNED_INT_24_8: number;
  247. }
  248. export function getDepthTexture(gl: GLRenderingContext): COMPAT_depth_texture | null {
  249. if (isWebGL2(gl)) {
  250. return {
  251. UNSIGNED_INT_24_8: gl.UNSIGNED_INT_24_8
  252. };
  253. } else {
  254. const ext = gl.getExtension('WEBGL_depth_texture');
  255. if (ext === null) return null;
  256. return {
  257. UNSIGNED_INT_24_8: ext.UNSIGNED_INT_24_8_WEBGL
  258. };
  259. }
  260. }
  261. export interface COMPAT_sRGB {
  262. readonly FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: number;
  263. readonly SRGB8_ALPHA8: number;
  264. readonly SRGB8: number;
  265. readonly SRGB: number;
  266. }
  267. export function getSRGB(gl: GLRenderingContext): COMPAT_sRGB | null {
  268. if (isWebGL2(gl)) {
  269. return {
  270. FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
  271. SRGB8_ALPHA8: gl.SRGB8_ALPHA8,
  272. SRGB8: gl.SRGB8,
  273. SRGB: gl.SRGB
  274. };
  275. } else {
  276. const ext = gl.getExtension('EXT_sRGB');
  277. if (ext === null) return null;
  278. return {
  279. FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
  280. SRGB8_ALPHA8: ext.SRGB8_ALPHA8_EXT,
  281. SRGB8: ext.SRGB_ALPHA_EXT,
  282. SRGB: ext.SRGB_EXT
  283. };
  284. }
  285. }
  286. export interface COMPAT_disjoint_timer_query {
  287. /** A GLint indicating the number of bits used to hold the query result for the given target. */
  288. QUERY_COUNTER_BITS: number
  289. /** A WebGLQuery object, which is the currently active query for the given target. */
  290. CURRENT_QUERY: number
  291. /** A GLuint64EXT containing the query result. */
  292. QUERY_RESULT: number
  293. /** A GLboolean indicating whether or not a query result is available. */
  294. QUERY_RESULT_AVAILABLE: number
  295. /** Elapsed time (in nanoseconds). */
  296. TIME_ELAPSED: number
  297. /** The current time. */
  298. TIMESTAMP: number
  299. /** A GLboolean indicating whether or not the GPU performed any disjoint operation. */
  300. GPU_DISJOINT: number
  301. /** Creates a new WebGLTimerQueryEXT. */
  302. createQuery: () => WebGLQuery
  303. /** Deletes a given WebGLTimerQueryEXT. */
  304. deleteQuery: (query: WebGLQuery) => void
  305. /** Returns true if a given object is a valid WebGLTimerQueryEXT. */
  306. isQuery: (query: WebGLQuery) => boolean
  307. /** The timer starts when all commands prior to beginQueryEXT have been fully executed. */
  308. beginQuery: (target: number, query: WebGLQuery) => void
  309. /** The timer stops when all commands prior to endQueryEXT have been fully executed. */
  310. endQuery: (target: number) => void
  311. /** Records the current time into the corresponding query object. */
  312. queryCounter: (query: WebGLQuery, target: number) => void
  313. /** Returns information about a query target. */
  314. getQuery: (target: number, pname: number) => WebGLQuery | number
  315. /** Return the state of a query object. */
  316. getQueryParameter: (query: WebGLQuery, pname: number) => number | boolean
  317. }
  318. export function getDisjointTimerQuery(gl: GLRenderingContext): COMPAT_disjoint_timer_query | null {
  319. if (isWebGL2(gl)) {
  320. // Firefox has EXT_disjoint_timer_query in webgl2
  321. const ext = gl.getExtension('EXT_disjoint_timer_query_webgl2') || gl.getExtension('EXT_disjoint_timer_query');
  322. if (ext === null) return null;
  323. return {
  324. QUERY_COUNTER_BITS: ext.QUERY_COUNTER_BITS_EXT,
  325. CURRENT_QUERY: gl.CURRENT_QUERY,
  326. QUERY_RESULT: gl.QUERY_RESULT,
  327. QUERY_RESULT_AVAILABLE: gl.QUERY_RESULT_AVAILABLE,
  328. TIME_ELAPSED: ext.TIME_ELAPSED_EXT,
  329. TIMESTAMP: ext.TIMESTAMP_EXT,
  330. GPU_DISJOINT: ext.GPU_DISJOINT_EXT,
  331. createQuery: gl.createQuery.bind(gl),
  332. deleteQuery: gl.deleteQuery.bind(gl),
  333. isQuery: gl.isQuery.bind(gl),
  334. beginQuery: gl.beginQuery.bind(gl),
  335. endQuery: gl.endQuery.bind(gl),
  336. queryCounter: ext.queryCounterEXT.bind(ext),
  337. getQuery: gl.getQuery.bind(gl),
  338. getQueryParameter: gl.getQueryParameter.bind(gl),
  339. };
  340. } else {
  341. const ext = gl.getExtension('EXT_disjoint_timer_query');
  342. if (ext === null) return null;
  343. return {
  344. QUERY_COUNTER_BITS: ext.QUERY_COUNTER_BITS_EXT,
  345. CURRENT_QUERY: ext.CURRENT_QUERY_EXT,
  346. QUERY_RESULT: ext.QUERY_RESULT_EXT,
  347. QUERY_RESULT_AVAILABLE: ext.QUERY_RESULT_AVAILABLE_EXT,
  348. TIME_ELAPSED: ext.TIME_ELAPSED_EXT,
  349. TIMESTAMP: ext.TIMESTAMP_EXT,
  350. GPU_DISJOINT: ext.GPU_DISJOINT_EXT,
  351. createQuery: ext.createQueryEXT.bind(ext),
  352. deleteQuery: ext.deleteQueryEXT.bind(ext),
  353. isQuery: ext.isQueryEXT.bind(ext),
  354. beginQuery: ext.beginQueryEXT.bind(ext),
  355. endQuery: ext.endQueryEXT.bind(ext),
  356. queryCounter: ext.queryCounterEXT.bind(ext),
  357. getQuery: ext.getQueryEXT.bind(ext),
  358. getQueryParameter: ext.getQueryObjectEXT.bind(ext),
  359. };
  360. }
  361. }
  362. //
  363. const TextureTestVertShader = `
  364. attribute vec4 aPosition;
  365. void main() {
  366. gl_Position = aPosition;
  367. }`;
  368. const TextureTestFragShader = `
  369. precision mediump float;
  370. uniform vec4 uColor;
  371. uniform sampler2D uTexture;
  372. void main() {
  373. gl_FragColor = texture2D(uTexture, vec2(0.5, 0.5)) * uColor;
  374. }`;
  375. const TextureTestTexCoords = new Float32Array([
  376. -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0
  377. ]);
  378. // adapted from https://stackoverflow.com/questions/28827511/
  379. export function testColorBuffer(gl: GLRenderingContext, type: number) {
  380. // setup shaders
  381. const vertShader = getShader(gl, { type: 'vert', source: TextureTestVertShader });
  382. const fragShader = getShader(gl, { type: 'frag', source: TextureTestFragShader });
  383. if (!vertShader || !fragShader) return false;
  384. // setup program
  385. const program = getProgram(gl);
  386. gl.attachShader(program, vertShader);
  387. gl.attachShader(program, fragShader);
  388. gl.linkProgram(program);
  389. gl.useProgram(program);
  390. // look up where the vertex data needs to go.
  391. const positionLocation = gl.getAttribLocation(program, 'aPosition');
  392. const colorLoc = gl.getUniformLocation(program, 'uColor');
  393. if (!colorLoc) {
  394. if (isDebugMode) {
  395. console.log(`error getting 'uColor' uniform location`);
  396. }
  397. return false;
  398. }
  399. // provide texture coordinates for the rectangle.
  400. const positionBuffer = gl.createBuffer();
  401. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  402. gl.bufferData(gl.ARRAY_BUFFER, TextureTestTexCoords, gl.STATIC_DRAW);
  403. gl.enableVertexAttribArray(positionLocation);
  404. gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  405. const whiteTex = gl.createTexture();
  406. const whiteData = new Uint8Array([255, 255, 255, 255]);
  407. gl.bindTexture(gl.TEXTURE_2D, whiteTex);
  408. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, whiteData);
  409. const tex = gl.createTexture();
  410. gl.bindTexture(gl.TEXTURE_2D, tex);
  411. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, type, null);
  412. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  413. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  414. const fb = gl.createFramebuffer();
  415. gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  416. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
  417. const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  418. if (status !== gl.FRAMEBUFFER_COMPLETE) {
  419. if (isDebugMode) {
  420. console.log(`error creating framebuffer for '${type}'`);
  421. }
  422. return false;
  423. }
  424. // Draw the rectangle.
  425. gl.bindTexture(gl.TEXTURE_2D, whiteTex);
  426. gl.uniform4fv(colorLoc, [0, 10, 20, 1]);
  427. gl.drawArrays(gl.TRIANGLES, 0, 6);
  428. gl.bindTexture(gl.TEXTURE_2D, tex);
  429. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  430. gl.clearColor(1, 0, 0, 1);
  431. gl.clear(gl.COLOR_BUFFER_BIT);
  432. gl.uniform4fv(colorLoc, [0, 1 / 10, 1 / 20, 1]);
  433. gl.drawArrays(gl.TRIANGLES, 0, 6);
  434. // Check if rendered correctly
  435. const pixel = new Uint8Array(4);
  436. gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  437. if (pixel[0] !== 0 || pixel[1] < 248 || pixel[2] < 248 || pixel[3] < 254) {
  438. if (isDebugMode) {
  439. console.log(`not able to actually render to '${type}' texture`);
  440. }
  441. return false;
  442. }
  443. // Check reading from float texture
  444. if (type === gl.FLOAT) {
  445. gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  446. const floatPixel = new Float32Array(4);
  447. gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, floatPixel);
  448. const error = gl.getError();
  449. if (error) {
  450. if (isDebugMode) {
  451. console.log(`error reading float pixels: '${getErrorDescription(gl, error)}'`);
  452. }
  453. return false;
  454. }
  455. }
  456. return true;
  457. }