@aura3d/rendering 4.0.3 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EnvironmentPlatform.d.ts.map +1 -1
- package/dist/EnvironmentPlatform.js +6 -2
- package/dist/EnvironmentPlatform.js.map +1 -1
- package/dist/ForwardPass.d.ts +2 -0
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +70 -5
- package/dist/ForwardPass.js.map +1 -1
- package/dist/Geometry.d.ts +40 -0
- package/dist/Geometry.d.ts.map +1 -1
- package/dist/Geometry.js +39 -2
- package/dist/Geometry.js.map +1 -1
- package/dist/IndexBuffer.d.ts +16 -1
- package/dist/IndexBuffer.d.ts.map +1 -1
- package/dist/IndexBuffer.js +46 -1
- package/dist/IndexBuffer.js.map +1 -1
- package/dist/InstancedPBRMaterial.d.ts +2 -0
- package/dist/InstancedPBRMaterial.d.ts.map +1 -1
- package/dist/InstancedPBRMaterial.js +1 -0
- package/dist/InstancedPBRMaterial.js.map +1 -1
- package/dist/Material.d.ts +9 -0
- package/dist/Material.d.ts.map +1 -1
- package/dist/Material.js +3 -0
- package/dist/Material.js.map +1 -1
- package/dist/PBRMaterial.d.ts +2 -0
- package/dist/PBRMaterial.d.ts.map +1 -1
- package/dist/PBRMaterial.js +1 -0
- package/dist/PBRMaterial.js.map +1 -1
- package/dist/PortableShaderMaterial.d.ts +13 -3
- package/dist/PortableShaderMaterial.d.ts.map +1 -1
- package/dist/PortableShaderMaterial.js +24 -12
- package/dist/PortableShaderMaterial.js.map +1 -1
- package/dist/RenderDevice.d.ts +98 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js +129 -0
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.d.ts +94 -0
- package/dist/Renderer.d.ts.map +1 -1
- package/dist/Renderer.js +321 -26
- package/dist/Renderer.js.map +1 -1
- package/dist/Sampler.d.ts +35 -0
- package/dist/Sampler.d.ts.map +1 -1
- package/dist/Sampler.js +96 -0
- package/dist/Sampler.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts +14 -0
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +94 -4
- package/dist/ShaderLibraryCore.js.map +1 -1
- package/dist/Texture.d.ts +53 -1
- package/dist/Texture.d.ts.map +1 -1
- package/dist/Texture.js +53 -2
- package/dist/Texture.js.map +1 -1
- package/dist/UnlitMaterial.d.ts +46 -0
- package/dist/UnlitMaterial.d.ts.map +1 -1
- package/dist/UnlitMaterial.js +32 -3
- package/dist/UnlitMaterial.js.map +1 -1
- package/dist/VertexFormat.d.ts +19 -1
- package/dist/VertexFormat.d.ts.map +1 -1
- package/dist/VertexFormat.js +40 -1
- package/dist/VertexFormat.js.map +1 -1
- package/dist/WebGL2Device.d.ts +16 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +31 -6
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +92 -1
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +417 -44
- package/dist/WebGPUDevice.js.map +1 -1
- package/dist/batching/BatchedGeometryPool.d.ts +22 -1
- package/dist/batching/BatchedGeometryPool.d.ts.map +1 -1
- package/dist/batching/BatchedGeometryPool.js +50 -2
- package/dist/batching/BatchedGeometryPool.js.map +1 -1
- package/dist/batching/DrawDataTexture.d.ts +21 -1
- package/dist/batching/DrawDataTexture.d.ts.map +1 -1
- package/dist/batching/DrawDataTexture.js +30 -4
- package/dist/batching/DrawDataTexture.js.map +1 -1
- package/dist/batching/MultiDrawBatch.d.ts +26 -0
- package/dist/batching/MultiDrawBatch.d.ts.map +1 -1
- package/dist/batching/MultiDrawBatch.js +64 -2
- package/dist/batching/MultiDrawBatch.js.map +1 -1
- package/dist/batching/index.d.ts +2 -2
- package/dist/batching/index.d.ts.map +1 -1
- package/dist/batching/index.js +2 -2
- package/dist/batching/index.js.map +1 -1
- package/dist/compute/ComputeContext.d.ts +84 -0
- package/dist/compute/ComputeContext.d.ts.map +1 -0
- package/dist/compute/ComputeContext.js +371 -0
- package/dist/compute/ComputeContext.js.map +1 -0
- package/dist/compute/index.d.ts +4 -0
- package/dist/compute/index.d.ts.map +1 -0
- package/dist/compute/index.js +3 -0
- package/dist/compute/index.js.map +1 -0
- package/dist/compute/types.d.ts +108 -0
- package/dist/compute/types.d.ts.map +1 -0
- package/dist/compute/types.js +25 -0
- package/dist/compute/types.js.map +1 -0
- package/dist/contracts/core.d.ts +1 -1
- package/dist/contracts/core.d.ts.map +1 -1
- package/dist/contracts/core.js.map +1 -1
- package/dist/contracts/flags.state.d.ts.map +1 -1
- package/dist/contracts/flags.state.js +20 -1
- package/dist/contracts/flags.state.js.map +1 -1
- package/dist/contracts/output.d.ts +1 -1
- package/dist/contracts/output.d.ts.map +1 -1
- package/dist/contracts/output.js.map +1 -1
- package/dist/contracts/post.d.ts +7 -0
- package/dist/contracts/post.d.ts.map +1 -1
- package/dist/contracts/post.js.map +1 -1
- package/dist/contracts/renderItem.d.ts +15 -0
- package/dist/contracts/renderItem.d.ts.map +1 -1
- package/dist/contracts/renderSource.d.ts +3 -1
- package/dist/contracts/renderSource.d.ts.map +1 -1
- package/dist/contracts/sampling.d.ts +23 -0
- package/dist/contracts/sampling.d.ts.map +1 -1
- package/dist/contracts/sampling.js.map +1 -1
- package/dist/environment/EnvironmentCache.d.ts.map +1 -1
- package/dist/environment/EnvironmentCache.js +7 -3
- package/dist/environment/EnvironmentCache.js.map +1 -1
- package/dist/environment/RoomEnvironmentScene.d.ts +2 -2
- package/dist/environment/RoomEnvironmentScene.d.ts.map +1 -1
- package/dist/environment/RoomEnvironmentScene.js.map +1 -1
- package/dist/geometry/Circle.d.ts +14 -0
- package/dist/geometry/Circle.d.ts.map +1 -0
- package/dist/geometry/Circle.js +31 -0
- package/dist/geometry/Circle.js.map +1 -0
- package/dist/geometry/Cone.d.ts +37 -0
- package/dist/geometry/Cone.d.ts.map +1 -0
- package/dist/geometry/Cone.js +129 -0
- package/dist/geometry/Cone.js.map +1 -0
- package/dist/geometry/Convex.d.ts +19 -0
- package/dist/geometry/Convex.d.ts.map +1 -0
- package/dist/geometry/Convex.js +27 -0
- package/dist/geometry/Convex.js.map +1 -0
- package/dist/geometry/Edges.d.ts +15 -0
- package/dist/geometry/Edges.d.ts.map +1 -0
- package/dist/geometry/Edges.js +98 -0
- package/dist/geometry/Edges.js.map +1 -0
- package/dist/geometry/Extrude.d.ts +29 -0
- package/dist/geometry/Extrude.d.ts.map +1 -0
- package/dist/geometry/Extrude.js +403 -0
- package/dist/geometry/Extrude.js.map +1 -0
- package/dist/geometry/Lathe.d.ts +20 -0
- package/dist/geometry/Lathe.d.ts.map +1 -0
- package/dist/geometry/Lathe.js +94 -0
- package/dist/geometry/Lathe.js.map +1 -0
- package/dist/geometry/Polyhedron.d.ts +21 -0
- package/dist/geometry/Polyhedron.d.ts.map +1 -0
- package/dist/geometry/Polyhedron.js +201 -0
- package/dist/geometry/Polyhedron.js.map +1 -0
- package/dist/geometry/Primitives.d.ts +38 -2
- package/dist/geometry/Primitives.d.ts.map +1 -1
- package/dist/geometry/Primitives.js +114 -0
- package/dist/geometry/Primitives.js.map +1 -1
- package/dist/geometry/Ring.d.ts +16 -0
- package/dist/geometry/Ring.d.ts.map +1 -0
- package/dist/geometry/Ring.js +44 -0
- package/dist/geometry/Ring.js.map +1 -0
- package/dist/geometry/RoundedBox.d.ts +22 -0
- package/dist/geometry/RoundedBox.d.ts.map +1 -0
- package/dist/geometry/RoundedBox.js +191 -0
- package/dist/geometry/RoundedBox.js.map +1 -0
- package/dist/geometry/Shape.d.ts +15 -0
- package/dist/geometry/Shape.d.ts.map +1 -0
- package/dist/geometry/Shape.js +44 -0
- package/dist/geometry/Shape.js.map +1 -0
- package/dist/geometry/Support.d.ts +137 -0
- package/dist/geometry/Support.d.ts.map +1 -0
- package/dist/geometry/Support.js +1066 -0
- package/dist/geometry/Support.js.map +1 -0
- package/dist/geometry/TorusKnot.d.ts +18 -0
- package/dist/geometry/TorusKnot.d.ts.map +1 -0
- package/dist/geometry/TorusKnot.js +83 -0
- package/dist/geometry/TorusKnot.js.map +1 -0
- package/dist/geometry/Tube.d.ts +19 -0
- package/dist/geometry/Tube.d.ts.map +1 -0
- package/dist/geometry/Tube.js +70 -0
- package/dist/geometry/Tube.js.map +1 -0
- package/dist/geometry/Wireframe.d.ts +19 -0
- package/dist/geometry/Wireframe.d.ts.map +1 -0
- package/dist/geometry/Wireframe.js +63 -0
- package/dist/geometry/Wireframe.js.map +1 -0
- package/dist/index.d.ts +38 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -3
- package/dist/index.js.map +1 -1
- package/dist/lanes/prd11.js +1 -1
- package/dist/lanes/prd11.js.map +1 -1
- package/dist/output/ToneMappingOperators.glsl.d.ts +1 -1
- package/dist/output/ToneMappingOperators.glsl.d.ts.map +1 -1
- package/dist/output/ToneMappingOperators.glsl.js +9 -1
- package/dist/output/ToneMappingOperators.glsl.js.map +1 -1
- package/dist/post/v2Entry.d.ts +1 -0
- package/dist/post/v2Entry.d.ts.map +1 -1
- package/dist/post/v2Entry.js.map +1 -1
- package/dist/post/v2Stages.d.ts +30 -1
- package/dist/post/v2Stages.d.ts.map +1 -1
- package/dist/post/v2Stages.js +87 -21
- package/dist/post/v2Stages.js.map +1 -1
- package/dist/program/ProgramCache.d.ts.map +1 -1
- package/dist/program/ProgramCache.js +12 -0
- package/dist/program/ProgramCache.js.map +1 -1
- package/dist/program/ProgramGenerator.d.ts.map +1 -1
- package/dist/program/ProgramGenerator.js +3 -0
- package/dist/program/ProgramGenerator.js.map +1 -1
- package/dist/reference/PostProcessPass.d.ts +1 -1
- package/dist/reference/PostProcessPass.d.ts.map +1 -1
- package/dist/reference/PostProcessPass.js +8 -1
- package/dist/reference/PostProcessPass.js.map +1 -1
- package/dist/renderer/CullingBatching.d.ts +44 -2
- package/dist/renderer/CullingBatching.d.ts.map +1 -1
- package/dist/renderer/CullingBatching.js +66 -11
- package/dist/renderer/CullingBatching.js.map +1 -1
- package/dist/shaders/chunks/lighting_mirrors.d.ts +2 -2
- package/dist/shaders/chunks/lighting_mirrors.d.ts.map +1 -1
- package/dist/shaders/chunks/lighting_mirrors.js.map +1 -1
- package/dist/shadows/DirectionalCascadeFitter.d.ts +8 -8
- package/dist/shadows/DirectionalCascadeFitter.d.ts.map +1 -1
- package/dist/shadows/DirectionalCascadeFitter.js.map +1 -1
- package/dist/shadows/ShadowAtlas.d.ts +5 -5
- package/dist/shadows/ShadowAtlas.d.ts.map +1 -1
- package/dist/shadows/ShadowAtlas.js.map +1 -1
- package/dist/vfx/ProceduralVolumeEmitter.d.ts +11 -0
- package/dist/vfx/ProceduralVolumeEmitter.d.ts.map +1 -1
- package/dist/vfx/ProceduralVolumeEmitter.js +20 -6
- package/dist/vfx/ProceduralVolumeEmitter.js.map +1 -1
- package/dist/webgl2/ContextLifecycle.d.ts +36 -0
- package/dist/webgl2/ContextLifecycle.d.ts.map +1 -1
- package/dist/webgl2/ContextLifecycle.js +110 -2
- package/dist/webgl2/ContextLifecycle.js.map +1 -1
- package/dist/webgl2/LegacyPost.d.ts +16 -1
- package/dist/webgl2/LegacyPost.d.ts.map +1 -1
- package/dist/webgl2/LegacyPost.js +49 -16
- package/dist/webgl2/LegacyPost.js.map +1 -1
- package/dist/webgl2/MultiDraw.d.ts.map +1 -1
- package/dist/webgl2/MultiDraw.js +42 -2
- package/dist/webgl2/MultiDraw.js.map +1 -1
- package/dist/webgl2/TextureUpload.d.ts +7 -0
- package/dist/webgl2/TextureUpload.d.ts.map +1 -1
- package/dist/webgl2/TextureUpload.js +54 -10
- package/dist/webgl2/TextureUpload.js.map +1 -1
- package/package.json +3 -3
package/dist/WebGPUDevice.js
CHANGED
|
@@ -5,6 +5,8 @@ import { normalizeWebGPUTemporalOptions, WEBGPU_TAA_FRAGMENT, WEBGPU_TAA_OUTPUT_
|
|
|
5
5
|
import { defaultWebGPUBloomWeights, normalizeWebGPUColorGradeOptions, normalizeWebGPUBloomOptions, webgpuBlurFragment, webgpuBloomCompositeFragment, webgpuBrightExtractFragment, webgpuColorGradeFragment, webgpuFxaaFragment, WEBGPU_POST_VERTEX_WGSL } from "./webgpu/WebGPUPostShaders.js";
|
|
6
6
|
import { DEFAULT_DEPTH_SHADER_MARKER } from "./ShaderLibraryCore.js";
|
|
7
7
|
import { reflectShaderSources } from "./ShaderReflection.js";
|
|
8
|
+
import { recordPostSkipped, recordPostStage } from "./renderer/postSkipped.js";
|
|
9
|
+
import { registeredPostPasses } from "./contracts/post.js";
|
|
8
10
|
import { Texture, bytesPerPixel, isCompressedTextureFormat } from "./Texture.js";
|
|
9
11
|
import { isTextureBinding, TextureBinding } from "./TextureBinding.js";
|
|
10
12
|
import {} from "./VertexFormat.js";
|
|
@@ -162,6 +164,13 @@ export class WebGPUDevice {
|
|
|
162
164
|
drawCalls = 0;
|
|
163
165
|
viewportWidth = 0;
|
|
164
166
|
viewportHeight = 0;
|
|
167
|
+
/** SC-01: viewport/scissor override (origin bottom-left, three convention) and the target it was set on; applies only while that target is bound. */
|
|
168
|
+
viewStateTarget = null;
|
|
169
|
+
viewportOverride = null;
|
|
170
|
+
scissorOverride = null;
|
|
171
|
+
scissorTestOn = false;
|
|
172
|
+
/** XR/SC-01: viewport scoped to the default (canvas) target that survives `beginFrame`. */
|
|
173
|
+
persistentViewportRect = null;
|
|
165
174
|
clearColor = [0, 0, 0, 1];
|
|
166
175
|
lastError = null;
|
|
167
176
|
nativeSubmissions = 0;
|
|
@@ -202,6 +211,14 @@ export class WebGPUDevice {
|
|
|
202
211
|
shaders = new Set();
|
|
203
212
|
renderTargets = new Set();
|
|
204
213
|
nativeSampledTextures = new Map();
|
|
214
|
+
/**
|
|
215
|
+
* threejs-parity ST-03 (L4): `Texture.revision` recorded at the last native
|
|
216
|
+
* upload. A mismatch on a cached binding re-uploads the texture in place —
|
|
217
|
+
* the same revision gate WebGL2 has in `webgl2/TextureUpload.ts` — so
|
|
218
|
+
* canvas/video/data textures bound through `texture.*` handles refresh
|
|
219
|
+
* on `needsUpdate` instead of staying frozen at the first upload.
|
|
220
|
+
*/
|
|
221
|
+
nativeSampledTextureRevisions = new Map();
|
|
205
222
|
activeRenderTarget = null;
|
|
206
223
|
canvasDepthTexture = null;
|
|
207
224
|
canvasDepthView = null;
|
|
@@ -225,6 +242,10 @@ export class WebGPUDevice {
|
|
|
225
242
|
}
|
|
226
243
|
return new WebGPUDevice(adapter, device, canvasContext, presentationFormat);
|
|
227
244
|
}
|
|
245
|
+
/** The native `GPUDevice` this render device owns; used by `createComputeContext` (SH-04). */
|
|
246
|
+
get nativeGpuDevice() {
|
|
247
|
+
return this.device;
|
|
248
|
+
}
|
|
228
249
|
constructor(adapter, device, canvasContext, presentationFormat) {
|
|
229
250
|
this.adapter = adapter;
|
|
230
251
|
this.device = device;
|
|
@@ -661,9 +682,65 @@ export class WebGPUDevice {
|
|
|
661
682
|
this.lastError = null;
|
|
662
683
|
this.viewportWidth = width;
|
|
663
684
|
this.viewportHeight = height;
|
|
685
|
+
this.viewportOverride = null;
|
|
686
|
+
this.scissorOverride = null;
|
|
687
|
+
this.scissorTestOn = false;
|
|
688
|
+
this.viewStateTarget = null;
|
|
664
689
|
this.releaseDisposedNativeSampledTextures();
|
|
665
690
|
this.ensureCanvasDepthAttachment(width, height);
|
|
666
691
|
}
|
|
692
|
+
/** threejs-parity L7/SC-01: raster viewport in target pixels, origin bottom-left; converted to WebGPU's top-left origin per draw pass. */
|
|
693
|
+
setViewport(x, y, width, height, options) {
|
|
694
|
+
this.assertAlive();
|
|
695
|
+
if (!(width > 0) || !(height > 0)) {
|
|
696
|
+
throw new RenderDeviceError("Viewport dimensions must be positive", "INVALID_VIEWPORT", { width, height });
|
|
697
|
+
}
|
|
698
|
+
if (options?.persistent) {
|
|
699
|
+
this.persistentViewportRect = { x, y, width, height };
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
this.viewportOverride = { x, y, width, height };
|
|
703
|
+
this.viewStateTarget = this.activeRenderTarget;
|
|
704
|
+
}
|
|
705
|
+
/** Clears the persistent viewport and the frame-scoped override. */
|
|
706
|
+
resetViewport() {
|
|
707
|
+
this.assertAlive();
|
|
708
|
+
this.persistentViewportRect = null;
|
|
709
|
+
this.viewportOverride = null;
|
|
710
|
+
this.viewStateTarget = null;
|
|
711
|
+
}
|
|
712
|
+
setScissor(x, y, width, height) {
|
|
713
|
+
this.assertAlive();
|
|
714
|
+
if (width < 0 || height < 0) {
|
|
715
|
+
throw new RenderDeviceError("Scissor dimensions must be non-negative", "INVALID_SCISSOR", { width, height });
|
|
716
|
+
}
|
|
717
|
+
this.scissorOverride = { x, y, width, height };
|
|
718
|
+
this.viewStateTarget = this.activeRenderTarget;
|
|
719
|
+
}
|
|
720
|
+
setScissorTest(enabled) {
|
|
721
|
+
this.assertAlive();
|
|
722
|
+
this.scissorTestOn = enabled;
|
|
723
|
+
this.viewStateTarget = this.activeRenderTarget;
|
|
724
|
+
}
|
|
725
|
+
/** Applies the live viewport/scissor override (if any) to a draw pass. Clears are full-attachment load ops and are never scissored. */
|
|
726
|
+
applyViewStateToPass(pass) {
|
|
727
|
+
const targetHeight = this.activeRenderTarget ? this.activeRenderTarget.height : this.viewportHeight;
|
|
728
|
+
if (this.viewStateTarget !== this.activeRenderTarget || !this.viewportOverride) {
|
|
729
|
+
const persistent = this.activeRenderTarget === null ? this.persistentViewportRect : null;
|
|
730
|
+
if (persistent)
|
|
731
|
+
pass.setViewport?.(persistent.x, targetHeight - persistent.y - persistent.height, persistent.width, persistent.height, 0, 1);
|
|
732
|
+
}
|
|
733
|
+
if (this.viewStateTarget !== this.activeRenderTarget)
|
|
734
|
+
return;
|
|
735
|
+
if (this.viewportOverride) {
|
|
736
|
+
const v = this.viewportOverride;
|
|
737
|
+
pass.setViewport?.(v.x, targetHeight - v.y - v.height, v.width, v.height, 0, 1);
|
|
738
|
+
}
|
|
739
|
+
if (this.scissorTestOn && this.scissorOverride) {
|
|
740
|
+
const r = this.scissorOverride;
|
|
741
|
+
pass.setScissorRect?.(r.x, targetHeight - r.y - r.height, r.width, r.height);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
667
744
|
clear(color) {
|
|
668
745
|
this.assertFrame();
|
|
669
746
|
this.clearColor = color;
|
|
@@ -1027,8 +1104,29 @@ export class WebGPUDevice {
|
|
|
1027
1104
|
const owned = [];
|
|
1028
1105
|
let current = source;
|
|
1029
1106
|
const track = (target) => { owned.push(target); current = target; };
|
|
1107
|
+
/*
|
|
1108
|
+
* threejs-parity SH-03/SH-06: C-13 custom passes registered via
|
|
1109
|
+
* `registerPostPass` (`app.post.addPostPass` / `effects.shaderPass`) run on
|
|
1110
|
+
* the native WebGPU chain too. Anchor resolution mirrors the v2 graph's
|
|
1111
|
+
* named slots onto this chain's pass order; a registered pass missing its
|
|
1112
|
+
* WGSL fragment is recorded `WGSL_PROGRAM_MISSING:<id>` and skipped — never
|
|
1113
|
+
* silently, never as a GLSL fallback.
|
|
1114
|
+
*/
|
|
1115
|
+
const customAt = this.webgpuCustomPassPositions(options.passes);
|
|
1116
|
+
const runCustomsAt = (position) => {
|
|
1117
|
+
const customs = customAt.get(position);
|
|
1118
|
+
if (!customs?.length)
|
|
1119
|
+
return;
|
|
1120
|
+
for (const custom of customs) {
|
|
1121
|
+
const out = this.executeWebGPUCustomPass(custom, current);
|
|
1122
|
+
if (out)
|
|
1123
|
+
track(out);
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1030
1126
|
try {
|
|
1031
|
-
for (
|
|
1127
|
+
for (let passIndex = 0; passIndex < options.passes.length; passIndex += 1) {
|
|
1128
|
+
runCustomsAt(passIndex);
|
|
1129
|
+
const pass = options.passes[passIndex];
|
|
1032
1130
|
if (pass.name === "bloom")
|
|
1033
1131
|
track(this.executeWebGPUBloom(current, pass.options));
|
|
1034
1132
|
else if (pass.name === "color-grade")
|
|
@@ -1061,6 +1159,7 @@ export class WebGPUDevice {
|
|
|
1061
1159
|
current = target;
|
|
1062
1160
|
}
|
|
1063
1161
|
}
|
|
1162
|
+
runCustomsAt(options.passes.length);
|
|
1064
1163
|
const target = options.outputTarget ?? null;
|
|
1065
1164
|
if (target === current)
|
|
1066
1165
|
throw new RenderDeviceError("Native WebGPU presentation source/output alias", "INVALID_RESOURCE");
|
|
@@ -1108,6 +1207,139 @@ export class WebGPUDevice {
|
|
|
1108
1207
|
throw error;
|
|
1109
1208
|
}
|
|
1110
1209
|
}
|
|
1210
|
+
/**
|
|
1211
|
+
* SH-03: maps each registered custom pass's `insertAt` anchor onto a position
|
|
1212
|
+
* in this native chain — `position` runs BEFORE `options.passes[position]` and
|
|
1213
|
+
* `options.passes.length` runs after the last pass (before the present copy).
|
|
1214
|
+
* Deterministic fallbacks keep passes attached when their anchor stage is not
|
|
1215
|
+
* in the submitted chain. Passes sharing a position sort by `order` ascending
|
|
1216
|
+
* (stable — registration order preserved for omitted keys).
|
|
1217
|
+
*/
|
|
1218
|
+
webgpuCustomPassPositions(passes) {
|
|
1219
|
+
const positions = new Map();
|
|
1220
|
+
const customs = registeredPostPasses();
|
|
1221
|
+
if (!customs.length)
|
|
1222
|
+
return positions;
|
|
1223
|
+
const indexOf = (...names) => passes.findIndex((pass) => names.includes(pass.name));
|
|
1224
|
+
const anchorPosition = (insertAt) => {
|
|
1225
|
+
switch (insertAt) {
|
|
1226
|
+
case "after-depth": return 0;
|
|
1227
|
+
case "before-taa": {
|
|
1228
|
+
const taa = indexOf("taa");
|
|
1229
|
+
if (taa >= 0)
|
|
1230
|
+
return taa;
|
|
1231
|
+
const bloom = indexOf("bloom");
|
|
1232
|
+
return bloom >= 0 ? bloom : 0;
|
|
1233
|
+
}
|
|
1234
|
+
case "after-taa": {
|
|
1235
|
+
const taa = indexOf("taa");
|
|
1236
|
+
if (taa >= 0)
|
|
1237
|
+
return taa + 1;
|
|
1238
|
+
// No TAA: the anchor still precedes bloom (v2 graph order is TAA → after-taa → bloom).
|
|
1239
|
+
const bloom = indexOf("bloom");
|
|
1240
|
+
return bloom >= 0 ? bloom : 0;
|
|
1241
|
+
}
|
|
1242
|
+
case "before-tonemap": {
|
|
1243
|
+
const tone = indexOf("tone-mapping");
|
|
1244
|
+
return tone >= 0 ? tone : passes.length;
|
|
1245
|
+
}
|
|
1246
|
+
case "after-tonemap": {
|
|
1247
|
+
const tone = indexOf("tone-mapping");
|
|
1248
|
+
return tone >= 0 ? tone + 1 : passes.length;
|
|
1249
|
+
}
|
|
1250
|
+
default: return passes.length;
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
const orderKey = (pass) => typeof pass.order === "number" && Number.isFinite(pass.order) ? pass.order : Number.MAX_SAFE_INTEGER;
|
|
1254
|
+
for (const pass of [...customs].sort((a, b) => orderKey(a) - orderKey(b))) {
|
|
1255
|
+
const position = anchorPosition(pass.insertAt);
|
|
1256
|
+
const list = positions.get(position) ?? [];
|
|
1257
|
+
list.push(pass);
|
|
1258
|
+
positions.set(position, list);
|
|
1259
|
+
}
|
|
1260
|
+
return positions;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* SH-03/SH-06: one C-13 custom pass as a native WGSL fullscreen draw.
|
|
1264
|
+
*
|
|
1265
|
+
* Bind convention (fixed by runWebGPUFullscreenPass): binding 0 = uniform
|
|
1266
|
+
* buffer (`vec4f` array — `values[0]` is `vec4(texelSize, timeSeconds, 0)`,
|
|
1267
|
+
* declared uniforms follow in registration order, one vec4 each), binding 1 =
|
|
1268
|
+
* linear sampler, binding 2 = `u_color`, binding 3 = `u_depthLinear` (only
|
|
1269
|
+
* when `inputs` declares "depth"), binding 4 = `u_velocity` (only for
|
|
1270
|
+
* "velocity"). The authored `fragment.wgsl` is a complete fragment stage;
|
|
1271
|
+
* its `@fragment` entry point is detected or defaults to `fs_main`.
|
|
1272
|
+
*
|
|
1273
|
+
* Returns null when the pass is disabled or has no WGSL (recorded as
|
|
1274
|
+
* `WGSL_PROGRAM_MISSING:<id>` in the post skipped-reasons channel).
|
|
1275
|
+
*/
|
|
1276
|
+
executeWebGPUCustomPass(pass, source) {
|
|
1277
|
+
const timeSeconds = typeof performance !== "undefined" ? performance.now() / 1000 : Date.now() / 1000;
|
|
1278
|
+
const frameContext = {
|
|
1279
|
+
device: this,
|
|
1280
|
+
width: source.width,
|
|
1281
|
+
height: source.height,
|
|
1282
|
+
timeSeconds,
|
|
1283
|
+
frameIndex: 0
|
|
1284
|
+
};
|
|
1285
|
+
if (pass.enabled && !pass.enabled(frameContext))
|
|
1286
|
+
return null;
|
|
1287
|
+
const wgsl = pass.fragment.wgsl;
|
|
1288
|
+
if (typeof wgsl !== "string" || !wgsl.trim()) {
|
|
1289
|
+
recordPostSkipped(`WGSL_PROGRAM_MISSING:${pass.id}`);
|
|
1290
|
+
return null;
|
|
1291
|
+
}
|
|
1292
|
+
const src = this.requireRenderTarget(source);
|
|
1293
|
+
const srcView = src.nativeView;
|
|
1294
|
+
if (!srcView) {
|
|
1295
|
+
throw new RenderDeviceError("WebGPU custom pass source target has no native view", "NATIVE_POST_UNSUPPORTED", { label: src.label });
|
|
1296
|
+
}
|
|
1297
|
+
recordPostStage(`custom:${pass.id}@${pass.insertAt}`);
|
|
1298
|
+
const wantsDepth = pass.inputs.includes("depth");
|
|
1299
|
+
const wantsVelocity = pass.inputs.includes("velocity");
|
|
1300
|
+
const target = this.createRenderTarget({
|
|
1301
|
+
width: src.width,
|
|
1302
|
+
height: src.height,
|
|
1303
|
+
format: pass.space === "display" ? "rgba8" : "rgba16f",
|
|
1304
|
+
label: `a3d-webgpu-custom-${pass.id}`
|
|
1305
|
+
});
|
|
1306
|
+
const views = [srcView];
|
|
1307
|
+
if (wantsDepth)
|
|
1308
|
+
views.push(this.requireRenderTarget(this.webgpuPostBlankTarget()).nativeView);
|
|
1309
|
+
if (wantsVelocity)
|
|
1310
|
+
views.push(this.requireRenderTarget(this.webgpuPostBlankTarget()).nativeView);
|
|
1311
|
+
const declared = pass.uniforms?.(frameContext) ?? {};
|
|
1312
|
+
const packed = [1 / src.width, 1 / src.height, timeSeconds, 0];
|
|
1313
|
+
for (const value of Object.values(declared)) {
|
|
1314
|
+
const scalars = Array.isArray(value) ? [...value] : [value];
|
|
1315
|
+
for (let offset = 0; offset < Math.max(1, scalars.length); offset += 4) {
|
|
1316
|
+
for (let lane = 0; lane < 4; lane += 1)
|
|
1317
|
+
packed.push(Number.isFinite(scalars[offset + lane]) ? scalars[offset + lane] : 0);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const entryPoint = wgsl.match(/@fragment\s*fn\s+([A-Za-z_]\w*)/)?.[1] ?? "fs_main";
|
|
1321
|
+
try {
|
|
1322
|
+
this.runWebGPUFullscreenPass({
|
|
1323
|
+
label: `webgpu-custom:${pass.id}`,
|
|
1324
|
+
fragment: wgsl,
|
|
1325
|
+
entryPoint,
|
|
1326
|
+
uniforms: new Float32Array(packed),
|
|
1327
|
+
views,
|
|
1328
|
+
target
|
|
1329
|
+
});
|
|
1330
|
+
return target;
|
|
1331
|
+
}
|
|
1332
|
+
catch (error) {
|
|
1333
|
+
target.dispose();
|
|
1334
|
+
throw error;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
/** 1×1 stand-in for declared depth/velocity inputs this chain cannot supply (matches the WebGL2 blankTex fallback). */
|
|
1338
|
+
webgpuPostBlankTarget() {
|
|
1339
|
+
this.webgpuPostBlank ??= this.createRenderTarget({ width: 1, height: 1, format: "rgba8", label: "a3d-webgpu-custom-blank" });
|
|
1340
|
+
return this.webgpuPostBlank;
|
|
1341
|
+
}
|
|
1342
|
+
webgpuPostBlank = null;
|
|
1111
1343
|
runWebGPUFullscreenPass(args) {
|
|
1112
1344
|
// No assertFrame here on purpose: these are target-to-target passes with
|
|
1113
1345
|
// their own encoders, so they run both inside a Renderer frame (scene ->
|
|
@@ -1229,6 +1461,7 @@ export class WebGPUDevice {
|
|
|
1229
1461
|
for (const resource of this.nativeSampledTextures.values())
|
|
1230
1462
|
resource.texture.destroy();
|
|
1231
1463
|
this.nativeSampledTextures.clear();
|
|
1464
|
+
this.nativeSampledTextureRevisions.clear();
|
|
1232
1465
|
this.canvasDepthTexture?.destroy();
|
|
1233
1466
|
this.canvasDepthTexture = null;
|
|
1234
1467
|
this.canvasDepthView = null;
|
|
@@ -1476,6 +1709,7 @@ export class WebGPUDevice {
|
|
|
1476
1709
|
});
|
|
1477
1710
|
this.nativeRenderPasses += 1;
|
|
1478
1711
|
pass.setPipeline(pipeline);
|
|
1712
|
+
this.applyViewStateToPass(pass);
|
|
1479
1713
|
pass.setVertexBuffer(0, vertexBuffer.handle);
|
|
1480
1714
|
if (bindGroup)
|
|
1481
1715
|
pass.setBindGroup?.(0, bindGroup);
|
|
@@ -1591,65 +1825,193 @@ export class WebGPUDevice {
|
|
|
1591
1825
|
actual: true
|
|
1592
1826
|
};
|
|
1593
1827
|
}
|
|
1828
|
+
// ST-03 (L4): live sources (video) may mark a new frame before the
|
|
1829
|
+
// revision comparison runs.
|
|
1830
|
+
texture.pollContentRevision?.();
|
|
1594
1831
|
let resource = this.nativeSampledTextures.get(texture);
|
|
1832
|
+
if (resource && this.nativeSampledTextureRevisions.get(texture) !== texture.revision) {
|
|
1833
|
+
// ST-03: revision-bumped content (Texture.update / handle needsUpdate /
|
|
1834
|
+
// a video frame) — re-upload into the same GPUTexture so every binding
|
|
1835
|
+
// built on it refreshes without re-allocating.
|
|
1836
|
+
if (this.uploadNativeSampledTextureRevision(texture, resource.texture)) {
|
|
1837
|
+
this.nativeSampledTextureRevisions.set(texture, texture.revision);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1595
1840
|
if (!resource) {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1841
|
+
const created = this.createNativeSampledTextureResource(texture);
|
|
1842
|
+
if (!created)
|
|
1843
|
+
return null;
|
|
1844
|
+
resource = created;
|
|
1845
|
+
this.nativeSampledTextures.set(texture, created);
|
|
1846
|
+
this.nativeSampledTextureRevisions.set(texture, texture.revision);
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
view: resource.view,
|
|
1850
|
+
sampler: this.device.createSampler(webgpuSamplerDescriptor(binding.sampler)),
|
|
1851
|
+
actual: true
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* threejs-parity ST-01/ST-02/ST-03 (L4): allocate and upload the native
|
|
1856
|
+
* texture for a `Texture`. Source-backed textures prefer
|
|
1857
|
+
* `queue.copyExternalImageToTexture` (the level-0 external-image copy
|
|
1858
|
+
* replaces the draw-to-canvas readback entirely); when a mip chain is
|
|
1859
|
+
* requested — or the queue predates the copy — the canvas readback path
|
|
1860
|
+
* still produces real downsized levels so mipmap samplers stay complete.
|
|
1861
|
+
* `2d-array` textures upload every layer in a single writeTexture and cube
|
|
1862
|
+
* textures write all six faces at `origin.z = face`.
|
|
1863
|
+
*/
|
|
1864
|
+
createNativeSampledTextureResource(texture) {
|
|
1865
|
+
if (!this.device.createTexture)
|
|
1866
|
+
return null;
|
|
1867
|
+
if (texture.source) {
|
|
1868
|
+
const copyExternalImageToTexture = this.device.queue.copyExternalImageToTexture;
|
|
1869
|
+
const useExternalCopy = typeof copyExternalImageToTexture === "function" && !texture.generateMipmaps;
|
|
1870
|
+
const sourceLevels = useExternalCopy ? [] : nativeTextureSourceLevels(texture);
|
|
1871
|
+
if (!useExternalCopy && sourceLevels.length === 0)
|
|
1872
|
+
return null;
|
|
1873
|
+
const nativeTexture = this.device.createTexture({
|
|
1874
|
+
label: texture.label,
|
|
1875
|
+
size: { width: texture.width, height: texture.height, depthOrArrayLayers: 1 },
|
|
1876
|
+
format: webgpuSampledTextureFormat(texture, "rgba8"),
|
|
1877
|
+
// copyExternalImageToTexture additionally requires RENDER_ATTACHMENT.
|
|
1878
|
+
usage: TEXTURE_USAGE.TEXTURE_BINDING | TEXTURE_USAGE.COPY_DST | (useExternalCopy ? TEXTURE_USAGE.RENDER_ATTACHMENT : 0),
|
|
1879
|
+
mipLevelCount: useExternalCopy ? 1 : sourceLevels.length
|
|
1880
|
+
});
|
|
1881
|
+
if (useExternalCopy) {
|
|
1882
|
+
copyExternalImageToTexture.call(this.device.queue, { source: texture.source, flipY: texture.flipY }, { texture: nativeTexture }, { width: texture.width, height: texture.height, depthOrArrayLayers: 1 });
|
|
1883
|
+
this.nativeTextureUploads += 1;
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1607
1886
|
const writeTexture = this.device.queue.writeTexture;
|
|
1608
|
-
if (!writeTexture)
|
|
1887
|
+
if (!writeTexture) {
|
|
1888
|
+
nativeTexture.destroy();
|
|
1609
1889
|
return null;
|
|
1890
|
+
}
|
|
1610
1891
|
sourceLevels.forEach((level, mipLevel) => {
|
|
1611
1892
|
writeTexture.call(this.device.queue, { texture: nativeTexture, mipLevel }, level.data, { bytesPerRow: level.bytesPerRow, rowsPerImage: level.height }, { width: level.width, height: level.height, depthOrArrayLayers: 1 });
|
|
1612
1893
|
this.nativeTextureUploads += 1;
|
|
1613
1894
|
});
|
|
1614
|
-
resource = { texture: nativeTexture, view: nativeTexture.createView() };
|
|
1615
1895
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
this.device.queue.writeTexture({ texture: nativeTexture, mipLevel }, level.data, { bytesPerRow: level.width * levelBytesPerPixel, rowsPerImage: level.height }, { width: level.width, height: level.height, depthOrArrayLayers: 1 });
|
|
1636
|
-
this.nativeTextureUploads += 1;
|
|
1637
|
-
}
|
|
1638
|
-
resource = { texture: nativeTexture, view: nativeTexture.createView() };
|
|
1896
|
+
return { texture: nativeTexture, view: nativeTexture.createView() };
|
|
1897
|
+
}
|
|
1898
|
+
if (texture.dimension === "cube") {
|
|
1899
|
+
const faces = texture.cubeFaces;
|
|
1900
|
+
if (faces.length !== 6)
|
|
1901
|
+
return null;
|
|
1902
|
+
const mipLevelCount = faces[0]?.mipLevels.length ?? 0;
|
|
1903
|
+
if (mipLevelCount === 0)
|
|
1904
|
+
return null;
|
|
1905
|
+
const nativeTexture = this.device.createTexture({
|
|
1906
|
+
label: texture.label,
|
|
1907
|
+
size: { width: texture.width, height: texture.height, depthOrArrayLayers: 6 },
|
|
1908
|
+
format: webgpuSampledTextureFormat(texture, texture.format),
|
|
1909
|
+
usage: TEXTURE_USAGE.TEXTURE_BINDING | TEXTURE_USAGE.COPY_DST,
|
|
1910
|
+
mipLevelCount
|
|
1911
|
+
});
|
|
1912
|
+
if (!this.writeNativeCubeFaceLevels(texture, nativeTexture)) {
|
|
1913
|
+
nativeTexture.destroy();
|
|
1914
|
+
return null;
|
|
1639
1915
|
}
|
|
1640
|
-
|
|
1916
|
+
return { texture: nativeTexture, view: nativeTexture.createView({ dimension: "cube" }) };
|
|
1641
1917
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1918
|
+
const sampledTexture = nativeSampledTextureLevels(texture);
|
|
1919
|
+
if (!sampledTexture)
|
|
1920
|
+
return null;
|
|
1921
|
+
const { levels, format } = sampledTexture;
|
|
1922
|
+
const baseLevel = levels[0];
|
|
1923
|
+
if (!baseLevel)
|
|
1924
|
+
return null;
|
|
1925
|
+
const levelBytesPerPixel = bytesPerPixel(format);
|
|
1926
|
+
if (levels.some((level) => level.data.byteLength < level.width * level.height * levelBytesPerPixel))
|
|
1927
|
+
return null;
|
|
1928
|
+
const depthOrArrayLayers = texture.dimension === "2d-array" ? texture.layers : 1;
|
|
1929
|
+
const nativeTexture = this.device.createTexture({
|
|
1930
|
+
label: texture.label,
|
|
1931
|
+
size: { width: baseLevel.width, height: baseLevel.height, depthOrArrayLayers },
|
|
1932
|
+
format: webgpuSampledTextureFormat(texture, format),
|
|
1933
|
+
usage: TEXTURE_USAGE.TEXTURE_BINDING | TEXTURE_USAGE.COPY_DST,
|
|
1934
|
+
mipLevelCount: levels.length
|
|
1935
|
+
});
|
|
1936
|
+
if (!this.writeNativeTextureLevels(texture, nativeTexture, levels, format)) {
|
|
1937
|
+
nativeTexture.destroy();
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
return { texture: nativeTexture, view: nativeTexture.createView() };
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* threejs-parity ST-01/ST-03 (L4): push every mip level of `levels` into
|
|
1944
|
+
* `nativeTexture`; for `2d-array` textures a single writeTexture covers all
|
|
1945
|
+
* `texture.layers` slices (the level data is stored layer-contiguous).
|
|
1946
|
+
*/
|
|
1947
|
+
writeNativeTextureLevels(texture, nativeTexture, levels, format) {
|
|
1948
|
+
const writeTexture = this.device.queue.writeTexture;
|
|
1949
|
+
if (!writeTexture)
|
|
1950
|
+
return false;
|
|
1951
|
+
const levelBytesPerPixel = bytesPerPixel(format);
|
|
1952
|
+
for (const [mipLevel, level] of levels.entries()) {
|
|
1953
|
+
writeTexture.call(this.device.queue, { texture: nativeTexture, mipLevel }, level.data, { bytesPerRow: level.width * levelBytesPerPixel, rowsPerImage: level.height }, { width: level.width, height: level.height, depthOrArrayLayers: texture.dimension === "2d-array" ? texture.layers : 1 });
|
|
1954
|
+
this.nativeTextureUploads += 1;
|
|
1955
|
+
}
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
/** threejs-parity ST-01 (L4): per-face per-level writeTexture for cube textures. */
|
|
1959
|
+
writeNativeCubeFaceLevels(texture, nativeTexture) {
|
|
1960
|
+
if (!this.device.queue.writeTexture)
|
|
1961
|
+
return false;
|
|
1962
|
+
const faceOrder = ["px", "nx", "py", "ny", "pz", "nz"];
|
|
1963
|
+
const levelBytesPerPixel = bytesPerPixel(texture.format);
|
|
1964
|
+
for (const [faceIndex, face] of faceOrder.entries()) {
|
|
1965
|
+
const faceData = texture.cubeFaces.find((candidate) => candidate.face === face);
|
|
1966
|
+
if (!faceData)
|
|
1967
|
+
return false;
|
|
1968
|
+
for (const [mipLevel, level] of faceData.mipLevels.entries()) {
|
|
1969
|
+
this.device.queue.writeTexture({ texture: nativeTexture, mipLevel, origin: { z: faceIndex } }, level.data, { bytesPerRow: level.width * levelBytesPerPixel, rowsPerImage: level.height }, { width: level.width, height: level.height, depthOrArrayLayers: 1 });
|
|
1970
|
+
this.nativeTextureUploads += 1;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return true;
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* threejs-parity ST-03 (L4): in-place re-upload for a revision-bumped
|
|
1977
|
+
* texture. Mirrors the create paths — external-image copy for source
|
|
1978
|
+
* textures (the video per-frame path), whole-chain writeTexture otherwise.
|
|
1979
|
+
* Returns false when the queue lacks the needed upload entry point so the
|
|
1980
|
+
* cached binding keeps its last-good content instead of corrupting it.
|
|
1981
|
+
*/
|
|
1982
|
+
uploadNativeSampledTextureRevision(texture, nativeTexture) {
|
|
1983
|
+
texture.pendingUpdateRegion = null;
|
|
1984
|
+
texture.needsUpdate = false;
|
|
1985
|
+
if (texture.source) {
|
|
1986
|
+
const copyExternalImageToTexture = this.device.queue.copyExternalImageToTexture;
|
|
1987
|
+
if (typeof copyExternalImageToTexture === "function" && !texture.generateMipmaps) {
|
|
1988
|
+
copyExternalImageToTexture.call(this.device.queue, { source: texture.source, flipY: texture.flipY }, { texture: nativeTexture }, { width: texture.width, height: texture.height, depthOrArrayLayers: 1 });
|
|
1989
|
+
this.nativeTextureUploads += 1;
|
|
1990
|
+
return true;
|
|
1991
|
+
}
|
|
1992
|
+
const sourceLevels = nativeTextureSourceLevels(texture);
|
|
1993
|
+
if (sourceLevels.length === 0 || !this.device.queue.writeTexture)
|
|
1994
|
+
return false;
|
|
1995
|
+
sourceLevels.forEach((level, mipLevel) => {
|
|
1996
|
+
this.device.queue.writeTexture({ texture: nativeTexture, mipLevel }, level.data, { bytesPerRow: level.bytesPerRow, rowsPerImage: level.height }, { width: level.width, height: level.height, depthOrArrayLayers: 1 });
|
|
1997
|
+
this.nativeTextureUploads += 1;
|
|
1998
|
+
});
|
|
1999
|
+
return true;
|
|
2000
|
+
}
|
|
2001
|
+
if (texture.dimension === "cube") {
|
|
2002
|
+
return this.writeNativeCubeFaceLevels(texture, nativeTexture);
|
|
2003
|
+
}
|
|
2004
|
+
const sampledTexture = nativeSampledTextureLevels(texture);
|
|
2005
|
+
if (!sampledTexture)
|
|
2006
|
+
return false;
|
|
2007
|
+
return this.writeNativeTextureLevels(texture, nativeTexture, sampledTexture.levels, sampledTexture.format);
|
|
1647
2008
|
}
|
|
1648
2009
|
releaseDisposedNativeSampledTextures() {
|
|
1649
2010
|
for (const [texture, resource] of this.nativeSampledTextures) {
|
|
1650
2011
|
if (texture instanceof Texture && texture.disposed) {
|
|
1651
2012
|
resource.texture.destroy();
|
|
1652
2013
|
this.nativeSampledTextures.delete(texture);
|
|
2014
|
+
this.nativeSampledTextureRevisions.delete(texture);
|
|
1653
2015
|
}
|
|
1654
2016
|
}
|
|
1655
2017
|
}
|
|
@@ -2076,7 +2438,18 @@ function nativeTextureSourceLevels(texture) {
|
|
|
2076
2438
|
if (!context)
|
|
2077
2439
|
return levels;
|
|
2078
2440
|
context.clearRect(0, 0, levelWidth, levelHeight);
|
|
2079
|
-
|
|
2441
|
+
if (texture.flipY) {
|
|
2442
|
+
// threejs-parity ST-02 (L4): flip the source at draw time so the CPU
|
|
2443
|
+
// readback matches UNPACK_FLIP_Y / copyExternalImageToTexture flipY.
|
|
2444
|
+
context.save();
|
|
2445
|
+
context.translate(0, levelHeight);
|
|
2446
|
+
context.scale(1, -1);
|
|
2447
|
+
context.drawImage(source, 0, 0, levelWidth, levelHeight);
|
|
2448
|
+
context.restore();
|
|
2449
|
+
}
|
|
2450
|
+
else {
|
|
2451
|
+
context.drawImage(source, 0, 0, levelWidth, levelHeight);
|
|
2452
|
+
}
|
|
2080
2453
|
const pixels = context.getImageData(0, 0, levelWidth, levelHeight).data;
|
|
2081
2454
|
const sourceBytesPerRow = levelWidth * 4;
|
|
2082
2455
|
const bytesPerRow = alignTo(sourceBytesPerRow, 256);
|