Instance data explanation

Hi! Diving ever deeper down the rabbit hole.

I’ve been working with making a LED wall specific node set. One part of this I’m trying out is splitting images, crop them and shove them into layers. As crop isn’t a native function, but a shader, I’ve been trying to merge a couple nodes into one. However, by making a function to crop rather than stuffing the shader part in the main node, I’ve stumbled across something I have dreaded a bit to get to (understand); the node instance data/event thingy.

In the code posted below, the issue I’m having is that I need “*instance” in the “VuoImage crop(…)” function. This however doesen’t seem to work outside the main “nodeEvent” function. Is it possible to get a somewhat simple (as in simple wording, I believe eli5 is the current popular phrasing) explanation of this and how it works?

                     "title" : "Make LED Image Layers",
                     "description" : "Creates image layers per panel for a LED screen",
					 "keywords" : [ ],
					 "version" : "1.0.0",
					 "dependencies" : [ ],
					 "node": {
						 "isInterface" : false

//Crop shader from the Vuo Crop Image node

static const char * cropFragmentShader = VUOSHADER_GLSL_SOURCE(120,

    varying vec4 fragmentTextureCoordinate;
    uniform sampler2D texture;
    uniform float x;
    uniform float y;
    uniform float width;
    uniform float height;

    void main(void)
        gl_FragColor = VuoGlsl_sample(texture, fragmentTextureCoordinate.xy * vec2(width, height) + vec2(x,y));

struct nodeInstanceData
    VuoShader shader;

struct nodeInstanceData * nodeInstanceInit(void)
    struct nodeInstanceData * instance = (struct nodeInstanceData *)malloc(sizeof(struct nodeInstanceData));
    VuoRegister(instance, free);

    instance->shader = VuoShader_make("Crop Image Pixels Shader");
    VuoShader_addSource(instance->shader, VuoMesh_IndividualTriangles, NULL, NULL, cropFragmentShader);

    return instance;

VuoImage crop                   //Function to crop the image and feed it back to the layers
        VuoImage cropImage,
        VuoInteger pixelWidth,
        VuoInteger pixelHeight,
        VuoInteger pixelPosX,
        VuoInteger pixelPosY
    VuoImage workImage = VuoImage_makeCopy(cropImage, false);

    if (!cropImage)
            return workImage = NULL;

        VuoShader_setUniform_VuoImage((*instance)->shader, "texture", workImage);
        VuoShader_setUniform_VuoReal ((*instance)->shader, "x",       pixelPosX);
        VuoShader_setUniform_VuoReal ((*instance)->shader, "y",       pixelPosY);
        VuoShader_setUniform_VuoReal ((*instance)->shader, "width",   pixelWidth);
        VuoShader_setUniform_VuoReal ((*instance)->shader, "height",  pixelHeight);

        return VuoImageRenderer_render((*instance)->shader, pixelWidth, pixelHeight, VuoImage_getColorDepth(workImage));

void nodeEvent
        VuoInstanceData(struct nodeInstanceData *) instance,
        VuoInputData(VuoText) name,
        VuoInputData(VuoImage) image,
        VuoInputData(VuoBoolean, {"default":false}) tileImage,
        VuoInputData(VuoPoint4d) screenSize,
        VuoInputData(VuoList_VuoPoint4d) positions,
        VuoInputData(VuoList_VuoReal) opacity,
        VuoInputData(VuoInteger) totalPanels,
        VuoOutputData(VuoList_VuoLayer) layers

    *layers = VuoListCreate_VuoLayer();

    if(tileImage == false) {
        for(int i = 1; i <= totalPanels; ++i){
            char a = i;
                            VuoText_insert(name, 100, &a),
                                VuoListGetValue_VuoPoint4d(positions, i).z,
                                VuoListGetValue_VuoPoint4d(positions, i).w,
                            VuoListGetValue_VuoPoint2d(positions, i),
                            VuoListGetValue_VuoReal(opacity, i),

Add a parameter to your crop function like this:

VuoImage crop                   //Function to crop the image and feed it back to the layers
        VuoInstanceData(struct nodeInstanceData *) instance,
        VuoImage cropImage,

You’ll also need to rename the nodeEvent function to nodeInstanceEvent.

The instance data is just any type of data that the node holds on to as long as the composition is running. For example, the Count node keeps track of its count. When an event hits the Count node’s Increment input port, the node adds the port value to the stored count and outputs the new count.

In the case of your node, the only reason for using instance data is to improve performance. If you wanted the node to be really slow, you could make the nodeEvent function create the shader and then throw it away each time. (For many data types it’s perfectly OK to create them and throw them away, but shaders happen to be expensive to make.) Instead, your node creates the shader when the composition starts running (nodeInstanceInit) and reuses it each time an event hits the node (nodeInstanceEvent).

Not quite eli5 level, but does that make sense?

More info in the API documentation — Developing a Node Class, subsection called “A stateful node class”.

Thank you! I’ve tried a bit, but think I was stumbling across unrelated stupidity on my end. The instanceFini which I completely forgot about threw me for a loop for some time. Got it to work-ish, but not production ready for the test I had in mind.