How do I copy Cogl.Texture's data dynamically from a Clutter.ShaderEffect?

Currently I’m mimicking ShaderToy style buffer pass using an extension, where I need to get texture data from an Effect and apply it to another Effect through Cogl.Pipeline.set_layer_texture(), below I’ve implemented a demo extension to test

import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
import Clutter from 'gi://Clutter';
import Cogl from 'gi://Cogl';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';

const DemoEffect = GObject.registerClass(
class DemoEffect extends Clutter.ShaderEffect {
    _init(params = {}) {
        super._init();

        if (params.name)
            this.set_name(params.name);

        this._bufferA = params.buff

        this.set_shader_source(this.transformShader(this._bufferA));
        this._startTime = GLib.get_monotonic_time() / 1e6;
    }

    vfunc_paint(node, paintContext, flags) {
        super.vfunc_paint(node, paintContext, flags);
        if(this._bufferA){
            return;
        }
        
        if(!this.texturesLoaded) {
            this.get_pipeline().set_layer_texture(0, this._texture);
            this.texturesLoaded = true;
        }

        let buffer = Main.layoutManager._backgroundGroup.get_children()
            .find(a => a.get_effect('buffer-a'));
        let bufferTex = buffer.get_effect('buffer-a').get_texture();
        
        bufferTex?.get_data(
            bufferTex.get_format(),
            this._rowstride,
            this._pixelData
        );

        this._texture.set_data(
            bufferTex?.get_format(),
            this._rowstride,
            this._pixelData,
            0
        );
    }

    vfunc_set_actor(actor) {
        super.vfunc_set_actor(actor);
        if (this._tickId) {
            GLib.Source.remove(this._tickId);
            this._tickId = null;
        }
        if (this._sizeId && this._actor) {
            this._actor.disconnect(this._sizeId);
            this._sizeId = null;
        }
        this._actor = actor;

        if(actor) {
            const interval = 1000/60;

            if(!this._bufferA) {
                let ctx = actor.get_context().get_backend().get_cogl_context();
                let width = actor.width;
                let height = actor.height;
                this._rowstride = width * 4;
                this._pixelData = new Uint8Array(this._rowstride * height);
                this._texture = Cogl.Texture2D.new_with_size(ctx, width, height);
                console.log('width: '+width+' height: '+height);
            }

            this._updateResolution(actor);

            this._sizeId = actor.connect('notify::size', () => {
                this._updateResolution(actor);
                this.texturesLoaded = false;
            });

            for (let i = 0; i < 4; i++) {
                this.set_uniform_value(`iChannel${i}`, parseInt(i));
            }

            this._tickId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, interval, () => {
                let now = (GLib.get_monotonic_time() / 1e6) - this._startTime;
                this.set_uniform_value('iTime', parseFloat(now));
                return GLib.SOURCE_CONTINUE;
            });
        }
    }

    _updateResolution(actor) {
        this.set_uniform_value('res[0]', parseFloat(actor.get_width()));
        this.set_uniform_value('res[1]', parseFloat(actor.get_height()));
    }

    transformShader(bufferA) {
        const header = `uniform float iTime;
uniform float res[2];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;

vec3 iResolution = vec3(res[0], res[1], 0.0);
vec2 fragCoord = cogl_tex_coord_in[0].st * iResolution.xy;

`;

        const footer = `
void main() {
    vec4 color = vec4(0.0);
    mainImage(color, fragCoord);
    cogl_color_out = color;
}`;

        const source = bufferA ? 
`void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord / iResolution.xy;
    float x = uv.x;
    float y = uv.y;
    float r = sin(x * 10.0 + iTime) * 0.5 + 0.5;
    float g = sin(y * 10.0 - iTime) * 0.5 + 0.5;
    fragColor = vec4(r, g, 0.0, 1.0);
}` 
        :
`void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord / iResolution.xy;
    vec4 a = texture(iChannel0, uv);
    fragColor = vec4(a.b, a.g, a.r, 1.0);
}`;

        return header + source + footer;
    }
});

export default class ShaderExtension extends Extension {
    enable() {
        this._bufferA = new Clutter.Actor({
            width: 100,
            height: 100,
            effect: new DemoEffect({
                name: 'buffer-a',
                buff: true
            })
        });

        this._final = new Clutter.Actor({
            width: 100,
            height: 100,
            effect: new DemoEffect({
                name: 'final-effect',
                buff: false
            })
        });

        this._final.set_position(global.stage.width/2, global.stage.height/2);

        Main.layoutManager._backgroundGroup.add_child(this._bufferA);
        Main.layoutManager._backgroundGroup.add_child(this._final);
    }

    disable() {
        Main.layoutManager._backgroundGroup.remove_child(this._bufferA);
        Main.layoutManager._backgroundGroup.remove_child(this._final);
        this._bufferA.destroy();
        this._bufferA = null;
        this._final.destroy();
        this._final = null;
    }
}

I’m trying to get texture data from existing effect from below code,

        let buffer = Main.layoutManager._backgroundGroup.get_children()
            .find(a => a.get_effect('buffer-a'));
        let bufferTex = buffer.get_effect('buffer-a').get_texture();
        
        bufferTex?.get_data(
            bufferTex.get_format(),
            this._rowstride,
            this._pixelData
        );

        this._texture.set_data(
            bufferTex?.get_format(),
            this._rowstride,
            this._pixelData,
            0
        );

it is giving like

== Stack trace for context 0x56355274b440 ==
#0   563552843438 i   file:///home/zombie/.local/share/gnome-shell/extensions/test@shader/extension.js:190 (4fc4b0c19c0 @ 290)
#1   5635528433a8 i   resource:///org/gnome/shell/ui/init.js:21 (2bf1e5a7ea60 @ 48)

Can anyone give suggestions to get texture data from an existing effect or any other way to do this type of low-level operation?

This issue has been solved because I’m using Cogl.Offscreen to draw on a Cogl.Texture then set that Texture as layer to my Main Effect’s pipeline

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.