Files
workinf_Blender_Wasm/web/protocol/render-capabilities.ts
mes123456 66d3f4e0f4
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
feat(web): complete portable Blender feature gaps
2026-09-06 19:33:50 +08:00

76 lines
4.3 KiB
TypeScript

import { blockedGate, capabilityIssue, readyGate, type CapabilityGateResult } from "./capability-gates";
import type { ShaderNodeType } from "./shader-graph";
export type RenderBackend = "WEBGL2" | "WEBGPU";
export type PostProcessPass = "FXAA" | "BLOOM" | "SSAO" | "SSR" | "TAA" | "DOF" | "MOTION_BLUR";
export type RenderPassName = "COMBINED" | "Z" | "MIST" | "NORMAL" | "VECTOR" | "UV" | "OBJECT_INDEX" | "MATERIAL_INDEX" | "SHADOW" | "AO" | "EMISSION" | "ENVIRONMENT" | "DIFFUSE_DIRECT" | "DIFFUSE_INDIRECT" | "GLOSSY_DIRECT" | "GLOSSY_INDIRECT" | "TRANSMISSION_DIRECT" | "TRANSMISSION_INDIRECT" | "VOLUME_DIRECT" | "VOLUME_INDIRECT" | "CRYPTOMATTE_OBJECT" | "CRYPTOMATTE_MATERIAL" | "CRYPTOMATTE_ASSET";
export type RenderCapabilityRequest =
| { kind: "ARBITRARY_SHADER"; nodeTypes: string[] }
| { kind: "VOLUME" }
| { kind: "SUBSURFACE" }
| { kind: "WEBGPU_BACKEND" }
| { kind: "POSTPROCESS"; passes: PostProcessPass[] }
| { kind: "RENDER_PASSES"; passes: RenderPassName[] };
export interface RenderCapabilityContext {
webgpuAvailable?: boolean;
webgpuRendererBundled?: boolean;
supportedShaderNodes?: ReadonlySet<ShaderNodeType>;
supportedPostProcessPasses?: ReadonlySet<PostProcessPass>;
supportedRenderPasses?: ReadonlySet<RenderPassName>;
volumeRendererAvailable?: boolean;
subsurfaceRendererAvailable?: boolean;
}
const boundedShaderNodes = new Set<ShaderNodeType>([
"RGB",
"VALUE",
"MATH",
"IMAGE_TEXTURE",
"NORMAL_MAP",
"PRINCIPLED",
"MATERIAL_OUTPUT",
]);
export function gateRenderCapability(
request: RenderCapabilityRequest,
context: RenderCapabilityContext = {},
): CapabilityGateResult {
if (request.kind === "ARBITRARY_SHADER") {
const supported = context.supportedShaderNodes ?? boundedShaderNodes;
const unsupported = [...new Set(request.nodeTypes.filter((type) => type === "UNSUPPORTED" || !supported.has(type as ShaderNodeType)))].sort();
if (unsupported.length === 0) return readyGate("PBR-012", "BOUNDED_SHADER_GRAPH");
return blockedGate("PBR-012", "ARBITRARY_SHADER", [
capabilityIssue("SHADER_NODE_UNSUPPORTED", `Web shader compiler does not support: ${unsupported.join(", ")}`, "nodeTypes"),
]);
}
if (request.kind === "VOLUME") {
return context.volumeRendererAvailable
? readyGate("PBR-012", "VOLUME")
: blockedGate("PBR-012", "VOLUME", [capabilityIssue("VOLUME_SHADER_UNAVAILABLE", "Volume transport is not available in the Web renderer")]);
}
if (request.kind === "SUBSURFACE") {
return context.subsurfaceRendererAvailable
? readyGate("PBR-012", "SUBSURFACE")
: blockedGate("PBR-012", "SUBSURFACE", [capabilityIssue("SUBSURFACE_SHADER_UNAVAILABLE", "Blender-compatible subsurface scattering is not available in the Web renderer")]);
}
if (request.kind === "WEBGPU_BACKEND") {
if (context.webgpuAvailable && context.webgpuRendererBundled) return readyGate("PBR-012", "WEBGPU_BACKEND");
const reason = context.webgpuAvailable ? "The WebGPU renderer bundle is not installed" : "WebGPU is unavailable in this browser or device";
return blockedGate("PBR-012", "WEBGPU_BACKEND", [capabilityIssue("WEBGPU_RENDERER_UNAVAILABLE", reason)]);
}
if (request.kind === "RENDER_PASSES") {
const supported = context.supportedRenderPasses ?? new Set<RenderPassName>(["COMBINED", "Z", "MIST", "NORMAL", "VECTOR", "UV", "OBJECT_INDEX", "MATERIAL_INDEX", "SHADOW", "AO", "EMISSION", "ENVIRONMENT", "DIFFUSE_DIRECT", "DIFFUSE_INDIRECT", "GLOSSY_DIRECT", "GLOSSY_INDIRECT", "TRANSMISSION_DIRECT", "TRANSMISSION_INDIRECT", "VOLUME_DIRECT", "VOLUME_INDIRECT", "CRYPTOMATTE_OBJECT", "CRYPTOMATTE_MATERIAL", "CRYPTOMATTE_ASSET"]);
const unsupported = [...new Set(request.passes.filter((pass) => !supported.has(pass)))];
return unsupported.length === 0
? readyGate("PBR-012", "RENDER_PASSES")
: blockedGate("PBR-012", "RENDER_PASSES", [capabilityIssue("RENDER_PASS_UNAVAILABLE", `Unsupported render passes: ${unsupported.join(", ")}`, "passes")]);
}
const supported = context.supportedPostProcessPasses ?? new Set<PostProcessPass>();
const unsupported = [...new Set(request.passes.filter((pass) => !supported.has(pass)))];
return unsupported.length === 0
? readyGate("PBR-012", "POSTPROCESS")
: blockedGate("PBR-012", "POSTPROCESS", [capabilityIssue("POSTPROCESS_PASS_UNAVAILABLE", `Unsupported post-process passes: ${unsupported.join(", ")}`, "passes")]);
}