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 }