10000 FrameGraph: multi-valued task properties by Popov72 · Pull Request #17247 · BabylonJS/Babylon.js · GitHub
[go: up one dir, main page]

Skip to content

Conversation

Popov72
Copy link
Contributor
@Popov72 Popov72 commented Oct 7, 2025

This is a PoC, intended to gather feedback on this feature (is it useful? How can it best be implemented? etc.).

Feature

Currently, properties in tasks are simple properties, such as public numCascades: number; in the FrameGraphCascadedShadowGeneratorTask class.

The idea is to be able to set a value for a property based on a condition. Of course, we could do this in user code:

if (shadowQuality === "low") shadowGenTask.numCascades = 2;
if (shadowQuality === "middle") shadowGenTask.numCascades = 3;
if (shadowQuality === "high") shadowGenTask.numCascades = 4;

This is a bit tedious, and the code can quickly become cluttered with numerous conditions if the frame graph has a high degree of variability (some parts of the graph can depend on the engine type, others on globalQuality, shadowQuality, ... parameters, etc.).

In this PoC, I suggest adding an additional variable numCascadesMulti, which allows you to define the property as follows:

shadowGenTask.numCascadesMulti = [
    { value: 2, evaluate: () => shadowQuality === "low" },
    { value: 3, evaluate: () => shadowQuality === "middle" },
    { value: 4, evaluate: () => shadowQuality === "high" },
];

When the graph is built, all multi properties of all tasks are parsed and evaluated: the first entry that returns true will set its value as the property value.

In this way, the variations are closely linked to the property and are clearly part of the graph itself, rather than additional code.

In addition, a new property public available?: () => boolean; has been added to the task class: if the property is defined and the function returns false, everything behaves as if the task were not in the task list: it will neither be built nor executed.

Example of use

Let's imagine that we want to display volumetric lighting only if the globalQuality parameter is set to “high”:

Without the PR:

const renderTask = new BABYLON.FrameGraphObjectRendererTask("renderScene", ...);
const imageProcessing = new BABYLON.FrameGraphImageProcessingTask("imgProcessing", ...);

if (globalQuality === "high") {
    const volumLightingTask = new FrameGraphVolumetricLighting("volumLighting", ...);

    volumLightingTask.sourceTexture = renderTask.outputTexture;
    imageProcessing.sourceTexture = volumLightingTask.outputTexture;
} else {
    imageProcessing.sourceTexture = renderTask.outputTexture;
}

With the PR:

const renderTask = new BABYLON.FrameGraphObjectRendererTask("renderScene", ...);
const volumLightingTask = new FrameGraphVolumetricLighting("volumLighting", ...);

volumLightingTask.available = () => globalQuality === "high";
volumLightingTask.sourceTexture = renderTask.outputTexture;

const imageProcessing = new BABYLON.FrameGraphImageProcessingTask("imgProcessing", ...);

imageProcessing.sourceTextureMulti = [
    { value: volumLightingTask.outputTexture, evaluate: () => globalQuality === "high" },
    { value: renderTask.outputTexture, evaluate: () => globalQuality !== "high" }
];

When you change the value of globalQuality, in the first case, you must reset the frame graph and re-execute the function that creates it. In the second case, you just need to execute frameGraph.build(). Furthermore, I think the code is more readable in the second case, and even more so if additional conditions are added to the code.

Implementation

You must use a decorator to indicate that a property must have a corresponding “multi” property:

@FrameGraphTaskMultiProperty("_numCascadesSetter")
public numCascades = CascadedShadowGenerator.DEFAULT_CASCADES_COUNT;

The decorator creates the numCascadesMulti property. However, in order for the property to be available in IntelliSense, I had to declare it in the class:

public numCascadesMulti: FrameGraphMultiValueType<number>;

I don't know if there's a way to make it available via IntelliSense without having to add this declaration...

The generator can take an additional parameter, which is the name of the “setter” function, a function that will be called when the property changes (in case you need to execute additional code when this happens):

protected _numCascadesSetter(_oldValue: number) {
    this._setupShadowGenerator();
}

Note that there are no breaking changes, numCascades still exists, and if you assign a value to it, this value takes precedence over what you might have assigned to numCascadesMulti: the setter for numCascades deletes the FrameGraphTaskProperty instance created when numCascadesMulti was first accessed.

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

1 similar comment
@bjsplat
Copy link
Collaborator
bjsplat commented Oct 7, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0