github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/shaders/mem.h (about)

     1  // SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
     2  
     3  layout(set = 0, binding = 0) buffer Memory {
     4      // offset into memory of the next allocation, initialized by the user.
     5      uint mem_offset;
     6      // mem_error tracks the status of memory accesses, initialized to NO_ERROR
     7      // by the user. ERR_MALLOC_FAILED is reported for insufficient memory.
     8      // If MEM_DEBUG is defined the following errors are reported:
     9      // - ERR_OUT_OF_BOUNDS is reported for out of bounds writes.
    10      // - ERR_UNALIGNED_ACCESS for memory access not aligned to 32-bit words.
    11      uint mem_error;
    12      uint[] memory;
    13  };
    14  
    15  // Uncomment this line to add the size field to Alloc and enable memory checks.
    16  // Note that the Config struct in setup.h grows size fields as well.
    17  //#define MEM_DEBUG
    18  
    19  #define NO_ERROR 0
    20  #define ERR_MALLOC_FAILED 1
    21  #define ERR_OUT_OF_BOUNDS 2
    22  #define ERR_UNALIGNED_ACCESS 3
    23  
    24  #ifdef MEM_DEBUG
    25  #define Alloc_size 16
    26  #else
    27  #define Alloc_size 8
    28  #endif
    29  
    30  // Alloc represents a memory allocation.
    31  struct Alloc {
    32      // offset in bytes into memory.
    33      uint offset;
    34  #ifdef MEM_DEBUG
    35      // size in bytes of the allocation.
    36      uint size;
    37  #endif
    38  };
    39  
    40  struct MallocResult {
    41      Alloc alloc;
    42      // failed is true if the allocation overflowed memory.
    43      bool failed;
    44  };
    45  
    46  // new_alloc synthesizes an Alloc from an offset and size.
    47  Alloc new_alloc(uint offset, uint size, bool mem_ok) {
    48      Alloc a;
    49      a.offset = offset;
    50  #ifdef MEM_DEBUG
    51      if (mem_ok) {
    52          a.size = size;
    53      } else {
    54          a.size = 0;
    55      }
    56  #endif
    57      return a;
    58  }
    59  
    60  // malloc allocates size bytes of memory.
    61  MallocResult malloc(uint size) {
    62      MallocResult r;
    63      uint offset = atomicAdd(mem_offset, size);
    64      r.failed = offset + size > memory.length() * 4;
    65      r.alloc = new_alloc(offset, size, !r.failed);
    66      if (r.failed) {
    67          atomicMax(mem_error, ERR_MALLOC_FAILED);
    68          return r;
    69      }
    70  #ifdef MEM_DEBUG
    71      if ((size & 3) != 0) {
    72          r.failed = true;
    73          atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
    74          return r;
    75      }
    76  #endif
    77      return r;
    78  }
    79  
    80  // touch_mem checks whether access to the memory word at offset is valid.
    81  // If MEM_DEBUG is defined, touch_mem returns false if offset is out of bounds.
    82  // Offset is in words.
    83  bool touch_mem(Alloc alloc, uint offset) {
    84  #ifdef MEM_DEBUG
    85      if (offset < alloc.offset/4 || offset >= (alloc.offset + alloc.size)/4) {
    86          atomicMax(mem_error, ERR_OUT_OF_BOUNDS);
    87          return false;
    88      }
    89  #endif
    90      return true;
    91  }
    92  
    93  // write_mem writes val to memory at offset.
    94  // Offset is in words.
    95  void write_mem(Alloc alloc, uint offset, uint val) {
    96      if (!touch_mem(alloc, offset)) {
    97          return;
    98      }
    99      memory[offset] = val;
   100  }
   101  
   102  // read_mem reads the value from memory at offset.
   103  // Offset is in words.
   104  uint read_mem(Alloc alloc, uint offset) {
   105      if (!touch_mem(alloc, offset)) {
   106          return 0;
   107      }
   108      uint v = memory[offset];
   109      return v;
   110  }
   111  
   112  // slice_mem returns a sub-allocation inside another. Offset and size are in
   113  // bytes, relative to a.offset.
   114  Alloc slice_mem(Alloc a, uint offset, uint size) {
   115  #ifdef MEM_DEBUG
   116      if ((offset & 3) != 0 || (size & 3) != 0) {
   117          atomicMax(mem_error, ERR_UNALIGNED_ACCESS);
   118          return Alloc(0, 0);
   119      }
   120      if (offset + size > a.size) {
   121          // slice_mem is sometimes used for slices outside bounds,
   122          // but never written.
   123          return Alloc(0, 0);
   124      }
   125      return Alloc(a.offset + offset, size);
   126  #else
   127      return Alloc(a.offset + offset);
   128  #endif
   129  }
   130  
   131  // alloc_write writes alloc to memory at offset bytes.
   132  void alloc_write(Alloc a, uint offset, Alloc alloc) {
   133      write_mem(a, offset >> 2, alloc.offset);
   134  #ifdef MEM_DEBUG
   135      write_mem(a, (offset >> 2) + 1, alloc.size);
   136  #endif
   137  }
   138  
   139  // alloc_read reads an Alloc from memory at offset bytes.
   140  Alloc alloc_read(Alloc a, uint offset) {
   141      Alloc alloc;
   142      alloc.offset = read_mem(a, offset >> 2);
   143  #ifdef MEM_DEBUG
   144      alloc.size = read_mem(a, (offset >> 2) + 1);
   145  #endif
   146      return alloc;
   147  }