1 /// ImageBase module
2 module gfx.graal.image;
3 
4 import gfx.core.rc;
5 import gfx.core.typecons;
6 import gfx.graal.format;
7 import gfx.graal.memory;
8 import gfx.graal.pipeline : CompareOp;
9 
10 import std.typecons : Flag;
11 
12 struct ImageInfo
13 {
14     ImageType type;
15     ImageDims dims;
16     Format format;
17     ImageUsage usage;
18     ImageTiling tiling;
19     uint layers=1;
20     uint samples=1;
21     uint levels=1;
22 
23     static ImageInfo d1 (in uint width) {
24         return ImageInfo(
25             ImageType.d1,
26             ImageDims(width, 1, 1)
27         );
28     }
29     static ImageInfo d2 (in uint width, in uint height) {
30         return ImageInfo(
31             ImageType.d2,
32             ImageDims(width, height, 1)
33         );
34     }
35     static ImageInfo d3 (in uint width, in uint height, in uint depth) {
36         return ImageInfo(
37             ImageType.d3,
38             ImageDims(width, height, depth)
39         );
40     }
41     static ImageInfo cube (in uint width, in uint height) {
42         return ImageInfo(
43             ImageType.cube,
44             ImageDims(width, height, 6)
45         );
46     }
47     static ImageInfo d1Array (in uint width, in uint layers) {
48         auto ii = ImageInfo(
49             ImageType.d1Array,
50             ImageDims(width, 1, 1)
51         );
52         ii.layers = layers;
53         return ii;
54     }
55     static ImageInfo d2Array (in uint width, uint height, in uint layers) {
56         auto ii = ImageInfo(
57             ImageType.d2Array,
58             ImageDims(width, height, 1)
59         );
60         ii.layers = layers;
61         return ii;
62     }
63     static ImageInfo cubeArray (in uint width, in uint height, in uint layers) {
64         auto ii = ImageInfo(
65             ImageType.cubeArray,
66             ImageDims(width, height, 1)
67         );
68         ii.layers = layers;
69         return ii;
70     }
71 
72     ImageInfo withFormat(in Format format) const {
73         ImageInfo ii = this;
74         ii.format = format;
75         return ii;
76     }
77     ImageInfo withUsage(in ImageUsage usage) const {
78         ImageInfo ii = this;
79         ii.usage = usage;
80         return ii;
81     }
82     ImageInfo withTiling(in ImageTiling tiling) const {
83         ImageInfo ii = this;
84         ii.tiling = tiling;
85         return ii;
86     }
87     ImageInfo withSamples(in uint samples) const {
88         ImageInfo ii = this;
89         ii.samples = samples;
90         return ii;
91     }
92     ImageInfo withLevels(in uint levels) const {
93         ImageInfo ii = this;
94         ii.levels = levels;
95         return ii;
96     }
97 }
98 
99 enum ImageType {
100     d1, d1Array,
101     d2, d2Array,
102     d3, cube, cubeArray
103 }
104 
105 bool isCube(in ImageType it) {
106     return it == ImageType.cube || it == ImageType.cubeArray;
107 }
108 
109 bool isArray(in ImageType it) {
110     return it == ImageType.d1Array || it == ImageType.d2Array || it == ImageType.cubeArray;
111 }
112 
113 enum CubeFace {
114     none,
115     posX, negX,
116     posY, negY,
117     posZ, negZ,
118 }
119 
120 /// an array of faces in the order that is expected during cube initialization
121 immutable cubeFaces = [
122     CubeFace.posX, CubeFace.negX,
123     CubeFace.posY, CubeFace.negY,
124     CubeFace.posZ, CubeFace.negZ,
125 ];
126 
127 struct ImageDims
128 {
129     uint width;
130     uint height=1;
131     uint depth=1;
132 }
133 
134 enum ImageUsage {
135     none = 0,
136     transferSrc             = 0x01,
137     transferDst             = 0x02,
138     sampled                 = 0x04,
139     storage                 = 0x08,
140     colorAttachment         = 0x10,
141     depthStencilAttachment  = 0x20,
142     transientAttachment     = 0x40,
143     inputAttachment         = 0x80,
144 
145     transfer                = transferSrc | transferDst,
146 }
147 
148 enum ImageLayout {
149 	undefined                       = 0,
150 	general                         = 1,
151 	colorAttachmentOptimal          = 2,
152 	depthStencilAttachmentOptimal   = 3,
153 	depthStencilReadOnlyOptimal     = 4,
154 	shaderReadOnlyOptimal           = 5,
155 	transferSrcOptimal              = 6,
156 	transferDstOptimal              = 7,
157 	preinitialized                  = 8,
158     presentSrc                      = 1000001002, // TODO impl actual mapping to vulkan
159 }
160 
161 enum ImageTiling {
162     optimal,
163     linear,
164 }
165 
166 enum ImageAspect {
167     color           = 0x01,
168     depth           = 0x02,
169     stencil         = 0x04,
170     depthStencil    = depth | stencil,
171 }
172 
173 
174 struct ImageSubresourceLayer
175 {
176     ImageAspect aspect;
177     uint mipLevel       = 0;
178     uint firstLayer     = 0;
179     uint layers         = 1;
180 }
181 
182 struct ImageSubresourceRange
183 {
184     ImageAspect aspect;
185     size_t firstLevel   = 0;
186     size_t levels       = 1;
187     size_t firstLayer   = 0;
188     size_t layers       = 1;
189 }
190 
191 enum CompSwizzle : ubyte
192 {
193     identity,
194     zero, one,
195     r, g, b, a,
196 }
197 
198 struct Swizzle
199 {
200     private CompSwizzle[4] rep;
201 
202     this(in CompSwizzle r, in CompSwizzle g, in CompSwizzle b, in CompSwizzle a)
203     {
204         rep = [r, g, b, a];
205     }
206 
207     static @property Swizzle identity() {
208         return Swizzle(
209             CompSwizzle.identity, CompSwizzle.identity,
210             CompSwizzle.identity, CompSwizzle.identity
211         );
212     }
213 
214     static @property Swizzle one() {
215         return Swizzle(
216             CompSwizzle.one, CompSwizzle.one,
217             CompSwizzle.one, CompSwizzle.one
218         );
219     }
220 
221     static @property Swizzle zero() {
222         return Swizzle(
223             CompSwizzle.zero, CompSwizzle.zero,
224             CompSwizzle.zero, CompSwizzle.zero
225         );
226     }
227 
228     static @property Swizzle opDispatch(string name)() {
229         bool isSwizzleIdent() {
230             foreach (char c; name) {
231                 switch (c) {
232                 case 'r': break;
233                 case 'g': break;
234                 case 'b': break;
235                 case 'a': break;
236                 case 'i': break;
237                 case 'o': break;
238                 case 'z': break;
239                 default: return false;
240                 }
241             }
242             return true;
243         }
244         CompSwizzle getComp(char c) {
245             switch (c) {
246             case 'r': return CompSwizzle.r;
247             case 'g': return CompSwizzle.g;
248             case 'b': return CompSwizzle.b;
249             case 'a': return CompSwizzle.a;
250             case 'i': return CompSwizzle.identity;
251             case 'o': return CompSwizzle.one;
252             case 'z': return CompSwizzle.zero;
253             default: assert(false);
254             }
255         }
256 
257         static assert(name.length == 4, "Error: Swizzle."~name~". Swizzle identifier must have four components.");
258         static assert(isSwizzleIdent(), "Wrong swizzle identifier: Swizzle."~name);
259         return Swizzle(
260             getComp(name[0]), getComp(name[1]), getComp(name[2]), getComp(name[3])
261         );
262     }
263 
264     size_t opDollar() const { return 4; }
265     CompSwizzle opIndex(size_t ind) const { return rep[ind]; }
266     const(CompSwizzle)[] opIndex() const { return rep[]; }
267     size_t[2] opSlice(size_t dim)(size_t start, size_t end) const {
268         return [start, end];
269     }
270     const(CompSwizzle)[] opIndex(size_t[2] slice) const {
271         return rep[slice[0] .. slice[1]];
272     }
273 }
274 
275 ///
276 unittest {
277     assert(!__traits(compiles, Swizzle.rrr));
278     assert(!__traits(compiles, Swizzle.qwer));
279 
280     assert(Swizzle.rgba == Swizzle(CompSwizzle.r, CompSwizzle.g, CompSwizzle.b, CompSwizzle.a));
281     assert(Swizzle.rrbb == Swizzle(CompSwizzle.r, CompSwizzle.r, CompSwizzle.b, CompSwizzle.b));
282     assert(Swizzle.aaag == Swizzle(CompSwizzle.a, CompSwizzle.a, CompSwizzle.a, CompSwizzle.g));
283     assert(Swizzle.iiii == Swizzle.identity);
284     assert(Swizzle.oooo == Swizzle.one);
285     assert(Swizzle.zzzz == Swizzle.zero);
286 }
287 
288 interface ImageBase
289 {
290     @property ImageInfo info();
291 
292     // TODO: deduce view type from subrange and image type
293     ImageView createView(ImageType viewtype, ImageSubresourceRange isr, Swizzle swizzle);
294 }
295 
296 interface Image : ImageBase, AtomicRefCounted
297 {
298     import gfx.graal.device : Device;
299 
300     /// Get the parent device
301     @property Device device();
302 
303     @property MemoryRequirements memoryRequirements();
304     /// The image keeps a reference of the device memory
305     void bindMemory(DeviceMemory mem, in size_t offset);
306     /// The memory bound to this image
307     @property DeviceMemory boundMemory();
308 }
309 
310 interface ImageView : AtomicRefCounted
311 {
312     @property ImageBase image();
313     @property ImageSubresourceRange subresourceRange();
314     @property Swizzle swizzle();
315 }
316 
317 /// Type of filter for texture sampling
318 enum Filter {
319     /// nearest sample is used
320     nearest,
321     /// sample is interpolated with neighboors
322     linear,
323 }
324 
325 /// Specifies how texture coordinates outside the range `[0, 1]` are handled.
326 enum WrapMode {
327     /// Repeat the texture. That is, sample the coordinate modulo `1.0`.
328     repeat,
329     /// Mirror the texture. Like tile, but uses abs(coord) before the modulo.
330     mirrorRepeat,
331     /// Clamp the texture to the value at `0.0` or `1.0` respectively.
332     clamp,
333     /// Use border color.
334     border,
335 }
336 
337 enum BorderColor
338 {
339     floatTransparent    = 0,
340     intTransparent      = 1,
341     floatBlack          = 2,
342     intBlack            = 3,
343     floatWhite          = 4,
344     intWhite            = 5,
345 }
346 
347 @property bool isFloat(in BorderColor color) pure {
348     return (cast(int)color & 0x01) == 0;
349 }
350 @property bool isInt(in BorderColor color) pure {
351     return (cast(int)color & 0x01) == 1;
352 }
353 @property bool isTransparent(in BorderColor color) pure {
354     return (cast(int)color & 0x06) == 0;
355 }
356 @property bool isBlack(in BorderColor color) pure {
357     return (cast(int)color & 0x06) == 2;
358 }
359 @property bool isWhite(in BorderColor color) pure {
360     return (cast(int)color & 0x06) == 4;
361 }
362 
363 /// Structure holding texture sampler information.
364 /// It is used to create samplers.
365 struct SamplerInfo {
366     /// The minification filter
367     Filter minFilter;
368     /// The magnification filter
369     Filter magFilter;
370     /// The filter used between mipmap levels
371     Filter mipmapFilter;
372     /// The wrap mode for look-ups outside of [ 0 , 1 ] in the three axis
373     WrapMode[3] wrapMode;
374     /// Enables anisotropy filtering. The value set serves as maximum covering fragments.
375     Option!float anisotropy;
376     float lodBias = 0f;
377     float[2] lodRange = [ 0f, 0f ];
378     /// Enables a comparison operation during lookup of depth/stencil based textures.
379     /// Mostly useful for shadow maps.
380     Option!CompareOp compare;
381     BorderColor borderColor;
382     /// If set, lookup is done in texel space rather than normalized coordinates.
383     Flag!"unnormalizeCoords" unnormalizeCoords;
384 
385     /// Initializes a non-filtering SamplerInfo
386     static @property SamplerInfo nearest() {
387         return SamplerInfo.init;
388     }
389 
390     /// Initializes a bilinear filtering SamplerInfo
391     static @property SamplerInfo bilinear() {
392         SamplerInfo si;
393         si.minFilter = Filter.linear;
394         si.magFilter = Filter.linear;
395         return si;
396     }
397 
398     /// Initializes a trilinear filtering SamplerInfo (that is also linear between mipmap levels)
399     static @property SamplerInfo trilinear() {
400         SamplerInfo si;
401         si.minFilter = Filter.linear;
402         si.magFilter = Filter.linear;
403         si.mipmapFilter = Filter.linear;
404         return si;
405     }
406 
407     /// Enables comparison operation for depth/stencil texture look-ups.
408     /// Use this with shadow samplers.
409     SamplerInfo withCompareOp(in CompareOp op) const {
410         SamplerInfo si = this;
411         si.compare = some(op);
412         return si;
413     }
414 
415     /// Set the wrap mode for the 3 axis
416     SamplerInfo withWrapMode(in WrapMode mode) const {
417         SamplerInfo si = this;
418         si.wrapMode = [ mode, mode, mode ];
419         return si;
420     }
421 }
422 
423 interface Sampler : AtomicRefCounted
424 {
425     import gfx.graal.device : Device;
426 
427     /// Get the parent device
428     @property Device device();
429 }