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     private CompSwizzle[4] rep;
200 
201     this(in CompSwizzle r, in CompSwizzle g, in CompSwizzle b, in CompSwizzle a)
202     {
203         rep = [r, g, b, a];
204     }
205 
206     static @property Swizzle identity() {
207         return Swizzle(
208             CompSwizzle.identity, CompSwizzle.identity,
209             CompSwizzle.identity, CompSwizzle.identity
210         );
211     }
212 
213     static @property Swizzle one() {
214         return Swizzle(
215             CompSwizzle.one, CompSwizzle.one,
216             CompSwizzle.one, CompSwizzle.one
217         );
218     }
219 
220     static @property Swizzle zero() {
221         return Swizzle(
222             CompSwizzle.zero, CompSwizzle.zero,
223             CompSwizzle.zero, CompSwizzle.zero
224         );
225     }
226 
227     static @property Swizzle opDispatch(string name)() {
228         bool isSwizzleIdent() {
229             foreach (char c; name) {
230                 switch (c) {
231                 case 'r': break;
232                 case 'g': break;
233                 case 'b': break;
234                 case 'a': break;
235                 case 'i': break;
236                 case 'o': break;
237                 case 'z': break;
238                 default: return false;
239                 }
240             }
241             return true;
242         }
243         CompSwizzle getComp(char c) {
244             switch (c) {
245             case 'r': return CompSwizzle.r;
246             case 'g': return CompSwizzle.g;
247             case 'b': return CompSwizzle.b;
248             case 'a': return CompSwizzle.a;
249             case 'i': return CompSwizzle.identity;
250             case 'o': return CompSwizzle.one;
251             case 'z': return CompSwizzle.zero;
252             default: assert(false);
253             }
254         }
255 
256         static assert(name.length == 4, "Error: Swizzle."~name~". Swizzle identifier must have four components.");
257         static assert(isSwizzleIdent(), "Wrong swizzle identifier: Swizzle."~name);
258         return Swizzle(
259             getComp(name[0]), getComp(name[1]), getComp(name[2]), getComp(name[3])
260         );
261     }
262 
263     size_t opDollar() const { return 4; }
264     CompSwizzle opIndex(size_t ind) const { return rep[ind]; }
265     const(CompSwizzle)[] opIndex() const { return rep[]; }
266     size_t[2] opSlice(size_t dim)(size_t start, size_t end) const {
267         return [start, end];
268     }
269     const(CompSwizzle)[] opIndex(size_t[2] slice) const {
270         return rep[slice[0] .. slice[1]];
271     }
272 }
273 
274 ///
275 unittest {
276     assert(!__traits(compiles, Swizzle.rrr));
277     assert(!__traits(compiles, Swizzle.qwer));
278 
279     assert(Swizzle.rgba == Swizzle(CompSwizzle.r, CompSwizzle.g, CompSwizzle.b, CompSwizzle.a));
280     assert(Swizzle.rrbb == Swizzle(CompSwizzle.r, CompSwizzle.r, CompSwizzle.b, CompSwizzle.b));
281     assert(Swizzle.aaag == Swizzle(CompSwizzle.a, CompSwizzle.a, CompSwizzle.a, CompSwizzle.g));
282     assert(Swizzle.iiii == Swizzle.identity);
283     assert(Swizzle.oooo == Swizzle.one);
284     assert(Swizzle.zzzz == Swizzle.zero);
285 }
286 
287 interface ImageBase
288 {
289     @property ImageInfo info();
290 
291     // TODO: deduce view type from subrange and image type
292     ImageView createView(ImageType viewtype, ImageSubresourceRange isr, Swizzle swizzle);
293 }
294 
295 interface Image : ImageBase, AtomicRefCounted
296 {
297     @property MemoryRequirements memoryRequirements();
298     /// The image keeps a reference of the device memory
299     void bindMemory(DeviceMemory mem, in size_t offset);
300 }
301 
302 interface ImageView : AtomicRefCounted
303 {
304     @property ImageBase image();
305     @property ImageSubresourceRange subresourceRange();
306     @property Swizzle swizzle();
307 }
308 
309 /// Type of filter for texture sampling
310 enum Filter {
311     ///
312     nearest,
313     ///
314     linear,
315 }
316 
317 
318 /// Specifies how texture coordinates outside the range `[0, 1]` are handled.
319 enum WrapMode {
320     /// Repeat the texture. That is, sample the coordinate modulo `1.0`.
321     repeat,
322     /// Mirror the texture. Like tile, but uses abs(coord) before the modulo.
323     mirrorRepeat,
324     /// Clamp the texture to the value at `0.0` or `1.0` respectively.
325     clamp,
326     /// Use border color.
327     border,
328 }
329 
330 enum BorderColor
331 {
332     floatTransparent    = 0,
333     intTransparent      = 1,
334     floatBlack          = 2,
335     intBlack            = 3,
336     floatWhite          = 4,
337     intWhite            = 5,
338 }
339 
340 @property bool isFloat(in BorderColor color) pure {
341     return (cast(int)color & 0x01) == 0;
342 }
343 @property bool isInt(in BorderColor color) pure {
344     return (cast(int)color & 0x01) == 1;
345 }
346 @property bool isTransparent(in BorderColor color) pure {
347     return (cast(int)color & 0x06) == 0;
348 }
349 @property bool isBlack(in BorderColor color) pure {
350     return (cast(int)color & 0x06) == 2;
351 }
352 @property bool isWhite(in BorderColor color) pure {
353     return (cast(int)color & 0x06) == 4;
354 }
355 
356 /// Structure holding texture sampler information.
357 /// It is used to create samplers.
358 struct SamplerInfo {
359     /// The minification filter
360     Filter minFilter;
361     /// The magnification filter
362     Filter magFilter;
363     /// The filter used between mipmap levels
364     Filter mipmapFilter;
365     /// The wrap mode for look-ups outside of [ 0 , 1 ] in the three axis
366     WrapMode[3] wrapMode;
367     /// Enables anisotropy filtering. The value set serves as maximum covering fragments.
368     Option!float anisotropy;
369     float lodBias = 0f;
370     float[2] lodRange = [ 0f, 0f ];
371     /// Enables a comparison operation during lookup of depth/stencil based textures.
372     /// Mostly useful for shadow maps.
373     Option!CompareOp compare;
374     BorderColor borderColor;
375     /// If set, lookup is done in texel space rather than normalized coordinates.
376     Flag!"unnormalizeCoords" unnormalizeCoords;
377 
378     /// Initializes a bilinear filtering SamplerInfo
379     static @property SamplerInfo bilinear() {
380         SamplerInfo si;
381         si.minFilter = Filter.linear;
382         si.magFilter = Filter.linear;
383         return si;
384     }
385 
386     /// Initializes a trilinear filtering SamplerInfo (that is also linear between mipmap levels)
387     static @property SamplerInfo trilinear() {
388         SamplerInfo si;
389         si.minFilter = Filter.linear;
390         si.magFilter = Filter.linear;
391         si.mipmapFilter = Filter.linear;
392         return si;
393     }
394 
395     /// Enables comparison operation for depth/stencil texture look-ups.
396     /// Use this with shadow samplers.
397     SamplerInfo withCompareOp(in CompareOp op) const {
398         SamplerInfo si = this;
399         si.compare = some(op);
400         return si;
401     }
402 
403     /// Set the wrap mode for the 3 axis
404     SamplerInfo withWrapMode(in WrapMode mode) const {
405         SamplerInfo si = this;
406         si.wrapMode = [ mode, mode, mode ];
407         return si;
408     }
409 }
410 
411 interface Sampler : AtomicRefCounted
412 {}