@forgeax/engine-render 0.1.25 → 0.1.26

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.
Files changed (86) hide show
  1. package/README.md +18 -0
  2. package/dist/assembly/factory.d.ts +1 -1
  3. package/dist/assembly/factory.d.ts.map +1 -1
  4. package/dist/assembly/material/assembly.d.ts +4 -17
  5. package/dist/assembly/material/assembly.d.ts.map +1 -1
  6. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -1
  7. package/dist/assembly/material/surface-projection.d.ts.map +1 -1
  8. package/dist/assembly/webgpu-renderer.d.ts +0 -1
  9. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  10. package/dist/authoring.mjs +1 -1
  11. package/dist/{chunk-JSEDTZYS.mjs → chunk-GF523LHF.mjs} +6 -6
  12. package/dist/chunk-GF523LHF.mjs.map +1 -0
  13. package/dist/{chunk-TCLDX7SZ.mjs → chunk-TMIASV2N.mjs} +403 -457
  14. package/dist/chunk-TMIASV2N.mjs.map +1 -0
  15. package/dist/{chunk-N3RT2EUR.mjs → chunk-YUQXSEHG.mjs} +87 -53
  16. package/dist/chunk-YUQXSEHG.mjs.map +1 -0
  17. package/dist/construct-renderer.mjs +153 -13
  18. package/dist/construct-renderer.mjs.map +1 -1
  19. package/dist/extract/contracts.d.ts +6 -6
  20. package/dist/extract/contracts.d.ts.map +1 -1
  21. package/dist/index.mjs +1 -1
  22. package/dist/internal.mjs +1 -1
  23. package/dist/pipeline-spec-types.d.ts +2 -0
  24. package/dist/pipeline-spec-types.d.ts.map +1 -1
  25. package/dist/pipeline-spec.d.ts.map +1 -1
  26. package/dist/record/frame.d.ts.map +1 -1
  27. package/dist/record/main-pass-geometry.d.ts +2 -2
  28. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  29. package/dist/record/main-pass-material.d.ts +20 -2
  30. package/dist/record/main-pass-material.d.ts.map +1 -1
  31. package/dist/record/main-pass-sprite-draws.d.ts +3 -3
  32. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  33. package/dist/record/main-pass.d.ts.map +1 -1
  34. package/dist/record/material-uniforms.d.ts +4 -0
  35. package/dist/record/material-uniforms.d.ts.map +1 -0
  36. package/dist/record/render-context.d.ts +2 -2
  37. package/dist/record/render-context.d.ts.map +1 -1
  38. package/dist/record/shadow-pass.d.ts +2 -3
  39. package/dist/record/shadow-pass.d.ts.map +1 -1
  40. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  41. package/dist/render-system-extract-tail.d.ts +2 -0
  42. package/dist/render-system-extract-tail.d.ts.map +1 -1
  43. package/dist/render-system-extract.d.ts +21 -10
  44. package/dist/render-system-extract.d.ts.map +1 -1
  45. package/dist/render-system.d.ts.map +1 -1
  46. package/dist/temporal/index.mjs +2 -2
  47. package/package.json +20 -20
  48. package/src/__tests__/fixtures/ai-weapon-spirit/README.md +20 -0
  49. package/src/__tests__/fixtures/ai-weapon-spirit/low-poly-toon.wgsl +178 -0
  50. package/src/__tests__/fixtures/ai-weapon-spirit/material.json +97 -0
  51. package/src/__tests__/fixtures/ai-weapon-spirit/painterly-surface.wgsl +55 -0
  52. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +3 -3
  53. package/src/__tests__/gpu-driven-view.browser.test.ts +11 -3
  54. package/src/__tests__/material-contract-inventory.unit.test.ts +43 -0
  55. package/src/__tests__/{material-static-dynamic-route.unit.test.ts → material-layer-projection.unit.test.ts} +30 -47
  56. package/src/__tests__/material-program-dispatch.unit.test.ts +48 -0
  57. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +2 -2
  58. package/src/__tests__/skinned-shadow-caster.test.ts +14 -14
  59. package/src/__tests__/surface-variant-provenance.integration.test.ts +1 -1
  60. package/src/assembly/factory.ts +0 -3
  61. package/src/assembly/material/assembly.ts +8 -40
  62. package/src/assembly/material/standard-layer-projection.ts +3 -1
  63. package/src/assembly/material/surface-projection.ts +2 -1
  64. package/src/assembly/webgpu-renderer.ts +17 -9
  65. package/src/extract/contracts.ts +6 -6
  66. package/src/materials.ts +3 -3
  67. package/src/pipeline-spec-types.ts +2 -0
  68. package/src/pipeline-spec.ts +7 -3
  69. package/src/record/frame.ts +7 -1
  70. package/src/record/main-pass-geometry.ts +340 -279
  71. package/src/record/main-pass-material.ts +87 -2
  72. package/src/record/main-pass-sprite-draws.ts +65 -168
  73. package/src/record/main-pass.ts +24 -246
  74. package/src/record/material-uniforms.ts +186 -0
  75. package/src/record/render-context.ts +4 -0
  76. package/src/record/shadow-pass.ts +86 -28
  77. package/src/recovery/render-system-candidate.ts +5 -1
  78. package/src/render-system-extract-tail.ts +51 -11
  79. package/src/render-system-extract.ts +73 -63
  80. package/src/render-system.ts +19 -0
  81. package/dist/assembly/material/pipeline-projection.d.ts +0 -30
  82. package/dist/assembly/material/pipeline-projection.d.ts.map +0 -1
  83. package/dist/chunk-JSEDTZYS.mjs.map +0 -1
  84. package/dist/chunk-N3RT2EUR.mjs.map +0 -1
  85. package/dist/chunk-TCLDX7SZ.mjs.map +0 -1
  86. package/src/assembly/material/pipeline-projection.ts +0 -63
@@ -1,9 +1,9 @@
1
1
  import { TRANSPARENT_SORT_MODE_LAYER_Z, TRANSPARENT_SORT_MODE_LAYER_Y } from './chunk-RXZ3RGOU.mjs';
2
2
  import { DEFAULT_CLUSTER_GRID, DEFAULT_STANDARD_PROFILE, createIblKernelCache, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, createStandardClusterLayout } from './chunk-E34VL5VI.mjs';
3
- import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-JSEDTZYS.mjs';
4
- import { GPU_SHADER_STAGE_COMPUTE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM, createHdrpBindGroupLayoutDescriptor, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, GPU_SHADER_STAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_VERTEX, worldEntityKey, instanceCollectionCacheKey, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE, pbrSkinMeshDynamicOffsets, buildPbrMaterialUserRegionEntries, buildBindGroupLayoutDescriptor, isStandardPbrMaterialShader, PROBE_BLEND_RECORD_BYTE_SIZE, SHADOW_CASTER_SHADER_ID, shadowCasterVariantSet, COOKIE_MATRIX_BYTES, matchPass, createPbrSkinMeshBindGroupEntries, getOpaqueResourceIdentity, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, physicalTextureFields, isCanonicalStandardPbrMaterialShader, isStandardPbrSkinMaterialShader, SKIN_MATERIAL_SHADER_ID, resolveRenderTargetMaterialSource, materialBindGroupLayoutIdentity } from './chunk-HKXTW355.mjs';
5
- import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, VertexColorVariantConflictError, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, StandardClusterTransportUnavailableError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, StandardLightBudgetExceededError, RendererOperationError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID, VideoUploadUnsupportedError } from './chunk-ZZ4YQ474.mjs';
6
- import { derive, ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, handleSlot, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates } from '@forgeax/engine-types';
3
+ import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-GF523LHF.mjs';
4
+ import { GPU_SHADER_STAGE_COMPUTE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM, createHdrpBindGroupLayoutDescriptor, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, GPU_SHADER_STAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_VERTEX, isStandardPbrMaterialShader, worldEntityKey, instanceCollectionCacheKey, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE, pbrSkinMeshDynamicOffsets, SHADOW_CASTER_SHADER_ID, buildPbrMaterialUserRegionEntries, buildBindGroupLayoutDescriptor, PROBE_BLEND_RECORD_BYTE_SIZE, shadowCasterVariantSet, COOKIE_MATRIX_BYTES, matchPass, createPbrSkinMeshBindGroupEntries, physicalTextureFields, isCanonicalStandardPbrMaterialShader, getOpaqueResourceIdentity, resolveRenderTargetMaterialSource, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, isStandardPbrSkinMaterialShader, SKIN_MATERIAL_SHADER_ID, materialBindGroupLayoutIdentity } from './chunk-HKXTW355.mjs';
5
+ import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, VertexColorVariantConflictError, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, StandardClusterTransportUnavailableError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, StandardLightBudgetExceededError, RendererOperationError, VideoUploadUnsupportedError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID } from './chunk-ZZ4YQ474.mjs';
6
+ import { derive, ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, handleSlot, resolveMaterialTextureCoordinates, KNOWN_PASS_KINDS } from '@forgeax/engine-types';
7
7
  import { ok as ok$1, err, RhiError } from '@forgeax/engine-rhi';
8
8
  import { mat3, vec3, mat4 } from '@forgeax/engine-math';
9
9
  import { deriveVertexLayoutProjection, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection } from '@forgeax/engine-geometry';
@@ -231,7 +231,7 @@ function cacheKeyOf(spec) {
231
231
  stripSegment,
232
232
  `vl:${vlDigest}`,
233
233
  renderStateHash(renderState)
234
- ].join(":") + uvSetCountSegment;
234
+ ].join(":") + uvSetCountSegment + (shader.vertexEntry === void 0 && shader.fragmentEntry === void 0 ? "" : `:entries:${JSON.stringify([shader.vertexEntry ?? null, shader.fragmentEntry ?? null])}`);
235
235
  }
236
236
  function variantSetFromVertexLayoutProjection(projection, authoredVariantSet) {
237
237
  const projected = projection.attributes.some((attribute) => attribute.key === "color");
@@ -289,14 +289,14 @@ function buildPipelineDescriptor(spec, modules) {
289
289
  const descriptor = {
290
290
  vertex: {
291
291
  module: modules.vertex,
292
- entryPoint: modules.vertexEntryPoint ?? "vs_main",
292
+ entryPoint: spec.shader.vertexEntry ?? modules.vertexEntryPoint ?? "vs_main",
293
293
  buffers: vertexBuffers
294
294
  }
295
295
  };
296
296
  if (attachments.colorFormats.length > 0) {
297
297
  descriptor.fragment = {
298
298
  module: modules.fragment,
299
- entryPoint: modules.fragmentEntryPoint ?? "fs_main",
299
+ entryPoint: spec.shader.fragmentEntry ?? modules.fragmentEntryPoint ?? "fs_main",
300
300
  targets: attachments.colorFormats.map((fmt) => {
301
301
  const target = { format: fmt };
302
302
  if (renderState?.blend !== void 0) {
@@ -4700,14 +4700,6 @@ function isEntityFullyTransparent(source) {
4700
4700
  }
4701
4701
  return true;
4702
4702
  }
4703
- function entityHasTransparentSubmesh(source) {
4704
- const mats = source.materials;
4705
- if (mats === void 0) return source.material.transparent === true;
4706
- for (let j = 0; j < mats.length; j++) {
4707
- if (mats[j]?.transparent === true) return true;
4708
- }
4709
- return false;
4710
- }
4711
4703
  function isImageError(error) {
4712
4704
  if (typeof error !== "object" || error === null) return false;
4713
4705
  const code = error.code;
@@ -5074,7 +5066,7 @@ function isMaterialBgAssemblyCacheHit(cached, material, materialBgl, materialBuf
5074
5066
  if (transmissionBackdropView != null) return false;
5075
5067
  return cached?.material === material && cached.materialResourceEpoch === materialResourceEpoch && cached.materialBgl === materialBgl && cached.materialBuffer === materialBuffer && cached.skylightResources.irradianceView === skylightResources.irradianceView && cached.skylightResources.irradianceSampler === skylightResources.irradianceSampler && cached.skylightResources.prefilterView === skylightResources.prefilterView && cached.skylightResources.prefilterSampler === skylightResources.prefilterSampler && cached.skylightResources.brdfLutView === skylightResources.brdfLutView && cached.skylightResources.brdfLutSampler === skylightResources.brdfLutSampler && cached.skylightResources.intensityBuffer === skylightResources.intensityBuffer;
5076
5068
  }
5077
- function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWorld = deps.world) {
5069
+ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWorld = deps.world, materialShaderId = submeshMaterial.materialShaderId) {
5078
5070
  const {
5079
5071
  runtime,
5080
5072
  pipelineState,
@@ -5092,9 +5084,9 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5092
5084
  } = deps;
5093
5085
  const effectiveSkylightResources = resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
5094
5086
  const smMaterialHandle = submeshMaterial.materialHandle;
5095
- const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}`;
5087
+ const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}:${materialShaderId ?? ""}`;
5096
5088
  const smHasVideoFields = (submeshMaterial.videoTextureFields?.size ?? 0) > 0;
5097
- const smShaderId = submeshMaterial.materialShaderId;
5089
+ const smShaderId = materialShaderId;
5098
5090
  const smSchema = (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0) ?? submeshMaterial.materialParamSchema;
5099
5091
  const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId, smSchema) : void 0;
5100
5092
  const smMaterialBgl = smPerShaderBgl ?? pipelineState.materialBindGroupLayout;
@@ -5326,6 +5318,52 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5326
5318
  }
5327
5319
  return smBindGroup;
5328
5320
  }
5321
+ var ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
5322
+ function prepareMaterialSkylight(c) {
5323
+ const { runtime, pipelineState, skylight, skylightCount } = c;
5324
+ const skylightFallback = pipelineState.skylightFallback;
5325
+ if (skylightFallback === null) {
5326
+ throw new RhiError({
5327
+ code: "webgpu-runtime-error",
5328
+ expected: "pipelineState.skylightFallback != null when PBR pipeline is active",
5329
+ hint: "createRenderer must allocate skylightFallback alongside the PBR pipeline (D-5 round-4)"
5330
+ });
5331
+ }
5332
+ let activeViews;
5333
+ runtime.device.queue.writeBuffer(skylightFallback.intensityBuffer, 0, ZERO_SKYLIGHT_PAYLOAD);
5334
+ if (skylight !== void 0 && skylightCount >= 1) {
5335
+ const [cr, cg, cb] = skylight.color;
5336
+ const [qx, qy, qz, qw] = skylight.rotation;
5337
+ const uniformPayload = new Float32Array([skylight.intensity, cr, cg, cb, qx, qy, qz, qw]);
5338
+ runtime.device.queue.writeBuffer(skylightFallback.intensityBuffer, 0, uniformPayload);
5339
+ const cache3 = getOrCreateIblCache(runtime.deviceScope);
5340
+ if (cache3.irradianceView !== void 0 && cache3.prefilterView !== void 0 && cache3.brdfLutView !== void 0) {
5341
+ activeViews = {
5342
+ irr: cache3.irradianceView,
5343
+ pref: cache3.prefilterView,
5344
+ brdf: cache3.brdfLutView
5345
+ };
5346
+ }
5347
+ }
5348
+ const skylightResources = activeViews !== void 0 ? {
5349
+ irradianceView: activeViews.irr,
5350
+ irradianceSampler: skylightFallback.sampler,
5351
+ prefilterView: activeViews.pref,
5352
+ prefilterSampler: skylightFallback.sampler,
5353
+ brdfLutView: activeViews.brdf,
5354
+ brdfLutSampler: skylightFallback.sampler,
5355
+ intensityBuffer: skylightFallback.intensityBuffer
5356
+ } : {
5357
+ irradianceView: skylightFallback.irradianceView,
5358
+ irradianceSampler: skylightFallback.sampler,
5359
+ prefilterView: skylightFallback.prefilterView,
5360
+ prefilterSampler: skylightFallback.sampler,
5361
+ brdfLutView: skylightFallback.brdfLutView,
5362
+ brdfLutSampler: skylightFallback.sampler,
5363
+ intensityBuffer: skylightFallback.intensityBuffer
5364
+ };
5365
+ return { skylightResources, activeViews };
5366
+ }
5329
5367
 
5330
5368
  // src/record/main-pass-geometry.ts
5331
5369
  function materialDiagnosticsEnabled2() {
@@ -5453,7 +5491,7 @@ function recordPointsLinesDraw(pass, plan) {
5453
5491
  }
5454
5492
  pass.draw(plan.vertexCount, 1, 0, 0);
5455
5493
  }
5456
- function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sampleCount, meshGroup2, meshBindGroup, resolveMaterialBindGroup, passKind = "forward") {
5494
+ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sampleCount, meshGroup2, meshBindGroup, resolveMaterialBindGroup, passKind = "forward", selectedDispatch) {
5457
5495
  const {
5458
5496
  runtime,
5459
5497
  pipelineState,
@@ -5467,6 +5505,17 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5467
5505
  viewBindGroup,
5468
5506
  viewBindGroupDynamicOffset = 0
5469
5507
  } = c;
5508
+ const materialPasses = /* @__PURE__ */ new Map();
5509
+ for (const dispatch of selectedDispatch ?? []) {
5510
+ let byMaterial = materialPasses.get(dispatch.renderableIndex);
5511
+ if (byMaterial === void 0) {
5512
+ byMaterial = /* @__PURE__ */ new Map();
5513
+ materialPasses.set(dispatch.renderableIndex, byMaterial);
5514
+ }
5515
+ const passes = byMaterial.get(dispatch.materialHandle);
5516
+ if (passes === void 0) byMaterial.set(dispatch.materialHandle, [dispatch]);
5517
+ else passes.push(dispatch);
5518
+ }
5470
5519
  const diagnosticsEnabled = materialDiagnosticsEnabled2();
5471
5520
  const colorFormatOverride = c.transparentColorFormat;
5472
5521
  const clusteredLighting = c.standardLighting?.kind === "clustered";
@@ -5495,19 +5544,19 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5495
5544
  const recordOcclusionCandidate = (draw) => {
5496
5545
  draw();
5497
5546
  };
5498
- const resolveMaterialPipeline = (materialShaderId, renderState, topology, indexFormat, variantSet, colorFormat, layoutProjection) => {
5547
+ const resolveMaterialPipeline = (materialShaderId, renderState, topology, indexFormat, variantSet, colorFormat, layoutProjection, vertexEntry, fragmentEntry) => {
5499
5548
  const reflectionFallbackFormats = materialShaderId === "forgeax::default-standard-pbr" && c.reflectionFallbackColorFormat !== void 0 ? [c.reflectionFallbackColorFormat] : void 0;
5500
5549
  const renderStateKey = renderStateHash(renderState);
5501
5550
  const shaderLookupCache = materialPipelineLookupCache.get(materialShaderId);
5502
5551
  const cachedLookups = shaderLookupCache?.get(renderStateKey);
5503
5552
  if (cachedLookups !== void 0) {
5504
5553
  for (const cached of cachedLookups) {
5505
- if (cached.isHdrTarget === isHdrTarget && cached.renderStateKey === renderStateKey && cached.topology === topology && cached.indexFormat === indexFormat && cached.variantSet === variantSet && cached.passKind === passKind && cached.layoutProjection.digest === layoutProjection.digest && cached.sampleCount === sampleCount && cached.colorFormatOverride === colorFormat && cached.additionalColorFormats?.join(",") === reflectionFallbackFormats?.join(",")) {
5554
+ if (cached.isHdrTarget === isHdrTarget && cached.renderStateKey === renderStateKey && cached.topology === topology && cached.indexFormat === indexFormat && cached.variantSet === variantSet && cached.vertexEntry === vertexEntry && cached.fragmentEntry === fragmentEntry && cached.passKind === passKind && cached.layoutProjection.digest === layoutProjection.digest && cached.sampleCount === sampleCount && cached.colorFormatOverride === colorFormat && cached.additionalColorFormats?.join(",") === reflectionFallbackFormats?.join(",")) {
5506
5555
  return cached.handle;
5507
5556
  }
5508
5557
  }
5509
5558
  }
5510
- const resolvedVariantSetResult = variantSet === void 0 && (materialShaderId === "forgeax::sprite" || materialShaderId === "forgeax::sprite-lit") ? { ok: true, value: void 0 } : variantSetFromVertexLayoutProjection(layoutProjection, variantSet);
5559
+ const resolvedVariantSetResult = runtime.getMaterialArtifact?.(materialShaderId) !== void 0 ? { ok: true, value: void 0 } : variantSet === void 0 && (materialShaderId === "forgeax::sprite" || materialShaderId === "forgeax::sprite-lit") ? { ok: true, value: void 0 } : variantSetFromVertexLayoutProjection(layoutProjection, variantSet);
5511
5560
  if (!resolvedVariantSetResult.ok) {
5512
5561
  runtime.errorRegistry.fire(resolvedVariantSetResult.error);
5513
5562
  return null;
@@ -5531,7 +5580,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5531
5580
  layoutProjection,
5532
5581
  void 0,
5533
5582
  void 0,
5534
- reflectionFallbackFormats
5583
+ reflectionFallbackFormats,
5584
+ vertexEntry,
5585
+ fragmentEntry
5535
5586
  ) ?? null;
5536
5587
  const handle = profilePhase === void 0 ? resolvePipeline() : profileGeometrySegment(c, passKind, "pipeline-selection", resolvePipeline);
5537
5588
  if (handle !== null) {
@@ -5542,6 +5593,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5542
5593
  topology,
5543
5594
  indexFormat,
5544
5595
  variantSet,
5596
+ vertexEntry,
5597
+ fragmentEntry,
5545
5598
  passKind,
5546
5599
  layoutProjection,
5547
5600
  sampleCount,
@@ -5577,7 +5630,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5577
5630
  continue;
5578
5631
  }
5579
5632
  if (matchedMaterials !== null && !matchedMaterials.has(entry.renderableIndex)) continue;
5580
- if (splitLdrSprite && isEntityFullyTransparent(entry.source)) continue;
5633
+ if (selectedDispatch === void 0 && splitLdrSprite && isEntityFullyTransparent(entry.source))
5634
+ continue;
5581
5635
  const stencilReference = entry.stencilReference ?? 0;
5582
5636
  if (stencilReference !== lastStencilReference) {
5583
5637
  pass.setStencilReference(stencilReference);
@@ -5793,182 +5847,180 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5793
5847
  const materialHandle = submeshMaterial.materialHandle ?? 0;
5794
5848
  if (materialHandles === void 0 || !materialHandles.has(materialHandle)) continue;
5795
5849
  }
5796
- if (splitLdrSprite && submeshMaterial.transparent === true) {
5797
- continue;
5798
- }
5799
- const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
5800
- const processValue = globalThis.process;
5801
- if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (submeshMaterial.materialShaderId === "forgeax::default-standard-pbr" || submeshMaterial.materialShaderId === "forgeax::pbr-skin")) {
5802
- console.error(
5803
- `[render-material] draw receipt: ${JSON.stringify({
5804
- entityIndex: entry.renderableIndex,
5805
- materialSlot,
5806
- materialHandle: submeshMaterial.materialHandle,
5807
- clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
5808
- shader: submeshMaterial.materialShaderId
5809
- })}`
5810
- );
5811
- }
5812
- perSubmeshBg = profilePhase === void 0 ? resolveMaterialBindGroup(
5813
- materialSlot,
5814
- submeshMaterial,
5815
- entry.source.entityKey,
5816
- entry.world ?? c.world
5817
- ) : profileGeometrySegment(
5818
- c,
5819
- passKind,
5820
- "material-bind-groups",
5821
- () => resolveMaterialBindGroup(
5850
+ const draws = selectedDispatch === void 0 ? [void 0] : materialPasses.get(entry.renderableIndex)?.get(submeshMaterial.materialHandle ?? 0) ?? [];
5851
+ for (const selectedPass of draws) {
5852
+ const selectedShaderId = selectedPass === void 0 ? submeshMaterial.materialShaderId : selectedPass.materialShaderId;
5853
+ const selectedRenderState = selectedPass === void 0 ? submeshMaterial.renderState : selectedPass.renderState;
5854
+ const selectedStencil = (selectedPass === void 0 ? entry.stencilReference : selectedPass.stencilReference) ?? 0;
5855
+ if (selectedStencil !== lastStencilReference) {
5856
+ pass.setStencilReference(selectedStencil);
5857
+ lastStencilReference = selectedStencil;
5858
+ }
5859
+ if (splitLdrSprite && (selectedPass === void 0 ? submeshMaterial.transparent === true : selectedRenderState?.blend !== void 0)) {
5860
+ continue;
5861
+ }
5862
+ const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
5863
+ const processValue = globalThis.process;
5864
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (submeshMaterial.materialShaderId === "forgeax::default-standard-pbr" || submeshMaterial.materialShaderId === "forgeax::pbr-skin")) {
5865
+ console.error(
5866
+ `[render-material] draw receipt: ${JSON.stringify({
5867
+ entityIndex: entry.renderableIndex,
5868
+ materialSlot,
5869
+ materialHandle: submeshMaterial.materialHandle,
5870
+ clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
5871
+ shader: submeshMaterial.materialShaderId
5872
+ })}`
5873
+ );
5874
+ }
5875
+ perSubmeshBg = profilePhase === void 0 ? resolveMaterialBindGroup(
5822
5876
  materialSlot,
5823
5877
  submeshMaterial,
5824
5878
  entry.source.entityKey,
5825
- entry.world ?? c.world
5826
- )
5827
- );
5828
- materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
5829
- pass.setBindGroup(1, perSubmeshBg, materialGroup1DynamicOffsets, 0, 1);
5830
- const smTopology = sm.topology;
5831
- const smMaterialShaderId = entry.source.skin !== void 0 ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId) ? submeshMaterial.materialShaderId : SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5832
- const probeBlendAvailable = probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
5833
- const isSpriteShader = smMaterialShaderId === "forgeax::sprite" || smMaterialShaderId === "forgeax::sprite-lit";
5834
- const basePipelineRenderState = isSpriteShader ? {
5835
- ...submeshMaterial.renderState,
5836
- depthWriteEnabled: false,
5837
- depthCompare: "less-equal",
5838
- cullMode: "none",
5839
- blend: submeshMaterial.renderState?.blend ?? SPRITE_PREMULTIPLIED_ALPHA_BLEND
5840
- } : geometryRenderStateForTopology(smTopology, submeshMaterial.renderState);
5841
- const pipelineRenderState = geometryRenderStateForPass(basePipelineRenderState, passKind);
5842
- let smPipelineHandle;
5843
- let materialGroup2Contract;
5844
- let materialPipelineEntry = null;
5845
- if (smMaterialShaderId === void 0 || smMaterialShaderId === "forgeax::default-unlit") {
5846
- const unlitShaderId = smMaterialShaderId ?? "forgeax::default-unlit";
5847
- const unlitProjectionVariantResult = variantSetFromVertexLayoutProjection(
5848
- entry.mesh.layoutProjection,
5849
- void 0
5879
+ entry.world ?? c.world,
5880
+ selectedShaderId
5881
+ ) : profileGeometrySegment(
5882
+ c,
5883
+ passKind,
5884
+ "material-bind-groups",
5885
+ () => resolveMaterialBindGroup(
5886
+ materialSlot,
5887
+ submeshMaterial,
5888
+ entry.source.entityKey,
5889
+ entry.world ?? c.world,
5890
+ selectedShaderId
5891
+ )
5850
5892
  );
5851
- if (!unlitProjectionVariantResult.ok) {
5852
- runtime.errorRegistry.fire(unlitProjectionVariantResult.error);
5893
+ materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
5894
+ pass.setBindGroup(1, perSubmeshBg, materialGroup1DynamicOffsets, 0, 1);
5895
+ const smTopology = sm.topology;
5896
+ const smMaterialShaderId = selectedPass !== void 0 ? selectedShaderId : entry.source.skin !== void 0 ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId) ? submeshMaterial.materialShaderId : SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5897
+ const probeBlendAvailable = probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
5898
+ const isSpriteShader = smMaterialShaderId === "forgeax::sprite" || smMaterialShaderId === "forgeax::sprite-lit";
5899
+ const basePipelineRenderState = isSpriteShader ? {
5900
+ ...selectedRenderState,
5901
+ depthWriteEnabled: false,
5902
+ depthCompare: "less-equal",
5903
+ cullMode: "none",
5904
+ blend: selectedRenderState?.blend ?? SPRITE_PREMULTIPLIED_ALPHA_BLEND
5905
+ } : geometryRenderStateForTopology(smTopology, selectedRenderState);
5906
+ const pipelineRenderState = geometryRenderStateForPass(basePipelineRenderState, passKind);
5907
+ let smPipelineHandle;
5908
+ let materialGroup2Contract;
5909
+ let materialPipelineEntry = null;
5910
+ if (smMaterialShaderId === void 0 || smMaterialShaderId === "forgeax::default-unlit") {
5911
+ const unlitShaderId = smMaterialShaderId ?? "forgeax::default-unlit";
5912
+ const unlitProjectionVariantResult = variantSetFromVertexLayoutProjection(
5913
+ entry.mesh.layoutProjection,
5914
+ void 0
5915
+ );
5916
+ if (!unlitProjectionVariantResult.ok) {
5917
+ runtime.errorRegistry.fire(unlitProjectionVariantResult.error);
5918
+ continue;
5919
+ }
5920
+ const unlitHasColor = unlitProjectionVariantResult.value === "VERTEX_COLOR_AVAILABLE=true";
5921
+ const unlitVariantSet = standardStorageVariantSet(
5922
+ runtime.device.caps.storageBuffer,
5923
+ unlitHasColor
5924
+ );
5925
+ const unlitRsp = resolveMaterialPipeline(
5926
+ unlitShaderId,
5927
+ pipelineRenderState,
5928
+ smTopology,
5929
+ entry.mesh.indexFormat,
5930
+ unlitVariantSet,
5931
+ colorFormatOverride,
5932
+ entry.mesh.layoutProjection,
5933
+ selectedPass?.vertexEntry,
5934
+ selectedPass?.fragmentEntry
5935
+ );
5936
+ materialPipelineEntry = unlitRsp;
5937
+ smPipelineHandle = unlitRsp?.pipeline ?? (colorFormatOverride === void 0 ? selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive) : null);
5938
+ } else if (smMaterialShaderId !== void 0) {
5939
+ const hasVertexColor = entry.mesh.layoutProjection.attributes.some(
5940
+ (attribute) => attribute.key === "color"
5941
+ );
5942
+ const capabilityVariantSet = standardTopologyVariantSet(
5943
+ c.standardLighting,
5944
+ runtime.device.caps.storageBuffer,
5945
+ hasVertexColor,
5946
+ probeBlendAvailable
5947
+ );
5948
+ const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
5949
+ const materialCapabilityVariantSet = transmissionAvailable && !capabilityVariantSet.includes("TRANSMISSION_AVAILABLE=") ? `${capabilityVariantSet === "" ? "" : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true` : capabilityVariantSet;
5950
+ const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? materialCapabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5951
+ const cachedPipeline = resolveMaterialPipeline(
5952
+ smMaterialShaderId,
5953
+ pipelineRenderState,
5954
+ smTopology,
5955
+ entry.mesh.indexFormat,
5956
+ variantSet,
5957
+ colorFormatOverride,
5958
+ entry.mesh.layoutProjection,
5959
+ selectedPass?.vertexEntry,
5960
+ selectedPass?.fragmentEntry
5961
+ );
5962
+ materialPipelineEntry = cachedPipeline;
5963
+ smPipelineHandle = cachedPipeline?.pipeline ?? null;
5964
+ } else {
5965
+ smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
5966
+ }
5967
+ if (smPipelineHandle === null) {
5968
+ if (diagnosticsEnabled) {
5969
+ console.error(
5970
+ `[render-material] draw skipped: ${JSON.stringify({
5971
+ entityKey: entry.source.entityKey,
5972
+ materialHandle: submeshMaterial.materialHandle,
5973
+ shader: smMaterialShaderId,
5974
+ reason: "pipeline-unavailable"
5975
+ })}`
5976
+ );
5977
+ }
5853
5978
  continue;
5854
5979
  }
5855
- const unlitHasColor = unlitProjectionVariantResult.value === "VERTEX_COLOR_AVAILABLE=true";
5856
- const unlitVariantSet = standardStorageVariantSet(
5857
- runtime.device.caps.storageBuffer,
5858
- unlitHasColor
5859
- );
5860
- const unlitRsp = resolveMaterialPipeline(
5861
- unlitShaderId,
5862
- pipelineRenderState,
5863
- smTopology,
5864
- entry.mesh.indexFormat,
5865
- unlitVariantSet,
5866
- colorFormatOverride,
5867
- entry.mesh.layoutProjection
5868
- );
5869
- materialPipelineEntry = unlitRsp;
5870
- smPipelineHandle = unlitRsp?.pipeline ?? (colorFormatOverride === void 0 ? selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive) : null);
5871
- } else if (smMaterialShaderId !== void 0) {
5872
- const hasVertexColor = entry.mesh.layoutProjection.attributes.some(
5873
- (attribute) => attribute.key === "color"
5874
- );
5875
- const capabilityVariantSet = standardTopologyVariantSet(
5876
- c.standardLighting,
5877
- runtime.device.caps.storageBuffer,
5878
- hasVertexColor,
5980
+ const instanceDraws2 = resolveGeometryInstanceBuffer(
5981
+ c,
5982
+ entry,
5983
+ identityInstanceDraws,
5879
5984
  probeBlendAvailable
5880
5985
  );
5881
- const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
5882
- const materialCapabilityVariantSet = transmissionAvailable && !capabilityVariantSet.includes("TRANSMISSION_AVAILABLE=") ? `${capabilityVariantSet === "" ? "" : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true` : capabilityVariantSet;
5883
- const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? materialCapabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5884
- const cachedPipeline = resolveMaterialPipeline(
5885
- smMaterialShaderId,
5886
- pipelineRenderState,
5887
- smTopology,
5888
- entry.mesh.indexFormat,
5889
- variantSet,
5890
- colorFormatOverride,
5891
- entry.mesh.layoutProjection
5892
- );
5893
- materialPipelineEntry = cachedPipeline;
5894
- smPipelineHandle = cachedPipeline?.pipeline ?? null;
5895
- } else {
5896
- smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
5897
- }
5898
- if (smPipelineHandle === null) {
5899
- if (diagnosticsEnabled) {
5986
+ if (instanceDraws2 === null) continue;
5987
+ if (!isSkinEntry) {
5988
+ materialGroup2Contract = materialPipelineEntry?.group2Contract;
5989
+ if (materialGroup2Contract === void 0) continue;
5990
+ const selectedGroup = selectMaterialGroup2(
5991
+ meshGroup2,
5992
+ meshBindGroup,
5993
+ materialGroup2Contract
5994
+ );
5995
+ if (selectedGroup === null) continue;
5996
+ group2BindGroup = selectedGroup;
5997
+ }
5998
+ if (diagnosticsEnabled && submeshMaterial.textureHandles !== void 0) {
5900
5999
  console.error(
5901
- `[render-material] draw skipped: ${JSON.stringify({
6000
+ `[render-material] draw submitted: ${JSON.stringify({
5902
6001
  entityKey: entry.source.entityKey,
5903
6002
  materialHandle: submeshMaterial.materialHandle,
5904
6003
  shader: smMaterialShaderId,
5905
- reason: "pipeline-unavailable"
6004
+ textureHandles: [...submeshMaterial.textureHandles.entries()].map(
6005
+ ([field, handle]) => ({
6006
+ field,
6007
+ handle
6008
+ })
6009
+ ),
6010
+ skin: isSkinEntry ? {
6011
+ resourcesReady: skinResources !== null,
6012
+ psoReady: skinPsoProbe !== null,
6013
+ paletteBufferReady: skinSlice?.buffer !== void 0,
6014
+ paletteByteOffset: skinSlice?.byteOffset,
6015
+ dynamicOffsets: [...group2DynamicOffsets]
6016
+ } : void 0,
6017
+ pipelineReady: true
5906
6018
  })}`
5907
6019
  );
5908
6020
  }
5909
- continue;
5910
- }
5911
- const instanceDraws2 = resolveGeometryInstanceBuffer(
5912
- c,
5913
- entry,
5914
- identityInstanceDraws,
5915
- probeBlendAvailable
5916
- );
5917
- if (instanceDraws2 === null) continue;
5918
- if (!isSkinEntry) {
5919
- materialGroup2Contract = materialPipelineEntry?.group2Contract;
5920
- if (materialGroup2Contract === void 0) continue;
5921
- const selectedGroup = selectMaterialGroup2(
5922
- meshGroup2,
5923
- meshBindGroup,
5924
- materialGroup2Contract
5925
- );
5926
- if (selectedGroup === null) continue;
5927
- group2BindGroup = selectedGroup;
5928
- }
5929
- if (diagnosticsEnabled && submeshMaterial.textureHandles !== void 0) {
5930
- console.error(
5931
- `[render-material] draw submitted: ${JSON.stringify({
5932
- entityKey: entry.source.entityKey,
5933
- materialHandle: submeshMaterial.materialHandle,
5934
- shader: smMaterialShaderId,
5935
- textureHandles: [...submeshMaterial.textureHandles.entries()].map(
5936
- ([field, handle]) => ({
5937
- field,
5938
- handle
5939
- })
5940
- ),
5941
- skin: isSkinEntry ? {
5942
- resourcesReady: skinResources !== null,
5943
- psoReady: skinPsoProbe !== null,
5944
- paletteBufferReady: skinSlice?.buffer !== void 0,
5945
- paletteByteOffset: skinSlice?.byteOffset,
5946
- dynamicOffsets: [...group2DynamicOffsets]
5947
- } : void 0,
5948
- pipelineReady: true
5949
- })}`
5950
- );
5951
- }
5952
- pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
5953
- if (profilePhase === void 0) {
5954
- recordOcclusionCandidate(
5955
- () => submitSubmeshDraws(
5956
- pass,
5957
- bindingState,
5958
- smPipelineHandle,
5959
- instanceDraws2,
5960
- entry.mesh.indexed,
5961
- sm.indexCount,
5962
- sm.vertexCount,
5963
- sm.indexOffset
5964
- )
5965
- );
5966
- } else {
5967
- profileGeometrySegment(
5968
- c,
5969
- passKind,
5970
- "draw-submit",
5971
- () => recordOcclusionCandidate(
6021
+ pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
6022
+ if (profilePhase === void 0) {
6023
+ recordOcclusionCandidate(
5972
6024
  () => submitSubmeshDraws(
5973
6025
  pass,
5974
6026
  bindingState,
@@ -5979,11 +6031,29 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5979
6031
  sm.vertexCount,
5980
6032
  sm.indexOffset
5981
6033
  )
5982
- )
5983
- );
5984
- }
5985
- if (diagnosticsEnabled && perSubmeshBg !== null) {
5986
- recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
6034
+ );
6035
+ } else {
6036
+ profileGeometrySegment(
6037
+ c,
6038
+ passKind,
6039
+ "draw-submit",
6040
+ () => recordOcclusionCandidate(
6041
+ () => submitSubmeshDraws(
6042
+ pass,
6043
+ bindingState,
6044
+ smPipelineHandle,
6045
+ instanceDraws2,
6046
+ entry.mesh.indexed,
6047
+ sm.indexCount,
6048
+ sm.vertexCount,
6049
+ sm.indexOffset
6050
+ )
6051
+ )
6052
+ );
6053
+ }
6054
+ if (diagnosticsEnabled && perSubmeshBg !== null) {
6055
+ recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
6056
+ }
5987
6057
  }
5988
6058
  }
5989
6059
  }
@@ -6578,8 +6648,13 @@ function interleaveSpriteInstanceBuffer(transforms, regions, includePrevious = f
6578
6648
  function spriteInstancesCacheHit(entry, snapshot, requestedBytes) {
6579
6649
  return entry !== void 0 && entry.uploadedArchVersion === snapshot.archVersion && entry.uploadedByteLength === requestedBytes;
6580
6650
  }
6581
- function computeSplitLdrSprite(validatedOrdered, tonemapActive) {
6651
+ function computeSplitLdrSprite(validatedOrdered, tonemapActive, dispatch) {
6582
6652
  if (tonemapActive) return false;
6653
+ if (dispatch !== void 0 && dispatch.length > 0) {
6654
+ return dispatch.some(
6655
+ (pass) => pass.tags.LightMode === "Forward" && pass.renderState?.blend !== void 0
6656
+ );
6657
+ }
6583
6658
  for (const entry of validatedOrdered) {
6584
6659
  if (entry === void 0) continue;
6585
6660
  const materials = entry.source.materials;
@@ -6591,7 +6666,7 @@ function computeSplitLdrSprite(validatedOrdered, tonemapActive) {
6591
6666
  }
6592
6667
  return false;
6593
6668
  }
6594
- function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, skylightResources, graphPass) {
6669
+ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, skylightResources, graphPass, selectedDispatch) {
6595
6670
  const {
6596
6671
  runtime,
6597
6672
  pipelineState,
@@ -6719,114 +6794,54 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
6719
6794
  materialSlotIndices,
6720
6795
  sampleCount,
6721
6796
  resolveMaterialBindGroup,
6722
- meshGroup2
6797
+ meshGroup2,
6798
+ selectedDispatch
6723
6799
  );
6724
6800
  if (graphPass === void 0) spritePass.end();
6725
6801
  }
6726
6802
  return geometryPassEnded;
6727
6803
  }
6728
- function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, meshGroup2) {
6729
- const { runtime, pipelineState, validatedOrdered } = c;
6730
- const isHdrTarget = c.tonemapActive || transparentPassColorFormat(c, pipelineState) === "rgba16float";
6731
- let lastPbrSubPipelineHandle = null;
6732
- let lastPbrSubVertexBuffer = null;
6733
- let lastPbrSubIndexBuffer = null;
6734
- for (let i = 0; i < validatedOrdered.length; i++) {
6735
- const entry = validatedOrdered[i];
6736
- if (entry === void 0) continue;
6737
- if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
6738
- const entShaderId = entry.source.material.materialShaderId;
6739
- const entIsSprite = entShaderId === "forgeax::sprite" || entShaderId === "forgeax::sprite-lit";
6740
- if (entIsSprite) continue;
6741
- if (!entityHasTransparentSubmesh(entry.source)) continue;
6742
- if (entry.source.skin !== void 0 || entry.source.instances !== void 0) continue;
6743
- const matsForRebind = entry.source.materials;
6744
- if (entry.mesh.vertexBuffer !== lastPbrSubVertexBuffer) {
6745
- spritePass.setVertexBuffer(0, entry.mesh.vertexBuffer.handle);
6746
- lastPbrSubVertexBuffer = entry.mesh.vertexBuffer;
6747
- }
6748
- if (entry.mesh.indexed && entry.mesh.indexBuffer !== null && entry.mesh.indexBuffer !== lastPbrSubIndexBuffer) {
6749
- spritePass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
6750
- lastPbrSubIndexBuffer = entry.mesh.indexBuffer;
6804
+ function recordSpriteTransparentPbrDraws(c, pass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, meshGroup2, selectedDispatch) {
6805
+ const isSprite = (shader) => shader === "forgeax::sprite" || shader === "forgeax::sprite-lit";
6806
+ const passes = selectedDispatch?.filter(
6807
+ (entry) => entry.renderState?.blend !== void 0 && !isSprite(entry.materialShaderId) && (matchedIndices === null || matchedIndices.has(entry.renderableIndex))
6808
+ );
6809
+ const matched = /* @__PURE__ */ new Map();
6810
+ const add = (index, handle) => {
6811
+ let handles = matched.get(index);
6812
+ if (handles === void 0) {
6813
+ handles = /* @__PURE__ */ new Set();
6814
+ matched.set(index, handles);
6751
6815
  }
6752
- for (let smIdx = 0; smIdx < entry.mesh.submeshes.length; smIdx++) {
6753
- const sm = entry.mesh.submeshes[smIdx];
6754
- if (sm === void 0) continue;
6755
- const matSlotIdx = sm.materialSlot;
6756
- const submeshMaterial = matsForRebind[matSlotIdx] ?? entry.source.material;
6757
- if (submeshMaterial.transparent !== true) continue;
6758
- const smShaderId = submeshMaterial.materialShaderId;
6759
- if (smShaderId === void 0) continue;
6760
- const probeBlendAvailable = runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== void 0 && isStandardPbrMaterialShader(smShaderId);
6761
- const probeBinding = probeBlendAvailable ? resolveProbeBlendBuffer(
6762
- c,
6763
- entry.source.probeBlendRecord,
6764
- worldEntityKey(entry.source.worldId, entry.source.entityKey)
6765
- ) : void 0;
6766
- const identityInstBg = resolveGeometryInstancesBindGroup(
6767
- c,
6768
- pipelineState.identityInstanceBuffer,
6769
- probeBinding?.buffer
6770
- );
6771
- if (probeBinding === void 0) spritePass.setBindGroup(3, identityInstBg);
6772
- else spritePass.setBindGroup(3, identityInstBg, [probeBinding.offset]);
6773
- const subVariantSet = standardTopologyVariantSet(
6774
- c.standardLighting,
6775
- runtime.device.caps.storageBuffer,
6776
- entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color"),
6777
- probeBlendAvailable
6778
- );
6779
- const projectionVariantSetResult = variantSetFromVertexLayoutProjection(
6780
- entry.mesh.layoutProjection,
6781
- subVariantSet
6782
- );
6783
- if (!projectionVariantSetResult.ok) {
6784
- runtime.errorRegistry.fire(projectionVariantSetResult.error);
6785
- continue;
6786
- }
6787
- const projectionVariantSet = projectionVariantSetResult.value;
6788
- const subPipelineEntry = c.runtime.getMaterialShaderPipelineEntry?.(
6789
- smShaderId,
6790
- isHdrTarget,
6791
- submeshMaterial.renderState,
6792
- sm.topology,
6793
- entry.mesh.indexFormat,
6794
- projectionVariantSet,
6795
- "forward",
6796
- void 0,
6797
- sampleCount,
6798
- // Non-sRGB blend view (same override the sprite path uses).
6799
- transparentPassColorFormat(c),
6800
- void 0,
6801
- void 0,
6802
- void 0,
6803
- entry.mesh.layoutProjection
6804
- ) ?? null;
6805
- if (subPipelineEntry === null) continue;
6806
- const submeshContract = subPipelineEntry.group2Contract;
6807
- const submeshGroup2 = selectMaterialGroup2(meshGroup2, c.meshBindGroup, submeshContract);
6808
- if (submeshGroup2 === null) continue;
6809
- spritePass.setBindGroup(2, submeshGroup2, [i * MESH_PER_ENTITY_STRIDE]);
6810
- const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
6811
- const subBg = resolveMaterialBindGroup(
6812
- materialSlot,
6813
- submeshMaterial,
6814
- entry.source.entityKey,
6815
- entry.world ?? c.world
6816
- );
6817
- spritePass.setBindGroup(1, subBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
6818
- const subPipeline = subPipelineEntry.pipeline;
6819
- if (lastPbrSubPipelineHandle !== subPipeline) {
6820
- spritePass.setPipeline(subPipeline);
6821
- lastPbrSubPipelineHandle = subPipeline;
6822
- }
6823
- if (entry.mesh.indexed) {
6824
- spritePass.drawIndexed(sm.indexCount, 1, sm.indexOffset, 0, 0);
6825
- } else {
6826
- spritePass.draw(sm.vertexCount, 1, 0, 0);
6816
+ handles.add(handle);
6817
+ };
6818
+ if (passes !== void 0) {
6819
+ for (const entry of passes) add(entry.renderableIndex, entry.materialHandle);
6820
+ } else {
6821
+ for (const entry of c.validatedOrdered) {
6822
+ if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
6823
+ if (isSprite(entry.source.material.materialShaderId)) continue;
6824
+ for (const material of entry.source.materials) {
6825
+ if (material.transparent === true) add(entry.renderableIndex, material.materialHandle ?? 0);
6827
6826
  }
6828
6827
  }
6829
6828
  }
6829
+ recordGeometryDraws(
6830
+ {
6831
+ ...c,
6832
+ splitLdrSprite: false,
6833
+ transparentColorFormat: transparentPassColorFormat(c)
6834
+ },
6835
+ pass,
6836
+ matched,
6837
+ materialSlotIndices,
6838
+ sampleCount,
6839
+ meshGroup2,
6840
+ c.meshBindGroup,
6841
+ resolveMaterialBindGroup,
6842
+ "forward",
6843
+ passes
6844
+ );
6830
6845
  }
6831
6846
  function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResources, spritePH, spritePH_withRegion, spriteLitPH, reportSpriteUnavailable) {
6832
6847
  const {
@@ -7353,16 +7368,11 @@ function shadowShaderMap(c) {
7353
7368
  byMaterial = /* @__PURE__ */ new Map();
7354
7369
  shaders.set(entry.renderableIndex, byMaterial);
7355
7370
  }
7356
- const candidateSlots = c.materialSlotIndices[entry.renderableIndex] ?? c.materialSlotIndices.flat();
7357
- const materialSlot = candidateSlots.find(
7358
- (slot) => c.materialSlots[slot]?.materialHandle === entry.materialHandle
7359
- );
7360
7371
  byMaterial.set(entry.materialHandle, {
7361
- materialHandle: entry.materialHandle,
7372
+ vertexEntry: entry.vertexEntry,
7373
+ fragmentEntry: entry.fragmentEntry,
7362
7374
  materialShaderId: entry.materialShaderId === "forgeax::default-standard-pbr" ? SHADOW_CASTER_SHADER_ID : entry.materialShaderId,
7363
- renderState: entry.renderState,
7364
- materialSlot,
7365
- paramSnapshot: entry.paramSnapshot
7375
+ renderState: entry.renderState
7366
7376
  });
7367
7377
  }
7368
7378
  }
@@ -7444,13 +7454,15 @@ function encodeDirectionalShadowPass(c, pass, cascadeIndex, viewport) {
7444
7454
  `view-shadow-directional-${cascadeIndex}`
7445
7455
  );
7446
7456
  const materialBg = ensureSpotShadowMaterialBg(c);
7447
- if (pipeline === null || viewBg === null || materialBg === null) return;
7448
- c.frameState.directionalShadowCacheRecorded = true;
7457
+ if (pipeline === null || viewBg === null || materialBg === null) {
7458
+ c.frameState.directionalShadowCacheRecorded = false;
7459
+ return;
7460
+ }
7449
7461
  pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
7450
7462
  pass.setPipeline(pipeline);
7451
7463
  pass.setBindGroup(0, viewBg, [0, 0]);
7452
7464
  pass.setBindGroup(1, materialBg, [0]);
7453
- recordShadowCasterDraws(
7465
+ const complete = recordShadowCasterDraws(
7454
7466
  c,
7455
7467
  pass,
7456
7468
  pipeline,
@@ -7458,6 +7470,7 @@ function encodeDirectionalShadowPass(c, pass, cascadeIndex, viewport) {
7458
7470
  buildMatchedRenderableIndices(c.dispatch, { LightMode: ["ShadowCaster"] }),
7459
7471
  shadowShaderMap(c)
7460
7472
  );
7473
+ c.frameState.directionalShadowCacheRecorded = cascadeIndex === 0 ? complete : c.frameState.directionalShadowCacheRecorded && complete;
7461
7474
  }
7462
7475
  function encodePointShadowPass(c, pass, snapshotIndex, face) {
7463
7476
  const snapshot = c.frameState.pointShadowSnapshots[snapshotIndex];
@@ -7553,13 +7566,18 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
7553
7566
  let shadowLastVertexBuffer = null;
7554
7567
  let shadowLastIndexBuffer = null;
7555
7568
  let shadowLastPipeline = shadowPipeline2;
7569
+ let materialDeps;
7570
+ let complete = true;
7556
7571
  for (let i = 0; i < validatedOrdered.length; i++) {
7557
7572
  const entry = validatedOrdered[i];
7558
7573
  if (entry === void 0) continue;
7559
7574
  if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
7560
7575
  if (entry.source.skin !== void 0) {
7561
7576
  const skinPipeline = skinnedShadowPipeline(c, entry);
7562
- if (skinPipeline === null) continue;
7577
+ if (skinPipeline === null) {
7578
+ complete = false;
7579
+ continue;
7580
+ }
7563
7581
  if (skinPipeline !== shadowLastPipeline) {
7564
7582
  shadowPass.setPipeline(skinPipeline);
7565
7583
  shadowLastPipeline = skinPipeline;
@@ -7619,7 +7637,7 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
7619
7637
  const entryShadowShaderId = shadowDispatch?.materialShaderId;
7620
7638
  const entryShadowRenderState = shadowDispatch?.renderState;
7621
7639
  let entryShadowPipeline = entry.source.skin !== void 0 ? shadowLastPipeline : shadowPipeline2;
7622
- if (entry.source.skin === void 0 && entryShadowShaderId !== void 0 && (entryShadowShaderId !== "forgeax::default-shadow-caster" || entryShadowRenderState !== void 0)) {
7640
+ if (entryShadowShaderId !== void 0 && (entryShadowShaderId !== "forgeax::default-shadow-caster" || entryShadowRenderState !== void 0 && entry.source.skin === void 0)) {
7623
7641
  entryShadowPipeline = runtime.getMaterialShaderPipeline?.(
7624
7642
  entryShadowShaderId,
7625
7643
  false,
@@ -7631,14 +7649,73 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
7631
7649
  // indexFormat
7632
7650
  void 0,
7633
7651
  // variantSet — shadow caster has no variant axes
7634
- "shadow-caster"
7652
+ "shadow-caster",
7635
7653
  // passKind
7636
- ) ?? shadowPipeline2;
7654
+ void 0,
7655
+ // meshAttributes
7656
+ 1,
7657
+ void 0,
7658
+ // colorFormatOverride
7659
+ void 0,
7660
+ // shaderUvSetCount
7661
+ void 0,
7662
+ // depthFormatOverride
7663
+ void 0,
7664
+ // vertexLayout
7665
+ entry.mesh.layoutProjection,
7666
+ void 0,
7667
+ // shaderModuleMode
7668
+ entry.source.skin === void 0 ? "pbr" : "pbr-skin",
7669
+ void 0,
7670
+ // additionalColorFormats
7671
+ shadowDispatch?.vertexEntry,
7672
+ shadowDispatch?.fragmentEntry
7673
+ ) ?? null;
7674
+ }
7675
+ if (entryShadowPipeline === null) {
7676
+ complete = false;
7677
+ continue;
7637
7678
  }
7638
7679
  if (entryShadowPipeline !== shadowLastPipeline && entryShadowPipeline !== null) {
7639
7680
  shadowPass.setPipeline(entryShadowPipeline);
7640
7681
  shadowLastPipeline = entryShadowPipeline;
7641
7682
  }
7683
+ if (entryShadowShaderId !== void 0 && entryShadowShaderId !== SHADOW_CASTER_SHADER_ID) {
7684
+ materialDeps ??= {
7685
+ runtime,
7686
+ pipelineState,
7687
+ world: c.world,
7688
+ store: c.store,
7689
+ materialSlice: STANDARD_PBR_UBO_SIZE,
7690
+ videoHighPerfAvailable: probeVideoHighPerfUpload(runtime.device),
7691
+ skylightResources: prepareMaterialSkylight(c).skylightResources,
7692
+ resolveRenderTargetTextureSource: runtime.resolveRenderTargetTextureSource,
7693
+ materialBgShared: c.frameState.materialBgShared,
7694
+ materialBgAssemblyCache: c.materialBgAssemblyCache,
7695
+ frameState: c.frameState,
7696
+ bindGroupCounts: c.bindGroupCounts
7697
+ };
7698
+ const materialSlot = c.materialSlotIndices[i]?.[sm.materialSlot] ?? c.materialSlotIndices[i]?.[0];
7699
+ if (materialSlot === void 0) {
7700
+ throw new RhiError({
7701
+ code: "rhi-descriptor-invalid",
7702
+ expected: "a prepared material slot for each shadow submesh",
7703
+ hint: "repair material slot preparation before recording shadows"
7704
+ });
7705
+ }
7706
+ const materialGroup = buildPerSubmeshMaterialBg(
7707
+ materialDeps,
7708
+ submeshMaterial,
7709
+ entry.source.entityKey,
7710
+ entry.world ?? c.world,
7711
+ entryShadowShaderId
7712
+ );
7713
+ shadowPass.setBindGroup(1, materialGroup, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
7714
+ } else {
7715
+ const materialGroup = ensureSpotShadowMaterialBg(c);
7716
+ if (materialGroup === null) continue;
7717
+ shadowPass.setBindGroup(1, materialGroup, [0]);
7718
+ }
7642
7719
  for (const instanceDraw of shadowInstanceDraws) {
7643
7720
  shadowPass.setBindGroup(3, instanceDraw.instanceBindGroup);
7644
7721
  if (entry.mesh.indexed) {
@@ -7649,6 +7726,7 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
7649
7726
  }
7650
7727
  }
7651
7728
  }
7729
+ return complete;
7652
7730
  }
7653
7731
  function ensureSpotShadowMaterialBg(c) {
7654
7732
  const { runtime, frameState, pipelineState } = c;
@@ -7708,7 +7786,6 @@ function ensureSpotShadowMaterialBg(c) {
7708
7786
  }
7709
7787
 
7710
7788
  // src/record/main-pass.ts
7711
- var ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
7712
7789
  function materialDiagnosticsEnabled3() {
7713
7790
  if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
7714
7791
  return false;
@@ -7757,8 +7834,6 @@ function recordMainPass(c, selector, options, graphPass) {
7757
7834
  meshBindGroup,
7758
7835
  frameState,
7759
7836
  bindGroupCounts,
7760
- skylight,
7761
- skylightCount,
7762
7837
  skyboxActive,
7763
7838
  splitLdrSprite,
7764
7839
  msaaActive,
@@ -7833,6 +7908,10 @@ function recordMainPass(c, selector, options, graphPass) {
7833
7908
  const matchedIndices = selector !== void 0 ? buildMatchedRenderableIndices(dispatch, selector) : null;
7834
7909
  const selectorMatchedMaterials = selector !== void 0 ? buildMatchedMaterialHandlesByRenderable(dispatch, selector) : null;
7835
7910
  const matchedMaterials = matchedMaterialsForRecordMode(c, selectorMatchedMaterials, recordMode);
7911
+ const selectedDispatch = dispatch.length === 0 ? void 0 : filterDispatchBySelector(
7912
+ dispatch,
7913
+ selector ?? { LightMode: [passKind === "deferred" ? "Deferred" : "Forward"] }
7914
+ );
7836
7915
  const recordContext = recordMode === void 0 ? c : {
7837
7916
  ...c,
7838
7917
  splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite
@@ -7848,47 +7927,7 @@ function recordMainPass(c, selector, options, graphPass) {
7848
7927
  }
7849
7928
  if (validatedOrdered.length > 0 && !clusteredBindGroupMissing) {
7850
7929
  const MATERIAL_SLICE = STANDARD_PBR_UBO_SIZE;
7851
- const skylightFallback = pipelineState.skylightFallback;
7852
- if (skylightFallback === null) {
7853
- throw new RhiError({
7854
- code: "webgpu-runtime-error",
7855
- expected: "pipelineState.skylightFallback != null when PBR pipeline is active",
7856
- hint: "createRenderer must allocate skylightFallback alongside the PBR pipeline (D-5 round-4)"
7857
- });
7858
- }
7859
- let activeViews;
7860
- runtime.device.queue.writeBuffer(skylightFallback.intensityBuffer, 0, ZERO_SKYLIGHT_PAYLOAD);
7861
- if (skylight !== void 0 && skylightCount >= 1) {
7862
- const [cr, cg, cb] = skylight.color;
7863
- const [qx, qy, qz, qw] = skylight.rotation;
7864
- const uniformPayload = new Float32Array([skylight.intensity, cr, cg, cb, qx, qy, qz, qw]);
7865
- runtime.device.queue.writeBuffer(skylightFallback.intensityBuffer, 0, uniformPayload);
7866
- const cache3 = getOrCreateIblCache(runtime.deviceScope);
7867
- if (cache3.irradianceView !== void 0 && cache3.prefilterView !== void 0 && cache3.brdfLutView !== void 0) {
7868
- activeViews = {
7869
- irr: cache3.irradianceView,
7870
- pref: cache3.prefilterView,
7871
- brdf: cache3.brdfLutView
7872
- };
7873
- }
7874
- }
7875
- const skylightResources = activeViews !== void 0 ? {
7876
- irradianceView: activeViews.irr,
7877
- irradianceSampler: skylightFallback.sampler,
7878
- prefilterView: activeViews.pref,
7879
- prefilterSampler: skylightFallback.sampler,
7880
- brdfLutView: activeViews.brdf,
7881
- brdfLutSampler: skylightFallback.sampler,
7882
- intensityBuffer: skylightFallback.intensityBuffer
7883
- } : {
7884
- irradianceView: skylightFallback.irradianceView,
7885
- irradianceSampler: skylightFallback.sampler,
7886
- prefilterView: skylightFallback.prefilterView,
7887
- prefilterSampler: skylightFallback.sampler,
7888
- brdfLutView: skylightFallback.brdfLutView,
7889
- brdfLutSampler: skylightFallback.sampler,
7890
- intensityBuffer: skylightFallback.intensityBuffer
7891
- };
7930
+ const { skylightResources, activeViews } = prepareMaterialSkylight(c);
7892
7931
  if (materialDiagnosticsEnabled3()) {
7893
7932
  const activeCache = activeViews === void 0 ? void 0 : getOrCreateIblCache(runtime.deviceScope);
7894
7933
  const prefilterViewMipCount = activeCache?.prefilterFaceViewsByMip?.length ?? (activeViews === void 0 ? 1 : 0);
@@ -7898,7 +7937,9 @@ function recordMainPass(c, selector, options, graphPass) {
7898
7937
  deviceGeneration: runtime.deviceScope.generation,
7899
7938
  active: activeViews === void 0 ? "fallback" : "active",
7900
7939
  cache: {
7901
- identity: getOpaqueResourceIdentity(activeCache ?? skylightFallback),
7940
+ identity: getOpaqueResourceIdentity(
7941
+ activeCache ?? pipelineState.skylightFallback
7942
+ ),
7902
7943
  generation: runtime.deviceScope.generation,
7903
7944
  prefilterMipCount: prefilterViewMipCount,
7904
7945
  prefilterViewMipCount
@@ -7984,110 +8025,13 @@ function recordMainPass(c, selector, options, graphPass) {
7984
8025
  bindGroupCounts,
7985
8026
  ...transmissionBackdropView === void 0 ? {} : { transmissionBackdropView }
7986
8027
  };
7987
- const buildPerSubmeshMaterialBg2 = (submeshMaterial, entityKey, materialWorld = world) => buildPerSubmeshMaterialBg(
8028
+ const buildPerSubmeshMaterialBg2 = (submeshMaterial, entityKey, materialWorld = world, materialShaderId = submeshMaterial.materialShaderId) => buildPerSubmeshMaterialBg(
7988
8029
  perSubmeshMaterialBgDeps,
7989
8030
  submeshMaterial,
7990
8031
  entityKey,
7991
- materialWorld
7992
- );
7993
- const cachedMaterialUboPayload = c.materialUboPayloadCache;
7994
- let materialUboPayload;
7995
- if (cachedMaterialUboPayload?.materialSlots === materialSlots && cachedMaterialUboPayload.materialSlotCount === materialSlotCount) {
7996
- materialUboPayload = cachedMaterialUboPayload.payload;
7997
- } else {
7998
- materialUboPayload = new Uint8Array(materialSlotCount * MATERIAL_PER_ENTITY_STRIDE);
7999
- const slotPayload = new Uint8Array(STANDARD_PBR_UBO_SIZE);
8000
- const slotPayloadF32 = new Float32Array(
8001
- slotPayload.buffer,
8002
- slotPayload.byteOffset,
8003
- slotPayload.byteLength / 4
8004
- );
8005
- for (let materialSlot = 0; materialSlot < materialSlots.length; materialSlot += 1) {
8006
- const mat = materialSlots[materialSlot];
8007
- const entry = validatedOrdered[materialSlotOwners[materialSlot] ?? -1];
8008
- if (mat === void 0 || entry === void 0) continue;
8009
- writePbrMaterialUboPayload(slotPayloadF32, mat);
8010
- const materialShaderId = mat.materialShaderId;
8011
- const schema = mat.materialParamSchema ?? (materialShaderId !== void 0 ? runtime.getParamSchema?.(materialShaderId) : void 0);
8012
- applyParamSnapshotToUbo(slotPayloadF32, schema, mat.paramSnapshot);
8013
- const processValue = globalThis.process;
8014
- if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (materialShaderId === "forgeax::default-standard-pbr" || materialShaderId === "forgeax::pbr-skin")) {
8015
- console.error(
8016
- `[render-material] UBO receipt: ${JSON.stringify({
8017
- entityIndex: entry.renderableIndex,
8018
- materialHandle: mat.materialHandle,
8019
- materialShaderId,
8020
- clearcoat: slotPayloadF32[32],
8021
- clearcoatRoughness: slotPayloadF32[33],
8022
- clearcoatNormalScale: slotPayloadF32[34],
8023
- paramSnapshot: mat.paramSnapshot
8024
- })}`
8025
- );
8026
- }
8027
- applyMaterialTextureUvScales(slotPayloadF32, mat, world);
8028
- {
8029
- const matHandleRaw = mat.baseColorTexture;
8030
- if (matHandleRaw !== void 0) {
8031
- const view = residentTextureView(entry.world ?? world, store, runtime, matHandleRaw);
8032
- if (view === void 0) {
8033
- const rawId = matHandleRaw;
8034
- if (!frameState.warnedMissingBaseColorTextureHandles.has(rawId)) {
8035
- frameState.warnedMissingBaseColorTextureHandles.add(rawId);
8036
- console.warn(
8037
- `[forgeax] baseColor texture ${rawId} missing GPU view, rendering debug pink (shader=${materialShaderId ?? "<none>"} entityIndex=${entry.renderableIndex})`
8038
- );
8039
- }
8040
- runtime.errorRegistry.fire(
8041
- new RhiError({
8042
- code: "asset-not-registered",
8043
- expected: "material baseColor TextureAsset uploaded to GPU",
8044
- hint: "register + uploadTexture the baseColor texture before draw([world], { cameraOwner: 0, resourceOwner: 0 }); rendering falls back to debug pink until then",
8045
- detail: { assetHandle: rawId }
8046
- })
8047
- );
8048
- slotPayloadF32[0] = 1;
8049
- slotPayloadF32[1] = 0.4;
8050
- slotPayloadF32[2] = 0.7;
8051
- }
8052
- }
8053
- }
8054
- materialUboPayload.set(slotPayload, materialSlot * MATERIAL_PER_ENTITY_STRIDE);
8055
- }
8056
- for (const entry of validatedOrdered) {
8057
- const mat = entry.source.material;
8058
- const materialShaderId = mat.materialShaderId;
8059
- if (materialShaderId !== "forgeax::sprite" && materialShaderId !== "forgeax::sprite-lit")
8060
- continue;
8061
- const slicesAndMode = mat.paramSnapshot?.slicesAndMode;
8062
- if (slicesAndMode === void 0 || slicesAndMode.length < 4) continue;
8063
- const slicesArr = [
8064
- slicesAndMode[0] ?? 0,
8065
- slicesAndMode[1] ?? 0,
8066
- slicesAndMode[2] ?? 0,
8067
- slicesAndMode[3] ?? 0
8068
- ];
8069
- if (slicesArr[0] === 0 && slicesArr[1] === 0 && slicesArr[2] === 0 && slicesArr[3] === 0)
8070
- continue;
8071
- detectNineSliceScaleTooSmall(
8072
- entry.source.transform.world,
8073
- slicesArr,
8074
- entry.renderableIndex,
8075
- frameState.warnedNineSliceScaleEntities,
8076
- runtime.metrics
8077
- );
8078
- }
8079
- c.materialUboPayloadCache = {
8080
- materialSlots,
8081
- materialSlotCount,
8082
- payload: materialUboPayload
8083
- };
8084
- }
8085
- const materialUboUpload = runtime.device.queue.writeBuffer(
8086
- pipelineState.materialUniformBuffer.buffer,
8087
- 0,
8088
- materialUboPayload
8032
+ materialWorld,
8033
+ materialShaderId
8089
8034
  );
8090
- if (!materialUboUpload.ok) throw materialUboUpload.error;
8091
8035
  const preparedMaterialBindGroups = new Array(materialSlotCount);
8092
8036
  for (let materialSlot = 0; materialSlot < materialSlotCount; materialSlot += 1) {
8093
8037
  const material = materialSlots[materialSlot];
@@ -8100,7 +8044,7 @@ function recordMainPass(c, selector, options, graphPass) {
8100
8044
  owner.world ?? world
8101
8045
  );
8102
8046
  }
8103
- const resolveMaterialBindGroup = (materialSlot, material, entityKey, materialWorld = world) => material.materialShaderId !== POINTS_LINES_MATERIAL_SHADER_ID ? preparedMaterialBindGroups[materialSlot] ?? buildPerSubmeshMaterialBg2(material, entityKey, materialWorld) : buildPerSubmeshMaterialBg2(material, entityKey, materialWorld);
8047
+ const resolveMaterialBindGroup = (materialSlot, material, entityKey, materialWorld = world, materialShaderId = material.materialShaderId) => materialShaderId === material.materialShaderId && materialShaderId !== POINTS_LINES_MATERIAL_SHADER_ID ? preparedMaterialBindGroups[materialSlot] ?? buildPerSubmeshMaterialBg2(material, entityKey, materialWorld, materialShaderId) : buildPerSubmeshMaterialBg2(material, entityKey, materialWorld, materialShaderId);
8104
8048
  pass.setBindGroup(0, viewBindGroup, [viewBindGroupDynamicOffset, 0]);
8105
8049
  const recordGeometry = () => {
8106
8050
  recordGeometryDraws(
@@ -8112,7 +8056,8 @@ function recordMainPass(c, selector, options, graphPass) {
8112
8056
  meshGroup2,
8113
8057
  meshBindGroup,
8114
8058
  resolveMaterialBindGroup,
8115
- passKind
8059
+ passKind,
8060
+ selectedDispatch
8116
8061
  );
8117
8062
  };
8118
8063
  if (c.profilePhase === void 0) {
@@ -8133,7 +8078,8 @@ function recordMainPass(c, selector, options, graphPass) {
8133
8078
  sampleCount,
8134
8079
  resolveMaterialBindGroup,
8135
8080
  skylightResources,
8136
- graphPass
8081
+ graphPass,
8082
+ selectedDispatch
8137
8083
  );
8138
8084
  }
8139
8085
  }
@@ -9983,6 +9929,6 @@ function addTaaResolvePass(graph, current, currentTemporal, previousColor, previ
9983
9929
  });
9984
9930
  }
9985
9931
 
9986
- export { BYTES_PER_DIRECT_LIGHT_SLOT, CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GpuBuffer, GpuTexture, InstanceProjectionStore, MATERIAL_PER_ENTITY_STRIDE, MESH_SSBO_BYTES, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addReflectionFallbackObservationPass, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpClusterMembershipBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForPass, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalBindGroupResources, getTemporalGpuState, getTemporalParamsBuffer, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isTemporalFullscreenBinding, passKindPolicyTable, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveGeometryInstanceBuffer, resolveOutputDither, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectSwapChainFormat, setIblComposedShaders, standardStorageVariantSet, standardTemporalLaneAdmission, standardTemporalPostOrder, standardTopologyVariantSet, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, variantSetFromVertexLayoutProjection, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeSpotModifierTextures, writeViewUbo };
9987
- //# sourceMappingURL=chunk-TCLDX7SZ.mjs.map
9988
- //# sourceMappingURL=chunk-TCLDX7SZ.mjs.map
9932
+ export { BYTES_PER_DIRECT_LIGHT_SLOT, CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GpuBuffer, GpuTexture, InstanceProjectionStore, MATERIAL_PER_ENTITY_STRIDE, MESH_SSBO_BYTES, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, STANDARD_PBR_UBO_SIZE, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addReflectionFallbackObservationPass, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyMaterialTextureUvScales, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpClusterMembershipBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, detectNineSliceScaleTooSmall, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForPass, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalBindGroupResources, getTemporalGpuState, getTemporalParamsBuffer, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isTemporalFullscreenBinding, passKindPolicyTable, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveGeometryInstanceBuffer, resolveOutputDither, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectSwapChainFormat, setIblComposedShaders, standardStorageVariantSet, standardTemporalLaneAdmission, standardTemporalPostOrder, standardTopologyVariantSet, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, variantSetFromVertexLayoutProjection, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePbrMaterialUboPayload, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeSpotModifierTextures, writeViewUbo };
9933
+ //# sourceMappingURL=chunk-TMIASV2N.mjs.map
9934
+ //# sourceMappingURL=chunk-TMIASV2N.mjs.map