1 module gfx.graal.memory; 2 3 import gfx.core.rc; 4 import gfx.graal.device; 5 6 /// Properties of memory allocated by the device 7 enum MemProps { 8 deviceLocal = 0x01, 9 hostVisible = 0x02, 10 hostCoherent = 0x04, 11 hostCached = 0x08, 12 lazilyAllocated = 0x10, 13 } 14 15 /// Structure representing all heaps and types of memory from a device. 16 /// A device can have different heaps each supporting different types. 17 struct MemoryProperties { 18 MemoryHeap[] heaps; 19 MemoryType[] types; 20 } 21 22 struct MemoryHeap { 23 size_t size; 24 MemProps props; 25 bool deviceLocal; 26 } 27 28 struct MemoryType { 29 uint index; 30 uint heapIndex; 31 size_t heapSize; 32 MemProps props; 33 } 34 35 struct MemoryRequirements { 36 size_t size; 37 size_t alignment; 38 MemProps props; 39 } 40 41 struct MemoryMap(T) 42 { 43 private Rc!DeviceMemory dm; 44 private size_t offset; 45 private T[] data; 46 47 private this(DeviceMemory dm, in size_t offset, T[] data) 48 { 49 this.dm = dm; 50 this.offset = offset; 51 this.data = data; 52 } 53 54 @disable this(this); 55 56 ~this() 57 { 58 // should handle dtor of MemoryMap.init 59 if (dm) dm.unmap(); 60 } 61 62 void addToSet(ref MappedMemorySet set) 63 { 64 set.addMM(MappedMemorySet.MM(dm.obj, offset, data.length*T.sizeof)); 65 } 66 67 size_t opDollar() { 68 return data.length; 69 } 70 71 size_t[2] opSlice(size_t beg, size_t end) { 72 return [beg, end]; 73 } 74 75 T[] opIndex() { 76 return data; 77 } 78 T opIndex(size_t index) { 79 return data[index]; 80 } 81 T[] opIndex(in size_t[2] slice) { 82 return data[ slice[0] .. slice[1] ]; 83 } 84 85 void opIndexAssign(in T[] vals) { 86 data[] = vals; 87 } 88 void opIndexAssign(in T val, size_t ind) { 89 data[ind] = val; 90 } 91 void opIndexAssign(in T val, size_t[2] slice) { 92 data[slice[0] .. slice[1]] = val; 93 } 94 void opIndexAssign(in T[] vals, size_t[2] slice) { 95 data[slice[0] .. slice[1]] = vals; 96 } 97 } 98 99 100 101 /// Map device memory to host visible memory. 102 /// Params: 103 /// dm = the device memory to be mapped 104 /// offset = the offset to the requested memory in bytes 105 /// count = the number of elements of type T to be mapped 106 /// Warning: offset and count are not in the same units. 107 /// This is necessary in order to allow a memory block to hold several arrays 108 /// of different element types. 109 auto mapMemory(T)(DeviceMemory dm, in size_t offset, in size_t count) 110 { 111 const size = count * T.sizeof; 112 auto slice = dm.map(offset, size)[0 .. size]; 113 return MemoryMap!T(dm, offset, retypeSlice!T(slice)); 114 } 115 116 interface DeviceMemory : AtomicRefCounted 117 { 118 @property uint typeIndex(); 119 @property size_t size(); 120 121 void* map(in size_t offset, in size_t size); 122 void unmap(); 123 } 124 125 126 127 /// cast a typed slice into a blob of bytes 128 /// (same representation; no copy is made) 129 void[] untypeSlice(T)(T[] slice) if(!is(T == const)) 130 { 131 if (slice.length == 0) return []; 132 auto loc = cast(void*)slice.ptr; 133 return loc[0 .. slice.length*T.sizeof]; 134 } 135 136 /// ditto 137 const(void)[] untypeSlice(T)(const(T)[] slice) 138 { 139 if (slice.length == 0) return []; 140 auto loc = cast(const(void)*)slice.ptr; 141 return loc[0 .. slice.length*T.sizeof]; 142 } 143 144 /// cast a blob of bytes into a typed slice 145 T[] retypeSlice(T)(void[] slice) if (!is(T == const)) 146 in { 147 assert (!slice.length || (slice.length % T.sizeof) == 0); 148 } 149 body { 150 if(slice.length == 0) return []; 151 auto loc = cast(T*)slice.ptr; 152 return loc[0 .. slice.length / T.sizeof]; 153 } 154 155 /// ditto 156 const(T)[] retypeSlice(T)(const(void)[] slice) 157 in { 158 assert (!slice.length || (slice.length % T.sizeof) == 0); 159 } 160 body { 161 if(slice.length == 0) return []; 162 auto loc = cast(const(T)*)slice.ptr; 163 return loc[0 .. slice.length / T.sizeof]; 164 } 165 166 unittest { 167 int[] slice = [1, 2, 3, 4]; 168 auto bytes = cast(ubyte[])untypeSlice(slice); 169 auto ints = retypeSlice!int(bytes); 170 assert(bytes.length == 16); 171 version(LittleEndian) { 172 assert(bytes == [ 173 1, 0, 0, 0, 174 2, 0, 0, 0, 175 3, 0, 0, 0, 176 4, 0, 0, 0, 177 ]); 178 } 179 else { 180 assert(bytes == [ 181 0, 0, 0, 1, 182 0, 0, 0, 2, 183 0, 0, 0, 3, 184 0, 0, 0, 4, 185 ]); 186 } 187 assert(ints.length == 4); 188 assert(ints == slice); 189 assert(ints.ptr == slice.ptr); 190 } 191 192 /// cast an array of typed slices to another array of blob of bytes 193 /// an allocation is performed for the top container (the array of arrays) 194 /// but the underlying data is moved without allocation 195 void[][] untypeSlices(T)(T[][] slices) if (!is(T == const)) { 196 void[][] res = new void[][slices.length]; 197 foreach(i, s; slices) { 198 res[i] = untypeSlice(s); 199 } 200 return res; 201 } 202 203 /// ditto 204 const(void)[][] untypeSlices(T)(const(T)[][] slices) { 205 const(void)[][] res = new const(void)[][slices.length]; 206 foreach(i, s; slices) { 207 res[i] = untypeSlice(s); 208 } 209 return res; 210 } 211 212 unittest { 213 int[][] texels = [ [1, 2], [3, 4] ]; 214 auto bytes = cast(ubyte[][])untypeSlices(texels); 215 assert(bytes.length == 2); 216 assert(bytes[0].length == 8); 217 assert(bytes[1].length == 8); 218 version(LittleEndian) { 219 assert(bytes == [ 220 [ 1, 0, 0, 0, 221 2, 0, 0, 0, ], 222 [ 3, 0, 0, 0, 223 4, 0, 0, 0, ], 224 ]); 225 } 226 else { 227 assert(bytes == [ 228 [ 0, 0, 0, 1, 229 0, 0, 0, 2, ], 230 [ 0, 0, 0, 3, 231 0, 0, 0, 4, ], 232 ]); 233 } 234 }