From 8178ac23c5161e22767badb46735f6793f066d44 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Mon, 15 Apr 2019 10:38:28 -0700 Subject: [PATCH] gl compute and state handling improvements --- package-lock.json | Bin 635844 -> 633446 bytes src/mol-canvas3d/canvas3d.ts | 4 -- src/mol-geo/util/marching-cubes/algorithm.ts | 2 + .../compute/histogram-pyramid/reduction.ts | 37 ++++++++++-- src/mol-gl/compute/histogram-pyramid/sum.ts | 16 ++++++ .../compute/marching-cubes/active-voxels.ts | 6 ++ .../compute/marching-cubes/isosurface.ts | 30 ++++++++-- src/mol-gl/renderer.ts | 35 +++++------- src/mol-gl/shader/gaussian-density.frag | 4 +- src/mol-gl/shader/gaussian-density.vert | 3 +- src/mol-gl/webgl/context.ts | 22 ++++++++ src/mol-gl/webgl/render-item.ts | 46 ++++++++------- src/mol-math/geometry/gaussian-density/gpu.ts | 53 +++++++++++------- src/tests/browser/marching-cubes.ts | 5 +- 14 files changed, 181 insertions(+), 82 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b6468e2567dbae6fc5e7f1fd17889ee8aceb881..ce603119b23d7d2f2eb7e9c8cb2c7c0421f37e54 100644 GIT binary patch delta 2779 zcmeHJeQcEV9nbT8pVzk6yV7g#u6J#Zw&}~bN*ycR#M%o5Y*uJ5;B-SOMfZ|*_BfXX zhk~sM1d(lCxN#o_wVV>8hHQ6^#NQ?}y-eb)NP2TJhy&UI8*wAFOoa{6?)yAvG0Xnm z{_?N;y?$RlFTdxGUabG?JN23E7ZTa~Jc*fO`r&6U+%tl3kK`4u$E8;^dk7vDh`;ZU zexqULDXCJt_7iDGdGeqX5Z`)ETC$Z#ULk7aUrY9AN1gm+7cx=VD;fvobq?fy%ESGS z$d{HPKF!0?SLNk*V|I)R@v6gU#NsS9U}~dYhD}+zKT*823>)_vPQ*QABBtp+aotV5 z((VEeww6;C$6q5ShT3Tm*<)0MTu3TKe84a<vD9?q>7!JPvnwSp{9gJdvZHDMAAeVK zApO4K!0?D!g5GY`iJ+fa6PZnZtoPA&v?OH*V*ez?TJOV_4-LO{)&<u!t|U@L4OaT5 zt42j4_Ea&Pe)_Vt@Ti{#`Bwx26qgV_MkUtSJ{<8-0fP6-CT2%8S0X;>#&A1zB3D5k zaj1%X3If0Xv<nfgr57cnCG*p+VrjW`ciMr%wQ2=g*2_Wb?t_V7sfq}hevE!7`_L^Z z1=w<exTPml7h*oS5RbOgLmWq`MkZvM=y;Da%(j_6Jl!l8U~2=dv1(p?l1kCvskzX1 zGGD?nyDpsWr8})uUqZ{Y5kN;77qstLGlbzL`oxB6_+e8;uv>MYV1VxrgeZuab}lfu z-%!vqpxHGnK&(vlA=J$DP51NR{&r#@H!$eTS}S5Z&1%GM(0ufU7^{7!88t?InMHUL zx2d4z4Ed2c#vHDDLNhVYOdAkvP%Dvomt*E0P)iXFkq@`sLNDPL3up^&d7hPUVv;)> zlFVXUpCk{)Hmf=UQ{>Le;p{?c#`dW>?R@Vo*2&nnRS%+Pn5@VR(y(!p=15FDtin0T zc?SQ?K~m?)K-?NRZl%>QP8e?FuJCRuDQO6u<J!-BN1YE>lc{6y95-rdPn^G%{1QIC zsN0Bi<pF-v$%tNSmz!-u5q*a%$PI7>gQaF|zTf0us606$6(_T-*ohkEGwL?Kfc93p zVsW?X>#~lb9H*bWNDk{@VrI#_&$NY=omvgTQPtLAD}u|kj@{`ZG2TW)gvmN-P9zbv z82-yF#`l&`gmC@J94zs>8MJhejdVw!vc`&BkH`a>S3DM`6%w4a>>rl+y`p6)?PtmF zp-M~~H1{C3$87vh8|4W_csacQ*XvRj`uDJ*&JL;7aCPans`HM()fH5WijVR&L~-aN zs>Y=zvmTiqMa9&e{8AV<m|J_AeO#@&$(aW&-<MoSZ&1rEHI*dx{jeSfy6Cs)xQlPu zj+of0^21ffj`-RqESmH#)r(z!U|pr3<F)WMRmpoOHBQcCC%;8ABT|pud1ClTtytbo zlRARubuT7f(%wQQp?XF2U9>xzh;MMBf1F&xdBy0FZ%#4ilOd7qR-c#VHs&Gzfl&zW z8?^qjrEn{GF@4prmjp*kI_%AuWR{4wleADu^slSN9c7FiyU~r7e<}51<`mUQ;OIe& zzHgM+eoWl9w~_5ri0R9$%Ge=R*UABw(V=l#l+SmXy})SdmMtGbP-fM!4;Q09#AjoN zsZPXB)8>4_;UB1lf~|J1Y2O|8a5=e)RG;6C{E==KagTX4znj^t&coO@*y}$ElS5?Y zo3D^v_VI&e10n%a!{y)UEr>m+YGV2ty_t^^{iQbF&Wq|dbWKbRsV^&Y$hZ&*u`j2# zsTMMekgB0N`_UAURmyqEnk#ViQNa$stO&oXesRUktDp1nKj%YKPO9}1GTl;<i2p-Y zRk41vQ6Y(m>+%IHS;O<=33c@m%&gLUqODi!l9B2%el8ZQ)_&GzxygsIU4~0s+Nt%F zS^pc*zff_A`wnPp=h<cxY_<>HM)0iU6Mx#K-v}e#sd*8bmC6LYt{;$ZTJjWbPn%AW z8rOg9vKNyn!zcRQ)t}+7Jlj^3{Y~Gs98*<_AF~OwO5|eFV}#j8Ll=FEjL87{V}@6( zi5Oc_pG)ZfYw5g&*}lr#vZW)pCQ?Ppi^^fw%WD-;=~uoYiM>_IulTLoY5GKYr`d4N ae=)i3l;*~0D}M)yI<!UNtE1*4W&Z{XEZ^$@ delta 2551 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 ZDfSOtw2;46Y8LLitwsFk_su`0{|B&Nf<gcQ diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index d32b4991e..358c5d390 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -208,19 +208,15 @@ namespace Canvas3D { case 'pick': renderer.setViewport(0, 0, pickWidth, pickHeight); objectPickTarget.bind(); - renderer.clear() renderer.render(scene, 'pickObject'); instancePickTarget.bind(); - renderer.clear() renderer.render(scene, 'pickInstance'); groupPickTarget.bind(); - renderer.clear() renderer.render(scene, 'pickGroup'); break; case 'draw': webgl.unbindFramebuffer(); renderer.setViewport(0, 0, canvas.width, canvas.height); - renderer.clear() renderer.render(scene, variant); if (debugHelper.isEnabled) { debugHelper.syncVisibility() diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts index d2f524f2c..e446ad257 100644 --- a/src/mol-geo/util/marching-cubes/algorithm.ts +++ b/src/mol-geo/util/marching-cubes/algorithm.ts @@ -173,6 +173,8 @@ class MarchingCubesState { ); this.verticesOnEdges[edgeId] = id + 1; + // TODO cache scalarField differences for slices + // TODO make calculation optional const n0x = sfg(sf, Math.max(0, li - 1), lj, lk) - sfg(sf, Math.min(this.nX - 1, li + 1), lj, lk) const n0y = sfg(sf, li, Math.max(0, lj - 1), lk) - sfg(sf, li, Math.min(this.nY - 1, lj + 1), lk) const n0z = sfg(sf, li, lj, Math.max(0, lk - 1)) - sfg(sf, li, lj, Math.min(this.nZ, lk + 1)) diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts index 2705e2866..0a33038ec 100644 --- a/src/mol-gl/compute/histogram-pyramid/reduction.ts +++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts @@ -14,6 +14,7 @@ import { ValueCell } from 'mol-util'; import { QuadSchema, QuadValues } from '../util'; import { Vec2 } from 'mol-math/linear-algebra'; import { getHistopyramidSum } from './sum'; +import { Framebuffer, createFramebuffer } from 'mol-gl/webgl/framebuffer'; const HistopyramidReductionSchema = { ...QuadSchema, @@ -61,12 +62,31 @@ function getLevelTexture(ctx: WebGLContext, level: number) { return tex } +type TextureFramebuffer = { texture: Texture, framebuffer: Framebuffer } +const LevelTexturesFramebuffers: TextureFramebuffer[] = [] +function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) { + let textureFramebuffer = LevelTexturesFramebuffers[level] + const size = Math.pow(2, level) + if (textureFramebuffer === undefined) { + const texture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') + const framebuffer = createFramebuffer(ctx.gl, ctx.stats) + texture.attachFramebuffer(framebuffer, 0) + textureFramebuffer = { texture, framebuffer } + textureFramebuffer.texture.define(size, size) + LevelTexturesFramebuffers[level] = textureFramebuffer + } + return textureFramebuffer +} + function setRenderingDefaults(ctx: WebGLContext) { const { gl, state } = ctx state.disable(gl.CULL_FACE) state.disable(gl.BLEND) state.disable(gl.DEPTH_TEST) + state.disable(gl.SCISSOR_TEST) state.depthMask(false) + state.colorMask(true, true, true, true) + state.clearColor(0, 0, 0, 0) } export interface HistogramPyramid { @@ -98,16 +118,19 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture) const pyramidTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') pyramidTexture.define(maxSize, maxSize) - const levelTextures: Texture[] = [] - for (let i = 0; i < levels; ++i) levelTextures.push(getLevelTexture(ctx, i)) + const levelTexturesFramebuffers: TextureFramebuffer[] = [] + for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i)) const renderable = getHistopyramidReductionRenderable(ctx, initialTexture) + ctx.state.currentRenderItemId = -1 setRenderingDefaults(ctx) let offset = 0; for (let i = 0; i < levels; i++) { const currLevel = levels - 1 - i - levelTextures[currLevel].attachFramebuffer(framebuffer, 0) + const tf = levelTexturesFramebuffers[currLevel] + tf.framebuffer.bind() + // levelTextures[currLevel].attachFramebuffer(framebuffer, 0) const size = Math.pow(2, currLevel) // console.log('size', size, 'draw-level', currLevel, 'read-level', levels - i) @@ -116,9 +139,10 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture) ValueCell.update(renderable.values.uSize, Math.pow(2, i + 1) / maxSize) if (i > 0) { - ValueCell.update(renderable.values.tPreviousLevel, levelTextures[levels - i]) + ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture) renderable.update() } + ctx.state.currentRenderItemId = -1 renderable.render() pyramidTexture.bind(0) @@ -140,15 +164,18 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture) offset += size; } + gl.finish() + // printTexture(ctx, pyramidTexture, 2) // - const finalCount = getHistopyramidSum(ctx, levelTextures[0]) + const finalCount = getHistopyramidSum(ctx, levelTexturesFramebuffers[0].texture) const height = Math.ceil(finalCount / Math.pow(2, levels)) const scale = Vec2.create(maxSize / inputTexture.width, maxSize / inputTexture.height) // console.log('height', height, 'finalCount', finalCount, 'scale', scale) + return { pyramidTex: pyramidTexture, count: finalCount, diff --git a/src/mol-gl/compute/histogram-pyramid/sum.ts b/src/mol-gl/compute/histogram-pyramid/sum.ts index 2880d4f89..cc360e859 100644 --- a/src/mol-gl/compute/histogram-pyramid/sum.ts +++ b/src/mol-gl/compute/histogram-pyramid/sum.ts @@ -54,17 +54,33 @@ function getSumTexture(ctx: WebGLContext) { /** name for shared framebuffer used for histogram-pyramid operations */ const FramebufferName = 'histogram-pyramid-sum' +function setRenderingDefaults(ctx: WebGLContext) { + const { gl, state } = ctx + state.disable(gl.CULL_FACE) + state.disable(gl.BLEND) + state.disable(gl.DEPTH_TEST) + state.disable(gl.SCISSOR_TEST) + state.depthMask(false) + state.colorMask(true, true, true, true) + state.clearColor(0, 0, 0, 0) +} + const sumArray = new Uint8Array(4) export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) { const { gl, framebufferCache } = ctx const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture) + ctx.state.currentRenderItemId = -1 + const framebuffer = framebufferCache.get(FramebufferName).value const sumTexture = getSumTexture(ctx) sumTexture.attachFramebuffer(framebuffer, 0) + setRenderingDefaults(ctx) + gl.viewport(0, 0, 1, 1) renderable.render() + gl.finish() ctx.readPixels(0, 0, 1, 1, sumArray) ctx.unbindFramebuffer() diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts index cf1367a8b..a3d5ab5ab 100644 --- a/src/mol-gl/compute/marching-cubes/active-voxels.ts +++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts @@ -56,7 +56,10 @@ function setRenderingDefaults(ctx: WebGLContext) { state.disable(gl.CULL_FACE) state.disable(gl.BLEND) state.disable(gl.DEPTH_TEST) + state.disable(gl.SCISSOR_TEST) state.depthMask(false) + state.colorMask(true, true, true, true) + state.clearColor(0, 0, 0, 0) } export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDimensions: Vec3, isoValue: number) { @@ -70,6 +73,7 @@ export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDime activeVoxelsTex.define(width, height) const renderable = getActiveVoxelsRenderable(ctx, cornerTex, gridDimensions, isoValue) + ctx.state.currentRenderItemId = -1 activeVoxelsTex.attachFramebuffer(framebuffer, 0) setRenderingDefaults(ctx) @@ -78,5 +82,7 @@ export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDime // console.log('at', readTexture(ctx, activeVoxelsTex)) + gl.finish() + return activeVoxelsTex } \ No newline at end of file diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 24a4cb508..1cb195275 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -77,8 +77,10 @@ function setRenderingDefaults(ctx: WebGLContext) { state.disable(gl.CULL_FACE) state.disable(gl.BLEND) state.disable(gl.DEPTH_TEST) - state.depthMask(false) state.enable(gl.SCISSOR_TEST) + state.depthMask(false) + state.colorMask(true, true, true, true) + state.clearColor(0, 0, 0, 0) } export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDimensions: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) { @@ -87,11 +89,25 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex const framebuffer = framebufferCache.get(FramebufferName).value - if (!vertexGroupTexture) vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - vertexGroupTexture.define(pyramidTex.width, pyramidTex.height) + let needsClear = false + + if (!vertexGroupTexture) { + vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') + vertexGroupTexture.define(pyramidTex.width, pyramidTex.height) + } else if (vertexGroupTexture.width !== pyramidTex.width || vertexGroupTexture.height !== pyramidTex.height) { + vertexGroupTexture.define(pyramidTex.width, pyramidTex.height) + } else { + needsClear = true + } - if (!normalTexture) normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - normalTexture.define(pyramidTex.width, pyramidTex.height) + if (!normalTexture) { + normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') + normalTexture.define(pyramidTex.width, pyramidTex.height) + } else if (normalTexture.width !== pyramidTex.width || normalTexture.height !== pyramidTex.height) { + normalTexture.define(pyramidTex.width, pyramidTex.height) + } else { + needsClear = true + } // const infoTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') // infoTex.define(pyramidTex.width, pyramidTex.height) @@ -109,6 +125,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex // indexTex.define(pyramidTex.width, pyramidTex.height) const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDimensions, transform, isoValue, levels, scale, count) + ctx.state.currentRenderItemId = -1 vertexGroupTexture.attachFramebuffer(framebuffer, 0) normalTexture.attachFramebuffer(framebuffer, 1) @@ -133,9 +150,12 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex setRenderingDefaults(ctx) gl.viewport(0, 0, pyramidTex.width, pyramidTex.height) gl.scissor(0, 0, pyramidTex.width, height) + if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT) renderable.render() state.disable(gl.SCISSOR_TEST) + gl.finish() + // 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 581310917..bf14f95b6 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -64,6 +64,7 @@ namespace Renderer { const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props }) const viewport = Viewport() + const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor) const view = Mat4.clone(camera.view) const invView = Mat4.invert(Mat4.identity(), view) @@ -98,23 +99,14 @@ namespace Renderer { uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)), uFogNear: ValueCell.create(camera.state.fogNear), uFogFar: ValueCell.create(camera.state.fogFar), - uFogColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.backgroundColor)), + uFogColor: ValueCell.create(bgColor), uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold), } const globalUniformList = Object.entries(globalUniforms) - const [ bgRed, bgGreen, bgBlue ] = Color.toRgbNormalized(p.backgroundColor) - gl.clearColor(bgRed, bgGreen, bgBlue, 1.0) - - if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) { - p.backgroundColor = props.backgroundColor - const [ r, g, b ] = Color.toRgbNormalized(p.backgroundColor) - gl.clearColor(r, g, b, 1.0) - ValueCell.update(globalUniforms.uFogColor, Vec3.set(globalUniforms.uFogColor.ref.value, r, g, b)) - } - let globalUniformsNeedUpdate = true + const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => { const program = r.getProgram(variant) if (r.state.visible) { @@ -175,13 +167,19 @@ namespace Renderer { ValueCell.update(globalUniforms.uFogNear, camera.state.fogNear) globalUniformsNeedUpdate = true + state.currentRenderItemId = -1 const { renderables } = scene + state.disable(gl.SCISSOR_TEST) + state.disable(gl.BLEND) + state.depthMask(true) + state.colorMask(true, true, true, true) + state.enable(gl.DEPTH_TEST) + state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0) + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) + if (variant === 'draw') { - 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) @@ -196,9 +194,6 @@ namespace Renderer { } } else { // picking - 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) } @@ -210,6 +205,7 @@ namespace Renderer { return { clear: () => { state.depthMask(true) + state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0) gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) }, render, @@ -221,9 +217,8 @@ namespace Renderer { } if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) { p.backgroundColor = props.backgroundColor - const [ r, g, b ] = Color.toRgbNormalized(p.backgroundColor) - gl.clearColor(r, g, b, 1.0) - ValueCell.update(globalUniforms.uFogColor, Vec3.set(globalUniforms.uFogColor.ref.value, r, g, b)) + Color.toVec3Normalized(bgColor, p.backgroundColor) + ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor)) } if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) { p.lightIntensity = props.lightIntensity diff --git a/src/mol-gl/shader/gaussian-density.frag b/src/mol-gl/shader/gaussian-density.frag index a78f893d0..2e7c82d3a 100644 --- a/src/mol-gl/shader/gaussian-density.frag +++ b/src/mol-gl/shader/gaussian-density.frag @@ -28,8 +28,6 @@ varying float vRadius; #pragma glslify: texture3dFrom2dNearest = require(./utils/texture3d-from-2d-nearest.glsl, intMod=intMod, intDiv=intDiv, foo=foo) // foo=foo is a workaround for a bug in glslify uniform vec3 uBboxSize; -uniform vec3 uBboxMin; -uniform vec3 uBboxMax; uniform vec3 uGridDim; uniform float uCurrentSlice; uniform float uCurrentX; @@ -56,7 +54,7 @@ void main() { #if defined(dCalcType_density) float radiusSq = vRadius * vRadius; float density = exp(-uAlpha * ((dist * dist) / radiusSq)); - gl_FragColor = vec4(density); + gl_FragColor.a = density; #elif defined(dCalcType_minDistance) gl_FragColor.a = 10000.0 - dist; // gl_FragColor.a = 1.0 - encodeFloatLog(dist); diff --git a/src/mol-gl/shader/gaussian-density.vert b/src/mol-gl/shader/gaussian-density.vert index f75571829..6db1ff7c8 100644 --- a/src/mol-gl/shader/gaussian-density.vert +++ b/src/mol-gl/shader/gaussian-density.vert @@ -20,7 +20,6 @@ varying float vRadius; uniform vec3 uBboxSize; uniform vec3 uBboxMin; -uniform vec3 uBboxMax; uniform vec3 uGridDim; uniform float uCurrentSlice; @@ -29,7 +28,7 @@ void main() { #if defined(dCalcType_groupId) vGroup = aGroup; #endif - float scale = max(uBboxSize.z, max(uBboxSize.x, uBboxSize.y)); + float scale = max(uBboxSize.x, uBboxSize.y); gl_PointSize = (vRadius / scale) * max(uGridDim.x, uGridDim.y) * 6.0; vPosition = (aPosition - uBboxMin) / uBboxSize; gl_Position = vec4(vPosition * 2.0 - 1.0, 1.0); diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index 09f567e25..52e957e04 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -245,6 +245,7 @@ function createStats(): WebGLStats { export type WebGLState = { currentProgramId: number currentMaterialId: number + currentRenderItemId: number enable: (cap: number) => void disable: (cap: number) => void @@ -252,6 +253,8 @@ export type WebGLState = { frontFace: (mode: number) => void cullFace: (mode: number) => void depthMask: (flag: boolean) => void + colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => void + clearColor: (red: number, green: number, blue: number, alpha: number) => void blendFunc: (src: number, dst: number) => void blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => void @@ -266,6 +269,8 @@ function createState(gl: GLRenderingContext): WebGLState { let currentFrontFace = gl.getParameter(gl.FRONT_FACE) let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE) let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK) + let currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK) + let currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE) let currentBlendSrcRGB = gl.getParameter(gl.BLEND_SRC_RGB) let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB) @@ -278,6 +283,7 @@ function createState(gl: GLRenderingContext): WebGLState { return { currentProgramId: -1, currentMaterialId: -1, + currentRenderItemId: -1, enable: (cap: number) => { if (enabledCapabilities[cap] !== true ) { @@ -310,6 +316,22 @@ function createState(gl: GLRenderingContext): WebGLState { currentDepthMask = flag } }, + colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => { + if (red !== currentColorMask[0] || green !== currentColorMask[1] || blue !== currentColorMask[2] || alpha !== currentColorMask[3]) + gl.colorMask(red, green, blue, alpha) + currentColorMask[0] = red + currentColorMask[1] = green + currentColorMask[2] = blue + currentColorMask[3] = alpha + }, + clearColor: (red: number, green: number, blue: number, alpha: number) => { + if (red !== currentClearColor[0] || green !== currentClearColor[1] || blue !== currentClearColor[2] || alpha !== currentClearColor[3]) + gl.clearColor(red, green, blue, alpha) + currentClearColor[0] = red + currentClearColor[1] = green + currentClearColor[2] = blue + currentClearColor[3] = alpha + }, blendFunc: (src: number, dst: number) => { if (src !== currentBlendSrcRGB || dst !== currentBlendDstRGB || src !== currentBlendSrcAlpha || dst !== currentBlendDstAlpha) { diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index e196b6ee0..b516fb809 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -96,8 +96,8 @@ export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, } export type ComputeRenderItem = RenderItem<keyof typeof ComputeRenderVariantDefines & string> -export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues) { - return createRenderItem(ctx, drawMode, shaderCode, schema, values, -1, ComputeRenderVariantDefines) +export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId = -1) { + return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, ComputeRenderVariantDefines) } /** @@ -164,25 +164,31 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof render: (variant: S) => { if (drawCount === 0 || instanceCount === 0) return const program = programs[variant].value - const vertexArray = vertexArrays[variant] - if (program.id !== state.currentProgramId || program.id !== currentProgramId || - materialId === -1 || materialId !== state.currentMaterialId - ) { - // console.log('program.id changed or materialId changed/-1', materialId) - if (program.id !== state.currentProgramId) program.use() - program.setUniforms(materialUniformValueEntries) - state.currentMaterialId = materialId - currentProgramId = program.id - } - program.setUniforms(uniformValueEntries) - program.bindTextures(textures) - if (vertexArrayObject && vertexArray) { - vertexArrayObject.bindVertexArray(vertexArray) - // need to bind elements buffer explicitly since it is not always recorded in the VAO - if (elementsBuffer) elementsBuffer.bind() + if (program.id === currentProgramId && state.currentRenderItemId === id) { + program.setUniforms(uniformValueEntries) + program.bindTextures(textures) } else { - if (elementsBuffer) elementsBuffer.bind() - program.bindAttributes(attributeBuffers) + const vertexArray = vertexArrays[variant] + if (program.id !== state.currentProgramId || program.id !== currentProgramId || + materialId === -1 || materialId !== state.currentMaterialId + ) { + // console.log('program.id changed or materialId changed/-1', materialId) + if (program.id !== state.currentProgramId) program.use() + program.setUniforms(materialUniformValueEntries) + state.currentMaterialId = materialId + currentProgramId = program.id + } + program.setUniforms(uniformValueEntries) + program.bindTextures(textures) + if (vertexArrayObject && vertexArray) { + vertexArrayObject.bindVertexArray(vertexArray) + // need to bind elements buffer explicitly since it is not always recorded in the VAO + if (elementsBuffer) elementsBuffer.bind() + } else { + if (elementsBuffer) elementsBuffer.bind() + program.bindAttributes(attributeBuffers) + } + state.currentRenderItemId = id } if (isDebugMode) { checkFramebufferStatus(ctx.gl) diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 8036d65c3..448f6dfa2 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -30,12 +30,11 @@ export const GaussianDensitySchema = { uCurrentSlice: UniformSpec('f'), uCurrentX: UniformSpec('f'), uCurrentY: UniformSpec('f'), - uBboxMin: UniformSpec('v3'), - uBboxMax: UniformSpec('v3'), - uBboxSize: UniformSpec('v3'), - uGridDim: UniformSpec('v3'), - uGridTexDim: UniformSpec('v3'), - uAlpha: UniformSpec('f'), + uBboxMin: UniformSpec('v3', true), + uBboxSize: UniformSpec('v3', true), + uGridDim: UniformSpec('v3', true), + uGridTexDim: UniformSpec('v3', true), + uAlpha: UniformSpec('f', true), tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'), dGridTexType: DefineSpec('string', ['2d', '3d']), @@ -106,7 +105,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat // - const { gl, framebufferCache } = webgl + const { gl, framebufferCache, state } = webgl const { uCurrentSlice, uCurrentX, uCurrentY } = renderable.values const framebuffer = framebufferCache.get(FramebufferName).value @@ -116,8 +115,12 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat if (!texture) texture = createTexture(webgl, 'image-float32', 'rgba', 'float', 'nearest') texture.define(texDimX, texDimY) - function render(fbTex: Texture) { + // console.log(renderable) + + function render(fbTex: Texture, clear: boolean) { + state.currentRenderItemId = -1 fbTex.attachFramebuffer(framebuffer, 0) + if (clear) gl.clear(gl.COLOR_BUFFER_BIT) let currCol = 0 let currY = 0 let currX = 0 @@ -126,25 +129,31 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat currCol -= texCols currY += dy currX = 0 + ValueCell.update(uCurrentY, currY) } - gl.viewport(currX, currY, dx, dy) - ValueCell.update(uCurrentSlice, i) + // console.log({ i, currX, currY }) ValueCell.update(uCurrentX, currX) - ValueCell.update(uCurrentY, currY) + ValueCell.update(uCurrentSlice, i) + gl.viewport(currX, currY, dx, dy) renderable.render() ++currCol currX += dx } } - setupMinDistanceRendering(webgl, renderable) - render(minDistanceTexture) - setupDensityRendering(webgl, renderable) - render(texture) + render(texture, true) + + setupMinDistanceRendering(webgl, renderable) + render(minDistanceTexture, true) + gl.finish() setupGroupIdRendering(webgl, renderable) - render(texture) + render(texture, false) + + // printTexture(webgl, texture, 1) + + gl.finish() return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim } } @@ -246,7 +255,6 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po uCurrentX: ValueCell.create(0), uCurrentY: ValueCell.create(0), uBboxMin: ValueCell.create(box.min), - uBboxMax: ValueCell.create(box.max), uBboxSize: ValueCell.create(extent), uGridDim: ValueCell.create(dimensions), uGridTexDim: ValueCell.create(Vec3.create(texDimX, texDimY, 0)), @@ -267,15 +275,18 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po 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) + state.disable(gl.DEPTH_TEST) + state.disable(gl.SCISSOR_TEST) + state.depthMask(false) + state.clearColor(0, 0, 0, 0) } function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) { const { gl, state } = webgl ValueCell.update(renderable.values.dCalcType, 'minDistance') renderable.update() + state.colorMask(false, false, false, true) state.blendFunc(gl.ONE, gl.ONE) // the shader writes 1 - dist so we set blending to MAX state.blendEquation(webgl.extensions.blendMinMax.MAX) @@ -285,6 +296,7 @@ function setupDensityRendering(webgl: WebGLContext, renderable: ComputeRenderabl const { gl, state } = webgl ValueCell.update(renderable.values.dCalcType, 'density') renderable.update() + state.colorMask(false, false, false, true) state.blendFunc(gl.ONE, gl.ONE) state.blendEquation(gl.FUNC_ADD) } @@ -294,7 +306,8 @@ function setupGroupIdRendering(webgl: WebGLContext, renderable: ComputeRenderabl ValueCell.update(renderable.values.dCalcType, 'groupId') renderable.update() // overwrite color, don't change alpha - state.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) + state.colorMask(true, true, true, false) + state.blendFunc(gl.ONE, gl.ZERO) state.blendEquation(gl.FUNC_ADD) } diff --git a/src/tests/browser/marching-cubes.ts b/src/tests/browser/marching-cubes.ts index a0ee2f538..783fd9dc4 100644 --- a/src/tests/browser/marching-cubes.ts +++ b/src/tests/browser/marching-cubes.ts @@ -56,7 +56,7 @@ async function init() { } const isoValue = Math.exp(-props.smoothness) - if (false) { + if (true) { console.time('gpu gaussian2') const densityTextureData2 = await computeGaussianDensityTexture2d(position, box, radius, props, webgl).run() webgl.waitForGpuCommandsCompleteSync() @@ -119,7 +119,7 @@ async function init() { // console.time('cpu gaussian') - const densityData = await computeGaussianDensity(position, box, radius, { ...props, useGpu: false }, webgl).run() + const densityData = await computeGaussianDensity(position, box, radius, { ...props, useGpu: true }, webgl).run() console.timeEnd('cpu gaussian') // console.log({ densityData }) @@ -134,7 +134,6 @@ async function init() { console.timeEnd('cpu mc') // console.log('surface', surface) Mesh.transformImmediate(surface, densityData.transform) - Mesh.computeNormalsImmediate(surface) const meshProps = { doubleSided: true, flatShaded: false, alpha: 1.0 } const meshValues = Mesh.Utils.createValuesSimple(surface, meshProps, Color(0x995511), 1) const meshState = Mesh.Utils.createRenderableState(meshProps) -- GitLab