export function createRendererPostprocessPasses(postprocess) {
    const passes = [];
    if (postprocess.bloom) {
        passes.push({ name: "bloom", options: postprocess.bloom === true ? {} : postprocess.bloom });
    }
    if (postprocess.toneMapping !== false) {
        passes.push({ name: "tone-mapping", options: postprocess.toneMapping ?? {} });
    }
    if (postprocess.colorGrade) {
        passes.push({ name: "color-grade", options: postprocess.colorGrade === true ? {} : postprocess.colorGrade });
    }
    if (postprocess.chromaticAberration) {
        passes.push({ name: "chromatic-aberration", options: postprocess.chromaticAberration === true ? {} : postprocess.chromaticAberration });
    }
    if (postprocess.filmGrain) {
        passes.push({ name: "film-grain", options: postprocess.filmGrain === true ? {} : postprocess.filmGrain });
    }
    if (postprocess.depthOfField) {
        passes.push({ name: "depth-of-field", options: postprocess.depthOfField });
    }
    if (postprocess.motionBlur) {
        passes.push({ name: "motion-blur", options: postprocess.motionBlur });
    }
    if (postprocess.contactShadow) {
        passes.push({ name: "contact-shadow", options: postprocess.contactShadow });
    }
    if (postprocess.ssao) {
        passes.push({ name: "ssao", options: postprocess.ssao });
    }
    if (postprocess.ssr) {
        passes.push({ name: "ssr", options: postprocess.ssr });
    }
    if (postprocess.taa) {
        passes.push({ name: "taa", options: postprocess.taa });
    }
    if (postprocess.outline) {
        passes.push({ name: "outline", options: postprocess.outline === true ? {} : postprocess.outline });
    }
    if (postprocess.fxaa) {
        passes.push({ name: "fxaa", options: postprocess.fxaa === true ? {} : postprocess.fxaa });
    }
    return passes;
}
export function createRendererPostprocessPlanDiagnostics(postprocess, context = {}) {
    const passes = createRendererPostprocessPasses(postprocess);
    const sourceTargetFormat = context.sourceTargetFormat ?? context.targetFormat ?? "rgba8";
    const targetFormat = context.targetFormat ?? sourceTargetFormat;
    const canFuseLdr = canFuseLdrPostprocessPlan(sourceTargetFormat, passes);
    const executionMode = passes.length === 0
        ? "none"
        : canFuseLdr
            ? context.nativeLdrPostprocess
                ? "renderer-owned-fused-ldr-native"
                : "renderer-owned-fused-ldr-readback"
            : "renderer-owned-pass-chain-readback";
    const missingInputs = missingPostprocessInputs(passes, context);
    const clarityWarnings = postprocessClarityWarnings(postprocess, passes, executionMode);
    const passDiagnostics = passes.map((pass) => {
        const requiresDepth = isDepthPostprocessPassName(pass.name);
        const hasDepthInput = requiresDepth && postprocessPassHasDepth(pass.options);
        const usesRendererOwnedDepth = requiresDepth && !hasDepthInput && context.rendererDepthAvailable === true;
        const requiresVelocity = pass.name === "motion-blur";
        const hasVelocityInput = requiresVelocity && postprocessPassHasVelocity(pass.options);
        return {
            name: pass.name,
            rendererOwned: true,
            publicPixelKernel: true,
            requiresDepth,
            hasDepthInput,
            usesRendererOwnedDepth,
            requiresVelocity,
            hasVelocityInput,
            usesReadback: executionMode !== "renderer-owned-fused-ldr-native"
        };
    });
    return {
        source: "Renderer.postprocessPlan",
        passCount: passes.length,
        passNames: passes.map((pass) => pass.name),
        targetFormat,
        sourceTargetFormat,
        executionMode,
        canFuseLdr,
        requiresDepthTexture: passes.some((pass) => isDepthPostprocessPassName(pass.name) && !postprocessPassHasDepth(pass.options)),
        missingInputs,
        readbackPassNames: executionMode === "renderer-owned-fused-ldr-native" ? [] : passes.map((pass) => pass.name),
        rendererOwnedPassNames: passes.map((pass) => pass.name),
        clarityWarnings,
        passes: passDiagnostics,
        claimBoundary: "This plan describes the renderer-owned postprocess pass chain for the current frame. It does not prove EffectComposer parity, LUT/AOV layers, temporal accumulation, or missing depth/velocity inputs unless the report explicitly shows those inputs."
    };
}
function canFuseLdrPostprocessPlan(sourceTargetFormat, passes) {
    return passes.length > 1
        && (sourceTargetFormat === "rgba8" || passes[0]?.name === "tone-mapping")
        && passes.every((pass) => pass.name === "tone-mapping" || pass.name === "color-grade" || pass.name === "fxaa")
        && passes.every((pass, index) => {
            const previousRank = index === 0 ? -1 : ldrFusionPassRank(passes[index - 1].name);
            return ldrFusionPassRank(pass.name) >= previousRank;
        });
}
function ldrFusionPassRank(name) {
    if (name === "tone-mapping")
        return 0;
    if (name === "color-grade")
        return 1;
    if (name === "fxaa")
        return 2;
    return Number.POSITIVE_INFINITY;
}
function isDepthPostprocessPassName(name) {
    return name === "depth-of-field" || name === "contact-shadow" || name === "ssao" || name === "ssr";
}
function postprocessPassHasDepth(options) {
    return typeof options === "object" && options !== null && "depth" in options && Boolean(options.depth);
}
function postprocessPassHasVelocity(options) {
    return typeof options === "object" && options !== null && "velocity" in options && Boolean(options.velocity);
}
function missingPostprocessInputs(passes, context) {
    const missing = [];
    for (const pass of passes) {
        if (isDepthPostprocessPassName(pass.name) && !postprocessPassHasDepth(pass.options) && context.rendererDepthAvailable !== true) {
            missing.push(`${pass.name}:depth`);
        }
        if (pass.name === "motion-blur" && !postprocessPassHasVelocity(pass.options)) {
            missing.push("motion-blur:velocity");
        }
    }
    return missing;
}
function postprocessClarityWarnings(postprocess, passes, executionMode) {
    const warnings = [];
    const bloom = typeof postprocess.bloom === "object" && postprocess.bloom !== null ? postprocess.bloom : undefined;
    if (bloom) {
        const threshold = bloom.threshold ?? 0.75;
        const intensity = bloom.intensity ?? 0.35;
        const radius = bloom.radius ?? 1;
        if (threshold < 0.48 || intensity > 0.62 || radius > 3) {
            warnings.push(`bloom-noise-risk threshold=${round3(threshold)} intensity=${round3(intensity)} radius=${round3(radius)}`);
        }
    }
    const filmGrain = typeof postprocess.filmGrain === "object" && postprocess.filmGrain !== null ? postprocess.filmGrain : undefined;
    if (filmGrain && (filmGrain.intensity ?? 0.08) >= 0.06) {
        warnings.push(`film-grain-noise-risk intensity=${round3(filmGrain.intensity ?? 0.08)}`);
    }
    if (executionMode === "renderer-owned-pass-chain-readback" && passes.length > 2) {
        warnings.push("multi-pass-readback-cost");
    }
    return warnings;
}
function round3(value) {
    return Math.round(value * 1000) / 1000;
}
//# sourceMappingURL=RendererPostprocessPlan.js.map