608617aeb34a — Leonard Ritter a month ago
* initial work subjective filtering
A => lib/tukan/ResourceGroup.sc +89 -0
@@ 0,0 1,89 @@ 
+
+using import FunctionChain
+using import tukan.gl
+
+# prototype for initialization from usage
+
+type ResourceGroup
+    let size = 0:usize
+    let setup = none
+    let teardown = none
+
+    inline __typecall (cls ...)
+        static-if (cls == this-type)
+            type (do ...) < cls :: @u8
+        else
+            let self = (bitcast (malloc-array u8 cls.size) cls)
+            cls.setup self
+            self
+
+    spice static (self T ...)
+        print "static" T
+        constructor := ('getarg ... 0)
+        destructor := ('getarg ... 1)
+        cls := ('typeof self)
+        offset := ('@ cls 'size) as usize
+        setup := ('@ cls 'setup)
+        teardown := ('@ cls 'teardown)
+        let setup teardown =
+            if ('none? setup)
+                str := (tostring cls)
+                setupstr := str .. "-setup"
+                teardownstr := str .. "-teardown"
+                let setup teardown =
+                    sc_prove `(FunctionChain setupstr)
+                    sc_prove `(FunctionChain teardownstr)
+                'set-symbol cls 'setup setup
+                'set-symbol cls 'teardown teardown
+                _ setup teardown
+            else
+                _ setup teardown
+        T as:= type
+        let rsize = ('sizeof T)
+        'set-symbol cls 'size (offset + rsize)
+        let ptrT = ('mutable (pointer.type T))
+        sc_prove
+            spice-quote
+                'append setup
+                    inline (self)
+                        let field = (ptrtoref (bitcast (getelementptr self offset) ptrT))
+                        spice-unquote
+                            if ('none? constructor)
+                                `(assign (T) field)
+                            else
+                                `(assign (constructor) field)
+                        ;
+                'append teardown
+                    inline (self)
+                        let field = (ptrtoref (bitcast (getelementptr self offset) ptrT))
+                        spice-unquote
+                            if ('none? destructor) ()
+                            else `(destructor field)
+                        __drop field
+                        ;
+
+        spice-quote
+            ptrtoref (bitcast (getelementptr self offset) ptrT)
+
+    inline __drop (self)
+        let cls = (typeof self)
+        cls.teardown self
+        free self
+        ;
+
+run-stage;
+
+type+ ResourceGroup
+    inline program (self ...)
+        'static self GL.Program
+            inline ()
+                call
+                    attach-shaders (GL.Program)
+                        ...
+
+    inline compute-program (self func ...)
+        program self (compute = func)
+
+do
+    let ResourceGroup
+    locals;

          
M lib/tukan/VertexPainter.sc +13 -48
@@ 9,7 9,6 @@ using import Rc
 using import .gl
 using import .rotation
 using import .projection
-using import .stage
 
 let shaders =
     do

          
@@ 121,20 120,21 @@ struct VertexPainter
             origin = (self.state.transform * v)
             color = (self.state.active_color * light_factor)
 
-    fn draw (self mode)
+    fn draw (self)
         if (not self.vertex_count)
             return;
-        GL.UseProgram self.program
-        GL.BindVertexArray self.vertex_array
-        GL.DrawArrays (mode as u32) 0 (self.vertex_count as i32)
-        reset self
-
-    fn flush (self)
-        draw self
+        let mode =
             switch self.active_primitive
             case Primitive.Wireframe GL.LINES
             pass Primitive.Solid
             default GL.TRIANGLES
+        GL.UseProgram self.program
+        GL.BindVertexArray self.vertex_array
+        GL.DrawArrays (mode as u32) 0 (self.vertex_count as i32)
+
+    fn flush (self)
+        draw self
+        reset self
 
     fn wireframe? (self)
         self.active_primitive == Primitive.Wireframe

          
@@ 335,6 335,7 @@ struct VertexPainter
                 let o2 = (deref (prim @ i))
                 triangle self o0 o1 o2
                 _ o2
+        'clear self.prims
 
     fn stroke (self)
         let wf? = (wireframe? self)

          
@@ 361,6 362,7 @@ struct VertexPainter
                 else
                     vertices self o1 o2
                 _ o2
+        'clear self.prims
         wireframe self wf?
 
     fn cuboid-dynamic-mask (self mask vertices...)

          
@@ 399,7 401,7 @@ struct VertexPainter
             cuboid-dynamic-mask self mask vertices...
 
     inline cuboid (self vertices...)
-        cuboid-mask 0xff:u8 vertices...
+        cuboid-mask self 0xff:u8 vertices...
 
     inline... AABB (self, c0 : vec3, c1 : vec3, mask : u8 = 0xff:u8)
         let x0 y0 z0 = (unpack c0)

          
@@ 478,45 480,8 @@ struct VertexPainter
 
     unlet cuboid-dynamic-mask
 
-let RcVertexPainter = (Rc VertexPainter)
-global g_vertexpainter : (Option RcVertexPainter)
-
-@@ 'on on-init
-fn ()
-    g_vertexpainter = (RcVertexPainter)
-    ;
-
-@@ 'on on-shutdown
-fn ()
-    g_vertexpainter = none
-    ;
-
-fn vertexpainter ()
-    Rc.clone
-        'force-unwrap g_vertexpainter
-
-struct VertexPainterSession
-    vp : RcVertexPainter
-
-    inline __typecall (cls)
-        let self = (super-type.__typecall cls (vertexpainter))
-        let vp = self.vp
-        if ((Rc.strong-count vp) == 2)
-            'clear vp
-        self
-
-    inline __drop (self)
-        let vp = self.vp
-        let depth = (Rc.strong-count vp)
-        if (depth == 2)
-            'flush vp
-        super-type.__drop self
-
-    inline __methodcall (name self ...)
-        name self.vp ...
-
 do
-    let VertexPainter = VertexPainterSession
+    let VertexPainter
 
     locals;
 

          
A => testing/test_resourcegroup.sc +37 -0
@@ 0,0 1,37 @@ 
+
+
+import ..lib.tukan.use
+using import tukan.ResourceGroup
+
+let CustomResourceGroup = (ResourceGroup "CustomResourceGroup")
+
+fn main (rg)
+    # somewhere in the function we realize we need a resource, so we
+        allocate it right where we need it; this will expand the resource group
+        with a new entry, and extend its constructor and destructor with ours.
+    let arr =
+        'static rg (mutable @i32)
+            inline ()
+                print "allocating"
+                malloc-array i32 6
+            inline (ptr)
+                print "freeing"
+                free ptr
+    for i in (range 6)
+        arr @ i = i
+    for i in (range 6)
+        print
+            arr @ i
+    ;
+
+# causes the allocations to be registered with our type
+static-typify main CustomResourceGroup
+
+# instantiation: from here on, our type can't be extended further
+let crg = (CustomResourceGroup)
+# has been pre-typed, so no additional member can be appended
+main crg
+# invoke destructor, which also invokes all custom destructors
+drop crg
+
+;

          
A => testing/test_subjective.sc +234 -0
@@ 0,0 1,234 @@ 
+
+using import glm
+using import Capture
+using import Option
+using import itertools
+
+import ..lib.tukan.use
+using import tukan.VertexPainter
+using import tukan.GLMain
+using import tukan.gl
+using import tukan.color
+using import tukan.normal
+using import tukan.isosurface
+using import tukan.ResourceGroup
+using import .testfragment
+
+let RG = (ResourceGroup "RG")
+
+global rg : (Option RG)
+
+inline decompose_normal_Linf (p)
+    r := (max (unpack (abs p)))
+    _ (p / r) r
+
+inline decompose_normal_L2 (p)
+    r := (length p)
+    _ (p / r) r
+
+inline decompose_normal_L1 (p)
+    r := ((+ (unpack (abs p))) / 3)
+    _ (p / r) r
+
+global rcp_c = 1.0
+
+#let decompose_normal = decompose_normal_Linf
+#let decompose_normal = decompose_normal_L2
+let decompose_normal = decompose_normal_L1
+
+inline invert (p)
+    #let pn r = (decompose_normal_L2 p)
+    #let pn r = (decompose_normal_Linf p)
+    let pn r = (decompose_normal_L1 p)
+    pn / r
+
+inline expand_rcp (p)
+    let pn r = (decompose_normal p)
+    #r := (min r 1.0)
+    pn * (r / (1.0 - (abs r)))
+
+inline contract_rcp (p)
+    let pn r = (decompose_normal p)
+    pn * (r / (1.0 + (abs r)))
+
+inline expand_exp (p)
+    let pn r = (decompose_normal p)
+    pn * (-(log2 (1.0 - r)))
+
+inline contract_exp (p)
+    let pn r = (decompose_normal p)
+    pn * (1.0 - (exp2 -r))
+
+#let expand contract = expand_rcp contract_rcp
+let expand contract = expand_exp contract_exp
+
+fn persp2 (p)
+    #p := (invert p)
+    p := p * rcp_c
+    p := (contract p)
+
+fn unpack_normal_snorm (encN)
+    let z = (1.0 - (abs encN.x) - (abs encN.y))
+    normalize
+        vec3
+            ? (z >= 0.0) encN.xy (oct_wrap encN.xy)
+            z
+
+fn persp1 (p)
+    static-if true
+        r := (p.z * 0.5 + 0.5)
+        r := (exp2 (8.0 * r))
+        #r := 1.0 / r
+        #r := (r / (1.0 - (abs r))) #+ 1.0
+        #r := (-(log2 (1.0 - r)))
+        #r := (/ r)
+        (unpack_normal_snorm p.xy) * r
+    else
+        p := (expand p)
+        p := p / rcp_c
+        #p := (invert p)
+
+
+fn map (p)
+    L := (length p)
+    cu := L - 10.0
+
+    c := 10.0
+    p := (mod (p + 0.5 * c) c) - 0.5 * c
+    max
+        (length p) - 3.0 #+ (L * 0.05)
+        -cu
+
+# all simplices within the cube have edge 0b000 - 0b111
+# the other two points are, counterclockwise
+    0b001 0b011 0b010 0b110 0b100 0b101
+inline simplex (n)
+    # returns indices of nth simplex (0..5)
+    q := ((ivec2 0b101100110010011001 0b100110010011001101) >> (n * 3)) & 7
+    ivec4 q 0b000 0b111
+
+fn draw (size pg-test frame)
+    let rg =
+        'force-unwrap rg
+    from (methodsof rg) let static
+    let painter = (static VertexPainter)
+    GL.ClearColor 0 0 0.5 1
+    GL.Enable GL.CULL_FACE
+    GL.Clear
+        |
+            GL.COLOR_BUFFER_BIT
+            GL.DEPTH_BUFFER_BIT
+            GL.STENCIL_BUFFER_BIT
+    GL.LineWidth 2.0
+
+    'setup-depth painter
+    'clear painter
+
+    from (methodsof painter) let screen color screen rotate AABB \
+        translate quad perspective scale light flush wireframe moveto lineto \
+        closepath fill stroke transform cuboid triangle
+    # setup view
+    light 0.25 -0.5 1
+    perspective size
+    transform cpu_shglobals.view
+    N := 64
+    d := (vec3 1) / N
+
+    t := cpu_shglobals.time * 0.1
+
+    rcp_c =
+        1.0
+        #exp2
+            mix
+                log2 64.0
+                log2 1024.0
+                (sin cpu_shglobals.time) * 0.5 + 0.5
+
+    fn tf (p)
+        static-if false
+            p
+        else
+            p := (persp1 p)
+            p := p / rcp_c
+            #p * 8.0
+            p * 4.0
+
+    #wireframe;
+    for x y z in (dim N N N)
+        let bias = 0.5
+        #let bias = 0.0
+        c := ((vec3 x y z) + bias) / N
+        pc := c * 2 - 1
+        #r := c.z * 0.8
+        #r := 0.5
+        r := 1.0
+        p0 := pc - d * r
+        p1 := pc + d * r
+        color c
+        #color
+            hue2rgb
+                (max (unpack (abs pc))) #+ t
+        #color c
+        local verts =
+            arrayof vec3
+                tf (vec3 p0.x p0.y p0.z)
+                tf (vec3 p0.x p0.y p1.z)
+                tf (vec3 p0.x p1.y p0.z)
+                tf (vec3 p0.x p1.y p1.z)
+                tf (vec3 p1.x p0.y p0.z)
+                tf (vec3 p1.x p0.y p1.z)
+                tf (vec3 p1.x p1.y p0.z)
+                tf (vec3 p1.x p1.y p1.z)
+        static-if false
+            cuboid (unpack verts)
+        elseif true
+            for i in (range 6)
+                let is = (simplex i)
+                local d : vec4
+                local p : (array vec3 4)
+                for k in (range 4)
+                    q := verts @ (is @ k)
+                    p @ k = q
+                    d @ k = (map q)
+                let c i = (tetfaces d)
+                if (c == 1)
+                    triangle
+                        mix (p @ i.x) (p @ i.y) (tetlerp (d @ i.x) (d @ i.y))
+                        mix (p @ i.x) (p @ i.w) (tetlerp (d @ i.x) (d @ i.w))
+                        mix (p @ i.x) (p @ i.z) (tetlerp (d @ i.x) (d @ i.z))
+                elseif (c == 2)
+                    quad
+                        mix (p @ i.y) (p @ i.z) (tetlerp (d @ i.y) (d @ i.z))
+                        mix (p @ i.y) (p @ i.w) (tetlerp (d @ i.y) (d @ i.w))
+                        mix (p @ i.x) (p @ i.w) (tetlerp (d @ i.x) (d @ i.w))
+                        mix (p @ i.x) (p @ i.z) (tetlerp (d @ i.x) (d @ i.z))
+        else
+            moveto (tf p0)
+            lineto (tf (vec3 p1.x p0.y p0.z))
+            moveto (tf p0)
+            lineto (tf (vec3 p0.x p1.y p0.z))
+            moveto (tf p0)
+            lineto (tf (vec3 p0.x p0.y p1.z))
+            stroke;
+    'draw painter
+
+render-fragment-shader
+    inline ()
+        let draw =
+            static-typify draw ivec2 GL.Program i32
+        rg = (RG)
+
+        _ draw none
+
+    #debug = true
+    #size = (ivec2 512)
+    title = "Subjective Filtering"
+
+#'run glmain
+    capture (event) {(view glmain)}
+
+    capture () {(view glmain)}
+        draw 0.0 (deref glmain.size) glmain
+
+;
+

          
M testing/test_tmt6.sc +126 -103
@@ 20,6 20,7 @@ using import glm
 using import glsl
 using import Array
 using import Box
+using import Option
 using import struct
 using import tukan.gl
 using import tukan.bitmap

          
@@ 38,8 39,11 @@ using import tukan.projection
 using import tukan.derivative
 using import tukan.isosurface
 using import tukan.hash
+using import tukan.ResourceGroup
 using import .testfragment
 
+let RG = (ResourceGroup "RG")
+
 let USE_PACKING =
     #true
     false

          
@@ 777,12 781,6 @@ fn voxelize ()
     if (index < buf-cells-in-info.count)
         subdivide-cell (deref (buf-cells-in.entries @ index))
 
-fn simplify ()
-    local_size THREADSIZE 1 1
-    let index = (deref gl_GlobalInvocationID.x)
-    if (index < buf-cells-in-info.count)
-        simplify-cell (deref (buf-cells-in.entries @ index))
-
 fn supershader ()
     local_size THREADSIZE 1 1
     let mode = (deref u-program)

          
@@ 807,7 805,21 @@ fn rasterize-vert ()
     geom_data.out = data
     ;
 
-inline transform-rotate (p)
+inline transform-dist (p)
+    (mat3 shglobals.view) * p
+
+inline transform-invert-dist (p)
+    (mat3 shglobals.view-inverse) * p
+
+inline transform-invert-pos (p)
+    v := (deref shglobals.view-inverse) * (vec4 p 1)
+    v.xyz
+
+inline transform-pos (p)
+    v := (deref shglobals.view) * (vec4 p 1)
+    v.xyz
+
+#inline transform-rotate (p)
     let a = ((deref shglobals.time) * 0.2)
     let c s = (cos a) (sin a)
     vec3

          
@@ 815,7 827,7 @@ inline transform-rotate (p)
         p.y
         s * p.x + c * p.z
 
-inline transform-invert-rotate (p)
+#inline transform-invert-rotate (p)
     let a = ((deref shglobals.time) * -0.2)
     let c s = (cos a) (sin a)
     vec3

          
@@ 823,7 835,7 @@ inline transform-invert-rotate (p)
         p.y
         s * p.x + c * p.z
 
-inline transform-pos (p)
+#inline transform-pos (p)
     v := (transform-rotate p)
     vec3 v.xy (v.z + 1.0)
 

          
@@ 865,7 877,7 @@ fn rasterize-geom ()
         coord := (transform-pos coord)
 
         ray_dir.out =
-            transform-invert-rotate coord
+            transform-invert-dist coord
 
         let proj =
             calc-projection;

          
@@ 1147,7 1159,7 @@ fn rasterize-frag ()
 
     coord := coord + p * d
     p := (transform-pos coord)
-    n := (transform-rotate n)
+    n := (transform-dist n)
 
     gl_FragDepth = (0.1 / p.z)
 

          
@@ 1390,6 1402,9 @@ fn shader (uv)
     #mixdown uv
     visualize-buffer uv
 
+let NUM_CMD_BUFFERS = 1
+let NUM_BUFFERS = 2
+
 inline main ()
 
     # pass overview:

          
@@ 1415,94 1430,34 @@ inline main ()
         in: indirect draw call argument
         out: rasterized voxel cubes
 
-    let NUM_CMD_BUFFERS = 1
-
-    global compute_cmd_buffers = (GL.Buffer)
-    setup-ssbo compute_cmd_buffers buf-compute-cmd NUM_CMD_BUFFERS
-
-    global draw_cmd_buffers = (GL.Buffer)
-    setup-ssbo draw_cmd_buffers buf-draw-cmd NUM_CMD_BUFFERS
-
-    let NUM_BUFFERS = 2
-
-    global cell_info_buffers =
-        arrayof GL.uint
-            GL.Buffer;
-            GL.Buffer;
+    global rg : (Option RG)
 
-    global cell_buffers =
-        arrayof GL.uint
-            GL.Buffer;
-            GL.Buffer;
-    let cell_info_buffer_sz = (sizeof u32)
-    let cell_buffer_sz = ((sizeof u32) * MAX_VOXELS)
-    for i in (range (NUM_BUFFERS as u32))
-        let buf = (cell_info_buffers @ i)
-        GL.NamedBufferData buf (i32 cell_info_buffer_sz) null GL.STREAM_COPY
-        GL.BindBufferRange GL.SHADER_STORAGE_BUFFER (BINDING_BUF_CELLS_IN_INFO + i)
-            \ buf 0:i64 (i64 cell_info_buffer_sz)
-        let buf = (cell_buffers @ i)
-        GL.NamedBufferData buf (i32 cell_buffer_sz) null GL.STREAM_COPY
-        GL.BindBufferRange GL.SHADER_STORAGE_BUFFER (BINDING_BUF_CELLS_IN + i)
-            \ buf 0:i64 (i64 cell_buffer_sz)
-
-    global vertex_buffer = (GL.Buffer)
-    let vertex_buffer_sz = ((sizeof CubeData) * MAX_VOXELS)
-    GL.NamedBufferData vertex_buffer (i32 vertex_buffer_sz) null GL.STREAM_COPY
-
-    global fb-scene-color = (GL.Texture GL.TEXTURE_2D)
-    'setup fb-scene-color
-        size = (ivec2 2048 2048)
-        format = GL.RGBA32F
-    do
-        let h = 2048
-        GL.ClearTexImage fb-scene-color 0 GL.RGBA GL.FLOAT null
+    fn per-frame-setup (size pg-test frame)
+        let rg =
+            'force-unwrap rg
+        from (methodsof rg) let static program compute-program
 
-    global rb-scene-depth = (GL.Renderbuffer)
-    setup-renderbuffer rb-scene-depth 2048 2048
-        format = GL.DEPTH_COMPONENT
-    global fb-scene = (GL.Framebuffer)
-    setup-framebuffer fb-scene
-        color = fb-scene-color
-        rb-depth = rb-scene-depth
-
-    global vao-empty = (GL.VertexArray)
-
-    global pg-rasterize = (GL.Program)
-    call
-        attach-shaders (deref pg-rasterize)
-            vertex = rasterize-vert
-            geometry = rasterize-geom
-            fragment = rasterize-frag
-            #debug = true
-
-    global pg-supershader = (GL.Program)
-    call
-        attach-shaders (deref pg-supershader)
-            compute = supershader
-
-    global pg-simplify = (GL.Program)
-    call
-        attach-shaders (deref pg-simplify)
-            compute = simplify
-            #debug = true
-
-    global pg-setup-compute = (GL.Program)
-    call
-        attach-shaders (deref pg-setup-compute)
-            compute = setup-compute-command
-            #debug = true
-
-    global pg-setup-draw = (GL.Program)
-    call
-        attach-shaders (deref pg-setup-draw)
-            compute = setup-draw-arrays-command
-            #debug = true
-
-    inline per-frame-setup (size pg-test frame)
-
-        GL.BindTextureUnit 0 fb-scene-color
-        GL.Uniform smp-screen 0
+        struct Cells
+            cell_info : (array GL.Buffer 2)
+            cell : (array GL.Buffer 2)
+        let cell_info_buffer_sz = (sizeof u32)
+        let cell_buffer_sz = ((sizeof u32) * MAX_VOXELS)
+        vvv bind cells
+        static Cells
+            inline ()
+                local cells : Cells
+                for i in (range (NUM_BUFFERS as u32))
+                    let buf = (cells.cell_info @ i)
+                    GL.NamedBufferData buf (i32 cell_info_buffer_sz) null GL.STREAM_COPY
+                    GL.BindBufferRange GL.SHADER_STORAGE_BUFFER (BINDING_BUF_CELLS_IN_INFO + i)
+                        \ buf 0:i64 (i64 cell_info_buffer_sz)
+                    let buf = (cells.cell @ i)
+                    GL.NamedBufferData buf (i32 cell_buffer_sz) null GL.STREAM_COPY
+                    GL.BindBufferRange GL.SHADER_STORAGE_BUFFER (BINDING_BUF_CELLS_IN + i)
+                        \ buf 0:i64 (i64 cell_buffer_sz)
+                cells
+        let cell_info_buffers = cells.cell_info
+        let cell_buffers = cells.cell
 
         inline bind-read-buffer (i)
             GL.BindBufferRange GL.SHADER_STORAGE_BUFFER

          
@@ 1536,8 1491,8 @@ inline main ()
                 cell_info_buffers @ i
                 \ 0:i64 (i64 cell_info_buffer_sz)
 
-        GL.UseProgram pg-supershader
-        GL.BindBuffer GL.DISPATCH_INDIRECT_BUFFER compute_cmd_buffers
+        let pg-setup-compute = (compute-program setup-compute-command)
+        let pg-supershader = (compute-program supershader)
 
         local idx = 0
         local level = 0

          
@@ 1563,12 1518,20 @@ inline main ()
             GL.DispatchCompute ((((1 << level) ** 3) // THREADSIZE) as u32) 1 1
             GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
 
+        let compute_cmd_buffers =
+            static GL.Buffer
+                inline ()
+                    let buf = (GL.Buffer)
+                    setup-ssbo buf buf-compute-cmd NUM_CMD_BUFFERS
+                    buf
+
         inline refine-voxels ()
             let i0 = (deref idx)
             idx = ((idx + 1) & 1)
             let i1 = (deref idx)
             level = level + 1
             let level = (deref level)
+
             GL.UseProgram pg-setup-compute
             GL.Uniform u-divisor THREADSIZE
             bind-read-buffer i0

          
@@ 1584,6 1547,14 @@ inline main ()
             GL.DispatchComputeIndirect 0
             GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
 
+        let vertex_buffer_sz = ((sizeof CubeData) * MAX_VOXELS)
+        let vertex_buffer =
+            static GL.Buffer
+                inline ()
+                    let buf = (GL.Buffer)
+                    GL.NamedBufferData buf (i32 vertex_buffer_sz) null GL.STREAM_COPY
+                    buf
+
         inline finalize-voxels ()
             let i0 = (deref idx)
             idx = ((idx + 1) & 1)

          
@@ 1598,7 1569,14 @@ inline main ()
             GL.DispatchCompute 1 1 1
             GL.MemoryBarrier GL.COMMAND_BARRIER_BIT
 
-            GL.UseProgram pg-simplify
+            @@ compute-program
+            fn simplify ()
+                local_size THREADSIZE 1 1
+                let index = (deref gl_GlobalInvocationID.x)
+                if (index < buf-cells-in-info.count)
+                    simplify-cell (deref (buf-cells-in.entries @ index))
+            GL.UseProgram simplify
+
             #GL.Uniform u-program ProgramSimplify
             GL.Uniform u-level level
             bind-read-buffer i0

          
@@ 1608,11 1586,41 @@ inline main ()
             GL.DispatchComputeIndirect 0
             GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
 
+        struct FB
+            color = (GL.Texture GL.TEXTURE_2D)
+            depth : GL.Renderbuffer
+            scene : GL.Framebuffer
+        vvv bind fb
+        static FB
+            inline ()
+                let h = 2048
+                local fb : FB
+                'setup fb.color
+                    size = (ivec2 h h)
+                    format = GL.RGBA32F
+                do
+                    GL.ClearTexImage fb.color 0 GL.RGBA GL.FLOAT null
+                setup-renderbuffer fb.depth h h
+                    format = GL.DEPTH_COMPONENT
+                setup-framebuffer fb.scene
+                    color = fb.color
+                    rb-depth = fb.depth
+                fb
+        let fb-scene fb-scene-color = fb.scene fb.color
+
         inline draw-surface ()
             #print-in-count (deref idx)
             GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+            let draw_cmd_buffers =
+                static GL.Buffer
+                    inline ()
+                        let buf = (GL.Buffer)
+                        setup-ssbo buf buf-draw-cmd NUM_CMD_BUFFERS
+                        buf
             GL.BindBuffer GL.DRAW_INDIRECT_BUFFER draw_cmd_buffers
-            GL.UseProgram pg-setup-draw
+
+            GL.UseProgram
+                compute-program setup-draw-arrays-command
             bind-read-buffer (deref idx)
             bind-ssbo draw_cmd_buffers buf-draw-cmd none 0
             GL.DispatchCompute 1 1 1

          
@@ 1633,12 1641,18 @@ inline main ()
                         GL.DEPTH_BUFFER_BIT
                         GL.STENCIL_BUFFER_BIT
 
-                GL.UseProgram pg-rasterize
+                GL.UseProgram
+                    program
+                        vertex = rasterize-vert
+                        geometry = rasterize-geom
+                        fragment = rasterize-frag
+
                 bind-read-buffer (deref idx)
                 GL.BindBufferRange GL.SHADER_STORAGE_BUFFER BINDING_BUF_VERTEX_ATTR1_IN
                         \ vertex_buffer 0:i64 (i64 vertex_buffer_sz)
                 GL.Uniform u-level (deref level)
-                GL.BindVertexArray vao-empty
+                GL.BindVertexArray
+                    static GL.VertexArray
                 GL.DrawArraysIndirect GL.POINTS null
 
                 GL.Disable GL.BLEND

          
@@ 1646,12 1660,21 @@ inline main ()
                 GL.Disable GL.CULL_FACE
                 GL.BindFramebuffer GL.FRAMEBUFFER 0
 
+        # set pg-test uniform
+        GL.Uniform smp-screen 0
+        GL.UseProgram pg-supershader
+        GL.BindBuffer GL.DISPATCH_INDIRECT_BUFFER compute_cmd_buffers
         init-voxels 5
         refine-voxels;
         #refine-voxels;
         #refine-voxels;
         finalize-voxels;
         draw-surface;
+        GL.BindTextureUnit 0 fb-scene-color
+
+    let per-frame-setup =
+        static-typify per-frame-setup ivec2 GL.Program i32
+    rg = (RG)
 
     _ per-frame-setup shader
 

          
M testing/test_vertexpainter.sc +37 -16
@@ 1,24 1,28 @@ 
 
 using import glm
+using import Capture
+using import Option
 
 import ..lib.tukan.use
 using import tukan.VertexPainter
 using import tukan.GLMain
 using import tukan.gl
+using import tukan.ResourceGroup
+using import .testfragment
 
-let glmain =
-    GLMain
-        title = "VertexPainter"
-        resizable = true
+let RG = (ResourceGroup "RG")
+
+global rg : (Option RG)
 
 global t = 0.0
 
-@@ 'on GLMain.on-draw
-fn draw (time size glmain)
-    let painter = (VertexPainter)
+fn draw (size pg-test frame)
+    let rg =
+        'force-unwrap rg
+    from (methodsof rg) let static
+    let painter = (static VertexPainter)
+    'clear painter
     'setup-depth painter
-    GL.BindFramebuffer GL.FRAMEBUFFER 0
-    GL.Viewport 0 0 (i32 size.x) (i32 size.y)
     GL.ClearColor 0 0 0.5 1
     GL.Enable GL.CULL_FACE
     GL.Clear

          
@@ 31,13 35,14 @@ fn draw (time size glmain)
 
     from (methodsof painter) let screen color screen rotate AABB \
         translate quad perspective scale light flush wireframe moveto lineto \
-        closepath fill stroke
+        closepath fill stroke transform
     # setup view
     light 0 0 1
     perspective size
-    translate
+    transform cpu_shglobals.view
+    #translate
         vec3 0 0 3
-    rotate
+    #rotate
         t * pi * 0.1
         vec3 1 0 0
     # setup object

          
@@ 48,8 53,8 @@ fn draw (time size glmain)
     let c0 c1 =
         vec3 -1 -0.5 -0.25
         vec3 1 0.5 0.25
-    switch 0
-    case 0
+    switch 1
+    case 1
         #wireframe;
         color 1 0 0
         AABB c0 c1 0b11

          
@@ 57,7 62,7 @@ fn draw (time size glmain)
         AABB c0 c1 0b1100
         color 0 0 1
         AABB c0 c1 0b110000
-    case 1
+    case 0
         moveto 0 0
         lineto 1 0
         lineto 1.5 0.5

          
@@ 66,8 71,24 @@ fn draw (time size glmain)
         lineto -0.5 0.5
         closepath;
     default;
+    'flush painter
 
-'run glmain
+render-fragment-shader
+    inline ()
+        let draw =
+            static-typify draw ivec2 GL.Program i32
+        rg = (RG)
+
+        _ draw none
+
+    #debug = true
+    #size = (ivec2 512)
+
+#'run glmain
+    capture (event) {(view glmain)}
+
+    capture () {(view glmain)}
+        draw 0.0 (deref glmain.size) glmain
 
 ;
 

          
M testing/testfragment.sc +33 -19
@@ 13,7 13,10 @@ using import tukan.gl
 using import tukan.sdl
 using import tukan.rotation
 
-let BUFFER_COUNT = 3
+let
+    BUFFER_COUNT = 3
+    MOUSE_SPEED_X = 0.001
+    MOUSE_SPEED_Y = 0.001
 
 struct ShaderGlobals plain
     view : mat4

          
@@ 23,6 26,8 @@ struct ShaderGlobals plain
     time : f32
     frame : i32
 
+global cpu_shglobals : ShaderGlobals
+
 uniform shglobals : ShaderGlobals
     binding = 64
 

          
@@ 44,6 49,7 @@ inline render-fragment-shader (func opts
 
     print "t: toggle timer display"
     print "o: toggle orbit mode"
+    print "backspace: reset to origin"
     print ".: toggle time"
     print "escape: toggle mouse capture"
     print "wsadrf: move"

          
@@ 52,6 58,8 @@ inline render-fragment-shader (func opts
 
     let frame-setup shader-func = (func)
 
+    let shader? = (not (none? shader-func))
+
     global timer_accum = 0.0
     global timer_accum_count = 0
     global enable_timer_display = true

          
@@ 104,11 112,12 @@ inline render-fragment-shader (func opts
 
     global pg-test = (GL.Program)
 
-    call
-        attach-shaders (deref pg-test)
-            vertex = test-vertex
-            fragment = test-fragment
-            debug = debug
+    static-if shader?
+        call
+            attach-shaders (deref pg-test)
+                vertex = test-vertex
+                fragment = test-fragment
+                debug = debug
 
     inline render-view (size)
         let buffer-index = (g_frame % BUFFER_COUNT)

          
@@ 140,7 149,8 @@ inline render-fragment-shader (func opts
                 GL.DEPTH_BUFFER_BIT
                 GL.STENCIL_BUFFER_BIT
 
-        GL.UseProgram pg-test
+        static-if shader?
+            GL.UseProgram pg-test
 
         let m m-inv =
             if orbit_mode

          
@@ 157,17 167,17 @@ inline render-fragment-shader (func opts
                     mat4
                         \  1  0  0  0
                         \  0  1  0  0
+                        \  0  0  1  0
                         \  0  0  1  1
-                        \  0  0  0  1
                 let inv-translate-matrix =
                     mat4
                         \  1  0  0  0
                         \  0  1  0  0
-                        \  0  0  1  -1
-                        \  0  0  0  1
+                        \  0  0  1  0
+                        \  0  0  -1  1
                 _
-                    rot-matrix * translate-matrix
-                    inv-translate-matrix * inv-rot-matrix
+                    translate-matrix * rot-matrix
+                    inv-rot-matrix * inv-translate-matrix
             else
                 cam-versor := (compute-cam-versor)
 

          
@@ 224,7 234,7 @@ inline render-fragment-shader (func opts
                     inv-scale-matrix * (transpose ((mat4 inv-rot-matrix) * inv-translate-matrix))
 
 
-        local data =
+        cpu_shglobals =
             ShaderGlobals
                 view = m
                 view-inverse = m-inv

          
@@ 232,15 242,16 @@ inline render-fragment-shader (func opts
                 time = g_time
                 frame = (deref g_frame)
                 size = size
-        bind-ubo (deref globals-data) shglobals &data buffer-index
+        bind-ubo (deref globals-data) shglobals &cpu_shglobals buffer-index
 
         frame-setup size pg-test (deref g_frame)
 
         g_frame = g_frame + 1
         if advance_time
             g_time += (/ 60.0)
-        GL.UseProgram pg-test
-        'draw screen
+        static-if shader?
+            GL.UseProgram pg-test
+            'draw screen
         GL.UseProgram 0
         GL.EndQuery GL.TIME_ELAPSED
 

          
@@ 248,8 259,8 @@ inline render-fragment-shader (func opts
         switch (event.type as (typeof SDL_KEYDOWN))
         case SDL_MOUSEMOTION
             if capturing
-                cam_yaw += (ftoangle ((event.motion.xrel as f32) * pi * 0.0005))
-                cam_pitch += (ftoangle ((event.motion.yrel as f32) * pi * 0.0005))
+                cam_yaw += (ftoangle ((event.motion.xrel as f32) * pi * MOUSE_SPEED_X))
+                cam_pitch += (ftoangle ((event.motion.yrel as f32) * pi * MOUSE_SPEED_Y))
                 cam_pitch = (clamp cam_pitch (ftoangle (pi * -0.5)) (ftoangle (pi * 0.5)))
 
         case SDL_KEYDOWN

          
@@ 266,6 277,9 @@ inline render-fragment-shader (func opts
                 print "timer display"
                     if enable_timer_display "enabled"
                     else "disabled"
+            case SDLK_BACKSPACE
+                print "resetting to origin"
+                cam_origin = (vec3 0)
             case SDLK_PERIOD
                 advance_time = (not advance_time)
                 print

          
@@ 290,6 304,6 @@ inline render-fragment-shader (func opts
             render-view (deref glmain.size)
 
 do
-    let render-fragment-shader shglobals
+    let render-fragment-shader shglobals cpu_shglobals
 
     locals;