Jelajahi Sumber

error and debugging related improvements to gl lib

Alexander Rose 6 tahun lalu
induk
melakukan
6066de6d52

+ 7 - 6
src/mol-gl/shader-code.ts

@@ -125,12 +125,7 @@ const glsl300VertPrefix = `#version 300 es
 `
 
 const glsl300FragPrefix = `#version 300 es
-#define varying in
-layout(location = 0) out highp vec4 out_FragColor;
-#define gl_FragColor out_FragColor
-#define gl_FragDepthEXT gl_FragDepth
-#define texture2D texture
-
+layout(location = 0) out highp vec4 out_FragData0;
 layout(location = 1) out highp vec4 out_FragData1;
 layout(location = 2) out highp vec4 out_FragData2;
 layout(location = 3) out highp vec4 out_FragData3;
@@ -139,6 +134,12 @@ layout(location = 5) out highp vec4 out_FragData5;
 layout(location = 6) out highp vec4 out_FragData6;
 layout(location = 7) out highp vec4 out_FragData7;
 
+#define varying in
+#define texture2D texture
+
+#define gl_FragColor out_FragData0
+#define gl_FragDepthEXT gl_FragDepth
+
 #define enabledStandardDerivatives
 #define enabledFragDepth
 #define requiredDrawBuffers

+ 8 - 9
src/mol-gl/webgl/context.ts

@@ -7,7 +7,7 @@
 import { createProgramCache, ProgramCache } from './program'
 import { createShaderCache, ShaderCache } from './shader'
 import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers } from './compat';
-import { createFramebufferCache, FramebufferCache } from './framebuffer';
+import { createFramebufferCache, FramebufferCache, checkFramebufferStatus } from './framebuffer';
 import { Scheduler } from 'mol-task';
 import { isProductionMode } from 'mol-util/debug';
 
@@ -39,6 +39,11 @@ function getErrorDescription(gl: GLRenderingContext, error: number) {
     return 'unknown error'
 }
 
+export function checkError(gl: GLRenderingContext) {
+    const error = gl.getError()
+    if (error) throw new Error(`WebGL error: '${getErrorDescription(gl, error)}'`)
+}
+
 function unbindResources (gl: GLRenderingContext) {
     // bind null to all texture units
     const maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
@@ -112,19 +117,13 @@ function waitForGpuCommandsCompleteSync(gl: GLRenderingContext): void {
 }
 
 function readPixels(gl: GLRenderingContext, x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) {
-    if (!isProductionMode && gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
-        console.error('Reading pixels failed. Framebuffer not complete.')
-        return
-    }
+    if (!isProductionMode) checkFramebufferStatus(gl)
     if (buffer instanceof Uint8Array) {
         gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
     } else {
         gl.readPixels(x, y, width, height, gl.RGBA, gl.FLOAT, buffer)
     }
-    if (!isProductionMode) {
-        const error = gl.getError()
-        if (error) console.log(`Error reading pixels: '${getErrorDescription(gl, error)}'`)
-    }
+    if (!isProductionMode) checkError(gl)
 }
 
 export function createImageData(buffer: ArrayLike<number>, width: number, height: number) {

+ 26 - 1
src/mol-gl/webgl/framebuffer.ts

@@ -7,10 +7,35 @@
 import { WebGLStats } from './context'
 import { idFactory } from 'mol-util/id-factory';
 import { ReferenceCache, createReferenceCache } from 'mol-util/reference-cache';
-import { GLRenderingContext } from './compat';
+import { GLRenderingContext, isWebGL2 } from './compat';
 
 const getNextFramebufferId = idFactory()
 
+function getFramebufferStatusDescription(gl: GLRenderingContext, status: number) {
+    switch (status) {
+        case gl.FRAMEBUFFER_COMPLETE: return 'complete'
+        case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return 'incomplete attachment'
+        case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return 'incomplete missing attachment'
+        case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return 'incomplete dimensions'
+        case gl.FRAMEBUFFER_UNSUPPORTED: return 'unsupported'
+    }
+    if (isWebGL2(gl)) {
+        switch (status) {
+            case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return 'incomplete multisample'
+            case gl.RENDERBUFFER_SAMPLES: return 'renderbuffer samples'
+        }
+    }
+    return 'unknown error'
+}
+
+export function checkFramebufferStatus(gl: GLRenderingContext) {
+    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)
+    if (status !== gl.FRAMEBUFFER_COMPLETE) {
+        const description = getFramebufferStatusDescription(gl, status)
+        throw new Error(`Framebuffer status: ${description}`)
+    }
+}
+
 export interface Framebuffer {
     readonly id: number
 

+ 6 - 2
src/mol-gl/webgl/program.ts

@@ -134,8 +134,12 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi
     vertShaderRef.value.attach(program)
     fragShaderRef.value.attach(program)
     gl.linkProgram(program)
-    if (!isProductionMode && !gl.getProgramParameter(program, gl.LINK_STATUS)) {
-        throw new Error(`Could not compile WebGL program. \n\n${gl.getProgramInfoLog(program)}`);
+    if (!isProductionMode) {
+        // no-op in FF on Mac, see https://bugzilla.mozilla.org/show_bug.cgi?id=1284425
+        // gl.validateProgram(program)
+        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
+            throw new Error(`Could not compile WebGL program. \n\n${gl.getProgramInfoLog(program)}`);
+        }
     }
 
     const locations = getLocations(gl, program, schema)

+ 16 - 1
src/mol-gl/webgl/render-item.ts

@@ -6,7 +6,7 @@
 
 import { createAttributeBuffers, createElementsBuffer, ElementsBuffer, createAttributeBuffer, AttributeKind } from './buffer';
 import { createTextures, Texture } from './texture';
-import { WebGLContext } from './context';
+import { WebGLContext, checkError } from './context';
 import { ShaderCode } from '../shader-code';
 import { Program } from './program';
 import { RenderableSchema, RenderableValues, AttributeSpec, getValueVersions, splitValues, Values } from '../renderable/schema';
@@ -15,6 +15,8 @@ import { deleteVertexArray, createVertexArray } from './vertex-array';
 import { ValueCell } from 'mol-util';
 import { ReferenceItem } from 'mol-util/reference-cache';
 import { TextureImage, TextureVolume } from 'mol-gl/renderable/util';
+import { checkFramebufferStatus } from './framebuffer';
+import { isProductionMode } from 'mol-util/debug';
 
 const getNextRenderItemId = idFactory()
 
@@ -181,11 +183,24 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
                 if (elementsBuffer) elementsBuffer.bind()
                 program.bindAttributes(attributeBuffers)
             }
+            if (!isProductionMode) {
+                checkFramebufferStatus(ctx.gl)
+            }
             if (elementsBuffer) {
                 instancedArrays.drawElementsInstanced(glDrawMode, drawCount, elementsBuffer._dataType, 0, instanceCount);
             } else {
                 instancedArrays.drawArraysInstanced(glDrawMode, 0, drawCount, instanceCount)
             }
+            if (!isProductionMode) {
+                try {
+                    checkError(ctx.gl)
+                } catch (e) {
+                    // console.log('shaderCode', shaderCode)
+                    // console.log('schema', schema)
+                    // console.log('attributeBuffers', attributeBuffers)
+                    throw new Error(`Error rendering item id ${id}: '${e}'`)
+                }
+            }
         },
         update: () => {
             resetValueChanges(valueChanges)