diff --git a/package-lock.json b/package-lock.json index 3e56bf553dd9a8ab29d8a547f0d8eb76e636aa8d..66479bb0b1072af8562b449de08b30246a2ca285 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/src/apps/canvas/index.ts b/src/apps/canvas/index.ts index db1a873e3f91795a36b3663e63f1417f75817cdf..03b49b69b33663d35650e5eebee7741439b4fd42 100644 --- a/src/apps/canvas/index.ts +++ b/src/apps/canvas/index.ts @@ -23,6 +23,8 @@ const assemblyId = urlQueryParameter('assembly') const pdbId = urlQueryParameter('pdb') if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId }) +app.loadPdbIdOrMmcifUrl('http://localhost:8091/ngl/data/1crn.cif') + // app.loadPdbIdOrMmcifUrl('3pqr') // app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4') diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index bd35a273e5f6cd52f55fbb6a2c7af42ad78b1ab4..768dfb01ba9c2d5e16c0f4f4f8f7efb5a7b10623 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -66,11 +66,11 @@ interface StructureViewProps { export async function StructureView(app: App, viewer: Viewer, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> { const active: { [k: string]: boolean } = { - cartoon: true, + cartoon: false, point: false, - surface: false, + surface: true, ballAndStick: false, - carbohydrate: true, + carbohydrate: false, spacefill: false, distanceRestraint: false, symmetryAxes: false, diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index 93924a73f7ee88ff24895b4c27d603cd45728dee..f811766691b590d518df73ac4c2a494bdd0c2e14 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -52,28 +52,33 @@ function unbindFramebuffer(gl: GLRenderingContext) { } const tmpPixel = new Uint8Array(1 * 4); -async function waitForGpuCommandsComplete(gl: GLRenderingContext) { - if (isWebGL2(gl)) { - const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); - if (sync) { - // TODO too slow in Firefox - // await new Promise(resolve => { - // const check = async () => { - // if (gl.getSyncParameter(sync, gl.SYNC_STATUS) === gl.SIGNALED) { - // gl.deleteSync(sync) - // resolve(); - // } else { - // setTimeout(check, 50) - // } - // }; - // setTimeout(check, 10) - // }) - gl.deleteSync(sync) + +function fence(gl: WebGL2RenderingContext) { + return new Promise(resolve => { + const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0) + if (!sync) { + console.warn('could not create a WebGL2 sync object') gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel) + resolve() } else { - console.warn('unable to get webgl sync object') - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel) + gl.flush(); // Ensure the fence is submitted. + const check = () => { + const status = gl.getSyncParameter(sync, gl.SYNC_STATUS) + if (status == gl.SIGNALED) { + gl.deleteSync(sync); + resolve(); + } else { + setTimeout(check, 0) + } + } + setTimeout(check, 0) } + }) +} + +async function waitForGpuCommandsComplete(gl: GLRenderingContext) { + if (isWebGL2(gl)) { + await fence(gl) } else { console.info('webgl sync object not supported in webgl 1') gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel) @@ -133,6 +138,7 @@ export interface Context { unbindFramebuffer: () => void readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => void + readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void> waitForGpuCommandsComplete: () => Promise<void> destroy: () => void } @@ -184,6 +190,22 @@ export function createContext(gl: GLRenderingContext): Context { throw new Error('Need "MAX_VERTEX_TEXTURE_IMAGE_UNITS" >= 4') } + let readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void> + if (isWebGL2(gl)) { + const pbo = gl.createBuffer() + readPixelsAsync = async (x: number, y: number, width: number, height: number, buffer: Uint8Array) => { + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo) + gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STATIC_COPY) + gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0) + await fence(gl) + gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, buffer); + } + } else { + readPixelsAsync = async (x: number, y: number, width: number, height: number, buffer: Uint8Array) => { + gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer) + } + } + return { gl, isWebGL2: isWebGL2(gl), @@ -225,6 +247,7 @@ export function createContext(gl: GLRenderingContext): Context { // console.error('Reading pixels failed. Framebuffer not complete.') // } }, + readPixelsAsync, waitForGpuCommandsComplete: () => waitForGpuCommandsComplete(gl), destroy: () => { diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 2c8a8ddda83090c2ac8a4f1e6675d3aaeb99b2a1..34a1f966166a542f91ab944c0d2b49f4340112b9 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -18,7 +18,7 @@ import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/rend import { Context, createContext, getGLContext } from 'mol-gl/webgl/context'; import { createFramebuffer } from 'mol-gl/webgl/framebuffer'; import { createTexture, Texture } from 'mol-gl/webgl/texture'; -import { GLRenderingContext } from 'mol-gl/webgl/compat'; +import { GLRenderingContext, isWebGL2 } from 'mol-gl/webgl/compat'; import { decodeIdRGB } from 'mol-geo/geometry/picking'; export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> { @@ -28,7 +28,7 @@ export async function GaussianDensityGPU(ctx: RuntimeContext, position: Position console.time('GaussianDensityTexture2d') const { scale, bbox, texture, dim } = await GaussianDensityTexture2d(ctx, webgl, position, box, radius, props) console.timeEnd('GaussianDensityTexture2d') - const { field, idField } = fieldFromTexture2d(webgl, texture, dim) + const { field, idField } = await fieldFromTexture2d(webgl, texture, dim) const transform = Mat4.identity() Mat4.fromScaling(transform, scale) @@ -310,7 +310,25 @@ function getTexture2dSize(maxTexSize: number, gridDim: Vec3) { return { texDimX, texDimY, texRows, texCols } } -function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { + +// function pick_nonblocking_getBufferSubData() { +// gl.readPixels(mouse.x, pickingTexture.height - mouse.y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + +// fence().then(function() { +// stats1.begin(); +// gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); +// stats1.end(); +// gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + +// var id = (readbackBuffer[0] << 16) | (readbackBuffer[1] << 8) | (readbackBuffer[2]); +// render(id); +// gl.finish(); +// stats2.end(); +// }); +// } + +async function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { + console.log('isWebGL2', isWebGL2(ctx.gl)) console.time('fieldFromTexture2d') const { gl } = ctx const [ dx, dy, dz ] = dim @@ -327,9 +345,23 @@ function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { const framebuffer = createFramebuffer(ctx) framebuffer.bind() - texture.attachFramebuffer(framebuffer, 0) - gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image) + + if (isWebGL2(gl)) { + const pbo = gl.createBuffer() + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo) + gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STATIC_COPY) + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0) + await ctx.waitForGpuCommandsComplete() + gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, image); + gl.deleteBuffer(pbo) + } else { + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image) + } + // gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image) + + framebuffer.destroy() + gl.finish() let j = 0 let tmpCol = 0 @@ -350,7 +382,6 @@ function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { tmpCol++ } - framebuffer.destroy() console.timeEnd('fieldFromTexture2d') return { field, idField }