#!/usr/bin/perl # -*- tab-width: 4; -*- # vi: set sw=2 ts=4 expandtab: # Copyright 2019-2020 The Khronos Group Inc. # SPDX-License-Identifier: Apache-2.0 use strict; use warnings; # N.B. 0 arguments, read stdin, write stdout. # 1 argument, read ARGV[0], write stdout. # 2 arguments, read ARGV[0], write ARGV[1]. my $infile = shift @ARGV; my $outfile = shift @ARGV; my $input; if (defined $infile) { open($input, '<', $infile); } else { $input = *STDIN; } if (defined $outfile) { open (my $output, '>', $outfile); select $output; } # Endianness is a parameter to the (non-block-compressed) generators # This doesn't have to be a number: $bigEndian = "myBigEndianFlag" will drop this argument in the generated code my $bigEndian = 0; # Keep track of formats we've seen to avoid duplicates my %foundFormats = (); # Check if we've processed a format. Returns true for extension formats # that have been promoted to core when the core format has been processed. # Usually only _KHR and _EXT extensions are promoted so only those are # checked. sub formatProcessed { my $format =$_[0]; my $format_noext = $format; $format_noext =~ s/_(EXT|KHR)$//; return (exists($foundFormats{$format}) || exists($foundFormats{$format_noext})); } print "/* Copyright 2019-2020 The Khronos Group Inc. */\n"; print "/* SPDX-", "License-Identifier: Apache-2.0 */\n\n"; print "/***************************** Do not edit. *****************************\n"; print " Automatically generated by makevk2dfd.pl.\n"; print " *************************************************************************/\n\n"; # Loop over each line of input # IMPORTANT: Do not use `<>` as that reads all files given on the # command line. while (my $line = <$input>) { # Match any format that starts with a channel description (some number of R, G, B, A or a number) # In PERL, "=~" performs a regex operation on the left argument # m// matches the regular expression my $format = ""; my $suffix = ""; my $channels = ""; my $scheme = ""; # VK_FORMAT_ is a simple 422 format if ($line =~ m/(VK_FORMAT_[RGBX0-9]+_422_UNORM(_4PACK16)?)/) { $format = $1; # Extract the format identifier from the rest of the line if (exists($foundFormats{$format})) { next } $foundFormats{$format} = 1; # VK_FORMAT_G8B8G8R8_422_UNORM, # VK_FORMAT_B8G8R8G8_422_UNORM, # VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, # VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, # VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, # VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, # VK_FORMAT_G16B16G16R16_422_UNORM, # VK_FORMAT_B16G16R16G16_422_UNORM, $format =~ m/VK_FORMAT_([RGBX0-9]+)_422_UNORM(_4PACK16)?/; my $formatChannels = $1; my $bits = ""; my $shiftBits = ""; my $position_xs = ""; my $position_ys = ""; my $last_green = 1; while ($formatChannels =~ m/([RGBX0-9]*)([RGB])([0-9]+)X?([0-9]+)?/) { # Processing from right to left for ease of regex expression my $channel = $2; my $bit = $3; my $padding = $4 ? $4 : ""; if ($channels ne "") { $channels = ", " . $channels; $bits = ", " . $bits; $shiftBits = ", " . $shiftBits; $position_xs = ", " . $position_xs; $position_ys = ", " . $position_ys; } # The default 4:2:2 positioning is currently cosited-even. # Block size is 2x1: Sample positions coded as 1/128 on X and 1/256 on Y # Cosited-even: # Position Sample # X Y X Y # Y0: 0.5, 0.5 -> 64, 128 # U : 0.5, 0.5 -> 64, 128 # Y1: 1.5, 0.5 -> 192, 128 # V : 0.5, 0.5 -> 64, 128 # Midpoint: # Position Sample # X Y X Y # Y0: 0.5, 0.5 -> 64, 128 # U : 1.0, 0.5 -> 128, 128 # Y1: 1.5, 0.5 -> 192, 128 # V : 1.0, 0.5 -> 128, 128 if ($channel eq 'R') { $channels = "2" . $channels; # 2 = Cr / V $position_xs = "64" . $position_xs; $position_ys = "128" . $position_ys; } elsif ($channel eq 'G') { $channels = "0" . $channels; # 0 = Y $position_xs = ($last_green ? "192" : "64") . $position_xs; $position_ys = "128" . $position_ys; $last_green = 0; } elsif ($channel eq 'B') { $channels = "1" . $channels; # 1 = Cb / U $position_xs = "64" . $position_xs; $position_ys = "128" . $position_ys; } $bits = $bit . $bits; if ($padding ne "") { $shiftBits = $padding . $shiftBits; } else { $shiftBits = "0" . $shiftBits; } $formatChannels = $1; } print "case $format: {\n"; print " int channels[] = {$channels}; int bits[] = {$bits}; int shiftBits[] = {$shiftBits};\n"; print " int position_xs[] = {$position_xs}; int position_ys[] = {$position_ys};\n"; print " return createDFD422($bigEndian, 4, bits, shiftBits, channels, position_xs, position_ys, s_UNORM);\n}\n"; } elsif ($line =~ m/VK_FORMAT_[RGBAE0-9]+_/) { # Set $format to the enum identifier ($line =~ m/(VK_FORMAT[A-Z0-9_]+)/); # $ holds the 'th parenthesised entry in the previous regex # (only one in this case) $format = $1; # Skip a format if we've already processed it if (!formatProcessed($format)) { if ($format =~ m/_E5B9G9R9/) { # Special case (assumed little-endian). print "case $format: {\n"; print " int bits[] = {0}; int channels[] = {0};\n"; print " return createDFDPacked(0, 6, bits, channels, s_UFLOAT);\n"; print "}\n"; $foundFormats{$format} = 1; } elsif ($format =~ m/_PACK/) { # Packed formats end "_PACK" - is this format packed? # Extract the channel identifiers and suffix from the packed format $format =~ m/VK_FORMAT_([RGBA0-9]+)_([^_]+)_PACK[0-9]+/; # The first parenthesised bit of regex is the channels ("R5G5B5" etc.) $channels = $1; # The second parenthesised bit of regex is the suffix ("UNORM" etc.) $suffix = $2; # N.B. We don't care about the total bit count (after "PACK" in the name) # Create an empty array of channel names and corresponding bits my @packChannels = (); my @packBits = (); # Loop over channels, separating out the last letter followed by a number while ($channels =~ m/([RGBA0-9]*)([RGBA])([0-9]+)/) { # Add the rightmost channel name to our array push @packChannels, $2; # Add the rightmost channel bits to the array push @packBits, $3; # Truncate the channels string to remove the channel we've processed $channels = $1; } # The number of channels we've found is the array length we've built my $numChannels = @packChannels; # Packed output needs a C block for local variables print "case $format: {\n"; # Start with a null list of channel ids my $channelIds = ""; # Loop over the channel names we've found foreach (@packChannels) { # Use a comma as a separator, so don't add it if the $channelIds string is empty if ($channelIds ne "") { $channelIds .= ","; } # Map the channel names to our internal numbering if ($_ eq 'R') { $channelIds .= "0"; } elsif ($_ eq 'G') { $channelIds .= "1"; } elsif ($_ eq 'B') { $channelIds .= "2"; } elsif ($_ eq 'A') { $channelIds .= "3"; } } # Channel bit counts are easier: we can use join() to make a comma-separated # string of the numbers in the array my $channelBits = join (',', @packBits); # Print initialisation for the two arrays we've created print " int channels[] = {" . $channelIds . "}; "; print "int bits[] = {" . $channelBits . "};\n"; # Now print the function call and close the block print " return createDFDPacked($bigEndian, $numChannels, bits, channels, s_$suffix);\n"; print "}\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; # If we're not packed, do we have a simple RGBA channel size list with a suffix? # N.B. We don't want to pick up downsampled or planar formats, which have more _-separated fields # - "$" matches the end of the format identifier } elsif ($format =~ m/VK_FORMAT_A([0-9]+)_([^_]+)(_[^_]+)?$/) { # Special case for alpha-only formats # Extract our "suffix" (e.g. "UNORM") and bytes per channel $suffix = $2; my $bytesPerChannel = $1 / 8; # Output the case entry print "case $format: return createDFDAlpha($bigEndian, $bytesPerChannel, s_$suffix);\n"; } elsif ($format =~ m/VK_FORMAT_([RGBA0-9]+)_([^_]+)$/) { # Extract our "channels" (e.g. "B8G8R8") and "suffix" (e.g. "UNORM") $channels = $1; $suffix = $2; my $rbswap = 0; my $numChannels = 0; # Non-packed format either start with red (R8G8B8A8) or blue (B8G8R8A8) # We have a special case to notice when we start with blue if (substr($channels,0,1) eq "B") { # Red and blue are swapped (B, G, R, A) - record this # N.B. createDFDUnpacked() just knows this and R,G,B,A channel order, not arbitrary $rbswap = 1; # We know we saw "B" for blue, so we must also have red and green $numChannels = 3; # If we have "A" in the channels as well, we have four channels if ($channels =~ m/A/) { $numChannels = 4; } } else { # We didn't start "B", so we're in conventional order (R, G, B, A) $rbswap = 0; # Check for the channel names present and map that to the number of channels if ($channels =~ m/A/) { $numChannels = 4; } elsif ($channels =~ m/B/) { $numChannels = 3; } elsif ($channels =~ m/G/) { $numChannels = 2; } else { $numChannels = 1; } } # In an unpacked format, all the channels are the same size, so we only need to check one $channels =~ m/R([0-9]+)/; # For unpacked, we need bytes per channel, not bits my $bytesPerChannel = $1 / 8; # Output the case entry print "case $format: return createDFDUnpacked($bigEndian, $numChannels, $bytesPerChannel, $rbswap, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } elsif ($format =~ m/R16G16_SFIXED5_NV/) { # Currently only this 2-channel SFIXED5 format exists so an # explicit match is used. # Output the case entry print "case $format: return createDFDUnpacked($bigEndian, 2, 2, 0, s_SFIXED5);\n"; } } # VK_FORMAT_ is a packed HDR formats with padding # R10X6_UNORM_PACK16 # R10X6G10X6_UNORM_2PACK16 # R10X6G10X6B10X6A10X6_UNORM_4PACK16 # R12X4_UNORM_PACK16 # R12X4G12X4_UNORM_2PACK16 # R12X4G12X4B12X4A12X4_UNORM_4PACK16 } elsif ($line =~ m/(VK_FORMAT_R10X6_UNORM_PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0}; int bits[] = {10}; int shiftBits[] = {6};\n"; print " return createDFDPackedShifted($bigEndian, 1, bits, shiftBits, channels, s_UNORM);\n}\n" } } elsif ($line =~ m/(VK_FORMAT_R10X6G10X6_UNORM_2PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0, 1}; int bits[] = {10, 10}; int shiftBits[] = {6, 6};\n"; print " return createDFDPackedShifted($bigEndian, 2, bits, shiftBits, channels, s_UNORM);\n}\n" } } elsif ($line =~ m/(VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0, 1, 2, 3}; int bits[] = {10, 10, 10, 10}; int shiftBits[] = {6, 6, 6, 6};\n"; print " return createDFDPackedShifted($bigEndian, 4, bits, shiftBits, channels, s_UNORM);\n}\n" } } elsif ($line =~ m/(VK_FORMAT_R12X4_UNORM_PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0}; int bits[] = {12}; int shiftBits[] = {4};\n"; print " return createDFDPackedShifted($bigEndian, 1, bits, shiftBits, channels, s_UNORM);\n}\n" } } elsif ($line =~ m/(VK_FORMAT_R12X4G12X4_UNORM_2PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0, 1}; int bits[] = {12, 12}; int shiftBits[] = {4, 4};\n"; print " return createDFDPackedShifted($bigEndian, 2, bits, shiftBits, channels, s_UNORM);\n}\n" } } elsif ($line =~ m/(VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)/) { $format = $1; # Extract the format identifier from the rest of the line if (!exists($foundFormats{$format})) { $foundFormats{$format} = 1; print "case $format: {\n"; print " int channels[] = {0, 1, 2, 3}; int bits[] = {12, 12, 12, 12}; int shiftBits[] = {4, 4, 4, 4};\n"; print " return createDFDPackedShifted($bigEndian, 4, bits, shiftBits, channels, s_UNORM);\n}\n" } # If we weren't VK_FORMAT_ plus a channel, we might be a compressed # format, that ends "_BLOCK" } elsif ($line =~ m/(VK_FORMAT_[A-Z0-9x_]+_BLOCK(_[A-Z]+)?)/) { # Extract the format identifier from the rest of the line $format = $1; # Skip a format if we've already processed it if (!formatProcessed($format)) { # Special-case BC1_RGB to separate it from BC1_RGBA if ($line =~ m/VK_FORMAT_BC1_RGB_([A-Z]+)_BLOCK/) { # Pull out the suffix ("UNORM" etc.) $suffix = $1; # Output the special case - a 4x4 BC1 block print "case $format: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; # Special case BC1_RGBA (but still extract the suffix with a regex) } elsif ($line =~ m/VK_FORMAT_BC1_RGBA_([A-Z]+)_BLOCK/) { $suffix = $1; print "case $format: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; # All the other BC formats don't have a channel identifier in the name, so we regex match them } elsif ($line =~ m/VK_FORMAT_(BC(?:[2-57]|6H))_([A-Z]+)_BLOCK/) { $scheme = $1; $suffix = $2; print "case $format: return createDFDCompressed(c_$scheme, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; # The ETC and EAC formats have two-part names (ETC2_R8G8B8, EAC_R11 etc.) starting with "E" } elsif ($line =~ m/VK_FORMAT_(E[^_]+_[^_]+)_([A-Z]+)_BLOCK/) { $scheme = $1; $suffix = $2; print "case $format: return createDFDCompressed(c_$scheme, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; # Finally, ASTC and PVRTC, the only cases where the block size is a parameter } elsif ($line =~ m/VK_FORMAT_ASTC_([0-9]+)x([0-9]+)(x([0-9]+))?_([A-Z]+)_BLOCK(_EXT)?/) { my $w = $1; my $h = $2; my $d = $4 ? $4 : '1'; $suffix = $5; print "case $format: return createDFDCompressed(c_ASTC, $w, $h, $d, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } elsif ($line =~ m/VK_FORMAT_PVRTC1_2BPP_([A-Z]+)_BLOCK_IMG/) { # Pull out the suffix ("UNORM" etc.) $suffix = $1; # Output the special case - an 8x4 PVRTC block print "case $format: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } elsif ($line =~ m/VK_FORMAT_PVRTC1_4BPP_([A-Z]+)_BLOCK_IMG/) { # Pull out the suffix ("UNORM" etc.) $suffix = $1; # Output the special case - an 8x4 PVRTC block print "case $format: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } elsif ($line =~ m/VK_FORMAT_PVRTC2_2BPP_([A-Z]+)_BLOCK_IMG/) { # Pull out the suffix ("UNORM" etc.) $suffix = $1; # Output the special case - an 8x4 PVRTC block print "case $format: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } elsif ($line =~ m/VK_FORMAT_PVRTC2_4BPP_([A-Z]+)_BLOCK_IMG/) { # Pull out the suffix ("UNORM" etc.) $suffix = $1; # Output the special case - an 8x4 PVRTC block print "case $format: return createDFDCompressed(c_PVRTC2, 4, 4, 1, s_$suffix);\n"; # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; } } } elsif ($line =~ m/(VK_FORMAT_X8_D24_UNORM_PACK32)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(24,0,4);\n"; } } elsif ($line =~ m/(VK_FORMAT_D32_SFLOAT_S8_UINT)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(32,8,8);\n"; } } elsif ($line =~ m/(VK_FORMAT_D16_UNORM_S8_UINT)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(16,8,4);\n"; } } elsif ($line =~ m/(VK_FORMAT_D24_UNORM_S8_UINT)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(24,8,4);\n"; } } elsif ($line =~ m/(VK_FORMAT_D32_SFLOAT)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(32,0,4);\n"; } } elsif ($line =~ m/(VK_FORMAT_S8_UINT)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(0,8,1);\n"; } } elsif ($line =~ m/(VK_FORMAT_D16_UNORM)/) { # Extract the format identifier from the rest of the line $format = $1; if (!exists($foundFormats{$format})) { # Add the format we've processed to our "done" hash $foundFormats{$format} = 1; print "case $format: return createDFDDepthStencil(16,0,2);\n"; } } # ...and continue to the next line } # vim:ai:ts=4:sts=4:sw=2:expandtab