├── .gitattributes ├── .github └── workflows │ ├── release.yaml │ └── tests.yaml ├── .gitignore ├── bin ├── dxc │ ├── dxc.exe │ ├── dxcompiler.dll │ └── dxil.dll ├── fxc │ ├── d3dcompiler_47.dll │ ├── d3dcsx_47.dll │ └── fxc.exe ├── glsl │ ├── linux │ │ └── validator │ ├── osx │ │ └── validator │ └── win64 │ │ ├── spirv-remap.exe │ │ └── validator.exe ├── lib │ └── libdxcompiler.dylib ├── macos │ ├── dxc │ └── spirv-cross └── nvn │ └── nvn_glslc.exe ├── cgu.py ├── docs └── v2.pmfx_doc ├── examples ├── outputs │ ├── v1_header.h │ ├── v1_info.json │ └── v2_info.json ├── v1 │ ├── v1_multi_examples.pmfx │ └── v1_single_shader.pmfx └── v2 │ ├── v2_examples.hlsl │ ├── v2_examples.pmfx │ └── v2_includes.pmfx ├── jsn.py ├── license.md ├── platform ├── glsl.h ├── hlsl.h ├── metal.h ├── pmfx.h └── pssl.h ├── pmfx.py ├── pmfx_pipeline.py └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.pmfx linguist-language=hlsl 3 | *.pmfx_doc linguist-language=jsonnet -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | setup: 8 | name: create release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: create_release 12 | id: create_release 13 | uses: actions/create-release@v1 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | with: 17 | tag_name: ${{ github.ref }} 18 | release_name: Release ${{ github.ref }} 19 | draft: false 20 | prerelease: false 21 | outputs: 22 | upload_url: ${{ steps.create_release.outputs.upload_url }} 23 | windows: 24 | needs: setup 25 | name: windows 26 | runs-on: windows-latest 27 | steps: 28 | - name: checkout 29 | uses: actions/checkout@v2 30 | - name: pyinstaller 31 | run: "pip install PyInstaller" 32 | - name: build 33 | run: py -3 -c "import pmfx; pmfx.build_executable()" 34 | - name: upload 35 | id: upload-release-asset 36 | uses: actions/upload-release-asset@v1 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | upload_url: ${{ needs.setup.outputs.upload_url }} 41 | asset_path: ./dist/Windows-x64.zip 42 | asset_name: Windows-x64.zip 43 | asset_content_type: application/zip 44 | macos: 45 | needs: setup 46 | name: macos 47 | runs-on: macos-latest 48 | steps: 49 | - name: checkout 50 | uses: actions/checkout@v2 51 | - name: pyinstaller 52 | uses: actions/setup-python@v5 53 | with: 54 | python-version: '3.9' 55 | - run: | 56 | pip3 install PyInstaller 57 | - name: build 58 | run: python3 -c "import pmfx; pmfx.build_executable()" 59 | - name: upload 60 | id: upload-release-asset 61 | uses: actions/upload-release-asset@v1 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | with: 65 | upload_url: ${{ needs.setup.outputs.upload_url }} 66 | asset_path: ./dist/macOS-x64.zip 67 | asset_name: macOS-x64.zip 68 | asset_content_type: application/zip 69 | linux: 70 | needs: setup 71 | name: linux 72 | runs-on: ubuntu-latest 73 | steps: 74 | - name: checkout 75 | uses: actions/checkout@v2 76 | - name: pyinstaller 77 | run: "pip3 install PyInstaller" 78 | - name: build 79 | run: python3 -c "import pmfx; pmfx.build_executable()" 80 | - name: upload 81 | id: upload-release-asset 82 | uses: actions/upload-release-asset@v1 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | with: 86 | upload_url: ${{ needs.setup.outputs.upload_url }} 87 | asset_path: ./dist/Linux-x64.zip 88 | asset_name: Linux-x64.zip 89 | asset_content_type: application/zip 90 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: tests 2 | on: [push] 3 | jobs: 4 | hlsl: 5 | runs-on: windows-latest 6 | steps: 7 | - name: checkout 8 | uses: actions/checkout@v2 9 | - name: hlsl-sm4-examples-v1 10 | run: | 11 | py -3 pmfx.py -v1 -i examples/v1 -shader_platform hlsl -shader_version 4_0 -o build/v1/hlsl_sm4 -t build/temp/v1/hlsl_sm4 -h build/v1/hlsl_sm4/headers 12 | - name: hlsl-sm5-examples-v1 13 | run: | 14 | py -3 pmfx.py -v1 -i examples/v1 -shader_platform hlsl -shader_version 5_0 -o build/v1/hlsl_sm5 -t build/temp/v1/hlsl_sm5 -h build/v1/hlsl_sm5/headers 15 | - name: hlsl-sm6-examples-v2 16 | run: | 17 | py -3 pmfx.py -i examples/v2 -shader_platform hlsl -shader_version 6_0 -o build/v2/hlsl_sm6 -t build/temp/v2/hlsl_sm6 18 | metal: 19 | runs-on: macos-latest 20 | steps: 21 | - name: checkout 22 | uses: actions/checkout@v2 23 | - name: metal-macos-examples-v1 24 | run: | 25 | python3 pmfx.py -v1 -i examples/v1 -metal_sdk macosx -shader_platform metal -shader_version 2.2 -o build/v1/metal_macos -t build/temp/v1/metal_macos -h build/v1/metal_macos/headers 26 | - name: metal-ios-examples-v1 27 | run: | 28 | python3 pmfx.py -v1 -i examples/v1 -metal_sdk iphoneos -shader_platform metal -shader_version 2.2 -o build/v1/metal_ios -t build/temp/v1/metal_ios -h build/v1/metal_ios/headers 29 | glsl: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: checkout 33 | uses: actions/checkout@v2 34 | - name: glsl-450-examples-v1 35 | run: | 36 | python3 pmfx.py -v1 -i examples/v1 -shader_platform glsl -shader_version 450 -o build/v1/glsl_450 -t build/temp/v1/glsl_450 -v_flip -h build/v1/glsl_450/headers 37 | - name: gles-300-examples-v1 38 | run: | 39 | python3 pmfx.py -v1 -i examples/v1 -shader_platform gles -shader_version 300 -o build/v1/gles_300 -t build/temp/v1/gles_300 -v_flip -h build/v1/gles_300/headers 40 | - name: spirv-420-examples-v1 41 | run: | 42 | python3 pmfx.py -v1 -i examples/v1 -shader_platform spirv -shader_version 420 -o build/v1/spirv_420 -t build/temp/v1/spirv_420 -h build/v1/spirv_420/headers -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | ehthumbs.db 3 | Desktop.ini 4 | $RECYCLE.BIN/ 5 | *.cab 6 | *.msi 7 | *.msm 8 | *.msp 9 | *.lnk 10 | .vscode 11 | .DS_Store 12 | .AppleDouble 13 | .LSOverride 14 | **__pycache__** 15 | *.pyc 16 | **.idea/ 17 | ._* 18 | build/ 19 | dist/ 20 | 21 | # Files that might appear in the root of a volume 22 | .DocumentRevisions-V100 23 | .fseventsd 24 | .Spotlight-V100 25 | .TemporaryItems 26 | .Trashes 27 | .VolumeIcon.icns 28 | 29 | # Directories potentially created on remote AFP share 30 | .AppleDB 31 | .AppleDesktop 32 | Network Trash Folder 33 | Temporary Items 34 | .apdisk 35 | build_config_user.json 36 | test_results/ 37 | output/ 38 | dist/ 39 | *.spec -------------------------------------------------------------------------------- /bin/dxc/dxc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/dxc/dxc.exe -------------------------------------------------------------------------------- /bin/dxc/dxcompiler.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/dxc/dxcompiler.dll -------------------------------------------------------------------------------- /bin/dxc/dxil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/dxc/dxil.dll -------------------------------------------------------------------------------- /bin/fxc/d3dcompiler_47.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/fxc/d3dcompiler_47.dll -------------------------------------------------------------------------------- /bin/fxc/d3dcsx_47.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/fxc/d3dcsx_47.dll -------------------------------------------------------------------------------- /bin/fxc/fxc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/fxc/fxc.exe -------------------------------------------------------------------------------- /bin/glsl/linux/validator: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/glsl/linux/validator -------------------------------------------------------------------------------- /bin/glsl/osx/validator: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/glsl/osx/validator -------------------------------------------------------------------------------- /bin/glsl/win64/spirv-remap.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/glsl/win64/spirv-remap.exe -------------------------------------------------------------------------------- /bin/glsl/win64/validator.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/glsl/win64/validator.exe -------------------------------------------------------------------------------- /bin/lib/libdxcompiler.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/lib/libdxcompiler.dylib -------------------------------------------------------------------------------- /bin/macos/dxc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/macos/dxc -------------------------------------------------------------------------------- /bin/macos/spirv-cross: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/macos/spirv-cross -------------------------------------------------------------------------------- /bin/nvn/nvn_glslc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polymonster/pmfx-shader/79df2ad107dc35dd02f6f92ab4b38fcc05ba5fbf/bin/nvn/nvn_glslc.exe -------------------------------------------------------------------------------- /cgu.py: -------------------------------------------------------------------------------- 1 | # cgu - code gen utilities for parsing c-like languages for use in code generation tools 2 | # copyright Alex Dixon 2020: https://github.com/polymonster/cgu/blob/master/license 3 | import re 4 | import json 5 | import sys 6 | 7 | 8 | # make code gen more readable and less fiddly 9 | def in_quotes(string): 10 | return "\"" + string + "\"" 11 | 12 | 13 | # append to string with newline print() style 14 | def src_line(line): 15 | line += "\n" 16 | return line 17 | 18 | 19 | # like c style unsigned wraparound 20 | def us(val): 21 | if val < 0: 22 | val = sys.maxsize + val 23 | return val 24 | 25 | 26 | # remove all single and multi line comments 27 | def remove_comments(source): 28 | lines = source.split("\n") 29 | inside_block = False 30 | conditioned = "" 31 | for line in lines: 32 | if inside_block: 33 | ecpos = line.find("*/") 34 | if ecpos != -1: 35 | inside_block = False 36 | line = line[ecpos+2:] 37 | else: 38 | continue 39 | cpos = line.find("//") 40 | mcpos = line.find("/*") 41 | if cpos != -1: 42 | conditioned += line[:cpos] + "\n" 43 | elif mcpos != -1: 44 | conditioned += line[:mcpos] + "\n" 45 | inside_block = True 46 | else: 47 | conditioned += line + "\n" 48 | return conditioned 49 | 50 | 51 | # generates a nice UI friendly name from, snake_case, camelCase or SCAREY_CASE and strip known prefixes 52 | def display_name(token, title): 53 | prefix = ["m_", "s_", "k_", "g_"] 54 | for p in prefix: 55 | if token.startswith(p): 56 | token = token[len(p):] 57 | break 58 | spaced = "" 59 | for i in range(len(token)): 60 | if i > 0: 61 | if token[i-1].islower() and token[i].isupper(): 62 | spaced += " " 63 | spaced += token[i] 64 | spaced = spaced.replace("_", " ") 65 | if title: 66 | spaced = spaced.title() 67 | else: 68 | spaced = spaced.capitalize() 69 | return spaced.strip() 70 | 71 | 72 | # separate alpha and numeric characters ie. TEXCOORD0 becomes (TEXCOORD, 0) 73 | def separate_alpha_numeric(src): 74 | name = re.sub(r'[0-9]', '', src) 75 | index = re.sub(r'[^0-9]','', src) 76 | if len(index) == 0: 77 | index = 0 78 | index = int(index) 79 | return (name, index) 80 | 81 | 82 | # finds the end of a body of text enclosed between 2 symbols ie. [], {}, <> 83 | def enclose(open_symbol, close_symbol, source, pos): 84 | pos = source.find(open_symbol, pos) 85 | stack = [open_symbol] 86 | pos += 1 87 | while len(stack) > 0 and pos < len(source): 88 | if source[pos] == open_symbol: 89 | stack.append(open_symbol) 90 | if source[pos] == close_symbol: 91 | stack.pop() 92 | pos += 1 93 | return pos 94 | 95 | 96 | # returns the interior range of the string contained within open_symbol and close_symbol 97 | def enclose_start_end(open_symbol, close_symbol, source, pos): 98 | pos = source.find(open_symbol, pos) 99 | if pos == -1: 100 | return (-1, -1) 101 | start_pos = pos+1 102 | stack = [open_symbol] 103 | pos += 1 104 | while len(stack) > 0 and pos < len(source): 105 | if source[pos] == open_symbol: 106 | stack.append(open_symbol) 107 | if source[pos] == close_symbol: 108 | stack.pop() 109 | pos += 1 110 | return (start_pos, pos-1) 111 | 112 | 113 | # parse a string and return the end position in source, taking into account escaped \" quotes 114 | def enclose_string(start, source): 115 | pos = start+1 116 | while True: 117 | pos = source.find("\"", pos) 118 | prev = pos - 1 119 | if prev > 0: 120 | if source[prev] == "\\": 121 | pos = pos + 1 122 | continue 123 | return pos+1 124 | # un-terminated string 125 | print("ERROR: unterminated string") 126 | assert 0 127 | 128 | 129 | # format source with indents 130 | def format_source(source, indent_size): 131 | formatted = "" 132 | lines = source.splitlines() 133 | indent = 0 134 | indents = ["{"] 135 | unindnets = ["}"] 136 | newline = False 137 | for line in lines: 138 | if newline and len(line) > 0 and line[0] != "}": 139 | formatted += "\n" 140 | newline = False 141 | cur_indent = indent 142 | line = line.strip() 143 | attr = line.find("[[") 144 | if len(line) < 1 or attr != -1: 145 | continue 146 | for c in line: 147 | if c in indents: 148 | indent += 1 149 | elif c in unindnets: 150 | indent -= 1 151 | cur_indent = indent 152 | newline = True 153 | formatted += " " * cur_indent * indent_size 154 | formatted += line 155 | formatted += "\n" 156 | return formatted 157 | 158 | 159 | # returns the name of a type.. ie struct , enum 160 | def type_name(type_declaration): 161 | # skip past templates (allow multiple template parameters) 162 | if type_declaration.find("<") != -1: 163 | template_end = enclose("<", ">", type_declaration, 0) 164 | return type_declaration[template_end:].strip().split()[0].strip(":") 165 | else: 166 | # assume the type name is the second token 167 | pos = type_declaration.find("{") 168 | name = type_declaration[:pos].strip().split()[1].strip(":") 169 | return name 170 | 171 | 172 | # get the typename stripping resource_array[21] arrays and return (resource_array, 21) 173 | def type_name_array(type_declaration): 174 | pos = type_declaration.find("[") 175 | if pos != -1: 176 | start, end = enclose_start_end("[", "]", type_declaration, 0) 177 | array_size_str = type_declaration[start:end] 178 | if len(array_size_str) == 0: 179 | array_size = -1 180 | else: 181 | array_size = int(array_size_str) 182 | return (type_declaration[:pos], array_size) 183 | return (type_declaration, None) 184 | 185 | 186 | # tidy source with consistent spaces, remove tabs and comments to make subsequent operations easier 187 | def sanitize_source(source): 188 | # replace tabs with spaces 189 | source = source.replace("\t", " ") 190 | # replace all spaces with single space 191 | source = re.sub(' +', ' ', source) 192 | # remove comments 193 | source = remove_comments(source) 194 | # remove empty lines and strip whitespace 195 | sanitized = "" 196 | for line in source.splitlines(): 197 | line = line.strip() 198 | if len(line) > 0: 199 | sanitized += src_line(line) 200 | return sanitized 201 | 202 | 203 | # finds token in source code 204 | def find_token(token, source): 205 | delimiters = [ 206 | "(", ")", "{", "}", ".", ",", "+", "-", "=", "*", "/", 207 | "&", "|", "~", "\n", "\t", "<", ">", "[", "]", ";", " " 208 | ] 209 | fp = source.find(token) 210 | if fp != -1: 211 | left = False 212 | right = False 213 | # check left 214 | if fp > 0: 215 | for d in delimiters: 216 | if source[fp - 1] == d: 217 | left = True 218 | break 219 | else: 220 | left = True 221 | # check right 222 | ep = fp + len(token) 223 | if fp < ep-1: 224 | for d in delimiters: 225 | if source[ep] == d: 226 | right = True 227 | break 228 | else: 229 | right = True 230 | if left and right: 231 | return fp 232 | # try again 233 | tt = find_token(token, source[fp + len(token):]) 234 | if tt == -1: 235 | return -1 236 | return fp+len(token) + tt 237 | return -1 238 | 239 | 240 | # replace all occurrences of token in source code 241 | def replace_token(token, replace, source): 242 | while True: 243 | pos = find_token(token, source) 244 | if pos == -1: 245 | break 246 | else: 247 | source = source[:pos] + replace + source[pos + len(token):] 248 | pass 249 | return source 250 | 251 | 252 | # find all occurences of token, with their location within source 253 | def find_all_tokens(token, source): 254 | pos = 0 255 | locations = [] 256 | while True: 257 | token_pos = find_token(token, source[pos:]) 258 | if token_pos != -1: 259 | token_pos += pos 260 | locations.append(token_pos) 261 | pos = token_pos + len(token) 262 | else: 263 | break 264 | return locations 265 | 266 | 267 | # return true if source[pos] is a whitespace char 268 | def is_whitespace(source, pos): 269 | whitespace = [' ', '\n', '\t'] 270 | return source[pos] in whitespace 271 | 272 | 273 | # finds the previous non whitespace 274 | def find_prev_non_whitespace(source, pos): 275 | pos -= 1 276 | while pos >= 0 and is_whitespace(source, pos): 277 | pos -= 1 278 | if pos >= 0 and pos < len(source): 279 | return source[pos] 280 | return None 281 | 282 | 283 | # finds the previous non whitespace 284 | def find_next_non_whitespace(source, pos): 285 | pos += 1 286 | while pos < len(source) and is_whitespace(source, pos): 287 | pos += 1 288 | if pos >= 0 and pos < len(source): 289 | return source[pos] 290 | return None 291 | 292 | 293 | # find all string literals in source 294 | def find_string_literals(source): 295 | pos = 0 296 | strings = [] 297 | while True: 298 | pos = source.find("\"", pos) 299 | if pos == -1: 300 | break 301 | end = enclose_string(pos, source) 302 | string = source[pos:end] 303 | strings.append(string) 304 | pos = end+1 305 | return strings 306 | 307 | 308 | # removes string literals and inserts a place holder, returning the ist of string literals and the conditioned source 309 | def placeholder_string_literals(source): 310 | strings = find_string_literals(source) 311 | index = 0 312 | for s in strings: 313 | source = source.replace(s, '""'.format(index)) 314 | index += 1 315 | return strings, source 316 | 317 | 318 | # replace placeholder literals with the strings 319 | def replace_placeholder_string_literals(strings, source): 320 | index = 0 321 | for s in strings: 322 | source = source.replace('""'.format(index), s) 323 | index += 1 324 | return source 325 | 326 | 327 | # get all enum member names and values 328 | def get_enum_members(declaration): 329 | start = declaration.find("{")+1 330 | end = enclose("{", "}", declaration, 0)-1 331 | body = declaration[start:end] 332 | members = body.split(",") 333 | conditioned = [] 334 | for member in members: 335 | if len(member.strip()) > 0: 336 | conditioned.append(member.strip()) 337 | enum_value = 0 338 | enum_members = [] 339 | for member in conditioned: 340 | if member.find("=") != -1: 341 | name_value = member.split("=") 342 | enum_members.append({ 343 | "name": name_value[0], 344 | "value": name_value[1] 345 | }) 346 | else: 347 | enum_members.append({ 348 | "name": member, 349 | "value": enum_value 350 | }) 351 | enum_value += 1 352 | return enum_members 353 | 354 | 355 | # get all struct member names, types, defaults and other metadata 356 | def get_struct_members(declaration): 357 | members = [] 358 | pos = declaration.find("{")+1 359 | while pos != -1: 360 | semantic_pos = declaration.find(":", pos) 361 | end_pos = declaration.find(";", pos) 362 | if end_pos == -1: 363 | break 364 | skip = end_pos 365 | if us(semantic_pos) < end_pos: 366 | end_pos = semantic_pos 367 | bracket_pos = declaration.find("{", pos) 368 | start_pos = pos 369 | if us(bracket_pos) < end_pos: 370 | end_pos = enclose("{", "}", declaration, start_pos) 371 | statement = declaration[start_pos:end_pos] 372 | member_type = "variable" 373 | if statement.find("(") != -1 and statement.find("=") == -1: 374 | member_type = "function" 375 | attrubutes = None 376 | attr_start = statement.find("[[") 377 | if attr_start != -1: 378 | attr_end = statement.find("]]") 379 | attrubutes = statement[attr_start+2:attr_end] 380 | decl = statement.strip() 381 | type_decl = breakdown_type_decl(decl) 382 | semantic = None 383 | if semantic_pos != -1: 384 | semantic = declaration[semantic_pos:skip].strip().strip(";").strip(":").strip() 385 | members.append({ 386 | "member_type": member_type, 387 | "data_type": type_decl["type"].strip(), 388 | "name": type_decl["name"].strip(), 389 | "default": type_decl["default"], 390 | "declaration": decl, 391 | "attributes": attrubutes, 392 | "semantic": semantic 393 | }) 394 | pos = skip + 1 395 | return members 396 | 397 | 398 | def get_members(type_specifier, declaration): 399 | lookup = { 400 | "enum": get_enum_members, 401 | "struct": get_struct_members, 402 | "cbuffer": get_struct_members 403 | } 404 | if type_specifier in lookup: 405 | return lookup[type_specifier](declaration) 406 | return [] 407 | 408 | 409 | # finds the fully qualified scope for a type declaration 410 | def get_type_declaration_scope(source, type_pos): 411 | scope_identifier = [ 412 | "namespace", 413 | "struct", 414 | "class" 415 | ] 416 | pos = 0 417 | scopes = [] 418 | while True: 419 | scope_start, i = find_first(source, scope_identifier, pos) 420 | if scope_start != -1: 421 | scp = source.find(";", scope_start) 422 | pp = source.find("{", scope_start) 423 | if us(pp) > scp: 424 | if scp == -1: 425 | return scopes 426 | pos = scp 427 | continue 428 | scope_end = enclose("{", "}", source, scope_start) 429 | if scope_end > type_pos > scope_start: 430 | scope_name = type_name(source[scope_start:scope_end]) 431 | scopes.append({ 432 | "type": i, 433 | "name": scope_name 434 | }) 435 | pos = source.find("{", scope_start) + 1 436 | else: 437 | pos = scope_end 438 | else: 439 | return scopes 440 | if pos > type_pos: 441 | return scopes 442 | return [] 443 | 444 | 445 | # return list of any typedefs for a particular type 446 | def find_typedefs(fully_qualified_name, source): 447 | pos = 0 448 | typedefs = [] 449 | typedef_names = [] 450 | while True: 451 | start_pos = find_token("typedef", source[pos:]) 452 | if start_pos != -1: 453 | start_pos += pos 454 | end_pos = start_pos + source[start_pos:].find(";") 455 | typedef = source[start_pos:end_pos] 456 | q = find_token(fully_qualified_name, typedef) 457 | if q != -1: 458 | typedefs.append(source[start_pos:end_pos]) 459 | name = typedef[q+len(fully_qualified_name):end_pos].strip() 460 | typedef_names.append(name) 461 | pos = end_pos 462 | else: 463 | break 464 | return typedefs, typedef_names 465 | 466 | 467 | # return list of any typedefs for a particular type 468 | def find_typedef_decls(source): 469 | pos = 0 470 | typedef_decls = [] 471 | while True: 472 | start_pos = find_token("typedef", source[pos:]) 473 | if start_pos != -1: 474 | start_pos += pos 475 | end_pos = start_pos + source[start_pos:].find(";") 476 | typedef_decls.append(source[start_pos:end_pos]) 477 | pos = end_pos 478 | else: 479 | break 480 | return typedef_decls 481 | 482 | 483 | def find_type_attributes(source, type_pos): 484 | delimiters = [";", "}"] 485 | attr = source[:type_pos].rfind("[[") 486 | first_d = us(-1) 487 | for d in delimiters: 488 | first_d = min(us(source[:type_pos].rfind(d)), first_d) 489 | if first_d == us(-1): 490 | first_d = -1 491 | if attr > first_d: 492 | attr_end = source[attr:].find("]]") 493 | return source[attr+2:attr+attr_end] 494 | return None 495 | 496 | 497 | # extract the type of the template parameter ie. StructuredBuffer (template_type = float4) 498 | def get_template_type(type_decl): 499 | start, end = enclose_start_end("<", ">", type_decl, 0) 500 | if start != -1 and end != -1: 501 | return type_decl[start:end] 502 | return None 503 | 504 | 505 | # finds all type declarations.. ie struct, enum. returning them in dict with name, and code 506 | def find_type_declarations(type_specifier, source): 507 | results = [] 508 | names = [] 509 | pos = 0 510 | while True: 511 | start_pos = find_token(type_specifier, source[pos:]) 512 | if start_pos != -1: 513 | start_pos += pos 514 | # handle forward decl 515 | fp, tok = find_first(source, ["{", ";"], start_pos) 516 | forward = False 517 | members = [] 518 | if tok == ";": 519 | declaration = source[start_pos:fp] 520 | forward = True 521 | end_pos = fp 522 | else: 523 | end_pos = enclose("{", "}", source, start_pos) 524 | declaration = source[start_pos:end_pos] 525 | members = get_members(type_specifier, declaration) 526 | scope = get_type_declaration_scope(source, start_pos) 527 | name = type_name(declaration) 528 | name, array_size = type_name_array(name) 529 | qualified_name = "" 530 | for s in scope: 531 | if s["type"] == "namespace": 532 | qualified_name += s["name"] + "::" 533 | qualified_name += name 534 | typedefs, typedef_names = find_typedefs(qualified_name, source) 535 | attributes = find_type_attributes(source, start_pos) 536 | decl = declaration.strip() 537 | results.append({ 538 | "type": type_specifier, 539 | "name": name, 540 | "qualified_name": qualified_name, 541 | "declaration": declaration.strip(), 542 | "position": pos, 543 | "members": members, 544 | "scope": scope, 545 | "typedefs": typedefs, 546 | "typedef_names": typedef_names, 547 | "attributes": attributes, 548 | "forward_declaration": forward, 549 | "template_type": get_template_type(decl), 550 | "array_size": array_size 551 | }) 552 | pos = end_pos+1 553 | else: 554 | break 555 | for r in results: 556 | names.append(r["name"]) 557 | names.append(r["qualified_name"]) 558 | for name in r["typedef_names"]: 559 | names.append(name) 560 | return results, names 561 | 562 | 563 | # find include statements 564 | def find_include_statements(source): 565 | includes = [] 566 | for line in source.splitlines(): 567 | if line.strip().startswith("#include"): 568 | includes.append(line) 569 | return includes 570 | 571 | 572 | # find pragma statements 573 | def find_pragma_statements(source): 574 | pragmas = [] 575 | for line in source.splitlines(): 576 | if line.strip().startswith("#pragma"): 577 | pragmas.append(line) 578 | return pragmas 579 | 580 | 581 | # finds the next token ignoring white space 582 | def next_token(source, start): 583 | white_space = [" ", "\n"] 584 | pos = start+1 585 | while True: 586 | if source[pos] in white_space: 587 | pos += 1 588 | else: 589 | return source[pos] 590 | if pos >= len(source): 591 | break 592 | return None 593 | 594 | 595 | # find first string 596 | def find_first(source, tokens, start): 597 | first = sys.maxsize 598 | first_token = "" 599 | for t in tokens: 600 | i = source.find(t, start) 601 | if first > i > -1: 602 | first = i 603 | first_token = t 604 | return first, first_token 605 | 606 | 607 | # find first token 608 | def find_first_token(source, tokens, start): 609 | first = sys.maxsize 610 | first_token = "" 611 | for t in tokens: 612 | i = find_token(t, source) 613 | if first > i > -1: 614 | first = i 615 | first_token = t 616 | return first, first_token 617 | 618 | 619 | def arg_list(args): 620 | args = args.replace("\n", " ") 621 | args = re.sub(' +', ' ', args) 622 | pos = 0 623 | a = [] 624 | while True: 625 | # find comma separators 626 | cp = args.find(",", pos) 627 | ep = args.find("=", pos) 628 | if cp == -1: 629 | # add final arg 630 | aa = args[pos:].strip() 631 | if len(aa) > 0: 632 | a.append(args[pos:].strip()) 633 | break 634 | if -1 < ep < cp: 635 | # handle function with default 636 | end, tok = find_first(args, [",", "{", "("], ep) 637 | if tok == ",": 638 | a.append(args[pos:end].strip()) 639 | pos = end+1 640 | continue 641 | elif tok == "(": 642 | end = enclose(tok, ")", args, end) 643 | else: 644 | end = enclose(tok, "}", args, end) 645 | a.append(args[pos:end]) 646 | end = args.find(",", end) 647 | if end == -1: 648 | end = len(args) 649 | pos = end+1 650 | else: 651 | # plain arg 652 | a.append(args[pos:cp].strip()) 653 | pos = cp+1 654 | return a 655 | 656 | 657 | # breakdown a type decl ie. StructName variable_name = {}, into data_type: StructName, name: variable_name, default = {} 658 | def breakdown_type_decl(a): 659 | decl = a 660 | # extract default 661 | dp = a.find("=") 662 | default = None 663 | if dp != -1: 664 | default = a[dp + 1:].strip() 665 | decl = a[:dp - 1] 666 | sp = decl.find(":") 667 | # extract semantic 668 | sp = decl.find(":") 669 | semantic = None 670 | if sp != -1: 671 | semantic = decl[sp + 1:].strip() 672 | decl = decl[:sp - 1] 673 | name_pos = decl.rfind(" ") 674 | name_pos = decl.rfind(" ") 675 | return { 676 | "type": decl[:name_pos].strip(), 677 | "name": decl[name_pos:].strip(), 678 | "default": default, 679 | "semantic": semantic 680 | } 681 | 682 | 683 | # break down arg decl (int a, int b, int c = 0) into contextual info 684 | def breakdown_function_args(args): 685 | args = arg_list(args) 686 | args_context = [] 687 | for a in args: 688 | a = a.strip() 689 | if len(a) == "": 690 | continue 691 | if a == "...": 692 | # va list 693 | args_context.append({ 694 | "type": "...", 695 | "name": "va_list", 696 | "default": None, 697 | "semantic": None 698 | }) 699 | else: 700 | args_context.append(breakdown_type_decl(a)) 701 | return args_context 702 | 703 | 704 | # parse return type of function and split out any template or inline 705 | def parse_return_type(statement): 706 | template = None 707 | inline = None 708 | rt = statement.strip("}") 709 | rt = rt.strip() 710 | tp = rt.find("template") 711 | if tp != -1: 712 | etp = enclose("<", ">", rt, tp) 713 | template = rt[tp:etp] 714 | rt = rt[etp:] 715 | ip = rt.find("inline") 716 | if ip != -1: 717 | ipe = ip+len("inline") 718 | inline = rt[:ipe] 719 | rt = rt[ipe:] 720 | return rt, template, inline 721 | 722 | 723 | # prepends attributes to soiurce coe of functions, structs etc 724 | def combine_src_attributes(attribs, src): 725 | if len(attribs) == 0: 726 | return src 727 | out = "[" 728 | for attrib in attribs: 729 | out += attrib 730 | out += "]\n" + src 731 | return out 732 | 733 | 734 | # find functions 735 | def find_functions(source): 736 | # look for parenthesis to identiy function decls 737 | functions = [] 738 | function_names = [] 739 | pos = 0 740 | attributes = [] 741 | while True: 742 | statement_end, statement_token = find_first(source, [";", "{", "}"], pos) 743 | if statement_end == -1: 744 | break 745 | statement = source[pos:statement_end].strip() 746 | src_start = pos 747 | pp = statement.find("(") 748 | ep = statement.find("=") 749 | skip = statement_end+1 750 | 751 | # check for attributes 752 | if len(statement) > 0: 753 | if statement[0] == "[": 754 | start, end = enclose_start_end("[", "]", statement, 0) 755 | attributes.append(statement[start:end]) 756 | pos += start + end + 1 757 | continue 758 | 759 | if pp != -1: 760 | next = next_token(statement, pp) 761 | if (ep == -1 or pp < ep) and next != "*": 762 | # this a function decl, so break down into context 763 | body = "" 764 | body_end = skip 765 | if statement_token == "{": 766 | body_end = enclose("{", "}", source, statement_end-1) 767 | body = source[statement_end-1:body_end] 768 | statement_end = body_end+1 769 | skip = body_end 770 | args_end = enclose("(", ")", statement, pp)-1 771 | name_pos = statement[:pp].rfind(" ") 772 | name = statement[name_pos+1:pp].strip() 773 | name_unscoped = name.rfind(":") 774 | qualifier = "" 775 | if name_unscoped != -1: 776 | qualifier = name[:name_unscoped-1] 777 | name = name[name_unscoped+1:] 778 | return_type, template, inline = parse_return_type(statement[:name_pos]) 779 | args = breakdown_function_args(statement[pp+1:args_end]) 780 | scope = get_type_declaration_scope(source, pos) 781 | functions.append({ 782 | "name": name, 783 | "qualifier": qualifier, 784 | "return_type": return_type, 785 | "args": args, 786 | "scope": scope, 787 | "body": body, 788 | "template": template, 789 | "inline": inline, 790 | "source": combine_src_attributes(attributes, source[src_start:body_end].strip()).strip(), 791 | "attributes": list(attributes) 792 | }) 793 | attributes.clear() 794 | function_names.append(name) 795 | pos = skip 796 | if pos > len(source): 797 | break 798 | return functions, function_names 799 | 800 | 801 | # returns prototype with no names, ie (int, int, float), from a function context 802 | def get_funtion_prototype(func): 803 | args = "" 804 | num_args = len(func["args"]) 805 | for a in range(0, num_args): 806 | args += func["args"][a]["type"] 807 | if a < num_args - 1: 808 | args += ", " 809 | if num_args == 0: 810 | args = "void" 811 | return "(" + args + ")" 812 | 813 | 814 | # find the line, column position within source 815 | def position_to_line_column(source, position): 816 | if position < 0 or position > len(source): 817 | raise ValueError("position out of bounds") 818 | 819 | # split the string into lines 820 | lines = source.splitlines(keepends=True) 821 | 822 | # find the line and column 823 | current_pos = 0 824 | for line_number, line in enumerate(lines, start=1): 825 | line_length = len(line) 826 | if current_pos + line_length > position: 827 | # Found the line 828 | column = position - current_pos + 1 # Convert to 1-based 829 | return line_number, column 830 | current_pos += line_length 831 | 832 | # If we exit the loop, something went wrong 833 | raise ValueError("position not found in string") 834 | 835 | 836 | # main function for scope 837 | def test(): 838 | # read source from file 839 | source = open("test.h", "r").read() 840 | 841 | # sanitize source to make further ops simpler 842 | source = sanitize_source(source) 843 | print("--------------------------------------------------------------------------------") 844 | print("sanitize source ----------------------------------------------------------------") 845 | print("--------------------------------------------------------------------------------") 846 | print(source) 847 | 848 | # find all include statements, fromsanitized source to ignore commented out ones 849 | includes = find_include_statements(source) 850 | print("--------------------------------------------------------------------------------") 851 | print("find includes ------------------------------------------------------------------") 852 | print("--------------------------------------------------------------------------------") 853 | print(includes) 854 | 855 | # find string literals within source 856 | print("--------------------------------------------------------------------------------") 857 | print("find strings literals ----------------------------------------------------------") 858 | print("--------------------------------------------------------------------------------") 859 | strings = find_string_literals(source) 860 | print(strings) 861 | 862 | # remove string literals to avoid conflicts when parsing 863 | print("--------------------------------------------------------------------------------") 864 | print("placeholder literals -----------------------------------------------------------") 865 | print("--------------------------------------------------------------------------------") 866 | strings, source = placeholder_string_literals(source) 867 | print(format_source(source, 4)) 868 | 869 | # find single token 870 | print("--------------------------------------------------------------------------------") 871 | print("find token ---------------------------------------------------------------------") 872 | print("--------------------------------------------------------------------------------") 873 | token = "SOME_TOKEN" 874 | token_pos = find_token(token, source) 875 | print("token pos: {}".format(token_pos)) 876 | print("token:" + source[token_pos:token_pos+len(token)]) 877 | 878 | print("--------------------------------------------------------------------------------") 879 | print("find all tokens ----------------------------------------------------------------") 880 | print("--------------------------------------------------------------------------------") 881 | token = "int" 882 | token_locations = find_all_tokens(token, source) 883 | for loc in token_locations: 884 | print("{}: ".format(loc) + source[loc:loc+10] + "...") 885 | 886 | # find structs 887 | print("--------------------------------------------------------------------------------") 888 | print("find structs -------------------------------------------------------------------") 889 | print("--------------------------------------------------------------------------------") 890 | structs, struct_names = find_type_declarations("struct", source) 891 | print(struct_names) 892 | print(json.dumps(structs, indent=4)) 893 | 894 | # find enums 895 | print("--------------------------------------------------------------------------------") 896 | print("find enums ---------------------------------------------------------------------") 897 | print("--------------------------------------------------------------------------------") 898 | enums, enum_names = find_type_declarations("enum", source) 899 | print(enum_names) 900 | print(json.dumps(enums, indent=4)) 901 | 902 | # find free functions 903 | print("--------------------------------------------------------------------------------") 904 | print("find functions -----------------------------------------------------------------") 905 | print("--------------------------------------------------------------------------------") 906 | functions, function_names = find_functions(source) 907 | print(function_names) 908 | print(json.dumps(functions, indent=4)) 909 | 910 | # replace placeholder literals 911 | print("--------------------------------------------------------------------------------") 912 | print("replace placeholder literals ---------------------------------------------------") 913 | print("--------------------------------------------------------------------------------") 914 | source = replace_placeholder_string_literals(strings, source) 915 | print(format_source(source, 4)) 916 | 917 | # display names 918 | print("--------------------------------------------------------------------------------") 919 | print(" display name ------------------------------------------------------------------") 920 | print("--------------------------------------------------------------------------------") 921 | print(display_name("m_snake_case_variable", False)) 922 | print(display_name("m_camelCaseVariable", False)) 923 | print(display_name("SCAREY_CASE_DEFINE", True)) 924 | 925 | 926 | # entry 927 | if __name__ == "__main__": 928 | test() 929 | -------------------------------------------------------------------------------- /docs/v2.pmfx_doc: -------------------------------------------------------------------------------- 1 | /// this is the full schema for pmfx -v2 specification 2 | /// members are listed with their default values, possible values for enums and type ranges are listed in the comments. 3 | 4 | /// you can import other .pmfx files, and utilise jsn merging and inheritence 5 | import other_files.pmfx 6 | 7 | /// top level structure, you can omit any sub objects if you do not need them. 8 | /// optional (null) members can be omited entirely 9 | { 10 | /// list of include files 11 | include: [ 12 | /// include hlsl source code 13 | "shader_file.hlsl" 14 | ] 15 | pipelines: { 16 | /// key-value objects of `pipeline` 17 | name: { 18 | // .. 19 | } 20 | } 21 | depth_stencil_states: { 22 | /// key-value objects of `depth_stencil_state` 23 | name: { 24 | // .. 25 | } 26 | } 27 | sampler_states: { 28 | /// key-value objects of `sampler_state` 29 | name: { 30 | // .. 31 | } 32 | } 33 | render_target_blend_states: { 34 | /// key-value objects of `render_target_blend_state` 35 | name: { 36 | // .. 37 | } 38 | } 39 | blend_states: { 40 | /// key-value objects of `blend_state` 41 | name: { 42 | // .. 43 | } 44 | } 45 | textures: { 46 | name: { 47 | // .. 48 | } 49 | } 50 | views: { 51 | name: { 52 | // .. 53 | } 54 | } 55 | render_graphs: { 56 | name: { 57 | // .. 58 | } 59 | } 60 | } 61 | 62 | /// full pipeline-state object specification 63 | pipeline: { 64 | /// optional - this will be auto-generated, but you can supply extra information to override 65 | vertex_layout: { 66 | // use the name of the input structure to set settings on an entire set of vertex input 67 | vs_input_instance: { 68 | // .. vertex_layout options (see below) 69 | } 70 | // use the name of a single arg or struct member to set individually 71 | normal: { 72 | // .. vertex_layout options (see below) 73 | } 74 | } 75 | /// auto-generated (based on shader usage and register bindings) 76 | pipeline_layout: { 77 | bindings: { 78 | // .. autogenerated 79 | } 80 | // add push constants in the pipeline to make buffer usages be visible as push constants instead of descriptors 81 | push_constants: { 82 | // .. autogenerated 83 | } 84 | static_samplers: { 85 | // .. autogenerated 86 | } 87 | } 88 | /// specify the name of a shader entry point inside .hlsl files to use as a vertex shader 89 | /// only makes sense on it's own in combination with ps. cs must be null 90 | /// type: optional (string or null) 91 | vs: null 92 | /// specify the name of a shader entry point inside .hlsl files to use as a pixel shader 93 | /// only makes sense in combination with vs, vs must be non-null and cs must be null 94 | /// type: optional (string or null) 95 | ps: null 96 | /// specify the name of a shader entry point inside .hlsl files to use as a compute shader 97 | /// only makes sense in on it's own (vs and ps must be null) 98 | /// type: optional (string or null) 99 | cs: null 100 | // specify a named depth_stencil_state 101 | // type: optional (string or null) if null or omitted defaults for depth_stencil_state are used. 102 | depth_stencil_state: null 103 | /// specify a named blend_state or a render_target_blend_state. 104 | /// if a render_target_blend_state is specified, independent_blend_enabled wil be inferred from the usage 105 | /// alpha_to_coverage_enabled: False and the render_target will consist of a single rneder target referecing the 106 | /// named render_target_blend_state. 107 | /// type: optional (string or null) if null or omitted defaults for blend_state are used. 108 | blend_state: null 109 | /// specify a named raster_state 110 | /// type: optional (string or null) if null or omitted defaults for raster_state are used. 111 | raster_state: null 112 | /// specify the name of cbuffers inside .hlsl shader code which should be used as push constants in the pipeline 113 | /// type: array [string] 114 | push_constants: [] 115 | /// key-value object of `static_samplers` 116 | static_samplers: null 117 | /// specify the mesh topology to use for this pipeline 118 | /// type: enum [Undefined, PointList, LineList, LineStrip, TriangleList, TriangleStrip, LineListAdj, LineStripAdj, TriangleListAdj, TriangleStripAdj, PatchList] 119 | topology: TriangleList 120 | /// specify a sample mask for which fragments to write in MSAA scenarios 121 | /// type: u32 122 | sample_mask: 0xffffffff 123 | } 124 | 125 | /// vertex layout, the following settings are applied to all members of a vertex input struct 126 | /// a vertex layout will be generated automatically for all inputs, use this to control instance and step rate 127 | vertex_layout: { 128 | /// slot to bind the buffer on 129 | /// type: in 130 | input_slot: 2 131 | /// specifies the type of input 132 | /// type: enum [PerVertex, PerInstance] 133 | input_slot_class: PerVertex 134 | /// the rate at which to step instance elements in this vertex buffer, 135 | /// not: default for vertex will be 0, for instances it will be 1 136 | /// type: int 137 | step_rate: 0 138 | } 139 | 140 | /// specify named sampler states to use as static samplers on this pipeline 141 | static_samplers: { 142 | /// sampler name (in hlsl = sampler0): sampler state name in pmfx 143 | sampler0: wrap_linear 144 | } 145 | 146 | /// depth stencil state. 147 | depth_stencil_state: { 148 | /// specifies whether to enable depth testing. set this to `true` to enable depth testing. 149 | /// type: bool 150 | depth_enabled: false 151 | /// identifies a portion of the depth-stencil buffer that can be modified by depth data. 152 | /// possible values: [Zero, All] 153 | depth_write_mask: Zero 154 | /// identifies a function that compares depth data against existing depth data. 155 | /// type: enum [Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always] 156 | depth_func: Always 157 | /// specifies whether to enable stencil testing. set this to `true` to enable stencil testing. 158 | /// type: bool 159 | stencil_enabled: false 160 | /// identify a portion of the depth-stencil buffer for reading stencil data. 161 | /// type: u8 int [0-255] 162 | stencil_read_mask: 0 163 | /// identify a portion of the depth-stencil buffer for writing stencil data. 164 | /// type: u8 int [0-255] 165 | stencil_write_mask: 0 166 | /// structure that describes how to use the results of the depth test and the stencil test for pixels 167 | /// whose surface normal is facing towards the camera. 168 | front_face: { 169 | /// value that identifies the stencil operation to perform when stencil testing fails. 170 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 171 | fail: Keep 172 | /// value that identifies the stencil operation to perform when stencil testing passes and depth testing fails. 173 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 174 | depth_fail: Keep 175 | /// typed value that identifies the stencil operation to perform when stencil testing and depth testing both pass. 176 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 177 | pass: Keep 178 | /// typed value that identifies the function that compares stencil data against existing stencil data. 179 | /// type: enum [Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always] 180 | func: Always 181 | } 182 | /// structure that describes how to use the results of the depth test and the stencil test for pixels 183 | /// whose surface normal is facing away from the camera. 184 | back_face: { 185 | /// value that identifies the stencil operation to perform when stencil testing fails. 186 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 187 | fail: Keep 188 | /// value that identifies the stencil operation to perform when stencil testing passes and depth testing fails. 189 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 190 | depth_fail: Keep 191 | /// typed value that identifies the stencil operation to perform when stencil testing and depth testing both pass. 192 | /// type: enum [Zero, Replace, IncrSat, DecrSat, Invert, Incr, Decr] 193 | pass: Keep 194 | /// typed value that identifies the function that compares stencil data against existing stencil data. 195 | /// type: enum [Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always] 196 | func: Always 197 | } 198 | } 199 | 200 | /// sampler state 201 | sampler_states: { 202 | /// value that specifies the filtering method to use when sampling a texture. 203 | /// type: enum [Point, Linear, Anisotropic] 204 | filter: Linear 205 | /// value that specifies the method to use for resolving a u texture coordinate that is outside the 0 to 1 range. 206 | /// type: enum [Wrap, Mirror, Clamp, Border, MirrorOnce] 207 | address_u: Wrap 208 | /// value that specifies the method to use for resolving a v texture coordinate that is outside the 0 to 1 range. 209 | /// type: enum [Wrap, Mirror, Clamp, Border, MirrorOnce] 210 | address_v: Wrap 211 | /// value that specifies the method to use for resolving a w texture coordinate that is outside the 0 to 1 range. 212 | /// /// type: enum [Wrap, Mirror, Clamp, Border, MirrorOnce] 213 | address_w: Wrap 214 | /// value that specifies a function that compares sampled data against existing sampled data. 215 | /// type: enum [Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always] 216 | comparison: None 217 | /// RGBA border color to use if `Border` is specified for address_u, address_v, or address_w. 218 | /// type: optional u32 int [None, 0x0000 - 0xFFFFFF] 219 | border_colour: None 220 | /// offset from the calculated mipmap level. 221 | /// type: float 222 | mip_lod_bias: 0.0 223 | /// lower end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level 224 | /// and any level higher than that is less detailed. 225 | /// type: int [1 - 16] 226 | max_aniso: 1 227 | /// lower end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level 228 | /// and any level higher than that is less detailed. 229 | /// type: float 230 | min_lod: 0.0 231 | /// upper end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level 232 | /// and any level higher than that is less detailed. 233 | /// type: float 234 | max_lod: float.max 235 | } 236 | 237 | /// specify blend state for a single render target 238 | render_target_blend_state: { 239 | /// specifies whether to enable blending. set this to `true` to enable blending. 240 | /// type: bool 241 | blend_enabled: false, 242 | /// specifies whether to enable a logical operation. set to `true` to enable a logical operation. 243 | /// **note you cannot enable both blend_enabled and logic_op_enabled 244 | /// type: bool 245 | logic_op_enabled: false, 246 | /// value that specifies the operation to perform on the rgb value that the pixel shader outputs. 247 | /// the `blend_op` member defines how to combine the `src_blend` and `dst_blend` operations. 248 | /// type: enum [Zero, One, SrcColour, InvSrcColour, SrcAlpha, InvSrcAlpha, DstAlpha, InvDstAlpha, DstColour, InvDstColour, SrcAlphaSat, BlendFactor, InvBlendFactor, Src1Colour, InvSrc1Colour, Src1Alpha, InvSrc1Alpha] 249 | src_blend: Zero, 250 | /// value that specifies the operation to perform on the rgb value that the pixel shader outputs. 251 | /// the `blend_op` member defines how to combine the `src_blend` and `dst_blend` operations. 252 | /// type: enum [Zero, One, SrcColour, InvSrcColour, SrcAlpha, InvSrcAlpha, DstAlpha, InvDstAlpha, DstColour, InvDstColour, SrcAlphaSat, BlendFactor, InvBlendFactor, Src1Colour, InvSrc1Colour, Src1Alpha, InvSrc1Alpha] 253 | dst_blend: Zero, 254 | /// value that defines how to combine the `src_blend` and `dst_blend` operations. 255 | /// type: enum [Add, Subtract, RevSubtract, Min, Max] 256 | blend_op: Add, 257 | /// value that specifies the operation to perform on the alpha value that the pixel shader outputs. 258 | /// the `blend_op_alpha` member defines how to combine the `src_blend_alpha` and `dst_blend_alpha` operations. 259 | /// type: enum [Zero, One, SrcAlpha, InvSrcAlpha, DstAlpha, InvDstAlpha, SrcAlphaSat, BlendFactor, InvBlendFactor, Src1Alpha, InvSrc1Alpha] 260 | src_blend_alpha: Zero, 261 | /// value that specifies the operation to perform on the alpha value that the pixel shader outputs. 262 | /// the `blend_op_alpha` member defines how to combine the `src_blend_alpha` and `dst_blend_alpha` operations. 263 | /// type: enum [Zero, One, SrcAlpha, InvSrcAlpha, DstAlpha, InvDstAlpha, SrcAlphaSat, BlendFactor, InvBlendFactor, Src1Alpha, InvSrc1Alpha] 264 | dst_blend_alpha: Zero, 265 | /// value that defines how to combine the `src_blend_alpha` and `dst_blend_alpha` operations. 266 | /// type: enum [Add, Subtract, RevSubtract, Min, Max] 267 | blend_op_alpha: Add, 268 | /// value that specifies the logical operation to configure for the render target. 269 | /// type: enum [Clear, Set, Copy, CopyInverted, NoOp, Invert, And, Nand, Or, Nor, Xor, Equiv, AndReverse, AndInverted, OrReverse, OrInverted] 270 | logic_op: Clear, 271 | /// value controls which channels of the render target to write. the chars RGBA are converted to a 4-bit integer mask. 272 | /// type: enum [R, G, B, A, None, All] 273 | write_mask: RGBA 274 | } 275 | 276 | /// specify blend state for a multiple render targets 277 | blend_state: { 278 | /// specify whether to enable alpha to coverage or not, set to `true` to enable. 279 | /// type: bool 280 | alpha_to_coverage_enabled: false 281 | /// specify whether to enable independent blending set to `true` to enable. 282 | /// if this is true it enables `src_blend_alpha`, `dst_blend_alpha` and `blend_op_alpha` on the `render_target_blend_state` 283 | /// type: bool 284 | independent_blend_enabled: false 285 | /// an array of `render_target_blend_state` to supply different blending controls for multiple render target configurations. 286 | render_target: [] 287 | } 288 | 289 | /// specify rasterizer state 290 | raster_states: { 291 | /// value that specifies the fill mode to use when rendering. 292 | /// type: enum [Wireframe, Solid] 293 | fill_mode: Solid 294 | /// value that specifies that triangles facing the specified direction are not drawn. 295 | /// type: enum [None, Front, Back] 296 | cull_mode: None 297 | /// determines if a triangle is front- or back-facing. If this member is `true`, 298 | /// a triangle will be considered front-facing if its vertices are counter-clockwise 299 | /// on the render target and considered back-facing if they are clockwise. 300 | /// type: bool 301 | front_ccw: false 302 | /// depth value added to a given pixel. 303 | /// type: int 304 | depth_bias: 0 305 | /// maximum depth bias of a pixel. 306 | /// type: float 307 | depth_bias_clamp: 0.0 308 | /// scalar on a given pixel's slope. 309 | /// type: float 310 | slope_scaled_depth_bias: 0.0 311 | /// specifies whether to enable clipping based on distance. 312 | /// type: bool 313 | depth_clip_enable: false 314 | /// specifies whether to use the quadrilateral or alpha line anti-aliasing algorithm on multisample antialiasing (MSAA) render targets. 315 | /// type: bool 316 | multisample_enable: false 317 | /// specifies whether to enable line antialiasing; only applies if doing line drawing and `multisample_enable` is false. 318 | /// type: bool 319 | antialiased_line_enable: false 320 | /// the sample count that is forced while UAV rendering or rasterizing. 321 | /// 0 indicates that the sample count is not forced. 322 | /// type: int [0, 1, 4, 8, 16] 323 | forced_sample_count: 0 324 | /// value that identifies whether conservative rasterization is on or off. 325 | /// type: bool 326 | conservative_raster_mode: false 327 | } 328 | 329 | /// specify a texture or render target.. spec is still work in progress, but here is a current example 330 | textures: { 331 | /// specify texture format 332 | /// type: enum [gfx::Format] 333 | format: RGBA8n, 334 | /// specify that the texture will be automatically sized in ratio of the designated window 335 | /// if this is supplied, width / height will be overriden 336 | /// type: optional object 337 | ratio: { 338 | /// window name (string) 339 | window: "window_name" 340 | /// scale ratio of the window 341 | scale: 1.0 342 | } 343 | /// optional filepath will load texture from disk and override the sizes 344 | filepath: "path.png" 345 | /// width of texture in pixels 346 | /// type: int 347 | width: 1 348 | /// height of texture in pixels 349 | /// type: int 350 | height: 1 351 | /// depth of texture in pixels / slices 352 | /// type: int 353 | depth: 1 354 | /// depth of texture in pixels / slices for texture arrays 355 | /// type: int 356 | array_layers: 1 357 | /// number of mip levels in the mip chain 358 | /// type: int 359 | mip_levels: 1 360 | /// number of samples for msaa 361 | /// type: int 362 | samples: 1 363 | /// signify this texture is a cubemap 364 | cubemap: False 365 | /// indicate how this texture will be used 366 | /// type: enum [RenderTarget, DepthStencil, DepthStencilReadOnly, UnorderedAccess, ShaderResource] 367 | usage: ShaderResource 368 | } 369 | 370 | /// specify a view (a render pass into a render target) 371 | view: { 372 | /// array of render targets to bind 373 | /// type: array 374 | render_target: [] 375 | /// clear colour to clear targets 376 | /// type: array of 4 floats (RGBA) for single channel 377 | /// or array of array of 4 floats (RGBA) for MRT clears 378 | clear_colour: [] 379 | /// array of depth stencils to bind 380 | depth_stencil: [] 381 | /// value to clear the depth buffer to 382 | /// type: float 383 | clear_depth: 1.0 384 | /// normalized viewport scaling, this in ratio to the render target size 385 | /// type: array of floats [left, top, right, bottom, near, far] 386 | viewport: [0.0, 0.0, 1.0, 1.0, 0.0, 1.0] 387 | /// custom data to pass a named camera for engine specific use 388 | /// type: string 389 | camera: "" 390 | } 391 | 392 | /// specify a render graph (collection of views with dependencies).. spec is still work in progress, but here is a current example 393 | render_graph: { 394 | mesh_debug: { 395 | grid: { 396 | view: "main_view" 397 | pipelines: ["imdraw_3d"] 398 | function: "render_grid" 399 | } 400 | meshes: { 401 | view: "main_view_no_clear" 402 | pipelines: ["mesh_debug"] 403 | function: "render_meshes" 404 | depends_on: ["grid"] 405 | } 406 | wireframe: { 407 | view: "main_view_no_clear" 408 | pipelines: ["wireframe_overlay"] 409 | function: "render_meshes" 410 | depends_on: ["meshes", "grid"] 411 | } 412 | } 413 | } -------------------------------------------------------------------------------- /examples/outputs/v1_header.h: -------------------------------------------------------------------------------- 1 | namespace forward_render 2 | { 3 | struct light_data 4 | { 5 | float4 pos_radius; 6 | float4 dir_cutoff; 7 | float4 colour; 8 | float4 data; 9 | }; 10 | struct distance_field_shadow 11 | { 12 | float4x4 world_matrix; 13 | float4x4 world_matrix_inv; 14 | }; 15 | struct area_light_data 16 | { 17 | float4 corners[4]; 18 | float4 colour; 19 | }; 20 | struct skinning_info 21 | { 22 | float4x4 bones[85]; 23 | }; 24 | struct per_pass_view 25 | { 26 | float4x4 vp_matrix; 27 | float4x4 view_matrix; 28 | float4x4 vp_matrix_inverse; 29 | float4x4 view_matrix_inverse; 30 | float4 camera_view_pos; 31 | float4 camera_view_dir; 32 | }; 33 | struct per_draw_call 34 | { 35 | float4x4 world_matrix; 36 | float4 user_data; 37 | float4 user_data2; 38 | float4x4 world_matrix_inv_transpose; 39 | }; 40 | struct per_pass_lights 41 | { 42 | float4 light_info; 43 | light_data lights[100]; 44 | }; 45 | struct per_pass_shadow 46 | { 47 | float4x4 shadow_matrix[100]; 48 | }; 49 | struct per_pass_shadow_distance_fields 50 | { 51 | distance_field_shadow sdf_shadow; 52 | }; 53 | struct per_pass_area_lights 54 | { 55 | float4 area_light_info; 56 | area_light_data area_lights[10]; 57 | }; 58 | struct cbuffer_single_light 59 | { 60 | light_data single_light; 61 | }; 62 | struct cbuffer_gi_volume 63 | { 64 | float4 gi_scene_size; 65 | float4 gi_volume_size; 66 | }; 67 | #define OMNI_SHADOW_SKINNED 2147483648 68 | #define OMNI_SHADOW_INSTANCED 1073741824 69 | #define DBG_POS_SKINNED 2147483648 70 | #define DBG_POS_INSTANCED 1073741824 71 | #define FORWARD_LIT_SKINNED 2147483648 72 | #define FORWARD_LIT_INSTANCED 1073741824 73 | #define FORWARD_LIT_UV_SCALE 2 74 | #define FORWARD_LIT_SDF_SHADOW 8 75 | #define FORWARD_LIT_GI 16 76 | struct forward_lit 77 | { 78 | float4 m_albedo; 79 | float m_roughness; 80 | float m_reflectivity; 81 | float2 m_padding; 82 | }; 83 | struct forward_lit_skinned 84 | { 85 | float4 m_albedo; 86 | float m_roughness; 87 | float m_reflectivity; 88 | float2 m_padding; 89 | }; 90 | struct forward_lit_instanced 91 | { 92 | float4 m_albedo; 93 | float m_roughness; 94 | float m_reflectivity; 95 | float2 m_padding; 96 | }; 97 | struct forward_lit_instanced_skinned 98 | { 99 | float4 m_albedo; 100 | float m_roughness; 101 | float m_reflectivity; 102 | float2 m_padding; 103 | }; 104 | struct forward_lit_uv_scale 105 | { 106 | float4 m_albedo; 107 | float2 m_uv_scale; 108 | float m_roughness; 109 | float m_reflectivity; 110 | }; 111 | struct forward_lit_uv_scale_skinned 112 | { 113 | float4 m_albedo; 114 | float2 m_uv_scale; 115 | float m_roughness; 116 | float m_reflectivity; 117 | }; 118 | struct forward_lit_uv_scale_instanced 119 | { 120 | float4 m_albedo; 121 | float2 m_uv_scale; 122 | float m_roughness; 123 | float m_reflectivity; 124 | }; 125 | struct forward_lit_uv_scale_instanced_skinned 126 | { 127 | float4 m_albedo; 128 | float2 m_uv_scale; 129 | float m_roughness; 130 | float m_reflectivity; 131 | }; 132 | struct forward_lit_sdf_shadow 133 | { 134 | float4 m_albedo; 135 | float m_roughness; 136 | float m_reflectivity; 137 | float m_surface_offset; 138 | float m_padding; 139 | }; 140 | struct forward_lit_sdf_shadow_skinned 141 | { 142 | float4 m_albedo; 143 | float m_roughness; 144 | float m_reflectivity; 145 | float m_surface_offset; 146 | float m_padding; 147 | }; 148 | struct forward_lit_sdf_shadow_instanced 149 | { 150 | float4 m_albedo; 151 | float m_roughness; 152 | float m_reflectivity; 153 | float m_surface_offset; 154 | float m_padding; 155 | }; 156 | struct forward_lit_sdf_shadow_instanced_skinned 157 | { 158 | float4 m_albedo; 159 | float m_roughness; 160 | float m_reflectivity; 161 | float m_surface_offset; 162 | float m_padding; 163 | }; 164 | struct forward_lit_sdf_shadow_uv_scale 165 | { 166 | float4 m_albedo; 167 | float2 m_uv_scale; 168 | float m_roughness; 169 | float m_reflectivity; 170 | float m_surface_offset; 171 | float3 m_padding; 172 | }; 173 | struct forward_lit_sdf_shadow_uv_scale_skinned 174 | { 175 | float4 m_albedo; 176 | float2 m_uv_scale; 177 | float m_roughness; 178 | float m_reflectivity; 179 | float m_surface_offset; 180 | float3 m_padding; 181 | }; 182 | struct forward_lit_sdf_shadow_uv_scale_instanced 183 | { 184 | float4 m_albedo; 185 | float2 m_uv_scale; 186 | float m_roughness; 187 | float m_reflectivity; 188 | float m_surface_offset; 189 | float3 m_padding; 190 | }; 191 | struct forward_lit_sdf_shadow_uv_scale_instanced_skinned 192 | { 193 | float4 m_albedo; 194 | float2 m_uv_scale; 195 | float m_roughness; 196 | float m_reflectivity; 197 | float m_surface_offset; 198 | float3 m_padding; 199 | }; 200 | struct forward_lit_gi 201 | { 202 | float4 m_albedo; 203 | float m_roughness; 204 | float m_reflectivity; 205 | float2 m_padding; 206 | }; 207 | struct forward_lit_gi_skinned 208 | { 209 | float4 m_albedo; 210 | float m_roughness; 211 | float m_reflectivity; 212 | float2 m_padding; 213 | }; 214 | struct forward_lit_gi_instanced 215 | { 216 | float4 m_albedo; 217 | float m_roughness; 218 | float m_reflectivity; 219 | float2 m_padding; 220 | }; 221 | struct forward_lit_gi_instanced_skinned 222 | { 223 | float4 m_albedo; 224 | float m_roughness; 225 | float m_reflectivity; 226 | float2 m_padding; 227 | }; 228 | struct forward_lit_gi_uv_scale 229 | { 230 | float4 m_albedo; 231 | float2 m_uv_scale; 232 | float m_roughness; 233 | float m_reflectivity; 234 | }; 235 | struct forward_lit_gi_uv_scale_skinned 236 | { 237 | float4 m_albedo; 238 | float2 m_uv_scale; 239 | float m_roughness; 240 | float m_reflectivity; 241 | }; 242 | struct forward_lit_gi_uv_scale_instanced 243 | { 244 | float4 m_albedo; 245 | float2 m_uv_scale; 246 | float m_roughness; 247 | float m_reflectivity; 248 | }; 249 | struct forward_lit_gi_uv_scale_instanced_skinned 250 | { 251 | float4 m_albedo; 252 | float2 m_uv_scale; 253 | float m_roughness; 254 | float m_reflectivity; 255 | }; 256 | struct forward_lit_gi_sdf_shadow 257 | { 258 | float4 m_albedo; 259 | float m_roughness; 260 | float m_reflectivity; 261 | float m_surface_offset; 262 | float m_padding; 263 | }; 264 | struct forward_lit_gi_sdf_shadow_skinned 265 | { 266 | float4 m_albedo; 267 | float m_roughness; 268 | float m_reflectivity; 269 | float m_surface_offset; 270 | float m_padding; 271 | }; 272 | struct forward_lit_gi_sdf_shadow_instanced 273 | { 274 | float4 m_albedo; 275 | float m_roughness; 276 | float m_reflectivity; 277 | float m_surface_offset; 278 | float m_padding; 279 | }; 280 | struct forward_lit_gi_sdf_shadow_instanced_skinned 281 | { 282 | float4 m_albedo; 283 | float m_roughness; 284 | float m_reflectivity; 285 | float m_surface_offset; 286 | float m_padding; 287 | }; 288 | struct forward_lit_gi_sdf_shadow_uv_scale 289 | { 290 | float4 m_albedo; 291 | float2 m_uv_scale; 292 | float m_roughness; 293 | float m_reflectivity; 294 | float m_surface_offset; 295 | float3 m_padding; 296 | }; 297 | struct forward_lit_gi_sdf_shadow_uv_scale_skinned 298 | { 299 | float4 m_albedo; 300 | float2 m_uv_scale; 301 | float m_roughness; 302 | float m_reflectivity; 303 | float m_surface_offset; 304 | float3 m_padding; 305 | }; 306 | struct forward_lit_gi_sdf_shadow_uv_scale_instanced 307 | { 308 | float4 m_albedo; 309 | float2 m_uv_scale; 310 | float m_roughness; 311 | float m_reflectivity; 312 | float m_surface_offset; 313 | float3 m_padding; 314 | }; 315 | struct forward_lit_gi_sdf_shadow_uv_scale_instanced_skinned 316 | { 317 | float4 m_albedo; 318 | float2 m_uv_scale; 319 | float m_roughness; 320 | float m_reflectivity; 321 | float m_surface_offset; 322 | float3 m_padding; 323 | }; 324 | #define SIMPLE_LIGHTING_SKINNED 2147483648 325 | #define SIMPLE_LIGHTING_INSTANCED 1073741824 326 | #define SIMPLE_LIGHTING_SSS 4 327 | struct simple_lighting 328 | { 329 | float4 m_albedo; 330 | float m_roughness; 331 | float m_reflectivity; 332 | float2 m_padding; 333 | }; 334 | struct simple_lighting_skinned 335 | { 336 | float4 m_albedo; 337 | float m_roughness; 338 | float m_reflectivity; 339 | float2 m_padding; 340 | }; 341 | struct simple_lighting_instanced 342 | { 343 | float4 m_albedo; 344 | float m_roughness; 345 | float m_reflectivity; 346 | float2 m_padding; 347 | }; 348 | struct simple_lighting_instanced_skinned 349 | { 350 | float4 m_albedo; 351 | float m_roughness; 352 | float m_reflectivity; 353 | float2 m_padding; 354 | }; 355 | struct simple_lighting_sss 356 | { 357 | float4 m_albedo; 358 | float m_roughness; 359 | float m_reflectivity; 360 | float m_sss_scale; 361 | float m_padding; 362 | }; 363 | struct simple_lighting_sss_skinned 364 | { 365 | float4 m_albedo; 366 | float m_roughness; 367 | float m_reflectivity; 368 | float m_sss_scale; 369 | float m_padding; 370 | }; 371 | struct simple_lighting_sss_instanced 372 | { 373 | float4 m_albedo; 374 | float m_roughness; 375 | float m_reflectivity; 376 | float m_sss_scale; 377 | float m_padding; 378 | }; 379 | struct simple_lighting_sss_instanced_skinned 380 | { 381 | float4 m_albedo; 382 | float m_roughness; 383 | float m_reflectivity; 384 | float m_sss_scale; 385 | float m_padding; 386 | }; 387 | #define GBUFFER_SKINNED 2147483648 388 | #define GBUFFER_INSTANCED 1073741824 389 | #define GBUFFER_UV_SCALE 2 390 | struct gbuffer 391 | { 392 | float4 m_albedo; 393 | float m_roughness; 394 | float m_reflectivity; 395 | float2 m_padding; 396 | }; 397 | struct gbuffer_skinned 398 | { 399 | float4 m_albedo; 400 | float m_roughness; 401 | float m_reflectivity; 402 | float2 m_padding; 403 | }; 404 | struct gbuffer_instanced 405 | { 406 | float4 m_albedo; 407 | float m_roughness; 408 | float m_reflectivity; 409 | float2 m_padding; 410 | }; 411 | struct gbuffer_instanced_skinned 412 | { 413 | float4 m_albedo; 414 | float m_roughness; 415 | float m_reflectivity; 416 | float2 m_padding; 417 | }; 418 | struct gbuffer_uv_scale 419 | { 420 | float4 m_albedo; 421 | float2 m_uv_scale; 422 | float m_roughness; 423 | float m_reflectivity; 424 | }; 425 | struct gbuffer_uv_scale_skinned 426 | { 427 | float4 m_albedo; 428 | float2 m_uv_scale; 429 | float m_roughness; 430 | float m_reflectivity; 431 | }; 432 | struct gbuffer_uv_scale_instanced 433 | { 434 | float4 m_albedo; 435 | float2 m_uv_scale; 436 | float m_roughness; 437 | float m_reflectivity; 438 | }; 439 | struct gbuffer_uv_scale_instanced_skinned 440 | { 441 | float4 m_albedo; 442 | float2 m_uv_scale; 443 | float m_roughness; 444 | float m_reflectivity; 445 | }; 446 | #define ZONLY_SKINNED 2147483648 447 | #define ZONLY_INSTANCED 1073741824 448 | struct single_light_directional 449 | { 450 | float4 m_albedo; 451 | float m_roughness; 452 | float m_reflectivity; 453 | float2 m_padding; 454 | }; 455 | #define LAMBERT_SKINNED 2147483648 456 | #define LAMBERT_INSTANCED 1073741824 457 | struct lambert 458 | { 459 | float4 m_albedo; 460 | }; 461 | struct lambert_skinned 462 | { 463 | float4 m_albedo; 464 | }; 465 | struct lambert_instanced 466 | { 467 | float4 m_albedo; 468 | }; 469 | struct lambert_instanced_skinned 470 | { 471 | float4 m_albedo; 472 | }; 473 | #define GI_SKINNED 2147483648 474 | #define GI_INSTANCED 1073741824 475 | struct gi 476 | { 477 | float4 m_albedo; 478 | float m_roughness; 479 | float m_reflectivity; 480 | float2 m_padding; 481 | }; 482 | struct gi_skinned 483 | { 484 | float4 m_albedo; 485 | float m_roughness; 486 | float m_reflectivity; 487 | float2 m_padding; 488 | }; 489 | struct gi_instanced 490 | { 491 | float4 m_albedo; 492 | float m_roughness; 493 | float m_reflectivity; 494 | float2 m_padding; 495 | }; 496 | struct gi_instanced_skinned 497 | { 498 | float4 m_albedo; 499 | float m_roughness; 500 | float m_reflectivity; 501 | float2 m_padding; 502 | }; 503 | } 504 | -------------------------------------------------------------------------------- /examples/outputs/v1_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmdline": "..\\third_party\\pmbuild\\scripts\\pmfx-shader\\build_pmfx.py -shader_platform glsl -shader_version 450 -i assets/shaders ../assets/shaders -o bin/win32/data/pmfx/glsl -h shader_structs -t build/temp/shaders -source -v_flip ", 3 | "files": [ 4 | { 5 | "name": "C:\\actions-runner-pmtech\\_work\\pmtech\\pmtech\\third_party\\pmbuild\\scripts\\pmfx-shader\\build_pmfx.py", 6 | "timestamp": 1653222595.2662177 7 | }, 8 | { 9 | "name": "C:\\actions-runner-pmtech\\_work\\pmtech\\pmtech\\third_party\\pmbuild\\scripts\\pmfx-shader\\platform\\pmfx.h", 10 | "timestamp": 1650810183.096692 11 | }, 12 | { 13 | "name": "C:\\actions-runner-pmtech\\_work\\pmtech\\pmtech\\third_party\\pmbuild\\scripts\\pmfx-shader\\platform\\glsl.h", 14 | "timestamp": 1651918963.725134 15 | }, 16 | { 17 | "name": "C:\\actions-runner-pmtech\\_work\\pmtech\\pmtech\\examples\\..\\assets\\shaders\\imgui.pmfx", 18 | "timestamp": 1650810162.782683 19 | } 20 | ], 21 | "techniques": [ 22 | { 23 | "vs": "vs_main", 24 | "ps": "ps_main", 25 | "name": "default", 26 | "texture_sampler_bindings": [ 27 | { 28 | "name": "tex", 29 | "data_type": "float4", 30 | "fragments": 1, 31 | "type": "texture_2d", 32 | "unit": 0 33 | } 34 | ], 35 | "structured_buffers": [], 36 | "descriptor_tables": [], 37 | "samplers": [], 38 | "cbuffers": [ 39 | { 40 | "name": "per_pass_vs", 41 | "location": 1, 42 | "space": -1 43 | } 44 | ], 45 | "vs_inputs": [ 46 | { 47 | "name": "position", 48 | "semantic_index": 0, 49 | "semantic_id": 1, 50 | "size": 8, 51 | "element_size": 4, 52 | "num_elements": 2, 53 | "offset": 0 54 | }, 55 | { 56 | "name": "tex_coord", 57 | "semantic_index": 0, 58 | "semantic_id": 2, 59 | "size": 8, 60 | "element_size": 4, 61 | "num_elements": 2, 62 | "offset": 8 63 | }, 64 | { 65 | "name": "colour", 66 | "semantic_index": 0, 67 | "semantic_id": 7, 68 | "size": 4, 69 | "element_size": 1, 70 | "num_elements": 4, 71 | "offset": 16 72 | } 73 | ], 74 | "instance_inputs": [], 75 | "vs_outputs": [ 76 | { 77 | "name": "position", 78 | "semantic_index": 0, 79 | "semantic_id": 0, 80 | "size": 16, 81 | "element_size": 4, 82 | "num_elements": 4, 83 | "offset": 0 84 | }, 85 | { 86 | "name": "colour", 87 | "semantic_index": 0, 88 | "semantic_id": 2, 89 | "size": 16, 90 | "element_size": 4, 91 | "num_elements": 4, 92 | "offset": 16 93 | }, 94 | { 95 | "name": "tex_coord", 96 | "semantic_index": 1, 97 | "semantic_id": 2, 98 | "size": 8, 99 | "element_size": 4, 100 | "num_elements": 2, 101 | "offset": 32 102 | } 103 | ], 104 | "vs_file": "default.vsc", 105 | "ps_file": "default.psc", 106 | "permutations": {}, 107 | "permutation_id": 0, 108 | "permutation_option_mask": 0 109 | } 110 | ], 111 | "failures": {} 112 | } -------------------------------------------------------------------------------- /examples/outputs/v2_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipelines": { 3 | "mesh_debug": { 4 | "0": { 5 | "vs": "basic/vs_mesh.vsc", 6 | "ps": "basic/ps_checkerboard.psc", 7 | "push_constants": [ 8 | "view_push_constants", 9 | "draw_push_constants" 10 | ], 11 | "depth_stencil_state": "depth_test_less", 12 | "raster_state": "cull_back", 13 | "topology": "TriangleList", 14 | "vs_hash:": 2233862273, 15 | "vertex_layout": [ 16 | { 17 | "name": "position", 18 | "semantic": "POSITION", 19 | "index": 0, 20 | "format": "RGB32f", 21 | "aligned_byte_offset": 0, 22 | "input_slot": 0, 23 | "input_slot_class": "PerVertex", 24 | "step_rate": 0 25 | }, 26 | { 27 | "name": "texcoord", 28 | "semantic": "TEXCOORD", 29 | "index": 0, 30 | "format": "RG32f", 31 | "aligned_byte_offset": 12, 32 | "input_slot": 0, 33 | "input_slot_class": "PerVertex", 34 | "step_rate": 0 35 | }, 36 | { 37 | "name": "normal", 38 | "semantic": "TEXCOORD", 39 | "index": 1, 40 | "format": "RGB32f", 41 | "aligned_byte_offset": 20, 42 | "input_slot": 0, 43 | "input_slot_class": "PerVertex", 44 | "step_rate": 0 45 | }, 46 | { 47 | "name": "tangent", 48 | "semantic": "TEXCOORD", 49 | "index": 2, 50 | "format": "RGB32f", 51 | "aligned_byte_offset": 32, 52 | "input_slot": 0, 53 | "input_slot_class": "PerVertex", 54 | "step_rate": 0 55 | }, 56 | { 57 | "name": "bitangent", 58 | "semantic": "TEXCOORD", 59 | "index": 3, 60 | "format": "RGB32f", 61 | "aligned_byte_offset": 44, 62 | "input_slot": 0, 63 | "input_slot_class": "PerVertex", 64 | "step_rate": 0 65 | } 66 | ], 67 | "ps_hash:": 2277378182, 68 | "descriptor_layout": { 69 | "bindings": [], 70 | "push_constants": [ 71 | { 72 | "shader_register": 0, 73 | "register_space": 0, 74 | "binding_type": "ConstantBuffer", 75 | "visibility": "Vertex", 76 | "num_values": 48 77 | }, 78 | { 79 | "shader_register": 1, 80 | "register_space": 0, 81 | "binding_type": "ConstantBuffer", 82 | "visibility": "Vertex", 83 | "num_values": 16 84 | } 85 | ], 86 | "static_samplers": [] 87 | }, 88 | "hash": 3191457657 89 | } 90 | }, 91 | "wireframe_overlay": { 92 | "0": { 93 | "vs": "basic/vs_mesh.vsc", 94 | "ps": "basic/ps_wireframe.psc", 95 | "push_constants": [ 96 | "view_push_constants", 97 | "draw_push_constants" 98 | ], 99 | "raster_state": "wireframe", 100 | "depth_stencil_state": "depth_test_always", 101 | "topology": "TriangleList", 102 | "vs_hash:": 2233862273, 103 | "vertex_layout": [ 104 | { 105 | "name": "position", 106 | "semantic": "POSITION", 107 | "index": 0, 108 | "format": "RGB32f", 109 | "aligned_byte_offset": 0, 110 | "input_slot": 0, 111 | "input_slot_class": "PerVertex", 112 | "step_rate": 0 113 | }, 114 | { 115 | "name": "texcoord", 116 | "semantic": "TEXCOORD", 117 | "index": 0, 118 | "format": "RG32f", 119 | "aligned_byte_offset": 12, 120 | "input_slot": 0, 121 | "input_slot_class": "PerVertex", 122 | "step_rate": 0 123 | }, 124 | { 125 | "name": "normal", 126 | "semantic": "TEXCOORD", 127 | "index": 1, 128 | "format": "RGB32f", 129 | "aligned_byte_offset": 20, 130 | "input_slot": 0, 131 | "input_slot_class": "PerVertex", 132 | "step_rate": 0 133 | }, 134 | { 135 | "name": "tangent", 136 | "semantic": "TEXCOORD", 137 | "index": 2, 138 | "format": "RGB32f", 139 | "aligned_byte_offset": 32, 140 | "input_slot": 0, 141 | "input_slot_class": "PerVertex", 142 | "step_rate": 0 143 | }, 144 | { 145 | "name": "bitangent", 146 | "semantic": "TEXCOORD", 147 | "index": 3, 148 | "format": "RGB32f", 149 | "aligned_byte_offset": 44, 150 | "input_slot": 0, 151 | "input_slot_class": "PerVertex", 152 | "step_rate": 0 153 | } 154 | ], 155 | "ps_hash:": 2344290471, 156 | "descriptor_layout": { 157 | "bindings": [], 158 | "push_constants": [ 159 | { 160 | "shader_register": 0, 161 | "register_space": 0, 162 | "binding_type": "ConstantBuffer", 163 | "visibility": "Vertex", 164 | "num_values": 48 165 | }, 166 | { 167 | "shader_register": 1, 168 | "register_space": 0, 169 | "binding_type": "ConstantBuffer", 170 | "visibility": "Vertex", 171 | "num_values": 16 172 | } 173 | ], 174 | "static_samplers": [] 175 | }, 176 | "hash": 2262681932 177 | } 178 | } 179 | }, 180 | "shaders": { 181 | "basic/vs_mesh.vsc": 2233862273, 182 | "basic/ps_checkerboard.psc": 2277378182, 183 | "basic/ps_wireframe.psc": 2344290471 184 | }, 185 | "depth_stencil_states": { 186 | "depth_test_less": { 187 | "depth_enabled": true, 188 | "depth_write_mask": "All", 189 | "depth_func": "Less", 190 | "stencil_enabled": false, 191 | "stencil_read_mask": 0, 192 | "stencil_write_mask": 0, 193 | "front_face": { 194 | "fail": "Keep", 195 | "depth_fail": "Keep", 196 | "pass": "Keep", 197 | "func": "Always" 198 | }, 199 | "back_face": { 200 | "fail": "Keep", 201 | "depth_fail": "Keep", 202 | "pass": "Keep", 203 | "func": "Always" 204 | }, 205 | "hash": 244148036 206 | }, 207 | "depth_test_equal": { 208 | "depth_enabled": true, 209 | "depth_write_mask": "All", 210 | "depth_func": "Equal", 211 | "stencil_enabled": false, 212 | "stencil_read_mask": 0, 213 | "stencil_write_mask": 0, 214 | "front_face": { 215 | "fail": "Keep", 216 | "depth_fail": "Keep", 217 | "pass": "Keep", 218 | "func": "Always" 219 | }, 220 | "back_face": { 221 | "fail": "Keep", 222 | "depth_fail": "Keep", 223 | "pass": "Keep", 224 | "func": "Always" 225 | }, 226 | "hash": 2252236709 227 | }, 228 | "depth_test_always": { 229 | "depth_enabled": true, 230 | "depth_write_mask": "All", 231 | "depth_func": "Less", 232 | "stencil_enabled": false, 233 | "stencil_read_mask": 0, 234 | "stencil_write_mask": 0, 235 | "front_face": { 236 | "fail": "Keep", 237 | "depth_fail": "Keep", 238 | "pass": "Keep", 239 | "func": "Always" 240 | }, 241 | "back_face": { 242 | "fail": "Keep", 243 | "depth_fail": "Keep", 244 | "pass": "Keep", 245 | "func": "Always" 246 | }, 247 | "hash": 244148036 248 | } 249 | }, 250 | "sampler_states": {}, 251 | "render_target_blend_states": {}, 252 | "blend_states": {}, 253 | "raster_states": { 254 | "wireframe": { 255 | "fill_mode": "Wireframe", 256 | "cull_mode": "None", 257 | "front_ccw": false, 258 | "depth_bias": -5, 259 | "depth_bias_clamp": 0.0, 260 | "slope_scaled_depth_bias": 0.0, 261 | "depth_clip_enable": false, 262 | "multisample_enable": false, 263 | "antialiased_line_enable": false, 264 | "forced_sample_count": 0, 265 | "conservative_raster_mode": false, 266 | "hash": 3249235164 267 | }, 268 | "cull_back": { 269 | "fill_mode": "Solid", 270 | "cull_mode": "Back", 271 | "front_ccw": false, 272 | "depth_bias": 0, 273 | "depth_bias_clamp": 0.0, 274 | "slope_scaled_depth_bias": 0.0, 275 | "depth_clip_enable": false, 276 | "multisample_enable": false, 277 | "antialiased_line_enable": false, 278 | "forced_sample_count": 0, 279 | "conservative_raster_mode": false, 280 | "hash": 2186240740 281 | } 282 | }, 283 | "textures": { 284 | "main_colour": { 285 | "format": "RGBA8n", 286 | "width": 1, 287 | "height": 1, 288 | "depth": 1, 289 | "array_levels": 1, 290 | "mip_levels": 1, 291 | "samples": 8, 292 | "usage": [ 293 | "ShaderResource", 294 | "RenderTarget" 295 | ], 296 | "ratio": { 297 | "window": "main_window", 298 | "scale": 1.0 299 | }, 300 | "hash": 1223573194 301 | }, 302 | "main_depth": { 303 | "format": "D24nS8u", 304 | "width": 1, 305 | "height": 1, 306 | "depth": 1, 307 | "array_levels": 1, 308 | "mip_levels": 1, 309 | "samples": 8, 310 | "usage": [ 311 | "ShaderResource", 312 | "DepthStencil" 313 | ], 314 | "ratio": { 315 | "window": "main_window", 316 | "scale": 1.0 317 | }, 318 | "hash": 2351906592 319 | } 320 | }, 321 | "views": { 322 | "main_view": { 323 | "viewport": [ 324 | 0.0, 325 | 0.0, 326 | 1.0, 327 | 1.0, 328 | 0.0, 329 | 1.0 330 | ], 331 | "scissor": [ 332 | 0.0, 333 | 0.0, 334 | 1.0, 335 | 1.0 336 | ], 337 | "render_target": [ 338 | "main_colour" 339 | ], 340 | "depth_stencil": [ 341 | "main_depth" 342 | ], 343 | "clear_colour": [ 344 | 0.45, 345 | 0.55, 346 | 0.6, 347 | 1.0 348 | ], 349 | "clear_depth": 1.0, 350 | "camera": "main_camera", 351 | "hash": 2840609343 352 | }, 353 | "main_view_no_clear": { 354 | "viewport": [ 355 | 0.0, 356 | 0.0, 357 | 1.0, 358 | 1.0, 359 | 0.0, 360 | 1.0 361 | ], 362 | "scissor": [ 363 | 0.0, 364 | 0.0, 365 | 1.0, 366 | 1.0 367 | ], 368 | "render_target": [ 369 | "main_colour" 370 | ], 371 | "depth_stencil": [ 372 | "main_depth" 373 | ], 374 | "clear_colour": null, 375 | "clear_depth": null, 376 | "camera": "main_camera", 377 | "hash": 4218044546 378 | } 379 | }, 380 | "update_graphs": {}, 381 | "render_graphs": { 382 | "mesh_debug": { 383 | "grid": { 384 | "view": "main_view", 385 | "pipelines": [ 386 | "imdraw_3d" 387 | ], 388 | "function": "render_grid" 389 | }, 390 | "meshes": { 391 | "view": "main_view_no_clear", 392 | "pipelines": [ 393 | "mesh_debug" 394 | ], 395 | "function": "render_meshes", 396 | "depends_on": [ 397 | "grid" 398 | ] 399 | }, 400 | "wireframe": { 401 | "view": "main_view_no_clear", 402 | "pipelines": [ 403 | "wireframe_overlay" 404 | ], 405 | "function": "render_meshes", 406 | "depends_on": [ 407 | "meshes", 408 | "grid" 409 | ] 410 | } 411 | } 412 | }, 413 | "filepath": "dev\\hotline\\target\\data\\shaders\\basic\\basic.json", 414 | "dependencies": [ 415 | "dev\\hotline\\hotline-data\\src\\shaders\\basic.hlsl", 416 | "dev\\hotline\\hotline-data\\src\\shaders\\hotline.pmfx", 417 | "dev\\pmfx-shader\\pmfx.py", 418 | "dev\\hotline\\hotline-data\\src\\shaders\\basic.pmfx" 419 | ] 420 | } -------------------------------------------------------------------------------- /examples/v1/v1_multi_examples.pmfx: -------------------------------------------------------------------------------- 1 | // input output structs with hlsl vertex shader semantics 2 | struct vs_input { 3 | float4 position : POSITION; 4 | float2 tex_coord: TEXCOORD0; 5 | }; 6 | 7 | struct vs_output { 8 | float4 position : SV_POSITION; 9 | float2 tex_coord: TEXCOORD0; 10 | }; 11 | 12 | struct vs_input_multi { 13 | float4 position : POSITION; 14 | float4 normal : TEXCOORD0; 15 | float4 texcoord : TEXCOORD1; 16 | float4 tangent : TEXCOORD2; 17 | float4 bitangent : TEXCOORD3; 18 | 19 | if:(SKINNED) { 20 | float4 blend_indices : TEXCOORD4; 21 | float4 blend_weights : TEXCOORD5; 22 | } 23 | }; 24 | 25 | // pixel shader output with hlsl pixel shader semantics 26 | struct ps_output { 27 | float4 colour : SV_Target; 28 | }; 29 | 30 | // define textures and structured buffers in here, you can alias the same texture units in different shaders 31 | shader_resources { 32 | // texture sampling 33 | texture_2d(tex2d, 0); 34 | texture_3d(tex3d, 1); 35 | texture_2d_array(texarray, 2); 36 | texture_cube(texcube, 3); 37 | 38 | // compute rw 39 | texture_2d_r(input_rgb, 0); 40 | texture_2d_w(output_l, 1); 41 | 42 | // tex compare sampling 43 | // assumes you will bind a sampler with comparison state in your graphics api. 44 | depth_2d(depth_tex2d, 7); 45 | depth_2d_array(depth_texarray, 15); 46 | 47 | // atomic 48 | structured_buffer_rw(atomic_uint, atomic_buffer, 0); 49 | }; 50 | 51 | // basic 2d shader taking screen space input coordinates 52 | vs_output vs_main_2d(vs_input input) { 53 | // pmfx needs input and output structs to deduce reflection info 54 | vs_output output; 55 | // read input, write output 56 | output.position = input.position; 57 | output.tex_coord = input.tex_coord; 58 | return output; 59 | } 60 | 61 | // write constant colour in pixel shader 62 | ps_output ps_constant_colour(vs_output input) { 63 | // use structs for output so pmfx can deduce reflection info 64 | ps_output output; 65 | // write constant colour 66 | output.colour = float4(1.0, 1.0, 1.0, 1.0); 67 | return output; 68 | } 69 | 70 | // sample a 2d texture using sample_texture on shader resource tex2d 71 | ps_output ps_texture_2d(vs_output input) { 72 | ps_output output; 73 | // sample texture, tex2d has a combined texture and sampler on slot 0 74 | output.colour = sample_texture(tex2d, input.tex_coord.xy); 75 | return output; 76 | } 77 | 78 | // sample a 2d texture at a specific mip-level using sample_texture on shader resource tex2d 79 | ps_output ps_texture_2d_mip_level(vs_output input) { 80 | ps_output output; 81 | // sample texture, tex2d has a combined texture and sampler on slot 0 82 | float lvl = 0.0; // select which mip level to sample from 83 | output.colour = sample_texture_level(tex2d, input.tex_coord.xy, lvl); 84 | return output; 85 | } 86 | 87 | // sample a 2d texture array using sample_texture on shader resource tex2d 88 | ps_output ps_texture_2d_array(vs_output input) { 89 | ps_output output; 90 | // sample texture array, texarray has a combined texture and sampler on slot 0 91 | float array_slice = 0.0; // this is the slice of the array to sample 92 | output.colour = sample_texture_array(texarray, input.tex_coord.xy, array_slice); 93 | return output; 94 | } 95 | 96 | // sample a 3d texture using sample_texture on shader resource tex3d 97 | ps_output ps_texture_3d(vs_output input) { 98 | ps_output output; 99 | // sample texture, tex3d has a combined texture and sampler on slot 0 100 | float3 n = float3(0.0, 1.0, 0.0); // sample with a 3d-coordinate 101 | output.colour = sample_texture(tex3d, n); 102 | return output; 103 | } 104 | 105 | // sample a cube texture using sample_texture on shader resource texcube 106 | ps_output ps_texture_cube(vs_output input) { 107 | ps_output output; 108 | // sample texture, texcube has a combined texture and sampler on slot 0 109 | float3 n = float3(0.0, 1.0, 0.0); // sample with a 3d-coordinate 110 | output.colour = sample_texture(texcube, n); 111 | return output; 112 | } 113 | 114 | // sample a cube texture using sample_texture on shader resource texcube 115 | ps_output ps_depth_texture_compare(vs_output input) { 116 | ps_output output; 117 | // sample texture and compare a depth texture 118 | float3 sp = float3(0.0, 0.0, 0.0); // assumes you would project and calculate shadow map position and depth 119 | float shadow = sample_depth_compare(depth_tex2d, sp.xy, sp.z); 120 | output.colour = float4(shadow, shadow, shadow, 1.0); 121 | return output; 122 | } 123 | 124 | // sample a cube texture using sample_texture on shader resource texcube 125 | ps_output ps_depth_texture_compare_array(vs_output input) { 126 | ps_output output; 127 | // sample texture and compare a depth texture 128 | float3 sp = float3(0.0, 0.0, 0.0); // assumes you would project and calculate shadow map position and depth 129 | float array_slice = 0.0; // use this to index into the array 130 | float shadow = sample_depth_compare_array(depth_texarray, sp.xy, array_slice, sp.z); 131 | output.colour = float4(shadow, shadow, shadow, 1.0); 132 | return output; 133 | } 134 | 135 | cbuffer per_pass_view : register(b0) { 136 | float4x4 vp_matrix; 137 | float4x4 view_matrix; 138 | }; 139 | 140 | cbuffer per_draw_call : register(b1) { 141 | float4x4 world_matrix; 142 | }; 143 | 144 | vs_output vs_permute(vs_input_multi input) 145 | { 146 | vs_output output; 147 | 148 | float4x4 wvp = mul(world_matrix, vp_matrix); 149 | float4x4 wm = world_matrix; 150 | 151 | output.tex_coord = float2(input.texcoord.x, 1.0 - input.texcoord.y); 152 | 153 | if:(SKINNED){ 154 | // .. do something different for skinning 155 | float4 sp = input.position; 156 | output.position = mul(sp, vp_matrix); 157 | output.world_pos = sp; 158 | } 159 | else:{ 160 | output.position = mul(input.position, wvp); 161 | } 162 | 163 | return output; 164 | } 165 | 166 | // read the input texture input_rgb using (read_texture) convert to grescale 167 | void cs_greyscale(uint3 gid : SV_DispatchThreadID) { 168 | // read texture in compute shader 169 | float4 col = read_texture(input_rgb, gid.xy); 170 | // convert greyscale 171 | float grey = dot(col.rgb, float3(0.2126, 0.7152, 0.0722)); 172 | // write the result back 173 | write_texture(output_l, float4(grey, grey, grey, 1.0), gid.xy); 174 | } 175 | 176 | // example of an atomic 177 | void cs_atomic(uint3 gid : SV_DispatchThreadID) { 178 | /// reads current value into index, and increments 179 | uint index; 180 | atomic_increment(atomic_buffer[0], index); 181 | } 182 | 183 | pmfx: { 184 | // supply vs and ps to create a render pipeline technique 185 | constant_colour: { 186 | vs: vs_main_2d 187 | ps: ps_constant_colour 188 | } 189 | 190 | texture_2d: { 191 | vs: vs_main_2d 192 | ps: ps_texture_2d 193 | } 194 | 195 | texture_2d_mip_level: { 196 | vs: vs_main_2d 197 | ps: ps_texture_2d_mip_level 198 | } 199 | 200 | texture_2d_array: { 201 | vs: vs_main_2d 202 | ps: ps_texture_2d_array 203 | } 204 | 205 | texture_3d: { 206 | vs: vs_main_2d 207 | ps: ps_texture_3d 208 | } 209 | 210 | texture_cube: { 211 | vs: vs_main_2d 212 | ps: ps_texture_cube 213 | } 214 | 215 | depth_texture_compare: { 216 | vs: vs_main_2d 217 | ps: ps_depth_texture_compare 218 | } 219 | 220 | depth_texture_compare_array: { 221 | vs: vs_main_2d 222 | ps: ps_depth_texture_compare_array 223 | 224 | supported_platforms: { 225 | hlsl: ["5_0"] 226 | metal: ["all"] 227 | } 228 | } 229 | 230 | permutation: { 231 | vs: vs_permute 232 | ps: ps_constant_colour 233 | } 234 | 235 | // supply cs and threads for compute 236 | basic_compute: { 237 | cs: cs_greyscale 238 | threads: [16, 16, 1] 239 | 240 | // compute might not be supported on all platforms, you can filter supported platforms as so 241 | supported_platforms: { 242 | hlsl: ["5_0"] 243 | metal: ["all"] 244 | } 245 | } 246 | 247 | basic_compute: { 248 | cs: cs_atomic 249 | threads: [16, 16, 1] 250 | 251 | // compute might not be supported on all platforms, you can filter supported platforms as so 252 | supported_platforms: { 253 | hlsl: ["5_0"] 254 | metal: ["all"] 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /examples/v1/v1_single_shader.pmfx: -------------------------------------------------------------------------------- 1 | struct vs_input 2 | { 3 | float4 position : POSITION; 4 | }; 5 | 6 | struct vs_output 7 | { 8 | float4 position : SV_POSITION0; 9 | }; 10 | 11 | struct ps_output 12 | { 13 | float4 colour : SV_Target; 14 | }; 15 | 16 | vs_output vs_main( vs_input input ) 17 | { 18 | vs_output output; 19 | 20 | output.position = input.position; 21 | 22 | return output; 23 | } 24 | 25 | ps_output ps_main( vs_output input ) 26 | { 27 | ps_output output; 28 | 29 | output.colour = float4( 1.0, 1.0, 1.0, 1.0 ); 30 | 31 | { 32 | // test scope for the shader parser to 33 | // correctly find the body of the main function 34 | } 35 | 36 | return output; 37 | } 38 | -------------------------------------------------------------------------------- /examples/v2/v2_examples.hlsl: -------------------------------------------------------------------------------- 1 | struct vs_output { 2 | float4 position : SV_POSITION; 3 | float4 world_pos : TEXCOORD0; 4 | float3 normal : TEXCOORD1; 5 | float3 tangent : TEXCOORD2; 6 | float3 bitangent : TEXCOORD3; 7 | float4 texcoord : TEXCOORD4; 8 | }; 9 | 10 | struct vs_input { 11 | float4 position : POSITION; 12 | float4 normal : TEXCOORD0; 13 | float4 texcoord : TEXCOORD1; 14 | float4 tangent : TEXCOORD2; 15 | float4 bitangent : TEXCOORD3; 16 | }; 17 | 18 | struct instance_input { 19 | float4 mat0 : TEXCOORD4; 20 | float4 mat2 : TEXCOORD5; 21 | float4 mat3 : TEXCOORD6; 22 | }; 23 | 24 | struct ps_output { 25 | float4 colour : SV_Target; 26 | }; 27 | 28 | struct sb_sb { 29 | float4 data; 30 | }; 31 | 32 | cbuffer per_pass_vs : register(b1) { 33 | float4x4 projection_matrix; 34 | float4 test; 35 | }; 36 | 37 | struct cbuffer_data { 38 | float4 data2; 39 | }; 40 | 41 | SamplerState decl_sampler : register(s0); 42 | 43 | ConstantBuffer decl_cbuffer_array[] : register(b2); 44 | ConstantBuffer decl_cbuffer_array_bounded[69] : register(b3, space0); 45 | 46 | StructuredBuffer decl_structured_buffer : register(u0, space0); 47 | RWStructuredBuffer decl_structured_buffer_rw : register(u1, space0); 48 | 49 | Texture1D decl_texture1d : register(t0); 50 | Texture2D decl_texture2d : register(t1); 51 | Texture3D decl_texture3d : register(t2); 52 | 53 | // alias resource types types on t0 54 | Texture2D textures[] : register(t0); 55 | TextureCube cubemaps[] : register(t0); 56 | Texture2DArray texture_arrays[] : register(t0); 57 | Texture3D volume_textures[] : register(t0); 58 | ConstantBuffer cbuffers[] : register(b0); 59 | 60 | void test_func() { 61 | pmfx_touch(decl_texture1d); 62 | pmfx_touch(decl_cbuffer_array); 63 | pmfx_touch(decl_cbuffer_array_bounded); 64 | } 65 | 66 | void test_func2() { 67 | pmfx_touch(decl_texture1d); 68 | pmfx_touch(decl_cbuffer_array); 69 | pmfx_touch(decl_cbuffer_array_bounded); 70 | } 71 | 72 | vs_output vs_main_permutations(vs_input input) { 73 | if:(SKINNED) { 74 | test_func(); 75 | return vs_output_default(); 76 | } 77 | else if:(INSTANCED) { 78 | return vs_output_default(); 79 | } 80 | return vs_output_default(); 81 | } 82 | 83 | vs_output vs_output_default() { 84 | vs_output output; 85 | output.position = float4(0.0, 0.0, 0.0, 1.0); 86 | output.world_pos = float4(0.0, 0.0, 0.0, 1.0); 87 | output.texcoord = float4(0.0, 0.0, 0.0, 1.0); 88 | output.normal = float3(0.0, 0.0, 0.0); 89 | output.tangent = float3(0.0, 0.0, 0.0); 90 | output.bitangent = float3(0.0, 0.0, 0.0); 91 | return output; 92 | } 93 | 94 | ps_output ps_output_default() { 95 | ps_output output; 96 | output.colour = float4(0.0, 0.0, 0.0, 1.0); 97 | return output; 98 | } 99 | 100 | vs_output vs_main(vs_input input, instance_input mat) { 101 | test_func(); 102 | return vs_output_default(); 103 | } 104 | 105 | vs_output vs_main_mixed_semantics(vs_input input, uint iid : SV_InstanceID) { 106 | test_func(); 107 | return vs_output_default(); 108 | } 109 | 110 | vs_output vs_main_separate_elements(float4 pos : POSITION, float4 tex : TEXCOORD0) { 111 | test_func(); 112 | return vs_output_default(); 113 | } 114 | 115 | ps_output ps_main() { 116 | test_func2(); 117 | return ps_output_default(); 118 | } 119 | 120 | vs_output vs_test_bindless_aliasing(vs_input input) { 121 | return vs_output_default(); 122 | } 123 | 124 | // 125 | // test that different textures can alias and bind on the same register / 126 | // 127 | 128 | ps_output ps_test_bindless_aliasing() { 129 | test_func(); 130 | // using this void cast touches the resources so they are detected by pmfx and compiled in 131 | pmfx_touch(textures); 132 | pmfx_touch(cubemaps); 133 | pmfx_touch(texture_arrays); 134 | pmfx_touch(volume_textures); 135 | 136 | // cbuffers go on a different slot 137 | pmfx_touch(cbuffers); 138 | return ps_output_default(); 139 | } 140 | 141 | // 142 | // test using a raw cbuffer member vs a scoped member and test 143 | // ambiguity 144 | 145 | cbuffer cbuffer_unscoped : register(b0) { 146 | float4x4 world_matrix; 147 | }; 148 | 149 | struct cbuffer_struct { 150 | float4x4 world_matrix; 151 | }; 152 | 153 | ConstantBuffer cbuffer_scoped : register(b0); 154 | 155 | vs_output vs_test_use_cbuffer_unscoped() { 156 | vs_output output = vs_output_default(); 157 | output.position = mul(world_matrix, output.position); 158 | return output; 159 | } 160 | 161 | vs_output vs_test_use_cbuffer_scoped() { 162 | vs_output output = vs_output_default(); 163 | output.position = mul(cbuffer_scoped.world_matrix, output.position); 164 | 165 | // test member 166 | float4x4 mat = cbuffer_scoped. world_matrix; 167 | 168 | return output; 169 | } 170 | 171 | // 172 | // test nested structures 173 | // 174 | 175 | struct buffer_view { 176 | uint2 location; 177 | uint size; 178 | uint strude; 179 | }; 180 | 181 | struct indirect_args { 182 | buffer_view ib; 183 | buffer_view vb; 184 | }; 185 | 186 | vs_output vs_test_nested_structures() { 187 | vs_output output = vs_output_default(); 188 | return output; 189 | } 190 | 191 | // 192 | // global types / compute sync 193 | // 194 | 195 | groupshared uint4 accumulated; 196 | 197 | void cs_mip_chain_texture2d(uint2 did: SV_DispatchThreadID) { 198 | 199 | accumulated = uint4(0, 0, 0, 0); 200 | 201 | GroupMemoryBarrierWithGroupSync(); 202 | 203 | uint original; 204 | InterlockedAdd(accumulated.x, 1, original); 205 | 206 | GroupMemoryBarrierWithGroupSync(); 207 | } 208 | 209 | // 210 | // testing valid possible combinations of descriptor layouts 211 | // 212 | 213 | // extent data 214 | struct extent_data { 215 | float3 pos; 216 | float3 extent; 217 | }; 218 | 219 | // draw data 220 | struct draw_data { 221 | float3x4 world_matrix; 222 | }; 223 | 224 | // structures of arrays for indriect / bindless lookups 225 | StructuredBuffer draws[] : register(t0, space0); 226 | StructuredBuffer extents[] : register(t0, space2); 227 | 228 | Texture2D textures : register(t1, space0); 229 | TextureCube volume_textures : register(t1, space1); 230 | 231 | vs_output vs_test_pipeline_layout(vs_input input) { 232 | vs_output output = vs_output_default(); 233 | 234 | pmfx_touch(draws); 235 | 236 | return output; 237 | } 238 | 239 | ps_output ps_test_pipeline_layout(vs_output input) { 240 | ps_output output; 241 | 242 | pmfx_touch(extents); 243 | pmfx_touch(textures); 244 | pmfx_touch(volume_textures); 245 | 246 | output.colour = float4(0.0, 0.0, 0.0, 0.0); 247 | return output; 248 | } 249 | 250 | // 251 | // test output to SV_Target without struct 252 | // 253 | 254 | float4 ps_output_semantic(vs_output input) : SV_Target { 255 | return float4(0.0, 0.0, 0.0, 0.0); 256 | } -------------------------------------------------------------------------------- /examples/v2/v2_examples.pmfx: -------------------------------------------------------------------------------- 1 | { 2 | include: [ 3 | "v2_examples.hlsl" 4 | ] 5 | depth_stencil_states: { 6 | depth_test_less: { 7 | } 8 | depth_test_never: { 9 | } 10 | } 11 | render_target_blend_states: { 12 | disabled: { 13 | blend_enabled: false 14 | } 15 | additive: { 16 | blend_enabled: true 17 | src_blend: One 18 | dst_blend: One 19 | } 20 | alpha: { 21 | blend_enabled: true 22 | src_blend: SrcAlpha 23 | dst_blend: InvSrcAlpha 24 | } 25 | } 26 | blend_states: { 27 | mrt_blend: { 28 | render_target: [ 29 | "additive" 30 | "alpha" 31 | ] 32 | } 33 | } 34 | pipelines: { 35 | basic_lit: { 36 | vs: vs_main 37 | ps: ps_main 38 | depth_stencil_state: depth_test_less 39 | vertex_layout: { 40 | vs_input: { 41 | input_slot: 1 42 | } 43 | instance_input: { 44 | input_slot: 2, 45 | input_slot_class: "PerInstance" 46 | } 47 | } 48 | } 49 | basic_lit_2: { 50 | vs: vs_main_mixed_semantics 51 | ps: ps_main 52 | depth_stencil_state: depth_test_never 53 | } 54 | basic_lit_3: { 55 | vs: vs_main_separate_elements 56 | ps: ps_main 57 | depth_stencil_state: depth_test_never 58 | } 59 | basic_lit_4: { 60 | vs: vs_main_permutations 61 | ps: ps_main 62 | depth_stencil_state: depth_test_never 63 | permutations: { 64 | SKINNED: [31, [0,1]], 65 | INSTANCED: [30, [0,1]] 66 | } 67 | } 68 | blend: { 69 | vs: vs_main 70 | ps: ps_main 71 | blend_state: mrt_blend 72 | } 73 | blend_without_mrt: { 74 | vs: vs_main 75 | ps: ps_main 76 | blend_state: alpha 77 | } 78 | bindless: { 79 | vs: vs_test_bindless_aliasing 80 | ps: ps_test_bindless_aliasing 81 | } 82 | test_cbuffer_unscoped: { 83 | vs: vs_test_use_cbuffer_unscoped 84 | ps: ps_main 85 | } 86 | test_cbuffer_scoped: { 87 | vs: vs_test_use_cbuffer_scoped 88 | ps: ps_main 89 | } 90 | test_nested_structures: { 91 | vs: vs_test_nested_structures 92 | ps: ps_main 93 | } 94 | test_desriptor_layout: { 95 | vs: vs_test_pipeline_layout 96 | ps: ps_test_pipeline_layout 97 | } 98 | test_output_sematic: { 99 | vs: vs_main 100 | ps: ps_output_semantic 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /examples/v2/v2_includes.pmfx: -------------------------------------------------------------------------------- 1 | import v2_examples.pmfx 2 | { 3 | include: [ 4 | "v2_examples.hlsl" 5 | ] 6 | } -------------------------------------------------------------------------------- /jsn.py: -------------------------------------------------------------------------------- 1 | # lightweight json format without the need for quotes, allowing comments, file importing, inheritence and more 2 | # copyright Alex Dixon 2019: https://github.com/polymonster/jsn/blob/master/license 3 | 4 | import json 5 | import sys 6 | import os 7 | import traceback 8 | import platform 9 | 10 | 11 | # print error with colour 12 | def print_error(msg): 13 | ERROR = '\033[91m' 14 | ENDC = '\033[0m' 15 | print(ERROR + msg + ENDC, flush=True) 16 | 17 | 18 | # print wanring with colour 19 | def print_warning(msg): 20 | WARNING = '\033[93m' 21 | ENDC = '\033[0m' 22 | print(WARNING + msg + ENDC, flush=True) 23 | 24 | 25 | # print error with colour 26 | def print_ok(msg): 27 | OK = '\033[92m' 28 | ENDC = '\033[0m' 29 | print(OK + msg + ENDC, flush=True) 30 | 31 | 32 | # struct to store the build info for jobs from parsed commandline args 33 | class BuildInfo: 34 | inputs = [] # list of files 35 | import_dirs = [] # lst of import directories to search 36 | output_dir = "" # output directory 37 | print_out = False # print out the resulting json from jsn to the console 38 | keep_vars = False # keep jsn_vars int the resulting json, set to false to pop them 39 | 40 | 41 | # parse command line args passed in 42 | def parse_args(): 43 | info = BuildInfo() 44 | if len(sys.argv) == 1: 45 | display_help() 46 | for i in range(1, len(sys.argv)): 47 | if sys.argv[i] == "-i": 48 | j = i + 1 49 | while j < len(sys.argv) and sys.argv[j][0] != '-': 50 | info.inputs.append(sys.argv[j]) 51 | j = j + 1 52 | i = j 53 | elif sys.argv[i] == "-I": 54 | j = i + 1 55 | while j < len(sys.argv) and sys.argv[j][0] != '-': 56 | info.import_dirs.append(sys.argv[j]) 57 | j = j + 1 58 | i = j 59 | elif sys.argv[i] == "-o": 60 | info.output_dir = sys.argv[i + 1] 61 | elif sys.argv[i] == "-p": 62 | info.print_out = True 63 | elif sys.argv[i] == "-keep_vars": 64 | info.keep_vars = True 65 | return info 66 | 67 | 68 | # help 69 | def display_help(): 70 | print("commandline arguments:") 71 | print(" -help display this message") 72 | print(" -i list of input files or directories to process") 73 | print(" -o output file or directory") 74 | print(" -I list of import directories, to search for imports") 75 | print(" -p print output to console") 76 | print(" -keep_vars keep jsn_vars in the output json") 77 | 78 | 79 | # do c like (u32)-1 80 | def us(v): 81 | if v == -1: 82 | return sys.maxsize 83 | return v 84 | 85 | 86 | # return string inside "quotes" to make code gen cleaner 87 | def in_quotes(string): 88 | if len(string) >= 2: 89 | if string[0] == "\"": 90 | return string 91 | return '"' + string + '"' 92 | 93 | 94 | # create a new dir for a file or a folder if it doesnt already exist and not throw an exception 95 | def create_dir(dst_file): 96 | dir = dst_file 97 | if is_file(dir): 98 | dir = os.path.dirname(dir) 99 | if len(dir) == 0: 100 | return 101 | os.makedirs(dir, exist_ok=True) 102 | 103 | 104 | # change extension 105 | def change_ext(file, ext): 106 | return os.path.splitext(file)[0] + ext 107 | 108 | 109 | # is_file 110 | def is_file(file): 111 | if len(os.path.splitext(file)[1]) > 0: 112 | return True 113 | return False 114 | 115 | 116 | # python json style dumps 117 | def format(jsn, indent=4): 118 | nl = ["{", "[", ","] 119 | el = ["}", "]"] 120 | id = ["{", "["] 121 | fmt = "" 122 | cur_indent = 0 123 | str_list = find_strings(jsn) 124 | for c in range(0, len(jsn)): 125 | char = jsn[c] 126 | if is_inside_quotes(str_list, c): 127 | fmt += char 128 | continue 129 | if char in el: 130 | fmt += "\n" 131 | cur_indent -= 4 132 | for i in range(0, cur_indent): 133 | fmt += " " 134 | fmt += char 135 | if char in nl: 136 | fmt += "\n" 137 | if char in id: 138 | cur_indent += 4 139 | for i in range(0, cur_indent): 140 | fmt += " " 141 | if char == ":": 142 | fmt += " " 143 | return fmt 144 | 145 | 146 | # check whether char jsn[pos] is inside quotes or not 147 | def is_inside_quotes(str_list, pos): 148 | for s in str_list: 149 | if pos < s[0]: 150 | break 151 | if s[0] < pos < s[1]: 152 | return s[1]+1 153 | return 0 154 | 155 | 156 | # find all string tokens within jsn source marked by start and end index 157 | def find_strings(jsn): 158 | quote_types = ["\"", "'"] 159 | oq = "" 160 | prev_char = "" 161 | istart = -1 162 | str_list = [] 163 | for ic in range(0, len(jsn)): 164 | c = jsn[ic] 165 | if c in quote_types: 166 | if oq == "": 167 | oq = c 168 | istart = ic 169 | elif oq == c and prev_char != "\\": 170 | oq = "" 171 | str_list.append((istart, ic)) 172 | if prev_char == "\\" and c == "\\": 173 | prev_char = "" 174 | else: 175 | prev_char = c 176 | return str_list 177 | 178 | 179 | # trims whitespace from lines 180 | def trim_whitespace(jsn): 181 | lines = jsn.split('\n') 182 | trimmed = "" 183 | for l in lines: 184 | trimmed += l.strip() + "\n" 185 | return trimmed 186 | 187 | 188 | # remove whitespace and newlines to simplify subsequent ops 189 | def clean_src(jsn): 190 | clean = "" 191 | inside_quotes = False 192 | for char in jsn: 193 | if char == '\"': 194 | inside_quotes = not inside_quotes 195 | if not inside_quotes: 196 | strip_char = char.strip() 197 | else: 198 | strip_char = char 199 | clean += strip_char 200 | return clean 201 | 202 | 203 | # remove comments, taken from https:/github.com/polymonster/stub-format/stub_format.py 204 | def remove_comments(file_data): 205 | lines = file_data.split("\n") 206 | inside_block = False 207 | conditioned = "" 208 | for line in lines: 209 | str_list = find_strings(line) 210 | if inside_block: 211 | ecpos = line.find("*/") 212 | if ecpos != -1: 213 | inside_block = False 214 | line = line[ecpos+2:] 215 | else: 216 | continue 217 | cpos = line.find("//") 218 | mcpos = line.find("/*") 219 | 220 | if is_inside_quotes(str_list, mcpos): 221 | mcpos = -1 222 | 223 | if is_inside_quotes(str_list, cpos): 224 | cpos = -1 225 | 226 | if cpos != -1: 227 | conditioned += line[:cpos] + "\n" 228 | elif mcpos != -1: 229 | conditioned += line[:mcpos] + "\n" 230 | inside_block = True 231 | else: 232 | conditioned += line + "\n" 233 | return conditioned 234 | 235 | 236 | # change single quotes to double quotes to support json5 237 | def change_quotes(jsn): 238 | str_list = find_strings(jsn) 239 | conditioned = "" 240 | prev = "" 241 | for c in range(0, len(jsn)): 242 | if c > 0: 243 | prev = jsn[c-1] 244 | char = jsn[c] 245 | if char == "\"": 246 | if is_inside_quotes(str_list, c): 247 | if prev != "\\": 248 | conditioned += "\\\"" 249 | continue 250 | if char == "'": 251 | if not is_inside_quotes(str_list, c): 252 | conditioned += "\"" 253 | continue 254 | conditioned += char 255 | return conditioned 256 | 257 | 258 | # remove line breaks within strings 259 | def collapse_line_breaks(jsn): 260 | str_list = find_strings(jsn) 261 | conditioned = "" 262 | skip = False 263 | for c in range(0, len(jsn)): 264 | char = jsn[c] 265 | if skip: 266 | skip = False 267 | continue 268 | if char == "\\" and c+1 < len(jsn) and jsn[c+1] == "\n": 269 | if is_inside_quotes(str_list, c): 270 | skip = True 271 | continue 272 | conditioned += char 273 | return conditioned 274 | 275 | 276 | # find first char in chars in string from pos 277 | def find_first(string, pos, chars): 278 | first = us(-1) 279 | for char in chars: 280 | first = min(us(string.find(char, pos)), first) 281 | return first 282 | 283 | 284 | # get value type, object, array, int, float, bool, hex, binary, binary shift 285 | def get_value_type(value): 286 | value = value.strip() 287 | if len(value) > 0: 288 | if value[0] == "\"": 289 | return "string" 290 | if value[0] == "{": 291 | return "object" 292 | if value[0] == "[": 293 | return "array" 294 | if value == 'true' or value == 'false': 295 | return "bool" 296 | if value.find(".") != -1: 297 | try: 298 | float(value) 299 | return "float" 300 | except ValueError: 301 | pass 302 | if value.find("0x") != -1: 303 | try: 304 | int(value[2:], 16) 305 | return "hex" 306 | except ValueError: 307 | pass 308 | if value.find("0b") != -1: 309 | try: 310 | int(value[2:], 2) 311 | return "binary" 312 | except ValueError: 313 | pass 314 | if value.find("<<") != -1 or value.find(">>") != -1: 315 | return "binary_shift" 316 | try: 317 | int(value) 318 | return "int" 319 | except ValueError: 320 | pass 321 | return "string" 322 | 323 | 324 | # find inherits inside unquoted objects - key(inherit_a, inherit_b) 325 | def get_inherits(object_key): 326 | if object_key[0] == "\"": 327 | return object_key, [] 328 | bp = object_key.find("(") 329 | if bp != -1: 330 | ep = object_key.find(")") 331 | i = object_key[bp+1:ep] 332 | ii = i.split(",") 333 | return object_key[:bp], ii 334 | return object_key, [] 335 | 336 | 337 | # finds the end of a pair of brackets, enclosing sub brackets inside them 338 | def enclose_brackets(open, close, string, pos): 339 | start = pos 340 | pos = string.find(open, pos) 341 | stack = [open] 342 | pos += 1 343 | while len(stack) > 0 and pos < len(string): 344 | if string[pos] == open: 345 | stack.append(open) 346 | if string[pos] == close: 347 | stack.pop() 348 | pos += 1 349 | return pos 350 | 351 | 352 | # add quotes and convert values to be json compatible 353 | def quote_value(value, pos, next): 354 | quoted = "" 355 | if get_value_type(value) == "string": 356 | quoted += in_quotes(value) 357 | pos = next 358 | elif get_value_type(value) == "hex": 359 | hex_value = int(value[2:], 16) 360 | quoted = str(hex_value) 361 | pos = next 362 | elif get_value_type(value) == "binary": 363 | bin_value = int(value[2:], 2) 364 | quoted = str(bin_value) 365 | pos = next 366 | elif get_value_type(value) == "binary_shift": 367 | components = value.split("|") 368 | bv = 0 369 | for comp in components: 370 | if comp.find("<<") != -1: 371 | comp = comp.split("<<") 372 | bv |= int(comp[0]) << int(comp[1]) 373 | elif comp.find(">>") != -1: 374 | comp = comp.split(">>") 375 | bv |= int(comp[0]) << int(comp[1]) 376 | else: 377 | bv |= int(comp) 378 | quoted = str(bv) 379 | pos = next 380 | elif get_value_type(value) == "float": 381 | f = value 382 | if f[0] == ".": 383 | f = "0" + f 384 | elif f[len(f)-1] == ".": 385 | f = f + "0" 386 | quoted = f 387 | pos = next 388 | elif get_value_type(value) == "int": 389 | i = value 390 | if i[0] == "+": 391 | i = i[1:] 392 | quoted = i 393 | pos = next 394 | return (quoted, pos) 395 | 396 | 397 | # add quotes to array items 398 | def quote_array(jsn): 399 | if not jsn: 400 | return "[" + jsn + "]" 401 | # arrays can contain mixed data so go element wise 402 | pos = 0 403 | element_wise = "" 404 | while True: 405 | elem_end = jsn.find(",", pos) 406 | if elem_end == -1: 407 | elem_end = len(jsn) 408 | elem = jsn[pos:elem_end].strip() 409 | if len(elem) == 0: 410 | break 411 | if get_value_type(elem) == "object": 412 | elem_end = enclose_brackets("{", "}", jsn, pos) 413 | sub_object = jsn[pos:elem_end] 414 | element_wise += quote_object(sub_object) 415 | elif get_value_type(elem) == "array": 416 | elem_end = enclose_brackets("[", "]", jsn, pos) 417 | sub_array = jsn[pos+1:elem_end-1] 418 | element_wise += quote_array(sub_array) 419 | elif elem[0] == '\"': 420 | elem_end += enclose_brackets("\"", "\"", jsn, pos) 421 | element_wise = jsn[pos:elem_end] 422 | elif elem == "null": 423 | element_wise += "null" 424 | else: 425 | element_wise += quote_value(elem, 0, 0)[0] 426 | if elem_end == len(jsn): 427 | break 428 | pos = elem_end+1 429 | if pos >= len(jsn): 430 | break 431 | element_wise += "," 432 | return "[" + element_wise + "]" 433 | 434 | 435 | # add quotes to unquoted keys, strings and strings in arrays 436 | def quote_object(jsn): 437 | delimiters = [",", "{"] 438 | pos = 0 439 | quoted = "" 440 | str_list = find_strings(jsn) 441 | while True: 442 | cur = pos 443 | pos = jsn.find(":", pos) 444 | if pos == -1: 445 | quoted += jsn[cur:] 446 | break 447 | # ignore : inside quotes 448 | iq = is_inside_quotes(str_list, pos) 449 | if iq: 450 | quoted += jsn[cur:iq] + ":" 451 | pos = iq + 1 452 | continue 453 | delim = 0 454 | for d in delimiters: 455 | dd = jsn[:pos].rfind(d) 456 | if dd != -1: 457 | delim = max(dd, delim) 458 | key = jsn[delim+1:pos].strip() 459 | # make sure we arent inside brackets, for multiple inheritence 460 | if key.find(")") != -1: 461 | bp = us(jsn[:pos].rfind("(")) 462 | ep = jsn.find(")", delim) 463 | if bp < delim < ep: 464 | delim = 0 465 | for d in delimiters: 466 | dd = jsn[:bp].rfind(d) 467 | if dd != -1: 468 | delim = max(dd, delim) 469 | key = jsn[delim + 1:pos].strip() 470 | pos += 1 471 | next = find_first(jsn, pos, [",", "]", "}"]) 472 | while is_inside_quotes(str_list, next): 473 | next = find_first(jsn, next+1, [",", "]", "}"]) 474 | # put key in quotes 475 | value = jsn[pos:next] 476 | inherit = "" 477 | if get_value_type(value) == "object": 478 | inherit = "{" 479 | pos += 1 480 | key, inherit_list = get_inherits(key) 481 | if len(inherit_list) > 0: 482 | inherit += in_quotes("jsn_inherit") + ": [" 483 | p = 0 484 | for i in inherit_list: 485 | if p > 0: 486 | inherit += ", " 487 | inherit += in_quotes(i.strip()) 488 | p += 1 489 | inherit += "]," 490 | qkey = in_quotes(key) 491 | quoted += jsn[cur:delim+1] 492 | quoted += qkey 493 | quoted += ":" 494 | quoted += inherit 495 | if get_value_type(value) == "array": 496 | end = enclose_brackets("[", "]", jsn, pos) 497 | quoted += quote_array(jsn[pos+1:end-1]) 498 | pos = end 499 | elif value == "null": 500 | quoted += "null" 501 | pos = pos + len("null") 502 | else: 503 | value = quote_value(value, pos, next) 504 | quoted += value[0] 505 | pos = value[1] 506 | return quoted 507 | 508 | 509 | # remove trailing commas from objects and arrays 510 | def remove_trailing_commas(jsn): 511 | trail = ["}", "]"] 512 | clean = "" 513 | for i in range(0, len(jsn)): 514 | j = i + 1 515 | char = jsn[i] 516 | if char == "," and j < len(jsn): 517 | if jsn[j] in trail: 518 | continue 519 | clean += char 520 | if clean[len(clean)-1] == ",": 521 | clean = clean[:len(clean)-1] 522 | return clean 523 | 524 | 525 | # inserts commas in place of newlines \n 526 | def add_new_line_commas(jsn): 527 | prev_char = "" 528 | corrected = "" 529 | ignore_previous = [",", ":", "{", "\n", "\\", "["] 530 | for char in jsn: 531 | if char == '\n' and prev_char not in ignore_previous: 532 | corrected += ',' 533 | corrected += char 534 | prev_char = char 535 | return corrected 536 | 537 | 538 | # inherit dict member wise 539 | def inherit_dict(dest, second): 540 | for k, v in second.items(): 541 | if type(v) == dict: 542 | if k not in dest or type(dest[k]) != dict: 543 | dest[k] = dict() 544 | inherit_dict(dest[k], v) 545 | else: 546 | if k not in dest: 547 | dest[k] = v 548 | 549 | 550 | # recursively merge dicts member wise 551 | def inherit_dict_recursive(d, d2): 552 | inherits = [] 553 | for k, v in d.items(): 554 | if k == "jsn_inherit": 555 | for i in v: 556 | inherits.append(i) 557 | if "jsn_inherit" in d.keys(): 558 | d.pop("jsn_inherit", None) 559 | for i in inherits: 560 | if i in d2.keys(): 561 | inherit_dict(d, d2[i]) 562 | else: 563 | print_error("[jsn error] missing key `" + i + "` used by jsn_inherit") 564 | sys.exit(1) 565 | for k, v in d.items(): 566 | if type(v) == dict: 567 | inherit_dict_recursive(v, d) 568 | 569 | 570 | # finds files to import (includes) 571 | def get_imports(jsn, import_dirs): 572 | imports = [] 573 | bp = jsn.find("{") 574 | head = jsn[:bp].split("\n") 575 | has_imports = False 576 | for i in head: 577 | if i.find("import") != -1: 578 | has_imports = True 579 | if not has_imports: 580 | return jsn[bp:], imports 581 | if not import_dirs: 582 | filedir = os.getcwd() 583 | import_dirs = [filedir] 584 | for i in head: 585 | if i.find("import") != -1: 586 | stripped = i[len("import"):].strip().strip("\"").strip() 587 | found = False 588 | for dir in import_dirs: 589 | import_path_dir = os.path.join(dir, stripped) 590 | if os.path.exists(import_path_dir): 591 | imports.append(import_path_dir) 592 | found = True 593 | break 594 | if not found: 595 | print_error("[jsn error]: cannot find import file " + stripped) 596 | sys.exit(1) 597 | return jsn[bp:], imports 598 | 599 | 600 | # finds all '${vars}' within a string returning in list [${va}, ${vb}, ...] 601 | def vars_in_string(string): 602 | pos = 0 603 | variables = [] 604 | while True: 605 | sp = string.find("${", pos) 606 | if sp != -1: 607 | ep = string.find("}", sp) 608 | variables.append(string[sp:ep + 1]) 609 | pos = sp + 2 610 | else: 611 | break 612 | return variables 613 | 614 | 615 | # resolves "${var}" into a typed value or a token pasted string, handle multiple vars within strings or arrays 616 | def resolve_vars(value, vars): 617 | value_string = str(value) 618 | vv = vars_in_string(value_string) 619 | count = 0 620 | for v in vv: 621 | var_name = v[2:len(v)-1] 622 | if var_name in vars.keys(): 623 | if type(value) == list: 624 | nl = list() 625 | for i in value: 626 | ri = resolve_vars(i, vars) 627 | if ri: 628 | nl.append(resolve_vars(i, vars)) 629 | else: 630 | nl.append(i) 631 | return nl 632 | else: 633 | if type(vars[var_name]) == str: 634 | value = value.replace(v, vars[var_name]) 635 | if len(vv) == count+1: 636 | return value 637 | else: 638 | return vars[var_name] 639 | else: 640 | print_error("[jsn error] undefined variable '" + var_name + "'") 641 | sys.exit(1) 642 | count += 1 643 | return None 644 | 645 | 646 | # replace ${} with variables in vars 647 | def resolve_vars_recursive(d, vars, keep_vars=False): 648 | stack_vars = vars.copy() 649 | if "jsn_vars" in d.keys(): 650 | for vk in d["jsn_vars"].keys(): 651 | stack_vars[vk] = d["jsn_vars"][vk] 652 | for k in d.keys(): 653 | value = d[k] 654 | if type(value) == dict: 655 | resolve_vars_recursive(d[k], stack_vars, keep_vars) 656 | elif type(value) == list: 657 | resolved_list = [] 658 | for i in value: 659 | ri = resolve_vars(i, stack_vars) 660 | if ri: 661 | resolved_list.append(ri) 662 | else: 663 | resolved_list.append(i) 664 | d[k] = resolved_list 665 | else: 666 | var = resolve_vars(d[k], stack_vars) 667 | if var: 668 | d[k] = var 669 | if "jsn_vars" in d.keys() and not keep_vars: 670 | d.pop("jsn_vars", None) 671 | 672 | 673 | # resolve platform specific keys, merging 674 | def resolve_platform_keys_recursive(d, platform_name): 675 | rm_keys = [] 676 | platform_dict = dict() 677 | for k in d.keys(): 678 | bp = k.find("<") 679 | ep = k.find(">") 680 | if bp != -1 and ep != -1: 681 | key_platform = k[bp+1:ep] 682 | key_base = k[:bp] 683 | if key_platform == platform_name: 684 | platform_dict[key_base] = d[k] 685 | rm_keys.append(k) 686 | value = d[k] 687 | if type(value) == dict: 688 | resolve_platform_keys_recursive(d[k], platform_name) 689 | for k in rm_keys: 690 | d.pop(k) 691 | inherit_dict(d, platform_dict) 692 | 693 | 694 | # check platform name and then recurse through dictionary selecting our current platform keys 695 | def resolve_platform_keys(d): 696 | name_lookup = { 697 | "Linux": "linux", 698 | "Darwin": "mac", 699 | "Windows": "windows" 700 | } 701 | platform_name = "unknown" 702 | if platform.system() in name_lookup: 703 | platform_name = name_lookup[platform.system()] 704 | else: 705 | print_warning("[jsn warning] unknown platform system " + platform.system()) 706 | resolve_platform_keys_recursive(d, platform_name) 707 | 708 | 709 | # load from file 710 | def load_from_file(filepath, import_dirs, keep_vars): 711 | jsn_contents = open(filepath).read() 712 | filepath = os.path.join(os.getcwd(), filepath) 713 | import_dirs.append(os.path.dirname(filepath)) 714 | return loads(jsn_contents, import_dirs, keep_vars=keep_vars) 715 | 716 | 717 | # convert jsn to json 718 | def loads(jsn, import_dirs=None, vars=True, keep_vars=False): 719 | jsn, imports = get_imports(jsn, import_dirs) 720 | jsn = remove_comments(jsn) 721 | jsn = change_quotes(jsn) 722 | jsn = trim_whitespace(jsn) 723 | jsn = add_new_line_commas(jsn) 724 | jsn = collapse_line_breaks(jsn) 725 | jsn = clean_src(jsn) 726 | jsn = quote_object(jsn) 727 | jsn = remove_trailing_commas(jsn) 728 | jsn = format(jsn) 729 | 730 | # validate 731 | try: 732 | j = json.loads(jsn) 733 | except: 734 | jsn_lines = jsn.split("\n") 735 | for l in range(0, len(jsn_lines)): 736 | print(str(l+1) + " " + jsn_lines[l]) 737 | traceback.print_exc() 738 | sys.exit(1) 739 | 740 | # import 741 | for i in imports: 742 | include_dict = loads(open(i, "r").read(), import_dirs, False, keep_vars=keep_vars) 743 | 744 | # nested var lookups, only works currently on special vars 745 | if "jsn_vars" in include_dict.keys(): 746 | vv = json.dumps(include_dict["jsn_vars"]) 747 | vv = vv.replace("${script_dir}", os.path.dirname(i)) 748 | vv = vv.replace("\\", "/") 749 | include_dict["jsn_vars"] = json.loads(vv) 750 | 751 | inherit_dict(j, include_dict) 752 | 753 | # resolve platform specific keys 754 | resolve_platform_keys(j) 755 | 756 | # inherit 757 | inherit_dict_recursive(j, j) 758 | 759 | # resolve vars 760 | if vars: 761 | resolve_vars_recursive(j, dict(), keep_vars) 762 | 763 | return j 764 | 765 | 766 | # return a list of all imports for this file 767 | def get_import_file_list(filepath, import_dirs=None): 768 | jsn = open(filepath, "r").read() 769 | jsn, imports = get_imports(jsn, import_dirs) 770 | for i in imports: 771 | recursive_imports = get_import_file_list(i, import_dirs) 772 | for ri in recursive_imports: 773 | if ri not in imports: 774 | imports.append(ri) 775 | abs_imports = [] 776 | for i in imports: 777 | abs_imports.append(os.path.normpath(os.path.join(os.getcwd(), i))) 778 | return abs_imports 779 | 780 | 781 | # convert jsn to json and write to a file 782 | def convert_jsn(info, input_file, output_file): 783 | print("converting: " + input_file + " to " + output_file) 784 | output_file = open(output_file, "w+") 785 | jdict = load_from_file(input_file, info.import_dirs, info.keep_vars) 786 | if info.print_out: 787 | print(json.dumps(jdict, indent=4)) 788 | output_file.write(json.dumps(jdict, indent=4)) 789 | output_file.close() 790 | 791 | 792 | def main(): 793 | info = parse_args() 794 | if len(info.inputs) == 0 or not info.output_dir: 795 | display_help() 796 | sys.exit(1) 797 | for i in info.inputs: 798 | if os.path.isdir(i): 799 | for root, dirs, files in os.walk(i): 800 | for file in files: 801 | output_file = info.output_dir 802 | if not is_file(output_file): 803 | output_file = os.path.join(info.output_dir, file) 804 | output_file = change_ext(output_file, ".json") 805 | create_dir(output_file) 806 | convert_jsn(info, os.path.join(root, file), output_file) 807 | else: 808 | output_file = info.output_dir 809 | if not is_file(output_file): 810 | output_file = os.path.join(info.output_dir, i) 811 | output_file = change_ext(output_file, ".json") 812 | create_dir(output_file) 813 | convert_jsn(info, i, output_file) 814 | 815 | 816 | # output .jsn files as json, 817 | if __name__ == "__main__": 818 | main() 819 | 820 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Dixon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /platform/glsl.h: -------------------------------------------------------------------------------- 1 | // precision qualifiers 2 | #ifdef GLES 3 | precision highp float; 4 | precision highp samplerCube; 5 | precision highp sampler2D; 6 | #endif 7 | 8 | #ifdef GLES3 9 | precision highp sampler2DShadow; 10 | precision highp sampler2DArray; 11 | precision highp sampler2DArrayShadow; 12 | precision highp sampler3D; 13 | #endif 14 | 15 | #ifdef PMFX_GLES_COMPUTE 16 | precision highp image2D; 17 | #endif 18 | 19 | #ifdef PMFX_GL_OES_EGL_image_external 20 | precision highp samplerExternalOES; 21 | #endif 22 | 23 | // defs 24 | #define float4x4 mat4 25 | #define float3x3 mat3 26 | #define float2x2 mat2 27 | #define float4 vec4 28 | #define float3 vec3 29 | #define float2 vec2 30 | #define uint4 uvec4 31 | #define uint3 uvec3 32 | #define uint2 uvec2 33 | #define int4 ivec4 34 | #define int3 ivec3 35 | #define int2 ivec2 36 | #define read3 ivec3 37 | #define read2 ivec2 38 | #define modf mod 39 | #define fmod mod 40 | #define frac fract 41 | #define lerp mix 42 | #define mul( A, B ) ((A) * (B)) 43 | #define mul_tbn( A, B ) ((B) * (A)) 44 | #define saturate( A ) (clamp( A, 0.0, 1.0 )) 45 | #define atan2( A, B ) (atan(A, B)) 46 | #define ddx dFdx 47 | #define ddy dFdy 48 | #define _pmfx_unroll 49 | #define _pmfx_loop 50 | 51 | // texture location binding is not supported on all glsl version's 52 | #ifdef PMFX_BINDING_POINTS 53 | #if PMFX_TEXTURE_OFFSET 54 | #define _tex_binding(sampler_index) layout(binding = sampler_index + PMFX_TEXTURE_OFFSET) 55 | #define _compute_tex_binding binding(layout_index) = layout_index + PMFX_TEXTURE_OFFSET 56 | #else 57 | #define _tex_binding(sampler_index) layout(binding = sampler_index) 58 | #define _compute_tex_binding(layout_index) binding = layout_index 59 | #endif 60 | #else 61 | #define _tex_binding(sampler_index) 62 | #endif 63 | 64 | // textures 65 | #define texture_2d( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler2D sampler_name 66 | #define texture_cube( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform samplerCube sampler_name 67 | 68 | // depth text formats for compare samples 69 | #define depth_2d( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler2DShadow sampler_name 70 | #define depth_cube( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform samplerCubeShadow sampler_name 71 | 72 | // non gles2 textures 73 | #ifndef GLES2 74 | #define texture_2d_array( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler2DArray sampler_name 75 | #define texture_3d( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler3D sampler_name 76 | #define depth_2d_array( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler2DArrayShadow sampler_name 77 | #else 78 | #define texture_2d_array( sampler_name, sampler_index ) uniform sampler2D sampler_name 79 | #define texture_3d( sampler_name, sampler_index ) uniform sampler2D sampler_name 80 | #define depth_2d_array( sampler_name, sampler_index ) uniform sampler2D sampler_name 81 | #endif 82 | 83 | // multisample texture 84 | #ifdef GLES 85 | #define sample_texture_2dms( sampler_name, x, y, fragment ) texture( sampler_name, vec2(0.0, 0.0) ) 86 | #define texture_2dms( type, samples, sampler_name, sampler_index ) uniform sampler2D sampler_name 87 | #define texture_cube_array( sampler_name, sampler_index ) uniform int sampler_name 88 | #define depth_cube_array( sampler_name, sampler_index ) uniform int sampler_name 89 | #else 90 | #define sample_texture_2dms( sampler_name, x, y, fragment ) texelFetch( sampler_name, ivec2( x, y ), fragment ) 91 | #define texture_2dms( type, samples, sampler_name, sampler_index ) _tex_binding(sampler_index) uniform sampler2DMS sampler_name 92 | #define texture_cube_array( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform samplerCubeArray sampler_name 93 | #define depth_cube_array( sampler_name, sampler_index ) _tex_binding(sampler_index) uniform samplerCubeArrayShadow sampler_name 94 | #endif 95 | 96 | // extension textures 97 | #ifdef PMFX_GL_OES_EGL_image_external 98 | #define texture_2d_external(name, sampler_index) uniform samplerExternalOES name 99 | #else 100 | #define texture_2d_external(name, sampler_index) texture_2d(name, sampler_index) 101 | #endif 102 | 103 | // compute 104 | #if !defined(GLES) || defined(PMFX_GLES_COMPUTE) 105 | #define texture_2d_rw( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform image2D image_name 106 | #define texture_2d_r( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform readonly image2D image_name 107 | #define texture_2d_w( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform writeonly image2D image_name 108 | #define texture_3d_rw( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform image3D image_name 109 | #define texture_3d_r( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform readonly image3D image_name 110 | #define texture_3d_w( image_name, layout_index ) texture3d_rw(image_name, layout_index) 111 | #define texture_2d_array_rw( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform image2DArray image_name 112 | #define texture_2d_array_r( image_name, layout_index ) layout (_compute_tex_binding(layout_index), rgba8) uniform readonly image2DArray image_name 113 | #define texture_2d_array_w( image_name, layout_index ) texture2d_array_rw(image_name, layout_index) 114 | #define read_texture( image_name, coord ) imageLoad(image_name, coord) 115 | #define write_texture( image_name, value, coord ) imageStore(image_name, coord, value) 116 | #define read_texture_array( image_name, coord, slice ) imageLoad(image_name, ivec3(coord.xy, slice)) 117 | #define write_texture_array( image_name, value, coord, slice ) imageStore(image_name, ivec3(coord.xy, slice), value) 118 | 119 | // bindless 120 | #define texture2d_table(name, type, dimension, register_index, space_index) _compute_tex_binding(register_index) uniform sampler2D name##dimension 121 | #define texture2d_rw_table(name, type, dimension, register_index, space_index) layout (_compute_tex_binding(register_index), rgba8) uniform image2D name##dimension 122 | #define cbuffer_table(name, type, dimension, register_index, space_index) layout(std430, binding=register_index) buffer name##_buffer { type name##dimension; } 123 | 124 | #endif 125 | 126 | #ifndef GLES 127 | #define structured_buffer(type, name, index) layout(std430, binding=index) buffer name##_buffer { type name[]; } 128 | #define structured_buffer_rw(type, name, index) layout(std430, binding=index) buffer name##_buffer { type name[]; } 129 | #define atomic_counter(name, index) structured_buffer_rw(uint, name, index) 130 | #else 131 | #ifdef PMFX_GLES_COMPUTE 132 | #define structured_buffer(type, name, index, buffer_name) layout(std430, binding=index) buffer buffer_name { type name[]; } 133 | #define structured_buffer_rw(type, name, index, buffer_name) layout(std430, binding=index) buffer buffer_name { type name[]; } 134 | #define atomic_counter(name, index, buffer_name) structured_buffer_rw(uint, name, index, buffer_name) 135 | #endif 136 | #endif 137 | 138 | // sampler 139 | #define sample_texture( sampler_name, V ) texture( sampler_name, V ) 140 | #define sample_texture_level( sampler_name, V, l ) textureLod( sampler_name, V, l ) 141 | #define sample_texture_grad( sampler_name, V, vddx, vddy ) textureGrad( sampler_name, V, vddx, vddy ) 142 | #define sample_texture_array( sampler_name, V, a ) texture( sampler_name, vec3(V, a) ) 143 | #define sample_texture_array_level( sampler_name, V, a, l ) textureLod( sampler_name, vec3(V, a), l ) 144 | #define sample_depth_compare( name, tc, compare_value ) texture( name, vec3(tc.xy, compare_value) ) 145 | #define sample_depth_compare_array( name, tc, a, compare_value ) texture( name, vec4(tc.xy, a, compare_value) ) 146 | #define sample_depth_compare_cube( name, tc, compare_value ) texture( name, vec4(tc.xyz, compare_value) ) 147 | 148 | // cube arrays are not supoorted on webgl / gles 3.0- 149 | #ifdef PMFX_TEXTURE_CUBE_ARRAY 150 | #define sample_texture_cube_array_level( sampler_name, V, a, l ) textureLod( sampler_name, vec4(V, a), l ) 151 | #define sample_texture_cube_array( sampler_name, V, a ) texture( sampler_name, vec4(V, a)) 152 | #define sample_depth_compare_cube_array( name, V, a, compare_value ) texture( name, vec4(V.xyz, a), compare_value ) 153 | #else 154 | #define sample_texture_cube_array_level( sampler_name, V, a, l ) vec4(0.0, 0.0, 0.0, 0.0) 155 | #define sample_texture_cube_array( sampler_name, V, a ) vec4(0.0, 0.0, 0.0, 0.0) 156 | #define sample_depth_compare_cube_array( name, V, a, compare_value ) vec4(0.0, 0.0, 0.0, 0.0) 157 | #endif 158 | 159 | // glsl 200 / gles 1.0 texture sample macros 160 | #define sample_texture_2d( sampler_name, V ) texture2D( sampler_name, V ) 161 | #define sample_texture_array_2d( sampler_name, V, a ) texture2D( sampler_name, V ) // not supported 162 | #define sample_texture_level_2d( sampler_name, V, l ) texture2DLod( sampler_name, V, l ) 163 | #define sample_texture_grad_2d( sampler_name, V, vddx, vddy ) texture2DLod( sampler_name, V, 0.0 ) 164 | #define sample_texture_cube( sampler_name, V ) textureCube( sampler_name, V ) 165 | #define sample_texture_level_cube( sampler_name, V, l ) textureCubeLod( sampler_name, V, l ) 166 | #define sample_texture_grad_cube( sampler_name, V, vddx, vddy ) textureCubeLod( sampler_name, V, 0.0 ) 167 | 168 | // matrix 169 | #define to_3x3( M4 ) float3x3(M4) 170 | #define from_columns_2x2(A, B) (transpose(float2x2(A, B))) 171 | #define from_rows_2x2(A, B) (float2x2(A, B)) 172 | #define from_columns_3x3(A, B, C) (transpose(float3x3(A, B, C))) 173 | #define from_rows_3x3(A, B, C) (float3x3(A, B, C)) 174 | #define from_columns_4x4(A, B, C, D) (transpose(float4x4(A, B, C, D))) 175 | #define from_rows_4x4(A, B, C, D) (float4x4(A, B, C, D)) 176 | #define unpack_vb_instance_mat( mat, r0, r1, r2, r3 ) mat[0] = r0; mat[1] = r1; mat[2] = r2; mat[3] = r3; 177 | #define to_data_matrix(mat) mat 178 | 179 | // clip 180 | #define remap_z_clip_space( d ) d // gl clip space is -1 to 1, and this is normalised device coordinate 181 | #define remap_depth( d ) (d = d * 0.5 + 0.5) 182 | #define remap_ndc_ray( r ) float2(r.x, r.y) 183 | #define depth_ps_output gl_FragDepth 184 | 185 | // atomics 186 | #if !defined(GLES) || defined(PMFX_GLES_COMPUTE) 187 | #define atomic_store(atomic, value) atomicExchange(atomic, value) 188 | #define atomic_increment(atomic, original) original = atomicAdd(atomic, 1u) 189 | #define atomic_decrement(atomic, original) original = atomicExchange(atomic, atomic - 1u) 190 | #define atomic_add(atomic, value, original) original = atomicAdd(atomic, value) 191 | #define atomic_subtract(atomic, value, original) original = atomicExchange(atomic, atomic - value) 192 | #define atomic_min(atomic, value, original) original = atomicMin(atomic, value) 193 | #define atomic_max(atomic, value, original) original = atomicMax(atomic, value) 194 | #define atomic_and(atomic, value, original) original = atomicAnd(atomic, value) 195 | #define atomic_or(atomic, value, original) original = atomicOr(atomic, value) 196 | #define atomic_xor(atomic, value, original) original = atomicXor(atomic, value) 197 | #define atomic_exchange(atomic, value, original) original = atomicExchange(atomic, value) 198 | #define threadgroup_barrier() barrier() 199 | #define device_barrier() barrier() 200 | #endif 201 | -------------------------------------------------------------------------------- /platform/hlsl.h: -------------------------------------------------------------------------------- 1 | // texture 2 | #define texture_2d_rw( name, index ) RWTexture2D name : register(u##index) 3 | #define texture_2d_r( name, index ) Texture2D name : register(t##index) 4 | #define texture_2d_w( name, index ) texture_2d_rw( name, index ) 5 | #define texture_3d_rw( name, index ) RWTexture3D name : register(u##index) 6 | #define texture_3d_r( name, index ) Texture3D name : register(t##index) 7 | #define texture_3d_w( name, index ) texture_3d_rw( name, index ) 8 | #define texture_2d_array_rw( name, index ) RWTexture2DArray : register(u##index) 9 | #define texture_2d_array_r( name, index ) Texture2DArray name : register(t##index) 10 | #define texture_2d_array_w( name, index ) texture_2d_array_rw(name, index) 11 | #define read_texture( name, gid ) name[gid] 12 | #define write_texture( name, val, gid ) name[gid] = val 13 | #define read_texture_array( name, gid, slice ) name[uint3(gid.xy, slice)] 14 | #define write_texture_array( name, val, gid, slice ) name[uint3(gid.xy, slice)] = val 15 | #define texture_2d( name, sampler_index ) Texture2D name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 16 | #define texture_3d( name, sampler_index ) Texture3D name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 17 | #define texture_2dms( type, samples, name, sampler_index ) Texture2DMS name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 18 | #define texture_cube( name, sampler_index ) TextureCube name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 19 | #define texture_2d_array( name, sampler_index ) Texture2DArray name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 20 | #define texture_cube_array( name, sampler_index ) TextureCubeArray name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index) 21 | #define texture_2d_external( name, sampler_index ) texture_2d( name, sampler_index ) 22 | 23 | // sampler 24 | #define sampler_state(name, sampler_index) SamplerState name : register(s##sampler_index) 25 | #define sampler_state_table(name, dimension, sampler_index) SamplerState name##dimension : register(s##sampler_index) 26 | 27 | // bindless resources 28 | #define texture2d_table(name, type, dimension, register_index, space_index) Texture2D name##dimension : register(t##register_index, space##space_index) 29 | #define texture2d_rw_table(name, type, dimension, register_index, space_index) RWTexture2D name##dimension : register(t##register_index, space##space_index) 30 | #define cbuffer_table(name, type, dimension, register_index, space_index) ConstantBuffer name##dimension : register(b##register_index, space##space_index) 31 | 32 | // depth texture (required for gl and metal) 33 | #define depth_2d( name, sampler_index ) Texture2D name : register(t##sampler_index); ; SamplerComparisonState sampler_##name : register(s##sampler_index) 34 | #define depth_2d_array( name, sampler_index ) Texture2DArray name : register(t##sampler_index); ; SamplerComparisonState sampler_##name : register(s##sampler_index) 35 | #define depth_cube( name, sampler_index ) TextureCube name : register(t##sampler_index); ; SamplerComparisonState sampler_##name : register(s##sampler_index) 36 | #define depth_cube_array( name, sampler_index ) TextureCubeArray name : register(t##sampler_index); ; SamplerComparisonState sampler_##name : register(s##sampler_index) 37 | 38 | // structured buffer 39 | #define structured_buffer_rw( type, name, index ) RWStructuredBuffer name : register(u##index) 40 | #define structured_buffer( type, name, index ) StructuredBuffer name : register(t##index) 41 | 42 | // combined texture samplers 43 | #define sample_texture_2dms( name, x, y, fragment ) name.Load( int2(x, y), int(fragment) ) 44 | #define sample_texture( name, V ) name.Sample(sampler_##name, V) 45 | #define sample_texture_level( name, V, l ) name.SampleLevel(sampler_##name, V, l) 46 | #define sample_texture_grad( name, V, vddx, vddy ) name.SampleGrad(sampler_##name, V, vddx, vddy ) 47 | #define sample_texture_array( name, V, a ) name.Sample(sampler_##name, float3(V.xy, a) ) 48 | #define sample_texture_array_level( name, V, a, l ) name.SampleLevel(sampler_##name, float3(V.xy, a), l) 49 | #define sample_texture_cube_array( name, V, a ) name.Sample(sampler_##name, float4(V.xyz, a) ) 50 | #define sample_texture_cube_array_level( name, V, a, l ) name.SampleLevel(sampler_##name, float4(V.xyz, a), l) 51 | 52 | // separate sampler / textures 53 | #define texture_sample(texture, sampler, coord) texture.Sample(sampler, coord) 54 | 55 | // gather / compare 56 | #define sample_depth_compare( name, tc, compare_value ) saturate(name.SampleCmp(sampler_##name, tc, compare_value)) 57 | #define sample_depth_compare_array( name, tc, a, compare_value ) saturate(name.SampleCmp(sampler_##name, float3(tc.xy, a), compare_value)) 58 | #define sample_depth_compare_cube( name, tc, compare_value ) saturate(name.SampleCmp(sampler_##name, tc, compare_value)) 59 | #define sample_depth_compare_cube_array( name, tc, a, compare_value ) saturate(name.SampleCmp(sampler_##name, float4(tc.x, tc.y, tc.z, a), compare_value)) 60 | 61 | // matrix 62 | #define to_3x3( M4 ) ((float3x3)M4) 63 | #define from_columns_2x2(A, B) (float2x2(A, B)) 64 | #define from_rows_2x2(A, B) (transpose(float2x2(A, B))) 65 | #define from_columns_3x3(A, B, C) (float3x3(A, B, C)) 66 | #define from_rows_3x3(A, B, C) (transpose(float3x3(A, B, C))) 67 | #define from_columns_4x4(A, B, C, D) (float4x4(A, B, C, D)) 68 | #define from_rows_4x4(A, B, C, D) (transpose(float4x4(A, B, C, D))) 69 | #define mul_tbn( A, B ) mul(A, B) 70 | #define unpack_vb_instance_mat( mat, r0, r1, r2, r3 ) mat[0] = r0; mat[1] = r1; mat[2] = r2; mat[3] = r3; mat = transpose(mat) 71 | #define to_data_matrix(mat) transpose(mat) 72 | 73 | // clip 74 | #define remap_z_clip_space( d ) (d = d * 0.5 + 0.5) 75 | #define remap_depth( d ) (d) 76 | #define remap_ndc_ray( r ) float2(r.x, r.y * -1.0) 77 | 78 | // defs 79 | #define mod(x, y) (x - y * floor(x/y)) 80 | #define fract frac 81 | #define _pmfx_unroll [unroll] 82 | #define _pmfx_loop [loop] 83 | #define mix lerp 84 | #pragma warning( disable : 3078) // 'i': loop control variable conflicts with a previous declaration 85 | #pragma warning( disable : 4000) // use of potentially uninitialized variable (from function return) 86 | #define read3 uint3 87 | #define read2 uint2 88 | 89 | // atomics 90 | #define atomic_uint uint 91 | #define atomic_int int 92 | #define atomic_counter(name, index) RWStructuredBuffer name : register(u##index) 93 | #define atomic_load(atomic) atomic 94 | #define atomic_store(atomic, value) atomic = value 95 | #define atomic_increment(atomic, original) InterlockedAdd(atomic, 1, original) 96 | #define atomic_decrement(atomic, original) InterlockedAdd(atomic, (int)-1, original) 97 | #define atomic_add(atomic, value, original) InterlockedAdd(atomic, value, original) 98 | #define atomic_subtract(atomic, value, original) InterlockedAdd(atomic, value, original) 99 | #define atomic_min(atomic, value, original) InterlockedAdd(atomic, (int)value, original) 100 | #define atomic_max(atomic, value, original) InterlockedMin(atomic, value, original) 101 | #define atomic_and(atomic, value, original) InterlockedMax(atomic, value, original) 102 | #define atomic_or(atomic, value, original) InterlockedOr(atomic, value, original) 103 | #define atomic_xor(atomic, value, original) InterlockedXor(atomic, value, original) 104 | #define atomic_exchange(atomic, value, original) InterlockedExchange(atomic, value, original) 105 | #define threadgroup_barrier GroupMemoryBarrierWithGroupSync 106 | #define device_barrier DeviceMemoryBarrierWithGroupSync 107 | -------------------------------------------------------------------------------- /platform/metal.h: -------------------------------------------------------------------------------- 1 | // compute texture 2 | #define texture_2d_rw( name, index ) texture2d name [[texture(index)]] 3 | #define texture_2d_r( name, index ) texture2d name [[texture(index)]] 4 | #define texture_2d_w( name, index ) texture2d name [[texture(index)]] 5 | #define texture_3d_rw( name, index ) texture3d name [[texture(index)]] 6 | #define texture_3d_r( name, index ) texture3d name [[texture(index)]] 7 | #define texture_3d_w( name, index ) texture3d name [[texture(index)]] 8 | #define texture_2d_array_rw( name, index ) texture2d_array name [[texture(index)]] 9 | #define texture_2d_array_r( name, index ) texture2d_array name [[texture(index)]] 10 | #define texture_2d_array_w( name, index ) texture2d_array name [[texture(index)]] 11 | #define read_texture( name, gid ) name.read(gid) 12 | #define write_texture( name, val, gid ) name.write(val, gid) 13 | #define read_texture_array( name, gid, slice ) name.read(gid, uint(slice)) 14 | #define write_texture_array( name, val, gid, slice ) name.write(val, gid, uint(slice)) 15 | 16 | // standard texture 17 | #define texture_2d( name, sampler_index ) texture2d name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 18 | #define texture_3d( name, sampler_index ) texture3d name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 19 | #define texture_2dms( type, samples, name, sampler_index ) texture2d_ms name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 20 | #define texture_cube( name, sampler_index ) texturecube name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 21 | #define texture_2d_array( name, sampler_index ) texture2d_array name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 22 | #define texture_cube_array( name, sampler_index ) texturecube_array name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 23 | #define texture_2d_external( name, sampler_index ) texture_2d( name, sampler_index ) 24 | 25 | // depth texture 26 | #define depth_2d( name, sampler_index ) depth2d name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 27 | #define depth_2d_array( name, sampler_index ) depth2d_array name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 28 | #define depth_cube( name, sampler_index ) depthcube name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 29 | #define depth_cube_array( name, sampler_index ) depthcube_array name [[texture(sampler_index)]], sampler sampler_##name [[sampler(sampler_index)]] 30 | 31 | // _arg macros are used to pass textures through functions from main 32 | #define texture_2d_arg( name ) thread texture2d& name, thread sampler& sampler_##name 33 | #define texture_2d_external_arg( name ) texture_2d_arg(name) 34 | #define texture_3d_arg( name ) thread texture3d& name, thread sampler& sampler_##name 35 | #define texture_2dms_arg( name ) thread texture2d_ms& name, thread sampler& sampler_##name 36 | #define texture_cube_arg( name ) thread texturecube& name, thread sampler& sampler_##name 37 | #define texture_2d_array_arg( name ) thread texture2d_array& name, thread sampler& sampler_##name 38 | #define texture_cube_array_arg( name ) thread texturecube_array& name, thread sampler& sampler_##name 39 | #define depth_2d_arg( name ) thread depth2d& name, thread sampler& sampler_##name 40 | #define depth_2d_array_arg( name ) thread depth2d_array& name, thread sampler& sampler_##name 41 | #define depth_cube_arg( name ) thread depthcube& name, thread sampler& sampler_##name 42 | #define depth_cube_array_arg( name ) thread depthcube_array& name, thread sampler& sampler_##name 43 | 44 | // structured buffers 45 | #define structured_buffer_rw( type, name, index ) device type* name [[buffer(index+BUF_OFFSET)]] 46 | #define structured_buffer_rw_arg( type, name, index ) device type* name [[buffer(index+BUF_OFFSET)]] 47 | #define structured_buffer( type, name, index ) constant type* name [[buffer(index+BUF_OFFSET)]] 48 | #define structured_buffer_arg( type, name, index ) constant type* name [[buffer(index+BUF_OFFSET)]] 49 | 50 | // sampler 51 | #define sample_texture( name, tc ) name.sample(sampler_##name, tc) 52 | #define sample_texture_2dms( name, x, y, fragment ) name.read(uint2(x, y), fragment) 53 | #define sample_texture_level( name, tc, l ) name.sample(sampler_##name, tc, level(l)) 54 | #define sample_texture_grad( name, tc, vddx, vddy ) name.sample(sampler_##name, tc, gradient3d(vddx, vddy)) 55 | #define sample_texture_array( name, tc, a ) name.sample(sampler_##name, tc, uint(a)) 56 | #define sample_texture_array_level( name, tc, a, l ) name.sample(sampler_##name, tc, uint(a), level(l)) 57 | #define sample_texture_cube_array( name, tc, a ) sample_texture_array(name, tc, a) 58 | #define sample_texture_cube_array_level( name, tc, a, l ) sample_texture_array_level(name, tc, a, l) 59 | 60 | // sampler compare / gather 61 | #define sample_depth_compare( name, tc, compare_value ) name.sample_compare(sampler_##name, tc, compare_value, min_lod_clamp(0)) 62 | #define sample_depth_compare_array( name, tc, a, compare_value ) name.sample_compare(sampler_##name, tc, uint(a), compare_value, min_lod_clamp(0)) 63 | #define sample_depth_compare_cube( name, tc, compare_value ) name.sample_compare(sampler_##name, tc, compare_value, min_lod_clamp(0)) 64 | #define sample_depth_compare_cube_array( name, tc, a, compare_value ) name.sample_compare(sampler_##name, tc, uint(a), compare_value, min_lod_clamp(0)) 65 | 66 | // matrix 67 | #define to_3x3( M4 ) float3x3(M4[0].xyz, M4[1].xyz, M4[2].xyz) 68 | #define from_columns_2x2(A, B) (transpose(float2x2(A, B))) 69 | #define from_rows_2x2(A, B) (float2x2(A, B)) 70 | #define from_columns_3x3(A, B, C) (transpose(float3x3(A, B, C))) 71 | #define from_rows_3x3(A, B, C) (float3x3(A, B, C)) 72 | #define from_columns_4x4(A, B, C, D) (transpose(float4x4(A, B, C, D))) 73 | #define from_rows_4x4(A, B, C, D) (float4x4(A, B, C, D)) 74 | #define mul( A, B ) ((A) * (B)) 75 | #define mul_tbn( A, B ) ((B) * (A)) 76 | #define unpack_vb_instance_mat( mat, r0, r1, r2, r3 ) mat[0] = r0; mat[1] = r1; mat[2] = r2; mat[3] = r3; 77 | #define to_data_matrix(mat) mat 78 | 79 | // clip 80 | #define remap_z_clip_space( d ) (d = d * 0.5 + 0.5) 81 | #define remap_ndc_ray( r ) float2(r.x, r.y) 82 | #define remap_depth( d ) (d) 83 | 84 | // defs 85 | #define ddx dfdx 86 | #define ddy dfdy 87 | #define discard discard_fragment() 88 | #define lerp mix 89 | #define frac fract 90 | #define mod(x, y) (x - y * floor(x/y)) 91 | #define _pmfx_unroll 92 | #define _pmfx_loop 93 | #define read3 uint3 94 | #define read2 uint2 95 | 96 | // atomics 97 | #define atomic_counter(name, index) structured_buffer_rw(atomic_uint, name, index) 98 | #define atomic_load(atomic, original) original = atomic_load_explicit(&atomic, memory_order_relaxed) 99 | #define atomic_store(atomic, value) atomic_store_explicit(&atomic, value, memory_order_relaxed) 100 | #define atomic_increment(atomic, original) original = atomic_fetch_add_explicit(&atomic, 1, memory_order_relaxed) 101 | #define atomic_decrement(atomic, original) original = atomic_fetch_sub_explicit(&atomic, 1, memory_order_relaxed) 102 | #define atomic_add(atomic, value, original) original = atomic_fetch_add_explicit(&atomic, value, memory_order_relaxed) 103 | #define atomic_subtract(atomic, value, original) original = atomic_fetch_sub_explicit(&atomic, value, memory_order_relaxed) 104 | #define atomic_min(atomic, value, original) original = atomic_fetch_min_explicit(&atomic, value, memory_order_relaxed) 105 | #define atomic_max(atomic, value, original) original = atomic_fetch_max_explicit(&atomic, value, memory_order_relaxed) 106 | #define atomic_and(atomic, value, original) original = atomic_fetch_and_explicit(&atomic, value, memory_order_relaxed) 107 | #define atomic_or(atomic, value, original) original = atomic_fetch_or_explicit(&atomic, value, memory_order_relaxed) 108 | #define atomic_xor(atomic, value, original) original = atomic_fetch_xor_explicit(&atomic, value, memory_order_relaxed) 109 | #define atomic_exchange(atomic, value, original) original = atomic_exchange_explicit(&atomic, value, memory_order_relaxed) 110 | #define threadgroup_barrier() threadgroup_barrier(mem_flags::mem_threadgroup) 111 | #define device_barrier() threadgroup_barrier(mem_flags::mem_device) 112 | -------------------------------------------------------------------------------- /platform/pmfx.h: -------------------------------------------------------------------------------- 1 | #define chebyshev_normalize( V ) (V.xyz / max( max(abs(V.x), abs(V.y)), abs(V.z) )) 2 | #define max3(v) max(max(v.x, v.y),v.z) 3 | #define max4(v) max(max(max(v.x, v.y),v.z), v.w) 4 | #define PI 3.14159265358979323846264 5 | 6 | 7 | -------------------------------------------------------------------------------- /platform/pssl.h: -------------------------------------------------------------------------------- 1 | // texture 2 | #define texture2d_rw( name, index ) RWTexture2D name : register(u##index) 3 | #define texture2d_r( name, index ) Texture2D name : register(t##index) 4 | #define texture2d_w( name, index ) texture2d_rw( name, index ) 5 | #define read_texture( name, gid ) name[gid] 6 | #define write_texture( name, val, gid ) name[gid] = val 7 | #define texture_2d( name, sampler_index ) Texture2D name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index); 8 | #define texture_3d( name, sampler_index ) Texture3D name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index); 9 | #define texture_2dms( type, samples, name, sampler_index ) MS_Texture2D name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index); 10 | #define texture_cube( name, sampler_index ) TextureCube name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index); 11 | #define texture_2d_array( name, sampler_index ) Texture2DArray name : register(t##sampler_index); ; SamplerState sampler_##name : register(s##sampler_index); 12 | #define texture_2d_external( name, sampler_index ) texture_2d( name, sampler_index ) 13 | 14 | // structured buffer 15 | #define structured_buffer_rw( type, name, index ) RWStructuredBuffer name : register(u##index); 16 | #define structured_buffer( type, name, index ) StructuredBuffer name : register(t##index); 17 | 18 | // sampler 19 | #define sample_texture_2dms( name, x, y, fragment ) name.Load( uint2(x, y), fragment ) 20 | #define sample_texture( name, V ) name.Sample(sampler_##name, V) 21 | #define sample_texture_level( name, V, l ) name.SampleLevel(sampler_##name, V, l) 22 | #define sample_texture_grad( name, V, vddx, vddy ) name.SampleGrad(sampler_##name, V, vddx, vddy ) 23 | #define sample_texture_array( name, V, a ) name.Sample(sampler_##name, float3(V.xy, a) ) 24 | #define sample_texture_array_level( name, V, a, l ) name.SampleLevel(sampler_##name, float3(V.xy, a), l) 25 | 26 | // matrix 27 | #define to_3x3( M4 ) ((float3x3)M4) 28 | #define from_columns_2x2(A, B) (float2x2(A, B)) 29 | #define from_rows_2x2(A, B) (transpose(float2x2(A, B))) 30 | #define from_columns_3x3(A, B, C) (float3x3(A, B, C)) 31 | #define from_rows_3x3(A, B, C) (transpose(float3x3(A, B, C))) 32 | #define from_columns_4x4(A, B, C, D) (float4x4(A, B, C, D)) 33 | #define from_rows_4x4(A, B, C, D) (transpose(float4x4(A, B, C, D))) 34 | #define mul_tbn( A, B ) mul(A, B) 35 | #define unpack_vb_instance_mat( mat, r0, r1, r2, r3 ) mat[0] = r0; mat[1] = r1; mat[2] = r2; mat[3] = r3; mat = transpose(mat) 36 | #define to_data_matrix(mat) transpose(mat) 37 | 38 | // clip 39 | #define remap_z_clip_space( d ) (d = d * 0.5 + 0.5) 40 | #define remap_depth( d ) (d) 41 | #define remap_ndc_ray( r ) float2(r.x, r.y * -1.0) 42 | 43 | // defs 44 | #define mod(x, y) (x - y * floor(x/y)) 45 | #define fract frac 46 | #define _pmfx_unroll [unroll] 47 | #define read3 uint3 48 | #define read2 uint2 49 | 50 | 51 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # pmfx-shader 2 | 3 | [![tests](https://github.com/polymonster/pmfx-shader/actions/workflows/tests.yaml/badge.svg)](https://github.com/polymonster/pmfx-shader/actions/workflows/tests.yaml)[![release](https://github.com/polymonster/pmfx-shader/actions/workflows/release.yaml/badge.svg)](https://github.com/polymonster/pmfx-shader/actions/workflows/release.yaml) 4 | 5 | A cross platform shader system with multi-threaded offline compilation or platform shader source code generation. Output json reflection info and `.h` or `.rs` code generation with your shader structs, fx-like techniques and compile time branch evaluation via (uber-shader) "permutations". Version 1.0 is now in maintenence mode and Version 2.0 is in progress which aims to offer wider support for more modern GPU features. 6 | 7 | ## Supported Targets 8 | 9 | - HLSL Shader Model 6 (pmfx -v2) 10 | - HLSL Shader Model 3+ 11 | - GLSL 330+ 12 | - GLES 300+ (WebGL 2.0) 13 | - GLSL 200 (compatibility) 14 | - GLES (WebGL 1.0) (compatibility) 15 | - SPIR-V. (Vulkan, OpenGL) 16 | - Metal 1.0+ (macOS, iOS, tvOS) 17 | - PSSL 18 | - NVN (Nintendo Switch) 19 | 20 | (compatibility) platforms for older hardware might not support all pmfx features and may have missing legacy features. 21 | 22 | ## Dependencies 23 | 24 | Windows users need [vcredist 2013](https://www.microsoft.com/en-us/download/confirmation.aspx?id=40784) for the glsl/spirv validator. 25 | 26 | ## Console Platforms 27 | 28 | Compilation for Orbis is supported but you will need the SDK's installed and the environment variables set. 29 | 30 | For NVN there is an [executable](https://github.com/polymonster/pmfx-shader/tree/master/bin/nvn) included to compile to `nvn_glsc` but it will require the `NvnGlslc32.dll` to be installed along with the SDK. 31 | 32 | ## Usage 33 | 34 | You can use from source by cloning this repository and build `pmfx.py`, or install the latest packaged [release](https://github.com/polymonster/pmfx-shader/releases) if you do not need access to the source code. 35 | 36 | ```text 37 | py -3 pmfx.py -help (windows) 38 | python3 pmfx.py -help (macos/linux) 39 | 40 | -------------------------------------------------------------------------------- 41 | pmfx shader (v2.0) ------------------------------------------------------------- 42 | -------------------------------------------------------------------------------- 43 | commandline arguments: 44 | -v1 compile using pmfx version 1 (legacy) will use v2 otherwise 45 | -num_threads 4 (default) 46 | -shader_platform 47 | -shader_version (optional) 48 | hlsl: 3_0, 4_0 (default), 5_0, 6_0 [-v2] 49 | glsl: 200, 330 (default), 420, 450 50 | gles: 100, 300, 310, 350 51 | spirv: 420 (default), 450 52 | metal: 2.0 (default) 53 | nvn: (glsl) 54 | -metal_sdk [metal only] 55 | -metal_min_os (optional) [metal only] <9.0 - 13.0 (ios), 10.11 - 10.15 (macos)> 56 | -nvn_exe [nvn only] 57 | -extensions (optional) [glsl/gles only] 58 | -nvn_extensions (optional) [nvn only] 59 | -i 60 | -o 61 | -t 62 | -h (optional) 63 | -d (optional) generate debuggable shader 64 | -f (optional) force build / compile even if dependencies are up-to-date 65 | -rs (optional) [-v2] 66 | -root_dir (optional) sets working directory here 67 | -source (optional) (generates platform source into -o no compilation) 68 | -stage_in <0, 1> (optional) [metal only] (default 1) 69 | uses stage_in for metal vertex buffers, 0 uses raw buffers 70 | -cbuffer_offset (optional) [metal only] (default 4) 71 | specifies an offset applied to cbuffer locations to avoid collisions with vertex buffers 72 | -texture_offset (optional) [vulkan only] (default 32) 73 | specifies an offset applied to texture locations to avoid collisions with buffers 74 | -v_flip (optional) [glsl only] (inserts glsl uniform to conditionally flip verts in the y axis) 75 | -args (optional) anything passed after this will be forward to the platform specific compiler 76 | for example for fxc.exe /Zpr or dxc.exe -Zpr etc.. check the compiler help for options 77 | -------------------------------------------------------------------------------- 78 | ``` 79 | 80 | ## Versions 81 | 82 | There are 2 code paths supported by pmfx, this is in an effort to keep up-to-date with modern graphics API's but also offer backward comptibility support to older graphics API's, mobile and web platforms. 83 | 84 | - [Version 1](https://github.com/polymonster/pmfx-shader#version-1) - (bindful render model, techniques, macro based cross platform shaders) 85 | - [Version 2](https://github.com/polymonster/pmfx-shader#version-2) - (bindless render model, pipelines, descriptors, SPIR-V based cross-compilation) 86 | 87 | ## Version 1 88 | 89 | Compile with the `-v1` flag to select version 1. 90 | 91 | A single file does all the shader parsing and code generation. Simple syntax changes are handled through macros and defines found in [platform](https://github.com/polymonster/pmfx-shader/tree/master/platform), so it is simple to add new features or change things to behave how you like. More complex differences between shader languages are handled through code-generation. 92 | 93 | This is a small part of the larger [pmfx](https://github.com/polymonster/pmtech/wiki/Pmfx) system found in [pmtech](https://github.com/polymonster/pmtech), it has been moved into a separate repository to be used with other projects, if you are interested to see how pmfx shaders are integrated please take a look [here](https://github.com/polymonster/pmtech). 94 | 95 | ### Compiling Examples 96 | 97 | Take a look at the example [code](https://github.com/polymonster/pmfx-shader/tree/master/examples/v1). 98 | 99 | ```text 100 | // metal macos 101 | python3 pmfx.py -v1 -shader_platform metal -metal_sdk macosx -metal_min_os 10.14 -shader_version 2.2 -i examples/v1 -o output/bin -h output/structs -t output/temp 102 | 103 | // metal ios 104 | python3 pmfx.py -v1 -shader_platform metal -metal_sdk iphoneos -metal_min_os 0.9 -shader_version 2.2 -i examples/v1 -o output/bin -h output/structs -t output/temp 105 | 106 | // spir-v vulkan 107 | python3 pmfx.py -v1 -shader_platform spirv -i examples/v1 -o output/bin -h output/structs -t output/temp 108 | 109 | // hlsl d3d11 110 | py -3 pmfx.py -v1 -shader_platform hlsl -shader_version 5_0 -i examples/v1 -o output/bin -h output/structs -t output/temp 111 | 112 | // glsl 113 | python3 pmfx.py -v1 -shader_platform glsl -shader_version 330 -i examples/v1 -o output/bin -h output/structs -t output/temp 114 | 115 | // gles 116 | python3 pmfx.py -v1 -shader_platform gles -shader_version 320 -i examples/v1 -o output/bin -h output/structs -t output/temp 117 | ``` 118 | 119 | ### Version 1 shader language 120 | 121 | Use mostly HLSL syntax for shaders, with some small differences: 122 | 123 | #### Always use structs for inputs and outputs 124 | 125 | ```hlsl 126 | struct vs_input 127 | { 128 | float4 position : POSITION; 129 | }; 130 | 131 | struct vs_output 132 | { 133 | float4 position : SV_POSITION0; 134 | }; 135 | 136 | vs_output vs_main( vs_input input ) 137 | { 138 | vs_output output; 139 | 140 | output.position = input.position; 141 | 142 | return output; 143 | } 144 | ``` 145 | 146 | #### Supported semantics and sizes 147 | 148 | pmfx will generate an input layout for you in the json reflection info, containing the stride of the vertex layout and the byte offsets to each of the elements. If you choose to use this, pmfx will assume the following sizes for semantics: 149 | 150 | ```hlsl 151 | POSITION // 32bit float 152 | TEXCOORD // 32bit float 153 | NORMAL // 32bit float 154 | TANGENT // 32bit float 155 | BITANGENT // 32bit float 156 | BLENDWEIGHTS // 32bit float 157 | COLOR // 8bit unsigned int 158 | BLENDINDICES // 8bit unsigned int 159 | ``` 160 | 161 | #### Shader resources 162 | 163 | Due to fundamental differences accross shader languages, shader resource declarations and access have a syntax unique to pmfx. Define a block of shader_resources to allow global textures or buffers as supported in HLSL and GLSL. 164 | 165 | ```c 166 | shader_resources 167 | { 168 | texture_2d( diffuse_texture, 0 ); 169 | texture_2dms( float4, 2, texture_msaa_2, 0 ); 170 | }; 171 | ``` 172 | 173 | #### Resource types 174 | 175 | ```c 176 | // cbuffers are the same as regular hlsl 177 | cbuffer per_view : register(b0) 178 | { 179 | float4x4 view_matrix; 180 | }; 181 | 182 | // texture types 183 | texture_2d( sampler_name, layout_index ); 184 | texture_2dms( type, samples, sampler_name, layout_index ); 185 | texture_2d_array( sampler_name, layout_index ); 186 | texture_cube( sampler_name, layout_index ); 187 | texture_cube_array( sampler_name, layout_index ); // requires sm 4+, gles 400+ 188 | texture_3d( sampler_name, layout_index ); 189 | texture_2d_external( sampler_name, layout_index ); // gles specific extension 190 | 191 | // depth formats are required for sampler compare ops 192 | depth_2d( sampler_name, layout_index ); 193 | depth_2d_array( sampler_name, layout_index ); 194 | depth_cube( sampler_name, layout_index ); 195 | depth_cube_array( sampler_name, layout_index ); 196 | 197 | // compute shader texture types 198 | texture_2d_r( image_name, layout_index ); 199 | texture_2d_w( image_name, layout_index ); 200 | texture_2d_rw( image_name, layout_index ); 201 | texture_3d_r( image_name, layout_index ); 202 | texture_3d_w( image_name, layout_index ); 203 | texture_3d_rw( image_name, layout_index ); 204 | texture_2d_array_r( image_name, layout_index ); 205 | texture_2d_array_w( image_name, layout_index ); 206 | texture_2d_array_rw( image_name, layout_index ); 207 | 208 | // compute shader buffer types 209 | structured_buffer( type, name, index ); 210 | structured_buffer_rw( type, name, index ); 211 | atomic_counter(name, index); 212 | 213 | // bindless resouce tables 214 | // name, type, dimension, register, space 215 | texture2d_table(texture0, float4, [], 0, 0); 216 | cbuffer_table(constant_buffer0, data, [], 1, 0); 217 | sampler_state_table(sampler0, [], 0); 218 | 219 | // smapler type 220 | sampler_state(sampler0, 0); 221 | ``` 222 | 223 | #### Accessing resources 224 | 225 | Textures and samplers are combined when using a binding renderer model. a `texture_2d` declares a texture and a sampler on the corresponding texture and sampler register index which is passed into the macro. The `sample_texture` can be used to sample textures of varying dimensions. 226 | 227 | ```c 228 | // sample texture 229 | float4 col = sample_texture( diffuse_texture, texcoord.xy ); 230 | float4 cube = sample_texture( cubemap_texture, normal.xyz ); 231 | float4 msaa_sample = sample_texture_2dms( msaa_texture, x, y, fragment ); 232 | float4 level = sample_texture_level( texture, texcoord.xy, mip_level); 233 | float4 array = sample_texture_array( texture, texcoord.xy, array_slice); 234 | float4 array_level = sample_texture_array_level( texture, texcoord.xy, array_slice, mip_level); 235 | 236 | // sample compare 237 | float shadow = sample_depth_compare( shadow_map, texcoord.xy, compare_ref); 238 | float shadow_array = sample_depth_compare_array( shadow_map, texcoord.xy, array_slice, compare_ref); 239 | float cube_shadow = sample_depth_compare_cube( shadow_map, texcoord.xyz, compare_ref); 240 | float cube_shadow_array = sample_depth_compare_cube_array( shadow_map, texcoord.xyz, array_slice, compare_ref); 241 | 242 | // compute rw texture 243 | float4 rwtex = read_texture( tex_rw, gid ); 244 | write_texture(rwtex, val, gid); 245 | 246 | // compute structured buffer 247 | struct val = structured_buffer[gid]; // read 248 | structured_buffer[gid] = val; // write 249 | 250 | // read type! 251 | // glsl expects ivec (int) to be pass to imageLoad, hlsl and metal require uint... 252 | // there is a `read` type you can use to be platform safe 253 | read3 read_coord = read3(x, y, z); 254 | read_texture( tex_rw, read_coord ); 255 | ``` 256 | 257 | ### Atomic Operations 258 | 259 | Support for glsl, hlsl and metal compatible atomics and memory barriers is available. The atomic_counter resource type is a RWStructuredBuffer in hlsl, a atomic_uint read/write buffer in Metal and a uniform atomic_uint in GLSL. 260 | 261 | ```hlsl 262 | // types 263 | atomic_uint u; 264 | atomic_int i; 265 | 266 | // operations 267 | atomic_load(atomic, original) 268 | atomic_store(atomic, value) 269 | atomic_increment(atomic, original) 270 | atomic_decrement(atomic, original) 271 | atomic_add(atomic, value, original) 272 | atomic_subtract(atomic, value, original) 273 | atomic_min(atomic, value, original) 274 | atomic_max(atomic, value, original) 275 | atomic_and(atomic, value, original) 276 | atomic_or(atomic, value, original) 277 | atomic_xor(atomic, value, original) 278 | atomic_exchange(atomic, value, original) 279 | threadgroup_barrier() 280 | device_barrier() 281 | 282 | // usage 283 | shader_resources 284 | { 285 | atomic_counter(counter, 0); // counter bound to index 0 286 | } 287 | 288 | // increments counter and stores the original value in 'index' 289 | uint index = 0; 290 | atomic_increment(counter, index); 291 | ``` 292 | 293 | #### Includes 294 | 295 | Include files are supported even though some shader platforms or versions may not support them natively. 296 | 297 | ```c 298 | #include "libs/lighting.pmfx" 299 | #include "libs/skinning.pmfx" 300 | #include "libs/globals.pmfx" 301 | #include "libs/sdf.pmfx" 302 | #include "libs/area_lights.pmfx" 303 | ``` 304 | 305 | #### Extensions 306 | 307 | To enable glsl extensions you can pass a list of strings to the `-extensions` commandline argument. The glsl extension will be inserted to the top of the generated code with `: require` set: 308 | 309 | ```text 310 | -extensions GL_OES_EGL_image_external GL_OES_get_program_binary 311 | ``` 312 | 313 | ## Unique pmfx features 314 | 315 | ### GLES 2.0 / GLSL 2.0 cbuffers 316 | 317 | cbuffers are emulated for older glsl versions, a cbuffer is packed into a single float4 array. The uniform float4 array (`glUniform4fv`) is named after the cbuffer, you can find the uniform location from this name using `glUniformLocation`. The count of the float4 array is the number of members the cbuffer where float4 and float4x4 are supported and float4x4 count for 4 array elements. You can use the generated c++ structs from pmfx to create a coherent copy of the uniform data on the cpu. GLES 2.0 / GLSL 2.0 cbuffers 318 | 319 | ### cbuffer_offset / texture_offset 320 | 321 | HLSL has different registers for textures, vertex buffers, cbuffers and un-ordered access views. Metal and Vulkan have some differences where the register indices are shared across different resource types. To avoid collisions in different API backends you can supply offsets using the following command line options. 322 | 323 | Metal: -cbuffer_offset (cbuffers start binding at this offset to allow vertex buffers to be bound to the slots prior to these offsets) 324 | 325 | Vulkan: -texture_offset (textures start binding at this point allowing uniform buffers to bind to the prior slots) 326 | 327 | ### v_flip 328 | 329 | OpenGL has different viewport co-ordinates to texture coordinate so when rendering to the backbuffer vs rendering into a render target you can get output results that are flipped in the y-axis, this can propagate it's way far into a code base with conditional "v_flips" happening during different render passes. 330 | 331 | To solve this issue in a cross platform way, pmfx will expose a uniform bool called "v_flip" in all gl vertex shaders, this allows you to conditionally flip the y-coordinate when rendering to the backbuffer or not. 332 | 333 | To make this work make sure you also change the winding glFrontFace(GL_CCW) to glFrontFace(GL_CW). 334 | 335 | ### cbuffer padding 336 | 337 | HLSL/Direct3D requires cbuffers to be padded to 16 bytes alignment, pmfx allows you to create cbuffers with any size and will pad the rest out for you. 338 | 339 | ### Techniques 340 | 341 | Single .pmfx file can contain multiple shader functions so you can share functionality, you can define a block of [jsn](https://github.com/polymonster/jsn) in the shader to configure techniques. (jsn is a more lenient and user friendly data format similar to json). 342 | 343 | Simply specify `vs`, `ps` or `cs` to select which function in the source to use for that shader stage. If no pmfx: json block is found you can still supply `vs_main` and `ps_main` which will be output as a technique named "default". 344 | 345 | ```jsonnet 346 | pmfx: 347 | { 348 | gbuffer: { 349 | vs: vs_main 350 | ps: ps_gbuffer 351 | } 352 | 353 | zonly: { 354 | vs: vs_main_zonly 355 | ps: ps_null 356 | } 357 | } 358 | ``` 359 | 360 | You can also use json to specify technique constants with range and ui type.. so you can later hook them into a gui: 361 | 362 | ```jsonnet 363 | constants: 364 | { 365 | albedo: { 366 | type: float4, widget: colour, default: [1.0, 1.0, 1.0, 1.0] 367 | } 368 | roughness: { 369 | type: float, widget: slider, min: 0, max: 1, default: 0.5 370 | } 371 | reflectivity: { 372 | type: float, widget: slider, min: 0, max: 1, default: 0.3 373 | } 374 | } 375 | ``` 376 | 377 | ![pmfx constants](https://github.com/polymonster/polymonster.github.io/raw/master/assets/wiki/pmfx-material.png) 378 | 379 | Access to technique constants is done with m_prefix. 380 | 381 | ```hlsl 382 | ps_output ps_main(vs_output input) 383 | { 384 | float4 col = m_albedo; 385 | } 386 | ``` 387 | 388 | ### Permutations 389 | 390 | Permutations provide an uber shader style compile time branch evaluation to generate optimal shaders but allowing for flexibility to share code as much as possible. The pmfx block is used here again, you can specify permutations inside a technique. 391 | 392 | ```yaml 393 | permutations: 394 | { 395 | SKINNED: [31, [0,1]] 396 | INSTANCED: [30, [0,1]] 397 | UV_SCALE: [1, [0,1]] 398 | } 399 | ``` 400 | 401 | The first parameter is a bit shift that we can check.. so skinned is 1<<31 and uv scale is 1<<1. The second value is number of options, so in the above example we just have on or off, but you could have a quality level 0-5 for instance. 402 | 403 | To insert a compile time evaluated branch in code, use a colon after if / else 404 | 405 | ```c++ 406 | if:(SKINNED) 407 | { 408 | float4 sp = skin_pos(input.position, input.blend_weights, input.blend_indices); 409 | output.position = mul( sp, vp_matrix ); 410 | } 411 | else: 412 | { 413 | output.position = mul( input.position, wvp ); 414 | } 415 | ``` 416 | 417 | For each permutation a shader is generated with the technique plus the permutation id. The id is generated from the values passed in the permutation object. 418 | 419 | Adding permutations can cause the number of generated shaders to grow exponentially, pmfx will detect redundant shader combinations using md5 hashing, to re-use duplicate permutation combinations and avoid un-necessary compilation. 420 | 421 | ### C++ Header 422 | 423 | After compilation a header is output for each .pmfx file containing c struct declarations for the cbuffers, technique constant buffers and vertex inputs. You can use these sturcts to fill buffers in your c++ code and use sizeof for buffer update calls in your graphics api. 424 | 425 | It also contains defines for the shader permutation id / flags that you can check and test against to select the correct shader permutations for a draw call (ie. skinned, instanced, etc). 426 | 427 | ```c++ 428 | namespace debug 429 | { 430 | struct per_pass_view 431 | { 432 | float4x4 view_projection_matrix; 433 | float4x4 view_matrix; 434 | }; 435 | struct per_pass_view_2d 436 | { 437 | float4x4 projection_matrix; 438 | float4 user_data; 439 | }; 440 | #define OMNI_SHADOW_SKINNED 2147483648 441 | #define OMNI_SHADOW_INSTANCED 1073741824 442 | #define FORWARD_LIT_SKINNED 2147483648 443 | #define FORWARD_LIT_INSTANCED 1073741824 444 | #define FORWARD_LIT_UV_SCALE 2 445 | #define FORWARD_LIT_SSS 4 446 | #define FORWARD_LIT_SDF_SHADOW 8 447 | } 448 | ``` 449 | 450 | A full example output c++ header can be viewed [here](https://github.com/polymonster/pmfx-shader/blob/master/examples/outputs/v1_header.json). 451 | 452 | ### JSON Reflection Info 453 | 454 | Each .pmfx file comes along with a json file containing reflection info. This info contains the locations textures / buffers are bound to, the size of structs, vertex layout description and more, at this point please remember the output reflection info is fully compliant json, and not lightweight jsn.. this is because of the more widespread support of json. 455 | 456 | ```json 457 | "texture_sampler_bindings": [ 458 | { 459 | "name": "gbuffer_albedo", 460 | "data_type": "float4", 461 | "fragments": 1, 462 | "type": "texture_2d", 463 | "unit": 0 464 | }] 465 | 466 | "vs_inputs": [ 467 | { 468 | "name": "position", 469 | "semantic_index": 0, 470 | "semantic_id": 1, 471 | "size": 16, 472 | "element_size": 4, 473 | "num_elements": 4, 474 | "offset": 0 475 | }] 476 | ``` 477 | 478 | You can take a look at a full example output file included with this repositiory [here](https://github.com/polymonster/pmfx-shader/blob/master/examples/outputs/v1_info.json). 479 | 480 | ## Version 2 481 | 482 | Version 2 is currently work in progress, HLSL is used as the authorting shader language and SPIRV-cross is used to cross compile to other platforms. 483 | 484 | Use `.hlsl` files and hlsl source code, create a `.pmfx` which can create pipelines from small amount of meta data: 485 | 486 | ```jsonnet 487 | import other_files.pmfx 488 | { 489 | // include shader source files 490 | include: [ 491 | "imdraw.hlsl" 492 | ] 493 | 494 | // create pipelines 495 | pipelines: { 496 | imdraw_2d: { 497 | vs: vs_2d 498 | ps: ps_main 499 | push_constants: ["view_push_constants"] 500 | topology: "LineList" 501 | } 502 | } 503 | } 504 | ``` 505 | 506 | Pipeline states can be specified and included in `.pmfx` files: 507 | 508 | ```jsonnet 509 | depth_stencil_states: { 510 | depth_test_less: { 511 | depth_enabled: true 512 | depth_write_mask: All 513 | depth_func: Less 514 | } 515 | } 516 | raster_states: { 517 | wireframe: { 518 | fill_mode: Wireframe 519 | depth_bias: -5 520 | } 521 | cull_back: { 522 | cull_mode: Back 523 | } 524 | } 525 | pipelines: { 526 | imdraw_mesh: { 527 | depth_stencil_state: "depth_test_less" 528 | } 529 | } 530 | ``` 531 | 532 | You can specify textures or render targets: 533 | 534 | ```jsonnet 535 | textures: { 536 | main_colour: { 537 | ratio: { 538 | window: "main_window", 539 | scale: 1.0 540 | } 541 | format: RGBA8n 542 | usage: [ShaderResource, RenderTarget] 543 | samples: 8 544 | } 545 | main_depth(main_colour): { 546 | format: D24nS8u 547 | usage: [ShaderResource, DepthStencil] 548 | } 549 | } 550 | ``` 551 | 552 | You can `views` that can act as a render pass with custom data, you can extend these objects to contain custom data such as cameras or render functions to hook into your own engines or entity component systems: 553 | 554 | ```jsonnet 555 | views: { 556 | main_view: { 557 | render_target: [ 558 | "main_colour" 559 | ] 560 | clear_colour: [0.45, 0.55, 0.60, 1.0] 561 | depth_stencil: [ 562 | "main_depth" 563 | ] 564 | clear_depth: 1.0 565 | viewport: [0.0, 0.0, 1.0, 1.0, 0.0, 1.0] 566 | camera: "main_camera" 567 | } 568 | main_view_no_clear(main_view): { 569 | clear_colour: null 570 | clear_depth: null 571 | } 572 | } 573 | ``` 574 | 575 | `render_graphs` can be used to supply a collection of views with dependencies which can then be used to generate execution order and resource state transitions. 576 | 577 | ```jsonnet 578 | render_graphs: { 579 | mesh_debug: { 580 | grid: { 581 | view: "main_view" 582 | pipelines: ["imdraw_3d"] 583 | function: "render_grid" 584 | } 585 | meshes: { 586 | view: "main_view_no_clear" 587 | pipelines: ["mesh_debug"] 588 | function: "render_meshes" 589 | depends_on: ["grid"] 590 | } 591 | wireframe: { 592 | view: "main_view_no_clear" 593 | pipelines: ["wireframe_overlay"] 594 | function: "render_meshes" 595 | depends_on: ["meshes", "grid"] 596 | } 597 | } 598 | } 599 | ``` 600 | 601 | `pmfx` supplies a framework and a schema to configure render state, it will still be down to you to implement that data in a graphics API or engine. If you want to take a look at an example project of how to use these features my graphics engine [hotline](https://github.com/polymonster/hotline) implemenents the feature set and utilises [serde](https://github.com/serde-rs/serde) to deserialise the output `json` directly into rust structures. 602 | 603 | Full [documentation](https://github.com/polymonster/pmfx-shader/blob/master/docs/v2.pmfx_doc) for pipeline specification is provided. 604 | 605 | ### Building 606 | 607 | Compilation is simple with command line args as so: 608 | 609 | ```text 610 | py -3 pmfx.py -shader_platform hlsl -shader_version 6_0 -i examples/v2/ -o build/data/shaders -t build/temp/shaders 611 | ``` 612 | 613 | ### Examples 614 | 615 | Take a look at the example [code](https://github.com/polymonster/pmfx-shader/tree/master/examples/v2). 616 | 617 | ### Output 618 | 619 | Compiled shaders and reflection information will be emitted to your chosen `-o` outout directory, Each `.pmfx` file will create a directory which it will compile shader binaries into. Shader compilation is minimised and reduced within single `.pmfx` files by sharing and re-using binaries which are identical across different shader permitations or stages. 620 | 621 | Pipeline layout, descriptor bindings and vertex layout can be automatically generated based on resource usage inside shaders, the whole pipeline is exported as `.json` along with the built shaders. Hashes for the various pieces of the render pipline states are stored so you can quickly check for pipelines that may need rebuilding as part of a hot reloading process. 622 | 623 | ```json 624 | "imdraw_2d": { 625 | "0": { 626 | "vs": "vs_2d.vsc", 627 | "ps": "ps_main.psc", 628 | "push_constants": [ 629 | "view_push_constants" 630 | ], 631 | "topology": "LineList", 632 | "vs_hash:": 2752841994, 633 | "vertex_layout": [ 634 | { 635 | "name": "position", 636 | "semantic": "POSITION", 637 | "index": 0, 638 | "format": "RG32f", 639 | "aligned_byte_offset": 0, 640 | "input_slot": 0, 641 | "input_slot_class": "PerVertex", 642 | "step_rate": 0 643 | }, 644 | { 645 | "name": "colour", 646 | "semantic": "TEXCOORD", 647 | "index": 0, 648 | "format": "RGBA32f", 649 | "aligned_byte_offset": 8, 650 | "input_slot": 0, 651 | "input_slot_class": "PerVertex", 652 | "step_rate": 0 653 | } 654 | ], 655 | "error_code": 0, 656 | "ps_hash:": 2326464525, 657 | "pipeline_layout": { 658 | "bindings": [], 659 | "push_constants": [ 660 | { 661 | "shader_register": 0, 662 | "register_space": 0, 663 | "binding_type": "ConstantBuffer", 664 | "visibility": "Vertex", 665 | "num_values": 16 666 | } 667 | ], 668 | "static_samplers": [] 669 | }, 670 | "hash": 3046174282 671 | } 672 | } 673 | ``` 674 | 675 | You can take a look an example output `json` reflection file included in this repository [here](https://github.com/polymonster/pmfx-shader/blob/master/examples/outputs/v2_info.json). 676 | 677 | ### Touch 678 | 679 | When building pipeline, descriptor or vertex layouts, `pmfx` will detect which resources you are using, when debugging you may comment out or remove references to used resources and this may cause knock on issues with the associated slots and layouts you expect in your graphics engine. You can use the `pmfx_touch` macro to ensure that a reosurce type is included in a particular layout to avoid this issue without throwing a warning. 680 | 681 | ```hlsl 682 | struct cbuffer_struct { 683 | float4x4 world_matrix; 684 | }; 685 | ConstantBuffer unused_cbuffer : register(b0); 686 | 687 | vs_output vs_test_use_cbuffer_unscoped() { 688 | vs_output output = vs_output_default(); 689 | 690 | // .. 691 | 692 | pmfx_touch(unused_cbuffer); 693 | 694 | return output; 695 | } 696 | ``` 697 | --------------------------------------------------------------------------------