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 }