123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { isDebugMode } from '../../mol-util/debug';
- import { getErrorDescription } from './context';
- import { getProgram } from './program';
- import { getShader } from './shader';
- export type GLRenderingContext = WebGLRenderingContext | WebGL2RenderingContext
- export function isWebGL(gl: any): gl is WebGLRenderingContext {
- return typeof WebGLRenderingContext !== 'undefined' && gl instanceof WebGLRenderingContext;
- }
- export function isWebGL2(gl: any): gl is WebGL2RenderingContext {
- return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
- }
- export interface COMPAT_instanced_arrays {
- drawArraysInstanced(mode: number, first: number, count: number, primcount: number): void;
- drawElementsInstanced(mode: number, count: number, type: number, offset: number, primcount: number): void;
- vertexAttribDivisor(index: number, divisor: number): void;
- readonly VERTEX_ATTRIB_ARRAY_DIVISOR: number;
- }
- export function getInstancedArrays(gl: GLRenderingContext): COMPAT_instanced_arrays | null {
- if (isWebGL2(gl)) {
- return {
- drawArraysInstanced: gl.drawArraysInstanced.bind(gl),
- drawElementsInstanced: gl.drawElementsInstanced.bind(gl),
- vertexAttribDivisor: gl.vertexAttribDivisor.bind(gl),
- VERTEX_ATTRIB_ARRAY_DIVISOR: gl.VERTEX_ATTRIB_ARRAY_DIVISOR
- };
- } else {
- const ext = gl.getExtension('ANGLE_instanced_arrays');
- if (ext === null) return null;
- return {
- drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
- drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
- vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),
- VERTEX_ATTRIB_ARRAY_DIVISOR: ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
- };
- }
- }
- export interface COMPAT_standard_derivatives {
- readonly FRAGMENT_SHADER_DERIVATIVE_HINT: number;
- }
- export function getStandardDerivatives(gl: GLRenderingContext): COMPAT_standard_derivatives | null {
- if (isWebGL2(gl)) {
- return { FRAGMENT_SHADER_DERIVATIVE_HINT: gl.FRAGMENT_SHADER_DERIVATIVE_HINT };
- } else {
- const ext = gl.getExtension('OES_standard_derivatives');
- if (ext === null) return null;
- return { FRAGMENT_SHADER_DERIVATIVE_HINT: ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES };
- }
- }
- export interface COMPAT_element_index_uint {
- }
- export function getElementIndexUint(gl: GLRenderingContext): COMPAT_element_index_uint | null {
- return isWebGL2(gl) ? {} : gl.getExtension('OES_element_index_uint');
- }
- export interface COMPAT_vertex_array_object {
- readonly VERTEX_ARRAY_BINDING: number;
- bindVertexArray(arrayObject: WebGLVertexArrayObject | null): void;
- createVertexArray(): WebGLVertexArrayObject | null;
- deleteVertexArray(arrayObject: WebGLVertexArrayObject): void;
- isVertexArray(value: any): value is WebGLVertexArrayObject;
- }
- export function getVertexArrayObject(gl: GLRenderingContext): COMPAT_vertex_array_object | null {
- if (isWebGL2(gl)) {
- return {
- VERTEX_ARRAY_BINDING: gl.VERTEX_ARRAY_BINDING,
- bindVertexArray: gl.bindVertexArray.bind(gl),
- createVertexArray: gl.createVertexArray.bind(gl),
- deleteVertexArray: gl.deleteVertexArray.bind(gl),
- isVertexArray: gl.isVertexArray.bind(gl)
- };
- } else {
- const ext = gl.getExtension('OES_vertex_array_object');
- if (ext === null) return null;
- return {
- VERTEX_ARRAY_BINDING: ext.VERTEX_ARRAY_BINDING_OES,
- bindVertexArray: ext.bindVertexArrayOES.bind(ext),
- createVertexArray: ext.createVertexArrayOES.bind(ext),
- deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),
- isVertexArray: ext.isVertexArrayOES.bind(ext)
- };
- }
- }
- export interface COMPAT_texture_float {
- }
- export function getTextureFloat(gl: GLRenderingContext): COMPAT_texture_float | null {
- return isWebGL2(gl) ? {} : gl.getExtension('OES_texture_float');
- }
- export interface COMPAT_texture_float_linear {
- }
- export function getTextureFloatLinear(gl: GLRenderingContext): COMPAT_texture_float_linear | null {
- return gl.getExtension('OES_texture_float_linear');
- }
- export interface COMPAT_texture_half_float {
- readonly HALF_FLOAT: number
- }
- export function getTextureHalfFloat(gl: GLRenderingContext): COMPAT_texture_half_float | null {
- if (isWebGL2(gl)) {
- return { HALF_FLOAT: gl.HALF_FLOAT };
- } else {
- const ext = gl.getExtension('OES_texture_half_float');
- if (ext === null) return null;
- return { HALF_FLOAT: ext.HALF_FLOAT_OES };
- }
- }
- export interface COMPAT_texture_half_float_linear {
- }
- export function getTextureHalfFloatLinear(gl: GLRenderingContext): COMPAT_texture_half_float_linear | null {
- return gl.getExtension('OES_texture_half_float_linear');
- }
- export interface COMPAT_blend_minmax {
- readonly MIN: number
- readonly MAX: number
- }
- export function getBlendMinMax(gl: GLRenderingContext): COMPAT_blend_minmax | null {
- if (isWebGL2(gl)) {
- return { MIN: gl.MIN, MAX: gl.MAX };
- } else {
- const ext = gl.getExtension('EXT_blend_minmax');
- if (ext === null) return null;
- return { MIN: ext.MIN_EXT, MAX: ext.MAX_EXT };
- }
- }
- export interface COMPAT_frag_depth {
- }
- export function getFragDepth(gl: GLRenderingContext): COMPAT_frag_depth | null {
- return isWebGL2(gl) ? {} : gl.getExtension('EXT_frag_depth');
- }
- export interface COMPAT_color_buffer_float {
- readonly RGBA32F: number;
- }
- export function getColorBufferFloat(gl: GLRenderingContext): COMPAT_color_buffer_float | null {
- if (isWebGL2(gl)) {
- if (gl.getExtension('EXT_color_buffer_float') === null) return null;
- gl.getExtension('EXT_float_blend');
- return { RGBA32F: gl.RGBA32F };
- } else {
- const ext = gl.getExtension('WEBGL_color_buffer_float');
- if (ext === null) {
- // test as support may not be advertised by browsers
- gl.getExtension('OES_texture_float');
- return testColorBuffer(gl, gl.FLOAT) ? { RGBA32F: 0x8814 } : null;
- }
- gl.getExtension('EXT_float_blend');
- return { RGBA32F: ext.RGBA32F_EXT };
- }
- }
- export interface COMPAT_color_buffer_half_float {
- readonly RGBA16F: number;
- }
- export function getColorBufferHalfFloat(gl: GLRenderingContext): COMPAT_color_buffer_half_float | null {
- if (isWebGL2(gl)) {
- if (gl.getExtension('EXT_color_buffer_half_float') === null) return null;
- gl.getExtension('EXT_float_blend');
- return { RGBA16F: gl.RGBA16F };
- } else {
- const ext = gl.getExtension('EXT_color_buffer_half_float');
- if (ext === null) {
- // test as support may not be advertised by browsers
- gl.getExtension('OES_texture_half_float');
- return testColorBuffer(gl, 0x8D61) ? { RGBA16F: 0x881A } : null;
- }
- gl.getExtension('EXT_float_blend');
- return { RGBA16F: ext.RGBA16F_EXT };
- }
- }
- export interface COMPAT_draw_buffers {
- drawBuffers(buffers: number[]): void;
- readonly COLOR_ATTACHMENT0: number;
- readonly COLOR_ATTACHMENT1: number;
- readonly COLOR_ATTACHMENT2: number;
- readonly COLOR_ATTACHMENT3: number;
- readonly COLOR_ATTACHMENT4: number;
- readonly COLOR_ATTACHMENT5: number;
- readonly COLOR_ATTACHMENT6: number;
- readonly COLOR_ATTACHMENT7: number;
- readonly DRAW_BUFFER0: number;
- readonly DRAW_BUFFER1: number;
- readonly DRAW_BUFFER2: number;
- readonly DRAW_BUFFER3: number;
- readonly DRAW_BUFFER4: number;
- readonly DRAW_BUFFER5: number;
- readonly DRAW_BUFFER6: number;
- readonly DRAW_BUFFER7: number;
- readonly MAX_COLOR_ATTACHMENTS: number;
- readonly MAX_DRAW_BUFFERS: number;
- }
- export function getDrawBuffers(gl: GLRenderingContext): COMPAT_draw_buffers | null {
- if (isWebGL2(gl)) {
- return {
- drawBuffers: gl.drawBuffers.bind(gl),
- COLOR_ATTACHMENT0: gl.COLOR_ATTACHMENT0,
- COLOR_ATTACHMENT1: gl.COLOR_ATTACHMENT1,
- COLOR_ATTACHMENT2: gl.COLOR_ATTACHMENT2,
- COLOR_ATTACHMENT3: gl.COLOR_ATTACHMENT3,
- COLOR_ATTACHMENT4: gl.COLOR_ATTACHMENT4,
- COLOR_ATTACHMENT5: gl.COLOR_ATTACHMENT5,
- COLOR_ATTACHMENT6: gl.COLOR_ATTACHMENT6,
- COLOR_ATTACHMENT7: gl.COLOR_ATTACHMENT7,
- DRAW_BUFFER0: gl.DRAW_BUFFER0,
- DRAW_BUFFER1: gl.DRAW_BUFFER1,
- DRAW_BUFFER2: gl.DRAW_BUFFER2,
- DRAW_BUFFER3: gl.DRAW_BUFFER3,
- DRAW_BUFFER4: gl.DRAW_BUFFER4,
- DRAW_BUFFER5: gl.DRAW_BUFFER5,
- DRAW_BUFFER6: gl.DRAW_BUFFER6,
- DRAW_BUFFER7: gl.DRAW_BUFFER7,
- MAX_COLOR_ATTACHMENTS: gl.MAX_COLOR_ATTACHMENTS,
- MAX_DRAW_BUFFERS: gl.MAX_DRAW_BUFFERS,
- };
- } else {
- const ext = gl.getExtension('WEBGL_draw_buffers');
- if (ext === null) return null;
- return {
- drawBuffers: ext.drawBuffersWEBGL.bind(ext),
- COLOR_ATTACHMENT0: ext.COLOR_ATTACHMENT0_WEBGL,
- COLOR_ATTACHMENT1: ext.COLOR_ATTACHMENT1_WEBGL,
- COLOR_ATTACHMENT2: ext.COLOR_ATTACHMENT2_WEBGL,
- COLOR_ATTACHMENT3: ext.COLOR_ATTACHMENT3_WEBGL,
- COLOR_ATTACHMENT4: ext.COLOR_ATTACHMENT4_WEBGL,
- COLOR_ATTACHMENT5: ext.COLOR_ATTACHMENT5_WEBGL,
- COLOR_ATTACHMENT6: ext.COLOR_ATTACHMENT6_WEBGL,
- COLOR_ATTACHMENT7: ext.COLOR_ATTACHMENT7_WEBGL,
- DRAW_BUFFER0: ext.DRAW_BUFFER0_WEBGL,
- DRAW_BUFFER1: ext.DRAW_BUFFER1_WEBGL,
- DRAW_BUFFER2: ext.DRAW_BUFFER2_WEBGL,
- DRAW_BUFFER3: ext.DRAW_BUFFER3_WEBGL,
- DRAW_BUFFER4: ext.DRAW_BUFFER4_WEBGL,
- DRAW_BUFFER5: ext.DRAW_BUFFER5_WEBGL,
- DRAW_BUFFER6: ext.DRAW_BUFFER6_WEBGL,
- DRAW_BUFFER7: ext.DRAW_BUFFER7_WEBGL,
- MAX_COLOR_ATTACHMENTS: ext.MAX_COLOR_ATTACHMENTS_WEBGL,
- MAX_DRAW_BUFFERS: ext.MAX_DRAW_BUFFERS_WEBGL,
- };
- }
- }
- export interface COMPAT_shader_texture_lod {
- }
- export function getShaderTextureLod(gl: GLRenderingContext): COMPAT_shader_texture_lod | null {
- return isWebGL2(gl) ? {} : gl.getExtension('EXT_shader_texture_lod');
- }
- export interface COMPAT_depth_texture {
- readonly UNSIGNED_INT_24_8: number;
- }
- export function getDepthTexture(gl: GLRenderingContext): COMPAT_depth_texture | null {
- if (isWebGL2(gl)) {
- return {
- UNSIGNED_INT_24_8: gl.UNSIGNED_INT_24_8
- };
- } else {
- const ext = gl.getExtension('WEBGL_depth_texture');
- if (ext === null) return null;
- return {
- UNSIGNED_INT_24_8: ext.UNSIGNED_INT_24_8_WEBGL
- };
- }
- }
- export interface COMPAT_sRGB {
- readonly FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: number;
- readonly SRGB8_ALPHA8: number;
- readonly SRGB8: number;
- readonly SRGB: number;
- }
- export function getSRGB(gl: GLRenderingContext): COMPAT_sRGB | null {
- if (isWebGL2(gl)) {
- return {
- FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
- SRGB8_ALPHA8: gl.SRGB8_ALPHA8,
- SRGB8: gl.SRGB8,
- SRGB: gl.SRGB
- };
- } else {
- const ext = gl.getExtension('EXT_sRGB');
- if (ext === null) return null;
- return {
- FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
- SRGB8_ALPHA8: ext.SRGB8_ALPHA8_EXT,
- SRGB8: ext.SRGB_ALPHA_EXT,
- SRGB: ext.SRGB_EXT
- };
- }
- }
- export interface COMPAT_disjoint_timer_query {
- /** A GLint indicating the number of bits used to hold the query result for the given target. */
- QUERY_COUNTER_BITS: number
- /** A WebGLQuery object, which is the currently active query for the given target. */
- CURRENT_QUERY: number
- /** A GLuint64EXT containing the query result. */
- QUERY_RESULT: number
- /** A GLboolean indicating whether or not a query result is available. */
- QUERY_RESULT_AVAILABLE: number
- /** Elapsed time (in nanoseconds). */
- TIME_ELAPSED: number
- /** The current time. */
- TIMESTAMP: number
- /** A GLboolean indicating whether or not the GPU performed any disjoint operation. */
- GPU_DISJOINT: number
- /** Creates a new WebGLTimerQueryEXT. */
- createQuery: () => WebGLQuery
- /** Deletes a given WebGLTimerQueryEXT. */
- deleteQuery: (query: WebGLQuery) => void
- /** Returns true if a given object is a valid WebGLTimerQueryEXT. */
- isQuery: (query: WebGLQuery) => boolean
- /** The timer starts when all commands prior to beginQueryEXT have been fully executed. */
- beginQuery: (target: number, query: WebGLQuery) => void
- /** The timer stops when all commands prior to endQueryEXT have been fully executed. */
- endQuery: (target: number) => void
- /** Records the current time into the corresponding query object. */
- queryCounter: (query: WebGLQuery, target: number) => void
- /** Returns information about a query target. */
- getQuery: (target: number, pname: number) => WebGLQuery | number
- /** Return the state of a query object. */
- getQueryParameter: (query: WebGLQuery, pname: number) => number | boolean
- }
- export function getDisjointTimerQuery(gl: GLRenderingContext): COMPAT_disjoint_timer_query | null {
- if (isWebGL2(gl)) {
- // Firefox has EXT_disjoint_timer_query in webgl2
- const ext = gl.getExtension('EXT_disjoint_timer_query_webgl2') || gl.getExtension('EXT_disjoint_timer_query');
- if (ext === null) return null;
- return {
- QUERY_COUNTER_BITS: ext.QUERY_COUNTER_BITS_EXT,
- CURRENT_QUERY: gl.CURRENT_QUERY,
- QUERY_RESULT: gl.QUERY_RESULT,
- QUERY_RESULT_AVAILABLE: gl.QUERY_RESULT_AVAILABLE,
- TIME_ELAPSED: ext.TIME_ELAPSED_EXT,
- TIMESTAMP: ext.TIMESTAMP_EXT,
- GPU_DISJOINT: ext.GPU_DISJOINT_EXT,
- createQuery: gl.createQuery.bind(gl),
- deleteQuery: gl.deleteQuery.bind(gl),
- isQuery: gl.isQuery.bind(gl),
- beginQuery: gl.beginQuery.bind(gl),
- endQuery: gl.endQuery.bind(gl),
- queryCounter: ext.queryCounterEXT.bind(ext),
- getQuery: gl.getQuery.bind(gl),
- getQueryParameter: gl.getQueryParameter.bind(gl),
- };
- } else {
- const ext = gl.getExtension('EXT_disjoint_timer_query');
- if (ext === null) return null;
- return {
- QUERY_COUNTER_BITS: ext.QUERY_COUNTER_BITS_EXT,
- CURRENT_QUERY: ext.CURRENT_QUERY_EXT,
- QUERY_RESULT: ext.QUERY_RESULT_EXT,
- QUERY_RESULT_AVAILABLE: ext.QUERY_RESULT_AVAILABLE_EXT,
- TIME_ELAPSED: ext.TIME_ELAPSED_EXT,
- TIMESTAMP: ext.TIMESTAMP_EXT,
- GPU_DISJOINT: ext.GPU_DISJOINT_EXT,
- createQuery: ext.createQueryEXT.bind(ext),
- deleteQuery: ext.deleteQueryEXT.bind(ext),
- isQuery: ext.isQueryEXT.bind(ext),
- beginQuery: ext.beginQueryEXT.bind(ext),
- endQuery: ext.endQueryEXT.bind(ext),
- queryCounter: ext.queryCounterEXT.bind(ext),
- getQuery: ext.getQueryEXT.bind(ext),
- getQueryParameter: ext.getQueryObjectEXT.bind(ext),
- };
- }
- }
- //
- const TextureTestVertShader = `
- attribute vec4 aPosition;
- void main() {
- gl_Position = aPosition;
- }`;
- const TextureTestFragShader = `
- precision mediump float;
- uniform vec4 uColor;
- uniform sampler2D uTexture;
- void main() {
- gl_FragColor = texture2D(uTexture, vec2(0.5, 0.5)) * uColor;
- }`;
- const TextureTestTexCoords = new Float32Array([
- -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0
- ]);
- // adapted from https://stackoverflow.com/questions/28827511/
- export function testColorBuffer(gl: GLRenderingContext, type: number) {
- // setup shaders
- const vertShader = getShader(gl, { type: 'vert', source: TextureTestVertShader });
- const fragShader = getShader(gl, { type: 'frag', source: TextureTestFragShader });
- if (!vertShader || !fragShader) return false;
- // setup program
- const program = getProgram(gl);
- gl.attachShader(program, vertShader);
- gl.attachShader(program, fragShader);
- gl.linkProgram(program);
- gl.useProgram(program);
- // look up where the vertex data needs to go.
- const positionLocation = gl.getAttribLocation(program, 'aPosition');
- const colorLoc = gl.getUniformLocation(program, 'uColor');
- if (!colorLoc) {
- if (isDebugMode) {
- console.log(`error getting 'uColor' uniform location`);
- }
- return false;
- }
- // provide texture coordinates for the rectangle.
- const positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, TextureTestTexCoords, gl.STATIC_DRAW);
- gl.enableVertexAttribArray(positionLocation);
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
- const whiteTex = gl.createTexture();
- const whiteData = new Uint8Array([255, 255, 255, 255]);
- gl.bindTexture(gl.TEXTURE_2D, whiteTex);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, whiteData);
- const tex = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, tex);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, type, null);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- const fb = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
- const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
- if (status !== gl.FRAMEBUFFER_COMPLETE) {
- if (isDebugMode) {
- console.log(`error creating framebuffer for '${type}'`);
- }
- return false;
- }
- // Draw the rectangle.
- gl.bindTexture(gl.TEXTURE_2D, whiteTex);
- gl.uniform4fv(colorLoc, [0, 10, 20, 1]);
- gl.drawArrays(gl.TRIANGLES, 0, 6);
- gl.bindTexture(gl.TEXTURE_2D, tex);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- gl.clearColor(1, 0, 0, 1);
- gl.clear(gl.COLOR_BUFFER_BIT);
- gl.uniform4fv(colorLoc, [0, 1 / 10, 1 / 20, 1]);
- gl.drawArrays(gl.TRIANGLES, 0, 6);
- // Check if rendered correctly
- const pixel = new Uint8Array(4);
- gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
- if (pixel[0] !== 0 || pixel[1] < 248 || pixel[2] < 248 || pixel[3] < 254) {
- if (isDebugMode) {
- console.log(`not able to actually render to '${type}' texture`);
- }
- return false;
- }
- // Check reading from float texture
- if (type === gl.FLOAT) {
- gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
- const floatPixel = new Float32Array(4);
- gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, floatPixel);
- const error = gl.getError();
- if (error) {
- if (isDebugMode) {
- console.log(`error reading float pixels: '${getErrorDescription(gl, error)}'`);
- }
- return false;
- }
- }
- return true;
- }
|