UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

120 lines 6.55 kB
import { Observable } from "../../../Misc/observable.js"; import { WebXRLayerWrapper } from "../../webXRLayerWrapper.js"; import { WebXRWebGPURenderTargetTextureProvider } from "../../webXRWebGPURenderTargetTextureProvider.js"; /** * Wraps xr composition layers for the WebGPU (XRGPUBinding) backend. * Mirrors {@link WebXRCompositionLayerWrapper} for WebGPU. * @internal */ export class WebXRWebGPUCompositionLayerWrapper extends WebXRLayerWrapper { constructor(getWidth, getHeight, layer, layerType, isMultiview, createRTTProvider) { super(getWidth, getHeight, layer, layerType, createRTTProvider); this.getWidth = getWidth; this.getHeight = getHeight; this.layer = layer; this.layerType = layerType; this.isMultiview = isMultiview; this.createRTTProvider = createRTTProvider; } } /** * Provides render target textures and other important rendering information for a given XRCompositionLayer * on the WebGPU backend. Mirrors {@link WebXRCompositionLayerRenderTargetTextureProvider}, but wraps * {@link XRGPUSubImage} GPUTextures instead of WebGL textures. * @internal */ export class WebXRWebGPUCompositionLayerRenderTargetTextureProvider extends WebXRWebGPURenderTargetTextureProvider { constructor(_xrSessionManager, _xrGPUBinding, layerWrapper, _depthStencilFormat) { super(_xrSessionManager.scene, layerWrapper); this._xrSessionManager = _xrSessionManager; this._xrGPUBinding = _xrGPUBinding; this.layerWrapper = layerWrapper; this._depthStencilFormat = _depthStencilFormat; this._lastSubImages = new Map(); /** * Per-eye render targets, indexed by eye (0 = left/none, 1 = right). Kept separate from the base * `_renderTargetTextures` registry: that array is an append-only owned list (each entry is disposed once * on provider dispose), whereas this map is a mutable per-eye slot. Reusing `_renderTargetTextures` as * both would let a rebuilt eye target both stay in the registry (leaking / double-disposing the old one) * and grow the registry unboundedly on repeated size changes. */ this._renderTargetTexturesByEye = []; /** * Fires every time a new render target texture is created (either for eye, for view, or for the entire frame) */ this.onRenderTargetTextureCreatedObservable = new Observable(); this._compositionLayer = layerWrapper.layer; } _getRenderTargetForSubImage(subImage, eye = "none") { const lastSubImage = this._lastSubImages.get(eye); const eyeIndex = eye == "right" ? 1 : 0; const colorTexture = subImage.colorTexture; const colorTextureWidth = colorTexture.width; const colorTextureHeight = colorTexture.height; const depthStencilTexture = subImage.depthStencilTexture ?? null; const existingRenderTarget = this._renderTargetTexturesByEye[eyeIndex]; const sizeChanged = !existingRenderTarget || lastSubImage?.colorTexture.width !== colorTextureWidth || lastSubImage?.colorTexture.height !== colorTextureHeight; if (sizeChanged) { if (existingRenderTarget) { // A previous target for this eye exists (the sub-image size changed, e.g. a scale-factor or // display-config change). Dispose it and remove it from the owned registry before creating the // replacement so the registry does not accumulate stale entries or double-dispose on teardown. this._destroyRenderTargetTexture(existingRenderTarget); } this._renderTargetTexturesByEye[eyeIndex] = this._createRenderTargetTextureFromGPUTextures(colorTextureWidth, colorTextureHeight, colorTexture, depthStencilTexture, this._depthStencilFormat, this.layerWrapper.isMultiview); this._framebufferDimensions = { framebufferWidth: colorTextureWidth, framebufferHeight: colorTextureHeight, }; this.onRenderTargetTextureCreatedObservable.notifyObservers({ texture: this._renderTargetTexturesByEye[eyeIndex], eye }); } else { // Same size: repoint the wrapped textures at this frame's GPUTextures, preserving the // RenderTargetTexture / InternalTexture identity held by the XR camera's outputRenderTarget. this._updateRenderTargetTextureFromGPUTextures(existingRenderTarget, colorTexture, depthStencilTexture); } this._lastSubImages.set(eye, subImage); // The projection-layer color/depth textures may be a texture ARRAY with one layer per eye // (depthOrArrayLayers > 1). The sub-image's view descriptor carries the authoritative array-layer // index for this eye, so route it into the render target's bind so each eye renders into its own // layer. For non-layered textures this is 0 (unchanged behavior). const renderTargetTexture = this._renderTargetTexturesByEye[eyeIndex]; renderTargetTexture.layerIndex = subImage.getViewDescriptor().baseArrayLayer ?? 0; return renderTargetTexture; } _getSubImageForEye(eye) { const currentFrame = this._xrSessionManager.currentFrame; if (currentFrame) { return this._xrGPUBinding.getSubImage(this._compositionLayer, currentFrame, eye); } return null; } getRenderTargetTextureForEye(eye) { const subImage = this._getSubImageForEye(eye); if (subImage) { return this._getRenderTargetForSubImage(subImage, eye); } return null; } getRenderTargetTextureForView(view) { return this.getRenderTargetTextureForEye(view?.eye); } _setViewportForSubImage(viewport, subImage) { const textureWidth = subImage.colorTexture.width; const textureHeight = subImage.colorTexture.height; const xrViewport = subImage.viewport; viewport.x = xrViewport.x / textureWidth; viewport.y = xrViewport.y / textureHeight; viewport.width = xrViewport.width / textureWidth; viewport.height = xrViewport.height / textureHeight; } trySetViewportForView(viewport, view) { const subImage = this._lastSubImages.get(view.eye) || this._getSubImageForEye(view.eye); if (subImage) { this._setViewportForSubImage(viewport, subImage); return true; } return false; } } //# sourceMappingURL=WebXRWebGPUCompositionLayer.js.map