Browse Source

wip, gpu mc

- reduced texture sizes
- structure gaussian surface texture-mesh
Alexander Rose 4 years ago
parent
commit
459c5aa5a7

+ 11 - 8
src/mol-gl/compute/histogram-pyramid/reduction.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -24,6 +24,7 @@ const HistopyramidReductionSchema = {
     tPreviousLevel: TextureSpec('texture', 'rgba', 'float', 'nearest'),
     uSize: UniformSpec('f'),
     uTexSize: UniformSpec('f'),
+    uFirst: UniformSpec('b'),
 };
 
 const HistogramPyramidName = 'histogram-pyramid';
@@ -47,6 +48,7 @@ function createHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture
         tPreviousLevel: ValueCell.create(initialTexture),
         uSize: ValueCell.create(0),
         uTexSize: ValueCell.create(0),
+        uFirst: ValueCell.create(true),
     };
 
     const schema = { ...HistopyramidReductionSchema };
@@ -109,16 +111,18 @@ export interface HistogramPyramid {
 
 export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
     const { gl } = ctx;
+    const w = inputTexture.getWidth();
+    const h = inputTexture.getHeight();
 
     // printTexture(ctx, inputTexture, 2)
-    if (inputTexture.getWidth() !== inputTexture.getHeight() || !isPowerOfTwo(inputTexture.getWidth())) {
+    if (w !== h || !isPowerOfTwo(w)) {
         throw new Error('inputTexture must be of square power-of-two size');
     }
 
     // This part set the levels
-    const levels = Math.ceil(Math.log(inputTexture.getWidth()) / Math.log(2));
+    const levels = Math.ceil(Math.log(w) / Math.log(2));
     const maxSize = Math.pow(2, levels);
-    // console.log('levels', levels, 'maxSize', maxSize, 'input', inputTexture.getWidth());
+    // console.log('levels', levels, 'maxSize', maxSize, 'input', w);
 
     const pyramidTexture = getTexture('pyramid', ctx, 'image-float32', 'rgba', 'float', 'nearest');
     pyramidTexture.define(maxSize, maxSize);
@@ -140,13 +144,13 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
         const currLevel = levels - 1 - i;
         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)
 
         ValueCell.update(renderable.values.uSize, Math.pow(2, i + 1) / maxSize);
         ValueCell.update(renderable.values.uTexSize, size);
+        ValueCell.updateIfChanged(renderable.values.uFirst, i === 0);
         if (i > 0) {
             ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture);
             renderable.update();
@@ -173,9 +177,8 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
 
     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)
-
+    // const scale = Vec2.create(maxSize / inputTexture.width, maxSize / inputTexture.height);
+    // console.log('height', height, 'finalCount', finalCount, 'scale', scale);
 
     return {
         pyramidTex: pyramidTexture,

+ 2 - 2
src/mol-gl/compute/marching-cubes/active-voxels.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -93,7 +93,7 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
     framebuffer.bind();
 
     if (!ctx.namedTextures[ActiveVoxelsName]) {
-        ctx.namedTextures[ActiveVoxelsName] = resources.texture('image-float32', 'rgba', 'float', 'nearest');
+        ctx.namedTextures[ActiveVoxelsName] = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
     }
     const activeVoxelsTex = ctx.namedTextures[ActiveVoxelsName];
     activeVoxelsTex.define(width, height);

+ 5 - 9
src/mol-gl/compute/marching-cubes/isosurface.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -44,7 +44,6 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
     if (ctx.namedComputeRenderables[IsosurfaceName]) {
         const v = ctx.namedComputeRenderables[IsosurfaceName].values;
 
-        ValueCell.update(v.uQuadScale, Vec2.create(1, height / Math.pow(2, levels)));
         ValueCell.update(v.tActiveVoxelsPyramid, activeVoxelsPyramid);
         ValueCell.update(v.tActiveVoxelsBase, activeVoxelsBase);
         ValueCell.update(v.tVolumeData, volumeData);
@@ -72,7 +71,6 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
         ...QuadValues,
         tTriIndices: ValueCell.create(getTriIndices()),
 
-        uQuadScale: ValueCell.create(Vec2.create(1, height / Math.pow(2, levels))),
         tActiveVoxelsPyramid: ValueCell.create(activeVoxelsPyramid),
         tActiveVoxelsBase: ValueCell.create(activeVoxelsBase),
         tVolumeData: ValueCell.create(volumeData),
@@ -109,6 +107,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
 export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) {
     const { gl, resources } = ctx;
     const { pyramidTex, height, levels, scale, count } = histogramPyramid;
+    const width = pyramidTex.getWidth();
 
     // console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim)
     // console.log('iso volumeData', volumeData)
@@ -118,18 +117,15 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     }
     const framebuffer = ctx.namedFramebuffers[IsosurfaceName];
 
-    const w = pyramidTex.getWidth();
-    const h = pyramidTex.getHeight();
-
     if (!vertexGroupTexture) {
         vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
     }
-    vertexGroupTexture.define(w, h);
+    vertexGroupTexture.define(width, height);
 
     if (!normalTexture) {
         normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
     }
-    normalTexture.define(w, h);
+    normalTexture.define(width, height);
 
     // const infoTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
     // infoTex.define(pyramidTex.width, pyramidTex.height)
@@ -170,7 +166,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     ]);
 
     setRenderingDefaults(ctx);
-    gl.viewport(0, 0, w, h);
+    gl.viewport(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
     renderable.render();
 

+ 16 - 5
src/mol-gl/shader/histogram-pyramid/reduction.frag.ts

@@ -5,17 +5,28 @@ precision highp sampler2D;
 // input texture (previous level used to evaluate the new level)
 uniform sampler2D tPreviousLevel;
 
-// 1/size of the previous level texture.
+// inverted size of the previous level texture.
 uniform float uSize;
 uniform float uTexSize;
+uniform bool uFirst;
 
 void main(void) {
     float k = 0.5 * uSize;
     vec2 position = floor((gl_FragCoord.xy / uTexSize) / uSize) * uSize;
-    float a = texture2D(tPreviousLevel, position).r;
-    float b = texture2D(tPreviousLevel, position + vec2(k, 0.)).r;
-    float c = texture2D(tPreviousLevel, position + vec2(0., k)).r;
-    float d = texture2D(tPreviousLevel, position + vec2(k, k)).r;
+    float a, b, c, d;
+
+    if (uFirst) {
+        a = texture2D(tPreviousLevel, position).r * 255.0;
+        b = texture2D(tPreviousLevel, position + vec2(k, 0.0)).r * 255.0;
+        c = texture2D(tPreviousLevel, position + vec2(0.0, k)).r * 255.0;
+        d = texture2D(tPreviousLevel, position + vec2(k, k)).r * 255.0;
+    } else {
+        a = texture2D(tPreviousLevel, position).r;
+        b = texture2D(tPreviousLevel, position + vec2(k, 0.0)).r;
+        c = texture2D(tPreviousLevel, position + vec2(0.0, k)).r;
+        d = texture2D(tPreviousLevel, position + vec2(k, k)).r;
+    }
+
     gl_FragColor.a = a;
     gl_FragColor.b = a + b;
     gl_FragColor.g = gl_FragColor.b + c;

+ 1 - 2
src/mol-gl/shader/marching-cubes/active-voxels.frag.ts

@@ -37,7 +37,6 @@ vec4 texture3dFrom2dNearest(sampler2D tex, vec3 pos, vec3 gridDim, vec2 texDim)
     float column = intMod(zSlice * gridDim.x, texDim.x) / gridDim.x;
     float row = floor(intDiv(zSlice * gridDim.x, texDim.x));
     vec2 coord = (vec2(column * gridDim.x, row * gridDim.y) + (pos.xy * gridDim.xy)) / (texDim / uScale);
-    // return texture2D(tex, coord + 0.5 / texDim);
     return texture2D(tex, coord);
 }
 
@@ -62,7 +61,7 @@ void main(void) {
 
     // get total triangles to generate for calculated MC case from triCount texture
     float totalTrianglesToGenerate = texture2D(tTriCount, vec2(intMod(c, 16.), floor(c / 16.)) / 16.).a;
-    gl_FragColor = vec4(vec3(floor(totalTrianglesToGenerate * 255.0 + 0.5) * 3.0), c);
+    gl_FragColor = vec4(vec3(totalTrianglesToGenerate * 3.0), c / 255.0);
 
     // gl_FragColor = vec4(255.0, 0.0, 0.0, voxel(posXYZ + c4 / uGridDim).a * 255.0);
     // gl_FragColor = vec4(255.0, 0.0, 0.0, voxel(posXYZ).a * 255.0);

+ 2 - 5
src/mol-gl/shader/marching-cubes/isosurface.frag.ts

@@ -19,8 +19,6 @@ uniform mat4 uGridTransform;
 // scale to volume data coord
 uniform vec2 uScale;
 
-// varying vec2 vCoordinate;
-
 #include common
 
 // cube corners
@@ -50,7 +48,6 @@ vec4 texture3dFrom2dNearest(sampler2D tex, vec3 pos, vec3 gridDim, vec2 texDim)
     float row = floor(intDiv(zSlice * gridDim.x, texDim.x));
     vec2 coord = (vec2(column * gridDim.x, row * gridDim.y) + (pos.xy * gridDim.xy)) / (texDim / uScale);
     return texture2D(tex, coord + 0.5 / (texDim / uScale));
-    // return texture2D(tex, coord);
 }
 
 vec4 voxel(vec3 pos) {
@@ -78,7 +75,7 @@ void main(void) {
     vec4 vI4 = vec4(vI);
 
     // traverse the different levels of the pyramid
-    for(int i = 1; i < 12; i++) {
+    for(int i = 1; i < 14; i++) {
         if(float(i) >= uLevels) break;
 
         offset -= diff;
@@ -103,7 +100,7 @@ void main(void) {
     vec2 coord2d = position / uScale;
     vec3 coord3d = floor(index3dFrom2d(coord2d) + 0.5);
 
-    float edgeIndex = floor(texture2D(tActiveVoxelsBase, position).a + 0.5);
+    float edgeIndex = floor(texture2D(tActiveVoxelsBase, position).a * 255.0 + 0.5);
 
     // current vertex for the up to 15 MC cases
     float currentVertex = vI - dot(m, starts);

+ 21 - 2
src/mol-repr/structure/complex-visual.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -29,9 +29,10 @@ import { Text } from '../../mol-geo/geometry/text/text';
 import { SizeTheme } from '../../mol-theme/size';
 import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
 import { createMarkers } from '../../mol-geo/geometry/marker-data';
-import { StructureParams, StructureMeshParams, StructureTextParams, StructureDirectVolumeParams, StructureLinesParams } from './params';
+import { StructureParams, StructureMeshParams, StructureTextParams, StructureDirectVolumeParams, StructureLinesParams, StructureTextureMeshParams } from './params';
 import { Clipping } from '../../mol-theme/clipping';
 import { Lines } from '../../mol-geo/geometry/lines/lines';
+import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
 
 export interface  ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
 
@@ -325,4 +326,22 @@ export function ComplexDirectVolumeVisual<P extends ComplexDirectVolumeParams>(b
         },
         geometryUtils: DirectVolume.Utils
     }, materialId);
+}
+
+// texture-mesh
+
+export const ComplexTextureMeshParams = { ...StructureTextureMeshParams, ...StructureParams };
+export type ComplexTextureMeshParams = typeof ComplexTextureMeshParams
+
+export interface ComplexTextureMeshVisualBuilder<P extends ComplexTextureMeshParams> extends ComplexVisualBuilder<P, TextureMesh> { }
+
+export function ComplexTextureMeshVisual<P extends ComplexTextureMeshParams>(builder: ComplexTextureMeshVisualBuilder<P>, materialId: number): ComplexVisual<P> {
+    return ComplexVisual<TextureMesh, P>({
+        ...builder,
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructure: Structure, currentStructure: Structure) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructure, currentStructure);
+            if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true;
+        },
+        geometryUtils: TextureMesh.Utils
+    }, materialId);
 }

+ 3 - 1
src/mol-repr/structure/representation/gaussian-surface.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -17,6 +17,8 @@ const GaussianSurfaceVisuals = {
     'gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface mesh', ctx, getParams, GaussianSurfaceMeshVisual),
     'structure-gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, StructureGaussianSurfaceMeshParams>) => ComplexRepresentation('Structure-Gaussian surface mesh', ctx, getParams, StructureGaussianSurfaceMeshVisual),
     'gaussian-surface-texture-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface texture-mesh', ctx, getParams, GaussianSurfaceTextureMeshVisual),
+    // TODO: don't enable yet as it breaks state sessions
+    // 'structure-gaussian-surface-texture-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, StructureGaussianSurfaceMeshParams>) => ComplexRepresentation('Structure-Gaussian surface texture-mesh', ctx, getParams, StructureGaussianSurfaceTextureMeshVisual),
     'gaussian-surface-wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianWireframeParams>) => UnitsRepresentation('Gaussian surface wireframe', ctx, getParams, GaussianWireframeVisual),
 };
 

+ 78 - 10
src/mol-repr/structure/visual/gaussian-surface-mesh.ts

@@ -1,12 +1,12 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual, UnitsTextureMeshVisual } from '../units-visual';
-import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps, computeStructureGaussianDensity } from './util/gaussian';
+import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps, computeStructureGaussianDensity, computeStructureGaussianDensityTexture2d } from './util/gaussian';
 import { VisualContext } from '../../visual';
 import { Unit, Structure } from '../../../mol-model/structure';
 import { Theme } from '../../../mol-theme/theme';
@@ -19,7 +19,7 @@ import { calcActiveVoxels } from '../../../mol-gl/compute/marching-cubes/active-
 import { createHistogramPyramid } from '../../../mol-gl/compute/histogram-pyramid/reduction';
 import { createIsosurfaceBuffers } from '../../../mol-gl/compute/marching-cubes/isosurface';
 import { Sphere3D } from '../../../mol-math/geometry';
-import { ComplexVisual, ComplexMeshParams, ComplexMeshVisual } from '../complex-visual';
+import { ComplexVisual, ComplexMeshParams, ComplexMeshVisual, ComplexTextureMeshVisual } from '../complex-visual';
 import { getUnitExtraRadius, getStructureExtraRadius } from './util/common';
 
 export const GaussianSurfaceMeshParams = {
@@ -124,17 +124,19 @@ const GaussianSurfaceName = 'gaussian-surface';
 async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityTextureProps, textureMesh?: TextureMesh): Promise<TextureMesh> {
     if (!ctx.webgl) throw new Error('webgl context required to create gaussian surface texture-mesh');
 
-    const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat } } = ctx.webgl;
+    const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat, colorBufferHalfFloat, textureHalfFloat } } = ctx.webgl;
     if (!namedTextures[GaussianSurfaceName]) {
-        namedTextures[GaussianSurfaceName] = colorBufferFloat && textureFloat
-            ? resources.texture('image-float32', 'rgba', 'float', 'linear')
-            : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
+        namedTextures[GaussianSurfaceName] = colorBufferHalfFloat && textureHalfFloat
+            ? resources.texture('image-float16', 'rgba', 'fp16', 'linear')
+            : colorBufferFloat && textureFloat
+                ? resources.texture('image-float32', 'rgba', 'float', 'linear')
+                : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
     }
 
     // console.time('computeUnitGaussianDensityTexture2d');
     const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
-    // console.log(densityTextureData)
-    // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture))
+    // console.log(densityTextureData);
+    // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture));
     // ctx.webgl.waitForGpuCommandsCompleteSync();
     // console.timeEnd('computeUnitGaussianDensityTexture2d');
 
@@ -162,7 +164,7 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit,
     //     framebuffers: ctx.webgl.namedFramebuffers,
     //     textures: ctx.webgl.namedTextures,
     // });
-    // ctx.webgl.waitForGpuCommandsCompleteSync()
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
     return surface;
 }
 
@@ -182,4 +184,70 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua
             if (newProps.includeParent !== currentProps.includeParent) state.createGeometry = true;
         }
     }, materialId);
+}
+
+//
+
+async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityTextureProps, textureMesh?: TextureMesh): Promise<TextureMesh> {
+    if (!ctx.webgl) throw new Error('webgl context required to create structure gaussian surface texture-mesh');
+
+    const { namedTextures, resources, extensions: { colorBufferFloat, textureFloat, colorBufferHalfFloat, textureHalfFloat } } = ctx.webgl;
+    if (!namedTextures[GaussianSurfaceName]) {
+        namedTextures[GaussianSurfaceName] = colorBufferHalfFloat && textureHalfFloat
+            ? resources.texture('image-float16', 'rgba', 'fp16', 'linear')
+            : colorBufferFloat && textureFloat
+                ? resources.texture('image-float32', 'rgba', 'float', 'linear')
+                : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
+    }
+
+    // console.time('computeUnitGaussianDensityTexture2d');
+    const densityTextureData = await computeStructureGaussianDensityTexture2d(structure, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
+    // console.log(densityTextureData);
+    // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture));
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('computeUnitGaussianDensityTexture2d');
+
+    const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
+
+    // console.time('calcActiveVoxels');
+    const activeVoxelsTex = calcActiveVoxels(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, isoLevel, densityTextureData.gridTexScale);
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('calcActiveVoxels');
+
+    // console.time('createHistogramPyramid');
+    const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('createHistogramPyramid');
+
+    // console.time('createIsosurfaceBuffers');
+    const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined);
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('createIsosurfaceBuffers');
+
+    const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
+    const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, boundingSphere, textureMesh);
+    // console.log({
+    //     renderables: ctx.webgl.namedComputeRenderables,
+    //     framebuffers: ctx.webgl.namedFramebuffers,
+    //     textures: ctx.webgl.namedTextures,
+    // });
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    return surface;
+}
+
+export function StructureGaussianSurfaceTextureMeshVisual(materialId: number): ComplexVisual<StructureGaussianSurfaceMeshParams> {
+    return ComplexTextureMeshVisual<StructureGaussianSurfaceMeshParams>({
+        defaultProps: PD.getDefaultValues(StructureGaussianSurfaceMeshParams),
+        createGeometry: createStructureGaussianSurfaceTextureMesh,
+        createLocationIterator: ElementIterator.fromStructure,
+        getLoci: getSerialElementLoci,
+        eachLocation: eachSerialElement,
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<StructureGaussianSurfaceMeshParams>, currentProps: PD.Values<StructureGaussianSurfaceMeshParams>) => {
+            if (newProps.resolution !== currentProps.resolution) state.createGeometry = true;
+            if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
+            if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
+            if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
+        }
+    }, materialId);
 }

+ 9 - 0
src/mol-repr/structure/visual/util/gaussian.ts

@@ -82,3 +82,12 @@ export function computeStructureGaussianDensityTexture(structure: Structure, pro
         return GaussianDensityTexture(webgl, position, box, radius, p, texture);
     });
 }
+
+export function computeStructureGaussianDensityTexture2d(structure: Structure, powerOfTwo: boolean, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
+    const { box } = structure.lookup3d.boundary;
+    const p = ensureReasonableResolution(box, props);
+    const { position, radius } = getStructureConformationAndRadius(structure, props.ignoreHydrogens, props.traceOnly);
+    return Task.create('Gaussian Density', async ctx => {
+        return GaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, p, texture);
+    });
+}