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 {}