diff --git a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts b/src/mol-geo/geometry/texture-mesh/texture-mesh.ts index 2cd745e3551f30ef10df78e455d8983ed201de69..9bc75584967e72b72782c434a3f6ca16ec43350a 100644 --- a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts +++ b/src/mol-geo/geometry/texture-mesh/texture-mesh.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> */ @@ -36,11 +36,36 @@ export interface TextureMesh { readonly vertexTexture: ValueCell<Texture>, readonly groupTexture: ValueCell<Texture>, readonly normalTexture: ValueCell<Texture>, + readonly doubleBuffer: TextureMesh.DoubleBuffer readonly boundingSphere: Sphere3D } export namespace TextureMesh { + export class DoubleBuffer { + private index = 0; + private textures: ({ vertex: Texture, group: Texture, normal: Texture } | undefined)[] = [] + + get() { + return this.textures[this.index]; + } + + set(vertex: Texture, group: Texture, normal: Texture) { + this.textures[this.index] = Object.assign(this.textures[this.index] || {}, { + vertex, group, normal + }); + this.index = (this.index + 1) % 2; + } + + destroy() { + for (const buffer of this.textures) { + buffer!.vertex.destroy(); + buffer!.group.destroy(); + buffer!.normal.destroy(); + } + } + } + export function create(vertexCount: number, groupCount: number, vertexTexture: Texture, groupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh { const width = vertexTexture.getWidth(); const height = vertexTexture.getHeight(); @@ -49,7 +74,9 @@ export namespace TextureMesh { textureMesh.groupCount = groupCount; ValueCell.update(textureMesh.geoTextureDim, Vec2.set(textureMesh.geoTextureDim.ref.value, width, height)); ValueCell.update(textureMesh.vertexTexture, vertexTexture); + ValueCell.update(textureMesh.groupTexture, groupTexture); ValueCell.update(textureMesh.normalTexture, normalTexture); + textureMesh.doubleBuffer.set(vertexTexture, groupTexture, normalTexture); Sphere3D.copy(textureMesh.boundingSphere, boundingSphere); return textureMesh; } else { @@ -61,6 +88,7 @@ export namespace TextureMesh { vertexTexture: ValueCell.create(vertexTexture), groupTexture: ValueCell.create(groupTexture), normalTexture: ValueCell.create(normalTexture), + doubleBuffer: new DoubleBuffer(), boundingSphere: Sphere3D.clone(boundingSphere), }; } diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 46719f4bee39f38971f4442811f8eccc9e6528a0..d1e88e5c31cf783e9e0c5cd64c8a62563eb0547d 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -19,7 +19,6 @@ import { quad_vert } from '../../../mol-gl/shader/quad.vert'; import { isosurface_frag } from '../../../mol-gl/shader/marching-cubes/isosurface.frag'; import { calcActiveVoxels } from './active-voxels'; import { isWebGL2 } from '../../webgl/compat'; -import { Scheduler } from '../../../mol-task'; const IsosurfaceSchema = { ...QuadSchema, @@ -193,29 +192,19 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex // -function delay() { - return new Promise(r => Scheduler.setImmediate(r)); -} - -export async function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) { +export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) { // console.time('calcActiveVoxels'); const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale); - // apply advanced magic to solve incomplete buffer rendering issue - await delay(); + // ctx.waitForGpuCommandsCompleteSync(); + // console.timeEnd('calcActiveVoxels'); // console.time('createHistogramPyramid'); const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim); - // apply advanced magic to solve incomplete buffer rendering issue - await delay(); - // ctx.waitForGpuCommandsCompleteSync(); // console.timeEnd('createHistogramPyramid'); // console.time('createIsosurfaceBuffers'); const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, packedGroup, vertexTexture, groupTexture, normalTexture); - // apply advanced magic to solve incomplete buffer rendering issue - await delay(); - // ctx.waitForGpuCommandsCompleteSync(); // console.timeEnd('createIsosurfaceBuffers'); diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 68791ed8549967c35f2f5038cd0406901985aa24..ab9c319d4644cd42e0f40e2f7674c8cdce5ad7e8 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -37,6 +37,7 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues) const textureValues: TextureValues = {}; const uniformValues: UniformValues = {}; const materialUniformValues: UniformValues = {}; + const bufferedUniformValues: UniformValues = {}; Object.keys(schema).forEach(k => { const spec = schema[k]; if (spec.type === 'attribute') attributeValues[k] = values[k]; @@ -45,11 +46,12 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues) if (spec.type === 'texture' && values[k] !== undefined) textureValues[k] = values[k]; // check if k exists in values to exclude global uniforms if (spec.type === 'uniform' && values[k] !== undefined) { - if (spec.isMaterial) materialUniformValues[k] = values[k]; + if (spec.variant === 'material') materialUniformValues[k] = values[k]; + else if (spec.variant === 'buffered') bufferedUniformValues[k] = values[k]; else uniformValues[k] = values[k]; } }); - return { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues }; + return { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues, bufferedUniformValues }; } export type Versions<T extends RenderableValues> = { [k in keyof T]: number } @@ -68,9 +70,9 @@ export function AttributeSpec<K extends AttributeKind>(kind: K, itemSize: Attrib return { type: 'attribute', kind, itemSize, divisor }; } -export type UniformSpec<K extends UniformKind> = { type: 'uniform', kind: K, isMaterial: boolean } -export function UniformSpec<K extends UniformKind>(kind: K, isMaterial = false): UniformSpec<K> { - return { type: 'uniform', kind, isMaterial }; +export type UniformSpec<K extends UniformKind> = { type: 'uniform', kind: K, variant?: 'material' | 'buffered' } +export function UniformSpec<K extends UniformKind>(kind: K, variant?: 'material' | 'buffered'): UniformSpec<K> { + return { type: 'uniform', kind, variant }; } export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter } @@ -180,7 +182,7 @@ export type InternalValues = Values<InternalSchema> export const ColorSchema = { // aColor: AttributeSpec('float32', 3, 0), // TODO - uColor: UniformSpec('v3', true), + uColor: UniformSpec('v3', 'material'), uColorTexDim: UniformSpec('v2'), tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'), dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance']), @@ -190,7 +192,7 @@ export type ColorValues = Values<ColorSchema> export const SizeSchema = { // aSize: AttributeSpec('float32', 1, 0), // TODO - uSize: UniformSpec('f', true), + uSize: UniformSpec('f', 'material'), uSizeTexDim: UniformSpec('v2'), tSize: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'), dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance']), @@ -251,7 +253,7 @@ export const BaseSchema = { /** * final alpha, calculated as `values.alpha * state.alpha` */ - uAlpha: UniformSpec('f', true), + uAlpha: UniformSpec('f', 'material'), uVertexCount: UniformSpec('i'), uInstanceCount: UniformSpec('i'), uGroupCount: UniformSpec('i'), diff --git a/src/mol-gl/renderable/texture-mesh.ts b/src/mol-gl/renderable/texture-mesh.ts index 66c5308fb59d94ffc337afccbefd47f8462b4356..11e3835e73bb7c69eb314332abcf6c72f37ab81f 100644 --- a/src/mol-gl/renderable/texture-mesh.ts +++ b/src/mol-gl/renderable/texture-mesh.ts @@ -13,7 +13,7 @@ import { ValueCell } from '../../mol-util'; export const TextureMeshSchema = { ...BaseSchema, - uGeoTexDim: UniformSpec('v2'), + uGeoTexDim: UniformSpec('v2', 'buffered'), tPosition: TextureSpec('texture', 'rgb', 'float', 'nearest'), tGroup: TextureSpec('texture', 'alpha', 'float', 'nearest'), tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'), diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 96498091e2e7cfa31842872b258dd14b0d0083e2..bcbfca4dc5452f42c967102b71698165e20c6c14 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -17,6 +17,8 @@ import { checkFramebufferStatus } from './framebuffer'; import { isDebugMode } from '../../mol-util/debug'; import { VertexArray } from './vertex-array'; import { fillSerial } from '../../mol-util/array'; +import { deepClone } from '../../mol-util/object'; +import { cloneUniformValues } from './uniform'; const getNextRenderItemId = idFactory(); @@ -123,10 +125,12 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: (schema as any).aVertex = AttributeSpec('float32', 1, 0); } - const { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues } = splitValues(schema, values); + const { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues, bufferedUniformValues } = splitValues(schema, values); const uniformValueEntries = Object.entries(uniformValues); const materialUniformValueEntries = Object.entries(materialUniformValues); + const backBufferUniformValueEntries = Object.entries(bufferedUniformValues); + const frontBufferUniformValueEntries = Object.entries(cloneUniformValues(bufferedUniformValues)); const defineValueEntries = Object.entries(defineValues); const versions = getValueVersions(values); @@ -192,6 +196,7 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: currentProgramId = program.id; } program.setUniforms(uniformValueEntries); + program.setUniforms(frontBufferUniformValueEntries); if (sharedTexturesList && sharedTexturesList.length > 0) { program.bindTextures(sharedTexturesList, 0); program.bindTextures(textures, sharedTexturesList.length); @@ -318,11 +323,20 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: if (schema[k].kind !== 'texture') { // console.log('texture version changed, uploading image', k); texture.load(value.ref.value as TextureImage<any> | TextureVolume<any>); - versions[k] = value.ref.version; valueChanges.textures = true; } else { textures[i][1] = value.ref.value as Texture; } + versions[k] = value.ref.version; + } + } + + for (let i = 0, il = backBufferUniformValueEntries.length; i < il; ++i) { + const [k, uniform] = backBufferUniformValueEntries[i]; + if (uniform.ref.version !== versions[k]) { + // console.log('back-buffer uniform version changed, updating front-buffer', k); + ValueCell.update(frontBufferUniformValueEntries[i][1], deepClone(uniform.ref.value)); + versions[k] = uniform.ref.version; } } diff --git a/src/mol-gl/webgl/uniform.ts b/src/mol-gl/webgl/uniform.ts index aa1e43b62c4c141c35530318b96d7450d72aaa51..ef97b2ae3b2e3ba9d140086f489d6d184f160853 100644 --- a/src/mol-gl/webgl/uniform.ts +++ b/src/mol-gl/webgl/uniform.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> */ @@ -9,6 +9,7 @@ import { ValueCell } from '../../mol-util'; import { GLRenderingContext } from './compat'; import { RenderableSchema } from '../../mol-gl/renderable/schema'; import { ValueOf } from '../../mol-util/type-helpers'; +import { deepClone } from '../../mol-util/object'; export type UniformKindValue = { 'b': boolean; 'b[]': boolean[] @@ -105,4 +106,12 @@ export function isUniformValueScalar(kind: UniformKind): boolean { default: return false; } +} + +export function cloneUniformValues(uniformValues: UniformValues): UniformValues { + const clonedValues: UniformValues = {}; + Object.keys(uniformValues).forEach(k => { + clonedValues[k] = ValueCell.create(deepClone(uniformValues[k].ref.value)); + }); + return clonedValues; } \ No newline at end of file diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 2176b4bbe07991f380861d21a2125406c7dc3200..f8d13730fbc96ddc52715d02cb672fc1cb2ece6c 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -33,14 +33,14 @@ export const GaussianDensitySchema = { uCurrentSlice: UniformSpec('f'), uCurrentX: UniformSpec('f'), uCurrentY: UniformSpec('f'), - uBboxMin: UniformSpec('v3', true), - uBboxSize: UniformSpec('v3', true), - uGridDim: UniformSpec('v3', true), - uGridTexDim: UniformSpec('v3', true), - uGridTexScale: UniformSpec('v2', true), - uAlpha: UniformSpec('f', true), - uResolution: UniformSpec('f', true), - uRadiusFactorInv: UniformSpec('f', true), + uBboxMin: UniformSpec('v3', 'material'), + uBboxSize: UniformSpec('v3', 'material'), + uGridDim: UniformSpec('v3', 'material'), + uGridTexDim: UniformSpec('v3', 'material'), + uGridTexScale: UniformSpec('v2', 'material'), + uAlpha: UniformSpec('f', 'material'), + uResolution: UniformSpec('f', 'material'), + uRadiusFactorInv: UniformSpec('f', 'material'), tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'), dGridTexType: DefineSpec('string', ['2d', '3d']), diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts index d81c86ec65a723b5d3c4001e56a1eff05b408c08..f6a56b7890e23fe83d72bd4b5c6eb460cf50be40 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts @@ -181,7 +181,8 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor; - const gv = await extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); + const buffer = textureMesh?.doubleBuffer.get(); + const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, buffer?.vertex, buffer?.group, buffer?.normal); const boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure)); const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh); @@ -211,6 +212,7 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua geometry.vertexTexture.ref.value.destroy(); geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); + geometry.doubleBuffer.destroy(); } }, materialId); } @@ -238,7 +240,8 @@ async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, str const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor; - const gv = await extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); + const buffer = textureMesh?.doubleBuffer.get(); + const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, buffer?.vertex, buffer?.group, buffer?.normal); const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure)); const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh); @@ -267,6 +270,7 @@ export function StructureGaussianSurfaceTextureMeshVisual(materialId: number): C geometry.vertexTexture.ref.value.destroy(); geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); + geometry.doubleBuffer.destroy(); } }, materialId); } \ No newline at end of file diff --git a/src/mol-repr/volume/isosurface.ts b/src/mol-repr/volume/isosurface.ts index 4dce1f1266929d900d8cedbfb1cf61d2ad32d334..d25e781d9c4ff76ae733c328b2729f0f66a8e13e 100644 --- a/src/mol-repr/volume/isosurface.ts +++ b/src/mol-repr/volume/isosurface.ts @@ -175,7 +175,8 @@ async function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Vol const { texture, gridDimension, gridTexDim, gridTexScale, transform } = VolumeIsosurfaceTexture.get(volume, ctx.webgl); - const gv = await extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, false, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); + const buffer = textureMesh?.doubleBuffer.get(); + const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, false, buffer?.vertex, buffer?.group, buffer?.normal); const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, Volume.getBoundingSphere(volume), textureMesh); @@ -200,6 +201,7 @@ export function IsosurfaceTextureMeshVisual(materialId: number): VolumeVisual<Is geometry.vertexTexture.ref.value.destroy(); geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); + geometry.doubleBuffer.destroy(); } }, materialId); }