cbaggers / varjo

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

Intentional variable capture in gpu-macros #171

Closed ghost closed 6 years ago

ghost commented 6 years ago

As I'm writing my genetic programming system, I needed to specify a language for it to use. This language is defined as gpu-macros limiting the system's access to the underlying structure through intentional variable capture. As I started getting things working and cleaned up by separating the language definition to its own file and other helpers elsewhere, I got into problems.

Separating these definitions resulted in varjo looking for the captured variables from the language definition file as a variable in stead of the location where the macro was used.

For example, I have the macro (self) defined in package GP.LANGUAGE as

(defmacro-g self ()
  `(aref (model-nodes data)
         (int (x gl-work-group-id))))

The purpose of this macro is to only allow the nodes in the system to do stuff with their own data in the model represented as the captured variable data. This variable is defined as an uniform in the shader in package GP.SHADERS.

(defun-g read-tester (&uniform (data model :ssbo))
  (declare (local-size :x 1 :y 1 :z 1))
  (setf (aref (node-outputs (self))
              0)
        (read-input 0))
  (values))

However, when compiling this shader, varjo gives the following error Varjo: Symbol GP.LANGUAGE::DATA is unidentified. [Condition of type VARJO-CONDITIONS:SYMBOL-UNIDENTIFIED]

It is true that this symbol doesn't exist in the package GP.LANGUAGE and it shouldn't as it is an uniform in the shader in the package GP.SHADERS.

ghost commented 6 years ago

It seems to work the same way with regular lisp macros. Apparently I just need to read more about how to use them properly.

cbaggers commented 6 years ago

Capture seems to work for me. Here is my test:

(defstruct-g model
  (nodes (:int 20)))

(defmacro-g self ()
  `(aref (model-nodes data)
         (int (x gl-work-group-id))))

(defun-g read-tester (&uniform (data model :ssbo))
  (declare (local-size :x 1 :y 1 :z 1))
  (self)
  (values))

(defpipeline-g foo-compute ()
  :compute read-tester)
EXAMPLES> (pull-g #'foo-compute)
("// compute-stage
#version 450

struct MODEL {
    int NODES[(20)];
};

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

layout(std140) buffer _SSBO_DATA
{
    int[20] NODES;
} DATA;

void main()
{
    DATA.NODES[int(gl_WorkGroupID.x)];
    return;
}

")
ghost commented 6 years ago

Yes it works perfectly as long as the macro and the gpu function are in the same package. Where I deed to figure things out is when those two are in separate packages.

Here is an example in regular lisp.

(defpackage lib
    (:use :cl :cl-user)
    (:export :foo))
(in-package :lib)

(defmacro foo ()
       '(print bar))

(defpackage test
    (:use :cl :cl-user :lib))
(in-package :test)

(defun baz ()
    (let ((bar 12))
      (foo)))

(baz)

The variable LIB::BAR is unbound.
   [Condition of type UNBOUND-VARIABLE]

And that just goes to show how little I know.

ghost commented 6 years ago

Do you know where I could read about, how I could tell that the variable bar in this last case is to be looked from the package of the enclosing environment, which in this case is test and not lib?

cbaggers commented 6 years ago

You could try adding (:intern :bar) to your lib package and :bar to the exports from lib package. Then lib:bar will be available in foo. I recommend reading more into symbols & packages in lisp and maybe to look into 'on lisp' which has a lot of info about macros.

good luck!