#Using a previous frame in compute shader/compositor effect

1 messages · Page 1 of 1 (latest)

cerulean spindle
#

Hi, I'm trying to use a compute shader for a true(ish) datamoshing effect. For the simulation to be somewhat accurate to how a keyframe corruption looks, I need a way to access a cumulatively previous frame. What I need is to, if necessary, render the exact same buffer to screen. Like, it should be a static image if I do nothing to it.

I tried getting the image from the shader with prevframe[view] = Image.create_from_data(size.x, size.y, false, Image.FORMAT_RGBAF, rd.texture_get_data(screentex, 0)), but that errors in Texture requires the 'RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT' to be set to be retrieved. and I'm not sure how to fix it, because I'm following the tutorial that's on the official compositor effect manual, and it's only dealing with the RID's of things, so I can't access the texture itself to set the correct bit? I'm not exactly sure how to do that.

I'll write all the pseudocode I'm trying in the next message, but it does error out.

Also, I don't know how to use a backbuffer copy in a shader like this.

#

Here's the pseudocode of what I'm trying (assume I'm taking the precautions of validating things, Discord won't let me write too long of a message.):

func _render_callback(effect_callback_type: int, render_data: RenderData) -> void:

    var scene_buffers : RenderSceneBuffersRD = render_data.get_render_scene_buffers()
    
    if prevframe.size() == 0:
        for i in scene_buffers.get_view_count():
            prevframe.append(scene_buffers.get_color_layer(i))

    var x_groups : int = (size.x - 1) / 8.0 + 1
    var y_groups : int = (size.y - 1) / 8.0 + 1

    for view in scene_buffers.get_view_count():
        var screentex : RID = scene_buffers.get_color_layer(view)
        var motiontex : RID = scene_buffers.get_velocity_layer(view)

        var uniform : RDUniform = RDUniform.new()
        uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
        uniform.binding = 0
        uniform.add_id(screentex)
        
        var uniform2 := RDUniform.new()
        uniform2.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
        uniform2.binding = 1
        uniform2.add_id(motiontex)
        
        var uniform3 := RDUniform.new()
        uniform3.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
        uniform3.binding = 2
        uniform3.add_id(prevframe[view])
        
        var image_uniform_set : RID = UniformSetCacheRD.get_cache(shader, 0, [uniform, uniform2, uniform3])
        
        var compute_list : int = rd.compute_list_begin()

        rd.compute_list_bind_compute_pipeline(compute_list, main_pipeline)
        rd.compute_list_bind_uniform_set(compute_list, image_uniform_set, 0)
        rd.compute_list_set_push_constant(compute_list, push_constants.to_byte_array(), push_constants.size() * 4)
        rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1)
        rd.compute_list_end()

        prevframe[view] = Image.create_from_data(size.x, size.y, false, Image.FORMAT_RGBAF, rd.texture_get_data(screentex, 0))

cerulean spindle
#

Update, tried rd.texture_update(screentex, 0, [rd.TEXTURE_USAGE_CAN_COPY_FROM_BIT, rd.TEXTURE_USAGE_CAN_COPY_TO_BIT, rd.TEXTURE_USAGE_CAN_UPDATE_BIT]), but Texture requires the 'RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT' to be set to be updatable.

cerulean spindle
#

I did it by creating a texture and setting it as readable, but now I get a black screen when running it, because it can't set the first frame as a texture...

#

Welp, now I have a different problem, but just to tell the solution to anyone else, I had to make a completely new texture

...
var prevframe := []
...
    if prevframe.size() == 0:
        for i in scene_buffers.get_view_count():
            prevframe.append(RDTextureFormat.new())
            prevframe[i].width = size.x
            prevframe[i].height = size.y
            prevframe[i].format = RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT
            prevframe[i].usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT
            prevframe[i] = rd.texture_create(prevframe[i], RDTextureView.new(), [Image.create(size.x, size.y, false, Image.FORMAT_RGBAF).get_data()])
...
    prevframe[view] = rd.texture_create_shared(RDTextureView.new(), prevframe[view])
#

The trick is creating a var and updating it once:

        if gambiarra == 0:
            gambiarra += 1
#

Pass the variable to the shader, and do a little trick:

    var push_constants : PackedFloat32Array = PackedFloat32Array()
    push_constants.append(size.x)
    push_constants.append(size.y)
    push_constants.append(Time.get_ticks_msec())
    push_constants.append(gambiarra)

And in the shader:

layout(push_constant, std430) readonly uniform Params {
    vec2 screen_size;
    float time;
    int gambi;
} variables;

...
    if(variables.gambi <= 1 || color.rgb == vec3(0.0))
    {
        color = original;
    }

    imageStore(prev_tex, uv, color);
    imageStore(screen_tex, uv, color);
#

Whenever you want to update the whole texture, just set the variable you picked to 0 again, and it should update by itself