UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

208 lines (207 loc) 5.08 kB
import { Vec2 } from "../../../core/math/vec2.js"; class WebgpuXrBridge { _binding = null; _layer = null; _cachedFramebufferSize = new Vec2(); constructor(xrBridge) { this.xrBridge = xrBridge; } destroy(device) { this._binding = null; this._layer = null; this._cachedFramebufferSize.set(0, 0); device._clearXrState(); } beginFrame(frame, referenceSpace) { const device = this.xrBridge.device; device._clearXrState(); if (!this._binding || !this._layer || !referenceSpace) { return; } const pose = frame.getViewerPose(referenceSpace); if (!pose || !pose.views?.length) { return; } const subImages = device.xrSubImages; for (let i = 0; i < pose.views.length; i++) { let sub; try { sub = this._binding.getViewSubImage(this._layer, pose.views[i]); } catch (e) { this.xrBridge._onBindingError?.(e); return; } const colorTexture = sub?.colorTexture; if (!colorTexture) continue; let viewDescriptor = null; if (typeof sub.getViewDescriptor === "function") { try { const desc = sub.getViewDescriptor(); if (desc) { viewDescriptor = { ...desc }; } } catch (e) { } } subImages.push({ colorTexture, viewDescriptor, viewport: sub.viewport, viewFormat: viewDescriptor?.format ?? colorTexture.format }); } const first = subImages[0]; if (first) { device.xrColorTexture = first.colorTexture; device.xrColorTextureViewFormat = first.viewFormat; this._cachedFramebufferSize.set(first.colorTexture.width, first.colorTexture.height); } } endFrame() { const device = this.xrBridge.device; device?._clearXrState(); } get presentationLayer() { return this._layer; } get graphicsBinding() { return this._binding; } getFramebufferSize(_frame, out) { const layer = this._layer; if (layer) { const lw = layer.textureWidth ?? layer.width; const lh = layer.textureHeight ?? layer.height; if (lw > 0 && lh > 0) { out.set(lw, lh); return; } } if (this._cachedFramebufferSize.x > 0 && this._cachedFramebufferSize.y > 0) { out.copy(this._cachedFramebufferSize); return; } out.set(0, 0); } getViewport(_frame, xrView) { if (this._binding && this._layer) { try { const sub = this._binding.getViewSubImage(this._layer, xrView); if (sub?.viewport) { return sub.viewport; } } catch { } } return { x: 0, y: 0, width: 0, height: 0 }; } attachPresentation(session, options) { const XRGPUBindingCtor = globalThis.XRGPUBinding; if (typeof XRGPUBindingCtor === "undefined") { this.xrBridge._onBindingError?.(new Error("XRGPUBinding is not available in this browser.")); return; } const device = this.xrBridge.device; const wgpu = device.wgpu; try { this._binding = new XRGPUBindingCtor(session, wgpu); } catch (ex) { this.xrBridge._onBindingError?.(ex); return; } const colorFormat = this._binding.getPreferredColorFormat(); const layerOpts = { colorFormat, scaleFactor: options.framebufferScaleFactor }; try { this._layer = this._binding.createProjectionLayer({ ...layerOpts, textureType: "texture-array" }); } catch { this._layer = this._binding.createProjectionLayer({ ...layerOpts, textureType: "texture" }); } session.updateRenderState({ layers: [this._layer], depthNear: options.depthNear, depthFar: options.depthFar }); } syncCameraColorTexture(xrCamera, texture) { if (!this._binding?.getCameraImage) { return; } const src = this._binding.getCameraImage(xrCamera); if (!src) { return; } const dst = texture.impl?.gpuTexture; if (!dst) { return; } const device = this.xrBridge.device; const encoder = device.getCommandEncoder(); const width = xrCamera.width; const height = xrCamera.height; encoder.copyTextureToTexture( { texture: src }, { texture: dst }, [width, height, 1] ); } syncCameraDepthTexture(depthInfo, _texture, _depthPixelFormat) { if (depthInfo?.texture) { } } releasePresentation() { this._binding = null; } onGraphicsDeviceLost() { const bridge = this.xrBridge; const session = bridge._session; if (!session) { return; } const rs = session.renderState; const device = bridge.device; this._binding = null; this._layer = null; this._cachedFramebufferSize.set(0, 0); device._clearXrState(); session.updateRenderState({ layers: [], depthNear: rs.depthNear, depthFar: rs.depthFar }); } onGraphicsDeviceRestored() { const bridge = this.xrBridge; if (!bridge._session) { return; } const eventHandler = bridge.eventHandler; setTimeout(() => { if (!bridge._session) { return; } try { const rs = bridge._session.renderState; bridge.attachPresentation(bridge._session, { framebufferScaleFactor: bridge._framebufferScaleFactor, depthNear: rs.depthNear, depthFar: rs.depthFar, onBindingError: bridge._onBindingError }); } catch (ex) { eventHandler.fire("error", ex); } }, 0); } } export { WebgpuXrBridge };