Browse Source

wip, gpu gaussian density

Alexander Rose 6 năm trước cách đây
mục cha
commit
79dba8eabc

+ 0 - 1
src/mol-geo/representation/structure/visual/gaussian-density-point.ts

@@ -69,7 +69,6 @@ export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointPr
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
-            if (newProps.readSlices !== currentProps.readSlices) state.createGeometry = true
             if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true
         }
     })

+ 0 - 1
src/mol-geo/representation/structure/visual/gaussian-density-volume.ts

@@ -68,7 +68,6 @@ export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolume
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
-            if (newProps.readSlices !== currentProps.readSlices) state.createGeometry = true
             if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true
         }
     })

+ 0 - 1
src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts

@@ -93,7 +93,6 @@ export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> {
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
-            if (newProps.readSlices !== currentProps.readSlices) state.createGeometry = true
             if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true
         }
     })

+ 0 - 1
src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts

@@ -53,7 +53,6 @@ export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeProps> {
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
-            if (newProps.readSlices !== currentProps.readSlices) state.createGeometry = true
             if (newProps.ignoreCache !== currentProps.ignoreCache) state.createGeometry = true
         }
     })

+ 17 - 4
src/mol-gl/webgl/texture.ts

@@ -114,7 +114,11 @@ export interface Texture {
     readonly internalFormat: number
     readonly type: number
 
-    define: (x: number, y: number, z: number) => void
+    readonly width: number
+    readonly height: number
+    readonly depth: number
+
+    define: (width: number, height: number, depth?: number) => void
     load: (image: TextureImage<any>) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
@@ -151,6 +155,8 @@ export function createTexture(ctx: Context, kind: TextureKind, _format: TextureF
     gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     gl.bindTexture(target, null)
 
+    let width = 0, height = 0, depth = 0
+
     let destroyed = false
     ctx.textureCount += 1
 
@@ -161,7 +167,12 @@ export function createTexture(ctx: Context, kind: TextureKind, _format: TextureF
         internalFormat,
         type,
 
-        define: (width: number, height: number, depth?: number) => {
+        get width () { return width },
+        get height () { return height },
+        get depth () { return depth },
+
+        define: (_width: number, _height: number, _depth?: number) => {
+            width = _width, height = _height, depth = _depth || 0
             gl.bindTexture(target, texture)
             if (target === gl.TEXTURE_2D) {
                 // TODO remove cast when webgl2 types are fixed
@@ -178,11 +189,13 @@ export function createTexture(ctx: Context, kind: TextureKind, _format: TextureF
             gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
             gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
             if (target === gl.TEXTURE_2D) {
-                const { array, width, height } = data as TextureImage<any>;
+                const { array, width: _width, height: _height } = data as TextureImage<any>
+                width = _width, height = _height;
                 // TODO remove cast when webgl2 types are fixed
                 (gl as WebGLRenderingContext).texImage2D(target, 0, internalFormat, width, height, 0, format, type, array)
             } else if (target === (gl as WebGL2RenderingContext).TEXTURE_3D) {
-                const { array, width, height, depth } = data as TextureVolume<any>;
+                const { array, width: _width, height: _height, depth: _depth } = data as TextureVolume<any>
+                width = _width, height = _height, depth = _depth;
                 (gl as WebGL2RenderingContext).texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, array)
             } else {
                 throw new Error('unknown texture target')

+ 0 - 1
src/mol-math/geometry/gaussian-density.ts

@@ -19,7 +19,6 @@ export const DefaultGaussianDensityProps = {
     resolution: 1,
     radiusOffset: 0,
     smoothness: 1.5,
-    readSlices: false,
     useGpu: true,
 }
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps

+ 180 - 196
src/mol-math/geometry/gaussian-density/gpu.ts

@@ -21,143 +21,48 @@ import { createFramebuffer } from 'mol-gl/webgl/framebuffer';
 import { createTexture, Texture, TextureAttachment } from 'mol-gl/webgl/texture';
 import { GLRenderingContext } from 'mol-gl/webgl/compat';
 
-let webglContext: Context
-function getWebGLContext() {
-    if (webglContext) return webglContext
-    const canvas = document.createElement('canvas')
-    const gl = getGLContext(canvas, {
-        alpha: true,
-        antialias: false,
-        depth: false,
-        preserveDrawingBuffer: true
-    })
-    if (!gl) throw new Error('Could not create a WebGL rendering context')
-    webglContext = createContext(gl)
-    return webglContext
-}
-
 export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps): Promise<DensityData> {
     // TODO allow passing a context via props
     const webgl = getWebGLContext()
 
-    if (webgl.maxDrawBuffers > 0) {
-        console.log('GaussianDensityMultiDrawBuffer')
-        const { texture, scale, bbox, dim } = await GaussianDensityMultiDrawBuffer(ctx, webgl, position, box, radius, props)
-
-        console.time('gpu gaussian density 3d texture read')
-        const field = fieldFromTexture3d(webgl, texture, dim)
-        console.timeEnd('gpu gaussian density 3d texture read')
-
-        const idData = field.space.create()
-        const idField = Tensor.create(field.space, idData)
-
-        const transform = Mat4.identity()
-        Mat4.fromScaling(transform, scale)
-        Mat4.setTranslation(transform, bbox.min)
-
-        const renderTarget = createRenderTarget(webgl, dim[0], dim[1])
-
-        return { field, idField, transform, renderTarget, bbox, gridDimension: dim }
-    } else {
-        console.log('GaussianDensitySingleDrawBuffer')
-        return GaussianDensitySingleDrawBuffer(ctx, webgl, position, box, radius, props)
-    }
-}
-
-async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps) {
-    const { resolution, radiusOffset } = props
-
-    const { indices, x, y, z } = position
-    const n = OrderedSet.size(indices)
-
-    const positions = new Float32Array(n * 3)
-    const radii = new Float32Array(n)
-
-    let maxRadius = 0
-
-    for (let i = 0; i < n; ++i) {
-        const j = OrderedSet.getAt(indices, i);
-
-        positions[i * 3] = x[j]
-        positions[i * 3 + 1] = y[j]
-        positions[i * 3 + 2] = z[j]
-        const r = radius(j) + radiusOffset
-        if (maxRadius < r) maxRadius = r
-        radii[i] = r
-
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'preparing density data', current: i, max: n })
-        }
-    }
-
-
-    const pad = maxRadius * 2 + resolution
-    const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad));
-    const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
-
-    const delta = getDelta(expandedBox, resolution)
-    const dim = Vec3.zero()
-    Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
-    console.log('grid dim gpu', dim)
-
-    return { drawCount: n, positions, radii, delta, expandedBox, dim }
-}
-
-function getGaussianDensityRenderObject(webgl: Context, drawCount: number, positions: Float32Array, radii: Float32Array, box: Box3D, dimensions: Vec3, smoothness: number) {
-    const extent = Vec3.sub(Vec3.zero(), box.max, box.min)
-
-    const values: GaussianDensityValues = {
-        drawCount: ValueCell.create(drawCount),
-        instanceCount: ValueCell.create(1),
-
-        aRadius: ValueCell.create(radii),
-        aPosition: ValueCell.create(positions),
-
-        uCurrentSlice: ValueCell.create(0),
-        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),
-        uAlpha: ValueCell.create(smoothness),
+    const useMultiDraw = webgl.maxDrawBuffers > 0
 
-        dDrawBuffers: ValueCell.create(Math.min(8, webgl.maxDrawBuffers)),
-    }
-    const state: RenderableState = {
-        visible: true,
-        depthMask: false
-    }
+    console.time('gpu gaussian density render')
+    const { texture, scale, bbox, dim } = useMultiDraw ?
+        await GaussianDensityMultiDrawBuffer(ctx, webgl, position, box, radius, props) :
+        await GaussianDensitySingleDrawBuffer(ctx, webgl, position, box, radius, props)
+    console.timeEnd('gpu gaussian density render')
 
-    const renderObject = createGaussianDensityRenderObject(values, state)
+    console.time('gpu gaussian density read')
+    const field = useMultiDraw ?
+        fieldFromTexture3d(webgl, texture, dim) :
+        fieldFromTexture2d(webgl, texture, dim)
+    console.timeEnd('gpu gaussian density read')
 
-    return renderObject
-}
+    const idData = field.space.create()
+    const idField = Tensor.create(field.space, idData)
 
-//
+    const transform = Mat4.identity()
+    Mat4.fromScaling(transform, scale)
+    Mat4.setTranslation(transform, bbox.min)
 
-function setRenderingDefaults(gl: GLRenderingContext) {
-    gl.disable(gl.CULL_FACE)
-    gl.frontFace(gl.CCW)
-    gl.cullFace(gl.BACK)
+    const renderTarget = createRenderTarget(webgl, dim[0], dim[1])
 
-    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
-    gl.blendEquation(gl.FUNC_ADD)
-    gl.enable(gl.BLEND)
+    return { field, idField, transform, renderTarget, bbox, gridDimension: dim }
 }
 
 //
 
-async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps): Promise<DensityData> {
-    const { readSlices, smoothness } = props
+async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps) {
+    const { smoothness } = props
 
     const { drawCount, positions, radii, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props)
+    const [ dx, dy, dz ] = dim
     const renderObject = getGaussianDensityRenderObject(webgl, drawCount, positions, radii, expandedBox, dim, smoothness)
     const renderable = createRenderable(webgl, renderObject)
 
     //
 
-    // TODO fallback to lower resolution when texture size is not large enough
     const maxTexSize = webgl.maxTextureSize
     let fboTexDimX = 0
     let fboTexDimY = dim[1]
@@ -172,93 +77,43 @@ async function GaussianDensitySingleDrawBuffer(ctx: RuntimeContext, webgl: Conte
         fboTexDimX = dim[0] * dim[2]
     }
 
-    console.log('dim', dim, 'cols', fboTexCols, 'rows', fboTexRows)
-
-    //
-
-    const space = Tensor.Space(dim, [2, 1, 0], Float32Array)
-    const data = space.create()
-    const field = Tensor.create(space, data)
-
-    const idData = space.create()
-    const idField = Tensor.create(space, idData)
-
     //
 
     const { gl } = webgl
     const { uCurrentSlice, uCurrentX, uCurrentY } = renderObject.values
 
-    const program = renderable.getProgram('draw')
-    const renderTarget = createRenderTarget(webgl, fboTexDimX, fboTexDimY)
+    const framebuffer = createFramebuffer(webgl)
+    framebuffer.bind()
 
+    const texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear')
+    texture.define(fboTexDimX, fboTexDimY)
+
+    const program = renderable.getProgram('draw')
     program.use()
-    renderTarget.bind()
     setRenderingDefaults(gl)
+    texture.attachFramebuffer(framebuffer, 0)
 
-    const slice = new Uint8Array(dim[0] * dim[1] * 4)
-
-    console.time('gpu gaussian density slices')
     let currCol = 0
     let currY = 0
     let currX = 0
-    let j = 0
-    for (let i = 0; i < dim[2]; ++i) {
+    for (let i = 0; i < dz; ++i) {
         if (currCol >= fboTexCols) {
             currCol -= fboTexCols
-            currY += dim[1]
+            currY += dy
             currX = 0
         }
-        gl.viewport(currX, currY, dim[0], dim[1])
+        gl.viewport(currX, currY, dx, dy)
         ValueCell.update(uCurrentSlice, i)
         ValueCell.update(uCurrentX, currX)
         ValueCell.update(uCurrentY, currY)
         renderable.render('draw')
-        if (readSlices) {
-            renderTarget.readBuffer(currX, currY, dim[0], dim[1], slice)
-            for (let iy = 0; iy < dim[1]; ++iy) {
-                for (let ix = 0; ix < dim[0]; ++ix) {
-                    data[j] = slice[4 * (iy * dim[0] + ix)] / 255
-                    ++j
-                }
-            }
-        }
         ++currCol
-        currX += dim[0]
-    }
-    console.timeEnd('gpu gaussian density slices')
-
-    //
-
-    if (!readSlices) {
-        console.time('gpu gaussian density full')
-        renderTarget.getBuffer()
-        const { array } = renderTarget.image
-        let idx = 0
-        let tmpCol = 0
-        let tmpRow = 0
-        for (let iz = 0; iz < dim[2]; ++iz) {
-            if (tmpCol >= fboTexCols ) {
-                tmpCol = 0
-                tmpRow += dim[1]
-            }
-            for (let iy = 0; iy < dim[1]; ++iy) {
-                for (let ix = 0; ix < dim[0]; ++ix) {
-                    data[idx] = array[4 * (tmpCol * dim[0] + (iy + tmpRow) * fboTexDimX + ix)] / 255
-                    idx++
-                }
-            }
-            tmpCol++
-        }
-        console.timeEnd('gpu gaussian density full')
+        currX += dx
     }
 
-    //
-
-    const transform = Mat4.identity()
-    Mat4.fromScaling(transform, Vec3.inverse(Vec3.zero(), delta))
-    Mat4.setTranslation(transform, expandedBox.min)
+    framebuffer.destroy() // clean up
 
-    return { field, idField, transform, renderTarget, bbox: expandedBox, gridDimension: dim }
+    return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim }
 }
 
 async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Context, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps) {
@@ -299,14 +154,12 @@ async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Contex
     gl.viewport(0, 0, dx, dy)
     setRenderingDefaults(gl)
 
-    //
-
+    // z-slices to be render with multi render targets
     const dzMulti = Math.floor(dz / drawBuffers) * drawBuffers
 
+    // render multi target
     const programMulti = renderable.getProgram('draw')
     programMulti.use()
-
-    console.time('gpu gaussian density 3d texture multi')
     for (let i = 0; i < dzMulti; i += drawBuffers) {
         ValueCell.update(uCurrentSlice, i)
         for (let k = 0; k < drawBuffers; ++k) {
@@ -314,54 +167,185 @@ async function GaussianDensityMultiDrawBuffer(ctx: RuntimeContext, webgl: Contex
         }
         renderable.render('draw')
     }
-    console.timeEnd('gpu gaussian density 3d texture multi')
 
+    // render single target
     ValueCell.updateIfChanged(renderable.values.dDrawBuffers, 1)
     renderable.update()
     const programSingle = renderable.getProgram('draw')
     programSingle.use()
-
-    console.time('gpu gaussian density 3d texture single')
     for (let i = dzMulti; i < dz; ++i) {
         ValueCell.update(uCurrentSlice, i)
         texture.attachFramebuffer(framebuffer, 0, i)
         renderable.render('draw')
     }
-    console.timeEnd('gpu gaussian density 3d texture single')
 
     // must detach framebuffer attachments before reading is possible
     for (let k = 0; k < drawBuffers; ++k) {
         texture.detachFramebuffer(framebuffer, k as TextureAttachment)
     }
-    framebuffer.destroy() // clean up
 
-    // throw new Error('foo')
+    framebuffer.destroy() // clean up
 
     return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim }
 }
 
 //
 
-function fieldFromTexture3d(ctx: Context, texture: Texture, dim: Vec3) {
+let webglContext: Context
+function getWebGLContext() {
+    if (webglContext) return webglContext
+    const canvas = document.createElement('canvas')
+    const gl = getGLContext(canvas, {
+        alpha: true,
+        antialias: false,
+        depth: false,
+        preserveDrawingBuffer: true
+    })
+    if (!gl) throw new Error('Could not create a WebGL rendering context')
+    webglContext = createContext(gl)
+    return webglContext
+}
+
+async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps) {
+    const { resolution, radiusOffset } = props
+
+    const { indices, x, y, z } = position
+    const n = OrderedSet.size(indices)
+
+    const positions = new Float32Array(n * 3)
+    const radii = new Float32Array(n)
+
+    let maxRadius = 0
+
+    for (let i = 0; i < n; ++i) {
+        const j = OrderedSet.getAt(indices, i);
+
+        positions[i * 3] = x[j]
+        positions[i * 3 + 1] = y[j]
+        positions[i * 3 + 2] = z[j]
+        const r = radius(j) + radiusOffset
+        if (maxRadius < r) maxRadius = r
+        radii[i] = r
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'preparing density data', current: i, max: n })
+        }
+    }
+
+
+    const pad = maxRadius * 2 + resolution
+    const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad));
+    const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
+
+    const delta = getDelta(expandedBox, resolution)
+    const dim = Vec3.zero()
+    Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
+    console.log('grid dim gpu', dim)
+
+    return { drawCount: n, positions, radii, delta, expandedBox, dim }
+}
+
+function getGaussianDensityRenderObject(webgl: Context, drawCount: number, positions: Float32Array, radii: Float32Array, box: Box3D, dimensions: Vec3, smoothness: number) {
+    const extent = Vec3.sub(Vec3.zero(), box.max, box.min)
+
+    const values: GaussianDensityValues = {
+        drawCount: ValueCell.create(drawCount),
+        instanceCount: ValueCell.create(1),
+
+        aRadius: ValueCell.create(radii),
+        aPosition: ValueCell.create(positions),
+
+        uCurrentSlice: ValueCell.create(0),
+        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),
+        uAlpha: ValueCell.create(smoothness),
+
+        dDrawBuffers: ValueCell.create(Math.min(8, webgl.maxDrawBuffers)),
+    }
+    const state: RenderableState = {
+        visible: true,
+        depthMask: false
+    }
+
+    const renderObject = createGaussianDensityRenderObject(values, state)
+
+    return renderObject
+}
+
+function setRenderingDefaults(gl: GLRenderingContext) {
+    gl.disable(gl.CULL_FACE)
+    gl.frontFace(gl.CCW)
+    gl.cullFace(gl.BACK)
+
+    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+    gl.blendEquation(gl.FUNC_ADD)
+    gl.enable(gl.BLEND)
+}
+
+function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) {
     const { gl } = ctx
     const [ dx, dy, dz ] = dim
+    const { width, height } = texture
+    const fboTexCols = Math.floor(width / dx)
+
+    const space = Tensor.Space(dim, [2, 1, 0], Float32Array)
+    const data = space.create()
+    const field = Tensor.create(space, data)
+
+    const image = new Uint8Array(width * height * 4)
+
+    const framebuffer = createFramebuffer(ctx)
+    framebuffer.bind()
+
+    texture.attachFramebuffer(framebuffer, 0)
+    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image)
+
+    let idx = 0
+    let tmpCol = 0
+    let tmpRow = 0
+    for (let iz = 0; iz < dz; ++iz) {
+        if (tmpCol >= fboTexCols ) {
+            tmpCol = 0
+            tmpRow += dy
+        }
+        for (let iy = 0; iy < dy; ++iy) {
+            for (let ix = 0; ix < dx; ++ix) {
+                data[idx] = image[4 * (tmpCol * dx + (iy + tmpRow) * width + ix)] / 255
+                idx++
+            }
+        }
+        tmpCol++
+    }
+
+    framebuffer.destroy()
+
+    return field
+}
+
+function fieldFromTexture3d(ctx: Context, texture: Texture, dim: Vec3) {
+    const { gl } = ctx
+    const { width, height, depth } = texture
 
     const space = Tensor.Space(dim, [2, 1, 0], Float32Array)
     const data = space.create()
     const field = Tensor.create(space, data)
 
-    const slice = new Uint8Array(dx * dy * 4)
+    const slice = new Uint8Array(width * height * 4)
 
     const framebuffer = createFramebuffer(ctx)
     framebuffer.bind()
 
     let j = 0
-    for (let i = 0; i < dz; ++i) {
+    for (let i = 0; i < depth; ++i) {
         texture.attachFramebuffer(framebuffer, 0, i)
-        gl.readPixels(0, 0, dx, dy, gl.RGBA, gl.UNSIGNED_BYTE, slice)
-        for (let iy = 0; iy < dim[1]; ++iy) {
-            for (let ix = 0; ix < dim[0]; ++ix) {
-                data[j] = slice[4 * (iy * dim[0] + ix)] / 255
+        gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, slice)
+        for (let iy = 0; iy < height; ++iy) {
+            for (let ix = 0; ix < width; ++ix) {
+                data[j] = slice[4 * (iy * width + ix)] / 255
                 ++j
             }
         }

+ 0 - 1
src/mol-model/structure/structure/unit/gaussian-density.ts

@@ -16,7 +16,6 @@ export const GaussianDensityParams = {
     radiusOffset: NumberParam('Radius Offset', '', 0, 0, 10, 0.1),
     smoothness: NumberParam('Smoothness', '', 1.5, 0.5, 2.5, 0.1),
     useGpu: BooleanParam('Use GPU', '', true),
-    readSlices: BooleanParam('Read Slices', '', false),
     ignoreCache: BooleanParam('Ignore Cache', '', false),
 }
 export const DefaultGaussianDensityProps = paramDefaultValues(GaussianDensityParams)