diff --git a/CHANGELOG.md b/CHANGELOG.md index effc3920e6e15b2ac9b10c10ebb1c666dddd6917..7d1789112f3e58c4638e5ecb2590acbaeecdc6a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Note that since we don't clearly distinguish between a public and private interf - Don't filter IndexPairBonds by element-based rules in MOL/SDF and MOL2 (without symmetry) models - Fix Glycam Saccharide Names used by default - Prefer WebGL1 for more Safari versions to avoid broken GPU surfaces rendering +- Reduce number of webgl state changes + - Add ``viewport`` and ``scissor`` to state object + - Add ``hasOpaque`` to scene object - Handle edge cases where some renderables would not get (correctly) rendered - Fix text background rendering for opaque text - Fix helper scenes not shown when rendering directly to draw target diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 9d907ce7790d0f19ae127c2e3657b0a8c228e371..d00f4e28252df88600043ddfcb94cbda1588401c 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -120,14 +120,13 @@ export class DrawPass { private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) { if (!this.wboit?.supported) throw new Error('expected wboit to be supported'); - this.colorTarget.bind(); + this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); renderer.clear(true); // render opaque primitives - this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); - this.colorTarget.bind(); - renderer.clearDepth(); - renderer.renderWboitOpaque(scene.primitives, camera, null); + if (scene.hasOpaque) { + renderer.renderWboitOpaque(scene.primitives, camera, null); + } if (PostprocessingPass.isEnabled(postprocessingProps)) { if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { @@ -165,14 +164,17 @@ export class DrawPass { if (toDrawingBuffer) { this.drawTarget.bind(); } else { - this.colorTarget.bind(); if (!this.packedDepth) { this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); + } else { + this.colorTarget.bind(); } } renderer.clear(true); - renderer.renderBlendedOpaque(scene.primitives, camera, null); + if (scene.hasOpaque) { + renderer.renderBlendedOpaque(scene.primitives, camera, null); + } if (!toDrawingBuffer) { // do a depth pass if not rendering to drawing buffer and diff --git a/src/mol-canvas3d/passes/fxaa.ts b/src/mol-canvas3d/passes/fxaa.ts index ff1a0e878775ca6a898c0983382e10ac834c9091..bbb02430284d72c25be9f93b76bf863970e018ee 100644 --- a/src/mol-canvas3d/passes/fxaa.ts +++ b/src/mol-canvas3d/passes/fxaa.ts @@ -44,8 +44,8 @@ export class FxaaPass { state.depthMask(false); const { x, y, width, height } = viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); state.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); diff --git a/src/mol-canvas3d/passes/marking.ts b/src/mol-canvas3d/passes/marking.ts index 73cde519fa611d2160c7a2c1788c5120146cf1e4..2093b5f2d1e2d0f818fbdd6519c2021f7654218d 100644 --- a/src/mol-canvas3d/passes/marking.ts +++ b/src/mol-canvas3d/passes/marking.ts @@ -64,8 +64,8 @@ export class MarkingPass { state.depthMask(false); const { x, y, width, height } = viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); state.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -82,8 +82,8 @@ export class MarkingPass { state.depthMask(false); const { x, y, width, height } = viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); } setSize(width: number, height: number) { diff --git a/src/mol-canvas3d/passes/multi-sample.ts b/src/mol-canvas3d/passes/multi-sample.ts index 82c861372d1c7677531d3861298695e764014da4..2137592b6ea329ea5d705e5cf13d3d162b14eecc 100644 --- a/src/mol-canvas3d/passes/multi-sample.ts +++ b/src/mol-canvas3d/passes/multi-sample.ts @@ -176,8 +176,8 @@ export class MultiSamplePass { state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); state.disable(gl.DEPTH_TEST); state.depthMask(false); - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); if (i === 0) { state.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -192,8 +192,8 @@ export class MultiSamplePass { compose.update(); this.bindOutputTarget(toDrawingBuffer); - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); state.disable(gl.BLEND); compose.render(); @@ -231,8 +231,8 @@ export class MultiSamplePass { state.disable(gl.BLEND); state.disable(gl.DEPTH_TEST); state.depthMask(false); - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); compose.render(); sampleIndex += 1; } else { @@ -267,8 +267,8 @@ export class MultiSamplePass { state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); state.disable(gl.DEPTH_TEST); state.depthMask(false); - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); if (sampleIndex === 0) { state.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -283,8 +283,8 @@ export class MultiSamplePass { drawPass.postprocessing.setOcclusionOffset(0, 0); this.bindOutputTarget(toDrawingBuffer); - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); const accumulationWeight = sampleIndex * sampleWeight; if (accumulationWeight > 0) { diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index fd41b8abe62071ec6a85734b6db554753e1ed03d..2f77af3e76956d447c819bc5d29a0d973cb8101b 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -538,8 +538,8 @@ export class PostprocessingPass { state.depthMask(false); const { x, y, width, height } = camera.viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); } private occlusionOffset: [x: number, y: number] = [0, 0]; diff --git a/src/mol-canvas3d/passes/smaa.ts b/src/mol-canvas3d/passes/smaa.ts index 3002b2ff33f3b4bd244675dc264ec4aa49f30c36..4ac7296fa717dcb72e3c790fd027275c6a5177b5 100644 --- a/src/mol-canvas3d/passes/smaa.ts +++ b/src/mol-canvas3d/passes/smaa.ts @@ -71,8 +71,8 @@ export class SmaaPass { state.depthMask(false); const { x, y, width, height } = viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); state.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); diff --git a/src/mol-geo/geometry/texture-mesh/color-smoothing.ts b/src/mol-geo/geometry/texture-mesh/color-smoothing.ts index 0a0a1786c5d2ae9412e09f698b33db3be74f7aea..6a9983a8f45c4556617cc63cb650f28bc6442b34 100644 --- a/src/mol-geo/geometry/texture-mesh/color-smoothing.ts +++ b/src/mol-geo/geometry/texture-mesh/color-smoothing.ts @@ -319,8 +319,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu if (isTimingMode) webgl.timer.mark('ColorAccumulate.render'); setAccumulateDefaults(webgl); - gl.viewport(0, 0, width, height); - gl.scissor(0, 0, width, height); + state.viewport(0, 0, width, height); + state.scissor(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); ValueCell.update(uCurrentY, 0); let currCol = 0; @@ -336,8 +336,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu // console.log({ i, currX, currY }); ValueCell.update(uCurrentX, currX); ValueCell.update(uCurrentSlice, i); - gl.viewport(currX, currY, dx, dy); - gl.scissor(currX, currY, dx, dy); + state.viewport(currX, currY, dx, dy); + state.scissor(currX, currY, dx, dy); accumulateRenderable.render(); ++currCol; currX += dx; @@ -371,8 +371,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu setNormalizeDefaults(webgl); texture.attachFramebuffer(framebuffer, 0); - gl.viewport(0, 0, width, height); - gl.scissor(0, 0, width, height); + state.viewport(0, 0, width, height); + state.scissor(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); normalizeRenderable.render(); if (isTimingMode) webgl.timer.markEnd('ColorNormalize.render'); diff --git a/src/mol-gl/compute/grid3d.ts b/src/mol-gl/compute/grid3d.ts index e291661ccae12a921b5d127ce6ecdbbc22617ce5..6a4922e34a6dcc497fdb5f672d94318fe0afe601 100644 --- a/src/mol-gl/compute/grid3d.ts +++ b/src/mol-gl/compute/grid3d.ts @@ -225,8 +225,8 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS> function resetGl(webgl: WebGLContext, w: number) { const { gl, state } = webgl; - gl.viewport(0, 0, w, w); - gl.scissor(0, 0, w, w); + state.viewport(0, 0, w, w); + state.scissor(0, 0, w, w); state.disable(gl.SCISSOR_TEST); state.disable(gl.BLEND); state.disable(gl.DEPTH_TEST); diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts index 8d162f5e99e8e69cf1f5fcb188a956336682d59f..b95ad7c44933a9398687654794311b74b52e8ddf 100644 --- a/src/mol-gl/compute/histogram-pyramid/reduction.ts +++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts @@ -122,7 +122,7 @@ export interface HistogramPyramid { export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid { if (isTimingMode) ctx.timer.mark('createHistogramPyramid'); - const { gl } = ctx; + const { gl, state } = ctx; const w = inputTexture.getWidth(); const h = inputTexture.getHeight(); @@ -146,7 +146,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, const framebuffer = getFramebuffer('pyramid', ctx); pyramidTex.attachFramebuffer(framebuffer, 0); - gl.viewport(0, 0, maxSizeX, maxSizeY); + state.viewport(0, 0, maxSizeX, maxSizeY); if (isWebGL2(gl)) { gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); } else { @@ -157,7 +157,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i)); const renderable = getHistopyramidReductionRenderable(ctx, inputTexture, levelTexturesFramebuffers[0].texture); - ctx.state.currentRenderItemId = -1; + state.currentRenderItemId = -1; setRenderingDefaults(ctx); let offset = 0; @@ -176,15 +176,15 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture); renderable.update(); } - ctx.state.currentRenderItemId = -1; - gl.viewport(0, 0, size, size); - gl.scissor(0, 0, size, size); + state.currentRenderItemId = -1; + state.viewport(0, 0, size, size); + state.scissor(0, 0, size, size); if (isWebGL2(gl)) { gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); } else { gl.clear(gl.COLOR_BUFFER_BIT); } - gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]); + state.scissor(0, 0, gridTexDim[0], gridTexDim[1]); renderable.render(); pyramidTex.bind(0); diff --git a/src/mol-gl/compute/histogram-pyramid/sum.ts b/src/mol-gl/compute/histogram-pyramid/sum.ts index a1cd5919a7bf5632b87d6ca0c8ea274ecff1caf4..65c36515d80ac9d2f1f7e3e87118c90fe240bcaf 100644 --- a/src/mol-gl/compute/histogram-pyramid/sum.ts +++ b/src/mol-gl/compute/histogram-pyramid/sum.ts @@ -68,7 +68,7 @@ const sumInts = new Int32Array(4); export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) { if (isTimingMode) ctx.timer.mark('getHistopyramidSum'); - const { gl, resources } = ctx; + const { gl, state, resources } = ctx; const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture); ctx.state.currentRenderItemId = -1; @@ -89,7 +89,7 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture setRenderingDefaults(ctx); - gl.viewport(0, 0, 1, 1); + state.viewport(0, 0, 1, 1); renderable.render(); gl.finish(); diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts index b16014c011b8eef48a74c800b69c595b463bdee7..c460512d509d791b3ebfc5eba5d07afefa7f32ef 100644 --- a/src/mol-gl/compute/marching-cubes/active-voxels.ts +++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts @@ -85,7 +85,7 @@ function setRenderingDefaults(ctx: WebGLContext) { export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) { if (isTimingMode) ctx.timer.mark('calcActiveVoxels'); - const { gl, resources } = ctx; + const { gl, state, resources } = ctx; const width = volumeData.getWidth(); const height = volumeData.getHeight(); @@ -106,10 +106,10 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim activeVoxelsTex.attachFramebuffer(framebuffer, 0); setRenderingDefaults(ctx); - gl.viewport(0, 0, width, height); - gl.scissor(0, 0, width, height); + state.viewport(0, 0, width, height); + state.scissor(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); - gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]); + state.scissor(0, 0, gridTexDim[0], gridTexDim[1]); renderable.render(); // console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim); diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 3c628b25554fbd79c14f59fee00d2fbf236c3c7c..0215937e512ad4839c8b5dc9fb9b16fe1cf044e1 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -127,7 +127,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex if (!drawBuffers) throw new Error('need WebGL draw buffers'); if (isTimingMode) ctx.timer.mark('createIsosurfaceBuffers'); - const { gl, resources, extensions } = ctx; + const { gl, state, resources, extensions } = ctx; const { pyramidTex, height, levels, scale, count } = histogramPyramid; const width = pyramidTex.getWidth(); @@ -192,7 +192,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex ]); setRenderingDefaults(ctx); - gl.viewport(0, 0, width, height); + state.viewport(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); renderable.render(); diff --git a/src/mol-gl/compute/util.ts b/src/mol-gl/compute/util.ts index 2e759efbb3def192fe627db0c448a0b81ba39bc5..bfef65236a7a6ee14392f4f0f50f2fc1da56cac0 100644 --- a/src/mol-gl/compute/util.ts +++ b/src/mol-gl/compute/util.ts @@ -125,8 +125,8 @@ export function readAlphaTexture(ctx: WebGLContext, texture: Texture) { state.clearColor(0, 0, 0, 0); state.blendFunc(gl.ONE, gl.ONE); state.blendEquation(gl.FUNC_ADD); - gl.viewport(0, 0, width, height); - gl.scissor(0, 0, width, height); + state.viewport(0, 0, width, height); + state.scissor(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); copy.render(); diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 420ae95520636efafd1c6402a86877e50353da3c..937131cb4d01d34dabff3337d93dedcc03369536 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -64,7 +64,7 @@ interface Renderer { renderDepthTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void - renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void + renderBlended: (scene: Scene, camera: ICamera, depthTexture: Texture | null) => void renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void @@ -359,8 +359,8 @@ namespace Renderer { state.colorMask(true, true, true, true); const { x, y, width, height } = viewport; - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); globalUniformsNeedUpdate = true; state.currentRenderItemId = -1; @@ -475,9 +475,13 @@ namespace Renderer { if (isTimingMode) ctx.timer.markEnd('Renderer.renderMarkingMask'); }; - const renderBlended = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { - renderBlendedOpaque(group, camera, depthTexture); - renderBlendedTransparent(group, camera, depthTexture); + const renderBlended = (scene: Scene, camera: ICamera, depthTexture: Texture | null) => { + if (scene.hasOpaque) { + renderBlendedOpaque(scene, camera, depthTexture); + } + if (scene.opacityAverage < 1) { + renderBlendedTransparent(scene, camera, depthTexture); + } }; const renderBlendedOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { @@ -714,8 +718,8 @@ namespace Renderer { } }, setViewport: (x: number, y: number, width: number, height: number) => { - gl.viewport(x, y, width, height); - gl.scissor(x, y, width, height); + state.viewport(x, y, width, height); + state.scissor(x, y, width, height); if (x !== viewport.x || y !== viewport.y || width !== viewport.width || height !== viewport.height) { Viewport.set(viewport, x, y, width, height); ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height)); diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 186484944b26d6c7e076416f634e5c19f0349537..a46be8ae129f3be7d75e5dd6e821181757923386 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -80,8 +80,12 @@ interface Scene extends Object3D { has: (o: GraphicsRenderObject) => boolean clear: () => void forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void + /** Marker average of primitive renderables */ readonly markerAverage: number + /** Opacity average of primitive renderables */ readonly opacityAverage: number + /** Is `true` if any primitive renderable (possibly) has any opaque part */ + readonly hasOpaque: boolean } namespace Scene { @@ -103,6 +107,7 @@ namespace Scene { let markerAverage = 0; let opacityAverage = 0; + let hasOpaque = false; const object3d = Object3D.create(); const { view, position, direction, up } = object3d; @@ -160,7 +165,9 @@ namespace Scene { } renderables.sort(renderableSort); + markerAverage = calculateMarkerAverage(); opacityAverage = calculateOpacityAverage(); + hasOpaque = calculateHasOpaque(); return true; } @@ -182,7 +189,10 @@ namespace Scene { const newVisibleHash = computeVisibleHash(); if (newVisibleHash !== visibleHash) { boundingSphereVisibleDirty = true; + markerAverage = calculateMarkerAverage(); opacityAverage = calculateOpacityAverage(); + hasOpaque = calculateHasOpaque(); + visibleHash = newVisibleHash; return true; } else { return false; @@ -220,6 +230,19 @@ namespace Scene { return count > 0 ? opacityAverage / count : 0; } + function calculateHasOpaque() { + if (primitives.length === 0) return false; + for (let i = 0, il = primitives.length; i < il; ++i) { + const p = primitives[i]; + if (!p.state.visible) continue; + + if (p.state.opaque) return true; + if (p.state.alphaFactor === 1 && p.values.alpha.ref.value === 1 && p.values.transparencyAverage.ref.value !== 1) return true; + if (p.values.dTransparentBackfaces?.ref.value === 'opaque') return true; + } + return false; + } + return { view, position, direction, up, @@ -247,6 +270,7 @@ namespace Scene { } markerAverage = calculateMarkerAverage(); opacityAverage = calculateOpacityAverage(); + hasOpaque = calculateHasOpaque(); }, add: (o: GraphicsRenderObject) => commitQueue.add(o), remove: (o: GraphicsRenderObject) => commitQueue.remove(o), @@ -283,7 +307,6 @@ namespace Scene { if (boundingSphereVisibleDirty) { calculateBoundingSphere(renderables, boundingSphereVisible, true); boundingSphereVisibleDirty = false; - visibleHash = computeVisibleHash(); } return boundingSphereVisible; }, @@ -293,6 +316,9 @@ namespace Scene { get opacityAverage() { return opacityAverage; }, + get hasOpaque() { + return hasOpaque; + }, }; } } diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index c3cd962a4f42716371db39688dcd4c66cd46ac22..f583a24468c0731260bf74252db9b546f7d19554 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -142,12 +142,12 @@ export function readPixels(gl: GLRenderingContext, x: number, y: number, width: if (isDebugMode) checkError(gl); } -function getDrawingBufferPixelData(gl: GLRenderingContext) { +function getDrawingBufferPixelData(gl: GLRenderingContext, state: WebGLState) { const w = gl.drawingBufferWidth; const h = gl.drawingBufferHeight; const buffer = new Uint8Array(w * h * 4); unbindFramebuffer(gl); - gl.viewport(0, 0, w, h); + state.viewport(0, 0, w, h); readPixels(gl, 0, 0, w, h, buffer); return PixelData.flipY(PixelData.create(buffer, w, h)); } @@ -345,15 +345,15 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal readPixelsAsync, waitForGpuCommandsComplete: () => waitForGpuCommandsComplete(gl), waitForGpuCommandsCompleteSync: () => waitForGpuCommandsCompleteSync(gl), - getDrawingBufferPixelData: () => getDrawingBufferPixelData(gl), + getDrawingBufferPixelData: () => getDrawingBufferPixelData(gl, state), clear: (red: number, green: number, blue: number, alpha: number) => { unbindFramebuffer(gl); state.enable(gl.SCISSOR_TEST); state.depthMask(true); state.colorMask(true, true, true, true); state.clearColor(red, green, blue, alpha); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - gl.scissor(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + state.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + state.scissor(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }, diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index e05dced716b9d365cf241e6e0a3191b4f4a19d3c..95f082d71eb2d1966d61e430d6639bc0e3b4170a 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -168,7 +168,7 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: getProgram: (variant: T) => programs[variant], render: (variant: T, sharedTexturesCount: number) => { - if (drawCount === 0 || instanceCount === 0 || ctx.isContextLost) return; + if (drawCount === 0 || instanceCount === 0) return; const program = programs[variant]; if (program.id === currentProgramId && state.currentRenderItemId === id) { program.setUniforms(uniformValueEntries); diff --git a/src/mol-gl/webgl/state.ts b/src/mol-gl/webgl/state.ts index d84c91bc8fd48ed129b996d74dfdada51f7b958c..dc6184d7e894b8d274a4c111a1fc8355436b1e88 100644 --- a/src/mol-gl/webgl/state.ts +++ b/src/mol-gl/webgl/state.ts @@ -69,6 +69,9 @@ export type WebGLState = { clearVertexAttribsState: () => void disableUnusedVertexAttribs: () => void + viewport: (x: number, y: number, width: number, height: number) => void + scissor: (x: number, y: number, width: number, height: number) => void + reset: () => void } @@ -95,6 +98,9 @@ export function createState(gl: GLRenderingContext): WebGLState { let maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const vertexAttribsState: number[] = []; + let currentViewport: [number, number, number, number] = gl.getParameter(gl.VIEWPORT); + let currentScissor: [number, number, number, number] = gl.getParameter(gl.SCISSOR_BOX); + const clearVertexAttribsState = () => { for (let i = 0; i < maxVertexAttribs; ++i) { vertexAttribsState[i] = 0; @@ -222,6 +228,26 @@ export function createState(gl: GLRenderingContext): WebGLState { } }, + viewport: (x: number, y: number, width: number, height: number) => { + if (x !== currentViewport[0] || y !== currentViewport[1] || width !== currentViewport[2] || height !== currentViewport[3]) { + gl.viewport(x, y, width, height); + currentViewport[0] = x; + currentViewport[1] = y; + currentViewport[2] = width; + currentViewport[3] = height; + } + }, + + scissor: (x: number, y: number, width: number, height: number) => { + if (x !== currentScissor[0] || y !== currentScissor[1] || width !== currentScissor[2] || height !== currentScissor[3]) { + gl.scissor(x, y, width, height); + currentScissor[0] = x; + currentScissor[1] = y; + currentScissor[2] = width; + currentScissor[3] = height; + } + }, + reset: () => { enabledCapabilities = {}; @@ -247,6 +273,9 @@ export function createState(gl: GLRenderingContext): WebGLState { for (let i = 0; i < maxVertexAttribs; ++i) { vertexAttribsState[i] = 0; } + + currentViewport = gl.getParameter(gl.VIEWPORT); + currentScissor = gl.getParameter(gl.SCISSOR_BOX); } }; } \ No newline at end of file diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 97c5f0c861cd460b0ecf629bc1722aca4b8475a9..05a26567d0017bbabde11f392d26bca3ccb07afe 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -166,8 +166,8 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat state.currentRenderItemId = -1; fbTex.attachFramebuffer(framebuffer, 0); if (clear) { - gl.viewport(0, 0, width, height); - gl.scissor(0, 0, width, height); + state.viewport(0, 0, width, height); + state.scissor(0, 0, width, height); gl.clear(gl.COLOR_BUFFER_BIT); } ValueCell.update(uCurrentY, 0); @@ -184,8 +184,8 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat // console.log({ i, currX, currY }); ValueCell.update(uCurrentX, currX); ValueCell.update(uCurrentSlice, i); - gl.viewport(currX, currY, dx, dy); - gl.scissor(currX, currY, dx, dy); + state.viewport(currX, currY, dx, dy); + state.scissor(currX, currY, dx, dy); renderable.render(); ++currCol; currX += dx; @@ -232,8 +232,8 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat const framebuffer = getFramebuffer(webgl); framebuffer.bind(); setRenderingDefaults(webgl); - gl.viewport(0, 0, dx, dy); - gl.scissor(0, 0, dx, dy); + state.viewport(0, 0, dx, dy); + state.scissor(0, 0, dx, dy); if (!texture) texture = colorBufferHalfFloat && textureHalfFloat ? resources.texture('volume-float16', 'rgba', 'fp16', 'linear')