Godot Version
4.7
Question
Hello,
I’m trying to make a compositor effect. The compositor is on camera3D, placed inside a SubViewport, inside a SubViewportContainer, and the camera is not instanciated at the start of the game.
I mixed different examples I’ve seen out there to create my code (in C#), but I’m facing an issue : my compositor effect is visible in the editor (when the compositor has [tool]) and works perfectly fine, but as soon as I run the project, the compositor stop appearing. From the tests I’ve done, the render goes throught TestEffect()(see code below), but never throught _RenderCallback(…)when in game.
I have no experience with compositor effects and compute shaders (only experienced fragment shaders) thus I kind of have no idea what could possibly be wrong.
here’s my code (TestEffect.cs) :
using Godot;
[GlobalClass]
[Tool]
public partial class TestEffect : CompositorEffect
{
public RenderingDevice rd;
public Rid shader;
public Rid pipeline;
public TestEffect()
{
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
if(!Engine.IsEditorHint()) GD.Print("Compositor effect instanciation in-game");
}
public override void _Notification( int what )
{
if ( what == NotificationPredelete )
{
if ( shader.IsValid )
{
try{
rd.FreeRid(shader);
}
catch {GD.Print("Failed to free the shader");}
}
}
}
public void _InitializeCompute()
{
rd = RenderingServer.GetRenderingDevice();
if ( rd == null )
{
GD.Print("Failed to get rendering device");
return;
}
var shaderFile = GD.Load<RDShaderFile>("res://Assets/Shaders/test_shader.glsl");
var shaderSpirv = shaderFile.GetSpirV();
GD.Print("Shader compiled");
shader = rd.ShaderCreateFromSpirV( shaderSpirv );
if ( shader.IsValid )
{
pipeline = rd.ComputePipelineCreate(shader);
GD.Print("Pipeline initialized");
}
}
public override void _RenderCallback( int callbacktype, RenderData pRenderData)
{
if(rd == null) _InitializeCompute();
if(rd == null) return;
if(!Engine.IsEditorHint()) GD.Print("Render Callback");
var renderSceneBuffers = ( RenderSceneBuffersRD ) pRenderData.GetRenderSceneBuffers();
Vector2I size = renderSceneBuffers.GetInternalSize();
var pushConstant = new float[]{
size.X,
size.Y
};
Vector3I groups = new Vector3I(size.X, size.Y, 1);
var bytesList = new List<byte>();
Array.ForEach( pushConstant, c => bytesList.AddRange( BitConverter.GetBytes( c ) ) );
var pushConstantBytes = bytesList.ToArray();
int viewCount = (int) renderSceneBuffers.GetViewCount();
for ( var i = 0; i < viewCount; i++)
{
var view = (uint) i;
Rid inputImage = renderSceneBuffers.GetColorLayer(view);
var uniform = new RDUniform();
uniform.UniformType = RenderingDevice.UniformType.Image;
uniform.Binding = 0;
uniform.AddId( inputImage );
var uniformSet = UniformSetCacheRD.GetCache(shader, 0, new Godot.Collections.Array<RDUniform>(){uniform});
var computeList = rd.ComputeListBegin();
rd.ComputeListBindComputePipeline(computeList, pipeline);
rd.ComputeListBindUniformSet(computeList, uniformSet, 0);
rd.ComputeListSetPushConstant(computeList, pushConstantBytes, (uint) pushConstantBytes.Length );
rd.ComputeListDispatch(computeList, (uint)groups.X, (uint)groups.Y, (uint)groups.Z);
rd.ComputeListEnd();
}
}
}
and my shader (test_shader.glsl) :
#[compute]
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(push_constant) uniform push_constants {
vec2 raster_size;
} p;
layout(rgba16f, set = 0, binding = 0) uniform image2D screen_tex;
void main() {
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
vec2 size = p.raster_size;
if (uv.x >= size.x || uv.y >= size.y)
return;
vec4 color = imageLoad(screen_tex, uv);
vec4 res;
if(color.a>0.) res = vec4(1., 0., 0., 1.);
else res = vec4(0., 0., 0., 0.);
imageStore(screen_tex, uv, res);
}
Some additional precisions : I verified the remote, the camera, subviewport and container are visible (and display effectively the content but without applying the compositor effect). The compositor effect is also enabled.
If you have any idea that could help, that would be awesome. Please feel free to ask for more details.