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 }