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