diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts index 7e00a876a4fade1cf2e5ac3d3cde33d733531058..2705e28666ac875b94aeba1b4fa002428d2bea8c 100644 --- a/src/mol-gl/compute/histogram-pyramid/reduction.ts +++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts @@ -11,7 +11,6 @@ import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; import { Texture, createTexture } from 'mol-gl/webgl/texture'; import { ShaderCode } from 'mol-gl/shader-code'; import { ValueCell } from 'mol-util'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { QuadSchema, QuadValues } from '../util'; import { Vec2 } from 'mol-math/linear-algebra'; import { getHistopyramidSum } from './sum'; @@ -62,11 +61,12 @@ function getLevelTexture(ctx: WebGLContext, level: number) { return tex } -function setRenderingDefaults(gl: GLRenderingContext) { - gl.disable(gl.CULL_FACE) - gl.disable(gl.BLEND) - gl.disable(gl.DEPTH_TEST) - gl.depthMask(false) +function setRenderingDefaults(ctx: WebGLContext) { + const { gl, state } = ctx + state.disable(gl.CULL_FACE) + state.disable(gl.BLEND) + state.disable(gl.DEPTH_TEST) + state.depthMask(false) } export interface HistogramPyramid { @@ -102,7 +102,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture) for (let i = 0; i < levels; ++i) levelTextures.push(getLevelTexture(ctx, i)) const renderable = getHistopyramidReductionRenderable(ctx, initialTexture) - setRenderingDefaults(gl) + setRenderingDefaults(ctx) let offset = 0; for (let i = 0; i < levels; i++) { diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts index 0ede2cd48e727808c87237f6daa42e08f2d01f77..cf1367a8b867a03c1ea64eb318afc9fa16db9fcb 100644 --- a/src/mol-gl/compute/marching-cubes/active-voxels.ts +++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts @@ -11,7 +11,6 @@ import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; import { Texture, createTexture } from 'mol-gl/webgl/texture'; import { ShaderCode } from 'mol-gl/shader-code'; import { ValueCell } from 'mol-util'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { Vec3 } from 'mol-math/linear-algebra'; import { QuadSchema, QuadValues } from '../util'; import { getTriCount } from './tables'; @@ -52,11 +51,12 @@ function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridD return createComputeRenderable(renderItem, values); } -function setRenderingDefaults(gl: GLRenderingContext) { - gl.disable(gl.CULL_FACE) - gl.disable(gl.BLEND) - gl.disable(gl.DEPTH_TEST) - gl.depthMask(false) +function setRenderingDefaults(ctx: WebGLContext) { + const { gl, state } = ctx + state.disable(gl.CULL_FACE) + state.disable(gl.BLEND) + state.disable(gl.DEPTH_TEST) + state.depthMask(false) } export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDimensions: Vec3, isoValue: number) { @@ -72,7 +72,7 @@ export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDime const renderable = getActiveVoxelsRenderable(ctx, cornerTex, gridDimensions, isoValue) activeVoxelsTex.attachFramebuffer(framebuffer, 0) - setRenderingDefaults(gl) + setRenderingDefaults(ctx) gl.viewport(0, 0, width, height) renderable.render() diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 7d69229971e3f7dfd555c98ec8c7cd1d311688a4..24a4cb508a6c7dc073d1cb0a526d7b9b66652383 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -11,7 +11,6 @@ import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; import { Texture, createTexture } from 'mol-gl/webgl/texture'; import { ShaderCode } from 'mol-gl/shader-code'; import { ValueCell } from 'mol-util'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { Vec3, Vec2, Mat4 } from 'mol-math/linear-algebra'; import { QuadSchema, QuadValues } from '../util'; import { HistogramPyramid } from '../histogram-pyramid/reduction'; @@ -73,16 +72,17 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture return createComputeRenderable(renderItem, values); } -function setRenderingDefaults(gl: GLRenderingContext) { - gl.disable(gl.CULL_FACE) - gl.disable(gl.BLEND) - gl.disable(gl.DEPTH_TEST) - gl.depthMask(false) - gl.enable(gl.SCISSOR_TEST) +function setRenderingDefaults(ctx: WebGLContext) { + const { gl, state } = ctx + state.disable(gl.CULL_FACE) + state.disable(gl.BLEND) + state.disable(gl.DEPTH_TEST) + state.depthMask(false) + state.enable(gl.SCISSOR_TEST) } export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDimensions: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) { - const { gl, framebufferCache } = ctx + const { gl, framebufferCache, state } = ctx const { pyramidTex, height, levels, scale, count } = histogramPyramid const framebuffer = framebufferCache.get(FramebufferName).value @@ -130,11 +130,11 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex // drawBuffers.COLOR_ATTACHMENT5 ]) - setRenderingDefaults(gl) + setRenderingDefaults(ctx) gl.viewport(0, 0, pyramidTex.width, pyramidTex.height) gl.scissor(0, 0, pyramidTex.width, height) renderable.render() - gl.disable(gl.SCISSOR_TEST) + state.disable(gl.SCISSOR_TEST) // const vgt = readTexture(ctx, vertexGroupTexture, pyramidTex.width, height) // console.log('vertexGroupTexture', vgt.array.subarray(0, 4 * count)) diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index ec9095bf11c03b5d8dc4483cebcbbfc672128740..d3d46423529fedd2138d3a69b4529efa6b346f9f 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -125,27 +125,27 @@ namespace Renderer { if (r.values.dDoubleSided) { if (r.values.dDoubleSided.ref.value) { - gl.disable(gl.CULL_FACE) + state.disable(gl.CULL_FACE) } else { - gl.enable(gl.CULL_FACE) + state.enable(gl.CULL_FACE) } } else { // webgl default - gl.disable(gl.CULL_FACE) + state.disable(gl.CULL_FACE) } if (r.values.dFlipSided) { if (r.values.dFlipSided.ref.value) { - gl.frontFace(gl.CW) - gl.cullFace(gl.FRONT) + state.frontFace(gl.CW) + state.cullFace(gl.FRONT) } else { - gl.frontFace(gl.CCW) - gl.cullFace(gl.BACK) + state.frontFace(gl.CCW) + state.cullFace(gl.BACK) } } else { // webgl default - gl.frontFace(gl.CCW) - gl.cullFace(gl.BACK) + state.frontFace(gl.CCW) + state.cullFace(gl.BACK) } r.render(variant) @@ -172,26 +172,26 @@ namespace Renderer { const { renderables } = scene if (variant === 'draw') { - gl.disable(gl.BLEND) - gl.enable(gl.DEPTH_TEST) - gl.depthMask(true) + state.disable(gl.BLEND) + state.enable(gl.DEPTH_TEST) + state.depthMask(true) for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i] if (r.state.opaque) renderObject(r, variant) } - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) - gl.enable(gl.BLEND) + state.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + state.enable(gl.BLEND) for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i] - gl.depthMask(r.values.uAlpha.ref.value === 1.0) + state.depthMask(r.values.uAlpha.ref.value === 1.0) if (!r.state.opaque) renderObject(r, variant) } } else { // picking - gl.disable(gl.BLEND) - gl.enable(gl.DEPTH_TEST) - gl.depthMask(true) + state.disable(gl.BLEND) + state.enable(gl.DEPTH_TEST) + state.depthMask(true) for (let i = 0, il = renderables.length; i < il; ++i) { renderObject(renderables[i], variant) } @@ -202,7 +202,7 @@ namespace Renderer { return { clear: () => { - gl.depthMask(true) + state.depthMask(true) gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) }, render, diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index fb59101169f71f72df25ad976ee3fe2f7933b523..09f567e2551d59c8e3f013f2f75de38680173928 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -160,49 +160,7 @@ export type WebGLExtensions = { drawBuffers: COMPAT_draw_buffers | null } -export type WebGLStats = { - bufferCount: number - framebufferCount: number - renderbufferCount: number - textureCount: number - vaoCount: number - - drawCount: number - instanceCount: number - instancedDrawCount: number -} - -export type WebGLState = { - currentProgramId: number - currentMaterialId: number -} - -/** A WebGL context object, including the rendering context, resource caches and counts */ -export interface WebGLContext { - readonly gl: GLRenderingContext - readonly isWebGL2: boolean - readonly pixelRatio: number - - readonly extensions: WebGLExtensions - readonly state: WebGLState - readonly stats: WebGLStats - - readonly shaderCache: ShaderCache - readonly programCache: ProgramCache - readonly framebufferCache: FramebufferCache - - readonly maxTextureSize: number - readonly maxDrawBuffers: number - - unbindFramebuffer: () => void - readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) => void - readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void> - waitForGpuCommandsComplete: () => Promise<void> - waitForGpuCommandsCompleteSync: () => void - destroy: () => void -} - -export function createContext(gl: GLRenderingContext): WebGLContext { +function createExtensions(gl: GLRenderingContext): WebGLExtensions { const instancedArrays = getInstancedArrays(gl) if (instancedArrays === null) { throw new Error('Could not find support for "instanced_arrays"') @@ -244,12 +202,34 @@ export function createContext(gl: GLRenderingContext): WebGLContext { console.log('Could not find support for "draw_buffers"') } - const state: WebGLState = { - currentProgramId: -1, - currentMaterialId: -1, + return { + instancedArrays, + standardDerivatives, + blendMinMax, + textureFloat, + textureFloatLinear, + elementIndexUint, + vertexArrayObject, + fragDepth, + colorBufferFloat, + drawBuffers } +} + +export type WebGLStats = { + bufferCount: number + framebufferCount: number + renderbufferCount: number + textureCount: number + vaoCount: number + + drawCount: number + instanceCount: number + instancedDrawCount: number +} - const stats: WebGLStats = { +function createStats(): WebGLStats { + return { bufferCount: 0, framebufferCount: 0, renderbufferCount: 0, @@ -260,19 +240,142 @@ export function createContext(gl: GLRenderingContext): WebGLContext { instanceCount: 0, instancedDrawCount: 0, } +} - const extensions: WebGLExtensions = { - instancedArrays, - standardDerivatives, - blendMinMax, - textureFloat, - textureFloatLinear, - elementIndexUint, - vertexArrayObject, - fragDepth, - colorBufferFloat, - drawBuffers +export type WebGLState = { + currentProgramId: number + currentMaterialId: number + + enable: (cap: number) => void + disable: (cap: number) => void + + frontFace: (mode: number) => void + cullFace: (mode: number) => void + depthMask: (flag: boolean) => void + + blendFunc: (src: number, dst: number) => void + blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => void + + blendEquation: (mode: number) => void + blendEquationSeparate: (modeRGB: number, modeAlpha: number) => void +} + +function createState(gl: GLRenderingContext): WebGLState { + const enabledCapabilities: { [k: number]: boolean } = {} + + let currentFrontFace = gl.getParameter(gl.FRONT_FACE) + let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE) + let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK) + + let currentBlendSrcRGB = gl.getParameter(gl.BLEND_SRC_RGB) + let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB) + let currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA) + let currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA) + + let currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB) + let currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA) + + return { + currentProgramId: -1, + currentMaterialId: -1, + + enable: (cap: number) => { + if (enabledCapabilities[cap] !== true ) { + gl.enable(cap) + enabledCapabilities[cap] = true + } + }, + disable: (cap: number) => { + if (enabledCapabilities[cap] !== false) { + gl.disable(cap) + enabledCapabilities[cap] = false + } + }, + + frontFace: (mode: number) => { + if (mode !== currentFrontFace) { + gl.frontFace(mode) + currentFrontFace = mode + } + }, + cullFace: (mode: number) => { + if (mode !== currentCullFace) { + gl.cullFace(mode) + currentCullFace = mode + } + }, + depthMask: (flag: boolean) => { + if (flag !== currentDepthMask) { + gl.depthMask(flag) + currentDepthMask = flag + } + }, + + blendFunc: (src: number, dst: number) => { + if (src !== currentBlendSrcRGB || dst !== currentBlendDstRGB || src !== currentBlendSrcAlpha || dst !== currentBlendDstAlpha) { + gl.blendFunc(src, dst) + currentBlendSrcRGB = src + currentBlendDstRGB = dst + currentBlendSrcAlpha = src + currentBlendDstAlpha = dst + } + }, + blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => { + if (srcRGB !== currentBlendSrcRGB || dstRGB !== currentBlendDstRGB || srcAlpha !== currentBlendSrcAlpha || dstAlpha !== currentBlendDstAlpha) { + gl.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha) + currentBlendSrcRGB = srcRGB + currentBlendDstRGB = dstRGB + currentBlendSrcAlpha = srcAlpha + currentBlendDstAlpha = dstAlpha + } + }, + + blendEquation: (mode: number) => { + if (mode !== currentBlendEqRGB || mode !== currentBlendEqAlpha) { + gl.blendEquation(mode) + currentBlendEqRGB = mode + currentBlendEqAlpha = mode + } + }, + blendEquationSeparate: (modeRGB: number, modeAlpha: number) => { + if (modeRGB !== currentBlendEqRGB || modeAlpha !== currentBlendEqAlpha) { + gl.blendEquationSeparate(modeRGB, modeAlpha) + currentBlendEqRGB = modeRGB + currentBlendEqAlpha = modeAlpha + } + } } +} + +/** A WebGL context object, including the rendering context, resource caches and counts */ +export interface WebGLContext { + readonly gl: GLRenderingContext + readonly isWebGL2: boolean + readonly pixelRatio: number + + readonly extensions: WebGLExtensions + readonly state: WebGLState + readonly stats: WebGLStats + + readonly shaderCache: ShaderCache + readonly programCache: ProgramCache + readonly framebufferCache: FramebufferCache + + readonly maxTextureSize: number + readonly maxDrawBuffers: number + + unbindFramebuffer: () => void + readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) => void + readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void> + waitForGpuCommandsComplete: () => Promise<void> + waitForGpuCommandsCompleteSync: () => void + destroy: () => void +} + +export function createContext(gl: GLRenderingContext): WebGLContext { + const extensions = createExtensions(gl) + const state = createState(gl) + const stats = createStats() const shaderCache: ShaderCache = createShaderCache(gl) const programCache: ProgramCache = createProgramCache(gl, state, extensions, shaderCache) diff --git a/src/mol-gl/webgl/program.ts b/src/mol-gl/webgl/program.ts index 34bf101c9abe6c4bc1322ed9c1af5c77d8eb1837..5997cc71165cb322693132650ee8412add4fe475 100644 --- a/src/mol-gl/webgl/program.ts +++ b/src/mol-gl/webgl/program.ts @@ -163,7 +163,10 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi setUniforms: (uniformValues: UniformsList) => { for (let i = 0, il = uniformValues.length; i < il; ++i) { const [k, v] = uniformValues[i] - if (v) uniformSetters[k](gl, locations[k], v.ref.value) + if (v) { + const l = locations[k] + if (l !== null) uniformSetters[k](gl, l, v.ref.value) + } } }, bindAttributes: (attribueBuffers: AttributeBuffers) => { @@ -176,10 +179,13 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi bindTextures: (textures: Textures) => { for (let i = 0, il = textures.length; i < il; ++i) { const [k, texture] = textures[i] - texture.bind(i as TextureId) - // TODO if the order and count of textures in a material can be made invariant - // this needs to be set only when the material changes - uniformSetters[k](gl, locations[k], i as TextureId) + const l = locations[k] + if (l !== null) { + // TODO if the order and count of textures in a material can be made invariant + // bind needs to be called only when the material changes + texture.bind(i as TextureId) + uniformSetters[k](gl, l, i as TextureId) + } } }, diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 5159ae4ff81a09e07f5a5c9500aeb07badfb88f9..8036d65c3ecf2df76bd4837711e96a30ad360554 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -14,7 +14,6 @@ import { ValueCell } from 'mol-util' import { createComputeRenderable, ComputeRenderable } from 'mol-gl/renderable' import { WebGLContext } from 'mol-gl/webgl/context'; import { createTexture, Texture } from 'mol-gl/webgl/texture'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { decodeFloatRGB } from 'mol-util/float-packing'; import { ShaderCode } from 'mol-gl/shader-code'; import { createComputeRenderItem } from 'mol-gl/webgl/render-item'; @@ -112,7 +111,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat const framebuffer = framebufferCache.get(FramebufferName).value framebuffer.bind() - setRenderingDefaults(gl) + setRenderingDefaults(webgl) if (!texture) texture = createTexture(webgl, 'image-float32', 'rgba', 'float', 'nearest') texture.define(texDimX, texDimY) @@ -167,7 +166,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat const framebuffer = framebufferCache.get(FramebufferName).value framebuffer.bind() - setRenderingDefaults(gl) + setRenderingDefaults(webgl) gl.viewport(0, 0, dx, dy) if (!texture) texture = createTexture(webgl, 'volume-float32', 'rgba', 'float', 'nearest') @@ -265,37 +264,38 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po return createComputeRenderable(renderItem, values) } -function setRenderingDefaults(gl: GLRenderingContext) { - gl.disable(gl.CULL_FACE) - gl.frontFace(gl.CCW) - gl.cullFace(gl.BACK) - gl.enable(gl.BLEND) +function setRenderingDefaults(ctx: WebGLContext) { + const { gl, state } = ctx + state.disable(gl.CULL_FACE) + state.frontFace(gl.CCW) + state.cullFace(gl.BACK) + state.enable(gl.BLEND) } function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { - const { gl } = webgl + const { gl, state } = webgl ValueCell.update(renderable.values.dCalcType, 'minDistance') renderable.update() - gl.blendFunc(gl.ONE, gl.ONE) + state.blendFunc(gl.ONE, gl.ONE) // the shader writes 1 - dist so we set blending to MAX - gl.blendEquation(webgl.extensions.blendMinMax.MAX) + state.blendEquation(webgl.extensions.blendMinMax.MAX) } function setupDensityRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { - const { gl } = webgl + const { gl, state } = webgl ValueCell.update(renderable.values.dCalcType, 'density') renderable.update() - gl.blendFunc(gl.ONE, gl.ONE) - gl.blendEquation(gl.FUNC_ADD) + state.blendFunc(gl.ONE, gl.ONE) + state.blendEquation(gl.FUNC_ADD) } function setupGroupIdRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { - const { gl } = webgl + const { gl, state } = webgl ValueCell.update(renderable.values.dCalcType, 'groupId') renderable.update() // overwrite color, don't change alpha - gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) - gl.blendEquation(gl.FUNC_ADD) + state.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) + state.blendEquation(gl.FUNC_ADD) } function getTexture2dSize(gridDim: Vec3) { diff --git a/src/tests/browser/render-structure.ts b/src/tests/browser/render-structure.ts index 80ba14f4459aca89a8a958fe19e8ca4606ddcbab..290afa43d6b56c3b607aafcfdf98f8a7ac46724d 100644 --- a/src/tests/browser/render-structure.ts +++ b/src/tests/browser/render-structure.ts @@ -138,6 +138,7 @@ async function init() { if (show.molecularSurface) canvas3d.add(molecularSurfaceRepr) if (show.gaussianSurface) canvas3d.add(gaussianSurfaceRepr) canvas3d.resetCamera() + // canvas3d.setProps({ trackball: { ...canvas3d.props.trackball, spin: true } }) } init() \ No newline at end of file