anchor.diff:      let scan = runtime.CreateComputeShader (Kernels.scanImageKernel2d add)
       let fixup = runtime.CreateComputeShader(Kernels.fixupImageKernel2d add)
   
  +    let next (range : ScanRange) =
  +        // (Kernels.scanSize - 1) + x * Kernels.scanSize <= range.count - 1
  +        // x * Kernels.scanSize <= range.count - 1 - Kernels.scanSize + 1
  +        // x <= (range.count - Kernels.scanSize) / Kernels.scanSize
  +        let innerCount = 1 + (range.count - Kernels.scanSize) / Kernels.scanSize
  +        let innerDelta = range.delta * Kernels.scanSize
  +        let innerOffset = range.offset + range.delta * (Kernels.scanSize - 1)
  +        { offset = innerOffset; delta = innerDelta; count = innerCount }
  +
  +
  +
       let release() =
           runtime.DeleteComputeShader scanTexture
           runtime.DeleteComputeShader scan
   
  -    let rec buildX (args : HashSet<IComputeShaderInputBinding>) (image : ITextureSubResource) (offset : int) (delta : int) (count : int) =
  -        
  -        if count <= 1 then
  +    let scanBlocks (args : HashSet<IComputeShaderInputBinding>) (dim : int) (image : ITextureSubResource) (range : ScanRange) =
  +        let input = runtime.NewInputBinding(scan)
  +        input.["inOutImage"] <- image
  +        input.["dimension"] <- dim
  +        input.["Offset"] <- range.offset
  +        input.["Size"] <- range.count
  +        input.["Delta"] <- range.delta
  +        input.Flush()
  +        args.Add input |> ignore
  +        
  +        let rest =
  +            match dim with
  +                | 0 -> image.Size.YZ
  +                | 1 -> image.Size.XZ
  +                | 2 -> image.Size.XY
  +                | _ -> failwith "[GPGPU] invalid dimension"
  +
  +        [
  +            ComputeCommand.Sync(image.Texture)
  +            ComputeCommand.Bind scan
  +            ComputeCommand.SetInput input
  +            ComputeCommand.Dispatch(V3i(ceilDiv range.count Kernels.scanSize, rest.X, rest.Y))
  +        ]
  +
  +    let repair (args : HashSet<IComputeShaderInputBinding>) (dim : int) (image : ITextureSubResource) (range : ScanRange) =
  +        let innerRange = next range
  +
  +        let args1 = runtime.NewInputBinding fixup
  +        args1.["inOutImage"] <- image
  +        args1.["dimension"] <- dim
  +        args1.["inputOffset"] <- innerRange.offset
  +        args1.["inputDelta"] <- innerRange.delta
  +        args1.["outputOffset"] <- range.offset
  +        args1.["outputDelta"] <- range.delta
  +        args1.["count"] <- range.count
  +        args1.["groupSize"] <- Kernels.scanSize
  +        args1.Flush()
  +        args.Add args1 |> ignore
  +
  +        let rest =
  +            match dim with
  +                | 0 -> image.Size.YZ
  +                | 1 -> image.Size.XZ
  +                | 2 -> image.Size.XY
  +                | _ -> failwith "[GPGPU] invalid dimension"
  +
  +        [
  +            // fix the shit
  +            ComputeCommand.Sync(image.Texture)
  +            ComputeCommand.Bind fixup
  +            ComputeCommand.SetInput args1
  +            ComputeCommand.Dispatch(V3i(ceilDiv (range.count - Kernels.scanSize) Kernels.halfScanSize, rest.X, rest.Y))
  +        ]
  +
  +
  +
  +    let rec buildDim (args : HashSet<IComputeShaderInputBinding>) (dim : int) (image : ITextureSubResource) (range : ScanRange) =
  +        
  +        if range.count <= 1 then
               []
           else
  -            let input = runtime.NewInputBinding(scan)
  -            input.["inOutImage"] <- image
  -            input.["dimension"] <- 0
  -            input.["Offset"] <- offset
  -            input.["Size"] <- count
  -            input.["Delta"] <- delta
  -            input.Flush()
  -
  -            args.Add input |> ignore
  -
               [
  -                yield ComputeCommand.Sync(image.Texture)
  -
  -                yield ComputeCommand.Bind scan
  -                yield ComputeCommand.SetInput input
  -                yield ComputeCommand.Dispatch(V2i(ceilDiv count Kernels.scanSize, image.Size.Y))
  -
  -                let innerOffset = offset + (Kernels.scanSize - 1) * delta
  -                let innerDelta = delta * Kernels.scanSize
  -                let innerCount = 1 + count / Kernels.scanSize
  -                yield! buildX args image innerOffset innerDelta innerCount // was y
  -
  -
  -                if innerCount > 1 then 
  -                    let args1 = runtime.NewInputBinding fixup
  -                    args1.["inOutImage"] <- image
  -                    args1.["dimension"] <- 0
  -                    args1.["inputOffset"] <- innerOffset
  -                    args1.["inputDelta"] <- innerDelta
  -                    args1.["outputOffset"] <- offset
  -                    args1.["outputDelta"] <- delta
  -                    args1.["count"] <- count
  -                    args1.["groupSize"] <- Kernels.scanSize
  -                    args1.Flush()
  -                    args.Add args1 |> ignore
  -
  -                    if count > Kernels.scanSize then
  -                        // fix the shit
  -                        yield ComputeCommand.Sync(image.Texture)
  -                        yield ComputeCommand.Bind fixup
  -                        yield ComputeCommand.SetInput args1
  -                        yield ComputeCommand.Dispatch(V2i(ceilDiv (count - Kernels.scanSize) Kernels.halfScanSize, image.Size.Y))
  -
  -                    ()
  +                yield! scanBlocks args dim image range
  +
  +                let innerRange = next range
  +                yield! buildDim args dim image innerRange
  +
  +                if range.count > Kernels.scanSize then
  +                    yield! repair args dim image range
   
               ]
  -
  -
  -    let rec buildY (args : HashSet<IComputeShaderInputBinding>) (image : ITextureSubResource) (offset : int) (delta : int) (count : int) =
  -        
  -        if count <= 1 then
  -            []
  -        else
  -            let input = runtime.NewInputBinding(scan)
  -            input.["inOutImage"] <- image
  -            input.["dimension"] <- 1
  -            input.["Offset"] <- offset
  -            input.["Size"] <- count
  -            input.["Delta"] <- delta
  -            input.Flush()
  -
  -            args.Add input |> ignore
  -
  -            [
  -                yield ComputeCommand.Sync(image.Texture)
  -
  -                yield ComputeCommand.Bind scan
  -                yield ComputeCommand.SetInput input
  -                yield ComputeCommand.Dispatch(V2i(ceilDiv count Kernels.scanSize, image.Size.X))
  -
  -                let innerOffset = offset + (Kernels.scanSize - 1)  * delta
  -                let innerDelta = delta * Kernels.scanSize
  -                let innerCount = 1 + count / Kernels.scanSize
  -                yield! buildY args image innerOffset innerDelta innerCount
  -
  -                if innerCount > 1 then
  -                    let args1 = runtime.NewInputBinding fixup
  -                    args1.["inOutImage"] <- image
  -                    args1.["dimension"] <- 1
  -                    args1.["inputOffset"] <- innerOffset
  -                    args1.["inputDelta"] <- innerDelta
  -                    args1.["outputOffset"] <- offset
  -                    args1.["outputDelta"] <- delta
  -                    args1.["count"] <- count
  -                    args1.["groupSize"] <- Kernels.scanSize
  -                    args1.Flush()
  -                    args.Add args1 |> ignore
  -
  -                    if count > Kernels.scanSize then
  -                        // fix the shit
  -                        yield ComputeCommand.Sync(image.Texture)
  -                        yield ComputeCommand.Bind fixup
  -                        yield ComputeCommand.SetInput args1
  -                        yield ComputeCommand.Dispatch(V2i(ceilDiv (count - Kernels.scanSize) Kernels.halfScanSize, image.Size.X))
  -
  -            ]
  -
  -
  -
   
           
   
  
positive.code:             yield ComputeCommand.Bind scanTexture
              yield ComputeCommand.SetInput xInput
              yield ComputeCommand.Dispatch(V2i(ceilDiv input.Size.X Kernels.scanSize, input.Size.Y))
  
              yield ComputeCommand.Sync(output.Texture)
  
              let innerCount = 1 + input.Size.X / Kernels.scanSize
              yield! buildX args output (Kernels.scanSize  - 1) Kernels.scanSize innerCount
              yield! buildY args output 0 1 input.Size.Y
          ]
  
  
  
negative.code: 
      static member Expr = add
  
  type private ScanRange = { offset : int; delta : int; count : int }
  
  type private ScanImage2d(runtime : IComputeRuntime, add : Expr<V4d -> V4d -> V4d>) =
  
      static let ceilDiv (v : int) (d : int) =
  