1ec8b4c14e0b — Leonard Ritter 4 months ago
* spread terrain meshing over multiple frames
2 files changed, 98 insertions(+), 58 deletions(-)

M lib/tukan/ResourceGroup.sc
M testing/test_cascade_dmc_cc_vvf.sc
M lib/tukan/ResourceGroup.sc +7 -0
@@ 100,6 100,13 @@ type ResourceGroup
 run-stage;
 
 type+ ResourceGroup
+    inline static-array (self T size func)
+        'static self (array T size)
+            inline ()
+                arrayof T
+                    va-map func
+                        va-range size
+
     inline program (self ...)
         'static self GL.Program
             inline ()

          
M testing/test_cascade_dmc_cc_vvf.sc +91 -58
@@ 38,6 38,7 @@ using import tukan.hash
 using import tukan.spherical
 using import tukan.ResourceGroup
 using import tukan.logcell
+using import tukan.Screen
 using import .testfragment
 
 let RG = (ResourceGroup "RG")

          
@@ 69,6 70,7 @@ FETCH_UV_OFFSET := 0.5
 FOG_RATE := 0.02 # 50% at 100 units
 
 let MAX_VERTICES = (20 * (1 << 20))
+MAX_SECTORS_PER_FRAME := 64 << 5
 
 let WORLD_SIZE = (uvec3 256)
 let WORLD_SCALE = (vec3 256.0)

          
@@ 93,7 95,7 @@ CASCADE_SIZE := (1 << MAX_CASCADE_DEPTH)
 CASCADE_CENTER := (CASCADE_SIZE // 2)
 
 let BINDING_BUF_SECTOR_IN = 1
-let BINDING_BUF_FACE_BRIDGE_IN = 2
+let BINDING_BUF_SECTOR_INOUT = 2
 let BINDING_BUF_VERTEX_IN = 3
 let BINDING_BUF_VERTEX_OUT = 4
 let BINDING_BUF_DRAW_VOXELS_CMD = 5

          
@@ 148,6 150,8 @@ let
 struct Sector plain
     key : u32
     flags : u32 # six face bits indicating where the next highest LOD level is
+    offset-min = 0xffffffff:u32 # index of first triangle in triangle buffer
+    offset-max = 0:u32 # index after last triangle in triangle buffer
 
 struct Sectors plain
     keys : (array Sector)

          
@@ 156,9 160,9 @@ buffer sector-in : Sectors
     binding = BINDING_BUF_SECTOR_IN
     \ readonly coherent
 
-buffer face-bridge-in : Sectors
-    binding = BINDING_BUF_FACE_BRIDGE_IN
-    \ readonly coherent
+buffer sector-inout : Sectors
+    binding = BINDING_BUF_SECTOR_INOUT
+    \ coherent
 
 buffer vertex-in : Vertices
     binding = BINDING_BUF_VERTEX_IN

          
@@ 174,9 178,6 @@ uniform smp-screen : sampler2D
 uniform mouse-state : i32
     location = UNIFORM_MOUSE_STATE
 
-uniform sector-offset : u32
-    location = UNIFORM_SECTOR_OFFSET
-
 fn simple-sphere (p)
     (length p) - 0.5
 

          
@@ 1142,6 1143,9 @@ fn generate-quad (v00 v01 v10 v11)
 
     # generate quad
     let ofs = (atomicAdd vertex-out.count 6)
+    sector := (sector-inout.keys @ gl_WorkGroupID.x)
+    atomicMin sector.offset-min ofs
+    atomicMax sector.offset-max (ofs + 6)
     entries := vertex-out.entries
     static-if USE_FLAT_SHADING
         n0 := (triangle-normal v00.pos v11.pos v10.pos)

          
@@ 1186,9 1190,9 @@ fn lod-skirt? (p sector-flags)
 
 fn generate-cell-verts ()
     local_size NATIVE_LANE_WIDTH 1 1
-    sector := (copy (sector-in.keys @ (gl_WorkGroupID.x + sector-offset)))
+    sector := (sector-inout.keys @ gl_WorkGroupID.x)
     sector-flags := (copy sector.flags)
-    let lvl sectorpos... = (decode-cell sector.key)
+    let lvl sectorpos... = (decode-cell (copy sector.key))
     #let lvl sectorpos... = (decode-cell 1:u32)
     lsectorpos := (ivec3 sectorpos...)
     sectorlod := (MAX_CASCADE_DEPTH - lvl)

          
@@ 1673,9 1677,18 @@ fn visualize-buffer (uv)
 
     return col
 
-fn shader (uv)
-    #mixdown uv
-    visualize-buffer uv
+inout uv : vec2 (location = 0)
+fn present-vert ()
+    uv.out =
+        ((Screen.set-vertex-position) * 0.5) + 0.5
+    ;
+
+fn present-frag ()
+    uv := (deref uv.in)
+    out_Color =
+        #mixdown uv
+        visualize-buffer uv
+    ;
 
 ################################################################################
 

          
@@ 1827,14 1840,25 @@ inline main ()
             #debug = true
 
     global rg : (Option RG)
+    global terrain-doublebuffer-index = 0
+
+    struct TerrainJob plain
+        sectors-processed = 0:usize
+
+        inline reset (self)
+            self.sectors-processed = 0
+        inline fresh? (self)
+            self.sectors-processed == 0
+        inline ready? (self)
+            self.sectors-processed >= (countof sectors)
+
+    global terrain-job : TerrainJob
+    'reset terrain-job
 
     fn per-frame-setup (size pg-test frame)
         let rg =
             'force-unwrap rg
-        from (methodsof rg) let static program compute-program indirect-draw-arrays-setup
-
-        GL.BindTextureUnit 0 fb-scene-color
-        GL.Uniform smp-screen 0
+        from (methodsof rg) let static static-array program compute-program indirect-draw-arrays-setup
 
         let world =
             static GL.Texture

          
@@ 1892,64 1916,65 @@ inline main ()
                     GL.DispatchCompute (unpack (((WORLD_SIZE >> (lod as u32)) + GROUP_SIZE - 1) // GROUP_SIZE))
                     GL.MemoryBarrier (GL.TEXTURE_FETCH_BARRIER_BIT | GL.SHADER_IMAGE_ACCESS_BARRIER_BIT)
 
-        # update terrain
-
-        collect-sectors;
-        sector-count := (countof sectors)
-        if (frame %  120 == 0)
-            print (sector-count as i32) "sectors"
-        let sector_buffer_sz = ((sizeof Sector) * SECTOR_CAPACITY)
-        let sector_buffer =
-            static GL.Buffer
-                inline ()
-                    let buf = (GL.Buffer)
-                    GL.NamedBufferData buf (i32 sector_buffer_sz) null GL.DYNAMIC_READ
-                    buf
-        GL.NamedBufferSubData sector_buffer 0
-            i32 (sector-count * (sizeof Sector))
-            & (sectors @ 0)
-
         let vertex_buffer_sz = ((sizeof Vertices) + (sizeof Vertex) * MAX_VERTICES)
-        let vertex_buffer =
-            static GL.Buffer
+        let vertex_buffers =
+            static-array GL.Buffer 3
                 inline ()
                     let buf = (GL.Buffer)
                     GL.NamedBufferData buf (i32 vertex_buffer_sz) null GL.STREAM_COPY
+                    GL.ClearNamedBufferSubData buf GL.R32UI 0 (sizeof u32) GL.RED_INTEGER GL.UNSIGNED_INT null
+                    buf
+
+        let sector_buffer_sz = ((sizeof Sector) * SECTOR_CAPACITY)
+        let sector_buffers =
+            static-array GL.Buffer 2
+                inline ()
+                    let buf = (GL.Buffer)
+                    GL.NamedBufferData buf (i32 sector_buffer_sz) null GL.STREAM_COPY
                     buf
 
-        do
-            # clear vertex buffer count
-            let ptr =
-                GL.MapNamedBufferRange vertex_buffer 0 (sizeof u32)
-                    | GL.MAP_WRITE_BIT
-                        GL.MAP_INVALIDATE_BUFFER_BIT
-                        #GL.MAP_UNSYNCHRONIZED_BIT
-            let ptr = (bitcast ptr (mutable pointer Vertices))
-            ptr.count = 0:u32
-            GL.UnmapNamedBuffer vertex_buffer
+        # update terrain
+
+        next-terrain-doublebuffer-index := terrain-doublebuffer-index ^ 1
+        next_sector_buffer := sector_buffers @ next-terrain-doublebuffer-index
+        next_vertex_buffer := vertex_buffers @ next-terrain-doublebuffer-index
+
+        if ('fresh? terrain-job)
+            collect-sectors;
+            GL.NamedBufferSubData next_sector_buffer 0
+                i32 ((countof sectors) * (sizeof Sector))
+                & (sectors @ 0)
+            GL.ClearNamedBufferSubData next_vertex_buffer GL.R32UI 0 (sizeof u32) GL.RED_INTEGER GL.UNSIGNED_INT null
+        sector-count := (countof sectors)
+        if ((cpu_shglobals.frame % 120) == 0)
+            print ((countof sectors) as i32) "sectors"
+
         GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
             BINDING_BUF_VERTEX_OUT
-            vertex_buffer
+            next_vertex_buffer
             \ 0:i64 (i64 vertex_buffer_sz)
+        sectors-to-process := (min MAX_SECTORS_PER_FRAME (sector-count as i32 - terrain-job.sectors-processed as i32))
         GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
-            BINDING_BUF_SECTOR_IN
-            sector_buffer
-            \ 0:i64 (i64 (sector-count * (sizeof Sector)))
+            BINDING_BUF_SECTOR_INOUT
+            next_sector_buffer
+            \ (i64 (terrain-job.sectors-processed * (sizeof Sector))) (i64 (sectors-to-process * (sizeof Sector)))
         GL.BindTextureUnit 1 world
         let pg-gen-cell = (compute-program generate-cell-verts)
         GL.UseProgram pg-gen-cell
         GL.Uniform smp-world 1
-        MAX_WORKGROUPS := 32768:u32
-        for i in (range 0:u32 (sector-count as u32) MAX_WORKGROUPS)
-            offset := i
-            GL.Uniform sector-offset offset
-            sz := (min MAX_WORKGROUPS (sector-count as u32 - i))
-            GL.DispatchCompute sz 1 1
+        GL.DispatchCompute (sectors-to-process as u32) 1 1
+        terrain-job.sectors-processed += sectors-to-process
 
         GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
 
+        if ('ready? terrain-job)
+            'reset terrain-job
+            terrain-doublebuffer-index ^= 1
+
         #############
 
+        vertex_buffer := vertex_buffers @ terrain-doublebuffer-index
+
         inline print-in-count ()
             let ptr =
                 GL.MapNamedBufferRange vertex_buffer 0 (sizeof u32)

          
@@ 1996,8 2021,6 @@ inline main ()
                     GL.STENCIL_BUFFER_BIT
 
             GL.UseProgram pg-rasterize
-            #GL.BindTextureUnit 1 world
-            #GL.Uniform smp-world 1
             GL.BindVertexArray vao-empty
             exec-draw-arrays GL.TRIANGLES
 

          
@@ 2005,11 2028,21 @@ inline main ()
             GL.Disable GL.CULL_FACE
             GL.BindFramebuffer GL.FRAMEBUFFER 0
 
+        let screen = (static Screen)
+        let pg-presentfb =
+            program
+                vertex = present-vert
+                fragment = present-frag
+        GL.UseProgram pg-presentfb
+        GL.BindTextureUnit 0 fb-scene-color
+        GL.Uniform smp-screen 0
+        'draw screen
+
     let per-frame-setup =
         static-typify per-frame-setup ivec2 GL.Program i32
     rg = (RG)
 
-    _ per-frame-setup shader
+    per-frame-setup
 
 fn program ()
     render-fragment-shader main