1 let sizes : Map = array@ 2 "bool" => 4 3 "int" => 4 4 "uint" => 4 5 "float" => 4 6 "vec2" => 8 7 "ivec2" => 8 8 "uvec2" => 8 9 "vec3" => 12 10 "vec4" => 16 11 12 type Binding = 13 val descriptor_type : DescriptorType 14 val descriptor_count : u32 15 val set : u32 16 val binding : u32 17 val name : String 18 var stage_flags : ShaderStageFlags @mut 19 20 def compare (other : Binding) = when 21 binding < other.binding -> Ordering/Less 22 binding > other.binding -> Ordering/Greater 23 descriptor_type as i32 < other.descriptor_type as i32 -> Ordering/Less 24 descriptor_type as i32 > other.descriptor_type as i32 -> Ordering/Greater 25 descriptor_count < other.descriptor_count -> Ordering/Less 26 descriptor_count > other.descriptor_count -> Ordering/Greater 27 else -> Ordering/Equal 28 29 is Compare 30 31 type Metadata = 32 val push_constant_stages : ShaderStageFlags 33 val push_constant_size : u32 34 val vertex_input_mask : u32 35 val fragment_output_mask : u32 36 val bindings : List<Binding> 37 38 let remove_comment (text : String) = 39 if text.contains "//" then 40 let position = text.index_of "//" 41 text.take position 42 else 43 text 44 45 let is_push_constant (text : String) = text.contains "push_constant" 46 47 let get_binding_lines (text : String) = 48 text 49 |> lines 50 |> filter { l -> l.trim_start.starts_with "layout" 51 && (l.contains "uniform" || l.contains " buffer ") } 52 |> map { remove_comment _ } 53 |> filter { not is_push_constant _ } 54 55 let get_lines (text : String) (term : String) = 56 text 57 |> lines 58 |> filter { l -> l.trim_start.starts_with "layout" && l.contains term } 59 |> map { remove_comment _ } 60 61 let get_content (text : String) = 62 let i = text.index_of '(' 63 let j = text.index_of ')' 64 text.substring (i + 1) j 65 66 let get_terms (text : String) = 67 let index = text.index_of ')' 68 let mut s = text.substring (index + 2) text.trim.length 69 if s.ends_with ';' then 70 s = s.drop_last 1 71 72 s.split ' ' |> map { _.trim } 73 74 let get_type (term_types : Map<String, DescriptorType>) (terms : List<String>) = 75 if (terms.contains "image2D" || terms.contains "image2DArray") 76 && terms.contains "writeonly" 77 then 78 return DescriptorType/StorageImage 79 80 for term in terms do 81 if term_types.contains term then 82 return term_types[term] 83 84 if terms.contains "uniform" 85 then DescriptorType/UniformBuffer 86 else DescriptorType/StorageBuffer 87 88 let get_count (text : String) : u32 = 89 if not text.contains '[' then 90 return 1 91 92 let i = text.index_of '[' 93 let j = text.index_of ']' 94 let s = text.substring (i + 1) j 95 96 u32.parse s 97 98 let get_name (text : String) = 99 let s = text.trim_end 100 assert s.last == ';' 101 let index = s.last_index_of ' ' 102 s.substring (index + 1) (s.length - 1) 103 104 let get_binding (term_types : Map<String, DescriptorType>) (text : String) = 105 let content = get_content text 106 let parts = content.split ',' |> map { _.trim } 107 let terms = get_terms text 108 let descriptor_type = get_type term_types terms 109 let descriptor_count = get_count text 110 let name = get_name text 111 let mut set = 0 112 let mut binding = 0 113 114 for part in parts do 115 if part.contains '=' then 116 let index = part.index_of '=' 117 let mut (key, value) = part.split_at index 118 key = key.trim 119 value = value.drop 1 |> trim 120 if key == "set" then 121 set = u32.parse value 122 else if key == "binding" then 123 binding = u32.parse value 124 125 Binding =descriptor_type 126 =descriptor_count 127 =set 128 =binding 129 =name 130 stage_flags = ShaderStageFlags@zero 131 132 let get_int (text : String) (name : String) : i32 = 133 let content = get_content text 134 let parts = content.split ',' |> map { _.trim } 135 for part in parts do 136 if part.contains '=' then 137 let index = part.index_of '=' 138 let mut (key, value) = part.split_at index 139 key = key.trim 140 if key == name then 141 value = value.drop 1 |> trim 142 let result = i32.parse value 143 return result 144 145 fail 146 147 let get_mask (text : String) (term : String) = 148 let mut mask : u32 = 0 149 for s in get_lines text term do 150 let location = get_int s "location" 151 mask |= 1 << location 152 153 mask 154 155 let get_push_constant_lines (text : String) = 156 let lines = text.lines 157 let maybe_layout_line = 158 lines.try_find_index { l -> l.trim_start.starts_with "layout" 159 && l.contains "push_constant" } 160 let list = List.new 161 if maybe_layout_line.is_none then 162 return list 163 164 let layout_line = maybe_layout_line.unwrap 165 let mut index = layout_line + 1 166 while not lines[index].contains '}' do 167 index += 1 168 169 for i = layout_line + 1 until index do 170 let line = lines[i].trim 171 if line.is_not_empty then 172 list.add line 173 174 list 175 176 let get_size (text : String) : u32 = 177 let s = text.split ' ' |> first 178 let count = get_count text 179 sizes[s] * count 180 181 let get_push_constant_size (text : String) : u32 = 182 let lines = get_push_constant_lines text 183 lines.map { get_size _ } |> sum 184 185 let remove_blocks (s : String) = 186 let sb = StringBuilder.new 187 let mut level = 0 188 let mut remove_end_of_line = false 189 let mut is_push_constant = false 190 191 for line in s.lines do 192 if not is_push_constant then 193 is_push_constant = line.contains "push_constant" 194 195 for c in line.chars do 196 if is_push_constant then 197 if c == '}' then 198 is_push_constant = false 199 200 sb.append c 201 else 202 if c == '{' then 203 level += 1 204 205 else if c == '}' then 206 level -= 1 207 if level == 0 then 208 remove_end_of_line = true 209 210 else if level == 0 then 211 sb.append c 212 213 if remove_end_of_line then 214 remove_end_of_line = false 215 216 else if level == 0 then 217 sb.append '\n' 218 219 sb.create 220 221 module metadata 222 223 def get (path : String) = 224 let text = path |> fs/read_text |> remove_blocks 225 226 let term_types = Map<String, DescriptorType>.new 227 term_types.add "sampler" DescriptorType/Sampler 228 add "sampler2D" DescriptorType/CombinedImageSampler 229 add "sampler2DMS" DescriptorType/CombinedImageSampler 230 add "sampler2DArray" DescriptorType/CombinedImageSampler 231 add "image2D" DescriptorType/StorageImage 232 add "image2DArray" DescriptorType/StorageImage 233 add "texture2D" DescriptorType/SampledImage 234 235 let stage_flags = when 236 path.ends_with ".vert" -> ShaderStageFlags/Vertex 237 path.ends_with ".frag" -> ShaderStageFlags/Fragment 238 path.ends_with ".comp" -> ShaderStageFlags/Compute 239 else -> fail 240 241 let bindings = get_binding_lines text |> map { get_binding term_types _ } 242 bindings.sort_by_key { _.binding } 243 for binding in bindings do 244 binding.stage_flags = stage_flags 245 246 let mut vertex_input_mask = 0 247 let mut fragment_output_mask = 0 248 let mut push_constant_stages = ShaderStageFlags@zero 249 250 let push_constant_size = get_push_constant_size text 251 if path.ends_with ".vert" then 252 vertex_input_mask = get_mask text " in " 253 if push_constant_size > 0 then 254 push_constant_stages = ShaderStageFlags/Vertex 255 256 else if path.ends_with ".frag" then 257 fragment_output_mask = get_mask text " out " 258 if push_constant_size > 0 then 259 push_constant_stages = ShaderStageFlags/Fragment 260 261 else if path.ends_with ".comp" then 262 if push_constant_size > 0 then 263 push_constant_stages = ShaderStageFlags/Compute 264 265 else 266 fail 267 268 Metadata =push_constant_stages 269 =push_constant_size 270 =vertex_input_mask 271 =fragment_output_mask 272 =bindings 273