1 /// Format description module 2 module gfx.graal.format; 3 4 import std.meta : AliasSeq; 5 6 /// numeric format of texel channels 7 enum NumFormat 8 { 9 uNorm, 10 sNorm, 11 uScaled, 12 sScaled, 13 uInt, 14 sInt, 15 uFloat, 16 sFloat, 17 sRgb, 18 } 19 20 mixin(surfaceCode!()); 21 mixin(formatCode!()); 22 23 /// description of a format 24 struct FormatDesc 25 { 26 SurfaceType surfaceType; 27 NumFormat numFormat; 28 bool packed; 29 } 30 31 /// get the description of a format 32 FormatDesc formatDesc(Format fmt) { 33 return fmtDescs[cast(uint)fmt]; 34 } 35 36 /// Format properties 37 struct FormatProperties { 38 FormatFeatures linearTiling; 39 FormatFeatures optimalTiling; 40 FormatFeatures buffer; 41 } 42 43 /// Format features 44 enum FormatFeatures { 45 sampledImage = 0x0001, 46 storageImage = 0x0002, 47 storageImageAtomic = 0x0004, 48 uniformTexelBuffer = 0x0008, 49 storageTexelBuffer = 0x0010, 50 storageTexelBufferAtomic = 0x0020, 51 vertexBuffer = 0x0040, 52 colorAttachment = 0x0080, 53 colorAttachmentBlend = 0x0100, 54 depthStencilAttachment = 0x0200, 55 blitSrc = 0x0400, 56 blitDst = 0x0800, 57 sampledImageFilterLinear = 0x1000, 58 } 59 60 61 private: 62 63 enum fmtDescriptors = AliasSeq!( 64 "R4_G4", true, NumFormat.uNorm, 65 "R4_G4_B4_A4", true, NumFormat.uNorm, 66 "B4_G4_R4_A4", true, NumFormat.uNorm, 67 "R5_G6_B5", true, NumFormat.uNorm, 68 "B5_G6_R5", true, NumFormat.uNorm, 69 "R5_G5_B5_A1", true, NumFormat.uNorm, 70 "B5_G5_R5_A1", true, NumFormat.uNorm, 71 "A1_R5_G5_B5", true, NumFormat.uNorm, 72 "R8", false, NumFormat.uNorm, NumFormat.sNorm, 73 NumFormat.uScaled, NumFormat.sScaled, 74 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 75 "R8_G8", false, NumFormat.uNorm, NumFormat.sNorm, 76 NumFormat.uScaled, NumFormat.sScaled, 77 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 78 "R8_G8_B8", false, NumFormat.uNorm, NumFormat.sNorm, 79 NumFormat.uScaled, NumFormat.sScaled, 80 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 81 "B8_G8_R8", false, NumFormat.uNorm, NumFormat.sNorm, 82 NumFormat.uScaled, NumFormat.sScaled, 83 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 84 "R8_G8_B8_A8", false, NumFormat.uNorm, NumFormat.sNorm, 85 NumFormat.uScaled, NumFormat.sScaled, 86 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 87 "B8_G8_R8_A8", false, NumFormat.uNorm, NumFormat.sNorm, 88 NumFormat.uScaled, NumFormat.sScaled, 89 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 90 "A8_B8_G8_R8", true, NumFormat.uNorm, NumFormat.sNorm, 91 NumFormat.uScaled, NumFormat.sScaled, 92 NumFormat.uInt, NumFormat.sInt, NumFormat.sRgb, 93 "A2_R10_G10_B10", true, NumFormat.uNorm, NumFormat.sNorm, 94 NumFormat.uScaled, NumFormat.sScaled, 95 NumFormat.uInt, NumFormat.sInt, 96 "A2_B10_G10_R10", true, NumFormat.uNorm, NumFormat.sNorm, 97 NumFormat.uScaled, NumFormat.sScaled, 98 NumFormat.uInt, NumFormat.sInt, 99 "R16", false, NumFormat.uNorm, NumFormat.sNorm, 100 NumFormat.uScaled, NumFormat.sScaled, 101 NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 102 "R16_G16", false, NumFormat.uNorm, NumFormat.sNorm, 103 NumFormat.uScaled, NumFormat.sScaled, 104 NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 105 "R16_G16_B16", false, NumFormat.uNorm, NumFormat.sNorm, 106 NumFormat.uScaled, NumFormat.sScaled, 107 NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 108 "R16_G16_B16_A16", false, NumFormat.uNorm, NumFormat.sNorm, 109 NumFormat.uScaled, NumFormat.sScaled, 110 NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 111 "R32", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 112 "R32_G32", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 113 "R32_G32_B32", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 114 "R32_G32_B32_A32", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 115 "R64", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 116 "R64_G64", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 117 "R64_G64_B64", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 118 "R64_G64_B64_A64", false, NumFormat.uInt, NumFormat.sInt, NumFormat.sFloat, 119 "B10_G11_R11", true, NumFormat.uFloat, 120 "E5_B9_G9_R9", true, NumFormat.uFloat, 121 "D16", false, NumFormat.uNorm, 122 "X8_D24", true, NumFormat.uNorm, 123 "D32", false, NumFormat.uNorm, 124 "S8", false, NumFormat.uInt, 125 "D16_S8", false, NumFormat.uNorm, // note: stencil is always uint 126 "D24_S8", false, NumFormat.uNorm, 127 "D32_S8", false, NumFormat.sFloat, 128 ); 129 130 template SurfFmtDesc(string n, bool p, NF...) { 131 alias surfName = n; 132 alias packed = p; 133 alias numFormats = NF; 134 } 135 136 alias surfFmtDescs = parseSurfFmtDescs!(fmtDescriptors); 137 138 139 immutable(FormatDesc)[] fmtDescs; 140 141 shared static this() 142 { 143 import std.format : format; 144 145 FormatDesc[] fd; 146 147 foreach (sfd; surfFmtDescs) { 148 mixin(format("alias SurfT = %s;", sfd.surfName)); 149 foreach (nf; sfd.numFormats) { 150 mixin(format("fd ~= FormatDesc(SurfaceType.%s, NumFormat.%s, %s);\n", 151 sfd.surfName, nf, sfd.packed)); 152 } 153 } 154 155 import std.exception : assumeUnique; 156 fmtDescs = assumeUnique(fd); 157 } 158 159 160 template parseSurfFmtDescs(Descs...) { 161 static if (Descs.length == 0) { 162 alias parseSurfFmtDescs = AliasSeq!(); 163 } 164 else { 165 static assert( is(typeof(Descs[0]) == string) ); 166 167 enum ns = nextString!(Descs[1 .. $]); 168 169 alias parseSurfFmtDescs = AliasSeq!( 170 SurfFmtDesc!(Descs[0 .. ns+1]), 171 parseSurfFmtDescs!(Descs[ns+1..$]) 172 ); 173 } 174 } 175 176 size_t nextString(Args...)() { 177 size_t n=0; 178 foreach(arg; Args) { 179 static if(is(typeof(arg) == string)) { 180 break; 181 } 182 else { 183 ++n; 184 } 185 } 186 return n; 187 } 188 189 template surfaceCode() { 190 import std.meta : staticMap; 191 192 enum surfaceCode = buildCode(); 193 194 alias surfDescs = staticMap!(SurfDesc, surfFmtDescs); 195 196 template SurfDesc(alias fmt) { 197 alias name = fmt.surfName; 198 alias numFormats = fmt.numFormats; 199 enum redBits = name.parseNumAftLetter('R'); 200 enum greenBits = name.parseNumAftLetter('G'); 201 enum blueBits = name.parseNumAftLetter('B'); 202 enum alphaBits = name.parseNumAftLetter('A'); 203 enum stencilBits = name.parseNumAftLetter('S'); 204 enum depthBits = name.parseNumAftLetter('D'); 205 enum sharedExponentBits = name.parseNumAftLetter('E'); 206 enum numComponents = name.parseNumComponents(); 207 208 enum totalBits = redBits + greenBits + blueBits + alphaBits + 209 depthBits + stencilBits + sharedExponentBits + 210 name.parseNumAftLetter('X'); 211 } 212 213 string buildCode() 214 { 215 string codeStr; 216 217 void code(Args...)(string fmt, Args args) { 218 import std.format : format; 219 codeStr ~= format(fmt, args); 220 } 221 222 // enum SurfaceType 223 224 code("/// the type of texture surface\n"); 225 code("public enum SurfaceType {\n"); 226 foreach(sd; surfDescs) { 227 code(" %s,\n", sd.name); 228 } 229 code("}\n\n"); 230 231 // properties 232 void switchProperty (string name)() { 233 code("/// get the %s of an image surface\n", name); 234 code("public @property uint %s(in SurfaceType st) {\n", name); 235 code(" switch(st) {\n"); 236 foreach(sd; surfDescs) { 237 mixin("enum num = sd."~name~";"); 238 static if (num > 0) { 239 code(" case SurfaceType.%s: return %s;\n", sd.name, num); 240 } 241 } 242 code(" default: return 0;\n"); 243 code(" }\n"); 244 code("}\n\n"); 245 } 246 247 switchProperty!("totalBits")(); 248 switchProperty!("numComponents")(); 249 switchProperty!("redBits")(); 250 switchProperty!("greenBits")(); 251 switchProperty!("blueBits")(); 252 switchProperty!("alphaBits")(); 253 switchProperty!("depthBits")(); 254 switchProperty!("stencilBits")(); 255 switchProperty!("sharedExponentBits")(); 256 257 // trait structs 258 foreach(sd; surfDescs) { 259 code("/// static descriptor of surface type %s\n", sd.name); 260 code("public struct %s {\n", sd.name); 261 code(" enum surfaceType = SurfaceType.%s;\n", sd.name); 262 code(" enum totalBits = %s;\n", sd.totalBits); 263 code(" enum redBits = %s;\n", sd.redBits); 264 code(" enum greenBits = %s;\n", sd.greenBits); 265 code(" enum blueBits = %s;\n", sd.blueBits); 266 code(" enum alphaBits = %s;\n", sd.alphaBits); 267 code(" enum depthBits = %s;\n", sd.depthBits); 268 code(" enum stencilBits = %s;\n", sd.stencilBits); 269 code(" enum numComponents = %s;\n", sd.numComponents); 270 code(" alias numFormats = AliasSeq!("); 271 foreach(nf; sd.numFormats) code("NumFormat.%s, ", nf); 272 code(");\n"); 273 // evaluate fmtBaseName for each surface instead of each format to lower ctfe work 274 code( 275 " private enum fmtBaseName = \"%s\";\n", 276 formatBaseName(sd.name, sd.redBits, sd.greenBits, sd.blueBits, sd.alphaBits) 277 ); 278 code("}\n"); 279 } 280 return codeStr; 281 } 282 283 } 284 285 template formatCode() 286 { 287 enum formatCode = buildCode(); 288 289 string buildCode() { 290 string codeStr; 291 292 void code(Args...)(string fmt, Args args) { 293 import std.format : format; 294 codeStr ~= format(fmt, args); 295 } 296 297 code("/// the format of an image\n"); 298 code("public enum Format {\n"); 299 code(" undefined = 0,\n"); 300 int i = 1; 301 foreach (sfd; surfFmtDescs) { 302 mixin("alias SurfT = "~sfd.surfName~";"); 303 foreach (nf; sfd.numFormats) { 304 const name = formatEnumName(SurfT.fmtBaseName, nf); 305 code(" %s = %s,\n", name, i++); 306 } 307 } 308 code("}\n"); 309 310 foreach (sfd; surfFmtDescs) { 311 mixin("alias SurfT = "~sfd.surfName~";"); 312 foreach (nf; sfd.numFormats) { 313 const name = formatTypeName(SurfT.fmtBaseName, nf); 314 code("/// static descriptor of image format %s\n", name); 315 code("public struct %s {\n", name); 316 code(" enum surfaceType = SurfaceType.%s;\n", sfd.surfName); 317 code(" enum numFormat = NumFormat.%s;\n", nf); 318 code(" enum packed = %s;\n", sfd.packed); 319 code("}\n"); 320 } 321 } 322 323 return codeStr; 324 } 325 } 326 327 string formatBaseName(in string name, in uint redBits, in uint greenBits, 328 in uint blueBits, in uint alphaBits) { 329 import std.algorithm : all, filter; 330 import std.array : array; 331 import std.conv : to; 332 import std.range : chain, only; 333 import std.uni : isNumber, toLower; 334 335 const contractNumComps = redBits > 0 && 336 only(greenBits, blueBits, alphaBits) 337 .filter!(n => n != 0) 338 .all!(n => n == redBits); 339 340 string res = name.toLower 341 .filter!(c => c != '_').to!string; 342 if (contractNumComps) { 343 res = res.filter!(c => !c.isNumber).to!string ~ redBits.to!string; 344 } 345 return res; 346 } 347 348 string formatEnumName(string baseName, NumFormat nf) { 349 import std.format : format; 350 return format("%s_%s", baseName, nf); 351 } 352 string formatTypeName(string baseName, NumFormat nf) { 353 import std.format : format; 354 import std.string : capitalize; 355 return format("%s_%s", capitalize(baseName), capitalize(format("%s", nf))); 356 } 357 358 ubyte parseNumComponents(string name) { 359 import std.uni : isAlpha; 360 import std.algorithm : count; 361 362 return cast(ubyte)name.count!((c) { 363 return c.isAlpha && c != '_' && c != 'E'; // shared exponent does not count as a component 364 }); 365 } 366 367 ubyte parseNumAftLetter(string name, char letter) { 368 import std.algorithm : find, countUntil; 369 import std.uni : isNumber; 370 import std.conv : to; 371 372 string n = name.find(letter); 373 if (n.length == 0) return 0; 374 n = n[1 .. $]; 375 auto end = n.countUntil!((c) { 376 return !c.isNumber; 377 }); 378 if (end != -1) n = n[0 .. end]; 379 return n.to!ubyte; 380 }