cbaggers / varjo

Lisp to GLSL Language Translator
BSD 2-Clause "Simplified" License
223 stars 23 forks source link

Accessing SSBO from outside of the main compute stage function #177

Closed ghost closed 6 years ago

ghost commented 6 years ago

I'm still trying to get the node functions to work outside of the main function. Maybe you could help me with figuring how to get around this one. As you know the models I'm developing are constructed from nodes and from the point of view of both cepl and varjo, those are both functions. Nodes being regular gpu functions and model being the main function in the pipeline.

Below I have an example code for a simple node that fails to build because of an undefined variable data. This is expected as that variable is not defined in the node, but in the model as ssbo.

(defun-g node-0 ()
  (when
      (eql (aref (node-inputs 
                  (aref (model-nodes data)
                        (int (x gl-work-group-id))))
                 0)
           10)
    (for (i 0) (< i 10) (++ i)
         (let* ((output-slot (aref (node-outputs 
                                    (aref (model-nodes data)
                                          (int (x gl-work-group-id))))
                                   (int i)))
                (output-node (int (trunc (/ output-slot
                                            10)))))
           (setf (aref (node-inputs 
                        (aref (model-nodes data)
                              output-node))
                       (int (mod output-slot
                                 10)))
                 30)))))

Copying this definition into the node function does pass the initial check.

(defun-g node (&uniform (data model :ssbo)) rest-of-the-function) However, when the model itself is defined as below.

(defun-g model-shader (&uniform (data model :ssbo))
     (declare (local-size :x 1 :y 1 :z 1))
     (let ((node-id (int (x gl-work-group-id))))
       (when (eql node-id 0)
         (node-0))
       (values)))

I get an interesting error.

<VARJO-CONDITIONS:BAD-MAKE-FUNCTION-ARGS
Varjo: Trying to define the function NODE-0 but the following argument are a bit odd:
VARJO.INTERNALS::&UNIFORM
Generally arguments are defined in the format (arg-name arg-type)
e.g. (&uniform :vec3)

As far as I think I know, the first definition of the node-0 would be translatable to valid glsl when the definitions from the model-shader are taken into account. And in the case where the definitions are provided as in the modified definition of node-0, there should be no problem at all. There is the possibility that the ssbo that the node-0 needs access to is defined incorrectly in the function definition of node-0, but I can't see what I'm doing wrong here.

For completeness, let me give definitions for the model struct as well.

(defstruct-g (node :layout :std-430)
         (inputs (:int 10))
         (outputs (:int 10)))

(defstruct-g (model :layout :std-430)
         (nodes (node 1000)))

and the expected glsl as the result from varjo

"// compute-stage
#version 460

struct NODE {
    int INPUTS[(10)];
    int OUTPUTS[(10)];
};
struct MODEL {
    NODE NODES[(1000)];
};

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(std430) buffer _SSBO_DATA
{
    NODE[1000] NODES;
} DATA;

void MODEL_SHADER();
void NODE_0();

void NODE_0()
{
    if ((DATA.NODES[int(gl_WorkGroupID.x)].INPUTS[0] == 10))
    {
        for (int I = 0;(I < 10);(++ I))
        {
            int OUTPUT_SLOT = DATA.NODES[int(gl_WorkGroupID.x)].OUTPUTS[I];
            int OUTPUT_NODE = int(trunc(float((OUTPUT_SLOT / 10))));
            DATA.NODES[OUTPUT_NODE].INPUTS[int(mod(float(OUTPUT_SLOT),float(10)))] = 30;
        }
    }
    return;
}

void MODEL_SHADER()
{
    int NODE_ID = int(gl_WorkGroupID.x);
    if ((NODE_ID == 0))
    {
        NODE_0();
    }
    return;
}

void main()
{
    MODEL_SHADER();
    return;
}

"
ghost commented 6 years ago

Defining node-0 inside of labels within the main function yields the correct code. However, defining the nodes this way forces recompilation of every node even when only one changes as the whole of the main function needs to be recompiled.

cbaggers commented 6 years ago

Recent changes in master have broken this feature. I'll fix this now.

The original node-0 function would need to take the data variable as an argument. Uniforms do not propagate to other functions automatically.

cbaggers commented 6 years ago

I have fixed the bug I mentioned an hour ago.


Next I compiled this code

(uiop:define-package :moo
    (:use :cl :cepl :vari))

(in-package :moo)

(defstruct-g my-data
  (foo (:int 1000)))

(defun-g a-helper ((x my-data))
  (let ((foo (my-data-foo x)))
    (aref foo 0)))

(defun-g my-stage (&uniform (y my-data :ssbo))
  (declare (local-size :x 1 :y 1 :z 1))
  (a-helper y) ;; ← here we pass the ssbo to the function
  (values))

(defpipeline-g some-compute-dodad ()
  :compute my-stage)

and then called (map-g #'some-compute-dodad nil) to make sure it built the pipeline and then ran (pull-g #'some-compute-dodad) and got:

("// compute-stage
#version 450

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(std140) buffer _SSBO_Y
{
    int[1000] FOO;
} Y;

int A_HELPER0();

int A_HELPER0()
{
    int[1000] FOO0 = Y.FOO;
    return FOO0[0];
}

void main()
{
    A_HELPER0(); 
    return;
}
")

Notice that Varjo knows that the SSBO cant be passed like a value in GLSL so it makes a special version of your function that doesnt take the argument.

ghost commented 6 years ago

So that is how it's supposed to work. I was looking at some examples in regular glsl and couldn't figure out how to tell what I meant to varjo. Add to that a little bug and I got confused. Thank you.

cbaggers commented 6 years ago

@tsili you're doing great work. thanks for all the input