├── .clang-format ├── .cmake-format ├── .github ├── samples │ ├── 001_HelloTriangle.jpg │ ├── 002_RenderToCubeMap.jpg │ ├── 003_RenderToCubeMapSinglePass.jpg │ ├── 004_YUV.jpg │ ├── 005_MeshShaders.jpg │ ├── 006_RayTracingHello.jpg │ ├── 007_RayTracingAO.jpg │ ├── 008_RayTracingMesh.jpg │ ├── 009_TriplanarMapping.jpg │ ├── Tiny_MeshLarge.jpg │ └── tiny_mesh_large_android.jpg ├── screenshot01.jpg ├── screenshot02.jpg └── workflows │ └── c-cpp.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── android ├── app │ ├── AndroidManifest.xml.in │ ├── MainActivity.java.in │ └── build.gradle.in ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.in ├── build └── .gitignore ├── cmake └── CommonMacros.txt ├── deploy_content.py ├── deploy_content_android.py ├── deploy_deps.py ├── lvk ├── CMakeLists.txt ├── HelpersImGui.cpp ├── HelpersImGui.h ├── LVK.cpp ├── LVK.h ├── LVK.mm ├── Pool.h └── vulkan │ ├── CMakeLists.txt │ ├── VulkanClasses.cpp │ ├── VulkanClasses.h │ ├── VulkanUtils.cpp │ └── VulkanUtils.h ├── samples ├── 001_HelloTriangle.cpp ├── 001_HelloTriangle_Slang.cpp ├── 002_RenderToCubeMap.cpp ├── 003_RenderToCubeMapSinglePass.cpp ├── 004_YUV.cpp ├── 005_MeshShaders.cpp ├── 006_SwapchainHDR.cpp ├── 009_TriplanarMapping.cpp ├── Bistro.h ├── CMakeLists.txt ├── DEMO_001_SolarSystem.cpp ├── README.md ├── RTX_001_Hello.cpp ├── RTX_002_AO.cpp ├── RTX_003_Pipeline.cpp ├── Tiny_MeshLarge.cpp ├── VulkanApp.cpp └── VulkanApp.h └── third-party ├── .gitignore ├── bootstrap-content.json ├── bootstrap-deps.json ├── bootstrap.py ├── content └── patches │ ├── saturn_rings.py │ └── sun_corona.py └── deps └── patches └── stb_impl ├── stb_image.c ├── stb_image_resize2.c └── stb_image_write.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlines: Left 7 | # AlingOperands: true # Unsupported 8 | AlignTrailingComments: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: Empty 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: true 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBinaryOperators: false 21 | BreakBeforeBraces: Attach 22 | BreakBeforeInheritanceComma: false 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializers: AfterColon 25 | BreakStringLiterals: true 26 | ColumnLimit: 140 27 | CompactNamespaces: false 28 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 29 | ConstructorInitializerIndentWidth: 2 30 | ContinuationIndentWidth: 4 31 | Cpp11BracedListStyle: true 32 | DerivePointerAlignment: false 33 | FixNamespaceComments: true 34 | IncludeBlocks: Preserve 35 | IndentCaseLabels: false 36 | IndentPPDirectives: None 37 | IndentWidth: 2 38 | IndentWrappedFunctionNames: false 39 | KeepEmptyLinesAtTheStartOfBlocks: false 40 | # LanguageKind: Cpp # Unsupported 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | # ObjCBinPackProtocolList: Never # Unsupported 44 | ObjCBlockIndentWidth: 2 45 | ObjCSpaceAfterProperty: true 46 | ObjCSpaceBeforeProtocolList: true 47 | PenaltyBreakAssignment: 5 48 | PenaltyBreakBeforeFirstCallParameter: 10 49 | PenaltyBreakComment: 60 50 | PenaltyBreakFirstLessLess: 20 51 | PenaltyBreakString: 1000 52 | PenaltyExcessCharacter: 1000000 53 | PenaltyReturnTypeOnItsOwnLine: 200 54 | PointerAlignment: Left 55 | ReflowComments: true 56 | SortIncludes: true 57 | SortUsingDeclarations: true 58 | SpaceAfterCStyleCast: false 59 | SpaceAfterTemplateKeyword: false 60 | SpaceBeforeAssignmentOperators: true 61 | # SpaceBeforeCtorInitializerColon: false # Unsupported 62 | # SpaceBeforeInheritanceColon: false # Unsupported 63 | SpaceBeforeParens: ControlStatements 64 | # SpaceBeforeRangeBasedForLoopColon: true # Unsupported 65 | SpaceInEmptyParentheses: false 66 | SpacesBeforeTrailingComments: 1 67 | SpacesInAngles: false 68 | SpacesInContainerLiterals: true 69 | SpacesInCStyleCastParentheses: false 70 | SpacesInParentheses: false 71 | SpacesInSquareBrackets: false 72 | Standard: Cpp11 73 | TabWidth: 2 74 | UseTab: Never 75 | ... 76 | -------------------------------------------------------------------------------- /.cmake-format: -------------------------------------------------------------------------------- 1 | { 2 | "_help_parse": "Options affecting listfile parsing", 3 | "parse": { 4 | "_help_additional_commands": [ 5 | "Specify structure for custom cmake functions" 6 | ], 7 | "additional_commands": { 8 | "foo": { 9 | "flags": [ 10 | "BAR", 11 | "BAZ" 12 | ], 13 | "kwargs": { 14 | "HEADERS": "*", 15 | "SOURCES": "*", 16 | "DEPENDS": "*" 17 | } 18 | } 19 | }, 20 | "_help_override_spec": [ 21 | "Override configurations per-command where available" 22 | ], 23 | "override_spec": {}, 24 | "_help_vartags": [ 25 | "Specify variable tags." 26 | ], 27 | "vartags": [], 28 | "_help_proptags": [ 29 | "Specify property tags." 30 | ], 31 | "proptags": [] 32 | }, 33 | "_help_format": "Options affecting formatting.", 34 | "format": { 35 | "_help_disable": [ 36 | "Disable formatting entirely, making cmake-format a no-op" 37 | ], 38 | "disable": false, 39 | "_help_line_width": [ 40 | "How wide to allow formatted cmake files" 41 | ], 42 | "line_width": 140, 43 | "_help_tab_size": [ 44 | "How many spaces to tab for indent" 45 | ], 46 | "tab_size": 2, 47 | "_help_use_tabchars": [ 48 | "If true, lines are indented using tab characters (utf-8", 49 | "0x09) instead of space characters (utf-8 0x20).", 50 | "In cases where the layout would require a fractional tab", 51 | "character, the behavior of the fractional indentation is", 52 | "governed by " 53 | ], 54 | "use_tabchars": false, 55 | "_help_fractional_tab_policy": [ 56 | "If is True, then the value of this variable", 57 | "indicates how fractional indentions are handled during", 58 | "whitespace replacement. If set to 'use-space', fractional", 59 | "indentation is left as spaces (utf-8 0x20). If set to", 60 | "`round-up` fractional indentation is replaced with a single", 61 | "tab character (utf-8 0x09) effectively shifting the column", 62 | "to the next tabstop" 63 | ], 64 | "fractional_tab_policy": "use-space", 65 | "_help_max_subgroups_hwrap": [ 66 | "If an argument group contains more than this many sub-groups", 67 | "(parg or kwarg groups) then force it to a vertical layout." 68 | ], 69 | "max_subgroups_hwrap": 8, 70 | "_help_max_pargs_hwrap": [ 71 | "If a positional argument group contains more than this many", 72 | "arguments, then force it to a vertical layout." 73 | ], 74 | "max_pargs_hwrap": 8, 75 | "_help_max_rows_cmdline": [ 76 | "If a cmdline positional group consumes more than this many", 77 | "lines without nesting, then invalidate the layout (and nest)" 78 | ], 79 | "max_rows_cmdline": 2, 80 | "_help_separate_ctrl_name_with_space": [ 81 | "If true, separate flow control names from their parentheses", 82 | "with a space" 83 | ], 84 | "separate_ctrl_name_with_space": false, 85 | "_help_separate_fn_name_with_space": [ 86 | "If true, separate function names from parentheses with a", 87 | "space" 88 | ], 89 | "separate_fn_name_with_space": false, 90 | "_help_dangle_parens": [ 91 | "If a statement is wrapped to more than one line, than dangle", 92 | "the closing parenthesis on its own line." 93 | ], 94 | "dangle_parens": false, 95 | "_help_dangle_align": [ 96 | "If the trailing parenthesis must be 'dangled' on its on", 97 | "line, then align it to this reference: `prefix`: the start", 98 | "of the statement, `prefix-indent`: the start of the", 99 | "statement, plus one indentation level, `child`: align to", 100 | "the column of the arguments" 101 | ], 102 | "dangle_align": "prefix", 103 | "_help_min_prefix_chars": [ 104 | "If the statement spelling length (including space and", 105 | "parenthesis) is smaller than this amount, then force reject", 106 | "nested layouts." 107 | ], 108 | "min_prefix_chars": 4, 109 | "_help_max_prefix_chars": [ 110 | "If the statement spelling length (including space and", 111 | "parenthesis) is larger than the tab width by more than this", 112 | "amount, then force reject un-nested layouts." 113 | ], 114 | "max_prefix_chars": 10, 115 | "_help_max_lines_hwrap": [ 116 | "If a candidate layout is wrapped horizontally but it exceeds", 117 | "this many lines, then reject the layout." 118 | ], 119 | "max_lines_hwrap": 2, 120 | "_help_line_ending": [ 121 | "What style line endings to use in the output." 122 | ], 123 | "line_ending": "unix", 124 | "_help_command_case": [ 125 | "Format command names consistently as 'lower' or 'upper' case" 126 | ], 127 | "command_case": "canonical", 128 | "_help_keyword_case": [ 129 | "Format keywords consistently as 'lower' or 'upper' case" 130 | ], 131 | "keyword_case": "upper", 132 | "_help_always_wrap": [ 133 | "A list of command names which should always be wrapped" 134 | ], 135 | "always_wrap": [], 136 | "_help_enable_sort": [ 137 | "If true, the argument lists which are known to be sortable", 138 | "will be sorted lexicographicall" 139 | ], 140 | "enable_sort": true, 141 | "_help_autosort": [ 142 | "If true, the parsers may infer whether or not an argument", 143 | "list is sortable (without annotation)." 144 | ], 145 | "autosort": false, 146 | "_help_require_valid_layout": [ 147 | "By default, if cmake-format cannot successfully fit", 148 | "everything into the desired linewidth it will apply the", 149 | "last, most agressive attempt that it made. If this flag is", 150 | "True, however, cmake-format will print error, exit with non-", 151 | "zero status code, and write-out nothing" 152 | ], 153 | "require_valid_layout": false, 154 | "_help_layout_passes": [ 155 | "A dictionary mapping layout nodes to a list of wrap", 156 | "decisions. See the documentation for more information." 157 | ], 158 | "layout_passes": {} 159 | }, 160 | "_help_markup": "Options affecting comment reflow and formatting.", 161 | "markup": { 162 | "_help_bullet_char": [ 163 | "What character to use for bulleted lists" 164 | ], 165 | "bullet_char": "*", 166 | "_help_enum_char": [ 167 | "What character to use as punctuation after numerals in an", 168 | "enumerated list" 169 | ], 170 | "enum_char": ".", 171 | "_help_first_comment_is_literal": [ 172 | "If comment markup is enabled, don't reflow the first comment", 173 | "block in each listfile. Use this to preserve formatting of", 174 | "your copyright/license statements." 175 | ], 176 | "first_comment_is_literal": false, 177 | "_help_literal_comment_pattern": [ 178 | "If comment markup is enabled, don't reflow any comment block", 179 | "which matches this (regex) pattern. Default is `None`", 180 | "(disabled)." 181 | ], 182 | "literal_comment_pattern": ".*", 183 | "_help_fence_pattern": [ 184 | "Regular expression to match preformat fences in comments", 185 | "default= ``r'^\\s*([`~]{3}[`~]*)(.*)$'``" 186 | ], 187 | "fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$", 188 | "_help_ruler_pattern": [ 189 | "Regular expression to match rulers in comments default=", 190 | "``r'^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'``" 191 | ], 192 | "ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$", 193 | "_help_explicit_trailing_pattern": [ 194 | "If a comment line matches starts with this pattern then it", 195 | "is explicitly a trailing comment for the preceeding", 196 | "argument. Default is '#<'" 197 | ], 198 | "explicit_trailing_pattern": "#<", 199 | "_help_hashruler_min_length": [ 200 | "If a comment line starts with at least this many consecutive", 201 | "hash characters, then don't lstrip() them off. This allows", 202 | "for lazy hash rulers where the first hash char is not", 203 | "separated by space" 204 | ], 205 | "hashruler_min_length": 10, 206 | "_help_canonicalize_hashrulers": [ 207 | "If true, then insert a space between the first hash char and", 208 | "remaining hash chars in a hash ruler, and normalize its", 209 | "length to fill the column" 210 | ], 211 | "canonicalize_hashrulers": true, 212 | "_help_enable_markup": [ 213 | "enable comment markup parsing and reflow" 214 | ], 215 | "enable_markup": true 216 | }, 217 | "_help_lint": "Options affecting the linter", 218 | "lint": { 219 | "_help_disabled_codes": [ 220 | "a list of lint codes to disable" 221 | ], 222 | "disabled_codes": [], 223 | "_help_function_pattern": [ 224 | "regular expression pattern describing valid function names" 225 | ], 226 | "function_pattern": "[0-9a-z_]+", 227 | "_help_macro_pattern": [ 228 | "regular expression pattern describing valid macro names" 229 | ], 230 | "macro_pattern": "[0-9A-Z_]+", 231 | "_help_global_var_pattern": [ 232 | "regular expression pattern describing valid names for", 233 | "variables with global (cache) scope" 234 | ], 235 | "global_var_pattern": "[A-Z][0-9A-Z_]+", 236 | "_help_internal_var_pattern": [ 237 | "regular expression pattern describing valid names for", 238 | "variables with global scope (but internal semantic)" 239 | ], 240 | "internal_var_pattern": "_[A-Z][0-9A-Z_]+", 241 | "_help_local_var_pattern": [ 242 | "regular expression pattern describing valid names for", 243 | "variables with local scope" 244 | ], 245 | "local_var_pattern": "[a-z][a-z0-9_]+", 246 | "_help_private_var_pattern": [ 247 | "regular expression pattern describing valid names for", 248 | "privatedirectory variables" 249 | ], 250 | "private_var_pattern": "_[0-9a-z_]+", 251 | "_help_public_var_pattern": [ 252 | "regular expression pattern describing valid names for public", 253 | "directory variables" 254 | ], 255 | "public_var_pattern": "[A-Z][0-9A-Z_]+", 256 | "_help_argument_var_pattern": [ 257 | "regular expression pattern describing valid names for", 258 | "function/macro arguments and loop variables." 259 | ], 260 | "argument_var_pattern": "[a-z][a-z0-9_]+", 261 | "_help_keyword_pattern": [ 262 | "regular expression pattern describing valid names for", 263 | "keywords used in functions or macros" 264 | ], 265 | "keyword_pattern": "[A-Z][0-9A-Z_]+", 266 | "_help_max_conditionals_custom_parser": [ 267 | "In the heuristic for C0201, how many conditionals to match", 268 | "within a loop in before considering the loop a parser." 269 | ], 270 | "max_conditionals_custom_parser": 2, 271 | "_help_min_statement_spacing": [ 272 | "Require at least this many newlines between statements" 273 | ], 274 | "min_statement_spacing": 1, 275 | "_help_max_statement_spacing": [ 276 | "Require no more than this many newlines between statements" 277 | ], 278 | "max_statement_spacing": 2, 279 | "max_returns": 6, 280 | "max_branches": 12, 281 | "max_arguments": 5, 282 | "max_localvars": 15, 283 | "max_statements": 50 284 | }, 285 | "_help_encode": "Options affecting file encoding", 286 | "encode": { 287 | "_help_emit_byteorder_mark": [ 288 | "If true, emit the unicode byte-order mark (BOM) at the start", 289 | "of the file" 290 | ], 291 | "emit_byteorder_mark": false, 292 | "_help_input_encoding": [ 293 | "Specify the encoding of the input file. Defaults to utf-8" 294 | ], 295 | "input_encoding": "utf-8", 296 | "_help_output_encoding": [ 297 | "Specify the encoding of the output file. Defaults to utf-8.", 298 | "Note that cmake only claims to support utf-8 so be careful", 299 | "when using anything else" 300 | ], 301 | "output_encoding": "utf-8" 302 | }, 303 | "_help_misc": "Miscellaneous configurations options.", 304 | "misc": { 305 | "_help_per_command": [ 306 | "A dictionary containing any per-command configuration", 307 | "overrides. Currently only `command_case` is supported." 308 | ], 309 | "per_command": {} 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /.github/samples/001_HelloTriangle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/001_HelloTriangle.jpg -------------------------------------------------------------------------------- /.github/samples/002_RenderToCubeMap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/002_RenderToCubeMap.jpg -------------------------------------------------------------------------------- /.github/samples/003_RenderToCubeMapSinglePass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/003_RenderToCubeMapSinglePass.jpg -------------------------------------------------------------------------------- /.github/samples/004_YUV.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/004_YUV.jpg -------------------------------------------------------------------------------- /.github/samples/005_MeshShaders.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/005_MeshShaders.jpg -------------------------------------------------------------------------------- /.github/samples/006_RayTracingHello.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/006_RayTracingHello.jpg -------------------------------------------------------------------------------- /.github/samples/007_RayTracingAO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/007_RayTracingAO.jpg -------------------------------------------------------------------------------- /.github/samples/008_RayTracingMesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/008_RayTracingMesh.jpg -------------------------------------------------------------------------------- /.github/samples/009_TriplanarMapping.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/009_TriplanarMapping.jpg -------------------------------------------------------------------------------- /.github/samples/Tiny_MeshLarge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/Tiny_MeshLarge.jpg -------------------------------------------------------------------------------- /.github/samples/tiny_mesh_large_android.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/samples/tiny_mesh_large_android.jpg -------------------------------------------------------------------------------- /.github/screenshot01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/screenshot01.jpg -------------------------------------------------------------------------------- /.github/screenshot02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/.github/screenshot02.jpg -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "**" ] 6 | pull_request: 7 | branches: [ "**" ] 8 | 9 | # https://github.com/git-lfs/git-lfs/issues/5749 10 | env: 11 | GIT_CLONE_PROTECTION_ACTIVE: false 12 | GIT_LFS_SKIP_SMUDGE: true 13 | 14 | jobs: 15 | build: 16 | name: "Android (Ubuntu)" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | submodules: recursive 22 | 23 | - name: Set up NDK 24 | uses: nttld/setup-ndk@v1.4.2 25 | with: 26 | ndk-version: r28 27 | 28 | - name: Set up JDK 29 | uses: actions/setup-java@v4 30 | with: 31 | java-version: 17 32 | distribution: 'oracle' 33 | 34 | - name: Install Ubuntu packages 35 | shell: bash 36 | run: | 37 | sudo sed -i 's/azure\.//' /etc/apt/sources.list 38 | sudo apt-get update 39 | sudo apt-get install -y gcc-12 clang-15 xorg-dev libxinerama-dev libxcursor-dev libgles2-mesa-dev libegl1-mesa-dev libglfw3-dev libglew-dev libstdc++-12-dev extra-cmake-modules libxkbcommon-x11-dev wayland-protocols ninja-build 40 | sudo apt remove cmake -y 41 | sudo pip install cmake==4.0.0 42 | which make 43 | cmake --version 44 | 45 | - name: Prepare Vulkan SDK 46 | uses: jakoch/install-vulkan-sdk-action@v1.1.1 47 | with: 48 | vulkan_version: 1.4.304.1 49 | install_runtime: true 50 | cache: true 51 | stripdown: true 52 | 53 | - name: Generate Android projects 54 | shell: bash 55 | env: 56 | CC: "clang-15" 57 | CXX: "clang++-15" 58 | run: | 59 | cmake -G "Unix Makefiles" -S "${{ github.workspace }}" -B build -DLVK_WITH_SAMPLES_ANDROID=ON 60 | 61 | - name: Assemble 009_TriplanarMapping APK 62 | run: | 63 | cd build/android/009_TriplanarMapping 64 | chmod +x ./gradlew 65 | ./gradlew assembleDebug 66 | 67 | - name: Assemble Tiny_MeshLarge APK 68 | run: | 69 | cd build/android/Tiny_MeshLarge 70 | chmod +x ./gradlew 71 | ./gradlew assembleDebug 72 | 73 | - name: Assemble 003_RenderToCubeMapSinglePass APK 74 | run: | 75 | cd build/android/003_RenderToCubeMapSinglePass 76 | chmod +x ./gradlew 77 | ./gradlew assembleDebug 78 | 79 | cmake-build: 80 | strategy: 81 | fail-fast: false 82 | matrix: 83 | config: 84 | - { 85 | name: "Windows - MSVC 2022 (glslang)", 86 | os: windows-latest, 87 | build_type: "Debug", 88 | cc: "cl", 89 | cxx: "cl", 90 | generators: "Visual Studio 17 2022", 91 | cmake_args: "-DLVK_WITH_TRACY=ON -DLVK_WITH_TRACY_GPU=ON" 92 | } 93 | # - { 94 | # name: "Windows - MSVC 2022 (Slang)", 95 | # os: windows-latest, 96 | # build_type: "Debug", 97 | # cc: "cl", 98 | # cxx: "cl", 99 | # generators: "Visual Studio 17 2022", 100 | # cmake_args: "-DLVK_WITH_TRACY=ON -DLVK_WITH_SLANG=ON" 101 | # } 102 | - { 103 | name: "Ubuntu - Clang", 104 | os: ubuntu-latest, 105 | build_type: "Debug", 106 | cc: "clang-15", 107 | cxx: "clang++-15", 108 | generators: "Unix Makefiles", 109 | } 110 | - { 111 | name: "Ubuntu - Clang (Wayland)", 112 | os: ubuntu-latest, 113 | build_type: "Debug", 114 | cc: "clang-15", 115 | cxx: "clang++-15", 116 | generators: "Unix Makefiles", 117 | cmake_args: "-DLVK_WITH_WAYLAND=ON" 118 | } 119 | # - { 120 | # name: "Ubuntu - GCC", 121 | # os: ubuntu-latest, 122 | # build_type: "Debug", 123 | # cc: "gcc-12", 124 | # cxx: "g++-12", 125 | # generators: "Unix Makefiles" 126 | # } 127 | runs-on: ${{ matrix.config.os }} 128 | 129 | steps: 130 | - uses: actions/checkout@v4 131 | with: 132 | submodules: recursive 133 | 134 | - if: startsWith(matrix.config.os, 'ubuntu') 135 | run: | 136 | sudo sed -i 's/azure\.//' /etc/apt/sources.list 137 | sudo apt-get update 138 | sudo apt-get install -y gcc-12 clang-15 xorg-dev libxinerama-dev libxcursor-dev libgles2-mesa-dev libegl1-mesa-dev libglfw3-dev libglew-dev libstdc++-12-dev extra-cmake-modules libxkbcommon-x11-dev wayland-protocols 139 | sudo apt remove cmake -y 140 | sudo pip install cmake==4.0.0 141 | 142 | - name: Prepare Vulkan SDK 143 | uses: jakoch/install-vulkan-sdk-action@v1.1.1 144 | with: 145 | vulkan_version: 1.4.304.1 146 | install_runtime: true 147 | cache: true 148 | stripdown: true 149 | 150 | - name: Get the number of CPU cores 151 | uses: SimenB/github-actions-cpu-cores@v2 152 | 153 | - name: Build 154 | shell: bash 155 | env: 156 | CC: ${{ matrix.config.cc }} 157 | CXX: ${{ matrix.config.cxx }} 158 | run: | 159 | cmake ${{ env.CMAKE_GENERATOR }} -S "${{ github.workspace }}" -B build ${{ matrix.config.cmake_args }} 160 | cd build 161 | cmake --build . --parallel ${{ steps.cpu-cores.outputs.count }} 162 | 163 | cmake-build-macos: 164 | strategy: 165 | fail-fast: false 166 | matrix: 167 | config: 168 | - { 169 | name: "macOS - Clang (Xcode)", 170 | os: macos-latest, 171 | build_type: "Debug", 172 | cc: "clang", 173 | cxx: "clang++", 174 | generators: "Xcode", 175 | cmake_args: "-DLVK_WITH_TRACY=OFF" 176 | } 177 | runs-on: ${{ matrix.config.os }} 178 | 179 | steps: 180 | - uses: actions/checkout@v4 181 | with: 182 | submodules: recursive 183 | 184 | - name: Install Vulkan SDK 185 | uses: jakoch/install-vulkan-sdk-action@v1.1.1 186 | with: 187 | vulkan_version: 1.4.304.1 188 | install_runtime: true 189 | cache: true 190 | stripdown: true 191 | 192 | - name: Get the number of CPU cores 193 | uses: SimenB/github-actions-cpu-cores@v2 194 | 195 | - name: Build 196 | shell: bash 197 | env: 198 | CC: ${{ matrix.config.cc }} 199 | CXX: ${{ matrix.config.cxx }} 200 | run: | 201 | cmake ${{ env.CMAKE_GENERATOR }} -S "${{ github.workspace }}" -B build ${{ matrix.config.cmake_args }} 202 | cd build 203 | cmake --build . --parallel ${{ steps.cpu-cores.outputs.count }} 204 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.19) 7 | 8 | include(CMakeDependentOption) 9 | include(FetchContent) 10 | 11 | project("LVK" CXX C) 12 | 13 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 14 | 15 | # cmake-format: off 16 | option(LVK_DEPLOY_DEPS "Deploy dependencies via CMake" ON) 17 | option(LVK_WITH_GLFW "Enable GLFW" ON) 18 | option(LVK_WITH_SAMPLES "Enable sample demo apps" ON) 19 | option(LVK_WITH_SAMPLES_ANDROID "Generate Android projects for demo apps" OFF) 20 | option(LVK_WITH_TRACY "Enable Tracy profiler" ON) 21 | option(LVK_WITH_TRACY_GPU "Enable Tracy GPU profiler" OFF) 22 | option(LVK_WITH_WAYLAND "Enable Wayland" OFF) 23 | option(LVK_WITH_IMPLOT "Enable ImPlot" ON) 24 | option(LVK_WITH_OPENXR "Enable OpenXR" OFF) 25 | option(LVK_WITH_ANDROID_VALIDATION "Enable validation layers on Android" ON) 26 | option(LVK_WITH_SLANG "Enable Slang compiler" OFF) 27 | 28 | cmake_dependent_option(LVK_WITH_VULKAN_PORTABILITY "Enable portability extension" ON "APPLE" OFF) 29 | 30 | set(LVK_ANDROID_ABI "arm64-v8a" CACHE STRING "Enabled ABI on Android") 31 | set(LVK_CUSTOM_MOLTENVK_PATH "" CACHE STRING "Path to custom MoltenVK") 32 | # cmake-format: on 33 | 34 | if(LVK_WITH_SAMPLES AND NOT LVK_WITH_GLFW) 35 | message(STATUS "WARNING: Setting LVK_WITH_SAMPLES=ON forces LVK_WITH_GLFW=ON") 36 | set(LVK_WITH_GLFW ON) 37 | endif() 38 | 39 | if(ANDROID) 40 | message(STATUS "WARNING: LVK_WITH_GLFW and LVK_WITH_SLANG were set to OFF for Android") 41 | set(LVK_WITH_GLFW OFF) 42 | set(LVK_WITH_SLANG OFF) 43 | endif() 44 | 45 | if(LVK_WITH_TRACY_GPU AND NOT LVK_WITH_TRACY) 46 | message(STATUS "WARNING: Setting LVK_WITH_TRACY_GPU=ON forces LVK_WITH_TRACY=ON") 47 | set(LVK_WITH_TRACY ON) 48 | endif() 49 | 50 | if(LVK_WITH_WAYLAND AND (ANDROID OR APPLE OR WIN32)) 51 | message(FATAL_ERROR "LVK_WITH_WAYLAND=ON can be set only on Linux") 52 | endif() 53 | 54 | if(APPLE) 55 | if(EXISTS ${LVK_CUSTOM_MOLTENVK_PATH}) 56 | message("Custom MoltenVK found at ${LVK_CUSTOM_MOLTENVK_PATH}") 57 | set(LVK_USE_CUSTOM_MOLTENVK ON) 58 | else() 59 | if(NOT LVK_WITH_VULKAN_PORTABILITY) 60 | message(FATAL_ERROR "LVK_WITH_VULKAN_PORTABILITY=ON must be set on macOS if Vulkan SDK is used") 61 | endif() 62 | set(LVK_USE_CUSTOM_MOLTENVK OFF) 63 | endif() 64 | endif() 65 | 66 | include(cmake/CommonMacros.txt) 67 | 68 | if(NOT LVK_USE_CUSTOM_MOLTENVK AND DEFINED ENV{VULKAN_SDK}) 69 | message(STATUS "VULKAN_SDK=$ENV{VULKAN_SDK}") 70 | if(NOT EXISTS $ENV{VULKAN_SDK}) 71 | message(FATAL_ERROR "$ENV{VULKAN_SDK} does not exist.") 72 | endif() 73 | endif() 74 | 75 | function(lvk_set_folder target folder_name) 76 | set_property(TARGET ${target} PROPERTY FOLDER ${folder_name}) 77 | endfunction() 78 | 79 | function(lvk_set_cxxstd target cpp_version) 80 | set_property(TARGET ${target} PROPERTY CXX_STANDARD ${cpp_version}) 81 | set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED ON) 82 | endfunction() 83 | 84 | # cmake-format: off 85 | message(STATUS "LVK_DEPLOY_DEPS = ${LVK_DEPLOY_DEPS}") 86 | message(STATUS "LVK_WITH_GLFW = ${LVK_WITH_GLFW}") 87 | message(STATUS "LVK_WITH_SAMPLES = ${LVK_WITH_SAMPLES}") 88 | message(STATUS "LVK_WITH_SAMPLES_ANDROID = ${LVK_WITH_SAMPLES_ANDROID}") 89 | message(STATUS "LVK_WITH_TRACY = ${LVK_WITH_TRACY}") 90 | message(STATUS "LVK_WITH_TRACY_GPU = ${LVK_WITH_TRACY_GPU}") 91 | message(STATUS "LVK_WITH_VULKAN_PORTABILITY = ${LVK_WITH_VULKAN_PORTABILITY}") 92 | message(STATUS "LVK_WITH_WAYLAND = ${LVK_WITH_WAYLAND}") 93 | message(STATUS "LVK_WITH_IMPLOT = ${LVK_WITH_IMPLOT}") 94 | message(STATUS "LVK_WITH_OPENXR = ${LVK_WITH_OPENXR}") 95 | # cmake-format: on 96 | 97 | # cmake-format: off 98 | if(LVK_WITH_SAMPLES_ANDROID) 99 | message(STATUS "LVK_ANDROID_ABI = ${LVK_ANDROID_ABI}") 100 | message(STATUS "LVK_WITH_ANDROID_VALIDATION = ${LVK_WITH_ANDROID_VALIDATION}") 101 | endif() 102 | # cmake-format: on 103 | 104 | if(NOT DEFINED CMAKE_BUILD_TYPE) 105 | set(CMAKE_BUILD_TYPE "Debug") 106 | endif() 107 | 108 | add_compile_options("$<$:-DDEBUG>") 109 | 110 | message(STATUS "Generator : " ${CMAKE_GENERATOR}) 111 | message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) 112 | 113 | set(LVK_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 114 | set(LVK_DEPS_CACHE_FILE "${LVK_ROOT_DIR}/third-party/.bootstrap-deps.json") 115 | find_package(Python3 COMPONENTS Interpreter) 116 | 117 | message(STATUS "LVK_ROOT_DIR = " ${LVK_ROOT_DIR}) 118 | 119 | # enable multithreaded builds for Visual Studio 120 | add_compile_options($<$:/MP>) 121 | 122 | if(LVK_DEPLOY_DEPS) 123 | # deploy dependencies during configuration 124 | execute_process(COMMAND ${Python3_EXECUTABLE} deploy_deps.py WORKING_DIRECTORY ${LVK_ROOT_DIR} COMMAND_ERROR_IS_FATAL ANY) 125 | 126 | if(NOT EXISTS ${LVK_DEPS_CACHE_FILE}) 127 | message(FATAL_ERROR "Cannot deploy dependencies.") 128 | endif() 129 | 130 | # regenerate dependencies when .bootstrap-deps.json is older than bootstrap-deps.json 131 | add_custom_command(COMMAND ${Python3_EXECUTABLE} deploy_deps.py OUTPUT ${LVK_DEPS_CACHE_FILE} 132 | DEPENDS "${LVK_ROOT_DIR}/third-party/bootstrap-deps.json" WORKING_DIRECTORY ${LVK_ROOT_DIR}) 133 | add_custom_target(LVKDependencies DEPENDS ${LVK_DEPS_CACHE_FILE}) 134 | 135 | lvk_set_folder(LVKDependencies "LVK") 136 | 137 | if(LVK_WITH_SLANG) 138 | FetchContent_Populate( 139 | slang 140 | GIT_REPOSITORY https://github.com/shader-slang/slang 141 | GIT_TAG v2025.6 142 | SOURCE_DIR ${LVK_ROOT_DIR}/third-party/deps/src/slang 143 | ) 144 | endif() 145 | endif() 146 | 147 | if(LVK_WITH_WAYLAND) 148 | include(${LVK_ROOT_DIR}/third-party/deps/src/cmake-wayland/FindWayland.cmake) 149 | if(NOT WAYLAND_FOUND) 150 | message(FATAL_ERROR "Wayland is not found in this OS") 151 | endif() 152 | endif() 153 | 154 | if(LVK_WITH_TRACY) 155 | add_definitions("-DTRACY_ENABLE=1") 156 | add_subdirectory(third-party/deps/src/tracy) 157 | lvk_set_folder(TracyClient "third-party") 158 | endif() 159 | 160 | if(LVK_WITH_OPENXR) 161 | if(NOT WIN32) 162 | message(FATAL_ERROR "OpenXR can be enabled only on Windows.") 163 | endif() 164 | add_subdirectory(third-party/deps/src/openxr-sdk) 165 | # cmake-format: off 166 | lvk_set_folder(openxr_loader "third-party/OpenXR") 167 | lvk_set_folder(generate_openxr_header "third-party/OpenXR") 168 | lvk_set_folder(xr_common_generated_files "third-party/OpenXR") 169 | lvk_set_folder(xr_global_generated_files "third-party/OpenXR") 170 | # cmake-format: on 171 | endif() 172 | 173 | # temporary 174 | if(NOT LVK_USE_CUSTOM_MOLTENVK AND NOT LVK_WITH_SLANG) 175 | find_package(Vulkan REQUIRED) 176 | endif() 177 | 178 | include_directories(.) 179 | include_directories(src) 180 | 181 | add_subdirectory(lvk) 182 | 183 | target_include_directories(LVKLibrary PUBLIC ${LVK_ROOT_DIR}) 184 | target_include_directories(LVKLibrary PUBLIC ${LVK_ROOT_DIR}/src) 185 | 186 | if(LVK_WITH_TRACY) 187 | target_compile_definitions(LVKVulkan PUBLIC "LVK_WITH_TRACY=1") 188 | if(LVK_WITH_TRACY_GPU) 189 | target_compile_definitions(LVKVulkan PUBLIC "LVK_WITH_TRACY_GPU=1") 190 | endif() 191 | endif() 192 | 193 | if(LVK_WITH_GLFW) 194 | target_compile_definitions(LVKLibrary PUBLIC "LVK_WITH_GLFW=1") 195 | endif() 196 | 197 | if(LVK_WITH_IMPLOT) 198 | target_compile_definitions(LVKLibrary PUBLIC "LVK_WITH_IMPLOT=1") 199 | endif() 200 | 201 | if(LVK_DEPLOY_DEPS) 202 | add_dependencies(LVKLibrary LVKDependencies) 203 | endif() 204 | 205 | add_library(LVKstb third-party/deps/patches/stb_impl/stb_image.c third-party/deps/patches/stb_impl/stb_image_resize2.c 206 | third-party/deps/patches/stb_impl/stb_image_write.c) 207 | target_include_directories(LVKstb PUBLIC "third-party/deps/src/stb") 208 | if(ANDROID) 209 | target_compile_definitions(LVKstb PUBLIC "STBI_NO_SIMD=1") 210 | target_compile_definitions(LVKstb PUBLIC "STBIR_NO_SIMD=1") 211 | endif() 212 | lvk_set_folder(LVKstb "LVK") 213 | 214 | # cmake-format: off 215 | set(MINILOG_BUILD_EXAMPLE OFF CACHE BOOL "") 216 | set(MINILOG_RAW_OUTPUT ON CACHE BOOL "") 217 | # cmake-format: on 218 | add_subdirectory(third-party/deps/src/minilog) 219 | lvk_set_folder(minilog "third-party") 220 | target_compile_definitions(minilog PUBLIC "-DMINILOG_ENABLE_VA_LIST=1") 221 | 222 | target_link_libraries(LVKLibrary PUBLIC minilog) 223 | target_include_directories(LVKLibrary PUBLIC "third-party/deps/src") 224 | 225 | if(LVK_WITH_GLFW) 226 | # cmake-format: off 227 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "") 228 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "") 229 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "") 230 | set(GLFW_INSTALL OFF CACHE BOOL "") 231 | set(GLFW_VULKAN_STATIC OFF CACHE BOOL "") 232 | if(WAYLAND_FOUND) 233 | set(GLFW_BUILD_WAYLAND ON) 234 | set(GLFW_BUILD_X11 OFF) 235 | else() 236 | set(GLFW_BUILD_WAYLAND OFF) 237 | set(GLFW_BUILD_X11 ON) 238 | endif() 239 | add_subdirectory(third-party/deps/src/glfw) 240 | lvk_set_folder(glfw "third-party/GLFW3") 241 | lvk_set_folder(update_mappings "third-party/GLFW3") 242 | # cmake-format: on 243 | endif() 244 | 245 | if(LVK_WITH_SAMPLES) 246 | include_directories("third-party/deps/src") 247 | include_directories("third-party/deps/src/ktx-software") 248 | include_directories("third-party/deps/src/glm") 249 | include_directories("third-party/deps/src/stb") 250 | include_directories("third-party/deps/src/taskflow") 251 | include_directories("third-party/deps/src/3D-Graphics-Rendering-Cookbook") 252 | if(UNIX AND NOT ANDROID) 253 | find_package(OpenGL REQUIRED) 254 | endif() 255 | # cmake-format: off 256 | set(KTX_FEATURE_DOC OFF CACHE BOOL "") 257 | set(KTX_FEATURE_GL_UPLOAD OFF CACHE BOOL "") 258 | set(KTX_FEATURE_JNI OFF CACHE BOOL "") 259 | set(KTX_FEATURE_KTX1 ON CACHE BOOL "") 260 | set(KTX_FEATURE_KTX2 ON CACHE BOOL "") 261 | set(KTX_FEATURE_LOADTEST_APPS OFF CACHE BOOL "") 262 | set(KTX_FEATURE_STATIC_LIBRARY ON CACHE BOOL "") 263 | set(KTX_FEATURE_TESTS OFF CACHE BOOL "") 264 | set(KTX_FEATURE_TOOLS OFF CACHE BOOL "") 265 | set(KTX_FEATURE_VK_UPLOAD OFF CACHE BOOL "") 266 | # cmake-format: on 267 | add_subdirectory(third-party/deps/src/ldrutils) 268 | lvk_set_folder(LUtils "third-party") 269 | add_subdirectory(third-party/deps/src/ktx-software) 270 | if(MSVC) 271 | target_compile_options(ktx PRIVATE "/wd5287") 272 | target_compile_options(ktx_read PRIVATE "/wd5287") 273 | endif() 274 | lvk_set_folder(ktx "third-party/ktx-software") 275 | lvk_set_folder(ktx_read "third-party/ktx-software") 276 | lvk_set_folder(ktx_version "third-party/ktx-software") 277 | lvk_set_folder(obj_basisu_cbind "third-party/ktx-software") 278 | lvk_set_folder(objUtil "third-party/ktx-software") 279 | if(TARGET astcenc-avx2-static) 280 | lvk_set_folder(astcenc-avx2-static "third-party/ktx-software") 281 | endif() 282 | add_subdirectory(third-party/deps/src/meshoptimizer) 283 | add_subdirectory(third-party/deps/src/fast_obj) 284 | add_subdirectory(samples) 285 | # cmake-format: off 286 | lvk_set_folder(meshoptimizer "third-party") 287 | lvk_set_folder(fast_obj_lib "third-party") 288 | # cmake-format: on 289 | endif() 290 | 291 | if(LVK_WITH_TRACY) 292 | target_link_libraries(LVKVulkan PUBLIC TracyClient) 293 | endif() 294 | 295 | if(LVK_WITH_OPENXR) 296 | target_compile_definitions(LVKVulkan PRIVATE "XR_USE_GRAPHICS_API_VULKAN=1") 297 | target_compile_definitions(LVKLibrary PUBLIC "LVK_WITH_OPENXR=1") 298 | target_link_libraries(LVKLibrary PUBLIC OpenXR::openxr_loader) 299 | target_include_directories(LVKLibrary PUBLIC "${LVK_ROOT_DIR}/third-party/deps/src/openxr-sdk/include") 300 | endif() 301 | 302 | # ImGui 303 | target_include_directories(LVKLibrary PUBLIC "${LVK_ROOT_DIR}/third-party/deps/src/imgui") 304 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## LightweightVK 2 | 3 | MIT License 4 | 5 | Copyright (c) Sergey Kosarevsky, 2023-2025 6 | 7 | ## Based on https://github.com/facebook/igl/ 8 | 9 | MIT License 10 | 11 | Copyright (c) Meta Platforms, Inc. and affiliates. 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | 31 | ## Dependencies 32 | 33 | 3D-Graphics-Rendering-Cookbook 34 | https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/blob/master/LICENSE 35 | 36 | 3D-Graphics-Rendering-Cookbook (2nd Edition) 37 | https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook-Second-Edition/blob/master/LICENSE 38 | 39 | Amazon Lumberyard Bistro 40 | https://developer.nvidia.com/orca/amazon-lumberyard-bistro 41 | https://casual-effects.com/data/ 42 | 43 | bc7env 44 | https://github.com/richgel999/bc7enc/blob/master/LICENSE 45 | 46 | Bootstrap 47 | https://github.com/corporateshark/bootstrapping/blob/master/LICENSE 48 | 49 | Damaged Helmet 50 | https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/DamagedHelmet/README.md 51 | 52 | EGL 53 | https://github.com/McNopper/EGL/blob/master/EGL/LICENCE.txt 54 | 55 | fast_obj 56 | https://github.com/thisistherk/fast_obj/blob/master/LICENSE 57 | 58 | fmt 59 | https://github.com/fmtlib/fmt/blob/master/LICENSE.rst 60 | 61 | glew 62 | https://github.com/nigels-com/glew/blob/master/LICENSE.txt 63 | 64 | glfw 65 | https://github.com/glfw/glfw/blob/master/LICENSE.md 66 | 67 | glm 68 | https://github.com/g-truc/glm 69 | 70 | glslang 71 | https://github.com/KhronosGroup/glslang/blob/main/LICENSE.txt 72 | 73 | googletest 74 | https://github.com/google/googletest/blob/main/LICENSE 75 | 76 | IGL 77 | https://github.com/facebook/igl/blob/main/LICENSE.md 78 | 79 | imgui 80 | https://github.com/ocornut/imgui/blob/master/LICENSE.txt 81 | 82 | implot 83 | https://github.com/epezent/implot/blob/master/LICENSE 84 | 85 | ios-cmake 86 | https://github.com/leetal/ios-cmake/blob/master/LICENSE.md 87 | 88 | ldrutils 89 | https://github.com/corporateshark/ldrutils/blob/master/LICENSE.md 90 | 91 | Meshoptimizer 92 | https://github.com/zeux/meshoptimizer/blob/master/LICENSE.md 93 | 94 | minilog 95 | https://github.com/corporateshark/minilog/blob/main/LICENSE 96 | 97 | stb 98 | https://github.com/nothings/stb/blob/master/LICENSE 99 | 100 | taskflow 101 | https://github.com/taskflow/taskflow/blob/master/LICENSE 102 | 103 | tracy 104 | https://github.com/wolfpld/tracy/blob/master/LICENSE 105 | 106 | volk 107 | https://github.com/zeux/volk/blob/master/LICENSE.md 108 | 109 | Vulkan Memory Allocator 110 | https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/blob/master/LICENSE.txt 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LightweightVK [![Build Status](https://github.com/corporateshark/lightweightvk/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/corporateshark/lightweightvk/actions) 2 | ======================== 3 | 4 | LightweightVK is a deeply refactored **bindless-only** fork of [IGL](https://github.com/facebook/igl) which is designed to run on top of **Vulkan 1.3** with optional **mesh shaders** and **ray tracing** support. 5 | 6 | The main goals of LightweightVK: 7 | 8 | 1. **Lean.** Minimalistic API without bloat (no `std::vector`, `std::unordered_map` etc in the API). 9 | 2. **Bindless.** Utilize Vulkan 1.3+ dynamic rendering, descriptor indexing, and buffer device address features for modern bindless-only API design. Ray tracing features are fully integrated with the bindless-only design. 10 | 3. **Agile.** A playground for experiments to enable quick exploration of ideas and adoption of Vulkan API changes. 11 | Designed for rapid prototyping of Vulkan-based renderers. 12 | 13 | There are no plans to keep this fork in sync with the upstream. 14 | 15 | ## Supported rendering backends 16 | 17 | * Vulkan 1.3 (Windows, Linux, Android) 18 | * optional **VK_KHR_acceleration_structure** (Windows, Linux, Android) 19 | * optional **VK_KHR_ray_tracing_pipeline** (Windows, Linux, Android) 20 | * optional **VK_KHR_ray_query** (Windows, Linux, Android) 21 | * optional **VK_EXT_mesh_shader** (Windows, Linux) 22 | * Vulkan 1.2 + extensions (MacOS) 23 | 24 | ## Supported platforms 25 | 26 | * Linux 27 | * Windows 28 | * MacOS (via MoltenVK) 29 | * Android 30 | 31 | ## API Support 32 | 33 | | | Windows | Linux | MacOS | Android | 34 | | ----------------------------- | -------------------------- | -------------------------- | -------------------------- | -------------------------- | 35 | | Vulkan 1.3 | :heavy_check_mark: | :heavy_check_mark: | :heavy_exclamation_mark: | :heavy_check_mark: | 36 | | Vulkan 1.2 | | | :heavy_check_mark: | | 37 | | VK_KHR_acceleration_structure | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | 38 | | VK_KHR_ray_tracing_pipeline | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | 39 | | VK_KHR_ray_query | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | 40 | | VK_EXT_mesh_shader | :heavy_check_mark: | :heavy_check_mark: | | | 41 | 42 | On MacOS, features required by LightweightVK are enabled via extensions `VK_KHR_dynamic_rendering`, `VK_EXT_subgroup_size_control`, 43 | `VK_EXT_extended_dynamic_state`, `VK_EXT_extended_dynamic_state2`, and `VK_KHR_synchronization2`. 44 | 45 | :heavy_exclamation_mark: `VK_KHR_maintenance4` is not yet supported in MoltenVK :heavy_exclamation_mark: 46 | 47 | Check the status of [Vulkan 1.3 support](https://github.com/KhronosGroup/MoltenVK/issues/1930) in MoltenVK. 48 | 49 | ## Build 50 | 51 | Before building, run the deployment scripts: 52 | 53 | ``` 54 | python3 deploy_content.py 55 | python3 deploy_deps.py 56 | ``` 57 | 58 | These scripts download external third-party dependencies. Please check [LICENSE.md](./LICENSE.md) for the full list. 59 | 60 | ### Windows 61 | 62 | ``` 63 | cd build 64 | cmake .. -G "Visual Studio 17 2022" 65 | ``` 66 | 67 | ### Linux 68 | 69 | ``` 70 | sudo apt-get install clang xorg-dev libxinerama-dev libxcursor-dev libgles2-mesa-dev libegl1-mesa-dev libglfw3-dev libglew-dev libstdc++-12-dev extra-cmake-modules libxkbcommon-x11-dev wayland-protocols 71 | cd build 72 | cmake .. -G "Unix Makefiles" 73 | ``` 74 | 75 | :heavy_exclamation_mark: Use `cmake .. -G "Unix Makefiles" -DLVK_WITH_WAYLAND=ON` to build for Wayland, X11 is used by default. 76 | 77 | ### MacOS 78 | 79 | :heavy_exclamation_mark: Be sure that VulkanSDK 1.3.296.0 for MacOS is installed https://vulkan.lunarg.com/sdk/home#mac 80 | 81 | ``` 82 | cd build 83 | cmake .. -G "Xcode" 84 | ``` 85 | 86 | ### Android 87 | 88 | :heavy_exclamation_mark: Be sure that [Android Studio](https://developer.android.com/studio) is set up. 89 | 90 | :heavy_exclamation_mark: Be sure that the `ANDROID_NDK` environment variable points to your Android NDK. 91 | 92 | :heavy_exclamation_mark: Be sure that the `JAVA_HOME` environment variable is set to the path of the Java Runtime. 93 | 94 | :heavy_exclamation_mark: Be sure that the `adb` platform tool is in the `PATH` environment variable. 95 | 96 | ``` 97 | cd build 98 | cmake .. -DLVK_WITH_SAMPLES_ANDROID=ON 99 | cd android/Tiny # or any other sample 100 | ./gradlew assembleDebug # or assembleRelease 101 | ``` 102 | You can also open the project in Android Studio and build it from there. 103 | 104 | Before running demo apps on your device, connect the device to a desktop machine and run the deployment script: 105 | 106 | ``` 107 | python3 deploy_content_android.py 108 | ``` 109 | 110 | > NOTE: To run demos on an Android device, it should support Vulkan 1.3. Please check https://vulkan.gpuinfo.org/listdevices.php?platform=android 111 | 112 | > NOTE: At the moment, demo apps do not support touch input on Android. 113 | 114 | ## Screenshots 115 | 116 | Check out [https://github.com/corporateshark/lightweightvk/samples](https://github.com/corporateshark/lightweightvk/tree/master/samples). 117 | 118 | A comprehensive set of examples can be found in this repository [3D Graphics Rendering Cookbook: 2nd edition](https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook-Second-Edition) and in the book 119 | [Vulkan 3D Graphics Rendering Cookbook - 2nd Edition](https://www.amazon.com/Vulkan-Graphics-Rendering-Cookbook-High-Performance/dp/1803248114). 120 | 121 | [![Vulkan 3D Graphics Rendering Cookbook](.github/screenshot01.jpg)](https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook-Second-Edition/tree/main/Chapter11/06_FinalDemo/src) 122 | ![image](.github/samples/007_RayTracingAO.jpg) 123 | [![Solar System Demo](.github/screenshot02.jpg)](https://github.com/corporateshark/lightweightvk/blob/master/samples/DEMO_001_SolarSystem.cpp) 124 | 125 | ## Interop with raw Vulkan API calls 126 | 127 | The header file `lvk/vulkan/VulkanUtils.h` offers a collection of functions that allow you to access the underlying Vulkan API objects from LightweightVK handles. This makes it easy to mix LVK and Vulkan code, as shown in the following example: 128 | 129 | ```c 130 | lvk::Holder vertexBuffer = ctx_->createBuffer({...}); 131 | ... 132 | lvk::ICommandBuffer& buffer = ctx_->acquireCommandBuffer(); 133 | VkCommandBuffer cmdBuf = getVkCommandBuffer(buffer); 134 | VkBuffer buf = getVkBuffer(vertexBuffer); 135 | vkCmdUpdateBuffer(cmdBuf, buf, 0, sizeof(params), ¶ms); 136 | ctx_->submit(buffer, ctx_->getCurrentSwapchainTexture()); 137 | ``` 138 | 139 | If you'd like to add more helper functions, feel free to submit a pull request. 140 | 141 | ## License 142 | 143 | LightweightVK is released under the MIT license, see [LICENSE.md](./LICENSE.md) for the full text as well as third-party library 144 | acknowledgements. 145 | -------------------------------------------------------------------------------- /android/app/AndroidManifest.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/MainActivity.java.in: -------------------------------------------------------------------------------- 1 | package org.lvk.samples; 2 | 3 | import android.content.Intent; 4 | import android.content.pm.PackageManager; 5 | import android.net.Uri; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.os.Environment; 9 | import android.provider.Settings; 10 | 11 | public class MainActivity extends android.app.NativeActivity { 12 | private static final String PERMISSION_READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"; 13 | private static final String PERMISSION_WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; 14 | private static final int REQUEST_PERMISSION_CODE = 1; 15 | 16 | static { 17 | System.loadLibrary("lvk_android_native_@APP_NAME@"); 18 | } 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | if (checkSelfPermission(PERMISSION_READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 24 | checkSelfPermission(PERMISSION_WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 25 | requestPermissions(new String[] {PERMISSION_READ_EXTERNAL_STORAGE, 26 | PERMISSION_WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE); 27 | } 28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 29 | if (!Environment.isExternalStorageManager()) { 30 | Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); 31 | Uri uri = Uri.fromParts("package", getPackageName(), null); 32 | intent.setData(uri); 33 | startActivity(intent); 34 | } 35 | } 36 | } 37 | 38 | @SuppressWarnings("deprecation") 39 | @Override 40 | public void onBackPressed() { 41 | System.gc(); 42 | System.exit(0); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/build.gradle.in: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'org.lvk.samples' 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | applicationId "org.lvk.samples.lvk_@APP_NAME@" 11 | minSdk 30 12 | targetSdk 34 13 | versionCode 1 14 | versionName "1.0" 15 | ndk { 16 | abiFilters @ANDROID_ABI_FILTERS@ 17 | } 18 | externalNativeBuild { 19 | cmake { 20 | targets "lvk_android_native_@APP_NAME@" 21 | cppFlags "-std=c++20" 22 | arguments "-DLVK_DEPLOY_DEPS=OFF", "-DLVK_WITH_GLFW=OFF", "-DLVK_WITH_SAMPLES=ON", "-DLVK_WITH_SAMPLES_ANDROID=ON", "-DLVK_WITH_TRACY=OFF", "-DLVK_WITH_VULKAN_PORTABILITY=OFF", "-DLVK_WITH_WAYLAND=OFF", "-DLVK_WITH_IMPLOT=ON", "-DLVK_WITH_OPENXR=OFF" 23 | } 24 | } 25 | } 26 | 27 | aaptOptions { 28 | noCompress 'png', 'ktx', 'data' 29 | } 30 | 31 | buildTypes { 32 | release { 33 | signingConfig signingConfigs.debug 34 | } 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | ndkVersion "28.0.13004108" 42 | externalNativeBuild { 43 | cmake { 44 | path file('../../../../CMakeLists.txt') 45 | version '4.0.0' 46 | } 47 | } 48 | buildFeatures { 49 | viewBinding true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.9.0' apply false 4 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corporateshark/lightweightvk/6bcd5bcfce70519b916e9ee0ffa89de433ad6a30/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Feb 10 20:00:48 GMT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/settings.gradle.in: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | rootProject.name = "@APP_NAME@" 17 | include ':app' 18 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | *.* 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /cmake/CommonMacros.txt: -------------------------------------------------------------------------------- 1 | # 2 | # based on https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/blob/master/CMake/CommonMacros.txt 3 | # 4 | 5 | cmake_minimum_required(VERSION 3.16) 6 | 7 | macro(lvk_setup_groups src_files) 8 | foreach(FILE ${src_files}) 9 | get_filename_component(PARENT_DIR "${FILE}" PATH) 10 | 11 | # skip src or include and changes /'s to \\'s 12 | set(GROUP "${PARENT_DIR}") 13 | string(REPLACE "/" "\\" GROUP "${GROUP}") 14 | 15 | source_group("${GROUP}" FILES "${FILE}") 16 | endforeach() 17 | endmacro() 18 | 19 | macro(lvk_set_folder target folder_name) 20 | set_property(TARGET ${target} PROPERTY FOLDER ${folder_name}) 21 | endmacro() 22 | 23 | macro(lvk_setup_target target) 24 | set_property(TARGET ${target} PROPERTY CXX_STANDARD 20) 25 | set_property(TARGET ${target} PROPERTY CXX_STANDARD_REQUIRED ON) 26 | endmacro() 27 | -------------------------------------------------------------------------------- /deploy_content.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | 8 | import os 9 | import sys 10 | 11 | folder = "third-party" 12 | script = os.path.join(folder, "bootstrap.py") 13 | json = os.path.join(folder, "bootstrap-content.json") 14 | base = os.path.join(folder, "content") 15 | 16 | try: 17 | os.mkdir(base) 18 | except FileExistsError: 19 | pass 20 | 21 | os.system( 22 | '"{}" {} -b {} --bootstrap-file={}'.format(sys.executable, script, base, json) 23 | ) 24 | -------------------------------------------------------------------------------- /deploy_content_android.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # LightweightVK 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | import os 8 | import subprocess 9 | 10 | def get_external_storage(): 11 | adb_command = ['adb', 'shell', 'echo', '$EXTERNAL_STORAGE'] 12 | try: 13 | process = subprocess.Popen(adb_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 14 | stdout, stderr = process.communicate() 15 | output = stdout.decode().strip() 16 | if stderr: 17 | print("Error executing adb shell command:", stderr.decode().strip()) 18 | return output 19 | except Exception as e: 20 | print("An error occurred:", e) 21 | return None 22 | 23 | external_storage_path = get_external_storage() 24 | if external_storage_path is not None: 25 | paths = [(os.path.join("third-party", "content"), 26 | os.path.join(external_storage_path, "LVK", "content").replace("\\", "/")), 27 | (os.path.join("third-party", "deps", "src", "3D-Graphics-Rendering-Cookbook", "data"), 28 | os.path.join(external_storage_path, "LVK", "deps", "src", "3D-Graphics-Rendering-Cookbook", "data").replace("\\", "/"))] 29 | for (desktop_path, android_path) in paths: 30 | try: 31 | print('Copying {} to {} ...'.format(desktop_path, android_path)) 32 | process = subprocess.Popen(['adb', 'push', desktop_path, android_path], shell=False) 33 | process.communicate() 34 | process.wait() 35 | print('Completed') 36 | except Exception as e: 37 | print("An error occurred:", e) 38 | else: 39 | print("External storage path is not found") 40 | -------------------------------------------------------------------------------- /deploy_deps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | 8 | import os 9 | import sys 10 | 11 | folder = "third-party" 12 | script = os.path.join(folder, "bootstrap.py") 13 | json = os.path.join(folder, "bootstrap-deps.json") 14 | base = os.path.join(folder, "deps") 15 | 16 | os.system( 17 | '"{}" {} -b {} --bootstrap-file={} --break-on-first-error'.format( 18 | sys.executable, script, base, json 19 | ) 20 | ) 21 | -------------------------------------------------------------------------------- /lvk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # LightweightVK 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.19) 7 | 8 | project(LVKLibrary CXX C) 9 | 10 | file(GLOB SRC_FILES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) 11 | file(GLOB HEADER_FILES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) 12 | if(APPLE) 13 | file(GLOB SRC_FILES_MM LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.mm) 14 | list(APPEND SRC_FILES ${SRC_FILES_MM}) 15 | endif() 16 | 17 | add_library(LVKLibrary ${SRC_FILES} ${HEADER_FILES}) 18 | 19 | lvk_setup_groups("${SRC_FILES}") 20 | lvk_setup_groups("${HEADER_FILES}") 21 | 22 | target_include_directories(LVKLibrary PUBLIC "${LVK_ROOT_DIR}/lvk") 23 | if(WAYLAND_FOUND) 24 | target_compile_definitions(LVKLibrary PUBLIC -DLVK_WITH_WAYLAND=1) 25 | endif() 26 | 27 | lvk_setup_target(LVKLibrary) 28 | lvk_set_folder(LVKLibrary "LVK") 29 | 30 | add_subdirectory(vulkan) 31 | 32 | target_link_libraries(LVKLibrary PUBLIC LVKVulkan) 33 | if(LVK_WITH_GLFW) 34 | target_link_libraries(LVKLibrary PUBLIC glfw) 35 | endif() 36 | 37 | if(APPLE) 38 | target_link_libraries(LVKLibrary PRIVATE "-framework Metal" "-framework AppKit" "-framework Foundation" "-framework QuartzCore") 39 | endif() 40 | -------------------------------------------------------------------------------- /lvk/HelpersImGui.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "HelpersImGui.h" 9 | 10 | #include "imgui/imgui.cpp" 11 | #include "imgui/imgui_draw.cpp" 12 | #include "imgui/imgui_tables.cpp" 13 | #include "imgui/imgui_widgets.cpp" 14 | #if defined(LVK_WITH_IMPLOT) 15 | #include "implot/implot.cpp" 16 | #include "implot/implot_items.cpp" 17 | #endif // LVK_WITH_IMPLOT 18 | 19 | #include 20 | 21 | static const char* codeVS = R"( 22 | layout (location = 0) out vec4 out_color; 23 | layout (location = 1) out vec2 out_uv; 24 | 25 | struct Vertex { 26 | float x, y; 27 | float u, v; 28 | uint rgba; 29 | }; 30 | 31 | layout(std430, buffer_reference) readonly buffer VertexBuffer { 32 | Vertex vertices[]; 33 | }; 34 | 35 | layout(push_constant) uniform PushConstants { 36 | vec4 LRTB; 37 | VertexBuffer vb; 38 | uint textureId; 39 | uint samplerId; 40 | } pc; 41 | 42 | void main() { 43 | float L = pc.LRTB.x; 44 | float R = pc.LRTB.y; 45 | float T = pc.LRTB.z; 46 | float B = pc.LRTB.w; 47 | mat4 proj = mat4( 48 | 2.0 / (R - L), 0.0, 0.0, 0.0, 49 | 0.0, 2.0 / (T - B), 0.0, 0.0, 50 | 0.0, 0.0, -1.0, 0.0, 51 | (R + L) / (L - R), (T + B) / (B - T), 0.0, 1.0); 52 | Vertex v = pc.vb.vertices[gl_VertexIndex]; 53 | out_color = unpackUnorm4x8(v.rgba); 54 | out_uv = vec2(v.u, v.v); 55 | gl_Position = proj * vec4(v.x, v.y, 0, 1); 56 | })"; 57 | 58 | static const char* codeFS = R"( 59 | layout (location = 0) in vec4 in_color; 60 | layout (location = 1) in vec2 in_uv; 61 | 62 | layout (location = 0) out vec4 out_color; 63 | 64 | layout (constant_id = 0) const bool kNonLinearColorSpace = false; 65 | 66 | layout(push_constant) uniform PushConstants { 67 | vec4 LRTB; 68 | vec2 vb; 69 | uint textureId; 70 | uint samplerId; 71 | } pc; 72 | 73 | void main() { 74 | vec4 c = in_color * texture(nonuniformEXT(sampler2D(kTextures2D[pc.textureId], kSamplers[pc.samplerId])), in_uv); 75 | // Render UI in linear color space to sRGB framebuffer. 76 | out_color = kNonLinearColorSpace ? vec4(pow(c.rgb, vec3(2.2)), c.a) : c; 77 | })"; 78 | 79 | namespace lvk { 80 | 81 | lvk::Holder ImGuiRenderer::createNewPipelineState(const lvk::Framebuffer& desc) { 82 | const uint32_t nonLinearColorSpace = ctx_.getSwapchainColorSpace() == ColorSpace_SRGB_NONLINEAR ? 1u : 0u; 83 | return ctx_.createRenderPipeline( 84 | { 85 | .smVert = vert_, 86 | .smFrag = frag_, 87 | .specInfo = {.entries = {{.constantId = 0, .size = sizeof(nonLinearColorSpace)}}, 88 | .data = &nonLinearColorSpace, 89 | .dataSize = sizeof(nonLinearColorSpace)}, 90 | .color = {{ 91 | .format = ctx_.getFormat(desc.color[0].texture), 92 | .blendEnabled = true, 93 | .srcRGBBlendFactor = lvk::BlendFactor_SrcAlpha, 94 | .dstRGBBlendFactor = lvk::BlendFactor_OneMinusSrcAlpha, 95 | }}, 96 | .depthFormat = desc.depthStencil.texture ? ctx_.getFormat(desc.depthStencil.texture) : lvk::Format_Invalid, 97 | .cullMode = lvk::CullMode_None, 98 | .debugName = "ImGuiRenderer: createNewPipelineState()", 99 | }, 100 | nullptr); 101 | } 102 | 103 | ImGuiRenderer::ImGuiRenderer(lvk::IContext& device, const char* defaultFontTTF, float fontSizePixels) : ctx_(device) { 104 | ImGui::CreateContext(); 105 | #if defined(LVK_WITH_IMPLOT) 106 | ImPlot::CreateContext(); 107 | #endif // LVK_WITH_IMPLOT 108 | 109 | ImGuiIO& io = ImGui::GetIO(); 110 | io.BackendRendererName = "imgui-lvk"; 111 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; 112 | 113 | updateFont(defaultFontTTF, fontSizePixels); 114 | 115 | vert_ = ctx_.createShaderModule({codeVS, Stage_Vert, "Shader Module: imgui (vert)"}); 116 | frag_ = ctx_.createShaderModule({codeFS, Stage_Frag, "Shader Module: imgui (frag)"}); 117 | samplerClamp_ = ctx_.createSampler({ 118 | .wrapU = lvk::SamplerWrap_Clamp, 119 | .wrapV = lvk::SamplerWrap_Clamp, 120 | .wrapW = lvk::SamplerWrap_Clamp, 121 | }); 122 | } 123 | 124 | ImGuiRenderer::~ImGuiRenderer() { 125 | ImGuiIO& io = ImGui::GetIO(); 126 | io.Fonts->TexID = 0; 127 | #if defined(LVK_WITH_IMPLOT) 128 | ImPlot::DestroyContext(); 129 | #endif // LVK_WITH_IMPLOT 130 | ImGui::DestroyContext(); 131 | } 132 | 133 | void ImGuiRenderer::updateFont(const char* defaultFontTTF, float fontSizePixels) { 134 | ImGuiIO& io = ImGui::GetIO(); 135 | 136 | ImFontConfig cfg = ImFontConfig(); 137 | cfg.FontDataOwnedByAtlas = false; 138 | cfg.RasterizerMultiply = 1.5f; 139 | cfg.SizePixels = ceilf(fontSizePixels); 140 | cfg.PixelSnapH = true; 141 | cfg.OversampleH = 4; 142 | cfg.OversampleV = 4; 143 | ImFont* font = nullptr; 144 | if (defaultFontTTF) { 145 | font = io.Fonts->AddFontFromFileTTF(defaultFontTTF, cfg.SizePixels, &cfg); 146 | } else { 147 | font = io.Fonts->AddFontDefault(&cfg); 148 | } 149 | 150 | io.Fonts->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; 151 | 152 | // init fonts 153 | unsigned char* pixels; 154 | int width, height; 155 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 156 | fontTexture_ = ctx_.createTexture({.type = lvk::TextureType_2D, 157 | .format = lvk::Format_RGBA_UN8, 158 | .dimensions = {(uint32_t)width, (uint32_t)height}, 159 | .usage = lvk::TextureUsageBits_Sampled, 160 | .data = pixels}, 161 | "ImGuiRenderer::fontTexture_"); 162 | io.Fonts->TexID = fontTexture_.index(); 163 | io.FontDefault = font; 164 | } 165 | 166 | void ImGuiRenderer::beginFrame(const lvk::Framebuffer& desc) { 167 | const lvk::Dimensions dim = ctx_.getDimensions(desc.color[0].texture); 168 | 169 | ImGuiIO& io = ImGui::GetIO(); 170 | io.DisplaySize = ImVec2(dim.width / displayScale_, dim.height / displayScale_); 171 | io.DisplayFramebufferScale = ImVec2(displayScale_, displayScale_); 172 | io.IniFilename = nullptr; 173 | 174 | if (pipeline_.empty()) { 175 | pipeline_ = createNewPipelineState(desc); 176 | } 177 | ImGui::NewFrame(); 178 | } 179 | 180 | void ImGuiRenderer::endFrame(lvk::ICommandBuffer& cmdBuffer) { 181 | static_assert(sizeof(ImDrawIdx) == 2); 182 | LVK_ASSERT_MSG(sizeof(ImDrawIdx) == 2, "The constants below may not work with the ImGui data."); 183 | 184 | ImGui::EndFrame(); 185 | ImGui::Render(); 186 | 187 | ImDrawData* dd = ImGui::GetDrawData(); 188 | 189 | const float fb_width = dd->DisplaySize.x * dd->FramebufferScale.x; 190 | const float fb_height = dd->DisplaySize.y * dd->FramebufferScale.y; 191 | if (fb_width <= 0 || fb_height <= 0 || dd->CmdListsCount == 0) { 192 | return; 193 | } 194 | 195 | cmdBuffer.cmdPushDebugGroupLabel("ImGui Rendering", 0xff00ff00); 196 | cmdBuffer.cmdBindDepthState({}); 197 | cmdBuffer.cmdBindViewport({ 198 | .x = 0.0f, 199 | .y = 0.0f, 200 | .width = fb_width, 201 | .height = fb_height, 202 | }); 203 | 204 | const float L = dd->DisplayPos.x; 205 | const float R = dd->DisplayPos.x + dd->DisplaySize.x; 206 | const float T = dd->DisplayPos.y; 207 | const float B = dd->DisplayPos.y + dd->DisplaySize.y; 208 | 209 | const ImVec2 clip_off = dd->DisplayPos; 210 | const ImVec2 clip_scale = dd->FramebufferScale; 211 | 212 | DrawableData& drawableData = drawables_[frameIndex_]; 213 | frameIndex_ = (frameIndex_ + 1) % LVK_ARRAY_NUM_ELEMENTS(drawables_); 214 | 215 | if (drawableData.numAllocatedIndices_ < dd->TotalIdxCount) { 216 | drawableData.ib_ = ctx_.createBuffer({ 217 | .usage = lvk::BufferUsageBits_Index, 218 | .storage = lvk::StorageType_HostVisible, 219 | .size = dd->TotalIdxCount * sizeof(ImDrawIdx), 220 | .debugName = "ImGui: drawableData.ib_", 221 | }); 222 | drawableData.numAllocatedIndices_ = dd->TotalIdxCount; 223 | } 224 | if (drawableData.numAllocatedVerteices_ < dd->TotalVtxCount) { 225 | drawableData.vb_ = ctx_.createBuffer({ 226 | .usage = lvk::BufferUsageBits_Storage, 227 | .storage = lvk::StorageType_HostVisible, 228 | .size = dd->TotalVtxCount * sizeof(ImDrawVert), 229 | .debugName = "ImGui: drawableData.vb_", 230 | }); 231 | drawableData.numAllocatedVerteices_ = dd->TotalVtxCount; 232 | } 233 | 234 | // upload vertex/index buffers 235 | { 236 | ImDrawVert* vtx = (ImDrawVert*)ctx_.getMappedPtr(drawableData.vb_); 237 | uint16_t* idx = (uint16_t*)ctx_.getMappedPtr(drawableData.ib_); 238 | for (int n = 0; n < dd->CmdListsCount; n++) { 239 | const ImDrawList* cmdList = dd->CmdLists[n]; 240 | memcpy(vtx, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert)); 241 | memcpy(idx, cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); 242 | vtx += cmdList->VtxBuffer.Size; 243 | idx += cmdList->IdxBuffer.Size; 244 | } 245 | ctx_.flushMappedMemory(drawableData.vb_, 0, dd->TotalVtxCount * sizeof(ImDrawVert)); 246 | ctx_.flushMappedMemory(drawableData.ib_, 0, dd->TotalIdxCount * sizeof(ImDrawIdx)); 247 | } 248 | 249 | uint32_t idxOffset = 0; 250 | uint32_t vtxOffset = 0; 251 | 252 | cmdBuffer.cmdBindIndexBuffer(drawableData.ib_, lvk::IndexFormat_UI16); 253 | cmdBuffer.cmdBindRenderPipeline(pipeline_); 254 | 255 | for (int n = 0; n < dd->CmdListsCount; n++) { 256 | const ImDrawList* cmdList = dd->CmdLists[n]; 257 | 258 | for (int cmd_i = 0; cmd_i < cmdList->CmdBuffer.Size; cmd_i++) { 259 | const ImDrawCmd cmd = cmdList->CmdBuffer[cmd_i]; 260 | LVK_ASSERT(cmd.UserCallback == nullptr); 261 | 262 | ImVec2 clipMin((cmd.ClipRect.x - clip_off.x) * clip_scale.x, (cmd.ClipRect.y - clip_off.y) * clip_scale.y); 263 | ImVec2 clipMax((cmd.ClipRect.z - clip_off.x) * clip_scale.x, (cmd.ClipRect.w - clip_off.y) * clip_scale.y); 264 | // clang-format off 265 | if (clipMin.x < 0.0f) clipMin.x = 0.0f; 266 | if (clipMin.y < 0.0f) clipMin.y = 0.0f; 267 | if (clipMax.x > fb_width ) clipMax.x = fb_width; 268 | if (clipMax.y > fb_height) clipMax.y = fb_height; 269 | if (clipMax.x <= clipMin.x || clipMax.y <= clipMin.y) 270 | continue; 271 | // clang-format on 272 | struct VulkanImguiBindData { 273 | float LRTB[4]; // ortho projection: left, right, top, bottom 274 | uint64_t vb = 0; 275 | uint32_t textureId = 0; 276 | uint32_t samplerId = 0; 277 | } bindData = { 278 | .LRTB = {L, R, T, B}, 279 | .vb = ctx_.gpuAddress(drawableData.vb_), 280 | .textureId = static_cast(cmd.TextureId), 281 | .samplerId = samplerClamp_.index(), 282 | }; 283 | cmdBuffer.cmdPushConstants(bindData); 284 | cmdBuffer.cmdBindScissorRect( 285 | {uint32_t(clipMin.x), uint32_t(clipMin.y), uint32_t(clipMax.x - clipMin.x), uint32_t(clipMax.y - clipMin.y)}); 286 | cmdBuffer.cmdDrawIndexed(cmd.ElemCount, 1u, idxOffset + cmd.IdxOffset, int32_t(vtxOffset + cmd.VtxOffset)); 287 | } 288 | idxOffset += cmdList->IdxBuffer.Size; 289 | vtxOffset += cmdList->VtxBuffer.Size; 290 | } 291 | 292 | cmdBuffer.cmdPopDebugGroupLabel(); 293 | } 294 | 295 | void ImGuiRenderer::setDisplayScale(float displayScale) { 296 | displayScale_ = displayScale; 297 | } 298 | 299 | } // namespace lvk 300 | -------------------------------------------------------------------------------- /lvk/HelpersImGui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #define IMGUI_DEFINE_MATH_OPERATORS 11 | 12 | #include 13 | #include 14 | 15 | namespace lvk { 16 | 17 | class ImGuiRenderer { 18 | public: 19 | explicit ImGuiRenderer(lvk::IContext& device, const char* defaultFontTTF = nullptr, float fontSizePixels = 24.0f); 20 | ~ImGuiRenderer(); 21 | 22 | void updateFont(const char* defaultFontTTF, float fontSizePixels); 23 | 24 | void beginFrame(const lvk::Framebuffer& desc); 25 | void endFrame(lvk::ICommandBuffer& cmdBuffer); 26 | 27 | void setDisplayScale(float displayScale); 28 | 29 | private: 30 | lvk::Holder createNewPipelineState(const lvk::Framebuffer& desc); 31 | 32 | private: 33 | lvk::IContext& ctx_; 34 | lvk::Holder vert_; 35 | lvk::Holder frag_; 36 | lvk::Holder pipeline_; 37 | lvk::Holder fontTexture_; 38 | lvk::Holder samplerClamp_; 39 | float displayScale_ = 1.0f; 40 | 41 | uint32_t frameIndex_ = 0; 42 | 43 | struct DrawableData { 44 | lvk::Holder vb_; 45 | lvk::Holder ib_; 46 | uint32_t numAllocatedIndices_ = 0; 47 | uint32_t numAllocatedVerteices_ = 0; 48 | }; 49 | 50 | DrawableData drawables_[3] = {}; 51 | }; 52 | 53 | } // namespace lvk 54 | -------------------------------------------------------------------------------- /lvk/LVK.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #if LVK_WITH_GLFW 9 | #define GLFW_INCLUDE_NONE 10 | #include 11 | 12 | // clang-format off 13 | #if __APPLE__ 14 | # define GLFW_EXPOSE_NATIVE_COCOA 15 | #else 16 | # error Unsupported OS 17 | #endif 18 | // clang-format on 19 | 20 | #include 21 | 22 | #import 23 | #import 24 | #import 25 | 26 | void* createCocoaWindowView(GLFWwindow* window) { 27 | NSWindow* nswindow = glfwGetCocoaWindow(window); 28 | CAMetalLayer* layer = [CAMetalLayer layer]; 29 | layer.device = MTLCreateSystemDefaultDevice(); 30 | layer.opaque = YES; 31 | layer.displaySyncEnabled = YES; 32 | NSScreen* screen = [NSScreen mainScreen]; 33 | CGFloat factor = [screen backingScaleFactor]; 34 | layer.contentsScale = factor; 35 | nswindow.contentView.layer = layer; 36 | nswindow.contentView.wantsLayer = YES; 37 | 38 | return nswindow.contentView; 39 | } 40 | #endif // LVK_WITH_GLFW 41 | -------------------------------------------------------------------------------- /lvk/Pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lvk/LVK.h" 8 | 9 | /// Pool<> is used only by the implementation 10 | namespace lvk { 11 | 12 | template 13 | class Pool { 14 | static constexpr uint32_t kListEndSentinel = 0xffffffff; 15 | struct PoolEntry { 16 | explicit PoolEntry(ImplObjectType& obj) : obj_(std::move(obj)) {} 17 | ImplObjectType obj_ = {}; 18 | uint32_t gen_ = 1; 19 | uint32_t nextFree_ = kListEndSentinel; 20 | }; 21 | uint32_t freeListHead_ = kListEndSentinel; 22 | uint32_t numObjects_ = 0; 23 | 24 | public: 25 | std::vector objects_; 26 | 27 | Handle create(ImplObjectType&& obj) { 28 | uint32_t idx = 0; 29 | if (freeListHead_ != kListEndSentinel) { 30 | idx = freeListHead_; 31 | freeListHead_ = objects_[idx].nextFree_; 32 | objects_[idx].obj_ = std::move(obj); 33 | } else { 34 | idx = (uint32_t)objects_.size(); 35 | objects_.emplace_back(obj); 36 | } 37 | numObjects_++; 38 | return Handle(idx, objects_[idx].gen_); 39 | } 40 | void destroy(Handle handle) { 41 | if (handle.empty()) 42 | return; 43 | assert(numObjects_ > 0); // double deletion 44 | const uint32_t index = handle.index(); 45 | assert(index < objects_.size()); 46 | assert(handle.gen() == objects_[index].gen_); // double deletion 47 | objects_[index].obj_ = ImplObjectType{}; 48 | objects_[index].gen_++; 49 | objects_[index].nextFree_ = freeListHead_; 50 | freeListHead_ = index; 51 | numObjects_--; 52 | } 53 | const ImplObjectType* get(Handle handle) const { 54 | if (handle.empty()) 55 | return nullptr; 56 | 57 | const uint32_t index = handle.index(); 58 | assert(index < objects_.size()); 59 | assert(handle.gen() == objects_[index].gen_); // accessing deleted object 60 | return &objects_[index].obj_; 61 | } 62 | ImplObjectType* get(Handle handle) { 63 | if (handle.empty()) 64 | return nullptr; 65 | 66 | const uint32_t index = handle.index(); 67 | assert(index < objects_.size()); 68 | assert(handle.gen() == objects_[index].gen_); // accessing deleted object 69 | return &objects_[index].obj_; 70 | } 71 | Handle getHandle(uint32_t index) const { 72 | assert(index < objects_.size()); 73 | if (index >= objects_.size()) 74 | return {}; 75 | 76 | return Handle(index, objects_[index].gen_); 77 | } 78 | Handle findObject(const ImplObjectType* obj) { 79 | if (!obj) 80 | return {}; 81 | 82 | for (size_t idx = 0; idx != objects_.size(); idx++) { 83 | if (objects_[idx].obj_ == *obj) { 84 | return Handle((uint32_t)idx, objects_[idx].gen_); 85 | } 86 | } 87 | 88 | return {}; 89 | } 90 | void clear() { 91 | objects_.clear(); 92 | freeListHead_ = kListEndSentinel; 93 | numObjects_ = 0; 94 | } 95 | uint32_t numObjects() const { 96 | return numObjects_; 97 | } 98 | }; 99 | 100 | } // namespace lvk 101 | -------------------------------------------------------------------------------- /lvk/vulkan/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # LightweightVK 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.19) 7 | 8 | project(LVKVulkan CXX C) 9 | 10 | file(GLOB SRC_FILES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.c) 11 | file(GLOB HEADER_FILES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) 12 | 13 | add_library(LVKVulkan ${SRC_FILES} ${HEADER_FILES}) 14 | 15 | lvk_setup_target(LVKVulkan) 16 | lvk_set_folder(LVKVulkan "LVK") 17 | 18 | lvk_setup_groups("${SRC_FILES}") 19 | lvk_setup_groups("${HEADER_FILES}") 20 | 21 | # glslang 22 | # cmake-format: off 23 | set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "") 24 | set(ENABLE_HLSL OFF CACHE BOOL "") 25 | set(ENABLE_CTEST OFF CACHE BOOL "") 26 | set(ENABLE_OPT OFF CACHE BOOL "") 27 | set(ENABLE_SPVREMAPPER OFF CACHE BOOL "") 28 | set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "") 29 | add_subdirectory(${LVK_ROOT_DIR}/third-party/deps/src/glslang "glslang") 30 | lvk_set_folder(GenericCodeGen "third-party/glslang") 31 | lvk_set_folder(glslang "third-party/glslang") 32 | lvk_set_folder(MachineIndependent "third-party/glslang") 33 | lvk_set_folder(OSDependent "third-party/glslang") 34 | lvk_set_folder(SPIRV "third-party/glslang") 35 | lvk_set_folder(glslang-default-resource-limits "third-party/glslang") 36 | # cmake-format: on 37 | 38 | # slang 39 | # cmake-format: off 40 | if(LVK_WITH_SLANG) 41 | set(SLANG_ENABLE_CUDA OFF CACHE BOOL "") 42 | set(SLANG_ENABLE_OPTIX OFF CACHE BOOL "") 43 | set(SLANG_ENABLE_NVAPI OFF CACHE BOOL "") 44 | set(SLANG_ENABLE_XLIB OFF CACHE BOOL "") 45 | set(SLANG_ENABLE_AFTERMATH OFF CACHE BOOL "") 46 | set(SLANG_ENABLE_DX_ON_VK OFF CACHE BOOL "") 47 | set(SLANG_ENABLE_GFX OFF CACHE BOOL "") 48 | set(SLANG_ENABLE_SLANGC OFF CACHE BOOL "") 49 | set(SLANG_ENABLE_SLANGRT ON CACHE BOOL "") 50 | set(SLANG_ENABLE_SLANG_GLSLANG OFF CACHE BOOL "") 51 | set(SLANG_ENABLE_TESTS OFF CACHE BOOL "") 52 | set(SLANG_ENABLE_EXAMPLES OFF CACHE BOOL "") 53 | set(SLANG_ENABLE_REPLAYER OFF CACHE BOOL "") 54 | set(SLANG_ENABLE_PREBUILT_BINARIES OFF CACHE BOOL "") 55 | set(SLANG_EMBED_CORE_MODULE_SOURCE OFF CACHE BOOL "") 56 | set(SLANG_ENABLE_DXIL OFF CACHE BOOL "") 57 | add_subdirectory(${LVK_ROOT_DIR}/third-party/deps/src/slang "slang") 58 | lvk_set_folder(compiler-core "third-party/slang") 59 | lvk_set_folder(core "third-party/slang") 60 | lvk_set_folder(slang "third-party/slang") 61 | lvk_set_folder(slangd "third-party/slang") 62 | lvk_set_folder(slang-rt "third-party/slang") 63 | lvk_set_folder(slang-rhi "third-party/slang") 64 | lvk_set_folder(slang-rhi-copy-files "third-party/slang") 65 | endif() 66 | # cmake-format: on 67 | 68 | # SPIRV-Reflect 69 | set(SPIRV_REFLECT_EXECUTABLE OFF CACHE BOOL "") 70 | set(SPIRV_REFLECT_STATIC_LIB ON CACHE BOOL "") 71 | add_subdirectory(${LVK_ROOT_DIR}/third-party/deps/src/SPIRV-Reflect "SPIRV-Reflect") 72 | lvk_set_folder(spirv-reflect-static "third-party") 73 | 74 | if(NOT LVK_USE_CUSTOM_MOLTENVK) 75 | find_package(Vulkan REQUIRED) 76 | endif() 77 | 78 | if(LVK_WITH_VULKAN_PORTABILITY) 79 | add_definitions("-DLVK_WITH_VULKAN_PORTABILITY") 80 | endif() 81 | 82 | target_link_libraries(LVKVulkan INTERFACE LVKLibrary) 83 | target_link_libraries(LVKVulkan PRIVATE glslang SPIRV glslang-default-resource-limits) 84 | target_link_libraries(LVKVulkan PRIVATE spirv-reflect-static) 85 | if(LVK_WITH_SLANG) 86 | target_link_libraries(LVKVulkan PRIVATE slang) 87 | target_link_libraries(LVKVulkan PRIVATE slang-rt) 88 | endif() 89 | 90 | if(LVK_USE_CUSTOM_MOLTENVK) 91 | target_include_directories(LVKVulkan PUBLIC "${LVK_CUSTOM_MOLTENVK_PATH}/include") 92 | target_link_libraries(LVKVulkan PUBLIC "${LVK_CUSTOM_MOLTENVK_PATH}/dylib/macOS/libMoltenVK.dylib") 93 | else() 94 | target_link_libraries(LVKVulkan PUBLIC Vulkan::Vulkan) 95 | endif() 96 | 97 | target_include_directories(LVKVulkan PUBLIC "${LVK_ROOT_DIR}/third-party/deps/src/") 98 | target_include_directories(LVKVulkan PUBLIC "${LVK_ROOT_DIR}/third-party/deps/src/volk") 99 | target_include_directories(LVKVulkan PUBLIC "${LVK_ROOT_DIR}/third-party/deps/src/vma/include") 100 | 101 | if(WIN32) 102 | add_definitions("-DVK_USE_PLATFORM_WIN32_KHR=1") 103 | add_definitions("-DNOMINMAX") 104 | elseif(ANDROID) 105 | add_definitions("-DVK_USE_PLATFORM_ANDROID_KHR=1") 106 | elseif(UNIX AND NOT APPLE) 107 | if(WAYLAND_FOUND) 108 | add_definitions("-DVK_USE_PLATFORM_WAYLAND_KHR=1") 109 | else() 110 | add_definitions("-DVK_USE_PLATFORM_XLIB_KHR=1") 111 | endif() 112 | elseif(APPLE) 113 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 114 | add_definitions("-DVK_USE_PLATFORM_MACOS_MVK=1") 115 | endif() 116 | endif() 117 | 118 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 119 | target_compile_options(LVKVulkan PRIVATE "-Wno-nullability-completeness") 120 | endif() 121 | -------------------------------------------------------------------------------- /lvk/vulkan/VulkanUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #define VMA_VULKAN_VERSION 1003000 11 | #define VMA_STATIC_VULKAN_FUNCTIONS 0 12 | #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 13 | 14 | // set to 1 to see very verbose debug console logs with Vulkan commands 15 | #define LVK_VULKAN_PRINT_COMMANDS 0 16 | 17 | #if !defined(VK_NO_PROTOTYPES) 18 | #define VK_NO_PROTOTYPES 1 19 | #endif // !defined(VK_NO_PROTOTYPES) 20 | 21 | // enable to use VulkanMemoryAllocator (VMA) 22 | #define LVK_VULKAN_USE_VMA 1 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #define VK_ASSERT(func) \ 33 | { \ 34 | const VkResult vk_assert_result = func; \ 35 | if (vk_assert_result != VK_SUCCESS) { \ 36 | LLOGW("Vulkan API call failed: %s:%i\n %s\n %s\n", \ 37 | __FILE__, \ 38 | __LINE__, \ 39 | #func, \ 40 | lvk::getVulkanResultString(vk_assert_result)); \ 41 | assert(false); \ 42 | } \ 43 | } 44 | 45 | #define VK_ASSERT_RETURN(func) \ 46 | { \ 47 | const VkResult vk_assert_result = func; \ 48 | if (vk_assert_result != VK_SUCCESS) { \ 49 | LLOGW("Vulkan API call failed: %s:%i\n %s\n %s\n", \ 50 | __FILE__, \ 51 | __LINE__, \ 52 | #func, \ 53 | lvk::getVulkanResultString(vk_assert_result)); \ 54 | assert(false); \ 55 | return getResultFromVkResult(vk_assert_result); \ 56 | } \ 57 | } 58 | 59 | typedef struct glslang_resource_s glslang_resource_t; 60 | 61 | struct StageAccess { 62 | VkPipelineStageFlags2 stage; 63 | VkAccessFlags2 access; 64 | }; 65 | 66 | namespace lvk { 67 | 68 | VkSemaphore createSemaphore(VkDevice device, const char* debugName); 69 | VkSemaphore createSemaphoreTimeline(VkDevice device, uint64_t initialValue, const char* debugName); 70 | VkFence createFence(VkDevice device, const char* debugName); 71 | VmaAllocator createVmaAllocator(VkPhysicalDevice physDev, VkDevice device, VkInstance instance, uint32_t apiVersion); 72 | uint32_t findQueueFamilyIndex(VkPhysicalDevice physDev, VkQueueFlags flags); 73 | VkResult setDebugObjectName(VkDevice device, VkObjectType type, uint64_t handle, const char* name); 74 | VkResult allocateMemory2(VkPhysicalDevice physDev, 75 | VkDevice device, 76 | const VkMemoryRequirements2* memRequirements, 77 | VkMemoryPropertyFlags props, 78 | VkDeviceMemory* outMemory); 79 | 80 | glslang_resource_t getGlslangResource(const VkPhysicalDeviceLimits& limits); 81 | Result compileShader(VkShaderStageFlagBits stage, 82 | const char* code, 83 | std::vector* outSPIRV, 84 | const glslang_resource_t* glslLangResource = nullptr); 85 | 86 | VkSamplerCreateInfo samplerStateDescToVkSamplerCreateInfo(const lvk::SamplerStateDesc& desc, const VkPhysicalDeviceLimits& limits); 87 | VkDescriptorSetLayoutBinding getDSLBinding(uint32_t binding, 88 | VkDescriptorType descriptorType, 89 | uint32_t descriptorCount, 90 | VkShaderStageFlags stageFlags, 91 | const VkSampler* immutableSamplers = nullptr); 92 | VkSpecializationInfo getPipelineShaderStageSpecializationInfo(lvk::SpecializationConstantDesc desc, VkSpecializationMapEntry* outEntries); 93 | VkPipelineShaderStageCreateInfo getPipelineShaderStageCreateInfo(VkShaderStageFlagBits stage, 94 | VkShaderModule shaderModule, 95 | const char* entryPoint, 96 | const VkSpecializationInfo* specializationInfo); 97 | VkBindImageMemoryInfo getBindImageMemoryInfo(const VkBindImagePlaneMemoryInfo* next, VkImage image, VkDeviceMemory memory); 98 | 99 | StageAccess getPipelineStageAccess(VkImageLayout state); 100 | 101 | void imageMemoryBarrier2(VkCommandBuffer buffer, 102 | VkImage image, 103 | StageAccess src, 104 | StageAccess dst, 105 | VkImageLayout oldImageLayout, 106 | VkImageLayout newImageLayout, 107 | VkImageSubresourceRange subresourceRange); 108 | 109 | VkSampleCountFlagBits getVulkanSampleCountFlags(uint32_t numSamples, VkSampleCountFlags maxSamplesMask); 110 | 111 | void setResultFrom(Result* outResult, VkResult result); 112 | Result getResultFromVkResult(VkResult result); 113 | const char* getVulkanResultString(VkResult result); 114 | const char* getVkDeviceFaultAddressTypeString(VkDeviceFaultAddressTypeEXT type); 115 | uint32_t getBytesPerPixel(VkFormat format); 116 | uint32_t getNumImagePlanes(VkFormat format); 117 | lvk::Format vkFormatToFormat(VkFormat format); 118 | lvk::ColorSpace vkColorSpaceToColorSpace(VkColorSpaceKHR colorSpace); 119 | VkFormat formatToVkFormat(lvk::Format format); 120 | VkCompareOp compareOpToVkCompareOp(lvk::CompareOp func); 121 | VkExtent2D getImagePlaneExtent(VkExtent2D plane0, lvk::Format format, uint32_t plane); 122 | 123 | // raw Vulkan helpers: use this if you want to interop LightweightVK API with your own raw Vulkan API calls 124 | VkDevice getVkDevice(const IContext* ctx); 125 | VkPhysicalDevice getVkPhysicalDevice(const IContext* ctx); 126 | VkCommandBuffer getVkCommandBuffer(const ICommandBuffer& buffer); 127 | VkBuffer getVkBuffer(const IContext* ctx, BufferHandle buffer); 128 | VkImage getVkImage(const IContext* ctx, TextureHandle texture); 129 | VkImageView getVkImageView(const IContext* ctx, TextureHandle texture); 130 | VkShaderModule getVkShaderModule(const IContext* ctx, ShaderModuleHandle shader); 131 | VkDeviceAddress getVkAccelerationStructureDeviceAddress(const IContext* ctx, AccelStructHandle accelStruct); 132 | VkAccelerationStructureKHR getVkAccelerationStructure(const IContext* ctx, AccelStructHandle accelStruct); 133 | VkBuffer getVkBuffer(const IContext* ctx, AccelStructHandle accelStruct); 134 | VkPipeline getVkPipeline(const IContext* ctx, RayTracingPipelineHandle pipeline); 135 | VkPipelineLayout getVkPipelineLayout(const IContext* ctx, RayTracingPipelineHandle pipeline); 136 | 137 | // properties/limits 138 | const VkPhysicalDeviceProperties2& getVkPhysicalDeviceProperties2(const IContext* ctx); 139 | const VkPhysicalDeviceVulkan11Properties& getVkPhysicalDeviceVulkan11Properties(const IContext* ctx); 140 | const VkPhysicalDeviceVulkan12Properties& getVkPhysicalDeviceVulkan12Properties(const IContext* ctx); 141 | const VkPhysicalDeviceVulkan13Properties& getVkPhysicalDeviceVulkan13Properties(const IContext* ctx); 142 | 143 | } // namespace lvk 144 | -------------------------------------------------------------------------------- /samples/001_HelloTriangle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | const char* codeVS = R"( 11 | #version 460 12 | layout (location=0) out vec3 color; 13 | const vec2 pos[3] = vec2[3]( 14 | vec2(-0.6, -0.4), 15 | vec2( 0.6, -0.4), 16 | vec2( 0.0, 0.6) 17 | ); 18 | const vec3 col[3] = vec3[3]( 19 | vec3(1.0, 0.0, 0.0), 20 | vec3(0.0, 1.0, 0.0), 21 | vec3(0.0, 0.0, 1.0) 22 | ); 23 | void main() { 24 | gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); 25 | color = col[gl_VertexIndex]; 26 | } 27 | )"; 28 | 29 | const char* codeFS = R"( 30 | #version 460 31 | layout (location=0) in vec3 color; 32 | layout (location=0) out vec4 out_FragColor; 33 | 34 | void main() { 35 | out_FragColor = vec4(color, 1.0); 36 | }; 37 | )"; 38 | 39 | VULKAN_APP_MAIN { 40 | const VulkanAppConfig cfg{ 41 | .width = 800, 42 | .height = 600, 43 | .resizable = true, 44 | }; 45 | VULKAN_APP_DECLARE(app, cfg); 46 | 47 | lvk::IContext* ctx = app.ctx_.get(); 48 | 49 | { 50 | lvk::Holder vert_ = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 51 | lvk::Holder frag_ = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 52 | 53 | lvk::Holder renderPipelineState_Triangle_ = ctx->createRenderPipeline( 54 | { 55 | .smVert = vert_, 56 | .smFrag = frag_, 57 | .color = {{.format = ctx->getSwapchainFormat()}}, 58 | }, 59 | nullptr); 60 | 61 | LVK_ASSERT(renderPipelineState_Triangle_.valid()); 62 | 63 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 64 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 65 | 66 | // this will clear the framebuffer 67 | buffer.cmdBeginRendering({.color = {{.loadOp = lvk::LoadOp_Clear, .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}}}}, 68 | {.color = {{.texture = ctx->getCurrentSwapchainTexture()}}}); 69 | buffer.cmdBindRenderPipeline(renderPipelineState_Triangle_); 70 | buffer.cmdPushDebugGroupLabel("Render Triangle", 0xff0000ff); 71 | buffer.cmdDraw(3); 72 | buffer.cmdPopDebugGroupLabel(); 73 | buffer.cmdEndRendering(); 74 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 75 | }); 76 | } 77 | 78 | VULKAN_APP_EXIT(); 79 | } 80 | -------------------------------------------------------------------------------- /samples/001_HelloTriangle_Slang.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | const char* codeSlang = R"( 24 | static const float2 pos[3] = float2[3]( 25 | float2(-0.6, -0.4), 26 | float2( 0.6, -0.4), 27 | float2( 0.0, 0.6) 28 | ); 29 | static const float3 col[3] = float3[3]( 30 | float3(1.0, 0.0, 0.0), 31 | float3(0.0, 1.0, 0.0), 32 | float3(0.0, 0.0, 1.0) 33 | ); 34 | 35 | struct OutVertex { 36 | float3 color; 37 | }; 38 | 39 | struct Fragment { 40 | float4 color; 41 | }; 42 | 43 | struct VertexStageOutput { 44 | OutVertex vertex : OutVertex; 45 | float4 sv_position : SV_Position; 46 | }; 47 | 48 | [shader("vertex")] 49 | VertexStageOutput vertexMain(uint vertexID : SV_VertexID) { 50 | VertexStageOutput output; 51 | 52 | output.vertex.color = col[vertexID]; 53 | output.sv_position = float4(pos[vertexID], 0.0, 1.0); 54 | 55 | return output; 56 | } 57 | 58 | [shader("fragment")] 59 | float4 fragmentMain(OutVertex vertex : OutVertex) : SV_Target { 60 | return float4(vertex.color, 1.0); 61 | } 62 | )"; 63 | 64 | int width_ = 800; 65 | int height_ = 600; 66 | FramesPerSecondCounter fps_; 67 | 68 | lvk::Holder renderPipelineState_Triangle_; 69 | std::unique_ptr ctx_; 70 | lvk::Holder vert_; 71 | lvk::Holder frag_; 72 | 73 | std::vector compileSlangToSPIRV(const char* code, lvk::ShaderStage stage) { 74 | using namespace Slang; 75 | 76 | ComPtr slangGlobalSession; 77 | if (SLANG_FAILED(slang::createGlobalSession(slangGlobalSession.writeRef()))) { 78 | return {}; 79 | } 80 | 81 | const slang::TargetDesc targetDesc = { 82 | .format = SLANG_SPIRV, 83 | .profile = slangGlobalSession->findProfile("spirv_1_6"), 84 | .flags = SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY, 85 | }; 86 | 87 | const slang::SessionDesc sessionDesc = { 88 | .targets = &targetDesc, 89 | .targetCount = 1, 90 | }; 91 | 92 | ComPtr session; 93 | if (SLANG_FAILED(slangGlobalSession->createSession(sessionDesc, session.writeRef()))) { 94 | return {}; 95 | } 96 | 97 | slang::IModule* slangModule = nullptr; 98 | { 99 | ComPtr diagnosticBlob; 100 | slangModule = session->loadModuleFromSourceString("", "", code, diagnosticBlob.writeRef()); 101 | if (diagnosticBlob) { 102 | LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); 103 | } 104 | if (!slangModule) { 105 | return {}; 106 | } 107 | } 108 | 109 | ComPtr entryPointVert; 110 | ComPtr entryPointFrag; 111 | slangModule->findEntryPointByName("vertexMain", entryPointVert.writeRef()); 112 | slangModule->findEntryPointByName("fragmentMain", entryPointFrag.writeRef()); 113 | 114 | Slang::List componentTypes; 115 | componentTypes.add(slangModule); 116 | int entryPointCount = 0; 117 | int vertexEntryPointIndex = entryPointCount++; 118 | componentTypes.add(entryPointVert); 119 | int fragmentEntryPointIndex = entryPointCount++; 120 | componentTypes.add(entryPointFrag); 121 | 122 | ComPtr composedProgram; 123 | { 124 | ComPtr diagnosticBlob; 125 | SlangResult result = session->createCompositeComponentType( 126 | componentTypes.getBuffer(), componentTypes.getCount(), composedProgram.writeRef(), diagnosticBlob.writeRef()); 127 | if (diagnosticBlob) { 128 | LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); 129 | } 130 | if (SLANG_FAILED(result)) { 131 | return {}; 132 | } 133 | } 134 | 135 | ComPtr spirvCode; 136 | { 137 | ComPtr diagnosticBlob; 138 | const int entryPoint = stage == lvk::Stage_Vert ? vertexEntryPointIndex : fragmentEntryPointIndex; 139 | SlangResult result = composedProgram->getEntryPointCode(entryPoint, 0, spirvCode.writeRef(), diagnosticBlob.writeRef()); 140 | if (diagnosticBlob) { 141 | LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); 142 | } 143 | if (SLANG_FAILED(result)) { 144 | return {}; 145 | } 146 | } 147 | 148 | const uint8_t* ptr = reinterpret_cast(spirvCode->getBufferPointer()); 149 | 150 | return std::vector(ptr, ptr + spirvCode->getBufferSize()); 151 | } 152 | 153 | lvk::Holder slangCreateShaderModule(const char* code, 154 | lvk::ShaderStage stage, 155 | const char* debugName, 156 | const bool dumpSPIRV = false) { 157 | const std::vector spirv = compileSlangToSPIRV(code, stage); 158 | 159 | if (dumpSPIRV) { 160 | std::ofstream fout("dump." + std::to_string(stage), std::ios::out | std::ios::binary); 161 | fout.write(reinterpret_cast(spirv.data()), spirv.size()); 162 | } 163 | 164 | return ctx_->createShaderModule({spirv.data(), spirv.size(), stage, debugName}); 165 | } 166 | 167 | void init() { 168 | vert_ = slangCreateShaderModule(codeSlang, lvk::Stage_Vert, "Shader Module: main (vert)"); 169 | frag_ = slangCreateShaderModule(codeSlang, lvk::Stage_Frag, "Shader Module: main (frag)"); 170 | 171 | renderPipelineState_Triangle_ = ctx_->createRenderPipeline( 172 | { 173 | .smVert = vert_, 174 | .smFrag = frag_, 175 | .color = {{.format = ctx_->getSwapchainFormat()}}, 176 | }, 177 | nullptr); 178 | 179 | LVK_ASSERT(renderPipelineState_Triangle_.valid()); 180 | } 181 | 182 | void destroy() { 183 | vert_ = nullptr; 184 | frag_ = nullptr; 185 | renderPipelineState_Triangle_ = nullptr; 186 | ctx_ = nullptr; 187 | } 188 | 189 | void resize() { 190 | if (!width_ || !height_) { 191 | return; 192 | } 193 | ctx_->recreateSwapchain(width_, height_); 194 | } 195 | 196 | void render() { 197 | if (!width_ || !height_) { 198 | return; 199 | } 200 | 201 | lvk::ICommandBuffer& buffer = ctx_->acquireCommandBuffer(); 202 | 203 | // This will clear the framebuffer 204 | buffer.cmdBeginRendering( 205 | {.color = {{.loadOp = lvk::LoadOp_Clear, .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}}}}, 206 | {.color = {{.texture = ctx_->getCurrentSwapchainTexture()}}}); 207 | buffer.cmdBindRenderPipeline(renderPipelineState_Triangle_); 208 | buffer.cmdPushDebugGroupLabel("Render Triangle", 0xff0000ff); 209 | buffer.cmdDraw(3); 210 | buffer.cmdPopDebugGroupLabel(); 211 | buffer.cmdEndRendering(); 212 | ctx_->submit(buffer, ctx_->getCurrentSwapchainTexture()); 213 | } 214 | 215 | int main(int argc, char* argv[]) { 216 | minilog::initialize(nullptr, {.threadNames = false}); 217 | 218 | GLFWwindow* window = lvk::initWindow("Vulkan Hello Triangle", width_, height_, true); 219 | 220 | ctx_ = lvk::createVulkanContextWithSwapchain(window, width_, height_, {}); 221 | if (!ctx_) { 222 | return 1; 223 | } 224 | init(); 225 | 226 | glfwSetFramebufferSizeCallback(window, [](GLFWwindow*, int width, int height) { 227 | width_ = width; 228 | height_ = height; 229 | resize(); 230 | }); 231 | 232 | double prevTime = glfwGetTime(); 233 | 234 | // main loop 235 | while (!glfwWindowShouldClose(window)) { 236 | const double newTime = glfwGetTime(); 237 | fps_.tick(newTime - prevTime); 238 | prevTime = newTime; 239 | render(); 240 | glfwPollEvents(); 241 | } 242 | 243 | // destroy all the Vulkan stuff before closing the window 244 | destroy(); 245 | 246 | glfwDestroyWindow(window); 247 | glfwTerminate(); 248 | 249 | return 0; 250 | } 251 | -------------------------------------------------------------------------------- /samples/002_RenderToCubeMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | const char* codeTriangleVS = R"( 11 | #version 460 12 | layout (location=0) out vec3 color; 13 | const vec2 pos[3] = vec2[3]( 14 | vec2(-0.6, -0.6), 15 | vec2( 0.6, -0.6), 16 | vec2( 0.0, 0.6) 17 | ); 18 | const vec3 col[6] = vec3[6]( 19 | vec3(1.0, 0.0, 0.0), 20 | vec3(0.0, 1.0, 0.0), 21 | vec3(0.0, 0.0, 1.0), 22 | vec3(1.0, 0.0, 1.0), 23 | vec3(1.0, 1.0, 0.0), 24 | vec3(0.0, 1.0, 1.0) 25 | ); 26 | layout(push_constant) uniform constants { 27 | uint face; 28 | float time; 29 | } pc; 30 | 31 | void main() { 32 | gl_Position = vec4(pos[gl_VertexIndex] * (1.5 + sin(pc.time)) * 0.5, 0.0, 1.0); 33 | color = col[pc.face]; 34 | } 35 | )"; 36 | 37 | const char* codeTriangleFS = R"( 38 | #version 460 39 | layout (location=0) in vec3 color; 40 | layout (location=0) out vec4 out_FragColor; 41 | 42 | void main() { 43 | out_FragColor = vec4(color, 1.0); 44 | }; 45 | )"; 46 | 47 | const char* codeVS = R"( 48 | layout (location=0) out vec3 dir; 49 | layout (location=1) out flat uint textureId; 50 | 51 | const vec3 vertices[8] = vec3[8]( 52 | vec3(-1.0,-1.0, 1.0), vec3( 1.0,-1.0, 1.0), vec3( 1.0, 1.0, 1.0), vec3(-1.0, 1.0, 1.0), 53 | vec3(-1.0,-1.0,-1.0), vec3( 1.0,-1.0,-1.0), vec3( 1.0, 1.0,-1.0), vec3(-1.0, 1.0,-1.0) 54 | ); 55 | 56 | layout(push_constant) uniform constants { 57 | mat4 mvp; 58 | uint texture0; 59 | } pc; 60 | 61 | void main() { 62 | vec3 v = vertices[gl_VertexIndex]; 63 | gl_Position = pc.mvp * vec4(v, 1.0); 64 | dir = v; 65 | textureId = pc.texture0; 66 | } 67 | )"; 68 | 69 | const char* codeFS = R"( 70 | layout (location=0) in vec3 dir; 71 | layout (location=1) in flat uint textureId; 72 | layout (location=0) out vec4 out_FragColor; 73 | 74 | void main() { 75 | out_FragColor = textureBindlessCube(textureId, 0, normalize(dir)); 76 | }; 77 | )"; 78 | 79 | VULKAN_APP_MAIN { 80 | const VulkanAppConfig cfg{ 81 | .width = 0, 82 | .height = 0, 83 | }; 84 | VULKAN_APP_DECLARE(app, cfg); 85 | 86 | lvk::IContext* ctx = app.ctx_.get(); 87 | 88 | { 89 | const uint16_t indexData[36] = {0, 1, 2, 2, 3, 0, 1, 5, 6, 6, 2, 1, 7, 6, 5, 5, 4, 7, 90 | 4, 0, 3, 3, 7, 4, 4, 5, 1, 1, 0, 4, 3, 2, 6, 6, 7, 3}; 91 | 92 | lvk::Holder ib0_ = ctx->createBuffer({ 93 | .usage = lvk::BufferUsageBits_Index, 94 | .storage = lvk::StorageType_Device, 95 | .size = sizeof(indexData), 96 | .data = indexData, 97 | .debugName = "Buffer: index", 98 | }); 99 | 100 | lvk::Holder texture_ = ctx->createTexture({ 101 | .type = lvk::TextureType_Cube, 102 | .format = lvk::Format_BGRA_UN8, 103 | .dimensions = {512, 512}, 104 | .usage = lvk::TextureUsageBits_Sampled | lvk::TextureUsageBits_Attachment, 105 | .debugName = "CubeMap", 106 | }); 107 | 108 | lvk::Holder vert_ = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 109 | lvk::Holder frag_ = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 110 | lvk::Holder vertTriangle_ = 111 | ctx->createShaderModule({codeTriangleVS, lvk::Stage_Vert, "Shader Module: triangle (vert)"}); 112 | lvk::Holder fragTriangle_ = 113 | ctx->createShaderModule({codeTriangleFS, lvk::Stage_Frag, "Shader Module: triangle (frag)"}); 114 | 115 | lvk::Holder renderPipelineState_Mesh_ = ctx->createRenderPipeline({ 116 | .smVert = vert_, 117 | .smFrag = frag_, 118 | .color = {{.format = ctx->getSwapchainFormat()}}, 119 | .cullMode = lvk::CullMode_Back, 120 | .frontFaceWinding = lvk::WindingMode_CW, 121 | .debugName = "Pipeline: mesh", 122 | }); 123 | lvk::Holder renderPipelineState_Triangle_ = ctx->createRenderPipeline({ 124 | .smVert = vertTriangle_, 125 | .smFrag = fragTriangle_, 126 | .color = {{.format = ctx->getFormat(texture_)}}, 127 | .debugName = "Pipeline: triangle", 128 | }); 129 | 130 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 131 | LVK_PROFILER_FUNCTION(); 132 | 133 | const float fov = float(45.0f * (M_PI / 180.0f)); 134 | const mat4 proj = glm::perspectiveLH(fov, aspectRatio, 0.1f, 500.0f); 135 | const mat4 view = glm::translate(mat4(1.0f), vec3(0.0f, 0.0f, 5.0f)); 136 | const mat4 model = glm::rotate(mat4(1.0f), (float)glfwGetTime(), glm::normalize(vec3(1.0f, 1.0f, 1.0f))); 137 | 138 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 139 | 140 | buffer.cmdPushDebugGroupLabel("Render to Cube Map", 0xff0000ff); 141 | for (uint8_t face = 0; face != 6; face++) { 142 | const lvk::ClearColorValue colors[] = {{0.3f, 0.1f, 0.1f, 1.0f}, 143 | {0.1f, 0.3f, 0.1f, 1.0f}, 144 | {0.1f, 0.1f, 0.3f, 1.0f}, 145 | {0.3f, 0.1f, 0.3f, 1.0f}, 146 | {0.3f, 0.3f, 0.1f, 1.0f}, 147 | {0.1f, 0.3f, 0.3f, 1.0f}}; 148 | buffer.cmdBeginRendering({.color = {{ 149 | .loadOp = lvk::LoadOp_Clear, 150 | .storeOp = lvk::StoreOp_Store, 151 | .layer = face, 152 | .clearColor = colors[face], 153 | }}}, 154 | {.color = {{.texture = texture_}}}); 155 | buffer.cmdBindRenderPipeline(renderPipelineState_Triangle_); 156 | const struct { 157 | uint32_t face; 158 | float time; 159 | } pc = { 160 | .face = face, 161 | .time = 10.0f * (float)glfwGetTime(), 162 | }; 163 | buffer.cmdPushConstants(pc); 164 | buffer.cmdDraw(3); 165 | buffer.cmdEndRendering(); 166 | } 167 | buffer.cmdPopDebugGroupLabel(); 168 | 169 | buffer.cmdBeginRendering({.color = {{ 170 | .loadOp = lvk::LoadOp_Clear, 171 | .storeOp = lvk::StoreOp_Store, 172 | .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}, 173 | }}}, 174 | {.color = {{.texture = ctx->getCurrentSwapchainTexture()}}}, 175 | {.textures = {lvk::TextureHandle(texture_)}}); 176 | { 177 | buffer.cmdBindRenderPipeline(renderPipelineState_Mesh_); 178 | buffer.cmdBindViewport({0.0f, 0.0f, (float)width, (float)height, 0.0f, +1.0f}); 179 | buffer.cmdBindScissorRect({0, 0, (uint32_t)width, (uint32_t)height}); 180 | buffer.cmdPushDebugGroupLabel("Render Mesh", 0xff0000ff); 181 | buffer.cmdBindDepthState({}); 182 | buffer.cmdBindIndexBuffer(ib0_, lvk::IndexFormat_UI16); 183 | struct { 184 | mat4 mvp; 185 | uint32_t texture; 186 | } bindings = { 187 | .mvp = proj * view * model, 188 | .texture = texture_.index(), 189 | }; 190 | buffer.cmdPushConstants(bindings); 191 | buffer.cmdDrawIndexed(3 * 6 * 2); 192 | buffer.cmdPopDebugGroupLabel(); 193 | } 194 | buffer.cmdEndRendering(); 195 | 196 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 197 | }); 198 | } 199 | 200 | VULKAN_APP_EXIT(); 201 | } 202 | -------------------------------------------------------------------------------- /samples/003_RenderToCubeMapSinglePass.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | #include 11 | 12 | const char* codeTriangleVS = R"( 13 | #version 460 14 | const vec2 pos[3] = vec2[3]( 15 | vec2(-0.6, -0.6), 16 | vec2( 0.6, -0.6), 17 | vec2( 0.0, 0.6) 18 | ); 19 | layout(push_constant) uniform constants { 20 | float time; 21 | } pc; 22 | 23 | void main() { 24 | gl_Position = vec4(pos[gl_VertexIndex] * (1.5 + sin(pc.time)) * 0.5, 0.0, 1.0); 25 | } 26 | )"; 27 | 28 | const char* codeTriangleFS = R"( 29 | #version 460 30 | layout (location=0) out vec4 out_FragColor0; 31 | layout (location=1) out vec4 out_FragColor1; 32 | layout (location=2) out vec4 out_FragColor2; 33 | layout (location=3) out vec4 out_FragColor3; 34 | layout (location=4) out vec4 out_FragColor4; 35 | layout (location=5) out vec4 out_FragColor5; 36 | 37 | void main() { 38 | out_FragColor0 = vec4(1.0, 0.0, 0.0, 1.0); 39 | out_FragColor1 = vec4(0.0, 1.0, 0.0, 1.0); 40 | out_FragColor2 = vec4(0.0, 0.0, 1.0, 1.0); 41 | out_FragColor3 = vec4(1.0, 0.0, 1.0, 1.0); 42 | out_FragColor4 = vec4(1.0, 1.0, 0.0, 1.0); 43 | out_FragColor5 = vec4(0.0, 1.0, 1.0, 1.0); 44 | }; 45 | )"; 46 | 47 | const char* codeVS = R"( 48 | layout (location=0) out vec3 dir; 49 | layout (location=1) out flat uint textureId; 50 | 51 | const vec3 vertices[8] = vec3[8]( 52 | vec3(-1.0,-1.0, 1.0), vec3( 1.0,-1.0, 1.0), vec3( 1.0, 1.0, 1.0), vec3(-1.0, 1.0, 1.0), 53 | vec3(-1.0,-1.0,-1.0), vec3( 1.0,-1.0,-1.0), vec3( 1.0, 1.0,-1.0), vec3(-1.0, 1.0,-1.0) 54 | ); 55 | 56 | layout(push_constant) uniform constants { 57 | mat4 mvp; 58 | uint texture0; 59 | } pc; 60 | 61 | void main() { 62 | vec3 v = vertices[gl_VertexIndex]; 63 | gl_Position = pc.mvp * vec4(v, 1.0); 64 | dir = v; 65 | textureId = pc.texture0; 66 | } 67 | )"; 68 | 69 | const char* codeFS = R"( 70 | layout (location=0) in vec3 dir; 71 | layout (location=1) in flat uint textureId; 72 | layout (location=0) out vec4 out_FragColor; 73 | 74 | void main() { 75 | out_FragColor = textureBindlessCube(textureId, 0, normalize(dir)); 76 | }; 77 | )"; 78 | 79 | VULKAN_APP_MAIN { 80 | const VulkanAppConfig cfg{ 81 | .width = 0, 82 | .height = 0, 83 | }; 84 | VULKAN_APP_DECLARE(app, cfg); 85 | 86 | lvk::IContext* ctx = app.ctx_.get(); 87 | 88 | { 89 | const VkPhysicalDeviceProperties& props = static_cast(ctx)->getVkPhysicalDeviceProperties(); 90 | 91 | if (props.limits.maxColorAttachments < 6) { 92 | LVK_ASSERT_MSG(false, "This demo needs at least 6 color attachments to be supported"); 93 | std::terminate(); 94 | } 95 | 96 | const uint16_t indexData[36] = {0, 1, 2, 2, 3, 0, 1, 5, 6, 6, 2, 1, 7, 6, 5, 5, 4, 7, 97 | 4, 0, 3, 3, 7, 4, 4, 5, 1, 1, 0, 4, 3, 2, 6, 6, 7, 3}; 98 | 99 | lvk::Holder ib0_ = ctx->createBuffer({ 100 | .usage = lvk::BufferUsageBits_Index, 101 | .storage = lvk::StorageType_Device, 102 | .size = sizeof(indexData), 103 | .data = indexData, 104 | .debugName = "Buffer: index", 105 | }); 106 | 107 | lvk::Holder texture_ = ctx->createTexture({ 108 | .type = lvk::TextureType_Cube, 109 | .format = lvk::Format_BGRA_UN8, 110 | .dimensions = {512, 512}, 111 | .usage = lvk::TextureUsageBits_Sampled | lvk::TextureUsageBits_Attachment, 112 | .debugName = "CubeMap", 113 | }); 114 | 115 | lvk::Holder vert_ = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 116 | lvk::Holder frag_ = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 117 | lvk::Holder vertTriangle_ = 118 | ctx->createShaderModule({codeTriangleVS, lvk::Stage_Vert, "Shader Module: triangle (vert)"}); 119 | lvk::Holder fragTriangle_ = 120 | ctx->createShaderModule({codeTriangleFS, lvk::Stage_Frag, "Shader Module: triangle (frag)"}); 121 | 122 | lvk::Holder renderPipelineState_Mesh_ = ctx->createRenderPipeline({ 123 | .smVert = vert_, 124 | .smFrag = frag_, 125 | .color = {{.format = ctx->getSwapchainFormat()}}, 126 | .cullMode = lvk::CullMode_Back, 127 | .frontFaceWinding = lvk::WindingMode_CW, 128 | .debugName = "Pipeline: mesh", 129 | }); 130 | lvk::Holder renderPipelineState_Triangle_ = ctx->createRenderPipeline({ 131 | .smVert = vertTriangle_, 132 | .smFrag = fragTriangle_, 133 | .color = {{.format = ctx->getFormat(texture_)}, 134 | {.format = ctx->getFormat(texture_)}, 135 | {.format = ctx->getFormat(texture_)}, 136 | {.format = ctx->getFormat(texture_)}, 137 | {.format = ctx->getFormat(texture_)}, 138 | {.format = ctx->getFormat(texture_)}}, 139 | .debugName = "Pipeline: triangle", 140 | }); 141 | 142 | // Main loop 143 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 144 | LVK_PROFILER_FUNCTION(); 145 | 146 | const float fov = float(45.0f * (M_PI / 180.0f)); 147 | const mat4 proj = glm::perspectiveLH(fov, aspectRatio, 0.1f, 500.0f); 148 | const mat4 view = glm::translate(mat4(1.0f), vec3(0.0f, 0.0f, 5.0f)); 149 | const mat4 model = glm::rotate(mat4(1.0f), (float)glfwGetTime(), glm::normalize(vec3(1.0f, 1.0f, 1.0f))); 150 | 151 | // Command buffers (1-N per thread): create, submit and forget 152 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 153 | 154 | buffer.cmdPushDebugGroupLabel("Render to Cube Map", 0xff0000ff); 155 | buffer.cmdBeginRendering( 156 | {.color = 157 | { 158 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 0, .clearColor = {0.3f, 0.1f, 0.1f, 1.0f}}, 159 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 1, .clearColor = {0.1f, 0.3f, 0.1f, 1.0f}}, 160 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 2, .clearColor = {0.1f, 0.1f, 0.3f, 1.0f}}, 161 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 3, .clearColor = {0.3f, 0.1f, 0.3f, 1.0f}}, 162 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 4, .clearColor = {0.3f, 0.3f, 0.1f, 1.0f}}, 163 | {.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .layer = 5, .clearColor = {0.1f, 0.3f, 0.3f, 1.0f}}, 164 | }}, 165 | {.color = { 166 | {.texture = texture_}, 167 | {.texture = texture_}, 168 | {.texture = texture_}, 169 | {.texture = texture_}, 170 | {.texture = texture_}, 171 | {.texture = texture_}, 172 | }}); 173 | buffer.cmdBindRenderPipeline(renderPipelineState_Triangle_); 174 | buffer.cmdPushConstants(float(10.0 * glfwGetTime())); 175 | buffer.cmdDraw(3); 176 | buffer.cmdEndRendering(); 177 | buffer.cmdPopDebugGroupLabel(); 178 | 179 | buffer.cmdBeginRendering({.color = {{ 180 | .loadOp = lvk::LoadOp_Clear, 181 | .storeOp = lvk::StoreOp_Store, 182 | .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}, 183 | }}}, 184 | {.color = {{.texture = ctx->getCurrentSwapchainTexture()}}}, 185 | {.textures = {lvk::TextureHandle(texture_)}}); 186 | { 187 | buffer.cmdBindRenderPipeline(renderPipelineState_Mesh_); 188 | buffer.cmdBindViewport({0.0f, 0.0f, (float)width, (float)height, 0.0f, +1.0f}); 189 | buffer.cmdBindScissorRect({0, 0, (uint32_t)width, (uint32_t)height}); 190 | buffer.cmdPushDebugGroupLabel("Render Mesh", 0xff0000ff); 191 | buffer.cmdBindDepthState({}); 192 | buffer.cmdBindIndexBuffer(ib0_, lvk::IndexFormat_UI16); 193 | struct { 194 | mat4 mvp; 195 | uint32_t texture; 196 | } bindings = { 197 | .mvp = proj * view * model, 198 | .texture = texture_.index(), 199 | }; 200 | buffer.cmdPushConstants(bindings); 201 | buffer.cmdDrawIndexed(3 * 6 * 2); 202 | buffer.cmdPopDebugGroupLabel(); 203 | } 204 | buffer.cmdEndRendering(); 205 | 206 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 207 | }); 208 | } 209 | 210 | VULKAN_APP_EXIT(); 211 | } 212 | -------------------------------------------------------------------------------- /samples/004_YUV.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | #include 10 | 11 | #include 12 | 13 | const char* codeVS = R"( 14 | #version 460 15 | layout (location=0) out vec2 uv; 16 | const vec2 pos[4] = vec2[4]( 17 | vec2(-1.0, -1.0), 18 | vec2(-1.0, +1.0), 19 | vec2(+1.0, -1.0), 20 | vec2(+1.0, +1.0) 21 | ); 22 | void main() { 23 | gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); 24 | uv = pos[gl_VertexIndex] * 0.5 + 0.5; 25 | uv.y = 1.0-uv.y; 26 | } 27 | )"; 28 | 29 | const char* codeFS = R"( 30 | layout (location=0) in vec2 uv; 31 | layout (location=0) out vec4 out_FragColor; 32 | 33 | layout (constant_id = 0) const uint textureId = 0; 34 | 35 | void main() { 36 | out_FragColor = texture(kSamplerYUV[textureId], uv); 37 | }; 38 | )"; 39 | 40 | size_t currentDemo_ = 0; 41 | 42 | // demonstrate different YUV formats 43 | struct YUVFormatDemo { 44 | const char* name; 45 | lvk::Format format; 46 | lvk::Holder texture; 47 | lvk::Holder renderPipelineState; 48 | }; 49 | 50 | struct Resources { 51 | lvk::Holder vert; 52 | lvk::Holder frag; 53 | std::vector demos; 54 | }; 55 | 56 | Resources res_; 57 | 58 | void createDemo(lvk::IContext* ctx, const char* contentFolder, const char* name, lvk::Format format, const char* fileName) { 59 | using namespace std::filesystem; 60 | path dir(contentFolder); 61 | int32_t texWidth = 1920; 62 | int32_t texHeight = 1080; 63 | FILE* file = fopen((dir / "src" / path(fileName)).string().c_str(), "rb"); 64 | SCOPE_EXIT { 65 | if (file) { 66 | fclose(file); 67 | } 68 | }; 69 | fseek(file, 0, SEEK_END); 70 | const uint32_t length = ftell(file); 71 | fseek(file, 0, SEEK_SET); 72 | 73 | LVK_ASSERT_MSG(file && length, "Cannot load textures. Run `deploy_content.py`/`deploy_content_android.py` before running this app."); 74 | if (!file || !length) { 75 | printf("Cannot load textures. Run `deploy_content.py`/`deploy_content_android.py` before running this app."); 76 | std::terminate(); 77 | } 78 | 79 | LVK_ASSERT(length == texWidth * texHeight * 3 / 2); 80 | 81 | std::vector pixels(length); 82 | fread(pixels.data(), 1, length, file); 83 | 84 | lvk::Holder texture = ctx->createTexture({ 85 | .type = lvk::TextureType_2D, 86 | .format = format, 87 | .dimensions = {(uint32_t)texWidth, (uint32_t)texHeight}, 88 | .usage = lvk::TextureUsageBits_Sampled, 89 | .data = pixels.data(), 90 | .debugName = name, 91 | }); 92 | 93 | const uint32_t textureId = texture.index(); 94 | 95 | res_.demos.push_back({ 96 | .name = name, 97 | .format = format, 98 | .texture = std::move(texture), 99 | .renderPipelineState = ctx->createRenderPipeline({ 100 | .topology = lvk::Topology_TriangleStrip, 101 | .smVert = res_.vert, 102 | .smFrag = res_.frag, 103 | .specInfo = {.entries = {{.constantId = 0, .size = sizeof(uint32_t)}}, .data = &textureId, .dataSize = sizeof(textureId)}, 104 | .color = {{.format = ctx->getSwapchainFormat()}}, 105 | .debugName = name, 106 | }), 107 | }); 108 | } 109 | 110 | VULKAN_APP_MAIN { 111 | const VulkanAppConfig cfg{ 112 | .width = 0, 113 | .height = 0, 114 | }; 115 | VULKAN_APP_DECLARE(app, cfg); 116 | 117 | lvk::IContext* ctx = app.ctx_.get(); 118 | 119 | res_.vert = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 120 | res_.frag = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 121 | 122 | createDemo(ctx, app.folderContentRoot_.c_str(), "YUV NV12", lvk::Format_YUV_NV12, "igl-samples/output_frame_900.nv12.yuv"); 123 | createDemo(ctx, app.folderContentRoot_.c_str(), "YUV 420p", lvk::Format_YUV_420p, "igl-samples/output_frame_900.420p.yuv"); 124 | 125 | #if !defined(ANDROID) 126 | app.addMouseButtonCallback([](auto* window, int button, int action, int mods) { 127 | if (action == GLFW_PRESS && !res_.demos.empty()) { 128 | currentDemo_ = (currentDemo_ + 1) % res_.demos.size(); 129 | } 130 | }); 131 | app.addKeyCallback([](GLFWwindow* window, int key, int, int action, int) { 132 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 133 | glfwSetWindowShouldClose(window, GLFW_TRUE); 134 | } else if (key == GLFW_KEY_T && action == GLFW_PRESS) { 135 | currentDemo_ = 0; 136 | if (!res_.demos.empty()) 137 | res_.demos.pop_back(); 138 | } else if (action == GLFW_PRESS && !res_.demos.empty()) { 139 | currentDemo_ = (currentDemo_ + 1) % res_.demos.size(); 140 | } 141 | }); 142 | #endif // !ANDROID 143 | 144 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 145 | const lvk::Framebuffer framebuffer = { 146 | .color = {{.texture = ctx->getCurrentSwapchainTexture()}}, 147 | }; 148 | 149 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 150 | 151 | buffer.cmdBeginRendering({.color = {{.loadOp = lvk::LoadOp_DontCare}}}, framebuffer); 152 | 153 | if (!res_.demos.empty()) { 154 | const YUVFormatDemo& demo = res_.demos[currentDemo_]; 155 | buffer.cmdBindRenderPipeline(demo.renderPipelineState); 156 | buffer.cmdDraw(4); 157 | { 158 | app.imgui_->beginFrame(framebuffer); 159 | const ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | 160 | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | 161 | ImGuiWindowFlags_NoMove; 162 | ImGui::SetNextWindowPos({15.0f, 15.0f}); 163 | ImGui::SetNextWindowBgAlpha(0.30f); 164 | ImGui::Begin("##FormatYUV", nullptr, flags); 165 | ImGui::Text("%s", demo.name); 166 | ImGui::Text("Press any key to change"); 167 | ImGui::End(); 168 | app.drawFPS(); 169 | app.imgui_->endFrame(buffer); 170 | } 171 | } 172 | 173 | buffer.cmdEndRendering(); 174 | 175 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 176 | }); 177 | 178 | // destroy all the Vulkan stuff before closing the window 179 | res_ = {}; 180 | 181 | VULKAN_APP_EXIT(); 182 | } 183 | -------------------------------------------------------------------------------- /samples/005_MeshShaders.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | // we are going to use raw Vulkan here to initialize VK_EXT_mesh_shader 11 | #include 12 | 13 | const char* codeTask = R"( 14 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 15 | 16 | void main() { 17 | EmitMeshTasksEXT(1, 1, 1); 18 | } 19 | )"; 20 | 21 | const char* codeMesh = R"( 22 | layout(triangles, max_vertices = 3, max_primitives = 1) out; 23 | 24 | layout (location=0) out vec3 color[3]; 25 | 26 | const vec2 pos[3] = vec2[3]( 27 | vec2(-0.6, -0.4), 28 | vec2( 0.6, -0.4), 29 | vec2( 0.0, 0.6) 30 | ); 31 | const vec3 col[3] = vec3[3]( 32 | vec3(1.0, 0.0, 0.0), 33 | vec3(0.0, 1.0, 0.0), 34 | vec3(0.0, 0.0, 1.0) 35 | ); 36 | void main() { 37 | SetMeshOutputsEXT(3, 1); 38 | 39 | for (uint i = 0; i != 3; i++) { 40 | gl_MeshVerticesEXT[i].gl_Position = vec4(pos[i], 0.0, 1.0); 41 | color[i] = col[i]; 42 | } 43 | 44 | gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = false; 45 | gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2); 46 | } 47 | )"; 48 | 49 | const char* codeFrag = R"( 50 | #version 460 51 | layout (location=0) in vec3 color; 52 | layout (location=0) out vec4 out_FragColor; 53 | 54 | void main() { 55 | out_FragColor = vec4(color, 1.0); 56 | }; 57 | )"; 58 | 59 | struct { 60 | lvk::Holder renderPipelineState_Triangle_; 61 | lvk::Holder task_; 62 | lvk::Holder mesh_; 63 | lvk::Holder frag_; 64 | } res; 65 | 66 | VULKAN_APP_MAIN { 67 | VkPhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures = { 68 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, 69 | .taskShader = VK_TRUE, 70 | .meshShader = VK_TRUE, 71 | }; 72 | const VulkanAppConfig cfg{ 73 | .width = 800, 74 | .height = 600, 75 | .resizable = true, 76 | .contextConfig = 77 | { 78 | .extensionsDevice = {"VK_EXT_mesh_shader"}, 79 | .extensionsDeviceFeatures = &meshShaderFeatures, 80 | }, 81 | }; 82 | VULKAN_APP_DECLARE(app, cfg); 83 | 84 | lvk::IContext* ctx = app.ctx_.get(); 85 | 86 | res.task_ = ctx->createShaderModule({codeTask, lvk::Stage_Task, "Shader Module: main (task)"}); 87 | res.mesh_ = ctx->createShaderModule({codeMesh, lvk::Stage_Mesh, "Shader Module: main (mesh)"}); 88 | res.frag_ = ctx->createShaderModule({codeFrag, lvk::Stage_Frag, "Shader Module: main (frag)"}); 89 | 90 | res.renderPipelineState_Triangle_ = ctx->createRenderPipeline( 91 | { 92 | .smTask = res.task_, 93 | .smMesh = res.mesh_, 94 | .smFrag = res.frag_, 95 | .color = {{.format = ctx->getSwapchainFormat()}}, 96 | }, 97 | nullptr); 98 | 99 | LVK_ASSERT(res.renderPipelineState_Triangle_.valid()); 100 | 101 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 102 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 103 | 104 | // This will clear the framebuffer 105 | buffer.cmdBeginRendering({.color = {{.loadOp = lvk::LoadOp_Clear, .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}}}}, 106 | {.color = {{.texture = ctx->getCurrentSwapchainTexture()}}}); 107 | buffer.cmdBindRenderPipeline(res.renderPipelineState_Triangle_); 108 | buffer.cmdPushDebugGroupLabel("Render Triangle", 0xff0000ff); 109 | buffer.cmdDrawMeshTasks({1, 1, 1}); 110 | 111 | buffer.cmdPopDebugGroupLabel(); 112 | buffer.cmdEndRendering(); 113 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 114 | }); 115 | 116 | // destroy all the Vulkan stuff before closing the window 117 | res = {}; 118 | 119 | VULKAN_APP_EXIT(); 120 | } 121 | -------------------------------------------------------------------------------- /samples/006_SwapchainHDR.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | const char* codeVS = R"( 11 | #version 460 12 | #extension GL_EXT_scalar_block_layout : require 13 | layout (location=0) out vec3 color; 14 | const vec2 pos[3] = vec2[3]( 15 | vec2(-0.6, -0.4), 16 | vec2( 0.6, -0.4), 17 | vec2( 0.0, 0.6) 18 | ); 19 | layout(scalar, push_constant) uniform constants { 20 | vec3 col[3]; 21 | }; 22 | 23 | void main() { 24 | gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); 25 | color = col[gl_VertexIndex]; 26 | } 27 | )"; 28 | 29 | const char* codeFS = R"( 30 | #version 460 31 | layout (location=0) in vec3 color; 32 | layout (location=0) out vec4 out_FragColor; 33 | 34 | void main() { 35 | out_FragColor = vec4(color, 1.0); 36 | }; 37 | )"; 38 | 39 | VULKAN_APP_MAIN { 40 | const VulkanAppConfig cfg{ 41 | .width = -80, 42 | .height = -80, 43 | .contextConfig = 44 | { 45 | .swapchainRequestedColorSpace = lvk::ColorSpace_HDR10, 46 | //.swapchainRequestedColorSpace = lvk::ColorSpace_SRGB_EXTENDED_LINEAR, 47 | }, 48 | }; 49 | VULKAN_APP_DECLARE(app, cfg); 50 | 51 | lvk::IContext* ctx = app.ctx_.get(); 52 | 53 | LLOGL("Swapchain format : %u\n", ctx->getSwapchainFormat() ); 54 | LLOGL("Swapchain color space: %u\n", ctx->getSwapchainColorSpace()); 55 | 56 | { 57 | lvk::Holder vert_ = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 58 | lvk::Holder frag_ = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 59 | 60 | lvk::Holder renderPipelineState_Triangle_ = ctx->createRenderPipeline( 61 | { 62 | .smVert = vert_, 63 | .smFrag = frag_, 64 | .color = {{.format = ctx->getSwapchainFormat()}}, 65 | }, 66 | nullptr); 67 | 68 | LVK_ASSERT(renderPipelineState_Triangle_.valid()); 69 | 70 | struct { 71 | vec3 rgb0; 72 | vec3 rgb1; 73 | vec3 rgb2; 74 | } pc = { 75 | .rgb0 = {1, 0, 0}, 76 | .rgb1 = {0, 1, 0}, 77 | .rgb2 = {0, 0, 1}, 78 | }; 79 | 80 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 81 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 82 | const lvk::Framebuffer fb = { 83 | .color = {{.texture = ctx->getCurrentSwapchainTexture()}}, 84 | }; 85 | buffer.cmdBeginRendering({.color = {{.loadOp = lvk::LoadOp_Clear, .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}}}}, fb); 86 | buffer.cmdBindRenderPipeline(renderPipelineState_Triangle_); 87 | buffer.cmdPushConstants(pc); 88 | buffer.cmdDraw(3); 89 | app.imgui_->beginFrame(fb); 90 | ImGui::Begin("Colors", nullptr, ImGuiWindowFlags_AlwaysAutoResize); 91 | ImGui::SliderFloat3("Color #0", glm::value_ptr(pc.rgb0), 0.0f, 1.0f); 92 | ImGui::SliderFloat3("Color #1", glm::value_ptr(pc.rgb1), 0.0f, 1.0f); 93 | ImGui::SliderFloat3("Color #2", glm::value_ptr(pc.rgb2), 0.0f, 1.0f); 94 | ImGui::End(); 95 | app.imgui_->endFrame(buffer); 96 | buffer.cmdEndRendering(); 97 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 98 | }); 99 | } 100 | 101 | VULKAN_APP_EXIT(); 102 | } 103 | -------------------------------------------------------------------------------- /samples/009_TriplanarMapping.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | constexpr uint32_t kNumObjects = 16; 16 | 17 | const char* codeVS = R"( 18 | layout (location=0) out vec3 color; 19 | layout (location=1) out vec3 normal; 20 | 21 | struct Vertex { 22 | float x, y, z; 23 | float r, g, b; 24 | }; 25 | 26 | layout(std430, buffer_reference) readonly buffer VertexBuffer { 27 | Vertex vertices[]; 28 | }; 29 | 30 | layout(std430, buffer_reference) readonly buffer PerFrame { 31 | mat4 proj; 32 | mat4 view; 33 | uint texture0; 34 | uint texture1; 35 | uint sampler0; 36 | }; 37 | 38 | layout(std430, buffer_reference) readonly buffer PerObject { 39 | mat4 model[]; 40 | }; 41 | 42 | layout(push_constant) uniform constants { 43 | PerFrame perFrame; 44 | PerObject perObject; 45 | VertexBuffer vb; 46 | } pc; 47 | 48 | void main() { 49 | mat4 proj = pc.perFrame.proj; 50 | mat4 view = pc.perFrame.view; 51 | mat4 model = pc.perObject.model[gl_InstanceIndex]; 52 | Vertex v = pc.vb.vertices[gl_VertexIndex]; 53 | gl_Position = proj * view * model * vec4(v.x, v.y, v.z, 1.0); 54 | color = vec3(v.r, v.g, v.b); 55 | normal = normalize(vec3(v.x, v.y, v.z)); // object space normal 56 | } 57 | )"; 58 | 59 | const char* codeFS = R"( 60 | layout (location=0) in vec3 color; 61 | layout (location=1) in vec3 normal; 62 | layout (location=0) out vec4 out_FragColor; 63 | 64 | layout(std430, buffer_reference) readonly buffer PerFrame { 65 | mat4 proj; 66 | mat4 view; 67 | uint texture0; 68 | uint texture1; 69 | uint sampler0; 70 | }; 71 | 72 | layout(push_constant) uniform constants { 73 | PerFrame perFrame; 74 | } pc; 75 | 76 | vec4 triplanar(uint tex, vec3 worldPos, vec3 normal) { 77 | // generate weights, show texture on both sides of the object (positive and negative) 78 | vec3 weights = abs(normal); 79 | // make the transition sharper 80 | weights = pow(weights, vec3(8.0)); 81 | // make sure the sum of all components is 1 82 | weights = weights / (weights.x + weights.y + weights.z); 83 | 84 | // sample the texture for 3 different projections 85 | vec4 cXY = textureBindless2D(tex, pc.perFrame.sampler0, worldPos.xy); 86 | vec4 cZY = textureBindless2D(tex, pc.perFrame.sampler0, worldPos.zy); 87 | vec4 cXZ = textureBindless2D(tex, pc.perFrame.sampler0, worldPos.xz); 88 | 89 | // combine the projected colors 90 | return cXY * weights.z + cZY * weights.x + cXZ * weights.y; 91 | } 92 | 93 | void main() { 94 | // triplanar mapping in object-space; for our icosahedron, object-space position and normal vectors are the same 95 | vec4 t0 = triplanar(pc.perFrame.texture0, normal, normal); 96 | vec4 t1 = triplanar(pc.perFrame.texture1, normal, normal); 97 | out_FragColor = vec4(color * (t0.rgb + t1.rgb), 1.0); 98 | }; 99 | )"; 100 | 101 | struct VertexPosUvw { 102 | vec3 pos; 103 | vec3 color; 104 | }; 105 | 106 | struct PerFrame { 107 | mat4 proj; 108 | mat4 view; 109 | uint32_t texture0; 110 | uint32_t texture1; 111 | uint32_t sampler; 112 | }; 113 | 114 | lvk::Holder texture0_; 115 | lvk::Holder texture1_; 116 | 117 | VULKAN_APP_MAIN { 118 | const VulkanAppConfig cfg{ 119 | .width = 1280, 120 | .height = 1024, 121 | .resizable = true, 122 | }; 123 | VULKAN_APP_DECLARE(app, cfg); 124 | 125 | lvk::IContext* ctx = app.ctx_.get(); 126 | 127 | // clang-format off 128 | // icosahedron 129 | const float t = (1.0f + sqrtf(5.0f)) / 2.0f; 130 | const VertexPosUvw vertexData[] = { 131 | {{-1, t, 0}, {0, 1, 0}}, 132 | {{ 1, t, 0}, {1, 1, 0}}, 133 | {{-1, -t, 0}, {0, 1, 0}}, 134 | {{ 1, -t, 0}, {1, 1, 0}}, 135 | 136 | {{0, -1, t}, {0, 0, 1}}, 137 | {{0, 1, t}, {0, 1, 1}}, 138 | {{0, -1, -t}, {0, 0, 1}}, 139 | {{0, 1, -t}, {0, 1, 1}}, 140 | 141 | {{ t, 0, -1}, {1, 0, 0}}, 142 | {{ t, 0, 1}, {1, 0, 1}}, 143 | {{-t, 0, -1}, {1, 0, 0}}, 144 | {{-t, 0, 1}, {1, 0, 1}}, 145 | }; 146 | // clang-format on 147 | 148 | lvk::Holder vb0_ = ctx->createBuffer({ 149 | .usage = lvk::BufferUsageBits_Storage, 150 | .storage = lvk::StorageType_Device, 151 | .size = sizeof(vertexData), 152 | .data = vertexData, 153 | .debugName = "Buffer: vertices", 154 | }); 155 | 156 | const uint16_t indexData[] = {0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 157 | 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1}; 158 | 159 | lvk::Holder ib0_ = ctx->createBuffer({ 160 | .usage = lvk::BufferUsageBits_Index, 161 | .storage = lvk::StorageType_Device, 162 | .size = sizeof(indexData), 163 | .data = indexData, 164 | .debugName = "Buffer: indices", 165 | }); 166 | 167 | lvk::Holder bufPerFrame = ctx->createBuffer({ 168 | .usage = lvk::BufferUsageBits_Storage, 169 | .storage = lvk::StorageType_HostVisible, 170 | .size = sizeof(PerFrame), 171 | .debugName = "Buffer: per frame", 172 | }); 173 | lvk::Holder bufModelMatrices = ctx->createBuffer({ 174 | .usage = lvk::BufferUsageBits_Storage, 175 | .storage = lvk::StorageType_HostVisible, 176 | .size = kNumObjects * sizeof(mat4), 177 | .debugName = "Buffer: model matrices", 178 | }); 179 | 180 | lvk::Holder sampler_ = ctx->createSampler({.debugName = "Sampler: linear"}, nullptr); 181 | 182 | // texture 0 183 | { 184 | const uint32_t texWidth = 256; 185 | const uint32_t texHeight = 256; 186 | std::vector pixels(texWidth * texHeight); 187 | for (uint32_t y = 0; y != texHeight; y++) { 188 | for (uint32_t x = 0; x != texWidth; x++) { 189 | // create a XOR pattern 190 | pixels[y * texWidth + x] = 0xFF000000 + ((x ^ y) << 16) + ((x ^ y) << 8) + (x ^ y); 191 | } 192 | } 193 | texture0_ = ctx->createTexture({ 194 | .type = lvk::TextureType_2D, 195 | .format = lvk::Format_BGRA_UN8, 196 | .dimensions = {texWidth, texHeight}, 197 | .usage = lvk::TextureUsageBits_Sampled, 198 | .data = pixels.data(), 199 | .debugName = "XOR pattern", 200 | }); 201 | } 202 | 203 | // texture 1 204 | { 205 | using namespace std::filesystem; 206 | path dir = app.folderContentRoot_; 207 | int32_t texWidth = 0; 208 | int32_t texHeight = 0; 209 | int32_t channels = 0; 210 | uint8_t* pixels = stbi_load( 211 | (dir / path("src/bistro/BuildingTextures/wood_polished_01_diff.png")).string().c_str(), &texWidth, &texHeight, &channels, 4); 212 | SCOPE_EXIT { 213 | stbi_image_free(pixels); 214 | }; 215 | if (!pixels) { 216 | LVK_ASSERT_MSG(false, "Cannot load textures. Run `deploy_content.py`/`deploy_content_android.py` before running this app."); 217 | LLOGW("Cannot load textures. Run `deploy_content.py`/`deploy_content_android.py` before running this app."); 218 | std::terminate(); 219 | } 220 | texture1_ = ctx->createTexture({ 221 | .type = lvk::TextureType_2D, 222 | .format = lvk::Format_RGBA_UN8, 223 | .dimensions = {(uint32_t)texWidth, (uint32_t)texHeight}, 224 | .usage = lvk::TextureUsageBits_Sampled, 225 | .data = pixels, 226 | .debugName = "wood_polished_01_diff.png", 227 | }); 228 | } 229 | 230 | vec3 axis_[kNumObjects]; // uninitialized 231 | 232 | // generate random rotation axes 233 | for (vec3& v : axis_) { 234 | v = glm::sphericalRand(1.0f); 235 | } 236 | 237 | lvk::Holder vert_ = ctx->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); 238 | lvk::Holder frag_ = ctx->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); 239 | 240 | lvk::Holder renderPipelineState_Mesh_ = ctx->createRenderPipeline({ 241 | .smVert = vert_, 242 | .smFrag = frag_, 243 | .color = {{.format = ctx->getSwapchainFormat()}}, 244 | .depthFormat = app.getDepthFormat(), 245 | .cullMode = lvk::CullMode_Back, 246 | .frontFaceWinding = lvk::WindingMode_CW, 247 | .debugName = "Pipeline: mesh", 248 | }); 249 | 250 | #if !defined(ANDROID) 251 | app.addKeyCallback([](GLFWwindow* window, int key, int, int action, int) { 252 | if (key == GLFW_KEY_T && action == GLFW_PRESS) { 253 | texture1_.reset(); 254 | } 255 | }); 256 | #endif // !ANDROID 257 | 258 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 259 | LVK_PROFILER_FUNCTION(); 260 | 261 | const float fov = float(45.0f * (M_PI / 180.0f)); 262 | const PerFrame perFrame = { 263 | .proj = glm::perspectiveLH(fov, aspectRatio, 0.1f, 100.0f), 264 | // place the "camera" behind the objects, the distance depends on the total number of objects 265 | .view = glm::translate(mat4(1.0f), vec3(0.0f, 0.0f, sqrtf(kNumObjects / 16) * 14.0f * t)), 266 | .texture0 = texture0_.index(), 267 | .texture1 = texture1_.index(), 268 | .sampler = sampler_.index(), 269 | }; 270 | 271 | mat4 modelMatrices[kNumObjects]; // uninitialized 272 | 273 | // rotate objects around random axes 274 | for (uint32_t i = 0; i != kNumObjects; i++) { 275 | const float direction = powf(-1, (float)(i + 1)); 276 | const uint32_t cubesInLine = (uint32_t)sqrt(kNumObjects); 277 | const vec3 offset = 278 | vec3(-1.5f * sqrt(kNumObjects) + 4.0f * (i % cubesInLine), -1.5f * sqrt(kNumObjects) + 4.0f * (i / cubesInLine), 0); 279 | modelMatrices[i] = glm::rotate(glm::translate(mat4(1.0f), offset), float(direction * glfwGetTime()), axis_[i]); 280 | } 281 | 282 | lvk::ICommandBuffer& buffer = ctx->acquireCommandBuffer(); 283 | 284 | buffer.cmdUpdateBuffer(bufPerFrame, perFrame); 285 | buffer.cmdUpdateBuffer(bufModelMatrices, modelMatrices); 286 | 287 | lvk::Framebuffer framebuffer = { 288 | .color = {{.texture = ctx->getCurrentSwapchainTexture()}}, 289 | .depthStencil = {app.getDepthTexture()}, 290 | }; 291 | buffer.cmdBeginRendering( 292 | lvk::RenderPass{.color = {{.loadOp = lvk::LoadOp_Clear, .storeOp = lvk::StoreOp_Store, .clearColor = {1.0f, 1.0f, 1.0f, 1.0f}}}, 293 | .depth = {.loadOp = lvk::LoadOp_Clear, .clearDepth = 1.0}}, 294 | framebuffer); 295 | { 296 | buffer.cmdBindRenderPipeline(renderPipelineState_Mesh_); 297 | buffer.cmdBindViewport({0.0f, 0.0f, (float)width, (float)height, 0.0f, +1.0f}); 298 | buffer.cmdBindScissorRect({0, 0, (uint32_t)width, (uint32_t)height}); 299 | buffer.cmdPushDebugGroupLabel("Render Mesh", 0xff0000ff); 300 | buffer.cmdBindDepthState({.compareOp = lvk::CompareOp_Less, .isDepthWriteEnabled = true}); 301 | buffer.cmdBindIndexBuffer(ib0_, lvk::IndexFormat_UI16); 302 | const struct { 303 | uint64_t perFrame; 304 | uint64_t perObject; 305 | uint64_t vb; 306 | } bindings = { 307 | .perFrame = ctx->gpuAddress(bufPerFrame), 308 | .perObject = ctx->gpuAddress(bufModelMatrices), 309 | .vb = ctx->gpuAddress(vb0_), 310 | }; 311 | buffer.cmdPushConstants(bindings); 312 | buffer.cmdDrawIndexed(LVK_ARRAY_NUM_ELEMENTS(indexData), kNumObjects); 313 | buffer.cmdPopDebugGroupLabel(); 314 | } 315 | app.imgui_->beginFrame(framebuffer); 316 | ImGui::SetNextWindowPos({0, 0}); 317 | ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); 318 | ImGui::Begin("Texture Viewer", nullptr, ImGuiWindowFlags_AlwaysAutoResize); 319 | ImGui::Image(texture1_.index(), ImVec2(512, 512)); 320 | if (texture1_.valid()) 321 | ImGui::Text("Press T to unload texture"); 322 | ImGui::End(); 323 | app.drawFPS(); 324 | app.imgui_->endFrame(buffer); 325 | buffer.cmdEndRendering(); 326 | 327 | ctx->submit(buffer, ctx->getCurrentSwapchainTexture()); 328 | }); 329 | 330 | // destroy all the Vulkan stuff before closing the window 331 | texture0_.reset(); 332 | texture1_.reset(); 333 | 334 | VULKAN_APP_EXIT(); 335 | } 336 | -------------------------------------------------------------------------------- /samples/Bistro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /* 9 | Helper functions to load and cache Bistro/Sponza meshes: 10 | 11 | bool loadAndCache(const std::string& folderContentRoot, const char* cacheFileName, const char* modelFileName) 12 | bool loadFromCache(const char* cacheFileName) 13 | 14 | The result is stored in the global variables: 15 | 16 | std::vector vertexData_; 17 | std::vector indexData_; 18 | std::vector cachedMaterials_; 19 | */ 20 | 21 | #pragma once 22 | 23 | #if !defined(_USE_MATH_DEFINES) 24 | #define _USE_MATH_DEFINES 25 | #endif // _USE_MATH_DEFINES 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #define GLM_ENABLE_EXPERIMENTAL 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | using glm::mat3; 43 | using glm::mat4; 44 | using glm::vec2; 45 | using glm::vec3; 46 | using glm::vec4; 47 | 48 | constexpr uint32_t kMeshCacheVersion = 0xC0DE000A; 49 | 50 | #define MAX_MATERIAL_NAME 128 51 | 52 | struct VertexData { 53 | vec3 position; 54 | uint32_t uv; // hvec2 55 | uint16_t normal; // Octahedral 16-bit https://www.shadertoy.com/view/llfcRl 56 | uint16_t mtlIndex; 57 | }; 58 | 59 | static_assert(sizeof(VertexData) == 5 * sizeof(uint32_t)); 60 | 61 | std::vector vertexData_; 62 | std::vector indexData_; 63 | 64 | struct CachedMaterial { 65 | char name[MAX_MATERIAL_NAME] = {}; 66 | vec3 ambient = vec3(0.0f); 67 | vec3 diffuse = vec3(0.0f); 68 | char ambient_texname[MAX_MATERIAL_NAME] = {}; 69 | char diffuse_texname[MAX_MATERIAL_NAME] = {}; 70 | char alpha_texname[MAX_MATERIAL_NAME] = {}; 71 | }; 72 | 73 | std::vector cachedMaterials_; 74 | 75 | vec2 msign(vec2 v) { 76 | return vec2(v.x >= 0.0 ? 1.0f : -1.0f, v.y >= 0.0 ? 1.0f : -1.0f); 77 | } 78 | 79 | // https://www.shadertoy.com/view/llfcRl 80 | uint16_t packSnorm2x8(vec2 v) { 81 | glm::uvec2 d = glm::uvec2(round(127.5f + v * 127.5f)); 82 | return d.x | (d.y << 8u); 83 | } 84 | 85 | // https://www.shadertoy.com/view/llfcRl 86 | uint16_t packOctahedral16(vec3 n) { 87 | n /= (abs(n.x) + abs(n.y) + abs(n.z)); 88 | return ::packSnorm2x8((n.z >= 0.0) ? vec2(n.x, n.y) : (vec2(1.0) - abs(vec2(n.y, n.x))) * msign(vec2(n))); 89 | } 90 | 91 | std::string normalizeTextureName(const char* n) { 92 | if (!n) 93 | return std::string(); 94 | LVK_ASSERT(strlen(n) < MAX_MATERIAL_NAME); 95 | std::string name(n); 96 | #if defined(__linux__) || defined(__APPLE__) || defined(ANDROID) 97 | std::replace(name.begin(), name.end(), '\\', '/'); 98 | #endif 99 | return name; 100 | } 101 | 102 | bool loadAndCache(const std::string& folderContentRoot, const char* cacheFileName, const char* modelFileName) { 103 | LVK_PROFILER_FUNCTION(); 104 | 105 | // load 3D model and cache it 106 | LLOGL("Loading `%s`... It can take a while in debug builds...\n", modelFileName); 107 | 108 | fastObjMesh* mesh = fast_obj_read((folderContentRoot + modelFileName).c_str()); 109 | SCOPE_EXIT { 110 | if (mesh) 111 | fast_obj_destroy(mesh); 112 | }; 113 | 114 | if (!LVK_VERIFY(mesh)) { 115 | LLOGW("Failed to load '%s'", modelFileName); 116 | LVK_ASSERT_MSG(false, "Did you read the tutorial at the top of this file?"); 117 | return false; 118 | } 119 | 120 | LLOGL("Loaded.\n"); 121 | 122 | uint32_t vertexCount = 0; 123 | 124 | for (uint32_t i = 0; i < mesh->face_count; ++i) 125 | vertexCount += mesh->face_vertices[i]; 126 | 127 | vertexData_.reserve(vertexCount); 128 | 129 | uint32_t vertexIndex = 0; 130 | 131 | for (uint32_t face = 0; face < mesh->face_count; face++) { 132 | for (uint32_t v = 0; v < mesh->face_vertices[face]; v++) { 133 | LVK_ASSERT(v < 3); 134 | const fastObjIndex gi = mesh->indices[vertexIndex++]; 135 | 136 | const float* p = &mesh->positions[gi.p * 3]; 137 | const float* n = &mesh->normals[gi.n * 3]; 138 | const float* t = &mesh->texcoords[gi.t * 2]; 139 | 140 | vertexData_.push_back({ 141 | .position = vec3(p[0], p[1], p[2]), 142 | .uv = glm::packHalf2x16(vec2(t[0], t[1])), 143 | .normal = packOctahedral16(vec3(n[0], n[1], n[2])), 144 | .mtlIndex = (uint16_t)mesh->face_materials[face], 145 | }); 146 | } 147 | } 148 | 149 | // repack the mesh as described in https://github.com/zeux/meshoptimizer 150 | { 151 | // 1. Generate an index buffer 152 | const size_t indexCount = vertexData_.size(); 153 | std::vector remap(indexCount); 154 | const size_t vertexCount = 155 | meshopt_generateVertexRemap(remap.data(), nullptr, indexCount, vertexData_.data(), indexCount, sizeof(VertexData)); 156 | // 2. Remap vertices 157 | std::vector remappedVertices; 158 | indexData_.resize(indexCount); 159 | remappedVertices.resize(vertexCount); 160 | meshopt_remapIndexBuffer(indexData_.data(), nullptr, indexCount, &remap[0]); 161 | meshopt_remapVertexBuffer(remappedVertices.data(), vertexData_.data(), indexCount, sizeof(VertexData), remap.data()); 162 | vertexData_ = remappedVertices; 163 | // 3. Optimize for the GPU vertex cache reuse and overdraw 164 | meshopt_optimizeVertexCache(indexData_.data(), indexData_.data(), indexCount, vertexCount); 165 | meshopt_optimizeOverdraw( 166 | indexData_.data(), indexData_.data(), indexCount, &vertexData_[0].position.x, vertexCount, sizeof(VertexData), 1.05f); 167 | meshopt_optimizeVertexFetch(vertexData_.data(), indexData_.data(), indexCount, vertexData_.data(), vertexCount, sizeof(VertexData)); 168 | } 169 | 170 | // loop over materials 171 | for (uint32_t mtlIdx = 0; mtlIdx != mesh->material_count; mtlIdx++) { 172 | const fastObjMaterial& m = mesh->materials[mtlIdx]; 173 | CachedMaterial mtl; 174 | mtl.ambient = vec3(m.Ka[0], m.Ka[1], m.Ka[2]); 175 | mtl.diffuse = vec3(m.Kd[0], m.Kd[1], m.Kd[2]); 176 | LVK_ASSERT(strlen(m.name) < MAX_MATERIAL_NAME); 177 | strcat(mtl.name, m.name); 178 | strcat(mtl.ambient_texname, normalizeTextureName(mesh->textures[m.map_Ka].name).c_str()); 179 | strcat(mtl.diffuse_texname, normalizeTextureName(mesh->textures[m.map_Kd].name).c_str()); 180 | strcat(mtl.alpha_texname, normalizeTextureName(mesh->textures[m.map_d].name).c_str()); 181 | cachedMaterials_.push_back(mtl); 182 | } 183 | 184 | LLOGL("Caching mesh...\n"); 185 | 186 | FILE* cacheFile = fopen(cacheFileName, "wb"); 187 | if (!cacheFile) { 188 | return false; 189 | } 190 | const uint32_t numMaterials = (uint32_t)cachedMaterials_.size(); 191 | const uint32_t numVertices = (uint32_t)vertexData_.size(); 192 | const uint32_t numIndices = (uint32_t)indexData_.size(); 193 | fwrite(&kMeshCacheVersion, sizeof(kMeshCacheVersion), 1, cacheFile); 194 | fwrite(&numMaterials, sizeof(numMaterials), 1, cacheFile); 195 | fwrite(&numVertices, sizeof(numVertices), 1, cacheFile); 196 | fwrite(&numIndices, sizeof(numIndices), 1, cacheFile); 197 | fwrite(cachedMaterials_.data(), sizeof(CachedMaterial), numMaterials, cacheFile); 198 | fwrite(vertexData_.data(), sizeof(VertexData), numVertices, cacheFile); 199 | fwrite(indexData_.data(), sizeof(uint32_t), numIndices, cacheFile); 200 | return fclose(cacheFile) == 0; 201 | } 202 | 203 | bool loadFromCache(const char* cacheFileName) { 204 | FILE* cacheFile = fopen(cacheFileName, "rb"); 205 | SCOPE_EXIT { 206 | if (cacheFile) { 207 | fclose(cacheFile); 208 | } 209 | }; 210 | if (!cacheFile) { 211 | return false; 212 | } 213 | #define CHECK_READ(expected, read) \ 214 | if ((read) != (expected)) { \ 215 | return false; \ 216 | } 217 | uint32_t versionProbe = 0; 218 | CHECK_READ(1, fread(&versionProbe, sizeof(versionProbe), 1, cacheFile)); 219 | if (versionProbe != kMeshCacheVersion) { 220 | LLOGL("Cache file has wrong version id\n"); 221 | return false; 222 | } 223 | uint32_t numMaterials = 0; 224 | uint32_t numVertices = 0; 225 | uint32_t numIndices = 0; 226 | CHECK_READ(1, fread(&numMaterials, sizeof(numMaterials), 1, cacheFile)); 227 | CHECK_READ(1, fread(&numVertices, sizeof(numVertices), 1, cacheFile)); 228 | CHECK_READ(1, fread(&numIndices, sizeof(numIndices), 1, cacheFile)); 229 | cachedMaterials_.resize(numMaterials); 230 | vertexData_.resize(numVertices); 231 | indexData_.resize(numIndices); 232 | CHECK_READ(numMaterials, fread(cachedMaterials_.data(), sizeof(CachedMaterial), numMaterials, cacheFile)); 233 | CHECK_READ(numVertices, fread(vertexData_.data(), sizeof(VertexData), numVertices, cacheFile)); 234 | CHECK_READ(numIndices, fread(indexData_.data(), sizeof(uint32_t), numIndices, cacheFile)); 235 | #undef CHECK_READ 236 | return true; 237 | } 238 | -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.19) 7 | 8 | set(PROJECT_NAME "LVK Samples") 9 | 10 | if(MSVC) 11 | add_definitions(-D_CONSOLE) 12 | else() 13 | add_compile_options(-Wno-deprecated-volatile) 14 | endif() 15 | 16 | if(WIN32) 17 | add_definitions("-DVK_USE_PLATFORM_WIN32_KHR=1") 18 | add_definitions("-DNOMINMAX") 19 | endif() 20 | 21 | if(ANDROID) 22 | if(DEFINED ENV{ANDROID_NDK}) 23 | cmake_path(SET NDK_PATH $ENV{ANDROID_NDK}) 24 | else() 25 | cmake_path(SET NDK_PATH $ENV{NDK_ROOT}) 26 | endif() 27 | endif() 28 | 29 | add_library(LVKVulkanApp VulkanApp.cpp VulkanApp.h) 30 | lvk_set_cxxstd(LVKVulkanApp 20) 31 | lvk_set_folder(LVKVulkanApp "LVK") 32 | target_link_libraries(LVKVulkanApp PUBLIC LVKLibrary) 33 | if(ANDROID) 34 | target_include_directories(LVKVulkanApp PUBLIC "${NDK_PATH}/sources/android/native_app_glue") 35 | endif() 36 | 37 | macro(ADD_DEMO app) 38 | if(NOT ANDROID) 39 | add_executable(${app} "${app}.cpp") 40 | lvk_set_cxxstd(${app} 20) 41 | lvk_set_folder(${app} ${PROJECT_NAME}) 42 | target_link_libraries(${app} PRIVATE LVKLibrary) 43 | target_link_libraries(${app} PRIVATE meshoptimizer) 44 | target_link_libraries(${app} PRIVATE fast_obj_lib) 45 | target_link_libraries(${app} PRIVATE LVKVulkanApp) 46 | if(UNIX AND NOT APPLE) 47 | target_link_libraries(${app} PRIVATE EGL) 48 | endif() 49 | target_link_libraries(${app} PRIVATE LVKstb) 50 | endif() 51 | 52 | # Android 53 | if(LVK_WITH_SAMPLES_ANDROID) 54 | if((NOT DEFINED ENV{ANDROID_NDK}) AND (NOT DEFINED ENV{NDK_ROOT})) 55 | message(FATAL_ERROR "ANDROID_NDK (or NDK_ROOT) environment variable is not set") 56 | endif() 57 | 58 | # One CMakeLists file is used for generation Android Project and for building native library. 59 | # The native library is built only if ANDROID is defined, otherwise Android project is generated. 60 | if(NOT ANDROID) 61 | file(COPY "${LVK_ROOT_DIR}/android/build.gradle" 62 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}") 63 | file(COPY "${LVK_ROOT_DIR}/android/gradle.properties" 64 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}") 65 | file(COPY "${LVK_ROOT_DIR}/android/gradlew" 66 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}") 67 | file(COPY "${LVK_ROOT_DIR}/android/gradlew.bat" 68 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}") 69 | file(COPY "${LVK_ROOT_DIR}/android/gradle/wrapper/gradle-wrapper.jar" 70 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}/gradle/wrapper") 71 | file(COPY "${LVK_ROOT_DIR}/android/gradle/wrapper/gradle-wrapper.properties" 72 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}/gradle/wrapper") 73 | 74 | set(APP_NAME ${app}) 75 | 76 | set(ABI_STRING "") 77 | foreach(str ${LVK_ANDROID_ABI}) 78 | if(NOT ${ABI_STRING} STRLESS "") 79 | set(ABI_STRING "${ABI_STRING}, ") 80 | endif() 81 | set(ABI_STRING "${ABI_STRING}'${str}'") 82 | endforeach() 83 | set(ANDROID_ABI_FILTERS ${ABI_STRING}) 84 | 85 | configure_file("${LVK_ROOT_DIR}/android/settings.gradle.in" 86 | "${CMAKE_BINARY_DIR}/android/${app}/settings.gradle") 87 | configure_file("${LVK_ROOT_DIR}/android/app/build.gradle.in" 88 | "${CMAKE_BINARY_DIR}/android/${app}/app/build.gradle") 89 | configure_file("${LVK_ROOT_DIR}/android/app/AndroidManifest.xml.in" 90 | "${CMAKE_BINARY_DIR}/android/${app}/app/src/main/AndroidManifest.xml") 91 | configure_file("${LVK_ROOT_DIR}/android/app/MainActivity.java.in" 92 | "${CMAKE_BINARY_DIR}/android/${app}/app/src/main/java/org/lvk/samples/MainActivity.java") 93 | 94 | if(LVK_WITH_ANDROID_VALIDATION) 95 | list(APPEND abdroid_abi arm64-v8a armeabi-v7a x86 x86_64) 96 | foreach(abi ${abdroid_abi}) 97 | string(FIND ${ABI_STRING} "${abi}" abi_substr_index) 98 | if(abi_substr_index GREATER -1) 99 | file(COPY "${LVK_ROOT_DIR}/third-party/deps/src/android-validation-layers/${abi}/libVkLayer_khronos_validation.so" 100 | DESTINATION "${CMAKE_BINARY_DIR}/android/${app}/app/src/main/jniLibs/${abi}") 101 | endif() 102 | endforeach() 103 | endif() 104 | endif() 105 | 106 | if(ANDROID) 107 | add_library(lvk_android_native_${app} SHARED 108 | "${app}.cpp" 109 | "${NDK_PATH}/sources/android/native_app_glue/android_native_app_glue.c") 110 | lvk_set_cxxstd(lvk_android_native_${app} 20) 111 | target_link_libraries(lvk_android_native_${app} PRIVATE 112 | LVKVulkanApp LVKLibrary LVKstb meshoptimizer fast_obj_lib android log) 113 | endif() 114 | endif() 115 | endmacro() 116 | 117 | macro(ADD_DEMO_SOURCES app srcs) 118 | if(ANDROID) 119 | target_sources(lvk_android_native_${app} PRIVATE ${srcs}) 120 | else() 121 | target_sources(${app} PRIVATE ${srcs}) 122 | endif() 123 | endmacro() 124 | 125 | macro(ADD_DEMO_LINK_LIBRARIES app libs) 126 | if(ANDROID) 127 | target_link_libraries(lvk_android_native_${app} PRIVATE ${libs}) 128 | else() 129 | target_link_libraries(${app} PRIVATE ${libs}) 130 | endif() 131 | endmacro() 132 | 133 | ADD_DEMO("001_HelloTriangle") 134 | ADD_DEMO("002_RenderToCubeMap") 135 | ADD_DEMO("003_RenderToCubeMapSinglePass") 136 | ADD_DEMO("004_YUV") 137 | if(WIN32 OR UNIX AND NOT (APPLE)) 138 | # Windows and Linux 139 | ADD_DEMO("005_MeshShaders") 140 | endif() 141 | ADD_DEMO("006_SwapchainHDR") 142 | ADD_DEMO("009_TriplanarMapping") 143 | if((WIN32 OR UNIX AND NOT (APPLE)) OR LVK_WITH_SAMPLES_ANDROID) 144 | # Windows, Linux and Android 145 | ADD_DEMO("RTX_001_Hello") 146 | ADD_DEMO("RTX_002_AO") 147 | ADD_DEMO("RTX_003_Pipeline") 148 | endif() 149 | 150 | ADD_DEMO("DEMO_001_SolarSystem") 151 | ADD_DEMO_LINK_LIBRARIES("DEMO_001_SolarSystem" LUtils) 152 | ADD_DEMO_LINK_LIBRARIES("DEMO_001_SolarSystem" ktx) 153 | ADD_DEMO_SOURCES("DEMO_001_SolarSystem" 154 | "${LVK_ROOT_DIR}/third-party/deps/src/3D-Graphics-Rendering-Cookbook/shared/UtilsCubemap.cpp") 155 | 156 | ADD_DEMO("Tiny_MeshLarge") 157 | ADD_DEMO_SOURCES("Tiny_MeshLarge" 158 | "${LVK_ROOT_DIR}/third-party/deps/src/3D-Graphics-Rendering-Cookbook/shared/UtilsCubemap.cpp") 159 | ADD_DEMO_SOURCES("Tiny_MeshLarge" 160 | "${LVK_ROOT_DIR}/third-party/deps/src/imgui/imgui_demo.cpp") 161 | ADD_DEMO_LINK_LIBRARIES("Tiny_MeshLarge" ktx) 162 | 163 | # Slang 164 | if(LVK_WITH_SLANG) 165 | ADD_DEMO("001_HelloTriangle_Slang") 166 | target_link_libraries(001_HelloTriangle_Slang PRIVATE slang-rt) 167 | target_link_libraries(001_HelloTriangle_Slang PRIVATE core) 168 | target_include_directories(001_HelloTriangle_Slang PRIVATE "${LVK_ROOT_DIR}/third-party/deps/src/slang/include") 169 | endif() 170 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # LightweightVK Samples 2 | 3 | ## 001_HelloTriangle 4 | 5 | ![lvk_android](../.github/samples/001_HelloTriangle.jpg) 6 | 7 | ## 002_RenderToCubeMap 8 | 9 | ![lvk_android](../.github/samples/002_RenderToCubeMap.jpg) 10 | 11 | ## 003_RenderToCubeMapSinglePass 12 | 13 | ![lvk_android](../.github/samples/003_RenderToCubeMapSinglePass.jpg) 14 | 15 | ## 004_YUV 16 | 17 | ![lvk_android](../.github/samples/004_YUV.jpg) 18 | 19 | ## 005_MeshShaders 20 | 21 | ![lvk_android](../.github/samples/005_MeshShaders.jpg) 22 | 23 | ## 009_TriplanarMapping 24 | 25 | ![lvk_android](../.github/samples/009_TriplanarMapping.jpg) 26 | 27 | ## DEMO_001_SolarSystem 28 | 29 | ![lvk_android](../.github/screenshot02.jpg) 30 | 31 | ## RTX_001_Hello 32 | 33 | ![lvk_android](../.github/samples/006_RayTracingHello.jpg) 34 | 35 | ## RTX_002_AO 36 | 37 | ![lvk_android](../.github/samples/007_RayTracingAO.jpg) 38 | 39 | ## RTX_003_Pipeline 40 | 41 | ![lvk_android](../.github/samples/008_RayTracingMesh.jpg) 42 | 43 | ## Tiny_MeshLarge 44 | 45 | ![lvk_android](../.github/samples/Tiny_MeshLarge.jpg) 46 | 47 | ### Performance measurements on Android 48 | 49 | ![lvk_android](../.github/samples/tiny_mesh_large_android.jpg) 50 | 51 | |Device|GPU|Resolution|MSAA|Texture quality|Frame time| 52 | |---|---|---|---|---|---| 53 | |Xiaomi 13T Pro|Immortalis-G715|2712x1220|8x|High (2048x2048)|26ms| 54 | |Xiaomi 13T Pro|Immortalis-G715|2712x1220|None|High (2048x2048)|16ms| 55 | |Xiaomi 13T Pro|Immortalis-G715|2712x1220|None|Low (256x256)|**14ms**| 56 | |Google Pixel 7 Pro|Mali-G710|3120x1440|8x|High (2048x2048)|85ms| 57 | |Google Pixel 7 Pro|Mali-G710|3120x1440|None|High (2048x2048)|62ms| 58 | |Google Pixel 7 Pro|Mali-G710|3120x1440|None|Low (256x256)|57ms| 59 | |Google Pixel 7 Pro|Mali-G710|2712x1220|8x|High (2048x2048)|80ms| 60 | |Google Pixel 7 Pro|Mali-G710|2712x1220|None|Low (256x256)|**54ms**| 61 | -------------------------------------------------------------------------------- /samples/RTX_001_Hello.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "VulkanApp.h" 9 | 10 | const char* codeRayGen = R"( 11 | #version 460 12 | #extension GL_EXT_ray_tracing : require 13 | #extension GL_EXT_buffer_reference : require 14 | #extension GL_EXT_nonuniform_qualifier : require 15 | 16 | layout (set = 0, binding = 2, rgba8) uniform image2D kTextures2DInOut[]; 17 | layout (set = 0, binding = 4) uniform accelerationStructureEXT kTLAS[]; 18 | 19 | layout(std430, buffer_reference) readonly buffer Camera { 20 | mat4 viewInverse; 21 | mat4 projInverse; 22 | }; 23 | 24 | layout(push_constant) uniform constants { 25 | Camera cam; 26 | uint outTexture; 27 | uint tlas; 28 | float time; 29 | }; 30 | 31 | layout(location = 0) rayPayloadEXT vec3 payload; 32 | 33 | const float tmin = 0.1; 34 | const float tmax = 500.0; 35 | 36 | void main() { 37 | vec2 pixelCenter = gl_LaunchIDEXT.xy + vec2(0.5); 38 | vec2 d = 2.0 * (pixelCenter / gl_LaunchSizeEXT.xy) - 1.0; 39 | 40 | vec4 origin = cam.viewInverse * vec4(0,0,0,1); 41 | vec4 target = cam.projInverse * vec4(d, 1, 1); 42 | vec4 direction = cam.viewInverse * vec4(normalize(target.xyz), 0); 43 | 44 | payload = vec3(0.0); 45 | 46 | traceRayEXT(kTLAS[tlas], gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); 47 | 48 | imageStore(kTextures2DInOut[outTexture], ivec2(gl_LaunchIDEXT.xy), vec4(payload, 1.0)); 49 | } 50 | )"; 51 | 52 | const char* codeMiss = R"( 53 | #version 460 54 | #extension GL_EXT_ray_tracing : require 55 | 56 | layout(location = 0) rayPayloadInEXT vec3 payload; 57 | 58 | layout(push_constant) uniform constants { 59 | vec2 cam; // just an opaque buffer reference - no access required 60 | uint outTexture; 61 | uint tlas; 62 | float time; 63 | }; 64 | 65 | vec2 rotate(vec2 v, float angle) { 66 | mat2 r = mat2(cos(angle), -sin(angle), 67 | sin(angle), cos(angle)); 68 | return r * (v-0.5*gl_LaunchSizeEXT.xy); 69 | } 70 | 71 | void main() { 72 | vec2 uv = rotate(gl_LaunchIDEXT.xy, 0.2 * time); 73 | vec2 pos = floor(uv / 64.0); 74 | payload = vec3(mod(pos.x + mod(pos.y, 2.0), 2.0)); 75 | })"; 76 | 77 | const char* codeClosestHit = R"( 78 | #version 460 79 | #extension GL_EXT_ray_tracing : require 80 | 81 | layout(location = 0) rayPayloadInEXT vec3 payload; 82 | hitAttributeEXT vec2 attribs; 83 | 84 | void main() { 85 | const vec3 baryCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); 86 | payload = baryCoords; 87 | } 88 | )"; 89 | 90 | lvk::IContext* ctx_ = nullptr; 91 | 92 | struct Resources { 93 | lvk::Holder BLAS; 94 | lvk::Holder TLAS; 95 | 96 | lvk::Holder vertexBuffer; 97 | lvk::Holder indexBuffer; 98 | lvk::Holder instancesBuffer; 99 | 100 | lvk::Holder storageImage; 101 | 102 | lvk::Holder raygen_; 103 | lvk::Holder miss_; 104 | lvk::Holder hit_; 105 | 106 | lvk::Holder ubo; 107 | 108 | lvk::Holder pipeline; 109 | } res; 110 | 111 | void createBottomLevelAccelerationStructure() { 112 | struct Vertex { 113 | float pos[3]; 114 | }; 115 | const float t = (1.0f + sqrtf(5.0f)) / 2.0f; 116 | const Vertex vertices[] = { 117 | {-1, t, 0}, 118 | {1, t, 0}, 119 | {-1, -t, 0}, 120 | {1, -t, 0}, 121 | 122 | {0, -1, t}, 123 | {0, 1, t}, 124 | {0, -1, -t}, 125 | {0, 1, -t}, 126 | 127 | {t, 0, -1}, 128 | {t, 0, 1}, 129 | {-t, 0, -1}, 130 | {-t, 0, 1}, 131 | }; 132 | 133 | const uint32_t indices[] = {0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 134 | 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1}; 135 | 136 | const glm::mat3x4 transformMatrix(1.0f); 137 | 138 | res.vertexBuffer = ctx_->createBuffer({ 139 | .usage = lvk::BufferUsageBits_AccelStructBuildInputReadOnly, 140 | .storage = lvk::StorageType_HostVisible, 141 | .size = sizeof(vertices), 142 | .data = vertices, 143 | }); 144 | res.indexBuffer = ctx_->createBuffer({ 145 | .usage = lvk::BufferUsageBits_AccelStructBuildInputReadOnly, 146 | .storage = lvk::StorageType_HostVisible, 147 | .size = sizeof(indices), 148 | .data = indices, 149 | }); 150 | lvk::Holder transformBuffer = ctx_->createBuffer({ 151 | .usage = lvk::BufferUsageBits_AccelStructBuildInputReadOnly, 152 | .storage = lvk::StorageType_HostVisible, 153 | .size = sizeof(glm::mat3x4), 154 | .data = &transformMatrix, 155 | }); 156 | 157 | res.BLAS = ctx_->createAccelerationStructure({ 158 | .type = lvk::AccelStructType_BLAS, 159 | .geometryType = lvk::AccelStructGeomType_Triangles, 160 | .vertexFormat = lvk::VertexFormat::Float3, 161 | .vertexBuffer = res.vertexBuffer, 162 | .numVertices = (uint32_t)LVK_ARRAY_NUM_ELEMENTS(vertices), 163 | .indexFormat = lvk::IndexFormat_UI32, 164 | .indexBuffer = res.indexBuffer, 165 | .transformBuffer = transformBuffer, 166 | .buildRange = {.primitiveCount = (uint32_t)LVK_ARRAY_NUM_ELEMENTS(indices) / 3}, 167 | .debugName = "BLAS", 168 | }); 169 | } 170 | 171 | void createTopLevelAccelerationStructure() { 172 | const lvk::AccelStructInstance instance{ 173 | // clang-format off 174 | .transform = {.matrix = {1.0f, 0.0f, 0.0f, 0.0f, 175 | 0.0f, 1.0f, 0.0f, 0.0f, 176 | 0.0f, 0.0f, 1.0f, 0.0f}}, 177 | // clang-format on 178 | .instanceCustomIndex = 0, 179 | .mask = 0xff, 180 | .instanceShaderBindingTableRecordOffset = 0, 181 | .flags = lvk::AccelStructInstanceFlagBits_TriangleFacingCullDisable, 182 | .accelerationStructureReference = ctx_->gpuAddress(res.BLAS), 183 | }; 184 | 185 | // Buffer for instance data 186 | res.instancesBuffer = ctx_->createBuffer(lvk::BufferDesc{ 187 | .usage = lvk::BufferUsageBits_AccelStructBuildInputReadOnly, 188 | .storage = lvk::StorageType_HostVisible, 189 | .size = sizeof(lvk::AccelStructInstance), 190 | .data = &instance, 191 | .debugName = "instanceBuffer", 192 | }); 193 | 194 | res.TLAS = ctx_->createAccelerationStructure({ 195 | .type = lvk::AccelStructType_TLAS, 196 | .geometryType = lvk::AccelStructGeomType_Instances, 197 | .instancesBuffer = res.instancesBuffer, 198 | .buildRange = {.primitiveCount = 1}, 199 | .buildFlags = lvk::AccelStructBuildFlagBits_PreferFastTrace | lvk::AccelStructBuildFlagBits_AllowUpdate, 200 | }); 201 | } 202 | 203 | VULKAN_APP_MAIN { 204 | const VulkanAppConfig cfg{ 205 | .width = -80, 206 | .height = -80, 207 | .resizable = true, 208 | }; 209 | VULKAN_APP_DECLARE(app, cfg); 210 | 211 | ctx_ = app.ctx_.get(); 212 | 213 | createBottomLevelAccelerationStructure(); 214 | createTopLevelAccelerationStructure(); 215 | 216 | const struct UniformData { 217 | glm::mat4 viewInverse; 218 | glm::mat4 projInverse; 219 | } uniformData = { 220 | .viewInverse = glm::inverse(glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -5.0f))), 221 | .projInverse = glm::inverse(glm::perspective(glm::radians(60.0f), (float)app.width_ / (float)app.height_, 0.1f, 1000.0f)), 222 | }; 223 | 224 | res.ubo = ctx_->createBuffer(lvk::BufferDesc{ 225 | .usage = lvk::BufferUsageBits_Storage, 226 | .storage = lvk::StorageType_Device, 227 | .size = sizeof(uniformData), 228 | .data = &uniformData, 229 | .debugName = "cameraBuffer", 230 | }); 231 | 232 | res.storageImage = ctx_->createTexture( 233 | lvk::TextureDesc{ 234 | .type = lvk::TextureType_2D, 235 | .format = lvk::Format_BGRA_UN8, 236 | .dimensions = {(uint32_t)app.width_, (uint32_t)app.height_, 1u}, 237 | .numLayers = 1, 238 | .numSamples = 1, 239 | .usage = lvk::TextureUsageBits_Storage, 240 | }, 241 | "storageImage"); 242 | 243 | res.raygen_ = ctx_->createShaderModule({codeRayGen, lvk::Stage_RayGen, "Shader Module: main (raygen)"}); 244 | res.miss_ = ctx_->createShaderModule({codeMiss, lvk::Stage_Miss, "Shader Module: main (miss)"}); 245 | res.hit_ = ctx_->createShaderModule({codeClosestHit, lvk::Stage_ClosestHit, "Shader Module: main (closesthit)"}); 246 | 247 | res.pipeline = ctx_->createRayTracingPipeline(lvk::RayTracingPipelineDesc{ 248 | .smRayGen = {lvk::ShaderModuleHandle(res.raygen_)}, 249 | .smClosestHit = {lvk::ShaderModuleHandle(res.hit_)}, 250 | .smMiss = {lvk::ShaderModuleHandle(res.miss_)}, 251 | }); 252 | 253 | app.run([&](uint32_t width, uint32_t height, float aspectRatio, float deltaSeconds) { 254 | lvk::ICommandBuffer& buffer = ctx_->acquireCommandBuffer(); 255 | 256 | const glm::mat3x4 transformMatrix = glm::rotate(glm::mat4(1.0f), (float)glfwGetTime(), glm::vec3(1, 1, 1)); 257 | ctx_->upload(res.instancesBuffer, &transformMatrix, sizeof(transformMatrix), offsetof(lvk::AccelStructInstance, transform)); 258 | 259 | struct { 260 | uint64_t camBuffer; 261 | uint32_t outTexture; 262 | uint32_t tlas; 263 | float time; 264 | } pc = { 265 | .camBuffer = ctx_->gpuAddress(res.ubo), 266 | .outTexture = res.storageImage.index(), 267 | .tlas = res.TLAS.index(), 268 | .time = (float)glfwGetTime(), 269 | }; 270 | 271 | buffer.cmdUpdateTLAS(res.TLAS, res.instancesBuffer); 272 | buffer.cmdBindRayTracingPipeline(res.pipeline); 273 | buffer.cmdPushConstants(pc); 274 | buffer.cmdTraceRays(width, height, 1, {.textures = {lvk::TextureHandle(res.storageImage)}}); 275 | buffer.cmdCopyImage(res.storageImage, ctx_->getCurrentSwapchainTexture(), ctx_->getDimensions(ctx_->getCurrentSwapchainTexture())); 276 | 277 | ctx_->submit(buffer, ctx_->getCurrentSwapchainTexture()); 278 | }); 279 | 280 | // destroy all the Vulkan stuff before closing the window 281 | res = {}; 282 | 283 | VULKAN_APP_EXIT(); 284 | } 285 | -------------------------------------------------------------------------------- /samples/VulkanApp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | // Based on https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook-Second-Edition/blob/main/shared/VulkanApp.cpp 9 | 10 | #include "VulkanApp.h" 11 | 12 | #include 13 | #include 14 | 15 | #if defined(ANDROID) 16 | #include 17 | #include 18 | 19 | double glfwGetTime() { 20 | timespec t = {0, 0}; 21 | clock_gettime(CLOCK_MONOTONIC, &t); 22 | return (double)t.tv_sec + 1.0e-9 * t.tv_nsec; 23 | } 24 | 25 | static const char* cmdToString(int32_t cmd) { 26 | #define CMD(cmd) \ 27 | case cmd: \ 28 | return #cmd 29 | switch (cmd) { 30 | CMD(APP_CMD_INPUT_CHANGED); 31 | CMD(APP_CMD_INIT_WINDOW); 32 | CMD(APP_CMD_TERM_WINDOW); 33 | CMD(APP_CMD_WINDOW_RESIZED); 34 | CMD(APP_CMD_WINDOW_REDRAW_NEEDED); 35 | CMD(APP_CMD_CONTENT_RECT_CHANGED); 36 | CMD(APP_CMD_GAINED_FOCUS); 37 | CMD(APP_CMD_LOST_FOCUS); 38 | CMD(APP_CMD_CONFIG_CHANGED); 39 | CMD(APP_CMD_LOW_MEMORY); 40 | CMD(APP_CMD_START); 41 | CMD(APP_CMD_RESUME); 42 | CMD(APP_CMD_SAVE_STATE); 43 | CMD(APP_CMD_PAUSE); 44 | CMD(APP_CMD_STOP); 45 | CMD(APP_CMD_DESTROY); 46 | } 47 | #undef CMD 48 | return ""; 49 | } 50 | 51 | extern "C" { 52 | 53 | static void handle_cmd(android_app* androidApp, int32_t cmd) { 54 | VulkanApp* app = (VulkanApp*)androidApp->userData; 55 | 56 | LLOGD("handle_cmd(%s)", cmdToString(cmd)); 57 | 58 | switch (cmd) { 59 | case APP_CMD_INIT_WINDOW: 60 | if (androidApp->window) { 61 | app->width_ = ANativeWindow_getWidth(androidApp->window) / app->cfg_.framebufferScalar; 62 | app->height_ = ANativeWindow_getHeight(androidApp->window) / app->cfg_.framebufferScalar; 63 | if (!app->ctx_) 64 | app->ctx_ = lvk::createVulkanContextWithSwapchain(androidApp->window, app->width_, app->height_, app->cfg_.contextConfig); 65 | app->canRender_ = true; 66 | } 67 | return; 68 | case APP_CMD_GAINED_FOCUS: 69 | app->canRender_ = app->ctx_ != nullptr; 70 | return; 71 | case APP_CMD_LOST_FOCUS: 72 | case APP_CMD_TERM_WINDOW: 73 | app->canRender_ = false; 74 | return; 75 | case APP_CMD_DESTROY: 76 | return; 77 | } 78 | } 79 | 80 | static void resize_callback(ANativeActivity* activity, ANativeWindow* window) { 81 | LLOGD("resize_callback()"); 82 | 83 | VulkanApp* app = (VulkanApp*)activity->instance; 84 | const int w = ANativeWindow_getWidth(window) / app->cfg_.framebufferScalar; 85 | const int h = ANativeWindow_getHeight(window) / app->cfg_.framebufferScalar; 86 | if (app->width_ != w || app->height_ != h) { 87 | app->width_ = w; 88 | app->height_ = h; 89 | if (app->ctx_) { 90 | app->ctx_->recreateSwapchain(w, h); 91 | app->depthTexture_.reset(); 92 | LLOGD("Swapchain recreated"); 93 | } 94 | } 95 | 96 | LLOGD("resize_callback()<-"); 97 | } 98 | 99 | } // extern "C" 100 | #endif // ANDROID 101 | 102 | #if defined(ANDROID) 103 | VulkanApp::VulkanApp(android_app* androidApp, const VulkanAppConfig& cfg) : androidApp_(androidApp), cfg_(cfg) { 104 | #else 105 | VulkanApp::VulkanApp(const VulkanAppConfig& cfg) : cfg_(cfg) { 106 | #endif // ANDROID 107 | minilog::initialize(nullptr, {.threadNames = false}); 108 | 109 | // we use minilog 110 | fpsCounter_.printFPS_ = false; 111 | 112 | // find the content folder 113 | { 114 | using namespace std::filesystem; 115 | #if defined(ANDROID) 116 | if (const char* externalStorage = std::getenv("EXTERNAL_STORAGE")) { 117 | folderThirdParty_ = (path(externalStorage) / "LVK" / "deps" / "src").string() + "/"; 118 | folderContentRoot_ = (path(externalStorage) / "LVK" / "content").string() + "/"; 119 | } 120 | #else 121 | path subdir("third-party/content/"); 122 | path dir = current_path(); 123 | // find the content somewhere above our current build directory 124 | while (dir != current_path().root_path() && !exists(dir / subdir)) { 125 | dir = dir.parent_path(); 126 | } 127 | if (!exists(dir / subdir)) { 128 | LLOGW("Cannot find the content directory. Run `deploy_content.py` before running this app."); 129 | LVK_ASSERT(false); 130 | return; 131 | } 132 | folderThirdParty_ = (dir / path("third-party/deps/src/")).string(); 133 | folderContentRoot_ = (dir / subdir).string(); 134 | #endif // ANDROID 135 | } 136 | 137 | #if defined(ANDROID) 138 | androidApp_->userData = this; 139 | androidApp_->onAppCmd = handle_cmd; 140 | 141 | int events = 0; 142 | android_poll_source* source = nullptr; 143 | 144 | LLOGD("Waiting for an Android window..."); 145 | 146 | while (!androidApp_->destroyRequested && !ctx_) { 147 | // poll until a Window is created 148 | if (ALooper_pollOnce(0, nullptr, &events, (void**)&source) >= 0) { 149 | if (source) 150 | source->process(androidApp_, source); 151 | } 152 | } 153 | 154 | LLOGD("...Android window ready!"); 155 | 156 | if (!ctx_) 157 | return; 158 | 159 | androidApp_->activity->instance = this; 160 | androidApp_->activity->callbacks->onNativeWindowResized = resize_callback; 161 | #else 162 | width_ = cfg.width; 163 | height_ = cfg.height; 164 | 165 | window_ = lvk::initWindow("Simple example", width_, height_, cfg.resizable); 166 | 167 | ctx_ = lvk::createVulkanContextWithSwapchain(window_, width_, height_, cfg.contextConfig); 168 | #endif // ANDROID 169 | 170 | imgui_ = std::make_unique( 171 | *ctx_, (folderThirdParty_ + "3D-Graphics-Rendering-Cookbook/data/OpenSans-Light.ttf").c_str(), 30.0f); 172 | #if !defined(ANDROID) 173 | glfwSetWindowUserPointer(window_, this); 174 | 175 | glfwSetFramebufferSizeCallback(window_, [](GLFWwindow* window, int width, int height) { 176 | VulkanApp* app = (VulkanApp*)glfwGetWindowUserPointer(window); 177 | if (app->width_ == width && app->height_ == height) 178 | return; 179 | app->width_ = width; 180 | app->height_ = height; 181 | app->ctx_->recreateSwapchain(width, height); 182 | app->depthTexture_.reset(); 183 | }); 184 | glfwSetMouseButtonCallback(window_, [](GLFWwindow* window, int button, int action, int mods) { 185 | VulkanApp* app = (VulkanApp*)glfwGetWindowUserPointer(window); 186 | if (button == GLFW_MOUSE_BUTTON_LEFT) { 187 | app->mouseState_.pressedLeft = action == GLFW_PRESS; 188 | } 189 | double xpos, ypos; 190 | glfwGetCursorPos(window, &xpos, &ypos); 191 | const ImGuiMouseButton_ imguiButton = (button == GLFW_MOUSE_BUTTON_LEFT) 192 | ? ImGuiMouseButton_Left 193 | : (button == GLFW_MOUSE_BUTTON_RIGHT ? ImGuiMouseButton_Right : ImGuiMouseButton_Middle); 194 | ImGuiIO& io = ImGui::GetIO(); 195 | io.MousePos = ImVec2((float)xpos, (float)ypos); 196 | io.MouseDown[imguiButton] = action == GLFW_PRESS; 197 | for (auto& cb : app->callbacksMouseButton) { 198 | cb(window, button, action, mods); 199 | } 200 | }); 201 | glfwSetScrollCallback(window_, [](GLFWwindow* window, double dx, double dy) { 202 | ImGuiIO& io = ImGui::GetIO(); 203 | io.MouseWheelH = (float)dx; 204 | io.MouseWheel = (float)dy; 205 | }); 206 | glfwSetCursorPosCallback(window_, [](GLFWwindow* window, double x, double y) { 207 | VulkanApp* app = (VulkanApp*)glfwGetWindowUserPointer(window); 208 | int width, height; 209 | glfwGetFramebufferSize(window, &width, &height); 210 | ImGui::GetIO().MousePos = ImVec2(x, y); 211 | app->mouseState_.pos.x = static_cast(x / width); 212 | app->mouseState_.pos.y = 1.0f - static_cast(y / height); 213 | }); 214 | glfwSetKeyCallback(window_, [](GLFWwindow* window, int key, int scancode, int action, int mods) { 215 | VulkanApp* app = (VulkanApp*)glfwGetWindowUserPointer(window); 216 | const bool pressed = action != GLFW_RELEASE; 217 | if (key == GLFW_KEY_ESCAPE && pressed) 218 | glfwSetWindowShouldClose(window, GLFW_TRUE); 219 | if (key == GLFW_KEY_W) 220 | app->positioner_.movement_.forward_ = pressed; 221 | if (key == GLFW_KEY_S) 222 | app->positioner_.movement_.backward_ = pressed; 223 | if (key == GLFW_KEY_A) 224 | app->positioner_.movement_.left_ = pressed; 225 | if (key == GLFW_KEY_D) 226 | app->positioner_.movement_.right_ = pressed; 227 | if (key == GLFW_KEY_1) 228 | app->positioner_.movement_.up_ = pressed; 229 | if (key == GLFW_KEY_2) 230 | app->positioner_.movement_.down_ = pressed; 231 | 232 | app->positioner_.movement_.fastSpeed_ = (mods & GLFW_MOD_SHIFT) != 0; 233 | 234 | if (key == GLFW_KEY_SPACE) { 235 | app->positioner_.lookAt(app->cfg_.initialCameraPos, app->cfg_.initialCameraTarget, app->cfg_.initialCameraUpVector); 236 | } 237 | for (auto& cb : app->callbacksKey) { 238 | cb(window, key, scancode, action, mods); 239 | } 240 | }); 241 | #endif // !ANDROID 242 | } 243 | 244 | VulkanApp::~VulkanApp() { 245 | imgui_ = nullptr; 246 | depthTexture_ = nullptr; 247 | ctx_ = nullptr; 248 | #if !defined(ANDROID) 249 | glfwDestroyWindow(window_); 250 | glfwTerminate(); 251 | #endif // !ANDROID 252 | } 253 | 254 | lvk::Format VulkanApp::getDepthFormat() const { 255 | return ctx_->getFormat(getDepthTexture()); 256 | } 257 | 258 | lvk::TextureHandle VulkanApp::getDepthTexture() const { 259 | if (depthTexture_.empty()) { 260 | depthTexture_ = ctx_->createTexture({ 261 | .type = lvk::TextureType_2D, 262 | .format = lvk::Format_Z_F32, 263 | .dimensions = {(uint32_t)width_, (uint32_t)height_}, 264 | .usage = lvk::TextureUsageBits_Attachment, 265 | .debugName = "Depth buffer", 266 | }); 267 | } 268 | 269 | return depthTexture_; 270 | } 271 | 272 | void VulkanApp::run(DrawFrameFunc drawFrame) { 273 | double timeStamp = glfwGetTime(); 274 | float deltaSeconds = 0.0f; 275 | 276 | #if defined(ANDROID) 277 | while (!androidApp_->destroyRequested) { 278 | #else 279 | while (!glfwWindowShouldClose(window_)) { 280 | #endif // !ANDROID 281 | if (fpsCounter_.tick(deltaSeconds)) { 282 | LLOGL("FPS: %.1f\n", fpsCounter_.getFPS()); 283 | } 284 | const double newTimeStamp = glfwGetTime(); 285 | deltaSeconds = static_cast(newTimeStamp - timeStamp); 286 | timeStamp = newTimeStamp; 287 | 288 | #if defined(ANDROID) 289 | android_poll_source* source = nullptr; 290 | const int result = ALooper_pollOnce(canRender_ ? 0 : -1, nullptr, nullptr, (void**)&source); 291 | 292 | if (result == ALOOPER_POLL_ERROR) { 293 | LLOGW("ALooper_pollOnce() returned an error"); 294 | break; 295 | } 296 | 297 | if (source) { 298 | source->process(androidApp_, source); 299 | } 300 | #else 301 | 302 | #if defined(__APPLE__) 303 | // a hacky workaround for retina displays 304 | glfwGetWindowSize(window_, &width_, &height_); 305 | #else 306 | glfwGetFramebufferSize(window_, &width_, &height_); 307 | #endif // __APPLE__ 308 | 309 | glfwPollEvents(); 310 | #endif // ANDROID 311 | 312 | if (!width_ || !height_) 313 | continue; 314 | const float ratio = width_ / (float)height_; 315 | 316 | positioner_.update(deltaSeconds, mouseState_.pos, ImGui::GetIO().WantCaptureMouse ? false : mouseState_.pressedLeft); 317 | 318 | if (ctx_ && canRender_) { 319 | drawFrame((uint32_t)width_, (uint32_t)height_, ratio, deltaSeconds); 320 | } 321 | } 322 | 323 | LLOGD("Terminating Android app..."); 324 | } 325 | 326 | void VulkanApp::drawFPS() { 327 | if (const ImGuiViewport* v = ImGui::GetMainViewport()) { 328 | ImGui::SetNextWindowPos({v->WorkPos.x + v->WorkSize.x - 15.0f, v->WorkPos.y + 15.0f}, ImGuiCond_Always, {1.0f, 0.0f}); 329 | } 330 | ImGui::SetNextWindowBgAlpha(0.30f); 331 | ImGui::SetNextWindowSize(ImVec2(ImGui::CalcTextSize("FPS : _______").x, 0)); 332 | if (ImGui::Begin("##FPS", 333 | nullptr, 334 | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | 335 | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove)) { 336 | ImGui::Text("FPS : %i", (int)fpsCounter_.getFPS()); 337 | ImGui::Text("Ms : %.1f", fpsCounter_.getFPS() > 0 ? 1000.0 / fpsCounter_.getFPS() : 0); 338 | } 339 | ImGui::End(); 340 | } 341 | -------------------------------------------------------------------------------- /samples/VulkanApp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LightweightVK 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | // Based on https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook-Second-Edition/blob/main/shared/VulkanApp.h 9 | 10 | #pragma once 11 | 12 | #if !defined(_USE_MATH_DEFINES) 13 | #define _USE_MATH_DEFINES 14 | #endif // _USE_MATH_DEFINES 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | // clang-format off 23 | #if defined(ANDROID) 24 | # include 25 | # include 26 | # include 27 | double glfwGetTime(); 28 | #else 29 | # include 30 | #endif 31 | // clang-format on 32 | 33 | #define GLM_ENABLE_EXPERIMENTAL 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | // clang-format off 40 | #if defined(ANDROID) 41 | # define VULKAN_APP_MAIN void android_main(android_app* androidApp) 42 | # define VULKAN_APP_DECLARE(app, config) VulkanApp app(androidApp, config) 43 | # define VULKAN_APP_EXIT() return 44 | #else 45 | # define VULKAN_APP_MAIN int main(int argc, char* argv[]) 46 | # define VULKAN_APP_DECLARE(app, config) VulkanApp app(config) 47 | # define VULKAN_APP_EXIT() return 0 48 | #endif 49 | // clang-format on 50 | 51 | using glm::mat3; 52 | using glm::mat4; 53 | using glm::vec2; 54 | using glm::vec3; 55 | using glm::vec4; 56 | 57 | using DrawFrameFunc = std::function; 58 | 59 | struct VulkanAppConfig { 60 | int width = -95; // 95% horizontally 61 | int height = -90; // 90% vertically 62 | #if defined(ANDROID) 63 | int framebufferScalar = 2; 64 | #else 65 | int framebufferScalar = 1; 66 | #endif // ANDROID 67 | bool resizable = false; 68 | vec3 initialCameraPos = vec3(0.0f, 0.0f, -2.5f); 69 | vec3 initialCameraTarget = vec3(0.0f, 0.0f, 0.0f); 70 | vec3 initialCameraUpVector = vec3(0.0f, 1.0f, 0.0f); 71 | lvk::ContextConfig contextConfig; 72 | }; 73 | 74 | class VulkanApp { 75 | public: 76 | #if defined(ANDROID) 77 | explicit VulkanApp(android_app* androidApp, const VulkanAppConfig& cfg = {}); 78 | #else 79 | explicit VulkanApp(const VulkanAppConfig& cfg = {}); 80 | #endif // ANDROID 81 | virtual ~VulkanApp(); 82 | 83 | virtual void run(DrawFrameFunc drawFrame); 84 | virtual void drawFPS(); 85 | 86 | lvk::Format getDepthFormat() const; 87 | lvk::TextureHandle getDepthTexture() const; 88 | #if !defined(ANDROID) 89 | void addMouseButtonCallback(GLFWmousebuttonfun cb) { 90 | callbacksMouseButton.push_back(cb); 91 | } 92 | void addKeyCallback(GLFWkeyfun cb) { 93 | callbacksKey.push_back(cb); 94 | } 95 | #endif // ANDROID 96 | public: 97 | std::string folderThirdParty_; 98 | std::string folderContentRoot_; 99 | int width_ = 0; 100 | int height_ = 0; 101 | bool canRender_ = true; 102 | #if defined(ANDROID) 103 | android_app* androidApp_ = nullptr; 104 | #else 105 | GLFWwindow* window_ = nullptr; 106 | #endif // ANDROID 107 | std::unique_ptr ctx_; 108 | mutable lvk::Holder depthTexture_; 109 | FramesPerSecondCounter fpsCounter_ = FramesPerSecondCounter(0.5f); 110 | std::unique_ptr imgui_; 111 | 112 | const VulkanAppConfig cfg_ = {}; 113 | 114 | CameraPositioner_FirstPerson positioner_ = {cfg_.initialCameraPos, cfg_.initialCameraTarget, cfg_.initialCameraUpVector}; 115 | Camera camera_ = Camera(positioner_); 116 | 117 | struct MouseState { 118 | vec2 pos = vec2(0.0f); 119 | bool pressedLeft = false; 120 | } mouseState_; 121 | 122 | protected: 123 | #if !defined(ANDROID) 124 | std::vector callbacksMouseButton; 125 | std::vector callbacksKey; 126 | #endif // ANDROID 127 | }; 128 | -------------------------------------------------------------------------------- /third-party/.gitignore: -------------------------------------------------------------------------------- 1 | content/ 2 | deps/ 3 | .bootstrap-content.json 4 | .bootstrap-deps.json 5 | -------------------------------------------------------------------------------- /third-party/bootstrap-content.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "bistro/PropTextures", 4 | "source": { 5 | "type": "archive", 6 | "url": "https://casual-effects.com/g3d/data10/research/model/bistro/PropTextures", 7 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 8 | } 9 | }, 10 | { 11 | "name": "bistro/OtherTextures", 12 | "source": { 13 | "type": "archive", 14 | "url": "https://casual-effects.com/g3d/data10/research/model/bistro/OtherTextures", 15 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 16 | } 17 | }, 18 | { 19 | "name": "bistro/BuildingTextures", 20 | "source": { 21 | "type": "archive", 22 | "url": "https://casual-effects.com/g3d/data10/research/model/bistro/BuildingTextures", 23 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 24 | } 25 | }, 26 | { 27 | "name": "bistro/Interior", 28 | "source": { 29 | "type": "archive", 30 | "url": "https://casual-effects.com/g3d/data10/research/model/bistro/Interior.zip", 31 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 32 | } 33 | }, 34 | { 35 | "name": "bistro/Exterior", 36 | "source": { 37 | "type": "archive", 38 | "url": "https://casual-effects.com/g3d/data10/research/model/bistro/Exterior.zip", 39 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 40 | } 41 | }, 42 | { 43 | "name": "skybox_hdr", 44 | "source": { 45 | "type": "sourcefile", 46 | "url": "https://github.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/raw/master/data/immenstadter_horn_2k.hdr", 47 | "user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 48 | } 49 | }, 50 | { 51 | "name": "glTF-Sample-Models", 52 | "source": { 53 | "type": "git", 54 | "url": "https://github.com/KhronosGroup/glTF-Sample-Models.git", 55 | "revision": "db9ff67c1116cfe28eb36320916bccd8c4127cc1" 56 | } 57 | }, 58 | { 59 | "name": "igl-samples", 60 | "source": { 61 | "type": "sourcefile", 62 | "url": "https://github.com/facebook/igl/raw/4e066d19b3461c05417542c14fa8ea5f64a70964/shell/resources/images/output_frame_900.420p.yuv", 63 | "sha1": "8f5648ed4b1503d17ff4a6b6713464e0bfdb7386" 64 | } 65 | }, 66 | { 67 | "name": "igl-samples", 68 | "source": { 69 | "type": "sourcefile", 70 | "url": "https://github.com/facebook/igl/raw/4e066d19b3461c05417542c14fa8ea5f64a70964/shell/resources/images/output_frame_900.nv12.yuv", 71 | "sha1": "afef6f0aa6a5b7e12fd79f87b0ae940113623fd6" 72 | } 73 | }, 74 | { 75 | "name": "sponza", 76 | "source": { 77 | "type": "git", 78 | "url": "https://github.com/jimmiebergmann/Sponza.git", 79 | "revision": "222338979d32f4f4818466291bdbc29f192b86ba" 80 | } 81 | }, 82 | { 83 | "name": "solarsystem", 84 | "source": { 85 | "type": "sourcefile", 86 | "url": "https://www.solarsystemscope.com/textures/download/2k_mercury.jpg", 87 | "sha1": "6f832f061a9c5689a0bd5bd68ff29f49ffc65d29" 88 | } 89 | }, 90 | { 91 | "name": "solarsystem", 92 | "source": { 93 | "type": "sourcefile", 94 | "url": "https://www.solarsystemscope.com/textures/download/2k_venus_surface.jpg", 95 | "sha1": "1549a36da4bb9bb6c99430dc1a8e7cc7ba07df96" 96 | } 97 | }, 98 | { 99 | "name": "solarsystem", 100 | "source": { 101 | "type": "sourcefile", 102 | "url": "https://www.solarsystemscope.com/textures/download/2k_mars.jpg", 103 | "sha1": "4dbaccfc147c10b70b9905683498fcd24a11af8e" 104 | } 105 | }, 106 | { 107 | "name": "solarsystem", 108 | "source": { 109 | "type": "sourcefile", 110 | "url": "https://www.solarsystemscope.com/textures/download/2k_jupiter.jpg", 111 | "sha1": "11ebb1d50e85505005cd5f13309c0bd6ee16b268" 112 | } 113 | }, 114 | { 115 | "name": "solarsystem", 116 | "source": { 117 | "type": "sourcefile", 118 | "url": "https://www.solarsystemscope.com/textures/download/2k_saturn.jpg", 119 | "sha1": "26b1ded029fabdd7e98dc607ecc17abcc96dd03e" 120 | } 121 | }, 122 | { 123 | "name": "solarsystem", 124 | "source": { 125 | "type": "sourcefile", 126 | "url": "https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/i/c1827fcd-9fde-48eb-8da3-489166bd8542/d6xt63j-169c5f5a-9033-417c-a48a-496ec8d05f77.jpg", 127 | "sha1": "b5f95fa2480822a5b7058ae30b08d532d4374825" 128 | }, 129 | "postprocess": { 130 | "type": "script", 131 | "file": "saturn_rings.py" 132 | } 133 | }, 134 | { 135 | "name": "solarsystem", 136 | "source": { 137 | "type": "sourcefile", 138 | "url": "https://www.solarsystemscope.com/textures/download/2k_uranus.jpg", 139 | "sha1": "39fba155ca70e51da9224b9653cbc598b0c41be1" 140 | } 141 | }, 142 | { 143 | "name": "solarsystem", 144 | "source": { 145 | "type": "sourcefile", 146 | "url": "https://www.solarsystemscope.com/textures/download/2k_neptune.jpg", 147 | "sha1": "4ff333c5cccc045dca033bdf1ae24719b26f053b" 148 | } 149 | }, 150 | { 151 | "name": "solarsystem", 152 | "source": { 153 | "type": "sourcefile", 154 | "url": "https://www.solarsystemscope.com/textures/download/2k_earth_daymap.jpg", 155 | "sha1": "67397b2a33b1c6673ea50af3bfda4244b078b820" 156 | } 157 | }, 158 | { 159 | "name": "solarsystem", 160 | "source": { 161 | "type": "sourcefile", 162 | "url": "https://www.solarsystemscope.com/textures/download/2k_sun.jpg", 163 | "sha1": "0833ed7cb52d4e0e3d7174e1d26f73d21fcb8a10" 164 | } 165 | }, 166 | { 167 | "name": "solarsystem", 168 | "source": { 169 | "type": "sourcefile", 170 | "url": "https://corporate.dukehealth.org/sites/default/files/api/images/aspect:1.0-width:768-position:center/ThinkstockPhotos-517462556.web_.jpg", 171 | "sha1": "68ae86dbbe76700d863591b60d222fc603cd8cf1", 172 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 173 | }, 174 | "postprocess": { 175 | "type": "script", 176 | "file": "sun_corona.py" 177 | } 178 | }, 179 | { 180 | "name": "solarsystem", 181 | "source": { 182 | "type": "sourcefile", 183 | "url": "https://www.solarsystemscope.com/textures/download/2k_moon.jpg", 184 | "sha1": "456ef7331c9230955de3b7c22346af6c613f7d3a" 185 | } 186 | }, 187 | { 188 | "name": "solarsystem", 189 | "source": { 190 | "type": "sourcefile", 191 | "url": "https://www.solarsystemscope.com/textures/download/2k_makemake_fictional.jpg", 192 | "sha1": "5e931975712dedb8ed394dbea3adde2491d03a9b" 193 | } 194 | }, 195 | { 196 | "name": "solarsystem", 197 | "source": { 198 | "type": "sourcefile", 199 | "url": "https://raw.githubusercontent.com/corporateshark/Mastering-Android-NDK/refs/heads/master/Chapter10/1_Asteroids/assets/deimos.obj", 200 | "sha1": "872aa578ec8a922da7bad31867394eea02516fcc" 201 | } 202 | }, 203 | { 204 | "name": "solarsystem", 205 | "source": { 206 | "type": "sourcefile", 207 | "url": "https://svs.gsfc.nasa.gov/vis/a000000/a003800/a003895/starmap_4k.jpg", 208 | "sha1": "2fb7d68ecd93d5dd1f69d8339e007f3b5886e33e" 209 | } 210 | } 211 | ] 212 | -------------------------------------------------------------------------------- /third-party/bootstrap-deps.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "meshoptimizer", 4 | "source": { 5 | "type": "git", 6 | "url": "https://github.com/zeux/meshoptimizer.git", 7 | "revision": "v0.23" 8 | } 9 | }, 10 | { 11 | "name": "glslang", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/KhronosGroup/glslang.git", 15 | "revision": "15.3.0" 16 | } 17 | }, 18 | { 19 | "name": "SPIRV-Reflect", 20 | "source": { 21 | "type": "git", 22 | "url": "https://github.com/KhronosGroup/SPIRV-Reflect.git", 23 | "revision": "c07ddbd7f3ef6f4be3edcf9a091d745d11bf0e2a" 24 | } 25 | }, 26 | { 27 | "name": "fast_obj", 28 | "source": { 29 | "type": "git", 30 | "url": "https://github.com/thisistherk/fast_obj.git", 31 | "revision": "9884aa86b00a74122719754006b63e6dfcba238f" 32 | } 33 | }, 34 | { 35 | "name": "glfw", 36 | "source": { 37 | "type": "git", 38 | "url": "https://github.com/glfw/glfw.git", 39 | "revision": "3.4" 40 | } 41 | }, 42 | { 43 | "name": "stb", 44 | "source": { 45 | "type": "git", 46 | "url": "https://github.com/nothings/stb.git", 47 | "revision": "ae721c50eaf761660b4f90cc590453cdb0c2acd0" 48 | } 49 | }, 50 | { 51 | "name": "3D-Graphics-Rendering-Cookbook/data", 52 | "source": { 53 | "type": "sourcefile", 54 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/data/OpenSans-Light.ttf", 55 | "sha1": "40442c189568184b6e6c27a25d69f14d91b65039" 56 | } 57 | }, 58 | { 59 | "name": "3D-Graphics-Rendering-Cookbook/shared", 60 | "source": { 61 | "type": "sourcefile", 62 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/UtilsFPS.h", 63 | "sha1": "cf19e032be65f471e12274d15dcc2989e8c2e469" 64 | } 65 | }, 66 | { 67 | "name": "3D-Graphics-Rendering-Cookbook/shared", 68 | "source": { 69 | "type": "sourcefile", 70 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/Trackball.h", 71 | "sha1": "da9a729862ba4df957e89984c1655186d2ec74f0" 72 | } 73 | }, 74 | { 75 | "name": "3D-Graphics-Rendering-Cookbook/shared", 76 | "source": { 77 | "type": "sourcefile", 78 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/Camera.h", 79 | "sha1": "d0fa491b2a075689ed5bf3f802269b546f267815" 80 | } 81 | }, 82 | { 83 | "name": "3D-Graphics-Rendering-Cookbook/shared", 84 | "source": { 85 | "type": "sourcefile", 86 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/UtilsMath.h", 87 | "sha1": "e50eb2ecfe9342ad66423493cd127ff00c1053b2" 88 | } 89 | }, 90 | { 91 | "name": "3D-Graphics-Rendering-Cookbook/shared", 92 | "source": { 93 | "type": "sourcefile", 94 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/UtilsCubemap.h", 95 | "sha1": "ddcfd57f144982bb2b2a43f0f47780be933a50df" 96 | } 97 | }, 98 | { 99 | "name": "3D-Graphics-Rendering-Cookbook/shared", 100 | "source": { 101 | "type": "sourcefile", 102 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/UtilsCubemap.cpp", 103 | "sha1": "37093b74ee4a3f9b5073e8c777511cba53793a2a" 104 | } 105 | }, 106 | { 107 | "name": "3D-Graphics-Rendering-Cookbook/shared", 108 | "source": { 109 | "type": "sourcefile", 110 | "url": "https://raw.githubusercontent.com/PacktPublishing/3D-Graphics-Rendering-Cookbook/e8bf80a2f4f2f9fafc49860f4d5b201101d42af8/shared/Bitmap.h", 111 | "sha1": "3c13995fb713547357ed6858018ed389ef76718b" 112 | } 113 | }, 114 | { 115 | "name": "openxr-sdk", 116 | "source": { 117 | "type": "git", 118 | "url": "https://github.com/KhronosGroup/OpenXR-SDK.git", 119 | "sha1": "release-1.1.47" 120 | } 121 | }, 122 | { 123 | "name": "glm", 124 | "source": { 125 | "type": "git", 126 | "url": "https://github.com/g-truc/glm.git", 127 | "revision": "1.0.0" 128 | } 129 | }, 130 | { 131 | "name": "taskflow", 132 | "source": { 133 | "type": "git", 134 | "url": "https://github.com/taskflow/taskflow.git", 135 | "revision": "v3.8.0" 136 | } 137 | }, 138 | { 139 | "name": "imgui", 140 | "source": { 141 | "type": "git", 142 | "url": "https://github.com/ocornut/imgui.git", 143 | "revision": "v1.91.9b" 144 | } 145 | }, 146 | { 147 | "name": "implot", 148 | "source": { 149 | "type": "git", 150 | "url": "https://github.com/epezent/implot", 151 | "revision": "3da8bd34299965d3b0ab124df743fe3e076fa222" 152 | } 153 | }, 154 | { 155 | "name": "volk", 156 | "source": { 157 | "type": "git", 158 | "url": "https://github.com/zeux/volk", 159 | "revision": "1.4.304" 160 | } 161 | }, 162 | { 163 | "name": "vma", 164 | "source": { 165 | "type": "git", 166 | "url": "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git", 167 | "revision": "v3.2.1" 168 | } 169 | }, 170 | { 171 | "name": "tracy", 172 | "source": { 173 | "type": "git", 174 | "url": "https://github.com/wolfpld/tracy.git", 175 | "revision": "v0.11.1" 176 | } 177 | }, 178 | { 179 | "name": "EGL", 180 | "source": { 181 | "type": "git", 182 | "url": "https://github.com/McNopper/EGL.git", 183 | "revision": "f20cdac3745a0d45ce8a8358ea40389278ae91e5" 184 | } 185 | }, 186 | { 187 | "name": "minilog", 188 | "source": { 189 | "type": "git", 190 | "url": "https://github.com/corporateshark/minilog", 191 | "revision": "784664e0ae28c9c1a01eece6a6c1a29855ac7856" 192 | } 193 | }, 194 | { 195 | "name": "ldrutils", 196 | "source": { 197 | "type": "git", 198 | "url": "https://github.com/corporateshark/ldrutils", 199 | "revision": "ccf0bc2f085ec7e8a7cee245c25910ef350b03fc" 200 | } 201 | }, 202 | { 203 | "name": "cmake-wayland", 204 | "source": { 205 | "type": "sourcefile", 206 | "url": "https://raw.githubusercontent.com/LunarG/VulkanSamples/master/cmake/FindWayland.cmake", 207 | "sha1": "c2f6e9245c257e6eff91a53602844175e12a6ecc" 208 | } 209 | }, 210 | { 211 | "name": "android-validation-layers", 212 | "source": { 213 | "type": "archive", 214 | "url": "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.304.1/android-binaries-1.4.304.1.zip", 215 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" 216 | } 217 | }, 218 | { 219 | "name": "ktx-software", 220 | "source": { 221 | "type": "git", 222 | "url": "https://github.com/KhronosGroup/KTX-Software.git", 223 | "revision": "v4.4.0", 224 | "recursive": false 225 | } 226 | } 227 | ] 228 | -------------------------------------------------------------------------------- /third-party/content/patches/saturn_rings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import shutil 5 | from pathlib import Path 6 | 7 | base_dir=os.getcwd() 8 | lib_dir=os.path.join(base_dir, "src", "solarsystem") 9 | 10 | shutil.move(os.path.join(lib_dir, "d6xt63j-169c5f5a-9033-417c-a48a-496ec8d05f77.jpg"), os.path.join(lib_dir, "1k_saturn_rings.jpg")) 11 | -------------------------------------------------------------------------------- /third-party/content/patches/sun_corona.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import shutil 5 | from pathlib import Path 6 | 7 | base_dir=os.getcwd() 8 | lib_dir=os.path.join(base_dir, "src", "solarsystem") 9 | 10 | shutil.move(os.path.join(lib_dir, "ThinkstockPhotos-517462556.web_.jpg"), os.path.join(lib_dir, "768_sun_corona.jpg")) 11 | -------------------------------------------------------------------------------- /third-party/deps/patches/stb_impl/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include 3 | -------------------------------------------------------------------------------- /third-party/deps/patches/stb_impl/stb_image_resize2.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 2 | #include 3 | -------------------------------------------------------------------------------- /third-party/deps/patches/stb_impl/stb_image_write.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_WRITE_IMPLEMENTATION 2 | #include 3 | --------------------------------------------------------------------------------