From 4fd607c17c22dbe021ee6fde251ded7912226636 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 24 Jun 2024 04:29:07 +0300 Subject: [PATCH 1/5] add useful worker proxy helper from webgpu implementation --- prismarine-viewer/viewer/lib/workerProxy.ts | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 prismarine-viewer/viewer/lib/workerProxy.ts diff --git a/prismarine-viewer/viewer/lib/workerProxy.ts b/prismarine-viewer/viewer/lib/workerProxy.ts new file mode 100644 index 000000000..80a8cdb4d --- /dev/null +++ b/prismarine-viewer/viewer/lib/workerProxy.ts @@ -0,0 +1,58 @@ +export function createWorkerProxy void>> (handlers: T): { __workerProxy: T } { + addEventListener('message', (event) => { + const { type, args } = event.data + if (handlers[type]) { + handlers[type](...args) + } + }) + return null as any +} + +/** + * in main thread + * ```ts + * // either: + * import type { importedTypeWorkerProxy } from './worker' + * // or: + * type importedTypeWorkerProxy = import('./worker').importedTypeWorkerProxy + * + * const workerChannel = useWorkerProxy(worker) + * ``` + */ +export const useWorkerProxy = void> }> (worker: Worker, autoTransfer = true): T['__workerProxy'] & { + transfer: (...args: Transferable[]) => T['__workerProxy'] +} => { + // in main thread + return new Proxy({} as any, { + get: (target, prop) => { + if (prop === 'transfer') return (...transferable: Transferable[]) => { + return new Proxy({}, { + get: (target, prop) => { + return (...args: any[]) => { + worker.postMessage({ + type: prop, + args, + }, transferable) + } + } + }) + } + return (...args: any[]) => { + const transfer = autoTransfer ? args.filter(arg => arg instanceof ArrayBuffer || arg instanceof MessagePort || arg instanceof ImageBitmap || arg instanceof OffscreenCanvas || arg instanceof ImageData) : [] + worker.postMessage({ + type: prop, + args, + }, transfer) + } + } + }) +} + +// const workerProxy = createWorkerProxy({ +// startRender (canvas: HTMLCanvasElement) { +// }, +// }) + +// const worker = useWorkerProxy(null, workerProxy) + +// worker. From 3b3efdcbfbcdfb57d28d5a849f98a71cf799c09d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 24 Jun 2024 06:27:08 +0300 Subject: [PATCH 2/5] put three.js renderer into worker for stable fps --- buildWorkers.mjs | 64 ++++++++++++ package.json | 3 +- pnpm-lock.yaml | 14 ++- prismarine-viewer/examples/playground.ts | 24 +++-- prismarine-viewer/viewer/lib/threeJsWorker.ts | 99 +++++++++++++++++++ .../viewer/lib/worldrendererThree.ts | 49 ++++++++- src/index.ts | 2 +- 7 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 buildWorkers.mjs create mode 100644 prismarine-viewer/viewer/lib/threeJsWorker.ts diff --git a/buildWorkers.mjs b/buildWorkers.mjs new file mode 100644 index 000000000..95aa402d5 --- /dev/null +++ b/buildWorkers.mjs @@ -0,0 +1,64 @@ +// main worker file intended for computing world geometry is built using prismarine-viewer/buildWorker.mjs +import { build, context } from 'esbuild' +import fs from 'fs' +import path from 'path' + +const watch = process.argv.includes('-w') + +const workers = ['./prismarine-viewer/viewer/lib/threeJsWorker.ts'] + +const result = await (watch ? context : build)({ + bundle: true, + platform: 'browser', + entryPoints: workers, + outdir: 'prismarine-viewer/public/', + write: false, + sourcemap: watch ? 'inline' : 'external', + minify: !watch, + treeShaking: true, + logLevel: 'info', + alias: { + 'three': './node_modules/three/src/Three.js', + events: 'events', // make explicit + buffer: 'buffer', + 'fs': 'browserfs/dist/shims/fs.js', + http: 'http-browserify', + perf_hooks: './src/perf_hooks_replacement.js', + crypto: './src/crypto.js', + stream: 'stream-browserify', + net: 'net-browserify', + assert: 'assert', + dns: './src/dns.js' + }, + inject: [ + './src/shims.js' + ], + plugins: [ + { + name: 'writeOutput', + setup(build) { + build.onEnd(({ outputFiles }) => { + for (const file of outputFiles) { + for (const dir of ['prismarine-viewer/public', 'dist']) { + const baseName = path.basename(file.path) + fs.writeFileSync(path.join(dir, baseName), file.contents) + } + } + }) + } + } + ], + loader: { + '.vert': 'text', + '.frag': 'text', + '.wgsl': 'text', + }, + mainFields: [ + 'browser', 'module', 'main' + ], + keepNames: true, +}) + +if (watch) { + await result.watch() +} diff --git a/package.json b/package.json index c03422604..abf4024b4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build && node scripts/build.js moveStorybookFiles", "start-experiments": "vite --config experiments/vite.config.ts --host", - "watch-other-workers": "echo NOT IMPLEMENTED", + "watch-other-workers": "node buildWorkers.mjs -w", "watch-mesher": "node prismarine-viewer/buildMesherWorker.mjs -w", "run-playground": "run-p watch-mesher watch-other-workers playground-server watch-playground", "run-all": "run-p start run-playground", @@ -117,6 +117,7 @@ "@types/wait-on": "^5.3.4", "@xmcl/installer": "^5.1.0", "assert": "^2.0.0", + "three-latest": "npm:three@latest", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "constants-browserify": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f2b48099..6c25d4e24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,6 +345,9 @@ importers: three: specifier: 0.154.0 version: 0.154.0(patch_hash=sj7ocb4p23jym6bkfgueanti2e) + three-latest: + specifier: npm:three@latest + version: three@0.165.0 timers-browserify: specifier: ^2.0.12 version: 2.0.12 @@ -413,7 +416,7 @@ importers: version: 4.7.2 three-stdlib: specifier: ^2.26.11 - version: 2.28.5(three@0.154.0(patch_hash=sj7ocb4p23jym6bkfgueanti2e)) + version: 2.28.5(three@0.165.0) three.meshline: specifier: ^1.3.0 version: 1.4.0 @@ -7864,6 +7867,9 @@ packages: three@0.154.0: resolution: {integrity: sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==} + three@0.165.0: + resolution: {integrity: sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==} + throttle-debounce@3.0.1: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} @@ -18021,7 +18027,7 @@ snapshots: dependencies: any-promise: 1.3.0 - three-stdlib@2.28.5(three@0.154.0(patch_hash=sj7ocb4p23jym6bkfgueanti2e)): + three-stdlib@2.28.5(three@0.165.0): dependencies: '@types/draco3d': 1.4.7 '@types/offscreencanvas': 2019.7.2 @@ -18029,12 +18035,14 @@ snapshots: draco3d: 1.5.6 fflate: 0.6.10 potpack: 1.0.2 - three: 0.154.0(patch_hash=sj7ocb4p23jym6bkfgueanti2e) + three: 0.165.0 three.meshline@1.4.0: {} three@0.154.0(patch_hash=sj7ocb4p23jym6bkfgueanti2e): {} + three@0.165.0: {} + throttle-debounce@3.0.1: {} throttleit@1.0.0: {} diff --git a/prismarine-viewer/examples/playground.ts b/prismarine-viewer/examples/playground.ts index 223fc6f2e..d80ef4df2 100644 --- a/prismarine-viewer/examples/playground.ts +++ b/prismarine-viewer/examples/playground.ts @@ -111,11 +111,21 @@ async function main () { const chunk1 = new Chunk() //@ts-ignore const chunk2 = new Chunk() - chunk1.setBlockStateId(targetPos, 34) - chunk2.setBlockStateId(targetPos.offset(1, 0, 0), 34) + const addNeighbor = (x, z, light = 15) => { + x += 2 + chunk1.setBlockStateId(targetPos.offset(x, 0, z), 1) + chunk1.setBlockLight(targetPos.offset(x, 1, z), light) + chunk1.setSkyLight(targetPos.offset(x, 1, z), light) + } + addNeighbor(0, 1) + addNeighbor(0, -1) + addNeighbor(1, 0) + addNeighbor(-1, 0) + chunk1.setBlockStateId(targetPos.offset(1, 1, 0), mcData.blocksByName['grass'].minStateId) + addNeighbor(0, 0, 0) const world = new World((chunkX, chunkZ) => { - // if (chunkX === 0 && chunkZ === 0) return chunk1 - // if (chunkX === 1 && chunkZ === 0) return chunk2 + if (chunkX === 0 && chunkZ === 0) return chunk1 + if (chunkX === 1 && chunkZ === 0) return chunk2 //@ts-ignore const chunk = new Chunk() return chunk @@ -138,7 +148,7 @@ async function main () { viewer.entities.onSkinUpdate = () => { viewer.render() } - viewer.world.mesherConfig.enableLighting = false + // viewer.world.mesherConfig.enableLighting = false viewer.listen(worldView) // Load chunks @@ -260,7 +270,7 @@ async function main () { const controls = new OrbitControls(viewer.camera, renderer.domElement) controls.target.set(targetPos.x + 0.5, targetPos.y + 0.5, targetPos.z + 0.5) - const cameraPos = targetPos.offset(2, 2, 2) + const cameraPos = targetPos.offset(3, 3, 3) const pitch = THREE.MathUtils.degToRad(-45) const yaw = THREE.MathUtils.degToRad(45) viewer.camera.rotation.set(pitch, yaw, 0, 'ZYX') @@ -409,7 +419,7 @@ async function main () { for (const update of Object.values(onUpdate)) { update() } - applyChanges(true) + // applyChanges(true) gui.openAnimated() }) diff --git a/prismarine-viewer/viewer/lib/threeJsWorker.ts b/prismarine-viewer/viewer/lib/threeJsWorker.ts new file mode 100644 index 000000000..2b13fbee9 --- /dev/null +++ b/prismarine-viewer/viewer/lib/threeJsWorker.ts @@ -0,0 +1,99 @@ +import * as THREE from 'three' +import { createWorkerProxy } from './workerProxy' +import * as tweenJs from '@tweenjs/tween.js' +import testGeometryJson from '../../examples/test-geometry.json' + +let material: THREE.Material +let scene = new THREE.Scene() +let camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000) +let renderer: THREE.WebGLRenderer +// scene.add(new THREE.AmbientLight(0xcc_cc_cc)) +// scene.add(new THREE.DirectionalLight(0xff_ff_ff, 0.5)) +scene.add(camera) +scene.background = new THREE.Color('lightblue') +scene.matrixAutoUpdate = false +scene.add(new THREE.AmbientLight(0xcc_cc_cc)) +scene.add(new THREE.DirectionalLight(0xff_ff_ff, 0.5)) + +THREE.ColorManagement.enabled = false + +let sections = new Map() +globalThis.sections = sections +globalThis.camera = camera +globalThis.scene = scene +globalThis.marks = {} + +let fps = 0 +let processedSinceLastRender = 0 +setInterval(() => { + // console.log('FPS', fps) + globalThis.fps = fps + globalThis.worstFps = Math.min(globalThis.worstFps ?? Infinity, fps) + fps = 0 +}, 1000) +setInterval(() => { + globalThis.worstFps = Infinity +}, 10000) +const render = () => { + tweenJs.update() + renderer.render(scene, camera) + globalThis.maxProcessed = Math.max(globalThis.maxProcessed ?? 0, processedSinceLastRender) + processedSinceLastRender = 0 + fps++ +} + +export const threeJsWorkerProxyType = createWorkerProxy({ + async canvas (canvas: OffscreenCanvas, textureBlob: Blob) { + const textureBitmap = await createImageBitmap(textureBlob) + const texture = new THREE.CanvasTexture(textureBitmap) + texture.magFilter = THREE.NearestFilter + texture.minFilter = THREE.NearestFilter + texture.flipY = false + texture.needsUpdate = true + material = new THREE.MeshLambertMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1, map: texture }) + + renderer = new THREE.WebGLRenderer({ canvas }) + renderer.outputColorSpace = THREE.LinearSRGBColorSpace + camera.aspect = canvas.width / canvas.height + camera.updateProjectionMatrix() + + renderer.setAnimationLoop(render) + }, + addGeometry (position: { x, y, z }, geometry?: { positions, normals, uvs, colors, indices }) { + const key = `${position.x},${position.y},${position.z}` + if (sections.has(key)) { + const section = sections.get(key)! + section.geometry.dispose() + scene.remove(section) + sections.delete(key) + } + if (!geometry) return + const bufferGeometry = new THREE.BufferGeometry() + bufferGeometry.setAttribute('position', new THREE.BufferAttribute(geometry.positions, 3)) + bufferGeometry.setAttribute('normal', new THREE.BufferAttribute(geometry.normals, 3)) + bufferGeometry.setAttribute('uv', new THREE.BufferAttribute(geometry.uvs, 2)) + bufferGeometry.setAttribute('color', new THREE.BufferAttribute(geometry.colors, 3)) + bufferGeometry.setIndex(geometry.indices) + const mesh = new THREE.Mesh(bufferGeometry, material) + // mesh.frustumCulled = false + const old = mesh.geometry.computeBoundingSphere + mesh.geometry.computeBoundingSphere = function () { + let start = performance.now() + // old.call(mesh.geometry) + globalThis.marks.computeBoundingSphere ??= 0 + globalThis.marks.computeBoundingSphere += performance.now() - start + mesh.geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 16) + } + mesh.position.set(position.x, position.y, position.z) + scene.add(mesh) + processedSinceLastRender++ + if (processedSinceLastRender > 5) { + render() + } + }, + updateCamera (position: { x, y, z }, rotation: { x, y, z }) { + // camera.position.set(position.x, position.y, position.z) + new tweenJs.Tween(camera.position).to({ x: position.x, y: position.y, z: position.z }, 50).start() + camera.rotation.set(rotation.x, rotation.y, rotation.z, 'ZYX') + } +}) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index cc89a8238..300da3263 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -8,6 +8,17 @@ import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon' import * as tweenJs from '@tweenjs/tween.js' import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib' import { disposeObject } from './threeJsUtils' +import { type threeJsWorkerProxyType } from 'prismarine-viewer/viewer/lib/threeJsWorker' +import { useWorkerProxy } from 'prismarine-viewer/viewer/lib/workerProxy' + +async function imageToBlob (url: string): Promise { + const response = await fetch(url) + const blob = await response.blob() + return blob +} + +const testSharedArrayBuffer = new SharedArrayBuffer(1024) +testSharedArrayBuffer[0] = 1 export class WorldRendererThree extends WorldRendererCommon { outputFormat = 'threeJs' as const @@ -17,6 +28,7 @@ export class WorldRendererThree extends WorldRendererCommon { signsCache = new Map() starField: StarField cameraSectionPos: Vec3 = new Vec3(0, 0, 0) + workerProxy?: ReturnType> get tilesRendered () { return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) @@ -25,6 +37,19 @@ export class WorldRendererThree extends WorldRendererCommon { constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public config: WorldRendererConfig) { super(config) this.starField = new StarField(scene) + + this.renderUpdateEmitter.addListener('textureDownloaded', async () => { + const rendererWorker = new Worker('./threeJsWorker.js') + this.workerProxy = useWorkerProxy(rendererWorker) + const img: HTMLImageElement = this.downloadedTextureImage + const blob = await imageToBlob(img.src) + const newCanvas = document.createElement('canvas') + newCanvas.width = outerWidth * window.devicePixelRatio + newCanvas.height = outerHeight * window.devicePixelRatio + newCanvas.id = 'viewer-canvas' + document.body.appendChild(newCanvas) + this.workerProxy.canvas(newCanvas.transferControlToOffscreen(), blob) + }) } timeUpdated (newTime: number): void { @@ -60,6 +85,9 @@ export class WorldRendererThree extends WorldRendererCommon { } } + lastUpdate = 0 + updates = [] as number[] + handleWorkerMessage (data: any): void { if (data.type !== 'geometry') return let object: THREE.Object3D = this.sectionObjects[data.key] @@ -72,6 +100,9 @@ export class WorldRendererThree extends WorldRendererCommon { const chunkCoords = data.key.split(',') if (!this.loadedChunks[chunkCoords[0] + ',' + chunkCoords[2]] || !data.geometry.positions.length || !this.active) return + this.updates.push(Math.floor(performance.now() - this.lastUpdate)) + this.lastUpdate = performance.now() + // if (!this.initialChunksLoad && this.enableChunksLoadDelay) { // const newPromise = new Promise(resolve => { // if (this.droppedFpsPercentage > 0.5) { @@ -86,6 +117,19 @@ export class WorldRendererThree extends WorldRendererCommon { // } // } + this.workerProxy?.transfer(data.geometry.positions.buffer, data.geometry.normals.buffer, data.geometry.uvs.buffer, data.geometry.colors.buffer).addGeometry({ + x: data.geometry.sx, + y: data.geometry.sy, + z: data.geometry.sz + }, { + positions: data.geometry.positions, + normals: data.geometry.normals, + uvs: data.geometry.uvs, + colors: data.geometry.colors, + indices: data.geometry.indices, + }) + if (!false) return + const geometry = new THREE.BufferGeometry() geometry.setAttribute('position', new THREE.BufferAttribute(data.geometry.positions, 3)) geometry.setAttribute('normal', new THREE.BufferAttribute(data.geometry.normals, 3)) @@ -158,12 +202,14 @@ export class WorldRendererThree extends WorldRendererCommon { new tweenJs.Tween(this.camera.position).to({ x: pos.x, y: pos.y, z: pos.z }, 50).start() } this.camera.rotation.set(pitch, yaw, 0, 'ZYX') + const posToObj = p => ({ x: p.x, y: p.y, z: p.z }) + this.workerProxy?.updateCamera(pos ?? posToObj(this.camera.position), posToObj(this.camera.rotation)) } render () { tweenJs.update() const cam = this.camera instanceof THREE.Group ? this.camera.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera - this.renderer.render(this.scene, cam) + // this.renderer.render(this.scene, cam) } renderSign (position: Vec3, rotation: number, isWall: boolean, isHanging: boolean, blockEntity) { @@ -292,6 +338,7 @@ export class WorldRendererThree extends WorldRendererCommon { this.cleanChunkTextures(x, z) for (let y = this.worldConfig.minY; y < this.worldConfig.worldHeight; y += 16) { + this.workerProxy?.addGeometry({ x, y, z }) this.setSectionDirty(new Vec3(x, y, z), false) const key = `${x},${y},${z}` const mesh = this.sectionObjects[key] diff --git a/src/index.ts b/src/index.ts index 72f270d42..840efba21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -120,7 +120,7 @@ try { // renderer.localClippingEnabled = true initWithRenderer(renderer.domElement) const renderWrapper = new ViewerWrapper(renderer.domElement, renderer) -renderWrapper.addToPage() +// renderWrapper.addToPage() watchValue(options, (o) => { renderWrapper.renderInterval = o.frameLimit ? 1000 / o.frameLimit : 0 renderWrapper.renderIntervalUnfocused = o.backgroundRendering === '5fps' ? 1000 / 5 : o.backgroundRendering === '20fps' ? 1000 / 20 : undefined From 43ab10274ad966f2c7a2a7ba5162a4d38ef36964 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 24 Jun 2024 06:49:55 +0300 Subject: [PATCH 3/5] test queue --- prismarine-viewer/viewer/lib/threeJsWorker.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/prismarine-viewer/viewer/lib/threeJsWorker.ts b/prismarine-viewer/viewer/lib/threeJsWorker.ts index 2b13fbee9..1bdd79d9b 100644 --- a/prismarine-viewer/viewer/lib/threeJsWorker.ts +++ b/prismarine-viewer/viewer/lib/threeJsWorker.ts @@ -34,8 +34,16 @@ setInterval(() => { setInterval(() => { globalThis.worstFps = Infinity }, 10000) +const meshesQueue = [] as any[] const render = () => { tweenJs.update() + const max = 5 + for (let i = 0; i < max; i++) { + const add = meshesQueue.pop() + if (add) { + scene.add(add) + } + } renderer.render(scene, camera) globalThis.maxProcessed = Math.max(globalThis.maxProcessed ?? 0, processedSinceLastRender) processedSinceLastRender = 0 @@ -85,11 +93,8 @@ export const threeJsWorkerProxyType = createWorkerProxy({ mesh.geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 16) } mesh.position.set(position.x, position.y, position.z) - scene.add(mesh) + meshesQueue.push(mesh) processedSinceLastRender++ - if (processedSinceLastRender > 5) { - render() - } }, updateCamera (position: { x, y, z }, rotation: { x, y, z }) { // camera.position.set(position.x, position.y, position.z) From 7bf904bb78e337c600555bcfdd7de16bcd255aaf Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 24 Jun 2024 06:50:17 +0300 Subject: [PATCH 4/5] build correctly --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abf4024b4..563ab20de 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "start": "node scripts/build.js copyFilesDev && node scripts/prepareData.mjs && node esbuild.mjs --watch", "start-watch-script": "nodemon -w esbuild.mjs --watch", - "build": "node scripts/build.js copyFiles && node scripts/prepareData.mjs -f && node esbuild.mjs --minify --prod", + "build": "node scripts/build.js copyFiles && node buildWorkers.mjs && node scripts/prepareData.mjs -f && node esbuild.mjs --minify --prod", "check-build": "tsc && pnpm build", "test:cypress": "cypress run", "test-unit": "vitest", From 336f8d5bc7478d0349fd97119712a5285fe9c5ad Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 24 Jun 2024 10:34:24 +0300 Subject: [PATCH 5/5] rm invalid test --- prismarine-viewer/viewer/lib/worldrendererThree.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index 300da3263..eb28a0ed5 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -17,9 +17,6 @@ async function imageToBlob (url: string): Promise { return blob } -const testSharedArrayBuffer = new SharedArrayBuffer(1024) -testSharedArrayBuffer[0] = 1 - export class WorldRendererThree extends WorldRendererCommon { outputFormat = 'threeJs' as const blockEntities = {}