Skip to content

Hendekagon/gamma-driver

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gamma-driver

Join the chat at https://gitter.im/kovasb/gamma

The WebGL API is a toxic brew of 9-argument functions and hidden mutable state. Gamma-Driver presents a simple model of the fundamental GL machine that can be easily manipulated via Clojurescript.

Gamma-driver is pre-release & under development.

Rationale

GL APIs, including WebGL, thwart the effective application of high-level language capabilities. This is due to 1) performance requirements that discourage any use of abstraction, and 2) The GPU state machine makes it difficult to design modular abstractions, because of the order-sensitivity of the state machine.

In practice, GL drivers ("engines") tend towards monoliths that painstakingly ensure that the execution order of their abstractions produces the right sequence of instructions. They are difficult to understand or modify, and its impossible to have interoperability between higher-level graphics software targeting different drivers.

Gamma-Driver decouples higher-level abstractions from drivers via data-oriented command lists. Abstractions, making full use of language facilities, create these command lists. Drivers are then free to interpret the command lists, including analysis and optimizations that would be impossible if the user abstractions were directly side-effecting into the GPU.

Design

The fundamental construct in GD is the command list, which is a data-oriented (nested) sequence of commands. These commands can be thought of as an intermediate representation (IR) for a program which will drive the GPU. They are somewhat higher-level than the WebGL API itself, but the translation is simple.

  (def commands
    (let [shader (shader/compile (example-shader))
          ab (gd/arraybuffer)
          attribute (assoc pos :shader (:id shader))]
      [(gd/current-shader shader)
       (gd/bind-attribute attribute ab)
       (gd/bind-arraybuffer ab pos-input)
       (gd/bind-framebuffer nil)
       (gd/draw-arrays start-input count-input)]))

Given a command list, we construct a driver by feeding it a command list and a WebGL context, and then exec! the driver.

(def driver
    (driver/driver
      commands
      {:gl (get-context "gl-canvas")}))
      
(driver/exec! driver {})      

Characterstics of the command list

The command list never directly talks about initialization or allocation of WebGL objects. WebGL objects are referred to symbolically, and are given unique ids at construction time:

(gd/arraybuffer)
--> {:tag :gamma.webgl.api/arraybuffer :id 27}

Usage of WebGL objects within commands will be detected by the driver and cause them to be allocated and initialized. Gamma-Driver assumes that for a given command list, the set of WebGL objects can be statically determined from the command list. If the set of objects must change, this should be dealt with higher-level machinery whose components meet this restriction.

Command lists generated by user code should make no attempt at optimizing state changes, because they will not compose. They should explicitly set all the state that they need. The driver then can perform global analysis and strip out unnecessary state changes.

Variables

The commands can contain variables, called inputs in GD. Inputs are given a unique id at construction time, and are bound by passing a hashmap as the second argument to driver/exec!.

  (def pos-input (gd/input))
  (def start-input (gd/input))
  (def count-input (gd/input))

  (def commands
    (let [shader (shader/compile (example-shader))
          ab (gd/arraybuffer)
          attribute (assoc pos :shader (:id shader))]
      [(gd/current-shader shader)
       (gd/bind-attribute attribute ab)
       (gd/bind-arraybuffer ab pos-input)
       (gd/bind-framebuffer nil)
       (gd/draw-arrays start-input count-input)]))

  (def driver
    (driver/driver
      commands
      {:gl (get-context "gl-canvas")}))

  (driver/exec!
    driver
    {pos-input    (->float32 [0 0 0 1 1 0])
     start-input 0
     count-input 3})

Abstracting over command lists

Drivers consume data-oriented command lists. This places minimal restrictions on how they are generated. Nevertheless, Gamma-Driver ships with a library of functions that abstract over command lists for common GL programming patterns, and their implemention strategy can serve as a guide for others seeking custom functionality.

The abstraction provided in GD abstracts over both the command list, and the inputs contained in the command list.

(let [r (r/shader-draw (example-shader))
        driver (driver/driver
                 (:commands r)
                 {:gl (get-context "gl-canvas")})]
    (driver/exec!
      driver
      (driver/assoc-inputs
        (:inputs r)
        {:shader {pos (->float32 [0 0 1 0 0 1])}
         :draw   {:start 0 :count 3}})))

About

GL state managment made simple

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Clojure 99.7%
  • HTML 0.3%