diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 8059b2d429a9fec697fcc0048a56f4059b195523..a7e3386a3d550f730c661043dc4759383d28340c 100644 --- a/src/mol-gl/shader-code.ts +++ b/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 diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index e09a5b9b1cc8fc2dbc78b6083c2ca43b24688db6..5d996889b561e08995cd7eceaa853076e1131281 100644 --- a/src/mol-gl/webgl/context.ts +++ b/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) { diff --git a/src/mol-gl/webgl/framebuffer.ts b/src/mol-gl/webgl/framebuffer.ts index 1a7475a7997dd3bf9ddb136b59e891d09ba919d6..997c7412e50ec1d42822a9236507110ceee725c3 100644 --- a/src/mol-gl/webgl/framebuffer.ts +++ b/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 diff --git a/src/mol-gl/webgl/program.ts b/src/mol-gl/webgl/program.ts index 44adc2bfc66bfa13cfeb0c110844f5570743fa4a..5c0faeb017e8b87e839d6a47fec01e1e5c93e8b8 100644 --- a/src/mol-gl/webgl/program.ts +++ b/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) diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 763a6e67010172af382acd2df976a31facf06f1d..299fe2d33a237ef97aef0baf032bc1758f65ff4d 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/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)