diff --git a/.gitignore b/.gitignore index 4f329d5..cb99575 100644 --- a/.gitignore +++ b/.gitignore @@ -9,13 +9,36 @@ *.aar *.ap_ *.aab +*.exe +*.lib +*.dll +*.a # ignore Media within samples samples/*/Media +tests/*/Media + +# ignore external folders (contents pulled by cmake fetchcontent) +samples/external +framework/external/.fetchcontent + +# ignore loose image files (eg screenshots) +*.bmp + +# ignore timestamp file +project/buildtimestamp.h + +# Ignore local config files +ConfigLocal.txt +ConfigLocal.cmake +ConfigLocal.properties # ignore generated (doxygen) documentation doc/html/ +# ignore generated timestamp file +project/buildtimestamp.h + # Files for the ART/Dalvik VM *.dex @@ -32,10 +55,12 @@ out/ # Gradle files .gradle/ build/ +samples/*/assets_tmp +tests/*/assets_tmp # Visual Studio files .vs/ -project/windows/solution/ +project/windows/solution*/ *.user *.suo @@ -51,6 +76,11 @@ proguard/ # Log Files *.log +# Generic editor backup files +*.tmp +*.temp +*.bak + # Android Studio Navigation editor temp files .navigation/ @@ -110,13 +140,10 @@ debug/ # imgui config imgui.ini -project/buildtimestamp.h -samples/hdrSwapchain/assets_tmp/ -samples/hello-gltf/assets_tmp/ -samples/rotatedCopy/assets_tmp/ -samples/shaderResolveTonemap/assets_tmp/ -samples/SubPass/assets_tmp/ -samples/BloomImageProcessing/assets_tmp/ -project/tools/ktx.dll -project/tools/ktxinfo.exe -project/tools/toktx.exe + +# archives +*.zip +*.7z +*.rar +*.gz +*.tar diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e22e54c..0000000 --- a/.gitmodules +++ /dev/null @@ -1,38 +0,0 @@ -[submodule "framework/external/VulkanMemoryAllocator"] - path = framework/external/VulkanMemoryAllocator - url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator -[submodule "framework/external/glm"] - path = framework/external/glm - url = https://github.com/g-truc/glm - shallow = true -[submodule "framework/external/tinyobjloader"] - path = framework/external/tinyobjloader - url = https://github.com/tinyobjloader/tinyobjloader -[submodule "framework/external/json"] - path = framework/external/json - url = https://github.com/nlohmann/json -[submodule "framework/external/tinygltf"] - path = framework/external/tinygltf - url = https://github.com/syoyo/tinygltf -[submodule "framework/external/imgui"] - path = framework/external/imgui - url = https://github.com/ocornut/imgui -[submodule "framework/external/eigen"] - path = framework/external/eigen - url = https://gitlab.com/libeigen/eigen.git - shallow = true -[submodule "framework/external/Vulkan-Headers"] - path = framework/external/Vulkan-Headers - url = https://github.com/KhronosGroup/Vulkan-Headers.git -[submodule "assets"] - path = assets - url = https://github.com/quic/game-assets-for-adreno-gpu-code-samples -[submodule "framework/external/KTX-Software"] - path = framework/external/KTX-Software - url = https://github.com/KhronosGroup/KTX-Software -[submodule "framework/external/portable-file-dialogs"] - path = framework/external/portable-file-dialogs - url = https://github.com/samhocevar/portable-file-dialogs -[submodule "framework/external/implot"] - path = framework/external/implot - url = https://github.com/epezent/implot diff --git a/01_BuildAndroid.bat b/01_BuildAndroid.bat deleted file mode 100644 index dabfd8c..0000000 --- a/01_BuildAndroid.bat +++ /dev/null @@ -1,4 +0,0 @@ -call cls -pushd project\android -call build.bat -popd diff --git a/01_Configure.bat b/01_Configure.bat new file mode 100644 index 0000000..5cacae3 --- /dev/null +++ b/01_Configure.bat @@ -0,0 +1,4 @@ +@echo off +cd /D "%~dp0" +python Configure.py + diff --git a/01_Configure.sh b/01_Configure.sh new file mode 100644 index 0000000..981c6fd --- /dev/null +++ b/01_Configure.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +cd "$(dirname "$0")" + +python Configure.py + diff --git a/02_Build.bat b/02_Build.bat new file mode 100644 index 0000000..16cc406 --- /dev/null +++ b/02_Build.bat @@ -0,0 +1,5 @@ +@echo off +cd /D "%~dp0" + +python Configure.py --build + diff --git a/02_Build.sh b/02_Build.sh new file mode 100644 index 0000000..9a66bc0 --- /dev/null +++ b/02_Build.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +cd "$(dirname "$0")" + +python Configure.py --build + diff --git a/02_BuildWindows.bat b/02_BuildWindows.bat deleted file mode 100644 index aa60047..0000000 --- a/02_BuildWindows.bat +++ /dev/null @@ -1,4 +0,0 @@ -call cls -pushd project\windows -call build.bat -popd diff --git a/03_BuildTools.bat b/03_BuildTools.bat deleted file mode 100644 index e264958..0000000 --- a/03_BuildTools.bat +++ /dev/null @@ -1,4 +0,0 @@ -call cls -pushd project\tools -call build.bat -popd diff --git a/Config.txt b/Config.txt new file mode 100644 index 0000000..e450e62 --- /dev/null +++ b/Config.txt @@ -0,0 +1,43 @@ +tests\ + empty; framework\base + vulkan; framework\vulkan + framework_test_vulkan + hello_gltf_vulkan +samples\ + vulkan; framework\vulkan + vk_extensions + graph_pipelines + image_processing + tile_shading + tile_memory + misc + sgsr + sgsr2 + others + hdr_swapchain + rotated_copy + sub_pass + external\ +framework\ + base; framework\external\GameSampleAssets, framework\external\glm, framework\external\tinyobjloader, framework\external\tinygltf, framework\external\json, framework\external\eigen + generic; framework\base, framework\external\imgui, framework\external\implot, framework\external\portable-file-dialogs, framework\external\KTX-Software, Tools + vulkan; framework\generic, framework\external\volk, framework\external\SPIRV-Cross, framework\external\glslang, framework\external\slang, framework\external\VulkanMemoryAllocator, framework\external\Vulkan-Headers + external\ + VulkanMemoryAllocator @ https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/archive/refs/tags/v3.0.1.tar.gz MD5:8571f3def0ff86f228e2864c907ba0b3 + tinyobjloader @ https://github.com/tinyobjloader/tinyobjloader/archive/e39c1737bc61c8dce28be7932cfe839d408e7838.zip + tinygltf @ https://github.com/syoyo/tinygltf/archive/925b83627a136d24411067031893dc8ea661444d.zip + imgui @ https://github.com/ocornut/imgui/archive/4f9ba19e520bea478f5cb654d37ef45e6404bd52.zip MD5:2d0aa43693cdada8abb9d49a44c1337b + implot @ https://github.com/epezent/implot/archive/f156599faefe316f7dd20fe6c783bf87c8bb6fd9.zip MD5:b95d158f69b1716da2cd7c17d63bdce4 + portable-file-dialogs @ https://github.com/samhocevar/portable-file-dialogs/archive/7f852d88a480020d7f91957cbcefe514fc95000c.zip MD5:ec1fd9e86f260b99a50294b8f53f872f + Vulkan-Headers @ https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.4.328.zip + glm @ https://github.com/g-truc/glm/archive/6ad79aae3eb5bf809c30bf1168171e9e55857e45.zip + GameSampleAssets @ https://github.com/SnapdragonStudios/game-assets-for-adreno-gpu-code-samples/archive/0ef8e70049ffd9ee4f9138b43815b21f959497d0.zip + volk @ https://github.com/zeux/volk/archive/1e0ec168f1726e6389b8647435a3018f0cef9428.zip + SPIRV-Cross @ https://github.com/KhronosGroup/SPIRV-Cross/archive/7affe74d77f93a622bb5002789d5332d32e512ee.zip + glslang @ https://github.com/KhronosGroup/glslang/archive/3a7f78758f8faa9a6e059b09e25fc64ede7fbfb0.zip + slang @ https://github.com/shader-slang/slang/archive/9c2024a7509baae921083d49a56e1321c51f00ec.zip + json @ https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz MD5:4b67aba51ddf17c798e80361f527f50e + eigen @ https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz MD5:4c527a9171d71a72a9d4186e65bea559 + KTX-Software @ https://github.com/KhronosGroup/KTX-Software/archive/refs/tags/v4.1.0.tar.gz MD5:b35fc412cdb3a00aa92aadcdd1e5f004 PATCH:..\cmake\KTX-Software.diff; Tools + D3D12MemoryAllocator @ https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator/archive/7597f717c7b32b74d263009ecc15985b517585c7.zip + diff --git a/Configure.py b/Configure.py new file mode 100644 index 0000000..764508c --- /dev/null +++ b/Configure.py @@ -0,0 +1,830 @@ +# +# Copyright (c) 2025 QUALCOMM Technologies Inc. +# All Rights Reserved. +# +import sys +import os +import argparse +import hashlib +import pathlib +import platform +import shutil +import tarfile +import subprocess +import tempfile +import zipfile + +tab_size = 8 # hard tab size + +parser = argparse.ArgumentParser( + description="Configuration tool for Snapdragon Studios's Sample Framework", + epilog="see README.md for additional help") +parser.add_argument('--build', help = 'Build with the current ConfigLocal settings, does require user input. Typically this option is used for subsequent builds after initial configuration.', action = 'store_true') +parser.add_argument('--clean', help = 'Clean all targets', action = 'store_true') +arguments = parser.parse_args() +enableUserInterface = not arguments.clean and not arguments.build + + +class Item: + def __init__(self, name, selected = -1, readOnly = False, saveable = True, groups = [], dependencies = [], location = None, function = None): + self.name = name + self.groups = groups + self.dependencies = dependencies + self.selected = selected + self.readOnly = readOnly + self.saveable = saveable + self.open = False + self.location = location + self.function = function + def select(self): + if self.function: + self.function(self) + else: + toggle_selected(self) + +class DisplayLine: + def __init__(self, item: Item, string: str): + self.item = item + self.string = string + def open(self): + self.item.open = True + def close(self): + self.item.open = False + +# determine correct keyboard handling code (per platform - we jump through these hoops so we do not need to install any external python libraries such as curses or pygame) +try: + import msvcrt + def get_key_blocking_windows(): + key = msvcrt.getch() + keyCode = 0 + if key == b'\xe0' or key == b'\000': + keyCode = int.from_bytes(msvcrt.getch(),byteorder='little') + return ('c' + str(keyCode)) # turns into c?? (where ?? is the decimal representation of the keyCode) + else: + return str(key, encoding='ascii') + get_key_blocking = get_key_blocking_windows +except ImportError: + import tty, sys, termios + def get_key_blocking_unix(): + stdinFd = sys.stdin.fileno() + savedAttr = termios.tcgetattr(stdinFd) + try: + tty.setraw(sys.stdin.fileno()) + key = sys.stdin.read(1) + # read another character if escaped (special key sequence such as cursor) + if key[0] == '\x1b': + key = 'e' + while True: + keyEscaped = sys.stdin.read(1) + key += keyEscaped + if keyEscaped.isalpha(): + break + finally: + termios.tcsetattr(stdinFd, termios.TCSADRAIN, savedAttr) + return key + get_key_blocking = get_key_blocking_unix + + +def toggle_selected(item: Item): + # toggle selection status of the item + recalculate_selections( groups, {item : 1 if item.selected == -1 else -1} ) + +def count_indent(s): + indent = 0 + for c in s: + if c.isspace(): + indent += 1 + elif c == '\t': + indent = ((indent + tab_size) / tab_size) * tab_size + else: + break + return indent + +def read_config_group(configLines, currentIndent = 0): + itemStrings = [] + groups = {} + currentLineIdx = 0 + while currentLineIdx < len(configLines): + configLine = configLines[currentLineIdx].rstrip() + #print(configLine + " " + str(currentIndent)) + + indent = count_indent(configLine) + configLine = configLine.strip() + if len(configLine) == 0: + # skip blank lines (or lines with no non-whitespace content) + currentLineIdx += 1 + continue + if currentLineIdx == 0 and indent != currentIndent: + raise Exception("indent mismatch error") + + if indent > currentIndent: + # start of child block + numProcessedLines, groupItemStrings, groupItemGroups = read_config_group(configLines[currentLineIdx:], indent) + currentLineIdx += numProcessedLines + groups[itemStrings[-1][0]] = [groupItemStrings, groupItemGroups] + elif indent < currentIndent: + # end of our block + break + else: + splitLine = [s.strip() for s in configLine.split(';', 1)] + name = splitLine[0].replace('\\', '/') + if len(splitLine) > 1 and len(splitLine[1]) > 0: + dependencies = [os.path.normpath(x.strip().replace('\\', '/')) for x in splitLine[1].split(',')] + else: + dependencies = [] + #print(dependencies) + itemStrings.append([name, dependencies]) + currentLineIdx += 1 + return currentLineIdx, itemStrings, groups + + +def read_config(folder, filename): + fullFilename = os.path.join(folder, filename) + if os.path.exists(fullFilename): + with open(fullFilename, 'r') as file: + configLines = file.readlines() + currentIndent = 0 + _, itemStrings, itemGroups = read_config_group(configLines) + #print(itemStrings) + #print(itemGroups) + + # make the Items + def make_items(itemStrings, itemGroups): + items = [] + for i in itemStrings: + name = i[0] + dependencies = i[1] + item_group = make_items(*itemGroups[name]) if name in itemGroups else [] + + splitLocation = [s.strip() for s in name.split("@")] + name = splitLocation[0] + location = splitLocation[1] if len(splitLocation)>1 else None + items.append( Item(name, -1, False, True, item_group, dependencies, location) ) + return items + items = make_items(itemStrings, itemGroups) + + #print(items) + return items + +# write a config file containing ONLY the items that are enabled (selected) +def write_enabled_config(groups, folder, filename): + filename = os.path.join(folder, filename) + with open(filename, 'w') as file: + + def write(groups, indent = 0): + for entry in groups: + if entry.saveable: + if entry.selected != -1: + #file.write(' ' * indent + entry.name + ((';' + ", ".join(entry.dependencies)) if len(entry.dependencies) > 0 else '') + '\n') + file.write(' ' * indent + entry.name + '\n') + write(entry.groups, indent + 1) + write(groups) + print("Wrote config to " + filename) + + +def recalculate_selections(groups, changed_selections): + if not changed_selections: + changed_selections = {i: i.selected for i in groups} + new_selections = {} + while True: + for item in changed_selections: + selected = changed_selections[item] + item.selected = selected + if item.groups and selected != 0: + for i in item.groups: + new_selections[i] = selected + if not new_selections: + break + changed_selections = new_selections + new_selections = {} + + + # now scan through and determine selection status (all, partial, none; 1 0 -1) for all items with children (from very top level down) + def recalculate_parent_selections(group): + allSelected = True + allClear = True + for item in group: + if item.groups: + childAllSelected, childAllClear = recalculate_parent_selections(item.groups) + item.selected = -1 if childAllClear else 1 if childAllSelected else 0 + allSelected = allSelected and childAllSelected + allClear = allClear and childAllClear + else: + allSelected = allSelected and item.selected == 1 + allClear = allClear and item.selected == -1 + if allClear and allSelected: + raise Exception("logic error") + return allSelected, allClear + recalculate_parent_selections(groups) + + # and re-set all the dependencies! + while True: + def find_dependencies(groups): + new_selections = {} + for item in groups: + if item.groups: + new_selections = {**new_selections, **find_dependencies(item.groups)} + if item.selected != -1 and item.dependencies: + for dependencyName in item.dependencies: + dependency = items_by_path[dependencyName] + if dependency.selected != 1: + # add as a new dependeny to set + new_selections[dependency] = 1 + return new_selections + new_selections = find_dependencies(groups) + if not new_selections: + break + # set all the dependencies + recalculate_selections(groups, new_selections) + +def clean_items(itemsToClean): + for item in itemsToClean: + cleanFunctionsMap[item.name]() + +def build_items(itemsToBuild): + success = True + for item in itemsToBuild: + if not buildFunctionsMap[item.name](): + success = False + return success + +def save_and_begin_processing_selected(_: Item): + write_enabled_config(groups, ".", "ConfigLocal.txt") + write_cmake_config(projectsItems, "ConfigLocal.cmake") + write_gradle_properties(projectsItems, "ConfigLocal.properties") + write_android_gradle_files(projectsItems) + + # clean selected items + itemsToClean = filter( lambda x: x.selected == 1, cleanTargetItems ) + clean_items( itemsToClean ) + + # download selected items + download(projectsItems) + + # see how many projects are set to build + selected_project_count = count_selected(projectsItems) + if selected_project_count == 0: + print("\nNo projects selected to be built") + else: + # build selected items + targetsToBuild = list(filter( lambda x: x.selected == 1, buildTargetItems )) + if not targetsToBuild: + print("\nNo targets selected to be built") + else: + if not build_items( targetsToBuild ): + print("ERROR BUILDING TARGETS (see output above)") + + print("\nCompleted.") + # quit the app! + exit(0) + +class Download: + def __init__(self, outputFolder, url, downloadDestination, md5Expected, patchFile): + self.outputFolder = outputFolder + self.url = url + self.downloadDestination = downloadDestination + self.md5Expected = md5Expected + self.patchFile = patchFile + +def find_downloads(groups, folder, onlySelected): + downloads = [] + for item in groups: + if not onlySelected or item.selected != -1: + if item.groups: + if item.name[-1] == '/': + subFolder = os.path.join(folder, item.name[:-1]) + else: + subFolder = folder + downloads.extend( find_downloads(item.groups, subFolder, onlySelected) ) + if item.location: + md5Expected = None + patchFile = None + security = item.location.split(' ', 2) + if len(security) > 1: + for s in security[1:]: + if s[:4] == 'MD5:': + md5Expected = s[4:] + elif s[:6] == 'PATCH:': + patchFile = os.path.normpath(os.path.join(folder, os.path.normcase(s[6:]))) + url = security[0] + elif len(security) == 1: + url = security[0] + else: + raise Exception("Error parsing download string " + item.location + ". Format is [ MD5:] [ PATCH:]") + + outputFolder = os.path.join(folder, item.name) + downloadDestination = os.path.join(folder, url.rsplit('/', 1)[-1]) + downloads.append( Download(outputFolder, url, downloadDestination, md5Expected, patchFile) ) + return downloads + +def download(groups): + + downloads = find_downloads(groups, '.', onlySelected=True) + + if not downloads: + return False + print("Preparing to download\n---------------------") + for download in downloads: + print("" + download.url + "\n -> " + download.outputFolder) + print("\n") + + for download in downloads: + outputFolder = download.outputFolder + url = download.url + downloadDestination = download.downloadDestination + md5Expected = download.md5Expected + patchFile = download.patchFile + + refreshFiles = False # assume everything up to date + + if os.path.exists(downloadDestination): + print("Found " + downloadDestination + " (skipping download)") + filename = downloadDestination + else: + destinationFolder = os.path.dirname(downloadDestination) + if not os.path.exists(destinationFolder): + print("Creating folder " + destinationFolder) + pathlib.Path(destinationFolder).mkdir(parents = True, exist_ok = True) + + print("Downloading " + url) + refreshFiles = True + import urllib.request, ssl + ssl._create_default_https_context = ssl._create_unverified_context + filename, headers = urllib.request.urlretrieve(url) + + if md5Expected: + md5hash = hashlib.md5() + with open(filename, 'rb') as file: + for b in iter(lambda: file.read(32768), b""): + md5hash.update(b) + if md5hash.hexdigest() != md5Expected: + os.remove(filename) + raise Exception("MD5 hash security check for " + filename + " (" + downloadDestination + ") failed.\n Expected " + md5Expected + " got " + md5hash.hexdigest()) + print(" MD5 hash verified") + + shutil.move(filename, downloadDestination) + filename = downloadDestination + print(" copied to " + filename) + + # prepare for files copied over destination folder + if refreshFiles and os.path.exists(outputFolder): + shutil.rmtree(outputFolder) + + if not os.path.exists(outputFolder): + filenameExtSplit = filename.rsplit('.', 2) + with tempfile.TemporaryDirectory() as tempExtractDir: # use a temporary directory as an extraction location + if len(filenameExtSplit)>1 and filenameExtSplit[-1].lower() == 'zip': + print(" extracting zip to " + tempExtractDir) + with zipfile.ZipFile(filename, 'r') as file: + file.extractall(tempExtractDir) + elif len(filenameExtSplit)>1 and filenameExtSplit[-2].lower() == 'tar': + print(" extracting tar to " + tempExtractDir) + + with tarfile.open(filename) as tar: + # check the tar only contains files/directories (no symlinks etc supported) and no attempts to write outside of the target directory + targetDir = pathlib.Path(outputFolder) + for tarinfo in tar.getmembers(): + if tarinfo.isfile() or tarinfo.isdir(): + tarinfo.uname = None + tarinfo.gname = None + outputPath = pathlib.Path( os.path.normpath(os.path.join(targetDir, tarinfo.name)) ) + if targetDir not in outputPath.parents: + raise Exception("Tar file " + filename + " prevented from write outside of safe directory structure (potential security issue). (" + str(outputPath) + ")") + + if sys.version_info[0] == 3 and sys.version_info[1] >= 12: + tar.extractall(path=tempExtractDir, filter = 'data') + else: + tar.extractall(path=tempExtractDir) + else: + raise Exception("Unknown file archive format for " + filename + " ( .tar.gz / .tar.xz / .zip supported)") + + # all the achives have a top level directory (and the contents we expect below it), get that directory name + print(" moving extracted files to " + outputFolder) + tempExtractDirContents = os.listdir(tempExtractDir) + if len(tempExtractDirContents)!=1: + raise Exception("Expected downloaded archive from " + url + " to contain a single top level folder, does not - actual contents: " + tempExtractDirContents) + extractedFolderPath = os.path.join(tempExtractDir, tempExtractDirContents[0]) + + # Move the top level folder out of the temporary extraction location to the correctly named destination + shutil.copytree(extractedFolderPath, outputFolder) + + if patchFile: + print(" patching using " + patchFile) + subprocess.run(["git", "apply","--verbose","--unsafe-paths","--ignore-space-change","--directory", outputFolder, patchFile]) + + elif refreshFiles: + raise Exception("Was expecting folder to be removed before extraction, try manually deleting? (folder: " + outputFolder + ")") + else: + print(" skipped extracting files to " + outputFolder + " (folder already exists)") + return True # did something + +# clean anything that (may have been) downloaded +def clean_externals(): + downloads = find_downloads(groups, '.', onlySelected=False) + for d in downloads: + if os.path.exists(d.downloadDestination): + print("Removing: " + d.downloadDestination) + os.remove(d.downloadDestination) + if os.path.isdir(d.outputFolder): + print("Removing: " + d.outputFolder) + shutil.rmtree(d.outputFolder, ignore_errors=True) + +def write_cmake_config(groups, filename): + with open(filename, "wt") as file: + file.write("# CMake file written by " + sys.argv[0] + "\n#\n# Defines which framework libraries and samples are enabled\n# Do not check this file in to version control (generate locally)\n\n") + file.write("cmake_minimum_required (VERSION 3.21)\n\n") + def write_items(groups, parentName, file): + for item in groups: + variableName = parentName + "_" + item.name.strip('\\/') + file.write("SET(" + variableName + (" True)\n" if item.selected != -1 else " False)\n")) + if item.groups: + if item.name[-1] == '/': + write_items(item.groups, variableName, file) + else: + write_items(item.groups, parentName, file) + write_items(groups, "FRAMEWORK", file) + print("Cmake build properties file \"" + filename + "\" written") + +def write_gradle_properties(groups, filename): + with open(filename, "wt") as file: + file.write("# Gradle properties file written by " + sys.argv[0] + "\n#\n# Defines which framework libraries and samples are enabled\n# Do not check this file in to version control (generate locally)\n\n") + def write_items(groups, parentName, file): + for item in groups: + variableName = parentName + "_" + item.name.strip('\\/') + file.write(variableName + (" = true\n" if item.selected != -1 else " = false\n")) + if item.groups: + if item.name[-1] == '/': + write_items(item.groups, variableName, file) + else: + write_items(item.groups, parentName, file) + write_items(groups, "FRAMEWORK", file) + print("Gradle (Android) build properties file \"" + filename + "\" written") + + + +def write_android_gradle_files(groups): + """ + * Generates Android build.gradle files for selected sample and test projects. + * @param groups : The top-level configuration groups containing all selectable items. + * @note This function creates build.gradle files under ./samples//build or ./tests//build + """ + build_gradle_template = """apply plugin: 'com.android.application' + +android {{ + compileSdkVersion 33 + namespace "com.quic.{project_name}" + lintOptions {{ + abortOnError false + }} + + String rootDir = "${{project.rootDir}}" + rootDir = rootDir.replace("\\\\", "/") + + defaultConfig {{ + applicationId "com.quic.{project_name}" + minSdkVersion 26 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + ndkVersion "${{project.ndkVersionDefault}}" + ndk {{ + abiFilters 'arm64-v8a' + }} + externalNativeBuild {{ + cmake {{ + arguments "-DPROJECT_ROOT_DIR=${{rootDir}}", "-DFRAMEWORK_DIR=${{rootDir}}/../../framework" + }} + }} + }} + + signingConfigs{{ + unsigned{{ + storeFile file("${{System.env.USERPROFILE}}/.android/debug.keystore") + storePassword = "android" + keyAlias = "androiddebugkey" + keyPassword = "android" + v2SigningEnabled = false + }} + }} + + buildTypes {{ + release {{ + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.unsigned + }} + debug {{ + debuggable = true + jniDebuggable = true + }} + }} + + sourceSets {{ + main {{ + jni.srcDirs = [] + manifest.srcFile '../project/android/AndroidManifest.xml' + res.srcDirs = ['../project/android/res'] + }} + main.assets {{ + srcDirs = ['assets'] + srcDirs += ['assets_tmp'] + }} + }} + + dependencies {{ + implementation project(':framework') + }} + + externalNativeBuild {{ + cmake {{ + version "3.25.0+" + path "../CMakeLists.txt" + buildStagingDirectory "../../../build/cxx/${{project.name}}" + }} + }} + + task copyTmpAssets(type: Copy) {{ + from "Media" + into "assets_tmp/build/Media" + }} + task removeTmpAssets(type: Delete) {{ + delete "assets_tmp" + }} + + afterEvaluate {{ + packageRelease.finalizedBy(removeTmpAssets) + preBuild.dependsOn(":framework:buildNeeded") + }} + + preBuild.dependsOn(copyTmpAssets) + + def overrideFile = file("${{project.projectDir}}/../override.gradle") + if (overrideFile.exists()) {{ + apply from: overrideFile + }} +}} +""" + + def write_items(groups): + for item in groups: + if item.selected != -1 and not item.groups: + # Check for existence in samples and tests + possible_paths = [os.path.join("samples", item.name), os.path.join("tests", item.name)] + target_path = None + for path in possible_paths: + if os.path.isdir(path): + target_path = path + break + + if target_path: + build_dir = os.path.join(target_path, "build") + os.makedirs(build_dir, exist_ok=True) + gradle_path = os.path.join(build_dir, "build.gradle") + with open(gradle_path, "w") as f: + f.write(build_gradle_template.format(project_name=item.name)) + print(f"Generated build.gradle for {target_path}") + else: + print(f"Skipped {item.name}: no matching folder in samples/ or tests/") + + if item.groups: + write_items(item.groups) + + write_items(groups) + +def count_selected(groups): + count = 0 + for item in groups: + if item.groups: + count += count_selected(item.groups) + else: + count += 1 if item.selected != -1 else 0 + return count + + +# load the list of projects (that we can build) and their dependencies, from Config.txt +projectsItems = read_config(".", "Config.txt") + +# +# setup top level menu items +# + +# Clean functions +cleanFunctionsMap = { + "Android" : lambda: shutil.rmtree("./build", ignore_errors=True), + "Linux" : lambda: shutil.rmtree("./project/linux/solution", ignore_errors=True), + "Windows ARM64": lambda: shutil.rmtree("./project/windows/solutionArm64", ignore_errors=True), + "Windows x64" : lambda: shutil.rmtree("./project/windows/solution", ignore_errors=True), + "Externals" : clean_externals +} + +# run the given batch file and return True if successful (errorcode == 0) +def RunBatch(filename): + retcode = subprocess.call([os.path.abspath(filename)], shell=False) + return retcode == 0 + +cleanTargetItems = [Item(n) for n in cleanFunctionsMap] +# Build functions +if platform.system() == 'Windows': + buildFunctionsMap = { + "Tools" : lambda: RunBatch("./project/tools/build.bat"), + "Android" : lambda: RunBatch("./project/android/build.bat"), + "Windows ARM64": lambda: RunBatch("./project/windows/buildArm64.bat"), + "Windows x64" : lambda: RunBatch("./project/windows/build.bat"), + } +else: + buildFunctionsMap = { + "Tools" : lambda: subprocess.Popen('source ' + os.path.abspath("./project/tools/build.sh"), shell=True, executable="/bin/bash"), + "Linux" : lambda: subprocess.Popen('source ' + os.path.abspath("./project/linux/build.sh"), shell=True, executable="/bin/bash"), + } +buildTargetItems = [Item(n) for n in buildFunctionsMap] + +groups = [ + Item("Clean", selected = False, saveable = False, groups = cleanTargetItems), + Item("Build Targets", selected = False, groups = buildTargetItems), + Item("Build Projects", groups = projectsItems), + Item("Save And Begin Processing", function = save_and_begin_processing_selected) +] + +# for all the projects build a lookup by 'path' (for named dependencies) +def make_items_by_path(groups, path): + items_by_path = {} + for item in groups: + if item.groups: + # not a leaf item + if item.name[-1] == '/': + # folder based grouping + group_items_by_path = make_items_by_path(item.groups, os.path.join(path, item.name[:-1])) + else: + # non folder grouping + group_items_by_path = make_items_by_path(item.groups, path) + if group_items_by_path: + items_by_path = {**items_by_path, **group_items_by_path} + else: + # leaf node (or empty 'folder') + if item.name[-1] == '/': + pass # we dont need to record empty folders! + else: + items_by_path[os.path.join(path, item.name)] = item + return items_by_path +items_by_path = make_items_by_path(groups, "") + + +# now load the 'local' settings (which items/projects are currently selected for our local build) +if groups: + localSelectedGroups = read_config(".", "ConfigLocal.txt") + if not localSelectedGroups: + localSelectedGroups = [] + + def set_selected(items, override_items): + leaf_selections = {} + items_dict = { g.name: g for g in items } + for override in override_items: + if override.name in items_dict: + item = items_dict[override.name] + if override.groups: + # parent node + leaf_selections = {**leaf_selections, **set_selected( item.groups, override.groups )} + else: + # leaf node + leaf_selections[item] = 1 + return leaf_selections + recalculate_selections(groups, set_selected(groups, localSelectedGroups)) + +# recaulculate whether items are selected, unselected or partially selected (some children selected and some unselected) +recalculate_selections(groups, {}) + + +# see if we are running the user interface or in 'commandline' mode! +if enableUserInterface: + + # + # Gui Mode + # + + # open the top level project groups + for topLevelItem in projectsItems: + topLevelItem.open = True + + + quitMode = 0 + cursorIdx = 0 + topLine = 0 + + # get number of lines in scrolling window + numLines = os.get_terminal_size().lines - 5 + # clear terminal + print('\x1bc') + + # UI loop (draw menu and wait-for / react-to key input) + while(True): + indent = 0 + + def make_display(item : Item, cursorIdx, indent = 0): + cursor = '>>' if cursorIdx == 0 else ' ' + selected = '' + if not item.function: + selected = '[-]' + if item.selected == 1: + selected = '[X]' + elif item.selected == -1: + selected = '[ ]' + more = '' if not item.groups else '...' + display = [DisplayLine(item, str(cursor + ' ' * indent + selected + item.name + more))] + if item.open: + for group in item.groups: + display.extend(make_display(group, cursorIdx - len(display), indent + 2)) + return display + + all_display = [] + for group in groups: + all_display.extend( make_display(group, cursorIdx - len(all_display)) ) + + for d in all_display[topLine : topLine+numLines]: + print(d.string) + for r in range(0, numLines - len(all_display)): + print() + print() + if quitMode == 0: + if all_display[cursorIdx].item.function: + print('Up/Down - Move, Return = Select') + else: + print('Up/Down - Move, Return = Toggle selection' + (', Right/left - Open/close sub-menu' if all_display[cursorIdx].item.groups else '')) + print('Q = Quit, S - Save Settings') + elif quitMode == 1: + print('B - Back, W - Write settings and quit, Q - Quit without saving') + + key = get_key_blocking() #msvcrt.getch() + print('\x1bc') + if quitMode ==0: + if key == 'x' or key == 'X': + quitMode = 1 + elif key == 'q' or key == 'Q': + quitMode = 1 + elif key == 's' or key == 'S': + write_enabled_config(groups, ".", "ConfigLocal.txt") + elif key == ' ' or key == '\r': + # select item (call function) + item = all_display[cursorIdx].item + item.select() + elif key == 'c72' or key == 'e[A': # up + if cursorIdx == 0: + cursorIdx = len(all_display) - 1 + topLine = max(0, len(all_display) - numLines) + else: + cursorIdx = cursorIdx - 1 + if topLine >= cursorIdx - 1 and topLine > 0: + topLine = max(0, cursorIdx - 1) + elif key == 'c80' or key == 'e[B': # down + cursorIdx = cursorIdx + 1 + if cursorIdx == len(all_display): + cursorIdx = 0 + topLine = 0 + else: + if cursorIdx >= topLine + numLines - 2 and topLine + numLines < len(all_display): + topLine = max(0, min(cursorIdx - numLines + 2, len(all_display) - numLines)) + elif key == 'c77' or key == 'e[C': # right + all_display[cursorIdx].open() + elif key == 'c75' or key == 'e[D': # left + all_display[cursorIdx].close() + + if topLine >= cursorIdx - 1 and topLine > 0: + topLine = cursorIdx - 1 + + elif quitMode == 1: + if key == 'b' or key == 'B': + quitMode = 0 + elif key == 'w' or key == 'W': + # for g in groups: + # write_folder(g) + write_enabled_config(groups, ".", "ConfigLocal.txt") + exit(0) + elif key == 'q' or key == 'Q': + print("Exited without writing") + exit(0) + +else: + # + # Commandline mode + # + write_cmake_config(projectsItems, "ConfigLocal.cmake") + write_gradle_properties(projectsItems, "ConfigLocal.properties") + write_android_gradle_files(projectsItems) + + if arguments.clean: + # clean everything! + clean_items( cleanTargetItems ) + + if arguments.build: + # always do the downloads (build will fail if we are missing a download we are dependent on) + download(projectsItems) + + # only build what was previously selected (and saved) + targetsToBuild = list(filter( lambda x: x.selected == 1, buildTargetItems)) + if not targetsToBuild: + print("\nNo targets to build (did you run 'python Configure.py' and select something to build?)") + else: + selected_project_count = count_selected(projectsItems) + if selected_project_count == 0: + print("\nNo projects selected to be built (did you run 'python Configure.py' and select something to build?)") + else: + build_items( targetsToBuild ) diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/README.md b/README.md index 5be4634..f20df60 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Vulkan Code Sample Framework +# Vulkan Sample Framework The Adreno™ GPU Vulkan Code Sample Framework is a lightweight collection of C++ classes and sample projects to demonstrate Vulkan rendering features on the Qualcomm Snapdragon Adreno™ GPU. @@ -6,88 +6,104 @@ Both Android and Microsoft Windows build targets are supported (some samples may ## Prerequisites +- Git https://git-scm.com/downloads +- Python (tested against 3.10.9) +- CMake (tested against 3.30+) https://cmake.org/download/ +- Vulkan SDK (1.3 or later) https://vulkan.lunarg.com/ + ### Windows -- Visual Studio (tested against 2019) -- CMake (tested against 3.18+) https://cmake.org/download/ -- Git https://git-scm.com/downloads -- Vulkan SDK (1.3.224 or later) https://vulkan.lunarg.com/ +- Visual Studio 2022 ### Android - Android Studio (install NDK and SDK) -- CMake (> 3.10) https://cmake.org/download/ -- Ninja https://github.com/ninja-build/ninja/releases (or install Cmake from Android Studio and add the cmake bin folder to your PATH - AFTER the CMake path from the step above) -- Java JDK https://www.oracle.com/java/technologies/javase-downloads.html - -## Getting the vkSampleFramework project +- Ninja https://github.com/ninja-build/ninja/releases
+ After installation ninja.exe should be copied in to the same directory as cmake.exe - this works around a current [July2022] open bug with gradle where it expects ninja to be in the same directory as cmake!) +- Java JDK
+ Ensure JAVA_HOME environment variable points to a current version of Java. While a 'standalone' Java can be used it is highly recommended to use the Java build shipped inside Android Studio as the Android build system and gradle are very sensitive to Java versions. Eg. `set JAVA_HOME=c:\Program Files\Android\Android Studio\jbr` -The framework uses submodules for both external code dependencies and to get the shared assets for the sample projects (vkSampleFrameworkAssets). +### Linux -`git clone --recursive https://github.com/quic/adreno-gpu-vulkan-code-sample-framework` +Tested using WSL running Ubuntu. -Will clone the vkSampleFramework and its submodules. - -Subsequent pulls can be done with - -`git pull --recurse-submodules` ## Build Setup ### Windows -Ensure CMake is in the Windows PATH +Ensure CMake and python are in the Windows PATH ### Android -Create *project/android/local.properties* with the location of our cmake install eg: -
cmake.dir=C\:\\Tools\\cmake\\3.10.2.4988404
-
+Ensure you have CMake and a recent version of Android SDK installed (Android Studio can install both). +Android builds require CMake version 3.25 and above, if when building Android projects gradle is throwing arrors about cmake or Android SDK/NDK versions you can override the defaults by creating `project/android/local.properties` and adding the location of your local Android SDK and Cmake installs eg: +
sdk.dir=C\:\\Users\\yournamehere\\AppData\\Local\\Android\\Sdk
+cmake.dir=c\:\\Program Files\\CMake
+ +### Linux -If necessary, also include on *local.properties* the ndk and sdk locations eg: +## Configuring -
ndk.dir=C\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk\\ndk\\21.0.6113669
-sdk.dir=C\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk
-
+In the root folder run the configuration/build script: +`01_Configure.bat` (source `01_Configure.sh` on Linux) -Create *project/android/gradle.properties* with the location of our jdk and build heap space parameter, eg: -
org.gradle.java.home=C\:\\Program Files\\Java\\jdk-11
-org.gradle.jvmargs=-Xms512M -Xmx4G
-
+Select the build targets and solutions you are interested in building (sub-menus can be opened with the 'right' arrow). +Then select 'Save And Begin Processing' which: +- saves your selections. +- creates configuration files for building (`ConfigLocal.cmake` / `ConfigLocal.properties`). +- cleans any previous builds (if targets to clean are selected) +- downloads any external project dependencies (eg tinygltfloader, glm, KTX-Software). +- runs project\tools\build.bat to build tools required for building. +- runs project\windows\build.bat and/or buildArm64.bat (if windows target is selected) + - which creates the MSVC solution containing your selected projects; project\windows\solution\SampleFramework.sln + - attempts to build this solution for both debug and release. +- runs project\android\build.bat (if Android target is selected) + - which runs gradle to build the apk targets for your selected projects. -Note: Setting up the jdk location does not affect the Java version used to launch the Gradle client VM, so make sure your *JAVA_HOME* is set to a valid java version -(this is specially true if you receive errors in the format: *Unsupported class file major version XX)* +## Subsequent builds -## Building +01_Configure.bat can be used to re-run builds after changes. -In the root folder there is a batch file for building and packaging on each platform +Alternately the Windows solution can be used to make changes and re-build Windows targets + - project\windows\solution\SampleFramework.sln + - project\windowsArm64\solution\SampleFramework.sln +and Android builds can be re-ran with + - project\android\build.bat -`01_BuildAndroid.bat` +If desired you can also open/build the SampleFramework using Android Studio. In Android Studio open the project/android folder (initial load of the projects takes a while, subsequent opens are fast). Using Android Studio to build is untested and not supported! If you are having problems building framework samples please test using the batch file build scripts before opening any support requests. -`02_BuildWindows.bat` +IMPORTANT NOTE: Most of the samples require binary assets to be generated before that sample can be run. Refer to each sample's README for instructions on how to perform that step for each sample you are interested in running. + +## Running -Android uses Gradle and CMake to build an Android .apk for each sample. +See the [Samples](samples) folder for instructions on building assets and running individual samples. -Windows uses CMake to build a Visual Studio solution that is then built using Visual Studio (and can then be opened in the Visual Studio IDE). An .exe is output for each sample. +Most samples also support a configuration file (`app_config.txt`) placed in the base of the sample's directory (Windows) or pushed loose to the sample's install folder (Android). -The Windows Visual Studio file is written to `project\windows\solution\vkSampleFramework.sln`. Once created for the first time it can be opened and used in Visual Studio 2019 (VS2019 has full support for editing CMakeLists.txt). When using VS2019 to compile and run, be sure to point the debugger to the correct 'Working Directory' for each sample. -Note: Depending on your sample you might need to perform certain steps before running these batch files, see the readme on the desired sample for more information about this. +### Android -## Running +Android apk are written to `build\android\\outputs\apk\debug\` . +If the Android apk was correctly built (see note above about binary assets) it can be installed with `apk install ` and run from Android. -See the [Samples](samples) folder for instructions on building assets and running individual samples. +### Windows + +Executables are written to `project\windows\solution\samples\\debug\` or `project\windowsArm64\solution\samples\\debug\`. +When running sample executables from command line ensure you are running from the relevant sample directory (not the location of the built exe). The Visual Studio solution is already configured to launch samples from the correct directory. ## Directory Structure - [/framework](framework) Contains the sample framework code. - [/framework/external](framework/external) -Contains external projects used by the framework (mostly as git submodules) +Contains external projects downloaded and extracted by Configure.py - [/project](project) Platform specific top level build files and build tools. - [/samples](samples) Samples that use the framework. +- [/samples/external](samples/external) +External projects shared between samples, downloaded and extracted by Configure.py ## License Adreno™ GPU Vulkan Code Sample Framework is licensed under the BSD 3-clause “New” or “Revised” License. Check out the [LICENSE](LICENSE) for more details. \ No newline at end of file diff --git a/assets b/assets deleted file mode 160000 index f0e8460..0000000 --- a/assets +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f0e84608e78e71e5c9a1d7c20c2e8d0b1bd7d095 diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 25dd51c..3d6a36a 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = SampleFramework +PROJECT_NAME = vkSampleFramework # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -44,7 +44,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Vulkan Code Sample Framework" +PROJECT_BRIEF = "Qualcomm Vulkan Sample Framework" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html index 6768647..8bd9a71 100644 --- a/doc/doxygen/footer.html +++ b/doc/doxygen/footer.html @@ -10,7 +10,7 @@ diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 946a841..238fe67 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -1,22 +1,27 @@ -cmake_minimum_required (VERSION 3.21 FATAL_ERROR) # 3.7 added FindVulkan, 3.10 supported MSVC c++17 (without manually setting flags), 3.19 (maybe before) supported C++20 on Win/Android, 3.21 added Android NDK support with NDK r23 although we are not currently using it! -cmake_policy(VERSION 3.7) +cmake_minimum_required (VERSION 3.25 FATAL_ERROR) # 3.7 added FindVulkan, 3.10 supported MSVC c++17 (without manually setting flags), 3.19 (maybe before) supported C++20 on Win/Android, 3.21 added Android NDK support with NDK r23 although we are not currently using it!, 3.25 for fetchontent features +include(FindVulkan) project (framework C CXX) set(CMAKE_CXX_STANDARD 20) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +define_property(TARGET PROPERTY FOLDER INHERITED BRIEF_DOCS "Set the folder name." FULL_DOCS "Use to organize targets in an IDE.") +set_property(DIRECTORY "." PROPERTY FOLDER "framework/") + +include("../ConfigLocal.cmake") -add_compile_definitions(IMGUI_DEFINE_MATH_OPERATORS) +# Pull down all the external dependencies needed by the Framework +# we only do this if we are a 'top level' framework project such as with Windows (Android gradle will include the framework in each application's project but we dont want to re-get externals) +option(FRAMEWORK_DOWNLOAD_EXTERNALS "Set download/fetch external repositiories" No) +include(cmake/ExternalDependencies.cmake) -# Platform and Graphics API independant source here -# Graphics API independant (base functionality) source here +set(FRAMEWORK_LIB_OUTPUT "" CACHE STRING "Location to output framework libraries (use on Android to output static libraries from gradle)") + +# Base functionality for Framework applications (including command-line executables) set(CPP_BASE_SRC - code/system/crc32c.hpp - code/system/glm_common.hpp - code/system/math_common.hpp - code/system/os_common.cpp - code/system/os_common.h + code/main/frameworkApplicationBase.cpp + code/main/frameworkApplicationBase.hpp code/mesh/instanceGenerator.cpp code/mesh/instanceGenerator.hpp code/mesh/meshLoader.cpp @@ -25,13 +30,23 @@ set(CPP_BASE_SRC code/mesh/meshIntermediate.hpp code/mesh/octree.cpp code/mesh/octree.hpp - code/texture/textureManager.cpp - code/texture/textureManager.hpp + code/system/config.cpp + code/system/config.h + code/system/containers.cpp + code/system/containers.h + code/system/crc32c.hpp + code/system/glm_common.hpp + code/system/math_common.hpp + code/system/os_common.cpp + code/system/os_common.h ) +# Graphics API agnostic framework code set(CPP_GENERIC_SRC - code/main/frameworkApplicationBase.cpp - code/main/frameworkApplicationBase.hpp + code/allocator/threadBufferResource.hpp + code/allocator/threadBufferResourceHelper.hpp + code/allocator/threadManagedBufferResourceAllocator.hpp + code/allocator/threadMonotonicBufferResourceAllocator.hpp code/animation/animation.cpp code/animation/animation.hpp code/animation/animationData.hpp @@ -43,6 +58,12 @@ set(CPP_GENERIC_SRC code/animation/skeletonData.hpp code/animation/skeletonGltfLoader.cpp code/animation/skeletonGltfLoader.hpp + code/animation/skin.cpp + code/animation/skin.hpp + code/animation/skinData.cpp + code/animation/skinData.hpp + code/animation/skinGltfLoader.cpp + code/animation/skinGltfLoader.hpp code/camera/camera.cpp code/camera/camera.hpp code/camera/cameraController.cpp @@ -54,8 +75,13 @@ set(CPP_GENERIC_SRC code/camera/cameraData.hpp code/camera/cameraGltfLoader.cpp code/camera/cameraGltfLoader.hpp + code/graphicsApi/commandList.hpp code/graphicsApi/graphicsApiBase.cpp code/graphicsApi/graphicsApiBase.hpp + code/graphicsApi/pipeline.hpp + code/graphicsApi/renderContext.hpp + code/graphicsApi/renderPass.hpp + code/graphicsApi/renderTarget.hpp code/gui/gui.hpp code/gui/imguiBase.cpp code/gui/imguiBase.hpp @@ -71,12 +97,31 @@ set(CPP_GENERIC_SRC code/light/lightGltfLoader.hpp code/light/lightLoader.cpp code/light/lightLoader.hpp - code/material/shader.hpp - code/material/shaderModule.hpp + code/material/computable.cpp + code/material/computable.hpp + code/material/descriptorSetLayout.hpp + code/material/drawable.hpp + code/material/drawableLoader.cpp + code/material/drawableLoader.hpp + code/material/material.cpp + code/material/material.hpp + code/material/materialT.hpp + code/material/materialPass.hpp + code/material/materialManagerT.hpp + code/material/pipeline.hpp code/material/pipelineLayout.hpp code/material/pipelineVertexInputState.hpp + code/material/shader.hpp + code/material/shaderDescription.cpp + code/material/shaderDescription.hpp + code/material/shaderModule.hpp + code/material/shaderManager.cpp + code/material/shaderManager.hpp + code/material/shaderManagerT.hpp + code/material/specializationConstants.hpp code/material/specializationConstantsLayout.hpp code/memory/buffer.hpp + code/memory/drawIndirectBuffer.hpp code/memory/memory.hpp code/memory/memoryManager.hpp code/memory/memoryMapped.hpp @@ -91,23 +136,23 @@ set(CPP_GENERIC_SRC code/shadow/shadow.cpp code/shadow/shadow.hpp code/system/assetManager.hpp - code/system/config.cpp - code/system/config.h - code/system/containers.cpp - code/system/containers.h code/system/profile.cpp code/system/profile.h code/system/timer.cpp code/system/timer.hpp - code/system/worker.cpp - code/system/worker.h + code/system/Worker.cpp + code/system/Worker.h code/texture/loaderKtx.cpp code/texture/loaderKtx.hpp code/texture/loaderPpm.cpp code/texture/loaderPpm.hpp + code/texture/sampler.cpp + code/texture/sampler.hpp code/texture/texture.hpp code/texture/textureFormat.cpp code/texture/textureFormat.hpp + code/texture/textureManager.cpp + code/texture/textureManager.hpp ) # OS independant (Vulkan targetted) source here @@ -121,37 +166,40 @@ set(CPP_VULKAN_SRC code/helper/postProcessStandard.hpp code/helper/postProcessSMAA.cpp code/helper/postProcessSMAA.hpp + code/helper/zbufferReduce.cpp + code/helper/zbufferReduce.hpp + code/material/vulkan/computable.cpp + code/material/vulkan/computable.hpp + code/material/vulkan/drawable.cpp + code/material/vulkan/drawable.hpp code/material/descriptorSetDescription.hpp - code/material/descriptorSetLayout.cpp - code/material/descriptorSetLayout.hpp - code/material/material.cpp - code/material/material.hpp - code/material/materialManager.cpp + code/material/vulkan/descriptorSetLayout.cpp + code/material/vulkan/descriptorSetLayout.hpp code/material/materialManager.hpp - code/material/materialManagerT.hpp code/material/materialProps.cpp code/material/materialProps.h code/material/materialShaderDefinition.cpp code/material/materialShaderDefinition.hpp - code/material/computable.cpp - code/material/computable.hpp - code/material/drawable.cpp - code/material/drawable.hpp + code/material/vulkan/material.cpp + code/material/vulkan/material.hpp + code/material/vulkan/materialManager.cpp + code/material/vulkan/materialManager.hpp + code/material/vulkan/materialPass.cpp + code/material/vulkan/materialPass.hpp + code/material/vulkan/pipeline.cpp + code/material/vulkan/pipeline.hpp code/material/vulkan/pipelineLayout.cpp code/material/vulkan/pipelineLayout.hpp code/material/vulkan/pipelineVertexInputState.cpp code/material/vulkan/pipelineVertexInputState.hpp - code/material/shaderDescription.cpp - code/material/shaderDescription.hpp - code/material/shaderManager.cpp - code/material/shaderManager.hpp - code/material/shaderManagerT.hpp code/material/vulkan/shader.cpp code/material/vulkan/shader.hpp + code/material/vulkan/shaderManager.hpp code/material/vulkan/shaderModule.cpp code/material/vulkan/shaderModule.hpp code/material/specializationConstantDescription.hpp - code/material/vulkan/material.hpp + code/material/vulkan/specializationConstants.cpp + code/material/vulkan/specializationConstants.hpp code/material/vulkan/specializationConstantsLayout.cpp code/material/vulkan/specializationConstantsLayout.hpp code/material/vulkan/vertexDescription.cpp @@ -176,10 +224,10 @@ set(CPP_VULKAN_SRC code/shadow/shadowVsm.hpp code/shadow/shadowVulkan.cpp code/shadow/shadowVulkan.hpp - code/texture/vulkan/imageWrapper.cpp - code/texture/vulkan/imageWrapper.hpp code/texture/vulkan/loaderKtx.cpp code/texture/vulkan/loaderKtx.hpp + code/texture/vulkan/sampler.cpp + code/texture/vulkan/sampler.hpp code/texture/vulkan/texture.cpp code/texture/vulkan/texture.hpp code/texture/vulkan/textureManager.cpp @@ -190,10 +238,21 @@ set(CPP_VULKAN_SRC code/vulkan/extension.hpp code/vulkan/extensionHelpers.cpp code/vulkan/extensionHelpers.hpp + code/vulkan/extensionLib.cpp + code/vulkan/extensionLib.hpp + code/vulkan/framebuffer.cpp + code/vulkan/framebuffer.hpp code/vulkan/MeshObject.cpp code/vulkan/MeshObject.h + code/vulkan/renderContext.cpp + code/vulkan/renderContext.hpp + code/vulkan/renderPass.cpp + code/vulkan/renderPass.hpp + code/vulkan/refHandle.hpp code/vulkan/renderTarget.cpp code/vulkan/renderTarget.hpp + code/vulkan/semaphore.cpp + code/vulkan/semaphore.hpp code/vulkan/TextureFuncts.cpp code/vulkan/TextureFuncts.h code/vulkan/vulkan.cpp @@ -208,46 +267,120 @@ set(CPP_VULKAN_SRC code/vulkan/vulkan_support.hpp ) +# DX12 specific source files here +set(CPP_DX12_SRC + code/main/applicationHelperBaseDx12.cpp + code/main/applicationHelperBaseDx12.hpp + code/dx12/dx12.cpp + code/dx12/dx12.hpp + code/dx12/commandList.cpp + code/dx12/commandList.hpp + code/dx12/descriptorHeapManager.cpp + code/dx12/descriptorHeapManager.hpp + code/dx12/renderPass.cpp + code/dx12/renderPass.hpp + code/dx12/renderContext.cpp + code/dx12/renderContext.hpp + code/dx12/renderTarget.cpp + code/dx12/renderTarget.hpp + code/gui/imguiDx12.cpp + code/gui/imguiDx12.hpp + code/material/dx12/computable.cpp + code/material/dx12/computable.hpp + code/material/dx12/descriptorSetLayout.hpp + code/material/dx12/descriptorSetLayout.cpp + code/material/dx12/drawableDx12.cpp + code/material/dx12/drawableDx12.hpp + code/material/dx12/material.cpp + code/material/dx12/material.hpp + code/material/dx12/materialManager.cpp + code/material/dx12/materialManager.hpp + code/material/dx12/materialPass.hpp + code/material/dx12/materialPass.cpp + code/material/dx12/pipeline.cpp + code/material/dx12/pipeline.hpp + code/material/dx12/pipelineLayout.cpp + code/material/dx12/pipelineLayout.hpp + code/material/dx12/pipelineVertexInputState.cpp + code/material/dx12/pipelineVertexInputState.hpp + code/material/dx12/shader.hpp + code/material/dx12/shaderModule.cpp + code/material/dx12/shaderModule.hpp + code/material/dx12/specializationConstantsLayout.hpp + code/material/dx12/vertexDescription.cpp + code/material/dx12/vertexDescription.hpp + code/memory/dx12/bufferObject.cpp + code/memory/dx12/bufferObject.hpp + code/memory/dx12/indexBufferObject.cpp + code/memory/dx12/indexBufferObject.hpp + code/memory/dx12/memoryMapped.hpp + code/memory/dx12/memoryManager.cpp + code/memory/dx12/memoryManager.hpp + code/memory/dx12/uniform.cpp + code/memory/dx12/uniform.hpp + code/memory/dx12/vertexBufferObject.cpp + code/memory/dx12/vertexBufferObject.hpp + code/texture/dx12/loaderKtx.cpp + code/texture/dx12/loaderKtx.hpp + code/texture/dx12/sampler.cpp + code/texture/dx12/sampler.hpp + code/texture/dx12/texture.cpp + code/texture/dx12/texture.hpp + code/texture/dx12/textureManager.cpp + code/texture/dx12/textureManager.hpp +) + # Platform Specific source files here if(WIN32) - set(CPP_BASE_SRC ${CPP_BASE_SRC} - code/system/windows/windowsAssetManager.cpp - ) - set(CPP_GENERIC_SRC ${CPP_GENERIC_SRC} - code/main/windows/winMain.cpp - code/gui/windows/imguiWindows.cpp - ) -else() - set(CPP_GENERIC_SRC ${CPP_GENERIC_SRC} - code/main/android/androidMain.cpp - code/memory/androidHardwareBuffer.cpp - code/memory/androidHardwareBuffer.hpp - code/gui/android/imguiAndroid.cpp - code/system/android/androidAssetManager.cpp - ) - set(CPP_VULKAN_SRC ${CPP_VULKAN_SRC} - ) - include_directories( ${ANDROID_NDK}/sources/android/native_app_glue/ ) + set(CPP_BASE_SRC ${CPP_BASE_SRC} + code/system/windows/windowsAssetManager.cpp + code/main/windows/winMain.cpp + ) + set(CPP_GENERIC_SRC ${CPP_GENERIC_SRC} + code/gui/windows/imguiWindows.cpp + ) +elseif(ANDROID) + set(CPP_BASE_SRC ${CPP_BASE_SRC} + code/system/android/androidAssetManager.cpp + code/main/android/androidMain.cpp + ) + set(CPP_GENERIC_SRC ${CPP_GENERIC_SRC} + code/memory/androidHardwareBuffer.cpp + code/memory/androidHardwareBuffer.hpp + code/gui/android/imguiAndroid.cpp + ) + set(CPP_VULKAN_SRC ${CPP_VULKAN_SRC} + ) + include_directories( ${ANDROID_NDK}/sources/android/native_app_glue/ ) +elseif(UNIX) + set(CPP_BASE_SRC ${CPP_BASE_SRC} + code/system/linux/linuxAssetManager.cpp + code/main/linux/linuxMain.cpp + ) + set(CPP_GENERIC_SRC ${CPP_GENERIC_SRC} + code/gui/linux/imguiLinux.cpp + ) endif() # Framework support for Ray Tracing/Query if(TRUE) - set(CPP_VULKAN_SRC ${CPP_VULKAN_SRC} - code/vulkanRT/accelerationStructure.cpp - code/vulkanRT/accelerationStructure.hpp - code/vulkanRT/accelerationInstanceBufferObject.cpp - code/vulkanRT/accelerationInstanceBufferObject.hpp - code/vulkanRT/extensionHelpersRT.hpp - code/vulkanRT/meshObjectRT.cpp - code/vulkanRT/meshObjectRT.hpp - code/vulkanRT/meshUpdateRT.cpp - code/vulkanRT/meshUpdateRT.hpp - code/vulkanRT/sceneRT.cpp - code/vulkanRT/sceneRT.hpp - code/vulkanRT/traceable.cpp - code/vulkanRT/traceable.hpp - code/vulkanRT/vulkanRT.cpp - code/vulkanRT/vulkanRT.hpp + set(CPP_VULKAN_SRC ${CPP_VULKAN_SRC} + code/rayTracing/accelerationStructure.hpp + code/vulkanRT/accelerationStructure.cpp + code/vulkanRT/accelerationStructure.hpp + code/vulkanRT/accelerationInstanceBufferObject.cpp + code/vulkanRT/accelerationInstanceBufferObject.hpp + code/vulkanRT/extensionLibRT.hpp + code/vulkanRT/meshObjectRT.cpp + code/vulkanRT/meshObjectRT.hpp + code/vulkanRT/meshUpdateRT.cpp + code/vulkanRT/meshUpdateRT.hpp + code/vulkanRT/sceneRT.cpp + code/vulkanRT/sceneRT.hpp + code/vulkanRT/traceable.cpp + code/vulkanRT/traceable.hpp + code/vulkanRT/vulkanRT.cpp + code/vulkanRT/vulkanRT.hpp ) endif() @@ -255,14 +388,13 @@ endif() set(EXTERNAL_BASE_SRC external/tinyobjloader/tiny_obj_loader.cc ) -set(EXTERNAL_SRC +set(EXTERNAL_GENERIC_SRC external/imgui/imgui.h external/imgui/imgui.cpp external/imgui/imgui_demo.cpp external/imgui/imgui_draw.cpp external/imgui/imgui_tables.cpp external/imgui/imgui_widgets.cpp - external/implot/implot.h external/implot/implot.cpp external/implot/implot_items.cpp @@ -273,15 +405,26 @@ set(EXTERNAL_VULKAN_SRC external/imgui/backends/imgui_impl_vulkan.cpp external/imgui/backends/imgui_impl_vulkan.h ) +set(EXTERNAL_DX12_SRC + external/D3D12MemoryAllocator/src/D3D12MemAlloc.cpp + external/D3D12MemoryAllocator/include/D3D12MemAlloc.h + external/imgui/backends/imgui_impl_dx12.cpp + external/imgui/backends/imgui_impl_dx12.h +) # Platform Specific external files to compile here if(WIN32) - set(EXTERNAL_SRC ${EXTERNAL_SRC} - external/imgui/backends/imgui_impl_win32.cpp - external/imgui/backends/imgui_impl_win32.h - ) -else() - set(EXTERNAL_SRC ${EXTERNAL_SRC} + set(EXTERNAL_GENERIC_SRC ${EXTERNAL_GENERIC_SRC} + external/imgui/backends/imgui_impl_win32.cpp + external/imgui/backends/imgui_impl_win32.h + ) +elseif(ANDROID) + set(EXTERNAL_GENERIC_SRC ${EXTERNAL_GENERIC_SRC} ) +elseif(UNIX) + set(EXTERNAL_GENERIC_SRC ${EXTERNAL_GENERIC_SRC} + external/imgui/backends/imgui_impl_glfw.cpp + external/imgui/backends/imgui_impl_glfw.h + ) endif() # Json schemas here (so they are shown in the Solution Explorer) @@ -295,127 +438,233 @@ set(BASE_NATVIS_SCHEMA external/glm/util/glm.natvis external/json/nlohmann_json.natvis ) -set(NATVIS_SCHEMA - external/VulkanMemoryAllocator/src/vk_mem_alloc.natvis +set(NATVIS_GENERIC_SCHEMA external/imgui/misc/debuggers/imgui.natvis ) +set(NATVIS_VULKAN_SCHEMA + external/VulkanMemoryAllocator/src/vk_mem_alloc.natvis +) +set(NATVIS_DX12_SCHEMA + external/D3D12MemoryAllocator/src/D3D12MemAlloc.natvis +) -# 'Helper' function to call add_sub_directory() and put everything inside a named folder (rather than it littering the top level folder) -define_property(TARGET PROPERTY FOLDER INHERITED BRIEF_DOCS "Set the folder name." FULL_DOCS "Use to organize targets in an IDE.") -function(add_subdirectory_with_folder _folder_name _folder) - add_subdirectory(${_folder} ${ARGN}) - set_property(DIRECTORY "${_folder}" PROPERTY FOLDER "${_folder_name}") -endfunction() - -# Add the external KXT-Software project (restrict to ktx_read library) -if(TRUE) - set(KTX_FEATURE_TOOLS OFF) - set(KTX_FEATURE_TESTS OFF) - set(KTX_FEATURE_STATIC_LIBRARY ON) - set(KTX_FEATURE_WRITE OFF) - set(KTX_FEATURE_VULKAN ON) - set(KTX_FEATURE_GL_UPLOAD OFF) - set(BASISU_SUPPORT_OPENCL OFF) - #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) - # STRING (REGEX REPLACE "/RTC1" "/O2" ${flag_var} "${${flag_var}}") # Enable /O2 optimization level in debug builds (ktx library only) - #endforeach(flag_var) - add_subdirectory_with_folder("external/KTX-Software" external/KTX-Software EXCLUDE_FROM_ALL) - #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) - # STRING (REGEX REPLACE "/O2" "/RTC1" ${flag_var} "${${flag_var}}") # Disable /O2 optimizetion (in debug) - #endforeach(flag_var) +# Create the gfx api agnostic framework libraries +if (FRAMEWORK_framework_base) + add_library(framework_base STATIC ${CPP_BASE_SRC} ${EXTERNAL_BASE_SRC} ${BASE_NATVIS_SCHEMA}) + target_include_directories(framework_base PUBLIC code) + target_include_directories(framework_base PUBLIC external) + target_include_directories(framework_base PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc + target_include_directories(framework_base PUBLIC external/json/single_include) + if(ANDROID) + target_link_libraries(framework_base PUBLIC android log) + target_compile_options(framework_base PRIVATE -Wno-nullability-completeness) + target_compile_options(framework_base PRIVATE "$<$:-O3>" "$<$:-O3>") + target_compile_definitions(framework_base PRIVATE OS_ANDROID) + elseif(WIN32) + target_compile_definitions(framework_base PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) + elseif(UNIX) + target_compile_definitions(framework_base PRIVATE OS_LINUX) + find_package(glfw3 REQUIRED) + target_link_libraries(framework_base glfw) + else() + message(FATAL_ERROR "Need to have either ANDROID or WIN32 or UNIX defined in cmake") + endif() + + # MSVC hierachy + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" FILES ${CPP_BASE_SRC}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_BASE_SRC}) endif() -# Create the gfx api agnostic framework libraries -add_library(framework_base STATIC ${CPP_BASE_SRC} ${EXTERNAL_BASE_SRC} ${BASE_NATVIS_SCHEMA}) -add_library(framework STATIC ${CPP_GENERIC_SRC} ${EXTERNAL_SRC} ${JSON_SCHEMA} ${NATVIS_SCHEMA}) -target_include_directories(framework_base PUBLIC code) -target_include_directories(framework_base PUBLIC external) -target_include_directories(framework_base PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc -target_include_directories(framework_base PUBLIC external/json/single_include) -target_include_directories(framework PUBLIC code) -target_include_directories(framework PUBLIC external) -target_include_directories(framework PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc -target_include_directories(framework PUBLIC external/json/single_include) -target_include_directories(framework PUBLIC external/imgui) -target_include_directories(framework PUBLIC external/implot) +if(FRAMEWORK_framework_generic) + add_library(framework STATIC ${CPP_GENERIC_SRC} ${EXTERNAL_GENERIC_SRC} ${JSON_SCHEMA} ${NATVIS_GENERIC_SCHEMA}) + target_include_directories(framework PUBLIC code) + target_include_directories(framework PUBLIC external) + target_include_directories(framework PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc + target_include_directories(framework PUBLIC external/json/single_include) + target_include_directories(framework PUBLIC external/imgui) + target_include_directories(framework PUBLIC external/implot) + if(ANDROID) + target_compile_options(framework PRIVATE -Wno-nullability-completeness;-Wno-deprecated-volatile;-Wno-deprecated-anon-enum-enum-conversion) + target_compile_options(framework PRIVATE "$<$:-O3>" "$<$:-O3>") + target_compile_definitions(framework PRIVATE OS_ANDROID) + elseif(WIN32) + target_compile_definitions(framework PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) + elseif(UNIX) + target_compile_definitions(framework PRIVATE OS_LINUX) + endif() + + # Vulkan backend or not, set no prototypes + target_compile_definitions(framework PUBLIC IMGUI_IMPL_VULKAN_NO_PROTOTYPES) + + # MSVC hierachy + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" FILES ${CPP_GENERIC_SRC}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_GENERIC_SRC}) +endif() # Make sure our graphics api flags are set (Android in particular may/will not have gone through a top level CMakeLists.txt to set these) if(NOT DEFINED FRAMEWORK_ENABLE_VULKAN) set(FRAMEWORK_ENABLE_VULKAN ON) + set(FRAMEWORK_ENABLE_DX12 OFF) endif() # Vulkan framework support -if(FRAMEWORK_ENABLE_VULKAN) +if(FRAMEWORK_ENABLE_VULKAN AND FRAMEWORK_framework_vulkan) + + if(ANDROID) + set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_ANDROID_KHR) + elseif(WIN32) + set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR) + endif() + + # Add vulkan framework dependency libraries + add_subdirectory(external/volk) + #add_subdirectory(external/SPIRV-Cross) + #add_subdirectory(external/glslang) + #add_subdirectory(external/slang) + add_library(framework_vulkan STATIC ${CPP_VULKAN_SRC} ${EXTERNAL_VULKAN_SRC}) - target_include_directories(framework_vulkan PUBLIC code) - target_include_directories(framework_vulkan PUBLIC external) - target_include_directories(framework_vulkan PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc - target_include_directories(framework_vulkan PUBLIC external/json/single_include) - target_include_directories(framework_vulkan PUBLIC external/imgui) - target_include_directories(framework_vulkan PUBLIC external/implot) + target_include_directories(framework_vulkan PUBLIC code) + target_include_directories(framework_vulkan PUBLIC external) + target_include_directories(framework_vulkan PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc + target_include_directories(framework_vulkan PUBLIC external/json/single_include) + target_include_directories(framework_vulkan PUBLIC external/imgui) + target_include_directories(framework_vulkan PUBLIC external/implot) target_link_libraries(framework_vulkan framework) - find_package(Vulkan REQUIRED) - set_target_properties(Vulkan::Vulkan PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "") # remove the Vulkan incude paths from the local VulkanSDK - target_include_directories(framework_vulkan PUBLIC external/Vulkan-Headers/include) # point the framework to the Vulkan includes that we have as a submodule - if (FRAMEWORK_WINDOWS_ARM64) - target_link_directories(framework_vulkan BEFORE INTERFACE ../project/windows/libs_arm64) - target_link_libraries(framework_vulkan vulkan-1.lib) + get_target_property(VulkanHeaders_INCLUDE_DIRS Vulkan::Headers INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(framework_vulkan PUBLIC "${VulkanHeaders_INCLUDE_DIRS}") + + #set_target_properties(Vulkan::Vulkan PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "") # remove the Vulkan incude paths from the local VulkanSDK + target_include_directories(framework_vulkan PUBLIC external/Vulkan-Headers/include) # point the framework to the Vulkan includes that we have as a submodule + target_include_directories(framework_vulkan PUBLIC external/volk) # point the framework to the volk includes that we have as a submodule + target_include_directories(framework_vulkan PUBLIC external/SPIRV-Cross/include) + target_include_directories(framework_vulkan PUBLIC external/glslang) + target_include_directories(framework_vulkan PUBLIC external/slang/include) + + # Link vulkan framework library dependencies + target_link_libraries(framework_vulkan volk) + #target_link_libraries(framework_vulkan spirv-cross-core) + #target_link_libraries(framework_vulkan spirv-cross-cpp) + #target_link_libraries(framework_vulkan spirv-cross-glsl) + #target_link_libraries(framework_vulkan spirv-cross-hlsl) + #target_link_libraries(framework_vulkan spirv-cross-msl) + #target_link_libraries(framework_vulkan spirv-cross-reflect) + #target_link_libraries(framework_vulkan spirv-cross-util) + #target_link_libraries(framework_vulkan SPIRV) + #target_link_libraries(framework_vulkan glslang) + #target_link_libraries(framework_vulkan glslang-default-resource-limits) + #target_link_libraries(framework_vulkan slang) + + target_compile_definitions(framework_vulkan PUBLIC VK_ENABLE_BETA_EXTENSIONS) + + if(ANDROID) + target_compile_options(framework_vulkan PRIVATE -Wno-nullability-completeness) + target_compile_options(framework_vulkan PRIVATE "$<$:-O3>" "$<$:-O3>") + target_compile_definitions(framework_vulkan PRIVATE OS_ANDROID) + target_compile_definitions(framework_vulkan PUBLIC VK_USE_PLATFORM_ANDROID_KHR) + elseif(WIN32) + target_compile_definitions(framework_vulkan PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) + target_compile_definitions(framework_vulkan PUBLIC VK_USE_PLATFORM_WIN32_KHR) + if (FRAMEWORK_WINDOWS_ARM64) + target_link_directories(framework_vulkan BEFORE INTERFACE ../project/windows/libs_arm64) + endif() + elseif(UNIX) + target_compile_definitions(framework_vulkan PRIVATE OS_LINUX) + endif() + + # MSVC hierachy + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" PREFIX "code" FILES ${CPP_VULKAN_SRC}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_VULKAN_SRC}) +endif() + +# DX12 framework support +if(FRAMEWORK_ENABLE_DX12 AND FRAMEWORK_framework_dx12) + add_library(framework_dx12 STATIC ${CPP_DX12_SRC} ${EXTERNAL_DX12_SRC}) + target_include_directories(framework_dx12 PUBLIC code) + target_include_directories(framework_dx12 PUBLIC external) + target_include_directories(framework_dx12 PUBLIC external/glm) # so code can do #include "glm/mat3x3.hpp" etc + target_include_directories(framework_dx12 PUBLIC external/json/single_include) + target_include_directories(framework_dx12 PUBLIC external/imgui) + target_include_directories(framework_dx12 PUBLIC external/implot) + target_include_directories(framework_dx12 PUBLIC external/D3D12MemoryAllocator/include) + + target_link_libraries(framework_dx12 framework) + target_link_libraries(framework_dx12 d3d12.lib) + target_link_libraries(framework_dx12 dxgi.lib) + target_link_libraries(framework_dx12 dxguid.lib) + + if(ANDROID) + message(FATAL_ERROR "Android build does not support DirectX12") else() - target_link_libraries(framework_vulkan Vulkan::Vulkan) + target_compile_definitions(framework_dx12 PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) endif() + + # MSVC hierachy + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" PREFIX "code" FILES ${CPP_DX12_SRC}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_DX12_SRC}) endif() # KTX library -target_link_libraries(framework ktx_read) +if(FRAMEWORK_framework_generic AND FRAMEWORK_framework_external_KTX-Software) + target_link_libraries(framework PUBLIC ktx_read) +endif() if(ANDROID) - # Setup Android native_app_glue - #include_directories( ${ANDROID_NDK}/sources/android/native_app_glue/ ) - #add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) - #target_link_libraries( framework app-glue ) - message("ndk at ${ANDROID_NDK}") - - # Export ANativeActivity_onCreate(), - # Refer to: https://github.com/android-ndk/ndk/issues/381. - set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") - set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -u android_main") - - target_link_libraries(framework_base android log) - - target_compile_options(framework_base PRIVATE -Wno-nullability-completeness) - target_compile_options(framework PRIVATE -Wno-nullability-completeness;-Wno-deprecated-volatile;-Wno-deprecated-anon-enum-enum-conversion) - target_compile_options(framework_vulkan PRIVATE -Wno-nullability-completeness) - - target_compile_definitions(framework_base PRIVATE OS_ANDROID) - target_compile_definitions(framework PRIVATE OS_ANDROID) - target_compile_definitions(framework_vulkan PRIVATE OS_ANDROID) -endif() + # Setup Android native_app_glue + #include_directories( ${ANDROID_NDK}/sources/android/native_app_glue/ ) + #add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) + #target_link_libraries( framework app-glue ) + message("ndk at ${ANDROID_NDK}") -if(WIN32) - target_compile_definitions(framework_base PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) - target_compile_definitions(framework PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) - if (FRAMEWORK_ENABLE_VULKAN) - target_compile_definitions(framework_vulkan PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS) - endif() + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u android_main") endif() # framework links framework_base -target_link_libraries(framework framework_base) - -if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.8") - # create MSVC hierachy (if appropriate) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" PREFIX "code" FILES ${CPP_VULKAN_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" FILES ${CPP_GENERIC_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/code" FILES ${CPP_BASE_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_BASE_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/external" PREFIX "external" FILES ${EXTERNAL_VULKAN_SRC}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/schema" PREFIX "schema" FILES ${JSON_SCHEMA}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "natvis" FILES ${NATVIS_SCHEMA}) +if(FRAMEWORK_framework_generic) + target_link_libraries(framework PUBLIC framework_base) +endif() + + + +# Potentially build shared library versions too and copy the static library into a more easily accessable location for potential use by other projects. +# We likely will do this for the 'framework' project only (projects that use the framework dont need to re-build the shared library) +if(FRAMEWORK_LIB_OUTPUT) + add_library(framework_base_shared SHARED code/main/framework_static_empty.cpp) + target_link_libraries(framework_base_shared PUBLIC framework_base) + add_custom_command(TARGET framework_base POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${FRAMEWORK_LIB_OUTPUT}/${CMAKE_BUILD_TYPE}/$) + + add_library(framework_shared SHARED code/main/framework_static_empty.cpp) + if(FRAMEWORK_framework_generic) + target_link_libraries(framework_shared PUBLIC framework) + add_custom_command(TARGET framework POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${FRAMEWORK_LIB_OUTPUT}/${CMAKE_BUILD_TYPE}/$) + if(FRAMEWORK_framework_external_KTX-Software) + add_custom_command(TARGET framework POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${FRAMEWORK_LIB_OUTPUT}/${CMAKE_BUILD_TYPE}/$) + endif() + endif() + + if (FRAMEWORK_ENABLE_VULKAN AND FRAMEWORK_framework_vulkan) + add_library(framework_vulkan_shared SHARED code/main/framework_static_empty.cpp) + target_link_libraries(framework_vulkan_shared PUBLIC framework_vulkan) + add_custom_command(TARGET framework_vulkan POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${FRAMEWORK_LIB_OUTPUT}/${CMAKE_BUILD_TYPE}/$) + endif() + if (FRAMEWORK_ENABLE_DX12) + add_library(framework_dx12_shared SHARED code/main/framework_static_empty.cpp) + target_link_libraries(framework_dx12_shared PUBLIC framework_dx12) + endif() + endif() + + +# MSVC hierachy +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/schema" PREFIX "schema" FILES ${JSON_SCHEMA}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "natvis" FILES ${NATVIS_SCHEMA}) + + diff --git a/framework/android/AndroidManifest.xml b/framework/android/AndroidManifest.xml new file mode 100644 index 0000000..4a201d0 --- /dev/null +++ b/framework/android/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/framework/build.gradle b/framework/build.gradle index db6e81a..3b007f4 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -1,50 +1,95 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 26 + compileSdkVersion 33 + namespace "com.qualcomm.sgs.framework" defaultConfig { minSdkVersion 26 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" + targetSdkVersion 33 + ndkVersion "${project.ndkVersionDefault}" ndk { abiFilters 'arm64-v8a' } + externalNativeBuild { + cmake { + arguments "-DFRAMEWORK_DOWNLOAD_EXTERNALS=On", "-DFRAMEWORK_LIB_OUTPUT=${rootDir}/../../build/android/${project.name}", "-DANDROID_ALLOW_UNDEFINED_SYMBOLS=On" + version "3.25.0+" + } + } + } + + lintOptions { + tasks.lint.enabled = false } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - // signingConfig signingConfigs.unsigned + packagingOptions { + //doNotStrip '**/*.so' + } } debug { debuggable = true jniDebuggable = true + packagingOptions { + //doNotStrip '**/*.so' + } } } sourceSets { main { - // jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - // //java.srcDirs = ['src'] - // res.srcDirs = ['project/android/res'] - // assets.srcDirs = ['assets'] - - // Uncomment this to enable validation - //jniLibs { - // srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" - //} + manifest.srcFile 'android/AndroidManifest.xml' } } externalNativeBuild { cmake { - version "3.10.2+" + version "3.25.0+" path 'CMakeLists.txt' + buildStagingDirectory "../build/cxx/${project.name}" } } + task extractFrameworkAarDebug(type: Copy) { + def aarPath = file("${project.buildDir}/outputs/aar/framework-debug.aar") + + from zipTree("${aarPath}") + into "${project.buildDir}/extracted/Debug" + include "**/*.so" + + eachFile { + def segments = it.getRelativePath().getSegments() as List + println segments + it.setPath(segments.tail().join("/")) + return it + } + includeEmptyDirs = false + } + + task extractFrameworkAarRelease(type: Copy) { + def aarPath = file("${project.buildDir}/outputs/aar/framework-release.aar") + doLast { + project.logger.lifecycle("Extracted ${aarPath} file(s).... into ${project.buildDir}/extracted/RelWithDebInfo/") + } + from zipTree("${aarPath}") + into "${project.buildDir}/extracted/RelWithDebInfo/" + include "**/*.so" + + eachFile { + def segments = it.getRelativePath().getSegments() as List + println segments + it.setPath(segments.tail().join("/")) + return it + } + includeEmptyDirs = false + } + + afterEvaluate { + //assembleDebug.finalizedBy extractFrameworkAarDebug + //assembleRelease.finalizedBy extractFrameworkAarRelease + } } diff --git a/framework/cmake/AddShadersDir.cmake b/framework/cmake/AddShadersDir.cmake new file mode 100644 index 0000000..a6f6668 --- /dev/null +++ b/framework/cmake/AddShadersDir.cmake @@ -0,0 +1,37 @@ + +# +# Build shaders +# Add everything with .frag .vert .comp .json (etc) extension from the shaders/ directory and build using Vulkan shader compiler. +# If a sample needs more fine-grained control over this it can 'include(CompileShadersHelper)' and call compile_glsl etc manually. +# +include(CompileShadersHelper) + +function(scan_for_shaders) + # Optional destination path for compiled shaders + set(SHADER_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders") + if(DEFINED SHADER_DESTINATION) + set(SHADER_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_DESTINATION}") + endif() + + # Use project name to generate unique target names + set(target_prefix "${PROJECT_NAME}") + + # Scan through shaders directory looking for shader source files and generate build commands for them + file(GLOB glsl_files "shaders/*.vert" "shaders/*.frag" "shaders/*.comp") + compile_glsl("${glsl_files}" "vulkan1.1" "${SHADER_OUTPUT_PATH}" "${target_prefix}_GLSL") + + # Ray Tracing and Mesh shaders need to target Vulkan 1.2 + file(GLOB rt_files "shaders/*.rgen" "shaders/*.rint" "shaders/*.rahit" "shaders/*.rchit" "shaders/*.rmiss" "shaders/*.rcall" "shaders/*.mesh" "shaders/*.task") + compile_glsl("${rt_files}" "spirv1.4" "${SHADER_OUTPUT_PATH}" "${target_prefix}_RTMESH") + + # HLSL files (compiled to SPIR-V). Entry point assumed to be "main". + file(GLOB hlsl_files "shaders/*.comp.hlsl") + compile_hlsl("${hlsl_files}" "vulkan1.1" "${SHADER_OUTPUT_PATH}" "${target_prefix}_HLSL") + + # JSON configuration files + file(GLOB json_files "shaders/*.json") + copy_json("${json_files}" "json" "${SHADER_OUTPUT_PATH}" "${target_prefix}_JSON") + + # Add shaders (sources) into a 'Shaders' folder for Visual Studio + source_group("shaders" FILES ${SHADERS_SRC}) +endfunction() \ No newline at end of file diff --git a/framework/cmake/CompileAlias.cmake b/framework/cmake/CompileAlias.cmake new file mode 100644 index 0000000..71d19bc --- /dev/null +++ b/framework/cmake/CompileAlias.cmake @@ -0,0 +1,73 @@ +# +# Parse and build shader alias +# +# Inputs: +# INPUT_ALIAS +# OUTPUT_SHADER +# OUTPUT_SHADER_DEP + +file(READ ${INPUT_ALIAS} ALIAS_JSON) + +string(JSON INPUT_SHADER GET ${ALIAS_JSON} Shader) +string(JSON INPUT_DEFINES GET ${ALIAS_JSON} Defines) +string(JSON TARGET_ENV ERROR_VARIABLE TARGET_ENV_JSON_ERROR GET ${ALIAS_JSON} TargetEnv) +if(NOT ${TARGET_ENV_JSON_ERROR} STREQUAL "NOTFOUND") + set(TARGET_ENV "vulkan1.1") +endif() + +string(JSON STAGE ERROR_VARIABLE STAGE_JSON_ERROR GET ${ALIAS_JSON} Stage) +if(NOT ${STAGE_JSON_ERROR} STREQUAL "NOTFOUND") + set(STAGE_OPT "") +else() + set(STAGE_OPT "-S ${STAGE}") +endif() + +# expand out the "Defines: []" JSON array +set(DEFINES "") +string(JSON INPUT_DEFINES_COUNT LENGTH ${INPUT_DEFINES}) +if(INPUT_DEFINES_COUNT GREATER 0) + math(EXPR INPUT_DEFINES_COUNT "${INPUT_DEFINES_COUNT} - 1") + foreach(DEFINE_IDX RANGE ${INPUT_DEFINES_COUNT}) + string(JSON DEFINE GET ${INPUT_DEFINES} ${DEFINE_IDX}) + list(APPEND DEFINES "-D${DEFINE}") + endforeach() +endif() +string(REPLACE ";" " " DEFINES "${DEFINES}") + +message(VERBOSE "Defines ${DEFINES}") + +cmake_path(REMOVE_FILENAME INPUT_ALIAS OUTPUT_VARIABLE INPUT_ALIAS_PATH) +cmake_path(APPEND I ${INPUT_ALIAS_PATH} ${INPUT_SHADER}) +set(INPUT_SHADER ${I}) +cmake_path(GET INPUT_SHADER EXTENSION LAST_ONLY INPUT_SHADER_EXT) + +#message("Shader ${INPUT_SHADER}") +#message("Defines ${INPUT_DEFINES}") +#message("GLSL_VALIDATOR ${GLSL_VALIDATOR}") +#message("Shader ext ${INPUT_SHADER_EXT}") + +if ( "${INPUT_SHADER_EXT}" STREQUAL ".hlsl" ) + # HLSL + message(VERBOSE "COMMAND ${DXC_EXE} ${SHADER_INCLUDE} -T cs_6_7 -spirv -Wno-conversion -fspv-target-env=${TARGET_ENV} ${DEFINES} -enable-16bit-types -E main -Fo ${OUTPUT_SHADER} -MF ${OUTPUT_SHADER_DEP} ${INPUT_SHADER}") + separate_arguments(ARGS NATIVE_COMMAND PROGRAM SEPARATE_ARGS "${DXC_EXE} ${SHADER_INCLUDE} -T cs_6_7 -spirv -Wno-conversion -fspv-target-env=${TARGET_ENV} ${DEFINES} -enable-16bit-types -E main -Fo ${OUTPUT_SHADER} ${INPUT_SHADER}") + execute_process( + COMMAND ${ARGS} "-MF ${OUTPUT_SHADER_DEP}" + COMMAND_ECHO STDOUT + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND ${ARGS} + COMMAND_ECHO STDOUT + COMMAND_ERROR_IS_FATAL ANY + ) +else() + # GLSL + message(VERBOSE "COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet ${STAGE_OPT} --target-env ${TARGET_ENV} ${DEFINES} ${INPUT_SHADER} -o ${OUTPUT_SHADER} --depfile ${OUTPUT_SHADER_DEP}") + separate_arguments(ARGS NATIVE_COMMAND PROGRAM SEPARATE_ARGS "${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet ${STAGE_OPT} --target-env ${TARGET_ENV} ${DEFINES} ${INPUT_SHADER} -o ${OUTPUT_SHADER} --depfile ${OUTPUT_SHADER_DEP}") + execute_process( + COMMAND ${ARGS} + COMMAND_ECHO STDOUT + COMMAND_ERROR_IS_FATAL ANY + ) +endif() + diff --git a/framework/cmake/CompileShadersHelper.cmake b/framework/cmake/CompileShadersHelper.cmake new file mode 100644 index 0000000..ee21404 --- /dev/null +++ b/framework/cmake/CompileShadersHelper.cmake @@ -0,0 +1,172 @@ +# +# Helper for building shaders +# Implements function "compile_glsl" which should be used to compile a list of shaders +# Implements function "compile_hlsl" which should be used to compile a list of shaders +# Implements function "copy_json" which copies a list of shader json configs +# Implements function "compile_alias" which compiles a list of shader .alias files +# + +# Make sure we have the Vulkan compiler +find_program( + GLSL_VALIDATOR + glslangValidator + DOC "Vulkan Shader Compiler (glslangValidator) (is Vulkan SDK installed?)" + REQUIRED +) + +# Make sure we have the DXC compiler. See if it is installed alngside Vulkan first. +if(DEFINED ENV{VULKAN_SDK}) + cmake_path(SET VULKAN_SDK_PATH NORMALIZE $ENV{VULKAN_SDK}) + find_program( + DXC_EXE + dxc + HINTS ${VULKAN_SDK_PATH} + PATH_SUFFIXES Bin + NO_DEFAULT_PATH + DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" + OPTIONAL + ) +endif() + +# We couldnt find dxc installed with Vulkan, look for it on the path (should find the Windows SDK version) +find_program( + DXC_EXE + dxc + DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" + REQUIRED +) + +# Runs the command to get the pluginval version (more recent versions of dcx support --version, older ones dont and print the version inside -help) +if (NOT DEFINED ENV{DXC_VERSION}) + message("DCX compiler found at: ${DXC_EXE}") + execute_process(COMMAND ${DXC_EXE} --help + OUTPUT_VARIABLE DXC_VERSION_RAW + ERROR_VARIABLE DXC_VERSION_RAW) + string(REGEX MATCH "Version: ([^\r\n]+)[\r\n]" + DXC_VERSION "${DXC_VERSION_RAW}") + string(REGEX REPLACE "Version: (.*)" + "\\1" + DXC_VERSION "${DXC_VERSION}") + message( "DXC version: ${DXC_VERSION}" ) + set(ENV{DXC_VERSION} ${DXC_VERSION}) +endif() + + +# Custom shader include direcotry +if(DEFINED SHADER_INCLUDE) + list(JOIN SHADER_INCLUDE ";" SHADER_INCLUDE_TMP) + cmake_path(CONVERT "${SHADER_INCLUDE_TMP}" TO_NATIVE_PATH_LIST SHADER_INCLUDE) + list(TRANSFORM SHADER_INCLUDE PREPEND "-I") +else() + set(SHADER_INCLUDE "-I.") +endif() + +# Ensure we have a place to put the .d dependency files emitted by the compiler +set(DEPENDS_PATH ${CMAKE_CURRENT_BINARY_DIR}/Media/Shaders/) +file(MAKE_DIRECTORY ${DEPENDS_PATH}) +if (NOT DEFINED SHADERS_SRC) + set(SHADERS_SRC "") +endif() + +function(compile_glsl files targetenv dst_dir target_name) + set(output_files "") + foreach(file ${files}) + get_filename_component(output_filename ${file} NAME) + set(output_shader "${dst_dir}/${output_filename}.spv") + set(output_shader_dep "${DEPENDS_PATH}/${output_filename}.spv.d") + + add_custom_command( + OUTPUT ${output_shader} + MAIN_DEPENDENCY ${file} + DEPFILE ${output_shader_dep} + COMMAND ${CMAKE_COMMAND} -E make_directory ${dst_dir} + COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V ${file} -o ${output_filename}.spv --target-env ${targetenv} --quiet --depfile ${output_shader_dep} + COMMAND ${CMAKE_COMMAND} -E rename ${output_filename}.spv ${output_shader} + ) + list(APPEND output_files ${output_shader}) + list(APPEND SHADERS_SRC ${file}) + endforeach() + + add_custom_target(${target_name} ALL DEPENDS ${output_files}) + set_target_properties(${target_name} PROPERTIES FOLDER "shaders") + if(TARGET ${PROJECT_NAME}) + add_dependencies(${PROJECT_NAME} ${target_name}) + endif() + set(SHADERS_SRC ${SHADERS_SRC} PARENT_SCOPE) +endfunction() + +function(compile_hlsl files targetenv dst_dir target_name) + set(output_files "") + foreach(file ${files}) + get_filename_component(output_filename ${file} NAME) + set(output_shader "${dst_dir}/${output_filename}.spv") + set(output_shader_dep "${DEPENDS_PATH}/${output_filename}.spv.d") + + add_custom_command( + OUTPUT ${output_shader} + MAIN_DEPENDENCY ${file} + DEPFILE ${output_shader_dep} + COMMAND ${CMAKE_COMMAND} -E make_directory ${dst_dir} + COMMAND ${DXC_EXE} ${SHADER_INCLUDE} -T cs_6_7 -spirv -Wno-conversion -fspv-target-env=${targetenv} -enable-16bit-types -E main ${file} -Fo ${output_filename}.spv -MF ${output_shader_dep} + COMMAND ${CMAKE_COMMAND} -E rename ${output_filename}.spv ${output_shader} + ) + list(APPEND output_files ${output_shader}) + list(APPEND SHADERS_SRC ${file}) + endforeach() + + add_custom_target(${target_name} ALL DEPENDS ${output_files}) + set_target_properties(${target_name} PROPERTIES FOLDER "shaders") + if(TARGET ${PROJECT_NAME}) + add_dependencies(${PROJECT_NAME} ${target_name}) + endif() + set(SHADERS_SRC ${SHADERS_SRC} PARENT_SCOPE) +endfunction() + +function(copy_json files targetenv dst_dir target_name) + set(output_files "") + foreach(file ${files}) + get_filename_component(output_filename ${file} NAME) + set(output_json "${dst_dir}/${output_filename}") + + add_custom_command( + OUTPUT ${output_json} + MAIN_DEPENDENCY ${file} + COMMAND ${CMAKE_COMMAND} -E make_directory ${dst_dir} + COMMAND ${CMAKE_COMMAND} -E copy ${file} ${output_json} + ) + list(APPEND output_files ${output_json}) + list(APPEND SHADERS_SRC ${file}) + endforeach() + + add_custom_target(${target_name} ALL DEPENDS ${output_files}) + set_target_properties(${target_name} PROPERTIES FOLDER "shaders") + if(TARGET ${PROJECT_NAME}) + add_dependencies(${PROJECT_NAME} ${target_name}) + endif() + set(SHADERS_SRC ${SHADERS_SRC} PARENT_SCOPE) +endfunction() + +function(compile_alias files) + foreach(file ${files}) + set(INPUT_ALIAS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + get_filename_component(OUTPUT_FILENAME ${file} NAME_WLE) + set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) + set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) + cmake_path(NATIVE_PATH INPUT_ALIAS NORMALIZE INPUT_ALIAS_NATIVE) + cmake_path(NATIVE_PATH OUTPUT_SHADER NORMALIZE OUTPUT_SHADER_NATIVE) + cmake_path(NATIVE_PATH OUTPUT_SHADER_DEP NORMALIZE OUTPUT_SHADER_DEP_NATIVE) + + add_custom_command( + OUTPUT ${OUTPUT_SHADER} + MAIN_DEPENDENCY ${INPUT_ALIAS} + DEPFILE ${OUTPUT_SHADER_DEP} + COMMENT "Aliasing ... ${INPUT_ALIAS} to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP})" + #COMMAND "echo Aliasing ... ${INPUT_ALIAS} to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP})" + COMMAND ${CMAKE_COMMAND} -DINPUT_ALIAS=${INPUT_ALIAS} -DOUTPUT_SHADER=${OUTPUT_SHADER} -DOUTPUT_SHADER_DEP=${OUTPUT_SHADER_DEP} -DGLSL_VALIDATOR=${GLSL_VALIDATOR} -DDXC_EXE=${DXC_EXE} -DSHADER_INCLUDE=${SHADER_INCLUDE} -P ${CMAKE_CURRENT_LIST_DIR}/CompileAlias.cmake + ) + list(APPEND SHADERS_SRC ${file}) + + unset(OUTPUT_JSON) + endforeach() + set(SHADERS_SRC ${SHADERS_SRC} PARENT_SCOPE) +endfunction() diff --git a/framework/cmake/ExternalDependencies.cmake b/framework/cmake/ExternalDependencies.cmake new file mode 100644 index 0000000..c54b96a --- /dev/null +++ b/framework/cmake/ExternalDependencies.cmake @@ -0,0 +1,318 @@ +# +# Cmake script to pull down external dependencies needed by the Framework project +# +cmake_minimum_required (VERSION 3.25) +if(POLICY CMP0169) + cmake_policy(SET CMP0169 OLD) +endif() + +set(FRAMEWORK_EXTERNALS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external) +message("FRAMEWORK_EXTERNALS_DIR ${FRAMEWORK_EXTERNALS_DIR}") + + +if (TRUE) + +# build the required externals +if(NOT FRAMEWORK_framework_external) + message(FATAL_ERROR "Expecting FRAMEWORK_framework_external to be set, did you run \"python configure.py\" to generate \"ConfigLocal.cmake\"?") +else() + + if(FRAMEWORK_framework_external_VulkanMemoryAllocator) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_tinyobjloader) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_tinygltf) + # Using as 'header only' library (nothing to do here) + endif() + + if (FRAMEWORK_framework_external_imgui) + endif() + + if(FRAMEWORK_framework_external_Vulkan-Headers) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_glm) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_json) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_eigen) + # Using as 'header only' library (nothing to do here) + endif() + + if(FRAMEWORK_framework_external_KTX-Software) + # Add the external KXT-Software project (restrict to ktx_read library) + set(KTX_FEATURE_TOOLS OFF) + set(KTX_FEATURE_TESTS OFF) + set(KTX_FEATURE_STATIC_LIBRARY ON) + set(KTX_FEATURE_WRITE OFF) + set(KTX_FEATURE_VULKAN ON) + set(KTX_FEATURE_GL_UPLOAD OFF) + set(BASISU_SUPPORT_OPENCL OFF) + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/RTC1" "/O2" ${flag_var} "${${flag_var}}") # Enable /O2 optimization level in debug builds (ktx library only) + #endforeach(flag_var) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # disable 'deprecated' warnings from cmake (ktx library throws a deprecation warning) + add_subdirectory(${FRAMEWORK_EXTERNALS_DIR}/KTX-Software EXCLUDE_FROM_ALL) + set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE) # restore 'deprecated' warnings from cmake + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/O2" "/RTC1" ${flag_var} "${${flag_var}}") # Disable /O2 optimizetion (in debug) + #endforeach(flag_var) + endif() + + if(FRAMEWORK_framework_external_D3D12MemoryAllocator) + # Using as 'header only' library (nothing to do here) + endif() + +endif() + + + + + +else() + +include(FetchContent) + +option(FRAMEWORK_DOWNLOAD_EXTERNALS "Set download/fetch external repositiories" Yes) + +set(FETCHCONTENT_QUIET Off) +set(FETCHCONTENT_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps) +set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # disable 'deprecated' warnings from cmake while we pull the depenencies (some of which have these warnings!) + +# Add the external KXT-Software project +function(AddKtxSubdirectory_full) + #set(_saved_CMAKE_MESSAGE_LOG_LEVEL ${CMAKE_MESSAGE_LOG_LEVEL}) + #set(CMAKE_MESSAGE_LOG_LEVEL NOTICE) # KTX is noisy fetching content, quieting it down! + set(KTX_FEATURE_TOOLS OFF) + set(KTX_FEATURE_TESTS OFF) + set(KTX_FEATURE_STATIC_LIBRARY ON) + set(KTX_FEATURE_WRITE OFF) + set(KTX_FEATURE_VULKAN ON) + set(KTX_FEATURE_GL_UPLOAD OFF) + set(BASISU_SUPPORT_OPENCL OFF) + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/RTC1" "/O2" ${flag_var} "${${flag_var}}") # Enable /O2 optimization level in debug builds (ktx library only) + #endforeach(flag_var) + #add_subdirectory_with_folder(${ktx-software_SOURCE_DIR} ${ktx-software_SOURCE_DIR} EXCLUDE_FROM_ALL) + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/O2" "/RTC1" ${flag_var} "${${flag_var}}") # Disable /O2 optimizetion (in debug) + #endforeach(flag_var) + + message("ktx-software_SOURCE_DIR ${ktx-software_SOURCE_DIR} ktx-software_BINARY_DIR ${ktx-software_BINARY_DIR}") + + add_subdirectory(${ktx-software_SOURCE_DIR} ${ktx-software_BINARY_DIR}) + #set(CMAKE_MESSAGE_LOG_LEVEL ${_saved_CMAKE_MESSAGE_LOG_LEVEL}) +endfunction() + +# Add the external KXT-Software project (restrict to ktx_read library) +function(AddKtxSubdirectory_partial) + set(KTX_FEATURE_TOOLS OFF) + set(KTX_FEATURE_TESTS OFF) + set(KTX_FEATURE_STATIC_LIBRARY ON) + set(KTX_FEATURE_WRITE OFF) + set(KTX_FEATURE_VULKAN ON) + set(KTX_FEATURE_GL_UPLOAD OFF) + set(BASISU_SUPPORT_OPENCL OFF) + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/RTC1" "/O2" ${flag_var} "${${flag_var}}") # Enable /O2 optimization level in debug builds (ktx library only) + #endforeach(flag_var) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # disable 'deprecated' warnings from cmake (ktx library throws a deprecation warning) + add_subdirectory(${ktx-software_SOURCE_DIR} EXCLUDE_FROM_ALL) + set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE) # restore 'deprecated' warnings from cmake + #foreach(flag_var CMAKE_CXX_FLAGS_DEBUG) + # STRING (REGEX REPLACE "/O2" "/RTC1" ${flag_var} "${${flag_var}}") # Disable /O2 optimizetion (in debug) + #endforeach(flag_var) + set_property(DIRECTORY "${ktx-software_SOURCE_DIR}" PROPERTY FOLDER "framework/external/KTX-Software") +endfunction() + + +if(FRAMEWORK_DOWNLOAD_EXTERNALS) + message("Downloading framework external dependencies") +else() + set(FETCHCONTENT_FULLY_DISCONNECTED On) + message("Adding framework external dependencies") +endif() + + +###################### +# TINY OBJECT LOADER # +###################### +message(STATUS ">>> Fetching Tiny Object Loader") +FetchContent_Declare( + tinyobjloader + GIT_REPOSITORY https://github.com/tinyobjloader/tinyobjloader + GIT_TAG e39c1737bc61c8dce28be7932cfe839d408e7838 + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/tinyobjloader +) +FetchContent_MakeAvailable(tinyobjloader) +set_property(DIRECTORY "${tinyobjloader_SOURCE_DIR}" PROPERTY FOLDER "framework/external/tinyobjloader") + +############# +# TINY GLTF # +############# +message(STATUS ">>> Fetching Tiny GLTF") +set(TINYGLTF_BUILD_EXAMPLES OFF CACHE INTERNAL "") +FetchContent_Declare( + tinygltf + GIT_REPOSITORY https://github.com/syoyo/tinygltf + GIT_TAG 925b83627a136d24411067031893dc8ea661444d + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/tinygltf +) +FetchContent_MakeAvailable(tinygltf) +set_property(DIRECTORY "${tinygltf_SOURCE_DIR}" PROPERTY FOLDER "framework/external/tinygltf") + +######### +# IMGUI # +######### +message(STATUS ">>> Fetching ImGui") +FetchContent_Declare( + imgui + #GIT_REPOSITORY https://github.com/ocornut/imgui + #GIT_TAG c71a50deb5ddf1ea386b91e60fa2e4a26d080074 + URL https://github.com/ocornut/imgui/archive/refs/tags/v1.87.tar.gz + URL_MD5 b154fabaa2b3f62e3ec6325be60bbad2 + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/imgui +) +# ImGui doesn't have a cmakelists, add_subdirectory(${imgui_SOURCE_DIR} ${imgui_BINARY_DIR}) will not work here, instead +# we add the source files manually in the framework projects +FetchContent_GetProperties(imgui) +if(NOT imgui_POPULATED) + FetchContent_Populate(imgui) +endif() + +####### +# GLM # +####### +message(STATUS ">>> Fetching GLM") +FetchContent_Declare( + glm + GIT_REPOSITORY https://github.com/g-truc/glm + GIT_TAG 6ad79aae3eb5bf809c30bf1168171e9e55857e45 + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/glm +) +FetchContent_MakeAvailable(glm) +set_property(DIRECTORY "${glm_SOURCE_DIR}" PROPERTY FOLDER "framework/external/glm") + +######## +# JSON # +######## +message(STATUS ">>> Fetching JSON") +FetchContent_Declare( + json + #GIT_REPOSITORY https://github.com/nlohmann/json + #GIT_TAG db78ac1d7716f56fc9f1b030b715f872f93964e4 + URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz + URL_MD5 4b67aba51ddf17c798e80361f527f50e + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/json +) +SET(JSON_BuildTests OFF) +FetchContent_MakeAvailable(json) +set_property(DIRECTORY "${json_SOURCE_DIR}" PROPERTY FOLDER "framework/external/json") + +########################### +# VULKAN MEMORY ALLOCATOR # +########################### +message(STATUS ">>> Fetching Vulkan Memory Allocator") +FetchContent_Declare( + vma + #GIT_REPOSITORY https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator + #GIT_TAG db4c1639bf30c51bbddcd813c6521b3473afa1a1 + URL https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/archive/refs/tags/v3.0.1.tar.gz + URL_MD5 8571f3def0ff86f228e2864c907ba0b3 + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/VulkanMemoryAllocator +) +# Using as 'header only' library +FetchContent_GetProperties(vma) +if(NOT vma_POPULATED) + FetchContent_Populate(vma) +endif() + +################## +# VULKAN HEADERS # +################## +message(STATUS ">>> Fetching Vulkan Headers") +FetchContent_Declare( + Vulkan-Headers + #GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Headers + #GIT_TAG cbcad3c0587dddc768d76641ea00f5c45ab5a278 + URL https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/vulkan-sdk-1.3.296.0.tar.gz + URL_MD5 c1e5eaee17f6dfde2e5b843d1e9380d4 + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/Vulkan-Headers +) +FetchContent_MakeAvailable(Vulkan-Headers) +set_property(DIRECTORY "${vulkan-headers_SOURCE_DIR}" PROPERTY FOLDER "framework/external/Vulkan-Headers") + +######### +# EIGEN # +######### +message(STATUS ">>> Fetching Eigen") +FetchContent_Declare( + eigen + #GIT_REPOSITORY https://gitlab.com/libeigen/eigen + #GIT_TAG 3147391d946bb4b6c68edd901f2add6ac1f31f8c + URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz + URL_MD5 4c527a9171d71a72a9d4186e65bea559 + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/eigen +) +# Eigen is header-only +FetchContent_GetProperties(eigen) +if(NOT eigen_POPULATED) + FetchContent_Populate(eigen) +endif() + +################ +# KTX SOFTWARE # +################ +message(STATUS ">>> Fetching KTX Software") +FetchContent_Declare( + KTX-Software + #GIT_REPOSITORY https://github.com/KhronosGroup/KTX-Software + #GIT_TAG 7149d4fc08bb00c070883d174e46e102a6974a8c + URL https://github.com/KhronosGroup/KTX-Software/archive/refs/tags/v4.1.0.tar.gz + URL_MD5 b35fc412cdb3a00aa92aadcdd1e5f004 + DOWNLOAD_EXTRACT_TIMESTAMP ON + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/KTX-Software +) +# Manually add the KTX library +FetchContent_GetProperties(KTX-Software) +if(NOT ktx-software_POPULATED) + FetchContent_Populate(KTX-Software) + AddKtxSubdirectory_partial() +endif() + +####################### +# DX Memory Allocator # +####################### +message(STATUS ">>> DX Memory Allocator") +FetchContent_Declare( + D3D12MemoryAllocator + GIT_REPOSITORY https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator + GIT_TAG 7597f717c7b32b74d263009ecc15985b517585c7 + SOURCE_DIR ${FRAMEWORK_EXTERNALS_DIR}/D3D12MemoryAllocator +) +FetchContent_MakeAvailable(D3D12MemoryAllocator) +set_property(DIRECTORY "${d3d12memoryallocator_SOURCE_DIR}" PROPERTY FOLDER "framework/external/D3D12MemoryAllocator") + + +set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE) # restore 'deprecated' warnings from cmake + + +endif() + + + + diff --git a/framework/cmake/KTX-Software.diff b/framework/cmake/KTX-Software.diff new file mode 100644 index 0000000..3da5fff --- /dev/null +++ b/framework/cmake/KTX-Software.diff @@ -0,0 +1,40 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5e239daf..e6b93a3f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -217,7 +217,8 @@ if(MSVC) + add_compile_options($<$:/W4>) + add_compile_options( $,/Gz,/O2> ) + elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" +- OR ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") ++ OR ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" ++ OR ${CMAKE_CXX_COMPILER_ID} MATCHES "AppleClang") + add_compile_options(-Wall -Wextra) + add_compile_options( $,-O0,-O3> ) + if(EMSCRIPTEN) +diff --git a/cmake/cputypetest.cmake b/cmake/cputypetest.cmake +index 31d38882..119533c5 100644 +--- a/cmake/cputypetest.cmake ++++ b/cmake/cputypetest.cmake +@@ -1,7 +1,7 @@ + # Copyright 2016, Simon Werta (@webmaster128). + # SPDX-License-Identifier: Apache-2.0 + +-cmake_minimum_required(VERSION 2.8.12) ++cmake_minimum_required(VERSION 3.6) + + set(cputypetest_code " + // +diff --git a/cmake/version.cmake b/cmake/version.cmake +index f9f20015..ce8c5532 100644 +--- a/cmake/version.cmake ++++ b/cmake/version.cmake +@@ -148,7 +148,7 @@ else() + set(KTX_VERSION_PATCH "0") + endif() + else() +- message(WARNING "Error retrieving version from GIT tag. Falling back to 0.0.0-noversion ") ++ #message(WARNING "Error retrieving version from GIT tag. Falling back to 0.0.0-noversion ") + set(KTX_VERSION_MAJOR "0" ) + set(KTX_VERSION_MINOR "0" ) + set(KTX_VERSION_PATCH "0" ) diff --git a/framework/cmake/ModelPackager.cmake b/framework/cmake/ModelPackager.cmake new file mode 100644 index 0000000..4264618 --- /dev/null +++ b/framework/cmake/ModelPackager.cmake @@ -0,0 +1,59 @@ +# +# Model Packager +# Copy model files from specified path to media path or optional destination. +# +function(add_gltf _path) + cmake_parse_arguments(args "" "DESTINATION" "" ${ARGN}) + + if(DEFINED GLOBAL_ASSET_BASE_PATH) + set(_path "${GLOBAL_ASSET_BASE_PATH}/${_path}") + endif() + + # Strip .gltf extension if present + get_filename_component(ext "${_path}" EXT) + if(ext STREQUAL ".gltf") + string(REGEX REPLACE "\\.gltf$" "" _path "${_path}") + endif() + + if(NOT EXISTS "${_path}.gltf") + message(FATAL_ERROR "ModelPackager -> Couldn't find .gltf file on given path ${_path}") + return() + endif() + + set(MODEL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Meshes") + if(DEFINED args_DESTINATION) + set(MODEL_PATH "${args_DESTINATION}") + elseif(DEFINED MESH_DESTINATION) + set(MODEL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${MESH_DESTINATION}") + endif() + + if(NOT EXISTS ${MODEL_PATH}) + file(MAKE_DIRECTORY ${MODEL_PATH}) + endif() + + set(ASSET_CACHE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../build/assets_cache/models") + if(NOT EXISTS ${ASSET_CACHE_PATH}) + file(MAKE_DIRECTORY ${ASSET_CACHE_PATH}) + endif() + + get_filename_component(model_name ${_path} NAME) + + set(cached_gltf "${ASSET_CACHE_PATH}/${model_name}.gltf") + set(cached_bin "${ASSET_CACHE_PATH}/${model_name}.bin") + + # Copy to cache if not already cached + if(NOT EXISTS ${cached_gltf}) + file(COPY "${_path}.gltf" DESTINATION ${ASSET_CACHE_PATH}) + endif() + + if(EXISTS "${_path}.bin" AND NOT EXISTS ${cached_bin}) + file(COPY "${_path}.bin" DESTINATION ${ASSET_CACHE_PATH}) + endif() + + # Copy from cache to destination + file(COPY ${cached_gltf} DESTINATION ${MODEL_PATH}) + + if(EXISTS ${cached_bin}) + file(COPY ${cached_bin} DESTINATION ${MODEL_PATH}) + endif() +endfunction() \ No newline at end of file diff --git a/framework/cmake/TexturePackager.cmake b/framework/cmake/TexturePackager.cmake new file mode 100644 index 0000000..221e83d --- /dev/null +++ b/framework/cmake/TexturePackager.cmake @@ -0,0 +1,181 @@ + +# +# Texture Packager +# Convert PNG textures from the specified path into its media equivalent. +# + +function(add_textures_from_path _path) + cmake_parse_arguments(args "UASTC" "SCALE;DESTINATION" "" ${ARGN}) + + if(DEFINED GLOBAL_ASSET_BASE_PATH) + set(_path "${GLOBAL_ASSET_BASE_PATH}/${_path}") + endif() + + if(NOT DEFINED args_SCALE) + set(args_SCALE 1.0) + endif() + + if(DEFINED args_UASTC) + set(TEXTURE_COMPRESSION "--encode" "uastc" "--zcmp") + else() + set(TEXTURE_COMPRESSION "") + endif() + + set(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") + if(NOT EXISTS ${CONVERTER_TOOL}) + message(WARNING "TexturePackager -> Texture converter tool wasn't found: ${CONVERTER_TOOL}") + return() + endif() + + set(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") + if(DEFINED args_DESTINATION) + set(TEXTURE_PATH "${args_DESTINATION}") + elseif(DEFINED TEXTURE_DESTINATION) + set(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${TEXTURE_DESTINATION}") + endif() + + if(NOT EXISTS ${TEXTURE_PATH}) + file(MAKE_DIRECTORY ${TEXTURE_PATH}) + endif() + + set(ASSET_CACHE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../build/assets_cache/textures") + if(NOT EXISTS ${ASSET_CACHE_PATH}) + file(MAKE_DIRECTORY ${ASSET_CACHE_PATH}) + endif() + + file(GLOB png_textures "${_path}/*.png") + list(LENGTH png_textures total_textures) + + message(STATUS "Preparing to convert ${total_textures} textures from '${_path}'") + + set(current_index 0) + foreach(file ${png_textures}) + math(EXPR current_index "${current_index} + 1") + get_filename_component(output_filename ${file} NAME_WE) + set(cached_output "${ASSET_CACHE_PATH}/${output_filename}.ktx") + set(final_output "${TEXTURE_PATH}/${output_filename}.ktx") + + if(EXISTS ${cached_output}) + file(COPY ${cached_output} DESTINATION ${TEXTURE_PATH}) + else() + set(PARAMS + "--genmipmap" + --scale ${args_SCALE} + ${TEXTURE_COMPRESSION} + "--" + "${cached_output}" + "${file}" + ) + execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} + ERROR_VARIABLE conv_error + RESULT_VARIABLE conv_retval) + + if(conv_error) + message(WARNING "TexturePackager -> ${conv_error}") + endif() + + file(COPY ${cached_output} DESTINATION ${TEXTURE_PATH}) + endif() + + # Progress bar simulation + math(EXPR percent_done "(${current_index} * 100) / ${total_textures}") + math(EXPR filled "(${percent_done} / 10)") + math(EXPR empty "10 - ${filled}") + + set(bar "") + if(filled GREATER 0) + foreach(i RANGE 1 ${filled}) + set(bar "${bar}#") + endforeach() + endif() + + if(empty GREATER 0) + foreach(i RANGE 1 ${empty}) + set(bar "${bar}-") + endforeach() + endif() + + message(STATUS "[${bar}] ${percent_done}% - Processed: '${output_filename}'") + + endforeach() +endfunction() + +function(add_texture _texture_path) + cmake_parse_arguments(args "UASTC" "SCALE;DESTINATION" "" ${ARGN}) + + if(DEFINED GLOBAL_ASSET_BASE_PATH) + set(_texture_path "${GLOBAL_ASSET_BASE_PATH}/${_texture_path}") + endif() + + if(NOT EXISTS ${_texture_path}) + message(WARNING "TexturePackager -> Requested texture doesn't exist: ${_texture_path}") + return() + endif() + + if(NOT DEFINED args_SCALE) + set(args_SCALE 1.0) + endif() + + if(DEFINED args_UASTC) + set(TEXTURE_COMPRESSION "--encode" "uastc" "--zcmp") + else() + set(TEXTURE_COMPRESSION "") + endif() + + set(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") + if(DEFINED args_DESTINATION) + set(TEXTURE_PATH "${args_DESTINATION}") + elseif(DEFINED TEXTURE_DESTINATION) + set(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${TEXTURE_DESTINATION}") + endif() + + set(ASSET_CACHE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../build/assets_cache/textures") + if(NOT EXISTS ${ASSET_CACHE_PATH}) + file(MAKE_DIRECTORY ${ASSET_CACHE_PATH}) + endif() + + if(NOT EXISTS ${TEXTURE_PATH}) + file(MAKE_DIRECTORY ${TEXTURE_PATH}) + endif() + + get_filename_component(output_filename ${_texture_path} NAME_WE) + get_filename_component(output_ext ${_texture_path} EXT) + + set(dst_output_path "${TEXTURE_PATH}/${output_filename}.ktx") + set(cached_output "${ASSET_CACHE_PATH}/${output_filename}.ktx") + + if(EXISTS ${dst_output_path}) + return() + endif() + + if(output_ext STREQUAL ".ktx") + message("Copying KTX texture: '${output_filename}'") + file(COPY ${_texture_path} DESTINATION ${TEXTURE_PATH}) + return() + endif() + + set(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") + if(NOT EXISTS ${CONVERTER_TOOL}) + message(WARNING "TexturePackager -> Texture converter tool wasn't found: ${CONVERTER_TOOL}") + return() + endif() + + message("Converting texture: '${output_filename}' to KTX") + set(PARAMS + "--genmipmap" + --scale ${args_SCALE} + ${TEXTURE_COMPRESSION} + "--" + "${cached_output}" + "${_texture_path}" + ) + execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} + ERROR_VARIABLE conv_error + RESULT_VARIABLE conv_retval) + + if(conv_error) + message(WARNING "TexturePackager -> ${conv_error}") + endif() + + file(COPY ${cached_output} DESTINATION ${TEXTURE_PATH}) +endfunction() \ No newline at end of file diff --git a/framework/code/allocator/threadBufferResource.hpp b/framework/code/allocator/threadBufferResource.hpp new file mode 100644 index 0000000..510fc49 --- /dev/null +++ b/framework/code/allocator/threadBufferResource.hpp @@ -0,0 +1,189 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "threadManagedBufferResourceAllocator.hpp" +#include "threadMonotonicBufferResourceAllocator.hpp" + +namespace core +{ + inline std::span GetThreadMonotonicLocalBufferResourceBlob() + { + static thread_local std::array buffer; + return buffer; + } + + inline std::span GetThreadManagedLocalBufferResourceBlob() + { + static thread_local std::array buffer; + return buffer; + } + + thread_local ThreadMonotonicBufferResourceAllocator s_monotonic_buffer_resource(GetThreadMonotonicLocalBufferResourceBlob().data(), GetThreadMonotonicLocalBufferResourceBlob().size()); + thread_local ThreadManagedBufferResourceAllocator s_managed_buffer_resource(GetThreadManagedLocalBufferResourceBlob().data(), GetThreadManagedLocalBufferResourceBlob().size()); +} + +namespace core +{ +//////////////////////////////////////////////////////////////////////////////// +// Class name: ThreadMonotonicMemoryScope +//////////////////////////////////////////////////////////////////////////////// +class ThreadMonotonicMemoryScope +{ +public: + + ThreadMonotonicMemoryScope() + { + s_buffer_monotonic_resource_usage_count++; + } + + ~ThreadMonotonicMemoryScope() + { + s_buffer_monotonic_resource_usage_count--; + if (s_buffer_monotonic_resource_usage_count == 0) + { + s_monotonic_buffer_resource.release(); + } + } + + inline ThreadMonotonicBufferResourceAllocator& GetResource() + { + return s_monotonic_buffer_resource; + } + + inline operator ThreadMonotonicBufferResourceAllocator& () + { + return s_monotonic_buffer_resource; + } + + inline operator ThreadMonotonicBufferResourceAllocator* () + { + return &s_monotonic_buffer_resource; + } + + template + inline operator std::pmr::polymorphic_allocator<_Ty>() + { + return std::pmr::polymorphic_allocator<_Ty>{ &s_monotonic_buffer_resource }; + } + + /* + * Allocates an object with lifetime tied to the monotonic buffer resource, returning a reference to it + */ + template + std::unique_ptr<_Ty, std::function> AllocateUniquePtr(Args&&... args) + { + auto& memory_resource = GetResource(); + _Ty* ptr = memory_resource.allocate(sizeof(_Ty)); + + ::new((void*)ptr) _Ty(std::forward(args)...); + + auto deleter = [](_Ty* p, std::pmr::memory_resource& alloc) + { + p->~_Ty(); + alloc.deallocate(p, sizeof(_Ty)); + }; + + return { ptr, std::bind(deleter, std::placeholders::_1, memory_resource) }; + } + + /* + * Allocates an object with lifetime tied to the monotonic buffer resource, returning a reference to it + * No destructor is called when it goes out of scope, for unique_ptr behavior use the AllocateUniquePtr() + */ + template + _Ty& AllocateObject(Args&&... args) + { + auto& memory_resource = GetResource(); + auto allocation = memory_resource.allocate_released(sizeof(_Ty), alignof(_Ty)); + _Ty* ptr = static_cast<_Ty*>(allocation); + ::new((void*)ptr) _Ty(std::forward(args)...); + + return *ptr; + } + + /* + * Allocates one or more objects with lifetime tied to the monotonic buffer resource, returning a reference + * to it + * No destructor is called when they go out of scope + */ + template + std::span<_Ty> AllocateObjects(std::size_t count, Args&&... args) + { + auto& memory_resource = GetResource(); + _Ty* ptr = static_cast<_Ty*>(memory_resource.allocate_released(sizeof(_Ty) * count, alignof(_Ty))); + + for (std::size_t i = 0; i < count; ++i) + { + ::new((void*)&ptr[i]) _Ty(std::forward(args)...); + } + + return std::span(ptr, count); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Class name: ThreadAutomaticMonotonicMemoryResource +//////////////////////////////////////////////////////////////////////////////// +class ThreadAutomaticMonotonicMemoryResource +{ +public: + + ThreadAutomaticMonotonicMemoryResource() = default; + ~ThreadAutomaticMonotonicMemoryResource() = default; + + inline std::pmr::memory_resource& GetResource() + { + return s_managed_buffer_resource; + } + + inline operator std::pmr::memory_resource& () + { + return s_managed_buffer_resource; + } + + inline operator std::pmr::memory_resource* () + { + return &s_managed_buffer_resource; + } + + template + inline operator std::pmr::polymorphic_allocator<_Ty>() + { + return std::pmr::polymorphic_allocator<_Ty>{ &GetResource() }; + } + + /* + * Allocates an object with lifetime tied to the monotonic buffer resource, returning a reference to it + */ + template + std::unique_ptr<_Ty, std::function> AllocateUniquePtr(Args&&... args) + { + auto& memory_resource = GetResource(); + _Ty* ptr = memory_resource.allocate(sizeof(_Ty)); + + ::new((void*)ptr) _Ty(std::forward(args)...); + + auto deleter = [](_Ty* p, std::pmr::memory_resource& alloc) + { + p->~_Ty(); + alloc.deallocate(p, sizeof(_Ty)); + }; + + return { ptr, std::bind(deleter, std::placeholders::_1, memory_resource) }; + } +}; +} \ No newline at end of file diff --git a/framework/code/allocator/threadBufferResourceHelper.hpp b/framework/code/allocator/threadBufferResourceHelper.hpp new file mode 100644 index 0000000..9910cc8 --- /dev/null +++ b/framework/code/allocator/threadBufferResourceHelper.hpp @@ -0,0 +1,108 @@ +/* +* This file is based on work by Microsoft Corporation. +* Original license: Apache-2.0 WITH LLVM-exception +* Copyright (c) Microsoft Corporation. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +* Original file: "memory_resource" +* +* Modifications: +* - Add support for thread-specific memory allocation counting +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace core +{ + thread_local uint32_t s_buffer_monotonic_resource_usage_count = 0; + + inline void CheckMemoryResourceAlignment(void* const _Ptr, const size_t _Align) noexcept + { +#if defined(_STL_ASSERT) + _STL_ASSERT((reinterpret_cast(_Ptr) & (_Align - 1)) == 0, + "Upstream resource did not respect alignment requirement."); +#endif + (void)_Ptr; + (void)_Align; + } + + ////////////////// + // STACK BUFFER // + ////////////////// + // Supporting stack buffer for buffer resource types + + template + struct StackBufferEntryLink // base class for intrusive singly-linked structures + { + StackBufferEntryLink* _Next; + }; + + template + struct StackBuffer { // intrusive stack of _Ty, which must derive from StackBufferEntryLink<_Tag> + using _Link_type = StackBufferEntryLink<_Tag>; + +#if defined(_STL_INTERNAL_STATIC_ASSERT) + _STL_INTERNAL_STATIC_ASSERT(is_base_of_v<_Link_type, _Ty>); +#endif + + constexpr StackBuffer() noexcept = default; + constexpr StackBuffer(StackBuffer&& _That) noexcept : _Head{_That._Head} { + _That._Head = nullptr; + } + constexpr StackBuffer& operator=(StackBuffer&& _That) noexcept { + _Head = _That._Head; + _That._Head = nullptr; + return *this; + } + + static constexpr _Link_type* _As_link(_Ty* const _Ptr) noexcept { + return static_cast<_Link_type*>(_Ptr); + } + + static constexpr _Ty* _As_item(_Link_type* const _Ptr) noexcept { + return static_cast<_Ty*>(_Ptr); + } + + constexpr bool _Empty() const noexcept { + return _Head == nullptr; + } + + constexpr _Ty* _Top() const noexcept { + return _As_item(_Head); + } + + constexpr void _Push(_Ty* const _Item) noexcept { + const auto _Ptr = _As_link(_Item); + _Ptr->_Next = _Head; + _Head = _Ptr; + } + + constexpr _Ty* _Pop() noexcept { // pre: _Head != nullptr + const auto _Result = _Head; + _Head = _Head->_Next; + return _As_item(_Result); + } + + constexpr void _Remove(_Ty* const _Item) noexcept { + const auto _Ptr = _As_link(_Item); + for (_Link_type** _Pnext = &_Head; *_Pnext; _Pnext = &(*_Pnext)->_Next) { + if (*_Pnext == _Ptr) { + *_Pnext = _Ptr->_Next; + break; + } + } + } + + _Link_type* _Head = nullptr; + }; + + ////////////////// + ////////////////// + ////////////////// +} \ No newline at end of file diff --git a/framework/code/allocator/threadManagedBufferResourceAllocator.hpp b/framework/code/allocator/threadManagedBufferResourceAllocator.hpp new file mode 100644 index 0000000..60f4b4f --- /dev/null +++ b/framework/code/allocator/threadManagedBufferResourceAllocator.hpp @@ -0,0 +1,209 @@ +/* +* This file is based on work by Microsoft Corporation. +* Original license: Apache-2.0 WITH LLVM-exception +* Copyright (c) Microsoft Corporation. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +* Original file: "memory_resource" +* +* Modifications: +* - Add support for thread-specific memory allocation counting +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "threadBufferResourceHelper.hpp" + +namespace core +{ + class ThreadManagedBufferResourceAllocator : public std::pmr::memory_resource { + public: + explicit ThreadManagedBufferResourceAllocator(std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Resource{_Upstream} {} // initialize this resource with upstream + + ThreadManagedBufferResourceAllocator(const size_t _Initial_size, std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Next_buffer_size(_Round(_Initial_size)), _Resource{_Upstream} { + // initialize this resource with upstream and initial allocation size + } + + ThreadManagedBufferResourceAllocator(void* const _Buffer, const size_t _Buffer_size, + std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Current_buffer(_Buffer), _Space_available(_Buffer_size), + _Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation), _Resource{_Upstream} { + // initialize this resource with upstream and initial buffer + } + + ThreadManagedBufferResourceAllocator() = default; + + explicit ThreadManagedBufferResourceAllocator(const size_t _Initial_size) noexcept // strengthened + : _Next_buffer_size(_Round(_Initial_size)) {} // initialize this resource with initial allocation size + + ThreadManagedBufferResourceAllocator(void* const _Buffer, const size_t _Buffer_size) noexcept // strengthened + : _Current_buffer(_Buffer), _Space_available(_Buffer_size), + _Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation) { + // initialize this resource with initial buffer + } + + ~ThreadManagedBufferResourceAllocator() noexcept override { + release(); + } + + ThreadManagedBufferResourceAllocator(const ThreadManagedBufferResourceAllocator&) = delete; + ThreadManagedBufferResourceAllocator& operator=(const ThreadManagedBufferResourceAllocator&) = delete; + + void release() noexcept /* strengthened */ + { + // Cannot share memory between different threads when using this allocator + assert(m_self_thread_id == std::this_thread::get_id()); + + // If this was hit, you are potentially leaking memory or returning locally allocated memory to + // the callee without the callee guarding such memory + assert(m_allocation_count == 0); + + if (_Chunks._Empty()) { + // nothing to release; potentially continues to use an initial block provided at construction + return; + } + + _Current_buffer = nullptr; + _Space_available = 0; + + // unscale _Next_buffer_size so the next allocation will be the same size as the most recent allocation + // (keep synchronized with ThreadManagedBufferResourceAllocator::_Scale) + const size_t _Unscaled = (_Next_buffer_size / 3 * 2 + alignof(_Header) - 1) & _Max_allocation; + _Next_buffer_size = (std::max)(_Unscaled, _Min_allocation); + + StackBuffer<_Header> _Tmp{}; + std::swap(_Tmp, _Chunks); + while (!_Tmp._Empty()) { + const auto _Ptr = _Tmp._Pop(); + _Resource->deallocate(_Ptr->_Base_address(), _Ptr->_Size, _Ptr->_Align); + } + } + + [[nodiscard]] std::pmr::memory_resource* upstream_resource() const noexcept /* strengthened */ { + // retrieve the upstream resource + return _Resource; + } + + protected: + void* do_allocate(const size_t _Bytes, const size_t _Align) override + { + // Cannot share memory between different threads when using this allocator + assert(m_self_thread_id == std::this_thread::get_id()); + + // allocate from the current buffer or a new larger buffer from upstream + if (!std::align(_Align, _Bytes, _Current_buffer, _Space_available)) { + _Increase_capacity(_Bytes, _Align); + } + + void* const _Result = _Current_buffer; + _Current_buffer = static_cast(_Current_buffer) + _Bytes; + _Space_available -= _Bytes; + + m_allocation_count++; + + return _Result; + } + + void do_deallocate(void* data, size_t, size_t) override + { + // Cannot share memory between different threads when using this allocator + assert(m_self_thread_id == std::this_thread::get_id()); + + m_allocation_count--; + if (!m_allocation_count) + { + release(); + } + } + + bool do_is_equal(const memory_resource& _That) const noexcept override { + return this == &_That; + } + + private: + struct _Header : StackBufferEntryLink<> { // track the size and alignment of an allocation from upstream + size_t _Size; + size_t _Align; + + _Header(const size_t _Size_, const size_t _Align_) : _Size{_Size_}, _Align{_Align_} {} + + void* _Base_address() const { // header is stored at the end of the allocated memory block + return const_cast(reinterpret_cast(this + 1) - _Size); + } + }; + + static constexpr size_t _Min_allocation = 2 * sizeof(_Header); + static constexpr size_t _Max_allocation = 0 - alignof(_Header); + + static constexpr size_t _Round(const size_t _Size) noexcept { + // return the smallest multiple of alignof(_Header) greater than _Size, + // clamped to the range [_Min_allocation, _Max_allocation] + if (_Size < _Min_allocation) { + return _Min_allocation; + } + + if (_Size >= _Max_allocation) { + return _Max_allocation; + } + + // Since _Max_allocation == -alignof(_Header), _Size < _Max_allocation implies that + // (_Size + alignof(_Header) - 1) does not overflow. + return (_Size + alignof(_Header) - 1) & _Max_allocation; + } + + static constexpr size_t _Scale(const size_t _Size) noexcept { + // scale _Size by 1.5, rounding up to a multiple of alignof(_Header), saturating to _Max_allocation + // (keep synchronized with ThreadManagedBufferResourceAllocator::release) +#pragma warning(push) +#pragma warning(disable : 26450) // TRANSITION, VSO-1828677 + constexpr auto _Max_size = (_Max_allocation - alignof(_Header) + 1) / 3 * 2; +#pragma warning(pop) + if (_Size >= _Max_size) { + return _Max_allocation; + } + + return (_Size + (_Size + 1) / 2 + alignof(_Header) - 1) & _Max_allocation; + } + + void _Increase_capacity(const size_t _Bytes, const size_t _Align) { // obtain a new buffer from upstream + if (_Bytes > _Max_allocation - sizeof(_Header)) { + throw; + } + + size_t _New_size = _Next_buffer_size; + if (_New_size < _Bytes + sizeof(_Header)) { + _New_size = (_Bytes + sizeof(_Header) + alignof(_Header) - 1) & _Max_allocation; + } + + const size_t _New_align = (std::max)(alignof(_Header), _Align); + + void* _New_buffer = _Resource->allocate(_New_size, _New_align); + CheckMemoryResourceAlignment(_New_buffer, _New_align); + + _Current_buffer = _New_buffer; + _Space_available = _New_size - sizeof(_Header); + _New_buffer = static_cast(_New_buffer) + _Space_available; + _Chunks._Push(::new (_New_buffer) _Header{_New_size, _New_align}); + + _Next_buffer_size = _Scale(_New_size); + } + + void* _Current_buffer = nullptr; // current memory block to parcel out to callers + size_t _Space_available = 0; // space remaining in current block + size_t _Next_buffer_size = _Min_allocation; // size of next block to allocate from upstream + StackBuffer<_Header> _Chunks{}; // list of memory blocks allocated from upstream + std::pmr::memory_resource* _Resource = std::pmr::get_default_resource(); // upstream resource from which to allocate + + uint32_t m_allocation_count = 0; + std::thread::id m_self_thread_id = std::this_thread::get_id(); + }; +} \ No newline at end of file diff --git a/framework/code/allocator/threadMonotonicBufferResourceAllocator.hpp b/framework/code/allocator/threadMonotonicBufferResourceAllocator.hpp new file mode 100644 index 0000000..ba65f02 --- /dev/null +++ b/framework/code/allocator/threadMonotonicBufferResourceAllocator.hpp @@ -0,0 +1,238 @@ +/* +* This file is based on work by Microsoft Corporation. +* Original license: Apache-2.0 WITH LLVM-exception +* Copyright (c) Microsoft Corporation. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +* Original file: "memory_resource" +* +* Modifications: +* - Add support for thread-specific memory allocation counting +* - Add memory leak guard (m_allocated_resources) +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "threadBufferResourceHelper.hpp" + +namespace core +{ + class ThreadMonotonicBufferResourceAllocator : public std::pmr::memory_resource { + public: + explicit ThreadMonotonicBufferResourceAllocator(std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Resource{_Upstream} {} // initialize this resource with upstream + + ThreadMonotonicBufferResourceAllocator(const size_t _Initial_size, std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Next_buffer_size(_Round(_Initial_size)), _Resource{_Upstream} { + // initialize this resource with upstream and initial allocation size + } + + ThreadMonotonicBufferResourceAllocator(void* const _Buffer, const size_t _Buffer_size, + std::pmr::memory_resource* const _Upstream) noexcept // strengthened + : _Current_buffer(_Buffer), _Space_available(_Buffer_size), + _Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation), _Resource{_Upstream} { + // initialize this resource with upstream and initial buffer + } + + ThreadMonotonicBufferResourceAllocator() = default; + + explicit ThreadMonotonicBufferResourceAllocator(const size_t _Initial_size) noexcept // strengthened + : _Next_buffer_size(_Round(_Initial_size)) {} // initialize this resource with initial allocation size + + ThreadMonotonicBufferResourceAllocator(void* const _Buffer, const size_t _Buffer_size) noexcept // strengthened + : _Current_buffer(_Buffer), _Space_available(_Buffer_size), + _Next_buffer_size(_Buffer_size ? _Scale(_Buffer_size) : _Min_allocation) { + // initialize this resource with initial buffer + } + + ~ThreadMonotonicBufferResourceAllocator() noexcept override { + release(); + } + + ThreadMonotonicBufferResourceAllocator(const ThreadMonotonicBufferResourceAllocator&) = delete; + ThreadMonotonicBufferResourceAllocator& operator=(const ThreadMonotonicBufferResourceAllocator&) = delete; + + void release() noexcept /* strengthened */ { + +#ifndef SAAA_SHIPPING_BUILD + // If this was hit, you are potentially leaking memory or returning locally allocated memory to + // the callee without the callee guarding such memory + // assert(m_allocated_resources.empty()); + assert(m_allocation_count == 0); +#endif + + if (_Chunks._Empty()) { + // nothing to release; potentially continues to use an initial block provided at construction + return; + } + + _Current_buffer = nullptr; + _Space_available = 0; + + // unscale _Next_buffer_size so the next allocation will be the same size as the most recent allocation + // (keep synchronized with ThreadMonotonicBufferResourceAllocator::_Scale) + const size_t _Unscaled = (_Next_buffer_size / 3 * 2 + alignof(_Header) - 1) & _Max_allocation; + _Next_buffer_size = (std::max)(_Unscaled, _Min_allocation); + + StackBuffer<_Header> _Tmp{}; + std::swap(_Tmp, _Chunks); + while (!_Tmp._Empty()) { + const auto _Ptr = _Tmp._Pop(); + _Resource->deallocate(_Ptr->_Base_address(), _Ptr->_Size, _Ptr->_Align); + } + } + + [[nodiscard]] std::pmr::memory_resource* upstream_resource() const noexcept /* strengthened */ { + // retrieve the upstream resource + return _Resource; + } + + void* allocate_released(const size_t _Bytes, const size_t _Align) + { +#ifndef SAAA_SHIPPING_BUILD + // If this was hit, the thread memory resource scope isn't available when the allocation + // was performed, ensure the ThreadMonotonicMemoryScope is left alive (it can be used + // recursivelly between function calls as long as the very first callee retains an active + // scope) + assert(s_buffer_monotonic_resource_usage_count > 0); +#endif + + // allocate from the current buffer or a new larger buffer from upstream + if (!std::align(_Align, _Bytes, _Current_buffer, _Space_available)) { + _Increase_capacity(_Bytes, _Align); + } + + void* const _Result = _Current_buffer; + _Current_buffer = static_cast(_Current_buffer) + _Bytes; + _Space_available -= _Bytes; + + return _Result; + } + + protected: + void* do_allocate(const size_t _Bytes, const size_t _Align) override { + +#ifndef SAAA_SHIPPING_BUILD + // If this was hit, the thread memory resource scope isn't available when the allocation + // was performed, ensure the ThreadMonotonicMemoryScope is left alive (it can be used + // recursivelly between function calls as long as the very first callee retains an active + // scope) + assert(s_buffer_monotonic_resource_usage_count > 0); +#endif + + // allocate from the current buffer or a new larger buffer from upstream + if (!std::align(_Align, _Bytes, _Current_buffer, _Space_available)) { + _Increase_capacity(_Bytes, _Align); + } + + void* const _Result = _Current_buffer; + _Current_buffer = static_cast(_Current_buffer) + _Bytes; + _Space_available -= _Bytes; + +#ifndef SAAA_SHIPPING_BUILD + // m_allocated_resources.emplace(_Result); + m_allocation_count++; +#endif + + return _Result; + } + + void do_deallocate(void* data, size_t, size_t) override + { +#ifndef SAAA_SHIPPING_BUILD + // m_allocated_resources.erase(data); + m_allocation_count--; +#endif + } + + bool do_is_equal(const memory_resource& _That) const noexcept override { + return this == &_That; + } + + private: + struct _Header : StackBufferEntryLink<> { // track the size and alignment of an allocation from upstream + size_t _Size; + size_t _Align; + + _Header(const size_t _Size_, const size_t _Align_) : _Size{_Size_}, _Align{_Align_} {} + + void* _Base_address() const { // header is stored at the end of the allocated memory block + return const_cast(reinterpret_cast(this + 1) - _Size); + } + }; + + static constexpr size_t _Min_allocation = 2 * sizeof(_Header); + static constexpr size_t _Max_allocation = 0 - alignof(_Header); + + static constexpr size_t _Round(const size_t _Size) noexcept { + // return the smallest multiple of alignof(_Header) greater than _Size, + // clamped to the range [_Min_allocation, _Max_allocation] + if (_Size < _Min_allocation) { + return _Min_allocation; + } + + if (_Size >= _Max_allocation) { + return _Max_allocation; + } + + // Since _Max_allocation == -alignof(_Header), _Size < _Max_allocation implies that + // (_Size + alignof(_Header) - 1) does not overflow. + return (_Size + alignof(_Header) - 1) & _Max_allocation; + } + + static constexpr size_t _Scale(const size_t _Size) noexcept { + // scale _Size by 1.5, rounding up to a multiple of alignof(_Header), saturating to _Max_allocation + // (keep synchronized with ThreadMonotonicBufferResourceAllocator::release) +#pragma warning(push) +#pragma warning(disable : 26450) // TRANSITION, VSO-1828677 + constexpr auto _Max_size = (_Max_allocation - alignof(_Header) + 1) / 3 * 2; +#pragma warning(pop) + if (_Size >= _Max_size) { + return _Max_allocation; + } + + return (_Size + (_Size + 1) / 2 + alignof(_Header) - 1) & _Max_allocation; + } + + void _Increase_capacity(const size_t _Bytes, const size_t _Align) { // obtain a new buffer from upstream + if (_Bytes > _Max_allocation - sizeof(_Header)) { + throw; + } + + size_t _New_size = _Next_buffer_size; + if (_New_size < _Bytes + sizeof(_Header)) { + _New_size = (_Bytes + sizeof(_Header) + alignof(_Header) - 1) & _Max_allocation; + } + + const size_t _New_align = (std::max)(alignof(_Header), _Align); + + void* _New_buffer = _Resource->allocate(_New_size, _New_align); + CheckMemoryResourceAlignment(_New_buffer, _New_align); + + _Current_buffer = _New_buffer; + _Space_available = _New_size - sizeof(_Header); + _New_buffer = static_cast(_New_buffer) + _Space_available; + _Chunks._Push(::new (_New_buffer) _Header{_New_size, _New_align}); + + _Next_buffer_size = _Scale(_New_size); + } + + void* _Current_buffer = nullptr; // current memory block to parcel out to callers + size_t _Space_available = 0; // space remaining in current block + size_t _Next_buffer_size = _Min_allocation; // size of next block to allocate from upstream + StackBuffer<_Header> _Chunks{}; // list of memory blocks allocated from upstream + std::pmr::memory_resource* _Resource = std::pmr::get_default_resource(); // upstream resource from which to allocate + +#ifndef SAAA_SHIPPING_BUILD + // std::unordered_set m_allocated_resources; + uint32_t m_allocation_count = 0; +#endif + }; +} \ No newline at end of file diff --git a/framework/code/animation/animation.cpp b/framework/code/animation/animation.cpp index 38daf85..5ccb958 100644 --- a/framework/code/animation/animation.cpp +++ b/framework/code/animation/animation.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,7 +13,7 @@ #include #include #include -#include "glm\gtx\quaternion.hpp" +#include "glm/gtx/quaternion.hpp" static const glm::quat cRotateToYUp{ 0.7071067094802856f, -0.7071068286895752f, 0.f, 0.f }; static const glm::quat cIdentityRotate = glm::identity(); @@ -167,7 +167,7 @@ AnimationIterator AnimationList::MakeIterator(const Animation& animation) } // Recalculate matrix array (one per nodeId) -void AnimationList::UpdateSkeletonMatrixes(const Skeleton& skeleton, AnimationIterator& iterator, std::span nodeMatrixs) +void AnimationList::UpdateSkeletonMatrixes(const Skeleton& skeleton, AnimationIterator& iterator, std::span nodeMatrixs) { const Animation& animation = iterator.animation; float time = iterator.time; @@ -175,26 +175,16 @@ void AnimationList::UpdateSkeletonMatrixes(const Skeleton& skeleton, AnimationIt const auto& animationDataNodes = animation.GetAnimationData().GetNodes(); assert(animationDataNodes.size() == iterator.nodeIterators.size()); - for (uint32_t animationNodeIndex =0; animationNodeIndex<(uint32_t)iterator.nodeIterators.size(); ++animationNodeIndex) + for (uint32_t animationNodeIndex = 0; animationNodeIndex < (uint32_t)iterator.nodeIterators.size(); ++animationNodeIndex) { const auto& nodeId = animationDataNodes[animationNodeIndex].NodeId; // gltf/nodeMatrixs id/index - glm::mat4 Matrix = glm::translate(animation.CalcLocalTranslation(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx )); - Matrix = Matrix * glm::toMat4(animation.CalcLocalRotation(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx)); - Matrix = Matrix * glm::scale(animation.CalcLocalScale(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx)); + glm::mat4 matrix = glm::translate(animation.CalcLocalTranslation(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx)); + matrix = matrix * glm::toMat4(animation.CalcLocalRotation(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx)); + matrix = matrix * glm::scale(animation.CalcLocalScale(animationNodeIndex, time, iterator.nodeIterators[animationNodeIndex].frameIdx)); - nodeMatrixs[nodeId] = glm::transpose(Matrix); - - const SkeletonNodeData* skeletonNodeData = skeleton.GetSkeletonData().GetNodeById(nodeId); - const SkeletonNodeData* skeletonNodeDataParent = skeletonNodeData->Parent(); - if (skeletonNodeDataParent) - { - nodeMatrixs[nodeId] = Matrix * nodeMatrixs[skeletonNodeDataParent->NodeId()]; - } - for (const auto& child : skeletonNodeData->Children()) - { - auto childWorld = Matrix * child.LocalTransform(); - nodeMatrixs[child.NodeId()] = glm::transpose(childWorld); - } + nodeMatrixs[nodeId] = matrix; } + + skeleton.TransformLocalToWorld(nodeMatrixs, nodeMatrixs); } diff --git a/framework/code/animation/animation.hpp b/framework/code/animation/animation.hpp index f37ca2b..542ecc4 100644 --- a/framework/code/animation/animation.hpp +++ b/framework/code/animation/animation.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -100,7 +100,7 @@ class AnimationList /// Recalculate matrix array /// @param nodeMatrixs array of matrixes we want to update (indexed by nodeId) - static void UpdateSkeletonMatrixes( const Skeleton&, AnimationIterator& iterator, std::span nodeMatrixs ); + static void UpdateSkeletonMatrixes( const Skeleton&, AnimationIterator& iterator, std::span nodeMatrixs ); protected: std::vector m_Animations; diff --git a/framework/code/animation/animationData.hpp b/framework/code/animation/animationData.hpp index a0bd2a4..da6fbbf 100644 --- a/framework/code/animation/animationData.hpp +++ b/framework/code/animation/animationData.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/animationGltfLoader.cpp b/framework/code/animation/animationGltfLoader.cpp index 1c6f6e6..d2dd11a 100644 --- a/framework/code/animation/animationGltfLoader.cpp +++ b/framework/code/animation/animationGltfLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/animationGltfLoader.hpp b/framework/code/animation/animationGltfLoader.hpp index 112f4a3..3603445 100644 --- a/framework/code/animation/animationGltfLoader.hpp +++ b/framework/code/animation/animationGltfLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/skeleton.cpp b/framework/code/animation/skeleton.cpp index f90f9d9..bb23e04 100644 --- a/framework/code/animation/skeleton.cpp +++ b/framework/code/animation/skeleton.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,7 +12,6 @@ Skeleton::Skeleton(const SkeletonData& skeletonData) : m_SkeletonData(skeletonData) { - m_WorldTransforms.resize( m_SkeletonData.m_NodesById.size(), glm::identity() ); // iterate through node hierarchy to calculate the world transforms. @@ -32,6 +31,30 @@ Skeleton::Skeleton(const SkeletonData& skeletonData) } } +void Skeleton::TransformLocalToWorld(const std::span local, std::span world) const +{ + assert(local.size() == world.size()); + assert(world.size() == m_SkeletonData.m_NodesById.size()); + + // iterate through node hierarchy to calculate the world transforms. + for (const auto* rootNode : m_SkeletonData.m_RootNodes) + { + const auto TransformTree = [local, world](auto& node, const auto& ParentTransform) -> void { + auto TransformTreeImpl = [local, world](auto& node, const auto& ParentTransform, auto& TransformTreeRef) -> void + { + const auto nodeId = node.NodeId(); + auto WorldTransform = ParentTransform * local[nodeId]; + world[nodeId] = WorldTransform; + for (auto& child : node.Children()) + TransformTreeRef(child, WorldTransform, TransformTreeRef); + }; + TransformTreeImpl(node, ParentTransform, TransformTreeImpl); + }; + TransformTree(*rootNode, glm::identity()); + } + +} + Skeleton::~Skeleton() { } diff --git a/framework/code/animation/skeleton.hpp b/framework/code/animation/skeleton.hpp index 4a4ce73..d980b19 100644 --- a/framework/code/animation/skeleton.hpp +++ b/framework/code/animation/skeleton.hpp @@ -1,13 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once +#include #include #include "system/glm_common.hpp" @@ -25,10 +26,14 @@ class Skeleton Skeleton(const SkeletonData&); ~Skeleton(); - const auto GetTransforms() const { return m_WorldTransforms; } + const auto& GetTransforms() const { return m_WorldTransforms; } const SkeletonData& GetSkeletonData() const { return m_SkeletonData; } + /// Transform local matrices for a skeleton to world space (using the skeleton hierarchy) + /// local and world can point to the same data if desired (skeleton is assumed to be a tree structure) + void TransformLocalToWorld(const std::span local, std::span world) const; + private: - const SkeletonData& m_SkeletonData; //NOT owned, do not delete before deleting this class! + const SkeletonData& m_SkeletonData; //NOT owned, do not delete before deleting this class! std::vector m_WorldTransforms; // Current world transforms by nodeId order (same order as @SkeletonData::NodesById). }; diff --git a/framework/code/animation/skeletonData.cpp b/framework/code/animation/skeletonData.cpp index 015eb79..1084b6a 100644 --- a/framework/code/animation/skeletonData.cpp +++ b/framework/code/animation/skeletonData.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/skeletonData.hpp b/framework/code/animation/skeletonData.hpp index 88462a5..c038b48 100644 --- a/framework/code/animation/skeletonData.hpp +++ b/framework/code/animation/skeletonData.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -54,6 +54,7 @@ class SkeletonData SkeletonData(std::vector&& Nodes, std::vector&& NodesById, std::vector&& rootNodes); const SkeletonNodeData* GetNodeById(uint32_t nodeId) const { return m_NodesById[nodeId]; } + const auto& GetRootNodes() const { return m_RootNodes; } protected: friend class Skeleton; diff --git a/framework/code/animation/skeletonGltfLoader.cpp b/framework/code/animation/skeletonGltfLoader.cpp index aef3f8c..1e10c43 100644 --- a/framework/code/animation/skeletonGltfLoader.cpp +++ b/framework/code/animation/skeletonGltfLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/skeletonGltfLoader.hpp b/framework/code/animation/skeletonGltfLoader.hpp index bb3d6c3..27f2c79 100644 --- a/framework/code/animation/skeletonGltfLoader.hpp +++ b/framework/code/animation/skeletonGltfLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/animation/skin.cpp b/framework/code/animation/skin.cpp new file mode 100644 index 0000000..2bf10c3 --- /dev/null +++ b/framework/code/animation/skin.cpp @@ -0,0 +1,22 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "skin.hpp" + +Skin::Skin(SkinData&& d) noexcept : m_SkinData(std::move(d)) +{ + m_SkinTransformMatrixes.resize(m_SkinData.GetNumJoints(), glm::identity()); +} + +void Skin::UpdateSkinMatrixes(const std::span sceneMatrixs) +{ + assert(m_SkinTransformMatrixes.size() == m_SkinData.GetNumJoints()); + + const auto& inverseBindMatrices = m_SkinData.GetInverseBindMatrices(); + for (uint32_t i = 0; int jointNodeId : m_SkinData.GetJoints()) + m_SkinTransformMatrixes[i++] = sceneMatrixs[jointNodeId] * inverseBindMatrices[i];//glm::transpose(m_SceneTransformMatrixes[jointNodeId]) * inverseBindMatrices[i]; +} diff --git a/framework/code/animation/skin.hpp b/framework/code/animation/skin.hpp new file mode 100644 index 0000000..5434aff --- /dev/null +++ b/framework/code/animation/skin.hpp @@ -0,0 +1,31 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "skinData.hpp" + +#include +#include +#include "system/glm_common.hpp" + + +class Skin +{ + Skin(const Skin&) = delete; + Skin& operator=(const Skin&) = delete; + + SkinData m_SkinData; + std::vector m_SkinTransformMatrixes; +public: + Skin(SkinData&& d) noexcept; + typedef decltype(m_SkinTransformMatrixes)::value_type tSkinMatrix; + + const auto& GetSkinTransformMatrices() const { return m_SkinTransformMatrixes; } + + void UpdateSkinMatrixes(const std::span sceneMatrixs); +}; diff --git a/framework/code/animation/skinData.cpp b/framework/code/animation/skinData.cpp new file mode 100644 index 0000000..1e9e9b1 --- /dev/null +++ b/framework/code/animation/skinData.cpp @@ -0,0 +1,8 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "skinData.hpp" diff --git a/framework/code/animation/skinData.hpp b/framework/code/animation/skinData.hpp new file mode 100644 index 0000000..e331285 --- /dev/null +++ b/framework/code/animation/skinData.hpp @@ -0,0 +1,37 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "system/glm_common.hpp" + +class SkinData { + friend class SkinGltfProcessor; + SkinData(const SkinData&) = delete; + SkinData& operator=(const SkinData&) = delete; + SkinData(std::string name, std::vector&& nodeIds, std::vector&& inverseBindMatrices) noexcept + : Name(std::move(name)) + , NodeIds(std::move(nodeIds)) + , m_InverseBindMatrices(std::move(inverseBindMatrices)) + {} +public: + SkinData(SkinData&&) noexcept = default; + SkinData& operator=(SkinData&&) noexcept = default; + + size_t GetNumJoints() const { return NodeIds.size(); } + const auto& GetJoints() const { return NodeIds; } + + const auto& GetInverseBindMatrices() const { return m_InverseBindMatrices; } + +protected: + std::string Name; + std::vector NodeIds; ///< gltf node indices (one per joint) + std::vector m_InverseBindMatrices; ///< one per NodeId (one per joint) +}; diff --git a/framework/code/animation/skinGltfLoader.cpp b/framework/code/animation/skinGltfLoader.cpp new file mode 100644 index 0000000..12c2acf --- /dev/null +++ b/framework/code/animation/skinGltfLoader.cpp @@ -0,0 +1,43 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "skinGltfLoader.hpp" +#include "skinData.hpp" +#include "mesh/meshLoader.hpp" +#include "system/os_common.h" +#include +#include + + +SkinGltfProcessor::SkinGltfProcessor() noexcept {} +SkinGltfProcessor::~SkinGltfProcessor() {} + + +bool SkinGltfProcessor::operator()(const tinygltf::Model& ModelData) +{ + const tinygltf::Scene& SceneData = ModelData.scenes[ModelData.defaultScene]; + + m_skins.reserve(ModelData.skins.size()); + for (const auto& skin : ModelData.skins) + { + + // Grab a pointer to the inverse bind matrix data (one per joint in the skin). + const tinygltf::Accessor& inverseBindMatricesAccessorData = ModelData.accessors[skin.inverseBindMatrices]; + const size_t inverseBindMatricesDataItemCount = inverseBindMatricesAccessorData.count; + const auto& ibmBuffer = ModelData.bufferViews[inverseBindMatricesAccessorData.bufferView]; + + if ((ibmBuffer.byteStride != 4 && ibmBuffer.byteStride != 0/*packed/default*/) || (ibmBuffer.byteOffset & 3) != 0) + { + LOGE("Error reading time data for gltf skin inverse bind matrices\"%s\" (expecting contiguous array of aligned matrix data)", skin.name.c_str()); + return false; + } + std::span ibmDataSrcPtr{(glm::mat4*)&ModelData.buffers[ibmBuffer.buffer].data[ibmBuffer.byteOffset + inverseBindMatricesAccessorData.byteOffset], inverseBindMatricesDataItemCount}; + + m_skins.push_back(SkinData{skin.name, std::vector{skin.joints}/*copy*/, std::vector{ibmDataSrcPtr.begin(), ibmDataSrcPtr.end()}}); + } + return true; +} diff --git a/framework/code/animation/skinGltfLoader.hpp b/framework/code/animation/skinGltfLoader.hpp new file mode 100644 index 0000000..d90f46c --- /dev/null +++ b/framework/code/animation/skinGltfLoader.hpp @@ -0,0 +1,32 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include + +// forward declarations +class SkinData; + +namespace tinygltf { + class Model; +}; + +/// Gltf model processor for skeleton (node hierarchy) data (passed in to @MeshLoader::LoadGltf) +/// Creates and populates a @SkeletonData based on the nodes inside the gltf model. +/// @ingroup Animation +class SkinGltfProcessor +{ + SkinGltfProcessor& operator=(const SkinGltfProcessor&) = delete; + SkinGltfProcessor(const SkinGltfProcessor&) = delete; +public: + SkinGltfProcessor() noexcept; + ~SkinGltfProcessor(); + bool operator()(const tinygltf::Model& ModelData); + std::vector m_skins; +}; diff --git a/framework/code/camera/camera.cpp b/framework/code/camera/camera.cpp index f6c58ba..3e681cf 100644 --- a/framework/code/camera/camera.cpp +++ b/framework/code/camera/camera.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,6 +12,7 @@ Camera::Camera() : m_Orthographic(false) + , m_Cut(false) , m_Aspect(1.0) , m_FOV(PI*0.25f) , m_NearClip(1.0f) @@ -21,6 +22,7 @@ Camera::Camera() , m_Jitter() , m_ProjectionMatrix(1.0) , m_ViewMatrix() + , m_ViewMatrixPreTranslation() , m_InverseViewProjection() { UpdateMatrices(); // set sensible proj/view matrices @@ -70,6 +72,13 @@ void Camera::SetJitter(const glm::vec2 jitter) m_Jitter = jitter; } +//----------------------------------------------------------------------------- +void Camera::SetCut(bool cut) +//----------------------------------------------------------------------------- +{ + m_Cut = cut; +} + //----------------------------------------------------------------------------- void Camera::UpdateMatrices() //----------------------------------------------------------------------------- @@ -91,6 +100,7 @@ void Camera::UpdateMatrices() //m_ViewMatrix = glm::translate(m_ViewMatrix, -m_CurrentCameraPos); auto translation = glm::mat4( 1.0f ); + m_ViewMatrixPreTranslation = m_ViewMatrix; translation = glm::translate( translation, -m_CurrentCameraPos ); m_ViewMatrix = m_ViewMatrix * translation; @@ -103,10 +113,6 @@ void Camera::UpdateMatrices() glm::mat4 Camera::GetProjectionWithJitter(const glm::vec3 jitter) const //----------------------------------------------------------------------------- { - auto jitteredProj = m_ProjectionMatrixNoJitter; - jitteredProj[2][0] += jitter.x; - jitteredProj[2][1] += jitter.y; - jitteredProj[2][2] += jitter.z; - return jitteredProj; + glm::mat4 jm = glm::translate(jitter); + return jm * m_ProjectionMatrixNoJitter; } - diff --git a/framework/code/camera/camera.hpp b/framework/code/camera/camera.hpp index 8d02684..6bcbf9b 100644 --- a/framework/code/camera/camera.hpp +++ b/framework/code/camera/camera.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -43,6 +43,9 @@ class Camera /// Set Jitter amount (applied in UpdateMatrices) void SetJitter(const glm::vec2 Jitter); + /// Set Camera 'cut' flag + void SetCut(bool cut); + /// Return current projection matrix with the given jitter amount added glm::mat4 GetProjectionWithJitter(const glm::vec3 jitter) const; @@ -50,7 +53,7 @@ class Camera template void UpdateController(float ElapsedTimeSeconds, T_CameraController& CameraController) { - CameraController.Update(ElapsedTimeSeconds, m_CurrentCameraPos, m_CurrentCameraRot); + CameraController.Update(ElapsedTimeSeconds, m_CurrentCameraPos, m_CurrentCameraRot, m_Cut); } /// Update camera matrices (based on current rotation/position) @@ -62,16 +65,20 @@ class Camera glm::mat4 ProjectionMatrix() const { return m_ProjectionMatrix; } ///<@returns the camera projection matrix (as computed by UpdateMatrices) glm::mat4 ProjectionMatrixNoJitter() const { return m_ProjectionMatrixNoJitter; } ///<@returns the camera projection matrix without any Jitter applied glm::mat4 ViewMatrix() const { return m_ViewMatrix; } ///<@returns the camera view matrix (as computed by UpdateMatrices) + glm::mat4 ViewMatrixPreTranslation() const { return m_ViewMatrixPreTranslation; }; glm::vec3 ViewDirection() const { return m_ViewMatrix[2]; } ///<@returns the current camera direction (view along the z axis) glm::mat4 InverseViewProjection() const { return m_InverseViewProjection; }///<@returns the inverse of the view projection matrix float NearClip() const { return m_NearClip; } ///<@returns the camera near clip distance float FarClip() const { return m_FarClip; } ///<@returns the camera far clip distance - float Fov() const { return m_FOV; } ///<@returns the camera field of view + float Fov() const { return m_FOV; } ///<@returns the camera field of view (in the vertical direction) float Aspect() const { return m_Aspect; } ///<@returns the camera aspect ratio + glm::vec2 Jitter() const { return m_Jitter; } ///<@returns the camera jitter offsets + bool Cut() const { return m_Cut; } ///<@returns if the camera position was suddently 'cut' (dependent on camera controller setting the m_Cut flag) protected: // Camera parameters bool m_Orthographic; + bool m_Cut; float m_Aspect; float m_FOV; float m_NearClip; @@ -88,5 +95,6 @@ class Camera glm::mat4 m_ProjectionMatrixNoJitter; glm::mat4 m_ProjectionMatrix; glm::mat4 m_ViewMatrix; + glm::mat4 m_ViewMatrixPreTranslation; glm::mat4 m_InverseViewProjection; }; diff --git a/framework/code/camera/cameraController.cpp b/framework/code/camera/cameraController.cpp index 28ab76c..caa5ef7 100644 --- a/framework/code/camera/cameraController.cpp +++ b/framework/code/camera/cameraController.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -89,6 +89,9 @@ enum CameraController::KeysDownBits CameraControllerBase::KeyToBits(uint32_t key case 'E': return KeysDownBits::eUp; break; + case 16: + return KeysDownBits::eShift; + break; default: return KeysDownBits::eNone; break; @@ -154,8 +157,9 @@ void CameraController::TouchUpEvent(int iPointerID, float xPos, float yPos) //----------------------------------------------------------------------------- -void CameraController::Update(float frameTime, glm::vec3& position, glm::quat& rot) +void CameraController::Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) { + cut = false; if (m_touchDown) { auto mouseDiff = m_LastMousePosition - m_CurrentMousePosition; @@ -168,28 +172,29 @@ void CameraController::Update(float frameTime, glm::vec3& position, glm::quat& r } + const float moveSpeed = m_MoveSpeed * ((m_KeysDown & KeysDownBits::eShift) == 0 ? 1.0f : 4.0f); if (m_KeysDown != KeysDownBits::eNone) { // Position change is relative to the camera rotation/direction. if (m_KeysDown & (KeysDownBits::eLeft | KeysDownBits::eRight)) { float direction = (m_KeysDown & KeysDownBits::eLeft) ? -1.0f : 1.0f; - position += rot * cVecViewRight * frameTime * m_MoveSpeed * direction; + position += rot * cVecViewRight * frameTime * moveSpeed * direction; } if (m_KeysDown & (KeysDownBits::eForward | KeysDownBits::eBackward)) { float direction = (m_KeysDown & KeysDownBits::eBackward) ? -1.0f : 1.0f; - position += rot * cVecViewForward * frameTime * m_MoveSpeed * direction; + position += rot * cVecViewForward * frameTime * moveSpeed * direction; } if (m_KeysDown & KeysDownBits::eUp) { glm::vec3 VecUp = glm::vec3(0.0f, 1.0f, 0.0f); - position += VecUp * frameTime * m_MoveSpeed; + position += VecUp * frameTime * moveSpeed; } if (m_KeysDown & KeysDownBits::eDown) { glm::vec3 VecUp = glm::vec3(0.0f, -1.0f, 0.0f); - position += VecUp * frameTime * m_MoveSpeed; + position += VecUp * frameTime * moveSpeed; } } } diff --git a/framework/code/camera/cameraController.hpp b/framework/code/camera/cameraController.hpp index 9246e64..bf2edab 100644 --- a/framework/code/camera/cameraController.hpp +++ b/framework/code/camera/cameraController.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -24,6 +24,7 @@ class CameraControllerBase virtual void TouchDownEvent(int iPointerID, float xPos, float yPos) {} virtual void TouchMoveEvent(int iPointerID, float xPos, float yPos) {} virtual void TouchUpEvent(int iPointerID, float xPos, float yPos) {} + virtual void TouchDoubleClickEvent(int iPointerID) {} /// Set screen size (for mouse events) void SetSize(uint32_t width, uint32_t height); @@ -38,7 +39,8 @@ class CameraControllerBase /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller - virtual void Update(float frameTime, glm::vec3& position, glm::quat& rot) = 0; + /// @param cut in - current camera cut, out - camera cut after applying controller + virtual void Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) = 0; protected: enum KeysDownBits { @@ -48,7 +50,8 @@ class CameraControllerBase eLeft = 0x4, eRight = 0x8, eUp = 0x10, - eDown = 0x20 + eDown = 0x20, + eShift = 0x40 }; typedef std::underlying_type_t KeysDownBits_base; static enum KeysDownBits KeyToBits(uint32_t key); @@ -86,7 +89,8 @@ class CameraController : public CameraControllerBase /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller - void Update(float frameTime, glm::vec3& position, glm::quat& rot) override; + /// @param cut in - current camera cut, out - camera cut after applying controller + void Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut ) override; protected: glm::vec2 m_LastMousePosition; diff --git a/framework/code/camera/cameraControllerAnim.cpp b/framework/code/camera/cameraControllerAnim.cpp index 567dedd..110c65b 100644 --- a/framework/code/camera/cameraControllerAnim.cpp +++ b/framework/code/camera/cameraControllerAnim.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,6 +9,7 @@ #include "cameraControllerAnim.hpp" #include "animation/animation.hpp" #include +#include #include static const float cMouseRotSpeed = 0.1f; @@ -47,10 +48,16 @@ void CameraControllerAnim::SetPathAnimation(const Animation * pAnimation, int no /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller -void CameraControllerAnim::Update(float frameTime, glm::vec3& position, glm::quat& rot) +/// @param cut in - current camera cut, out - camera cut after applying controller +void CameraControllerAnim::Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) { - m_CameraAnimationTime += frameTime * m_CameraAnimationSpeed; - m_CameraAnimationTime = std::fmodf(m_CameraAnimationTime, m_Animation->GetEndTime()); + cut = false; + const float cameraTimeStep = frameTime * m_CameraAnimationSpeed; + m_CameraAnimationTime += cameraTimeStep; + if ((m_CameraAnimationTime > m_Animation->GetEndTime() && cameraTimeStep > 0.0f) || + (m_CameraAnimationTime < 0.0f && cameraTimeStep < 0.0f)) + cut = true; + m_CameraAnimationTime = ::fmodf(m_CameraAnimationTime, m_Animation->GetEndTime()); if (m_CameraAnimationTime < 0.0f) m_CameraAnimationTime += m_Animation->GetEndTime(); @@ -133,8 +140,9 @@ void CameraControllerAnimControllable::TouchUpEvent( int iPointerID, float xPos, /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller -void CameraControllerAnimControllable::Update( float frameTime, glm::vec3& position, glm::quat& rot ) +void CameraControllerAnimControllable::Update( float frameTime, glm::vec3& position, glm::quat& rot, bool& cut ) { + cut = false; if (m_LookaroundTouchId != -1) { auto mouseDiff = m_LastLookaroundTouchPosition - m_CurrentLookaroundTouchPosition; @@ -152,9 +160,9 @@ void CameraControllerAnimControllable::Update( float frameTime, glm::vec3& posit m_CameraZoom += m_CameraZoomVelocity; if (m_CameraZoom > m_CameraZoomMax) - m_CameraZoom *= std::expf( 0.1f * (m_CameraZoomMax - m_CameraZoom) ); + m_CameraZoom *= ::expf( 0.1f * (m_CameraZoomMax - m_CameraZoom) ); else if (m_CameraZoom < m_CameraZoomMin) - m_CameraZoom *= std::expf( 0.1f * (m_CameraZoom - m_CameraZoomMin) ); + m_CameraZoom *= ::expf( 0.1f * (m_CameraZoom - m_CameraZoomMin) ); if (m_MovementTouchId != -1) { @@ -174,9 +182,9 @@ void CameraControllerAnimControllable::Update( float frameTime, glm::vec3& posit m_CameraElevationRotation += m_directionVelocity.y; if (m_CameraElevationRotation > m_CameraElevationRotationMax) - m_CameraElevationRotation *= std::expf( 0.1f * (m_CameraElevationRotationMax - m_CameraElevationRotation) ); + m_CameraElevationRotation *= ::expf( 0.1f * (m_CameraElevationRotationMax - m_CameraElevationRotation) ); else if (m_CameraElevationRotation < m_CameraElevationRotationMin) - m_CameraElevationRotation *= std::expf( 0.1f * (m_CameraElevationRotation - m_CameraElevationRotationMin) ); + m_CameraElevationRotation *= ::expf( 0.1f * (m_CameraElevationRotation - m_CameraElevationRotationMin) ); // Countdown the timer until the camera automatically moves m_AnimationRestartTimer -= frameTime; @@ -194,8 +202,12 @@ void CameraControllerAnimControllable::Update( float frameTime, glm::vec3& posit else animationSpeedLerp = 0.0f; - m_CameraAnimationTime += frameTime * m_CameraAnimationSpeed * animationSpeedLerp * m_animationLastRotationDirection; - m_CameraAnimationTime = std::fmodf( m_CameraAnimationTime, m_Animation->GetEndTime() ); + const float cameraTimeStep = frameTime * m_CameraAnimationSpeed * animationSpeedLerp * m_animationLastRotationDirection; + m_CameraAnimationTime += cameraTimeStep; + if ((m_CameraAnimationTime > m_Animation->GetEndTime() && cameraTimeStep > 0.0f) || + (m_CameraAnimationTime < 0.0f && cameraTimeStep < 0.0f)) + cut = true; + m_CameraAnimationTime = ::fmodf( m_CameraAnimationTime, m_Animation->GetEndTime() ); if (m_CameraAnimationTime < 0.0f) m_CameraAnimationTime += m_Animation->GetEndTime(); diff --git a/framework/code/camera/cameraControllerAnim.hpp b/framework/code/camera/cameraControllerAnim.hpp index c0828a3..b3cb425 100644 --- a/framework/code/camera/cameraControllerAnim.hpp +++ b/framework/code/camera/cameraControllerAnim.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -39,7 +39,8 @@ class CameraControllerAnim : public CameraControllerBase /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller - void Update(float frameTime, glm::vec3& position, glm::quat& rot) override; + /// @param cut in - current camera cut, out - camera cut after applying controller + void Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) override; protected: const Animation* m_Animation = nullptr; ///< pointer to the animation controlling this camera postion (not owned by us) @@ -71,7 +72,7 @@ class CameraControllerAnimControllable : public CameraControllerAnim /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller - void Update( float frameTime, glm::vec3& position, glm::quat& rot ) override; + void Update( float frameTime, glm::vec3& position, glm::quat& rot, bool& cut ) override; protected: glm::vec2 m_LastMovementTouchPosition; diff --git a/framework/code/camera/cameraControllerTouch.cpp b/framework/code/camera/cameraControllerTouch.cpp index c617a4d..271b49e 100644 --- a/framework/code/camera/cameraControllerTouch.cpp +++ b/framework/code/camera/cameraControllerTouch.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,7 +9,7 @@ #include "cameraControllerTouch.hpp" static const float cMouseRotSpeed = 0.1f; -static const float cMouseMoveSpeeed = 0.001f; +static const float cTouchMoveSpeedMultipler = 0.001f; // Helpers constexpr glm::vec3 cVecViewRight = glm::vec3( 1.0f, 0.0f, 0.0f ); // x-direction (vector pointing to right of screen)! @@ -91,15 +91,15 @@ void CameraControllerTouch::TouchUpEvent(int iPointerID, float xPos, float yPos) //----------------------------------------------------------------------------- -void CameraControllerTouch::Update(float frameTime, glm::vec3& position, glm::quat& rot) +void CameraControllerTouch::Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) { + cut = false; if (m_LookaroundTouchId != -1) { auto mouseDiff = m_LastLookaroundTouchPosition - m_CurrentLookaroundTouchPosition; auto angleChange = mouseDiff * frameTime * m_RotateSpeed; m_LastLookaroundTouchPosition = m_CurrentLookaroundTouchPosition; - // one (touch) rotation axis is relative to the view direction, other is relative to world - prevents camera from 'twisting' although does introduce gimbal when looking along the UP axis and rotationg left/right. rot = glm::angleAxis( angleChange.x, m_WorldUp ) * rot * glm::angleAxis( angleChange.y, cVecViewRight ); rot = glm::normalize( rot ); @@ -108,7 +108,7 @@ void CameraControllerTouch::Update(float frameTime, glm::vec3& position, glm::qu if (m_MovementTouchId != -1) { auto mouseDiff = m_LastMovementTouchPosition - m_CurrentMovementTouchPosition; - auto directionChange = mouseDiff * frameTime * m_MoveSpeed; + auto directionChange = mouseDiff * frameTime * m_MoveSpeed * cTouchMoveSpeedMultipler; position -= rot * cVecViewRight * directionChange.x; position += rot * cVecViewForward * directionChange.y; diff --git a/framework/code/camera/cameraControllerTouch.hpp b/framework/code/camera/cameraControllerTouch.hpp index f07d943..89a4614 100644 --- a/framework/code/camera/cameraControllerTouch.hpp +++ b/framework/code/camera/cameraControllerTouch.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -36,7 +36,8 @@ class CameraControllerTouch : public CameraControllerBase /// @param frameTime time in seconds since last Update. /// @param position in - current camera position, out - camera position after applying controller /// @param rot in - current camera rotation, out - camera rotation after applying controller - void Update(float frameTime, glm::vec3& position, glm::quat& rot) override; + /// @param cut in - current camera cut, out - camera cut after applying controller + void Update(float frameTime, glm::vec3& position, glm::quat& rot, bool& cut) override; protected: glm::vec2 m_LastMovementTouchPosition; diff --git a/framework/code/camera/cameraData.hpp b/framework/code/camera/cameraData.hpp index 0912aaa..33c55ed 100644 --- a/framework/code/camera/cameraData.hpp +++ b/framework/code/camera/cameraData.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/camera/cameraGltfLoader.cpp b/framework/code/camera/cameraGltfLoader.cpp index c8f48df..2664c3d 100644 --- a/framework/code/camera/cameraGltfLoader.cpp +++ b/framework/code/camera/cameraGltfLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/camera/cameraGltfLoader.hpp b/framework/code/camera/cameraGltfLoader.hpp index 644f5ec..75e42af 100644 --- a/framework/code/camera/cameraGltfLoader.hpp +++ b/framework/code/camera/cameraGltfLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/dx12/commandList.cpp b/framework/code/dx12/commandList.cpp new file mode 100644 index 0000000..053d574 --- /dev/null +++ b/framework/code/dx12/commandList.cpp @@ -0,0 +1,107 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "commandList.hpp" +#include "dx12.hpp" + +//----------------------------------------------------------------------------- +CommandList::CommandList() noexcept +//----------------------------------------------------------------------------- + : CommandListBase() +{ +} + +//----------------------------------------------------------------------------- +CommandList::~CommandList() +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +CommandList::CommandList(CommandList&& other) noexcept +//--------------------------------------------------------------------------- +{ + assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandList will not resize) +} + +//----------------------------------------------------------------------------- +CommandList& CommandList::operator=(CommandList && other) noexcept +//----------------------------------------------------------------------------- +{ + assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandList will not resize) + return *this; +} + +//----------------------------------------------------------------------------- +bool CommandList::Initialize(Dx12* gfxApi, const std::string& Name, CommandListBase::Type CommandListType, uint32_t QueueIndex, ID3D12PipelineState* pPipelineState) +//----------------------------------------------------------------------------- +{ + m_pDx12 = gfxApi; + m_Name = Name; + m_Type = CommandListType; + // ensure framework enums match Dx12 enum values so they can be cast + static_assert(uint32_t( Type::Direct ) == uint32_t( D3D12_COMMAND_LIST_TYPE_DIRECT )); + static_assert(uint32_t( Type::Bundle ) == uint32_t( D3D12_COMMAND_LIST_TYPE_BUNDLE )); + static_assert(uint32_t( Type::Compute ) == uint32_t( D3D12_COMMAND_LIST_TYPE_COMPUTE )); + static_assert(uint32_t( Type::Copy ) == uint32_t( D3D12_COMMAND_LIST_TYPE_COPY )); + static_assert(uint32_t( Type::VideoDecode ) == uint32_t( D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE )); + static_assert(uint32_t( Type::VideoProcess ) == uint32_t( D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS )); + static_assert(uint32_t( Type::VideoEncode ) == uint32_t( D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE )); + + // Reset draw counts + m_NumDrawCalls = 0; + m_NumTriangles = 0; + + if (!gfxApi->CreateCommandList( (D3D12_COMMAND_LIST_TYPE)m_Type, pPipelineState, m_CommandList)) + return false; + Dx12::SetName(m_CommandList.Get(), Name); + return true; +} + +//----------------------------------------------------------------------------- +bool CommandList::Reset() +//----------------------------------------------------------------------------- +{ + m_NumDrawCalls = 0; + m_NumTriangles = 0; + + //if (!Dx12::CheckError( "CmdList Reset", m_CommandList.Get()->Reset())) + // return false; + + return true; +} + +//----------------------------------------------------------------------------- +bool CommandList::Begin(ID3D12PipelineState* pPipelineState) +//----------------------------------------------------------------------------- +{ + m_CommandAllocator = m_pDx12->OpenCommandAllocator( (D3D12_COMMAND_LIST_TYPE)m_Type ); + if (!m_CommandAllocator) + return false; + return Dx12::CheckError( "CommandListReset", m_CommandList->Reset(m_CommandAllocator.Get(), pPipelineState) ); +} + +//----------------------------------------------------------------------------- +bool CommandList::End() +//----------------------------------------------------------------------------- +{ + assert(m_CommandAllocator); + bool success = Dx12::CheckError( "CommandListClose", m_CommandList->Close() ); + m_pDx12->CloseCommandAllocator( (D3D12_COMMAND_LIST_TYPE)m_Type, std::move(m_CommandAllocator) ); + return success; +} + +//----------------------------------------------------------------------------- +void CommandList::Release() +//----------------------------------------------------------------------------- +{ + CommandListBase::Release(); + + assert(!m_CommandAllocator); // expected to be closed + m_CommandList.Reset(); +} diff --git a/framework/code/dx12/commandList.hpp b/framework/code/dx12/commandList.hpp new file mode 100644 index 0000000..d101699 --- /dev/null +++ b/framework/code/dx12/commandList.hpp @@ -0,0 +1,52 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include +#include +#include "graphicsApi/commandList.hpp" + +// Forward declares +using namespace Microsoft::WRL; // for ComPtr +class Dx12; + + +template<> +class CommandList final : public CommandListBase +{ + CommandList(const CommandList&) = delete; + CommandList& operator=(const CommandList&) = delete; +public: + CommandList() noexcept; + ~CommandList(); + CommandList(CommandList&&) noexcept; + CommandList& operator=(CommandList&&) noexcept; + + bool Initialize(Dx12*, const std::string& Name = {}, Type CommandListType = Type::Direct, uint32_t QueueIndex = 0, ID3D12PipelineState* pPipelineState = nullptr); + + // Begin command buffer + bool Begin(ID3D12PipelineState* pPipelineState = nullptr); + + bool End(); + + bool Reset(); + + /// @brief Release the Dx12 resources used by this wrapper and cleanup. + void Release() override; + + ID3D12GraphicsCommandList* Get() const { return m_CommandList.Get(); } + ID3D12GraphicsCommandList* operator->() const { return m_CommandList.Get(); } + ID3D12GraphicsCommandList* operator*() const { return m_CommandList.Get(); } + +private: + ComPtr m_CommandList; + ComPtr m_CommandAllocator; // We take ownership of the allocator for the duration of recording (and return to Dx12 class in End) + Dx12* m_pDx12 = nullptr; +}; + diff --git a/framework/code/dx12/descriptorHeapManager.cpp b/framework/code/dx12/descriptorHeapManager.cpp new file mode 100644 index 0000000..bdf067f --- /dev/null +++ b/framework/code/dx12/descriptorHeapManager.cpp @@ -0,0 +1,146 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "descriptorHeapManager.hpp" +#include "dx12.hpp" +#include + +// Forward declarations +using namespace Microsoft::WRL; // for ComPtr + + +//----------------------------------------------------------------------------- +bool DescriptorHeapManager::Initialize(ComPtr descriptorHeap, uint32_t maxHandles, uint32_t handleSize) +//----------------------------------------------------------------------------- +{ + m_DescriptorHeap = std::move(descriptorHeap); + m_HandleSize = handleSize; + m_FreeDescriptors.clear(); + m_FreeDescriptors.push_back({ 0,maxHandles }); + m_TemporaryAllocations.fill({}); + return true; +} + +//----------------------------------------------------------------------------- +DescriptorTableHandle DescriptorHeapManager::Allocate(uint32_t numHandles) +//----------------------------------------------------------------------------- +{ + auto foundIt = std::find_if(m_FreeDescriptors.begin(), m_FreeDescriptors.end(), [numHandles](const auto& freeRange) -> bool { + uint32_t freeSize = freeRange.end - freeRange.start; + return freeSize >= numHandles; + }); + if (foundIt == m_FreeDescriptors.end()) + return {}; // out of space! + + const uint32_t handleStartIdx = foundIt->start; + foundIt->start += numHandles; + if (foundIt->start == foundIt->end) + // Block used completely + m_FreeDescriptors.erase(foundIt); + + return DescriptorTableHandle{ m_DescriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + UINT64(handleStartIdx) * m_HandleSize, + m_DescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + UINT64(handleStartIdx) * m_HandleSize, + numHandles, + m_HandleSize}; +} + +//----------------------------------------------------------------------------- +DescriptorTableHandle DescriptorHeapManager::AllocateTemporary(uint32_t frameIndex, uint32_t numHandles) +//----------------------------------------------------------------------------- +{ + // Search/allocate in reverse (opposite direction from Allocate, so temporaries are at the top of the descriptor heap) + auto foundIt = std::find_if(m_FreeDescriptors.rbegin(), m_FreeDescriptors.rend(), [numHandles](const auto& freeRange) -> bool { + uint32_t freeSize = freeRange.end - freeRange.start; + return freeSize >= numHandles; + }); + if (foundIt == m_FreeDescriptors.rend()) + return {}; // out of space! + + const uint32_t handleStartIdx = foundIt->end - numHandles; + foundIt->end -= numHandles; + if (foundIt->start == foundIt->end) + // Block used completely + m_FreeDescriptors.erase((++foundIt).base());//convert reverse iterator to forward and erase from free + + DescriptorTableHandle handle{ m_DescriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + UINT64(handleStartIdx) * m_HandleSize, + m_DescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + SIZE_T(handleStartIdx) * m_HandleSize, + numHandles, + m_HandleSize}; + m_TemporaryAllocations[frameIndex].push_back({ handleStartIdx, handleStartIdx + numHandles }); + + return handle; +} + +//----------------------------------------------------------------------------- +void DescriptorHeapManager::Free(DescriptorTableHandle&& handle) +//----------------------------------------------------------------------------- +{ + assert(handle.numHandles > 0); + const uint32_t handleStartIdx = (handle.GetCpuHandle(0).ptr - m_DescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr) / m_HandleSize; + const uint32_t handleEndIdx = handleStartIdx + handle.numHandles; +} + +//----------------------------------------------------------------------------- +void DescriptorHeapManager::Free(Range range) +//----------------------------------------------------------------------------- +{ + const uint32_t startIdx = range.start; + const uint32_t endIdx = range.end; + assert(startIdx < endIdx); + + auto nextFreeIt = std::find_if(m_FreeDescriptors.begin(), m_FreeDescriptors.end(), [startIdx](const auto& freeRange) -> bool { + return (startIdx < freeRange.start); + }); + if (nextFreeIt == m_FreeDescriptors.end()) + { + assert(m_FreeDescriptors.empty() || m_FreeDescriptors.back().end <= startIdx); + if (!m_FreeDescriptors.empty() && m_FreeDescriptors.back().end == startIdx) + // add to the end of the final free block + m_FreeDescriptors.back().end = endIdx; + else + // add a new 'final' free block + m_FreeDescriptors.push_back({ startIdx, endIdx }); + } + else + { + assert(nextFreeIt->start <= endIdx); + if (endIdx == nextFreeIt->start) + // Extend the next free block to include this newly freed block + nextFreeIt->start = startIdx; + else + { + if (nextFreeIt != m_FreeDescriptors.begin() && (nextFreeIt - 1)->end == startIdx) + { + if (nextFreeIt->start == endIdx) + { + // Combine previous free block with the next free block (we just plugged the gap) + (nextFreeIt - 1)->end = nextFreeIt->end; + m_FreeDescriptors.erase(nextFreeIt); + } + else + // Just extend the previous free block + (nextFreeIt - 1)->end = endIdx; + } + else + // Make a new free block in the middle of the allocations (add a hole) + m_FreeDescriptors.insert(nextFreeIt, { startIdx, endIdx }); + } + } +} + +//----------------------------------------------------------------------------- +void DescriptorHeapManager::FreeTemporaries(uint32_t frameIndex) +//----------------------------------------------------------------------------- +{ + for (const auto& alloc : m_TemporaryAllocations[frameIndex]) + { + Free(alloc); + } + m_TemporaryAllocations[frameIndex].clear(); +} diff --git a/framework/code/dx12/descriptorHeapManager.hpp b/framework/code/dx12/descriptorHeapManager.hpp new file mode 100644 index 0000000..5454455 --- /dev/null +++ b/framework/code/dx12/descriptorHeapManager.hpp @@ -0,0 +1,84 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include +#include +#include + +#define NUM_SWAPCHAIN_BUFFERS (8) + +// Forward declarations +struct ID3D12DescriptorHeap; +class Dx12; + + +struct DescriptorTableHandle +{ + DescriptorTableHandle(const DescriptorTableHandle&) = default; + DescriptorTableHandle& operator=(const DescriptorTableHandle&) = default; + DescriptorTableHandle() noexcept {}; + DescriptorTableHandle(UINT64 _gpu, UINT64 _cpu, uint32_t _numHandles, uint32_t _handleSize) noexcept + : gpu{.ptr=_gpu}, cpu{.ptr=_cpu}, numHandles(_numHandles), handleSize(_handleSize) {} + DescriptorTableHandle(DescriptorTableHandle && other) noexcept { + *this = std::move(other); + } + DescriptorTableHandle& operator=(DescriptorTableHandle && other) noexcept { + if (this != &other) + { + gpu = other.gpu; + other.gpu = {}; + cpu = other.cpu; + other.cpu = {}; + numHandles = other.numHandles; + other.numHandles = 0; + handleSize = other.handleSize; + other.handleSize = 0; + } + return *this; + } + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(size_t index) const { return {cpu.ptr + size_t(index)*handleSize}; } + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle() const { return gpu; } + uint32_t numHandles = 0; + uint32_t handleSize = 0; +private: + D3D12_GPU_DESCRIPTOR_HANDLE gpu{}; + D3D12_CPU_DESCRIPTOR_HANDLE cpu{}; +}; + + +struct DescriptorTableHandleAndRootIndex +{ + uint32_t rootIndex; + DescriptorTableHandle descriptorTable; +}; + + + +class DescriptorHeapManager +{ + DescriptorHeapManager(const DescriptorHeapManager&) = delete; + DescriptorHeapManager& operator=(const DescriptorHeapManager&) = delete; +public: + DescriptorHeapManager() noexcept {} + bool Initialize(Microsoft::WRL::ComPtr descriptorHeap, uint32_t maxHandles, uint32_t handleSize); + DescriptorTableHandle Allocate(uint32_t numHandles); + DescriptorTableHandle AllocateTemporary(uint32_t frameIndex, uint32_t numHandles); + void Free(DescriptorTableHandle&&); + void FreeTemporaries(uint32_t frameIndex); + ID3D12DescriptorHeap* const GetHeap() const { return m_DescriptorHeap.Get(); } +private: + Microsoft::WRL::ComPtr m_DescriptorHeap; + uint32_t m_HandleSize = 0; + struct Range { uint32_t start = 0, end = 0; }; + std::vector m_FreeDescriptors; + std::array, NUM_SWAPCHAIN_BUFFERS> m_TemporaryAllocations; +protected: + void Free(Range); +}; diff --git a/framework/code/dx12/dx12.cpp b/framework/code/dx12/dx12.cpp new file mode 100644 index 0000000..4c167f0 --- /dev/null +++ b/framework/code/dx12/dx12.cpp @@ -0,0 +1,712 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "dx12.hpp" +#include "system/os_common.h" +#include +#include +#include "texture/texture.hpp" + +static const uint32_t cnMaxSamplers = 64; +static const uint32_t cnMaxShaderResourceViewHandles = 16384; + + +//----------------------------------------------------------------------------- +Dx12::Dx12() : GraphicsApiBase() + , m_SwapchainFormat(TextureFormat::UNDEFINED) +//----------------------------------------------------------------------------- +{} + +//----------------------------------------------------------------------------- +Dx12::~Dx12() +//----------------------------------------------------------------------------- +{ +// DestroyFrameBuffers(); +// DestroySwapchainRenderPass(); +// DestroySwapChain(); + + m_MemoryManager.Destroy(); +} + +//----------------------------------------------------------------------------- +bool Dx12::Init(uintptr_t hWnd, uintptr_t hInst, const Dx12::tSelectSurfaceFormatFn& SelectSurfaceFormatFn, const Dx12::tConfigurationFn& CustomConfigurationFn ) +//----------------------------------------------------------------------------- +{ + m_hWnd = (HWND)hWnd; + m_hInstance = (HINSTANCE)hInst; + + if (!InitDevice()) + { + return false; + } + if (!InitMemoryManager()) + { + return false; + } + if (!CreateQueues()) + { + return false; + } + if (!CreateCommandAllocators()) + { + return false; + } + if (!CreateSwapchain()) + { + return false; + } + if (!CreateSamplerHeap()) + { + return false; + } + if (!CreateShaderResourceViewHeap()) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::CheckError(const char*const pPrefix, HRESULT result) +//----------------------------------------------------------------------------- +{ + if (result == S_OK) + return true; + LOGE("DX12 error: (%ld) from %s", (long)result, pPrefix); + return false; +} + +//----------------------------------------------------------------------------- +bool Dx12::InitDevice() +//----------------------------------------------------------------------------- +{ +#if defined(_DEBUG) + // Enable debug layer before everything else. + { + ComPtr debugI; + if (!CheckError("D3D12GetDebugInterface", D3D12GetDebugInterface(IID_PPV_ARGS(&debugI)))) + return false; + debugI->EnableDebugLayer(); + } +#endif + + ComPtr dxgiFactory; + if (!CheckError( "CreateDXGIFactory2", CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(&dxgiFactory)) )) + return false; + + // Ignore warp drivers (could add as an option here if we waned) + ComPtr dxgiAdapter; + UINT adaptorIdx=0; + bool foundValidAdaptor = false; + // Grab the first non-software adaptor. + ///TODO: pass in desired adaptor index + while (dxgiFactory->EnumAdapters1(adaptorIdx, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND) + { + DXGI_ADAPTER_DESC1 adaptorDescription; + dxgiAdapter->GetDesc1(&adaptorDescription); + if ((adaptorDescription.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0) + { + foundValidAdaptor = true; + break; + } + ++adaptorIdx; + } + if (!foundValidAdaptor) + return false; + ComPtr device; + if (!CheckError("D3D12CreateDevice", D3D12CreateDevice(dxgiAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)))) + return false; + + m_DxgiFactory = dxgiFactory; + m_Device = device; + m_Adaptor = dxgiAdapter; + + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::InitMemoryManager() +//----------------------------------------------------------------------------- +{ + return m_MemoryManager.Initialize(m_Adaptor.Get(), m_Device.Get()); +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateQueues() +//----------------------------------------------------------------------------- +{ + D3D12_COMMAND_QUEUE_DESC queueDesc{}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 0; + ComPtr graphicsQueue; + if (!CheckError("CreateCommandQueue", m_Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&graphicsQueue)))) + return false; + + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE; + queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 0; + ComPtr computeQueue; + if (!CheckError("CreateCommandQueue", m_Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&computeQueue)))) + return false; + + m_DirectCommandQueue = graphicsQueue; + m_ComputeCommandQueue = computeQueue; + + CheckError("CreateQueues Fence", m_Device->CreateFence(m_DirectCommandQueueFenceValue++, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_DirectCommandQueueFence))); + + CheckError( "CreateQueues Fence", m_Device->CreateFence( m_ComputeCommandQueueFenceValue++, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS( &m_ComputeCommandQueueFence ) ) ); + + + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateCommandAllocators() + //----------------------------------------------------------------------------- +{ + typedef std::pair tAC; + decltype(m_commandAllocators) commandAllocators{}; + + for (auto& [allocatorCount, allocatorType] : { tAC{1 + NUM_SWAPCHAIN_BUFFERS, D3D12_COMMAND_LIST_TYPE_DIRECT}, tAC{64, D3D12_COMMAND_LIST_TYPE_BUNDLE}, tAC{NUM_SWAPCHAIN_BUFFERS, D3D12_COMMAND_LIST_TYPE_COMPUTE}, tAC{NUM_SWAPCHAIN_BUFFERS, D3D12_COMMAND_LIST_TYPE_COPY} }) + { + for(auto i=0;iCreateCommandAllocator(allocatorType, IID_PPV_ARGS(&commandAllocators[allocatorType].emplace_back())))) + return false; + } + m_commandAllocators = std::move(commandAllocators); + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateCommandList(D3D12_COMMAND_LIST_TYPE commandListType, ID3D12PipelineState*const pPipelineState, ComPtr& pCmdList/*out*/) +//----------------------------------------------------------------------------- +{ + assert(pCmdList.Get() == nullptr); // check not already allocated + bool error = false; + + // Get a command allocator for duretion of the create (no longer needed after we close it). THREAD UNSAFE (for the duration of this function) + if (m_commandAllocators[commandListType].empty()) + return false; + auto commandAllocator = m_commandAllocators[commandListType].back(); + + // Allocate the command buffer from the allocator + auto RetVal = m_Device->CreateCommandList(0,//nodeMask + commandListType, + commandAllocator.Get(), + pPipelineState, + IID_PPV_ARGS(&pCmdList)); + if (!CheckError("CreateCommandList", RetVal)) + return false; + return CheckError("CloseCommandList", pCmdList->Close()); +} + +//----------------------------------------------------------------------------- +ComPtr Dx12::OpenCommandAllocator(D3D12_COMMAND_LIST_TYPE listType) +//----------------------------------------------------------------------------- +{ + auto a = m_commandAllocators[listType].back(); + m_commandAllocators[listType].pop_back(); + return a; +} + +//----------------------------------------------------------------------------- +void Dx12::CloseCommandAllocator(D3D12_COMMAND_LIST_TYPE listType, ComPtr && commandAllocator) +//----------------------------------------------------------------------------- +{ + m_commandAllocators[listType].push_back( std::move(commandAllocator) ); +} + +//----------------------------------------------------------------------------- +uint64_t Dx12::CommandListExecute( ID3D12CommandList* const pCommandList ) +//----------------------------------------------------------------------------- +{ + std::span commandLists{&pCommandList, 1}; + + m_DirectCommandQueue->ExecuteCommandLists( commandLists.size(), commandLists.data() ); + const uint64_t valueToSignal = m_DirectCommandQueueFenceValue++; + m_DirectCommandQueue->Signal( m_DirectCommandQueueFence.Get(), valueToSignal ); + return valueToSignal; +} + +//----------------------------------------------------------------------------- +uint64_t Dx12::ComputeCommandListExecute(ID3D12CommandList* const pCommandList) +//----------------------------------------------------------------------------- +{ + std::span commandLists{ &pCommandList, 1 }; + + m_ComputeCommandQueue->ExecuteCommandLists(commandLists.size(), commandLists.data()); + const uint64_t valueToSignal = m_ComputeCommandQueueFenceValue++; + m_ComputeCommandQueue->Signal(m_ComputeCommandQueueFence.Get(), valueToSignal); + return valueToSignal; +} + +//----------------------------------------------------------------------------- +ID3D12GraphicsCommandList* const Dx12::StartSetupCommandBuffer() +//----------------------------------------------------------------------------- +{ + assert(!m_SetupCommandList); + + ComPtr commandList; + if (!CreateCommandList(D3D12_COMMAND_LIST_TYPE_DIRECT, nullptr, commandList)) + return nullptr; + auto commandAllocator = OpenCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT ); + if (!commandAllocator) + return nullptr; + if (!Dx12::CheckError( "CommandListReset", commandList->Reset( commandAllocator.Get(), nullptr))) + return nullptr; + m_SetupCommandAllocator = commandAllocator; + m_SetupCommandList = commandList; + return m_SetupCommandList.Get(); +} + +//----------------------------------------------------------------------------- +void Dx12::FinishSetupCommandBuffer(ID3D12GraphicsCommandList* const cmdList) +//----------------------------------------------------------------------------- +{ + assert(cmdList); + if (cmdList != m_SetupCommandList.Get()) + { + LOGE("Setup CommandList does not match he one passed in to FinishSetupCommandBuffer!"); + } + CheckError("CmdList Close", cmdList->Close()); + + CommandListExecute(cmdList); + QueueWaitIdle(0); + + CloseCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, std::move(m_SetupCommandAllocator) ); + m_SetupCommandList = nullptr; +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateSwapchain() +//----------------------------------------------------------------------------- +{ + m_SwapchainFormat = TextureFormat::R8G8B8A8_UNORM; + + DXGI_SWAP_CHAIN_DESC1 swapchainDesc{}; + swapchainDesc.Width = 0; /* get from window */ + swapchainDesc.Height = 0; /* get from window */ + swapchainDesc.Format = TextureFormatToDx( m_SwapchainFormat ); + swapchainDesc.SampleDesc = { 1,0 }; + swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchainDesc.BufferCount = NUM_SWAPCHAIN_BUFFERS; + swapchainDesc.Scaling = DXGI_SCALING_NONE; + swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapchainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapchainDesc.Flags = 0;///TODO: optional tearing + + ComPtr swapchain; + if (!CheckError("CreateSwapChainForHwnd", m_DxgiFactory->CreateSwapChainForHwnd(m_DirectCommandQueue.Get(), m_hWnd, &swapchainDesc, nullptr, nullptr, &swapchain))) + return false; + + // Make a descriptor heap for the swapchain render targets + uint32_t rtvHandleSize = 0; + ComPtr descriptorHeap = CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, swapchainDesc.BufferCount, &rtvHandleSize); + + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + for (UINT i = 0; i < swapchainDesc.BufferCount; ++i) + { + if (!CheckError("Swapchain GetBuffer", swapchain->GetBuffer(i, IID_PPV_ARGS(&m_FrameBuffers[i])))) + return false; + m_Device->CreateRenderTargetView(m_FrameBuffers[i].Get(), nullptr, descHandle); + descHandle.ptr += rtvHandleSize; + } + + m_SwapchainDescriptorHeap = descriptorHeap; + m_SwapchainDescriptorSize = rtvHandleSize; + m_SwapchainBufferCount = swapchainDesc.BufferCount; + + swapchain.As(&m_Swapchain); // to IDXGISwapChain3 + m_SwapchainPresentIndx = m_Swapchain->GetCurrentBackBufferIndex(); + m_SwapchainCurrentIndx = 0; + + // Take the surface dimensions from the swapchain + m_Swapchain->GetDesc1(&swapchainDesc); + m_SurfaceWidth = swapchainDesc.Width; + m_SurfaceHeight = swapchainDesc.Height; + + //DELETE THIS BLOCK + // dumb sync object for waiting for framesync (slow). + { + HRESULT hr = m_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_Fence)); + m_FenceValue = 0; + + // Create an event handle to use for frame synchronization. + m_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (m_FenceEvent == nullptr) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + + return true; +} + +//----------------------------------------------------------------------------- +ComPtr Dx12::CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t numDescriptors, uint32_t* pOutDescriptorHandleSize) +//----------------------------------------------------------------------------- +{ + ComPtr heap; + D3D12_DESCRIPTOR_HEAP_DESC heapDesc{}; + heapDesc.Type = heapType; + heapDesc.NumDescriptors = numDescriptors; + heapDesc.Flags = (heapType != D3D12_DESCRIPTOR_HEAP_TYPE_RTV && heapType != D3D12_DESCRIPTOR_HEAP_TYPE_DSV) ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + heapDesc.NodeMask = 0; + if (!CheckError("CreateDescriptorHeap", m_Device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap)))) + return {}; + if (pOutDescriptorHandleSize) + { + *pOutDescriptorHandleSize = m_Device->GetDescriptorHandleIncrementSize(heapType); + } + + return heap; +} + +//----------------------------------------------------------------------------- +bool Dx12::FrameInit(uint32_t WhichFrame) +//----------------------------------------------------------------------------- +{ + m_ShaderResourceViewDescriptorHeap.FreeTemporaries(WhichFrame); + m_SamplerDescriptorHeap.FreeTemporaries(WhichFrame); + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::QueueWaitIdle(uint32_t QueueIndex) const +//----------------------------------------------------------------------------- +{ + assert(QueueIndex == 0); + + // Wait until the previous frame is finished. + if (m_DirectCommandQueueFence->GetCompletedValue() <= m_DirectCommandQueueFenceValue) + { + m_DirectCommandQueueFence->SetEventOnCompletion(m_DirectCommandQueueFenceValue-1, m_FenceEvent); + WaitForSingleObject(m_FenceEvent, INFINITE); + } + + assert(QueueIndex == 0); + return true; +} + +//----------------------------------------------------------------------------- +bool Dx12::WaitUntilIdle() const +//----------------------------------------------------------------------------- +{ + // blah! + return true; +} + +//----------------------------------------------------------------------------- +void Dx12::BackbufferRenderSetup(uint32_t whichFrame, ID3D12GraphicsCommandList* pCommandList) +//----------------------------------------------------------------------------- +{ + const D3D12_VIEWPORT viewport{ + .TopLeftX = 0.0f, + .TopLeftY = 0.0f, + .Width = (float)GetSurfaceWidth(), + .Height = (float)GetSurfaceHeight(), + .MinDepth = 0.0f, + .MaxDepth = 1.0f + }; + const D3D12_RECT scissor{ + .left = 0, + .top = 0, + .right = (LONG)GetSurfaceWidth(), + .bottom = (LONG)GetSurfaceHeight() + }; + + pCommandList->RSSetViewports(1, &viewport); + pCommandList->RSSetScissorRects(1, &scissor); + + D3D12_RESOURCE_BARRIER backbufferBarrier{ + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE , + .Transition = { + .pResource = m_FrameBuffers[whichFrame].Get(), + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + .StateBefore = D3D12_RESOURCE_STATE_PRESENT, + .StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET + } + }; + pCommandList->ResourceBarrier(1, &backbufferBarrier); + + D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle{ .ptr = m_SwapchainDescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + whichFrame * m_SwapchainDescriptorSize }; + pCommandList->OMSetRenderTargets(1, &descriptorHandle, FALSE, nullptr); + + const float clearColor[4] {}; + pCommandList->ClearRenderTargetView(descriptorHandle, clearColor, 0, nullptr); +} + +//----------------------------------------------------------------------------- +void Dx12::BackbufferPresentSetup(uint32_t whichFrame, ID3D12GraphicsCommandList* pCommandList) +//----------------------------------------------------------------------------- +{ + // Backbuffer transition to presentable + D3D12_RESOURCE_BARRIER backbufferBarrier{ + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE , + .Transition = { + .pResource = m_FrameBuffers[whichFrame].Get(), + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + .StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET, + .StateAfter = D3D12_RESOURCE_STATE_PRESENT + } + }; + pCommandList->ResourceBarrier(1, &backbufferBarrier); +} + +//----------------------------------------------------------------------------- +bool Dx12::PresentSwapchain() +//----------------------------------------------------------------------------- +{ + bool error = CheckError("PresentSwapchain", m_Swapchain->Present(1/*SyncInterval*/, 0/*Flags*/)); + + // Signal and increment the fence value. + const UINT64 fence = m_FenceValue; + m_DirectCommandQueue->Signal(m_Fence.Get(), fence); + m_FenceValue++; + + return error; +} + +//----------------------------------------------------------------------------- +Dx12::BufferIndexAndFence Dx12::SetNextBackBuffer() +//----------------------------------------------------------------------------- +{ + QueueWaitIdle(0); + + // Get next frame + const auto SwapchainPresentIndx = m_Swapchain->GetCurrentBackBufferIndex(); + + // Grab the swapchain index and then increment. + // This index is decoupled from the present index returned by GetCurrentBackBufferIndex (which may not run in sequence depending on present modes etc). + const uint32_t CurrentIndex = m_SwapchainCurrentIndx++; + if( m_SwapchainCurrentIndx == m_SwapchainBufferCount ) + m_SwapchainCurrentIndx = 0; + return {CurrentIndex, SwapchainPresentIndx/*, Fence, BackBufferSemaphore*/}; +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateSamplerHeap() +//----------------------------------------------------------------------------- +{ + uint32_t handleSize = 0; + auto heap = CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, cnMaxSamplers, &handleSize); + if (!heap) + return false; + return m_SamplerDescriptorHeap.Initialize(std::move(heap), cnMaxSamplers, handleSize); +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateShaderResourceViewHeap() +//----------------------------------------------------------------------------- +{ + uint32_t handleSize = 0; + auto heap = CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, cnMaxShaderResourceViewHandles, &handleSize); + if (!heap) + return false; + return m_ShaderResourceViewDescriptorHeap.Initialize(std::move(heap), cnMaxShaderResourceViewHandles, handleSize); +} + +//----------------------------------------------------------------------------- +DescriptorTableHandle Dx12::AllocateShaderResourceViewDescriptors(uint32_t numHandles, const std::optional frameNumTemporary) +//----------------------------------------------------------------------------- +{ + if (frameNumTemporary.has_value()) + return m_ShaderResourceViewDescriptorHeap.AllocateTemporary(*frameNumTemporary, numHandles); + else + return m_ShaderResourceViewDescriptorHeap.Allocate(numHandles); +} + +//----------------------------------------------------------------------------- +DescriptorTableHandle Dx12::AllocateSamplerDescriptors(uint32_t numHandles, const std::optional frameNumTemporary) +//----------------------------------------------------------------------------- +{ + if (frameNumTemporary.has_value()) + return m_SamplerDescriptorHeap.AllocateTemporary( *frameNumTemporary, numHandles); + else + return m_SamplerDescriptorHeap.Allocate(numHandles); +} + +//----------------------------------------------------------------------------- +void Dx12::FreeShaderResourceViewDescriptors(DescriptorTableHandle&& handle) +//----------------------------------------------------------------------------- +{ + return m_ShaderResourceViewDescriptorHeap.Free(std::move(handle)); +} + +//----------------------------------------------------------------------------- +void Dx12::FreeSamplerDescriptors(DescriptorTableHandle&& handle) +//----------------------------------------------------------------------------- +{ + return m_SamplerDescriptorHeap.Free(std::move(handle)); +} + +//----------------------------------------------------------------------------- +void Dx12::SetDescriptorHeaps(ID3D12GraphicsCommandList* pCommandList) const +//----------------------------------------------------------------------------- +{ + std::array descriptorHeaps{ m_ShaderResourceViewDescriptorHeap.GetHeap(), m_SamplerDescriptorHeap.GetHeap() }; + pCommandList->SetDescriptorHeaps(descriptorHeaps.size(), descriptorHeaps.data()); +} + +//----------------------------------------------------------------------------- +bool Dx12::CreateSampler(const D3D12_SAMPLER_DESC& desc, D3D12_CPU_DESCRIPTOR_HANDLE destHandle) +//----------------------------------------------------------------------------- +{ + m_Device->CreateSampler(&desc, destHandle); + return true; +} + +//----------------------------------------------------------------------------- +TextureFormat Dx12::GetBestSurfaceDepthFormat(bool NeedStencil) const +//----------------------------------------------------------------------------- +{ + TextureFormat desiredDepthFormat = NeedStencil ? TextureFormat::D32_SFLOAT_S8_UINT : TextureFormat::D32_SFLOAT; + + D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { .Format = TextureFormatToDx(desiredDepthFormat) }; + if (!CheckError("CheckFeatureSupport", m_Device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) + { + return TextureFormat::UNDEFINED; + } + if ((formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL) == 0) + { + assert(0);///TODO: try some other formats if the selected one isnt supported + return TextureFormat::UNDEFINED; + } + + return desiredDepthFormat; +} + +//----------------------------------------------------------------------------- +bool Dx12::IsTextureFormatSupported(TextureFormat format) const +//----------------------------------------------------------------------------- +{ + D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { .Format = TextureFormatToDx(format) }; + if (CheckError("CheckFeatureSupport", m_Device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) + { + return false; + } + ///TODO: what are we wanting to check here? support for what feature? + if ((formatSupport.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D) == 0) + { + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +/*static*/ uint32_t Dx12::FormatBytesPerPixel(DXGI_FORMAT format) +//----------------------------------------------------------------------------- +{ + switch (format) { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 16; + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 12; + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 8; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 4; + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + return 2; + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_R1_UNORM: + return 1; + default: + assert(0); + return 1; + } +} + +//----------------------------------------------------------------------------- +void Dx12::SetName(ID3D12DeviceChild* pObject, const std::string& Name) +//----------------------------------------------------------------------------- +{ + pObject->SetPrivateData(WKPDID_D3DDebugObjectName, Name.size(), Name.c_str()); +} + +//----------------------------------------------------------------------------- +void Dx12::SetName(ID3D12DeviceChild* pObject, const std::string_view& Name) +//----------------------------------------------------------------------------- +{ + pObject->SetPrivateData(WKPDID_D3DDebugObjectName, Name.size(), Name.data()); +} diff --git a/framework/code/dx12/dx12.hpp b/framework/code/dx12/dx12.hpp new file mode 100644 index 0000000..6fbcb5d --- /dev/null +++ b/framework/code/dx12/dx12.hpp @@ -0,0 +1,255 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#define NOMINMAX +#include "graphicsApi/graphicsApiBase.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "descriptorHeapManager.hpp" +#include "memory/dx12/memoryManager.hpp" + +using namespace Microsoft::WRL; // for ComPtr + +// Forward declarations +template class IndexBuffer; +template class VertexBuffer; +template class MemoryManager; +enum class TextureFormat; +DXGI_FORMAT TextureFormatToDx(TextureFormat f); + +#define NUM_SWAPCHAIN_BUFFERS (8) + +struct SurfaceFormat +{ + TextureFormat format; +}; + + +/// DirectX 12 API implementation +/// Contains DirectX12 top level (driver etc) objects and provides a simple initialization interface. +class Dx12 : public GraphicsApiBase +{ + Dx12(const Dx12&) = delete; + Dx12& operator=(const Dx12&) = delete; +public: + // typedefs + using ViewportClass = D3D12_VIEWPORT; + using Rect2DClass = D3D12_RECT; + using MemoryManager = MemoryManager; + using BufferHandleType = ID3D12Resource*; + + struct AppConfiguration + { + /// (optional) override of the framebuffer depth format. Setting to UNDEFINED will disable the creation of a depth buffer during InitSwapChain + std::optional SwapchainDepthFormat; + }; + +public: + Dx12(); + virtual ~Dx12(); + + typedef std::function )> tSelectSurfaceFormatFn; + typedef std::function tConfigurationFn; + + /// @brief Main entry point to Dx12. + /// Initialize the Dx12 device/driver and create objects needed to start recording and executing command lists + /// @param hWnd windows window handle + /// @param hInst windows application instance + /// @param iDesiredMSAA + /// @return + bool Init(uintptr_t hWnd, uintptr_t hInst, const tSelectSurfaceFormatFn& SelectSurfaceFormatFn = nullptr, const tConfigurationFn& CustomConfigurationFn = nullptr ); + + /// @brief Prepare for update of frame + bool FrameInit(uint32_t WhichFrame); + + /// Stall CPU thread until GPU device is idle + /// Not recommended for use in a regular rendering pipeline as it will introduce an undesirable stall. + /// Implements base class pure virtual. + bool WaitUntilIdle() const override; + + /// Stall CPU thread until GPU queue is idle + bool QueueWaitIdle(uint32_t QueueIndex) const; + + // Accessors + MemoryManager& GetMemoryManager() { return m_MemoryManager; } + const MemoryManager& GetMemoryManager() const { return m_MemoryManager; } + auto GetDevice() const { return m_Device.Get(); } + + uint32_t GetSurfaceWidth() const { return m_SurfaceWidth; } ///< Swapchain width + uint32_t GetSurfaceHeight() const { return m_SurfaceHeight; } ///< Swapchain height + TextureFormat GetSwapchainFormat() const { return m_SwapchainFormat; } ///< Swapchain format + uint32_t GetSwapchainBufferCount() const { return m_SwapchainBufferCount; } ///< Swapchain number of images + + // + // Helpers + // + + /// @brief Check a windows hresult error code, and (on error) output error message string including pPrefix + /// @return return true on 'ok', false on 'error' + static bool CheckError(const char* const pPrefix, HRESULT result); + + /// @brief Create a command list object ready for recording commands. + /// @param commandListType type of command list (type must be supported by CreateCommandAllocators) + /// @param pCmdList output command list object + /// @return true on success + bool CreateCommandList(D3D12_COMMAND_LIST_TYPE commandListType, ID3D12PipelineState* const pPipelineState, ComPtr& pCmdList/*out*/); + + /// @brief Obtain a command allocator for use by ID3D12CommandList::Reset + /// @param commandListType type of command list allocator (type must be supported by CreateCommandAllocators) + /// @returns a valid command list allocator (or nullptr object) that can be used to record commands and MUST then be returned to Dx12 via CloseCommandAllocator + ComPtr OpenCommandAllocator(D3D12_COMMAND_LIST_TYPE listType); + + /// @brief Returns a command allocator back to the pool of allocators than can be grabbed by OpenCommandAllocator + /// Expected that the 'closed' command allocator is not used after this function is called + /// @param commandListType type of command list allocator (must match the type passed to the matching OpenCommandAllocator) + void CloseCommandAllocator(D3D12_COMMAND_LIST_TYPE listType, ComPtr && commandAllocator); + + /// @brief Submit/execute a command list on the GPU + /// @return value that will be signalled when this command completes + uint64_t CommandListExecute(ID3D12CommandList* const); + + /// @brief Submit/execute a compute command list on the GPU + /// @return value that will be signalled when this command completes + uint64_t ComputeCommandListExecute(ID3D12CommandList* const); + + /// @brief Setup a command buffer ready to record 'setup' commands such as texture transfers etc + /// @return command buffer ready to be recorded into + ID3D12GraphicsCommandList* const StartSetupCommandBuffer(); + /// @brief Close and execute the given command buffer (expected to have been created by StartSetupCommandBuffer) + /// Will wait for command buffer execution to complete. + void FinishSetupCommandBuffer(ID3D12GraphicsCommandList* const); + + void CreateRenderTargetView(); + + /// @brief Add commands to transition and setup the backbuffer to be a render target (do prior to commands rendering to the backbuffer) + void BackbufferRenderSetup(uint32_t whichFrame, ID3D12GraphicsCommandList* pCommandList); + + /// @brief Add commands to transition and setup the backbuffer to be presentable (do prior to calling PresentSwapchain) + void BackbufferPresentSetup(uint32_t whichFrame, ID3D12GraphicsCommandList* pCommandList); + + /// @brief Present the swapchain to the display + bool PresentSwapchain(); + + /// Current buffer index (that can be filled) and the fence that should be signalled when the GPU completes this buffer and the semaphore to wait on before starting rendering. + struct BufferIndexAndFence + { + /// Current frame index (internal - always in order) + const uint32_t idx; + /// Current backbuffer index (from vkAcquireNextImageKHR - may be 'out of order') + const uint32_t swapchainPresentIdx; + /// Fence set when GPU completes rendering this buffer + //const VkFence fence; + /// Backbuffer semaphore (start of rendering pipeline needs to wait for this) + //const VkSemaphore semaphore; + }; + + /// Get the next image to render to and the fence to signal at the end, then queue a wait until the image is ready + /// @return index of next backbuffer and the fence to signal upon comdbuffer completion. May not be in sequential order! + BufferIndexAndFence SetNextBackBuffer(); + + DescriptorTableHandle AllocateShaderResourceViewDescriptors(uint32_t numHandles, const std::optional frameNumTemporary = std::nullopt); + DescriptorTableHandle AllocateSamplerDescriptors(uint32_t numHandles, const std::optional frameNumTemporary = std::nullopt ); + void FreeShaderResourceViewDescriptors(DescriptorTableHandle &&); + void FreeSamplerDescriptors(DescriptorTableHandle &&); + void SetDescriptorHeaps(ID3D12GraphicsCommandList* pCommandList) const; + const auto& GetShaderResourceViewDescriptorHeap() const { return m_ShaderResourceViewDescriptorHeap; } + + bool CreateSampler(const D3D12_SAMPLER_DESC&, D3D12_CPU_DESCRIPTOR_HANDLE destHandle); + + /// @brief return the supported depth format with the highest precision depth/stencil supported with optimal tiling + /// @param NeedStencil set if we have to have a format with stencil bits (defaulted to false). + TextureFormat GetBestSurfaceDepthFormat(bool NeedStencil = false) const; + + /// Check if the Dx12 device supports the given texture format. + /// @return true if format is supported + bool IsTextureFormatSupported(TextureFormat) const; + + /// Get the number of bytes per pixel of the given DXGI_FORMAT + static uint32_t FormatBytesPerPixel(DXGI_FORMAT format); + + /// @brief Helper to set debug name (ascii) on any D3D12 object + static void SetName(ID3D12DeviceChild* pObject, const std::string& Name); + /// @brief Helper to set debug name (ascii) on any D3D12 object + static void SetName(ID3D12DeviceChild* pObject, const std::string_view& Name); + + // Helper + ComPtr CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t numDescriptors, uint32_t* pOutDescriptorHandleSize ); + +protected: + // Internal initialization functions (called by Init). + bool InitMemoryManager(); + bool InitDevice(); + bool CreateQueues(); + bool CreateCommandAllocators(); + bool CreateSwapchain(); + bool CreateSamplerHeap(); + bool CreateShaderResourceViewHeap(); + +private: + // Windows window/app handles + HINSTANCE m_hInstance = 0; + HWND m_hWnd = 0; + + // D3d objects + ComPtr m_DxgiFactory; + ComPtr m_Device; + ComPtr m_Adaptor; + + ComPtr m_Swapchain; + ComPtrm_SwapchainDescriptorHeap; + uint32_t m_SwapchainDescriptorSize = 0; + TextureFormat m_SwapchainFormat; + uint32_t m_SwapchainBufferCount = 0; + + ComPtr m_DirectCommandQueue; + ComPtr m_DirectCommandQueueFence; // Fence that is signalled on every execute + uint64_t m_DirectCommandQueueFenceValue = 0; // Value that will be signalled by m_DirectCommandQueueFence on the next execute + + ComPtr m_ComputeCommandQueue; + ComPtr m_ComputeCommandQueueFence; // Fence that is signalled on every execute + uint64_t m_ComputeCommandQueueFenceValue = 0;// Value that will be signalled by m_ComputeCommandQueueFence on the next execute + + ComPtr m_SetupCommandList; // Command list created and returned by StartSetupCommandBuffer + ComPtr m_SetupCommandAllocator; // Command allocator used by command list returned by StartSetupCommandBuffer + + static const size_t cNumCommandListTypes = 4; + std::array< std::deque< ComPtr >, cNumCommandListTypes > m_commandAllocators{}; + + // Memory manager + MemoryManager m_MemoryManager; + + std::array, NUM_SWAPCHAIN_BUFFERS> m_FrameBuffers; + + /// Current frame index (internal - always in order) + uint32_t m_SwapchainCurrentIndx = 0; + /// Current swapchain/backbuffer index (from directx) + uint32_t m_SwapchainPresentIndx = 0; + + // TEMPORARY + HANDLE m_FenceEvent = 0; + ComPtr m_Fence; + UINT64 m_FenceValue = 0; + // END TEMPORARY + + uint32_t m_SurfaceWidth = 0; ///< Swapchain width + uint32_t m_SurfaceHeight = 0; ///< Swapchain height + + DescriptorHeapManager m_ShaderResourceViewDescriptorHeap; + DescriptorHeapManager m_SamplerDescriptorHeap; +}; diff --git a/framework/code/dx12/meshObject.cpp b/framework/code/dx12/meshObject.cpp new file mode 100644 index 0000000..092eb12 --- /dev/null +++ b/framework/code/dx12/meshObject.cpp @@ -0,0 +1,89 @@ +//============================================================================================================ +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include +#include "system/glm_common.hpp" +//#include "memory/indexBufferObject.hpp" +//#include "memory/memoryManager.hpp" + +// Forward declarations +class VertexFormat; +class AssetManager; +class VertexBufferObject; +class MeshObjectIntermediate; + +/// Defines a simple object for creating and holding DX12 state corresponding to a single mesh. +template +class MeshObject +{ + MeshObject(const MeshObject&) = delete; + MeshObject& operator=(const MeshObject&) = delete; +public: + MeshObject() = default; + MeshObject(MeshObject&&) noexcept = default; + MeshObject& operator=(MeshObject&&) noexcept = default; + ~MeshObject(); + + /// Builds a screen space mesh as a single Vertex Buffer containing relevant vertex positions, normals and colors. Vertices are held in TRIANGLE_LIST format. + /// Vertex layout corresponds to MeshObject::vertex_layout structure. + /// @returns true on success + static bool CreateScreenSpaceMesh(MemoryManager& memoryManager, glm::vec4 PosLLRadius, glm::vec4 UVLLRadius, uint32_t binding, MeshObject* meshObject); + /// Helper for CreateScreenSpaceMesh that has a x,y position from -1,-1 to 1,1 and UV from 0,0 to 1,1. + /// @returns true on success + static bool CreateScreenSpaceMesh(MemoryManager& memoryManager, uint32_t binding, MeshObject* meshObject) + { + return CreateScreenSpaceMesh(memoryManager, glm::vec4(-1.0f, -1.0f, 2.0f, 2.0f), glm::vec4(0.0f, 0.0f, 1.0f, 1.0f), binding, meshObject); + } + + /// Create a MeshObject from a 'fat' MeshObjectIntermediate object, rearranging the vertex data to match the supplied vertex format(s). + /// Can have multiple VertexFormats, which will create multiple vertex buffers (eg if we want to split vertex position data away from other vertex attributes) + /// @param pVertexFormat format of the vertex data being output + /// @returns true on success + static bool CreateMesh(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, uint32_t binding, const std::span pVertexFormat, MeshObject* meshObjectOut); + + virtual bool Destroy(); + + /// Helper to create a IndexBufferObject, IF the mesh object has index buffer data. + /// @returns true for success (including no index buffer data existing in IndexBufferObject), false on error. + static bool CreateIndexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, std::optional& indexBufferOut, VkBufferUsageFlags usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + + // These MUST match the order of the attrib locations and sFormat must reflect the layout of this struct too! + struct vertex_layout + { + float pos[3]; // SHADER_ATTRIB_LOC_POSITION + float normal[3]; // SHADER_ATTRIB_LOC_NORMAL + float uv[2]; // SHADER_ATTRIB_LOC_TEXCOORD0 + float color[4]; // SHADER_ATTRIB_LOC_COLOR + float tangent[3]; // SHADER_ATTRIB_LOC_TANGENT + // float binormal[3]; // SHADER_ATTRIB_LOC_BITANGENT + static VertexFormat sFormat; + }; + +public: + uint32_t m_NumVertices; + std::vector m_VertexBuffers; + std::optionalm_IndexBuffer; +}; + + +namespace MeshObjectHelper +{ + /// Create a MeshObject for the first shape in a .gltf file (no materials). + /// Returned MeshObject does not have an index buffer (3 verts per triangle) and data is in the MeshObject::vertex_layout format. + /// @returns true on success + static bool LoadGLTF(MemoryManager& memoryManager, AssetManager&, const std::string& filename, uint32_t binding, MeshObject* meshObject); + +} // namespace MeshObjectHelper; + + +template +MeshObject::~MeshObject() {} diff --git a/framework/code/dx12/renderPass.cpp b/framework/code/dx12/renderPass.cpp new file mode 100644 index 0000000..380df9f --- /dev/null +++ b/framework/code/dx12/renderPass.cpp @@ -0,0 +1,57 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include +#include +#include "renderPass.hpp" +#include "dx12/dx12.hpp" + +// Forward declarations +class Dx12; + + +RenderPass::RenderPass() noexcept +{ +} + +RenderPass::~RenderPass() +{ +} + +RenderPass::RenderPass( RenderPass&& ) noexcept +{ + assert( 0 ); // move operator needed to keep containers happy, but currently not expected to be called (and so not implemented) +} + + +RenderPass::RenderPass(std::span ColorFormats, TextureFormat DepthFormat) noexcept +{ + std::transform(ColorFormats.begin(), ColorFormats.end(), + std::back_inserter(mRenderTargetFormats), [](TextureFormat t) { return TextureFormatToDx(t); }); + mRenderTargetDepthFormat = TextureFormatToDx( DepthFormat ); +} + + +RenderPass CreateRenderPass(Dx12& dx12, + std::span ColorFormats, + TextureFormat DepthFormat, + Msaa Msaa, + RenderPassInputUsage ColorInputUsage, + RenderPassOutputUsage ColorOutputUsage, + bool ShouldClearDepth, + RenderPassOutputUsage DepthOutputUsage, + std::span ResolveFormats) +{ + return RenderPass{ ColorFormats, DepthFormat }; +} + +void ReleaseRenderPass(Dx12& dx12, RenderPass& renderPass) +{ + renderPass = {}; +} + diff --git a/framework/code/dx12/renderPass.hpp b/framework/code/dx12/renderPass.hpp new file mode 100644 index 0000000..9765eff --- /dev/null +++ b/framework/code/dx12/renderPass.hpp @@ -0,0 +1,58 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "graphicsApi/renderPass.hpp" +#include "texture/textureFormat.hpp" +#include "graphicsApi/graphicsApiBase.hpp" + +// Forward declarations +class Dx12; + + +/// Simple wrapper around VkRenderPass. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// Specialization of RenderPass +/// @ingroup Material +template<> +class RenderPass +{ + RenderPass& operator=(const RenderPass&) = delete; + RenderPass(const RenderPass&) = delete; +public: + RenderPass() noexcept; + RenderPass(RenderPass&&) noexcept; + RenderPass(std::span ColorFormats, TextureFormat DepthFormat ) noexcept; + RenderPass& operator=(RenderPass&&) noexcept = default; + ~RenderPass(); + + operator bool() const { return true; } // currently always valid! + + const auto& GetRenderTargetFormats() const { return mRenderTargetFormats; } + DXGI_FORMAT GetRenderTargetDepthFormat() const { return mRenderTargetDepthFormat; } + +protected: + std::vector mRenderTargetFormats; + DXGI_FORMAT mRenderTargetDepthFormat = DXGI_FORMAT_UNKNOWN; +}; + + +RenderPass CreateRenderPass(Dx12&, + std::span < const TextureFormat > ColorFormats, + TextureFormat DepthFormat, + Msaa Msaa, + RenderPassInputUsage ColorInputUsage, + RenderPassOutputUsage ColorOutputUsage, + bool ShouldClearDepth, + RenderPassOutputUsage DepthOutputUsage, + std::span < const TextureFormat > ResolveFormats); + +void ReleaseRenderPass(Dx12& dx12, RenderPass& renderPass); diff --git a/framework/code/dx12/renderTarget.cpp b/framework/code/dx12/renderTarget.cpp new file mode 100644 index 0000000..430a326 --- /dev/null +++ b/framework/code/dx12/renderTarget.cpp @@ -0,0 +1,404 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "renderTarget.hpp" +#include "system/os_common.h" +#include "commandList.hpp" + + +//----------------------------------------------------------------------------- +RenderTarget::RenderTarget() +//----------------------------------------------------------------------------- +{ + // class is fully initialized by member constructors and member value initilization in the class definition +} + +//----------------------------------------------------------------------------- +RenderTarget::~RenderTarget() +//----------------------------------------------------------------------------- +{ + Release(true/*assume we own the framebuffers*/); +} + +//----------------------------------------------------------------------------- +RenderTarget::RenderTarget( RenderTarget&& src ) noexcept +//----------------------------------------------------------------------------- +{ + *this = std::move( src ); +} + +//----------------------------------------------------------------------------- +RenderTarget& RenderTarget::operator=( RenderTarget&& src) noexcept +//----------------------------------------------------------------------------- +{ + if (this != &src) + { + m_Name = std::move(src.m_Name); + m_Width = src.m_Width; + src.m_Width = 0; + m_Height = src.m_Height; + src.m_Height = 0; + m_pLayerFormats = std::move( src.m_pLayerFormats ); + m_Msaa = std::move( src.m_Msaa ); + m_FilterMode = std::move( src.m_FilterMode ); + m_DepthFormat = src.m_DepthFormat; + src.m_DepthFormat = TextureFormat::UNDEFINED; + m_ColorAttachments = std::move( src.m_ColorAttachments ); + m_ClearColors = std::move( src.m_ClearColors ); + m_ResolveAttachments = std::move( src.m_ResolveAttachments ); + m_DepthAttachment = std::move( src.m_DepthAttachment ); + //m_FrameBuffer = src.m_FrameBuffer; + //src.m_FrameBuffer = VK_NULL_HANDLE; + //m_FrameBufferDepthOnly = src.m_FrameBufferDepthOnly; + //src.m_FrameBufferDepthOnly = VK_NULL_HANDLE; + + m_pGfxApi = src.m_pGfxApi; + src.m_pGfxApi = nullptr; + } + + return *this; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::Initialize( Dx12* pGfxApi, const RenderTargetInitializeInfo& info, const char* pName ) +//----------------------------------------------------------------------------- +{ + const size_t numColorAttachments = info.LayerFormats.size(); + + m_pGfxApi = pGfxApi; + m_DepthFormat = info.DepthFormat; + + m_Msaa.assign( info.Msaa.begin(), info.Msaa.end() ); + if (info.Msaa.empty()) + m_Msaa.resize( numColorAttachments, Msaa::Samples1 ); + else + m_Msaa.resize( numColorAttachments, info.Msaa.back() ); + + m_FilterMode.assign( info.FilterModes.begin(), info.FilterModes.end() ); + if (info.FilterModes.empty()) + m_FilterMode.resize( numColorAttachments, SamplerFilter::Linear ); + else + m_FilterMode.resize( numColorAttachments, info.FilterModes.back() ); + + m_Width = info.Width; + m_Height = info.Height; + + // If we have a name, save it + if (pName != NULL) + { + m_Name = pName; + } + + m_ColorAttachments.clear(); + m_ColorAttachments.reserve( numColorAttachments ); + m_ClearColors.resize( numColorAttachments, {{ 0.0f, 0.0f, 0.0f, 0.0f }} ); + m_ResolveAttachments.clear(); + + m_pLayerFormats.assign( info.LayerFormats.begin(), info.LayerFormats.end() ); + + for (auto i = 0; i < numColorAttachments; ++i) + { + m_ColorAttachments.emplace_back( CreateTextureObject( *m_pGfxApi, m_Width, m_Height, info.LayerFormats[i], i < info.TextureTypes.size() ? info.TextureTypes[i] : TT_RENDER_TARGET, m_Name.c_str(), m_Msaa[i])); + } + + if (m_DepthFormat != TextureFormat::UNDEFINED) + m_DepthAttachment = CreateTextureObject( *m_pGfxApi, m_Width, m_Height, m_DepthFormat, TT_DEPTH_TARGET, m_Name.c_str(), m_Msaa[0] ); + else + m_DepthFormat = {}; + + + // Make a descriptor heap for the swapchain render targets + ComPtr descriptorHeap; + if (GetNumColorLayers() > 0) + { + uint32_t rtvHandleSize = 0; + descriptorHeap = m_pGfxApi->CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE_RTV, GetNumColorLayers(), &rtvHandleSize ); + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + // Color + for (UINT i = 0; i < m_ColorAttachments.size(); ++i) + { + const auto& attachment = m_ColorAttachments[i]; + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc{.Format = attachment.GetResourceViewDesc().Format, + .ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D}; + m_pGfxApi->GetDevice()->CreateRenderTargetView( attachment.GetResource(), &rtvDesc, descHandle ); + descHandle.ptr += rtvHandleSize; + } + } + + // Depth + ComPtr depthDescriptorHeap; + if (!m_DepthAttachment.IsEmpty()) + { + uint32_t dsvHandleSize = 0; + depthDescriptorHeap = m_pGfxApi->CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1, &dsvHandleSize ); + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = depthDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + const auto& attachment = m_DepthAttachment; + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc{.Format = attachment.GetResourceViewDesc().Format, + .ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D, + .Flags = D3D12_DSV_FLAG_NONE}; + m_pGfxApi->GetDevice()->CreateDepthStencilView( attachment.GetResource(), &dsvDesc, descHandle ); + } + + m_descriptorHeap = std::move( descriptorHeap ); + m_depthDescriptorHeap = std::move( depthDescriptorHeap ); + return true; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::Initialize(Dx12* pGfxApi, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, std::span Msaa, const char* pName) +//----------------------------------------------------------------------------- +{ + m_pGfxApi = pGfxApi; + m_DepthFormat = DepthFormat; + m_Msaa.assign(Msaa.begin(), Msaa.end()); + m_Msaa.resize(pLayerFormats.size(), Msaa::Samples1); + m_FilterMode.resize(pLayerFormats.size(), SamplerFilter::Linear); + + m_Width = uiWidth; + m_Height = uiHeight; + + // If we have a name, save it + if (pName != NULL) + { + m_Name = pName; + } + + m_ColorAttachments.clear(); + m_ColorAttachments.reserve(pLayerFormats.size()); + m_ClearColors.resize(pLayerFormats.size(), {{ 0.0f, 0.0f, 0.0f, 0.0f }}); + m_ResolveAttachments.clear(); + + m_pLayerFormats.assign(pLayerFormats.begin(), pLayerFormats.end()); + + for(auto i=0;i< pLayerFormats.size(); ++i) + { + m_ColorAttachments.emplace_back( CreateTextureObject( *m_pGfxApi, m_Width, m_Height, pLayerFormats[i], TT_RENDER_TARGET, m_Name.c_str(), m_Msaa.empty() ? Msaa::Samples1 : m_Msaa[0] ) ); + } + + if (m_DepthFormat != TextureFormat::UNDEFINED) + m_DepthAttachment = CreateTextureObject( *m_pGfxApi, m_Width, m_Height, m_DepthFormat, TT_DEPTH_TARGET, m_Name.c_str(), m_Msaa.empty() ? Msaa::Samples1 : m_Msaa[0] ); + else + m_DepthFormat = {}; + + + // Make a descriptor heap for the swapchain render targets + ComPtr descriptorHeap; + if (GetNumColorLayers() > 0) + { + uint32_t rtvHandleSize = 0; + descriptorHeap = m_pGfxApi->CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE_RTV, GetNumColorLayers(), &rtvHandleSize ); + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + // Color + for (UINT i = 0; i < m_ColorAttachments.size(); ++i) + { + const auto& attachment = m_ColorAttachments[i]; + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc{.Format = attachment.GetResourceViewDesc().Format, + .ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D}; + m_pGfxApi->GetDevice()->CreateRenderTargetView( attachment.GetResource(), &rtvDesc, descHandle ); + descHandle.ptr += rtvHandleSize; + } + } + + // Depth + ComPtr depthDescriptorHeap; + if (!m_DepthAttachment.IsEmpty()) + { + uint32_t dsvHandleSize = 0; + depthDescriptorHeap = m_pGfxApi->CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1, &dsvHandleSize ); + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = depthDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + const auto& attachment = m_DepthAttachment; + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc{.Format = attachment.GetResourceViewDesc().Format, + .ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D, + .Flags = D3D12_DSV_FLAG_NONE}; + m_pGfxApi->GetDevice()->CreateDepthStencilView( attachment.GetResource(), &dsvDesc, descHandle ); + } + + m_descriptorHeap = std::move(descriptorHeap); + m_depthDescriptorHeap = std::move( depthDescriptorHeap ); + return true; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeDepth() +//----------------------------------------------------------------------------- +{ + if (m_DepthFormat != TextureFormat::UNDEFINED) + { + char szName[256]; + sprintf(szName, "%s: Depth", m_Name.c_str()); + m_DepthAttachment = CreateTextureObject(*m_pGfxApi, m_Width, m_Height, m_DepthFormat, TT_DEPTH_TARGET, m_Name.c_str(), m_Msaa.empty() ? Msaa::Samples1 : m_Msaa[0]); + } + else + { + ReleaseTexture(*m_pGfxApi, &m_DepthAttachment); + } + return true; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeColor(const std::span TextureTypes) +//----------------------------------------------------------------------------- +{ + const auto NumColorLayers = GetNumColorLayers(); + LOGI("Creating Render Target (%s): (%d x %d); %d color layer[s]", m_Name.c_str(), m_Width, m_Height, NumColorLayers); + + m_ColorAttachments.clear(); + m_ColorAttachments.reserve(NumColorLayers); + + m_ClearColors.clear(); + m_ClearColors.resize(NumColorLayers, {{0.0f,0.0f,0.0f,0.0f}}); + + CreateTexObjectInfo createInfo{}; + createInfo.uiWidth = m_Width; + createInfo.uiHeight = m_Height; + + // First create the actual texture objects... + char szName[256]; + for (size_t WhichLayer = 0; WhichLayer < NumColorLayers; WhichLayer++) + { + sprintf(szName, "%s: Color", m_Name.c_str()); + + createInfo.Format = m_pLayerFormats[WhichLayer]; + createInfo.TexType = (WhichLayer < TextureTypes.size()) ? TextureTypes[WhichLayer] : TT_RENDER_TARGET; + createInfo.pName = m_Name.c_str(); + createInfo.Msaa = m_Msaa[WhichLayer]; + createInfo.FilterMode = m_FilterMode[WhichLayer]; + + m_ColorAttachments.emplace_back(CreateTextureObject(*m_pGfxApi, createInfo)); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeResolve(const std::span ResolveTextureFormats) +//----------------------------------------------------------------------------- +{ + m_ResolveAttachments.clear(); + + if (!m_Msaa.empty() && !ResolveTextureFormats.empty()) + { + const auto NumColorLayers = GetNumColorLayers(); + LOGI("Creating Render Target (%s): (%d x %d); %d resolve layer[s]", m_Name.c_str(), m_Width, m_Height, NumColorLayers); + + m_ResolveAttachments.reserve(NumColorLayers); + + // Create texture objects to resolve in to. + char szName[256]; + for (size_t WhichLayer = 0; WhichLayer < NumColorLayers; WhichLayer++) + { + if (m_Msaa[WhichLayer] != Msaa::Samples1 && WhichLayer < ResolveTextureFormats.size() && ResolveTextureFormats[WhichLayer] != TextureFormat::UNDEFINED) + { + sprintf(szName, "%s: Color Resolve", m_Name.c_str()); + + const TEXTURE_TYPE TextureType = TT_RENDER_TARGET; + m_ResolveAttachments.emplace_back(CreateTextureObject(*m_pGfxApi, m_Width, m_Height, ResolveTextureFormats[WhichLayer], TextureType, m_Name.c_str())); + } + } + } + return true; +} + +//----------------------------------------------------------------------------- +void RenderTarget::SetClearColors(const std::span clearColors) +//----------------------------------------------------------------------------- +{ + assert(clearColors.size() == m_ColorAttachments.size()); + m_ClearColors.assign( std::begin(clearColors), std::end(clearColors) ); +} + +//----------------------------------------------------------------------------- +void RenderTarget::Release(bool bReleaseFramebuffers) +//----------------------------------------------------------------------------- +{ + if (m_pGfxApi == nullptr) + return; + + for (auto& ColorAttachment : m_ColorAttachments) + { + ColorAttachment.Release(m_pGfxApi); + } + m_ColorAttachments.clear(); + m_ClearColors.clear(); + m_pLayerFormats.clear(); + m_FilterMode.clear(); + + for (auto& ResolveAttachment : m_ResolveAttachments) + { + ResolveAttachment.Release(m_pGfxApi); + } + m_ResolveAttachments.clear(); + + m_Msaa.clear(); + + m_DepthAttachment.Release(m_pGfxApi); + m_DepthFormat = TextureFormat::UNDEFINED; + +// if (m_FrameBufferDepthOnly != VK_NULL_HANDLE && bReleaseFramebuffers) +// vkDestroyFramebuffer(m_pGfxApi->m_VulkanDevice, m_FrameBufferDepthOnly, NULL); +// m_FrameBufferDepthOnly = VK_NULL_HANDLE; + +// if (m_FrameBuffer != VK_NULL_HANDLE && bReleaseFramebuffers) +// vkDestroyFramebuffer(m_pGfxApi->m_VulkanDevice, m_FrameBuffer, NULL); +// m_FrameBuffer = VK_NULL_HANDLE; + + m_Height = 0; + m_Width = 0; + m_Name = std::string{}; +} + +//----------------------------------------------------------------------------- +void RenderTarget::SetRenderTarget(CommandList& commandList) +//----------------------------------------------------------------------------- +{ + const D3D12_VIEWPORT viewport{ + .TopLeftX = 0.0f, + .TopLeftY = 0.0f, + .Width = (float)m_Width, + .Height = (float)m_Height, + .MinDepth = 0.0f, + .MaxDepth = 1.0f + }; + const D3D12_RECT scissor{ + .left = 0, + .top = 0, + .right = (LONG)m_Width, + .bottom = (LONG)m_Height + }; + + commandList->RSSetViewports( 1, &viewport ); + commandList->RSSetScissorRects( 1, &scissor ); + + //D3D12_RESOURCE_BARRIER backbufferBarrier{ + // .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + // .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE , + // .Transition = { + // .pResource = m_FrameBuffers[whichFrame].Get(), + // .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + // .StateBefore = D3D12_RESOURCE_STATE_PRESENT, + // .StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET + // } + //}; + //pCommandList->ResourceBarrier( 1, &backbufferBarrier ); + + D3D12_CPU_DESCRIPTOR_HANDLE colorDescriptorHandle{.ptr = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr}; + D3D12_CPU_DESCRIPTOR_HANDLE depthDescriptorHandle{.ptr = m_depthDescriptorHeap ? m_depthDescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr : 0}; + commandList->OMSetRenderTargets( GetNumColorLayers(), &colorDescriptorHandle, FALSE, m_DepthAttachment.IsEmpty() ? nullptr : &depthDescriptorHandle); + + const float clearColor[4] {}; + commandList->ClearRenderTargetView( colorDescriptorHandle, clearColor, 0, nullptr ); + + if (!m_DepthAttachment.IsEmpty()) + { + commandList->ClearDepthStencilView( depthDescriptorHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr ); + } +} diff --git a/framework/code/dx12/renderTarget.hpp b/framework/code/dx12/renderTarget.hpp new file mode 100644 index 0000000..22f141b --- /dev/null +++ b/framework/code/dx12/renderTarget.hpp @@ -0,0 +1,97 @@ +//============================================================================= +// +// +// Copyright (c) 2023 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#pragma once +#include +#include +#include +#include +#include +#include "graphicsApi/renderTarget.hpp" +#include "texture/dx12/texture.hpp" + + +class Dx12; +template class Texture; +template class CommandList; + +union ClearColor +{ + float r32g32b32a32_float[4]; +}; + +template<> +class RenderTarget final : public RenderTargetBase +{ + RenderTarget(const RenderTarget&) = delete; + RenderTarget& operator=(const RenderTarget&) = delete; +public: + RenderTarget(); + ~RenderTarget(); + RenderTarget( RenderTarget&& ) noexcept; + RenderTarget& operator=( RenderTarget&& ) noexcept; + uint32_t GetNumColorLayers() const { return (uint32_t)m_pLayerFormats.size(); } + + bool Initialize(Dx12* pGfxApi, const RenderTargetInitializeInfo& info, const char* pName); + + bool Initialize(Dx12* pGfxApi, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, std::span Msaa = {}, const char* pName = NULL); + bool Initialize(Dx12* pGfxApi, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, Msaa Msaa = Msaa::Samples1, const char* pName = NULL) + { + RenderTargetInitializeInfo info{ + .Width = uiWidth, + .Height = uiHeight, + .LayerFormats = pLayerFormats, + .DepthFormat = DepthFormat, + .Msaa = {&Msaa, 1}, + //.TextureTypes + //.FilterModes(filtermode) + }; + //return Initialize(pGfxApi, uiWidth, uiHeight, pLayerFormats, DepthFormat, { &Msaa,1 }, pName); + return Initialize( pGfxApi, info, pName ); + } + + void SetRenderTarget( CommandList& ); +private: + template friend class RenderTargetArray; + bool InitializeDepth(); + bool InitializeColor(const std::span TextureTypes); + bool InitializeResolve(const std::span ResolveTextureFormats); + + void SetClearColors(const std::span clearColors); + + void Release(bool bReleaseFramebuffers /*set true if we are the owner of the framebuffers (and so want to clean them up)*/); + + // Attributes +public: + std::string m_Name; + + uint32_t m_Width = 0; + uint32_t m_Height = 0; + + std::vector m_pLayerFormats; + std::vector m_Msaa; + std::vector m_FilterMode; + TextureFormat m_DepthFormat = TextureFormat::UNDEFINED; + + // The Color Attachments + + std::vector m_ColorAttachments; + std::vector m_ClearColors; + + // The Resolve Attachments + std::vector m_ResolveAttachments; + + // The Depth Attachment + TextureDx12 m_DepthAttachment; + +private: + Dx12* m_pGfxApi = nullptr; + + ComPtr m_descriptorHeap; + ComPtr m_depthDescriptorHeap; +}; diff --git a/framework/code/graphicsApi/commandList.hpp b/framework/code/graphicsApi/commandList.hpp index f203ad0..41880cc 100644 --- a/framework/code/graphicsApi/commandList.hpp +++ b/framework/code/graphicsApi/commandList.hpp @@ -1,40 +1,69 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2023 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once // Forward declarations -template class CommandListT; +template class CommandList; // Base graphics Command list container class -class CommandList +class CommandListBase { - CommandList(const CommandList&) = delete; - CommandList& operator=(const CommandList&) = delete; + CommandListBase(const CommandListBase&) = delete; + CommandListBase& operator=(const CommandListBase&) = delete; public: - CommandList() noexcept {} - ~CommandList() {} + CommandListBase() noexcept {} + virtual ~CommandListBase() {} - template using tApiDerived = CommandListT; // make apiCast work! + std::string m_Name; + uint32_t m_NumDrawCalls = 0; + uint32_t m_NumTriangles = 0; + + enum class Type + { + // Match Dx12 types (Vulkan supports a subset) + Primary = 0, + Direct = Primary, + Secondary = 1, + Bundle = Secondary, + Compute = 2, + Copy, + VideoDecode, + VideoProcess, + VideoEncode + } m_Type = Type::Primary; + + virtual void Release() = 0; + + template using tApiDerived = CommandList; // make apiCast work! }; +inline void CommandListBase::Release() +{ + m_Name.clear(); + m_NumDrawCalls = 0; + m_NumTriangles = 0; + m_Type = Type::Primary; +} + + // Templated command list container class (templated against graphics api) // Expected for this class to be specialized for each graphics api. template -class CommandListT : public CommandList +class CommandList : public CommandListBase { - CommandListT(const CommandListT&) = delete; - CommandListT& operator=(const CommandListT&) = delete; + CommandList(const CommandList&) = delete; + CommandList& operator=(const CommandList&) = delete; public: - CommandListT() noexcept {} // This class is expected to be specialized! - ~CommandListT() = delete; // This class is expected to be specialized! + CommandList() noexcept {} // This class is expected to be specialized! + ~CommandList() = delete; // This class is expected to be specialized! protected: - static_assert(sizeof(CommandListT) != sizeof(CommandList)); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof(CommandList) != sizeof(CommandListBase)); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/graphicsApi/graphicsApiBase.cpp b/framework/code/graphicsApi/graphicsApiBase.cpp index b5732ec..b681658 100644 --- a/framework/code/graphicsApi/graphicsApiBase.cpp +++ b/framework/code/graphicsApi/graphicsApiBase.cpp @@ -1,10 +1,9 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2023 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #include "graphicsApiBase.hpp" diff --git a/framework/code/graphicsApi/graphicsApiBase.hpp b/framework/code/graphicsApi/graphicsApiBase.hpp index 7b62bad..21556ac 100644 --- a/framework/code/graphicsApi/graphicsApiBase.hpp +++ b/framework/code/graphicsApi/graphicsApiBase.hpp @@ -1,10 +1,9 @@ -//============================================================================================================ -// +//============================================================================= // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include @@ -31,7 +30,7 @@ enum class RenderPassOutputUsage { /// @brief Helpers to convert from base type to graphics api derived type /// Use ONLY if you are sure the base object ACTUALLY matches give graphics api. /// @tparam T_GFXAPI graphics api to cast to. -/// @param rBase base object (eg Texture) +/// @param rBase base object (eg TextureBase) template const auto& apiCast(const T& rBase) { using tDerived = typename T::template tApiDerived; @@ -58,7 +57,7 @@ auto* apiCast(T* rBase) { template auto apiCast(std::unique_ptr&& rBase) { using tDerived = typename T::template tApiDerived; - return std::unique_ptr(static_cast(rBase.release())); + return std::unique_ptr(static_cast(rBase.get())); } diff --git a/framework/code/graphicsApi/pipeline.hpp b/framework/code/graphicsApi/pipeline.hpp new file mode 100644 index 0000000..883c98b --- /dev/null +++ b/framework/code/graphicsApi/pipeline.hpp @@ -0,0 +1,29 @@ +//============================================================================= +// +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +// Forward declarations + + +/// Simple wrapper around VkPipeline or DirectX render target data used by BeginRenderPass and CreateGraphicsPipelineState. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// This template class expected to be specialized (if this template throws compiler errors then the code is not using the specialization classes which is an issue!) +/// @ingroup Material +template +class Pipeline +{ + Pipeline& operator=(const Pipeline&) = delete; + Pipeline(const Pipeline&) = delete; +public: + Pipeline() noexcept = delete; + Pipeline( Pipeline&&) noexcept = delete; + ~Pipeline() = delete; + + static_assert(sizeof( Pipeline) >= 1); // Ensure this class template is specialized (and not used as-is) +}; + diff --git a/framework/code/graphicsApi/renderContext.hpp b/framework/code/graphicsApi/renderContext.hpp new file mode 100644 index 0000000..824f1d2 --- /dev/null +++ b/framework/code/graphicsApi/renderContext.hpp @@ -0,0 +1,28 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +// Forward declarations + + +/// Simple wrapper for a rendering context (RenderContext and Pipeline or a dynamic rendering state) +/// This template class expected to be specialized (if this template throws compiler errors then the code is not using the specialization classes which is an issue!) +/// @ingroup Material +template +class RenderContext +{ + RenderContext& operator=(const RenderContext&) = delete; + RenderContext(const RenderContext&) = delete; +public: + RenderContext() noexcept = delete; + RenderContext(RenderContext&&) noexcept = delete; + ~RenderContext() = delete; + + static_assert(sizeof(RenderContext) >= 1); // Ensure this class template is specialized (and not used as-is) +}; + diff --git a/framework/code/graphicsApi/renderPass.hpp b/framework/code/graphicsApi/renderPass.hpp new file mode 100644 index 0000000..69e72ac --- /dev/null +++ b/framework/code/graphicsApi/renderPass.hpp @@ -0,0 +1,29 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +// Forward declarations + + +/// Simple wrapper around VkRenderPass or DirectX render target data used by BeginRenderPass and CreateGraphicsPipelineState. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// This template class expected to be specialized (if this template throws compiler errors then the code is not using the specialization classes which is an issue!) +/// @ingroup Material +template +class RenderPass +{ + RenderPass& operator=(const RenderPass&) = delete; + RenderPass(const RenderPass&) = delete; +public: + RenderPass() noexcept = delete; + RenderPass(RenderPass&&) noexcept = delete; + ~RenderPass() = delete; + + static_assert(sizeof(RenderPass) >= 1); // Ensure this class template is specialized (and not used as-is) +}; + diff --git a/framework/code/graphicsApi/renderTarget.hpp b/framework/code/graphicsApi/renderTarget.hpp new file mode 100644 index 0000000..5d0e331 --- /dev/null +++ b/framework/code/graphicsApi/renderTarget.hpp @@ -0,0 +1,45 @@ +//============================================================================= +// +// Copyright (c) 2024 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "texture/textureFormat.hpp" +#include "texture/texture.hpp" +#include +#include + +class GraphicsApiBase; +template class RenderTarget; + +struct RenderTargetInitializeInfo +{ + uint32_t Width = 0; + uint32_t Height = 0; + std::span LayerFormats = {}; + TextureFormat DepthFormat = TextureFormat::UNDEFINED; + const std::span TextureTypes = {}; + const std::optional DepthTextureType = std::nullopt; + std::span Msaa = {}; + const std::span ResolveTextureFormats = {}; + const std::span FilterModes = {}; +}; + +class RenderTargetBase +{ + RenderTargetBase( const RenderTargetBase& ) = delete; + RenderTargetBase& operator=( const RenderTargetBase& ) = delete; +public: + RenderTargetBase() = default; + virtual ~RenderTargetBase() = default; + template using tApiDerived = RenderTarget; // make apiCast work! +}; + + +template +class RenderTarget : public RenderTargetBase +{ +protected: + static_assert(sizeof( RenderTarget ) != sizeof( RenderTargetBase )); // Ensure this class template is specialized (and not used as-is) +}; diff --git a/framework/code/gui/android/imguiAndroid.cpp b/framework/code/gui/android/imguiAndroid.cpp index daa07fc..c62947b 100644 --- a/framework/code/gui/android/imguiAndroid.cpp +++ b/framework/code/gui/android/imguiAndroid.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -22,9 +22,9 @@ GuiImguiPlatform::~GuiImguiPlatform() {} -bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight) +bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight) { - if (!GuiImguiBase::Initialize(windowHandle, renderWidth, renderHeight)) + if (!GuiImguiBase::Initialize(windowHandle, renderFormat, renderWidth, renderHeight)) { return false; } diff --git a/framework/code/gui/android/imguiAndroid.hpp b/framework/code/gui/android/imguiAndroid.hpp index d3cd4f9..ac41815 100644 --- a/framework/code/gui/android/imguiAndroid.hpp +++ b/framework/code/gui/android/imguiAndroid.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/gui/gui.hpp b/framework/code/gui/gui.hpp index d21ad45..8bf4087 100644 --- a/framework/code/gui/gui.hpp +++ b/framework/code/gui/gui.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,6 +11,8 @@ /// Game (onscreen) GUI functionality. #include +enum class TextureFormat; + /// /// @brief (Pure virtual) base for gui implementations (gui solution agnostic). @@ -20,7 +22,7 @@ class Gui { public: virtual ~Gui() = 0; - virtual bool Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) = 0; + virtual bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) = 0; virtual void Update() = 0; /// @returns True if the GUI is capturing mouse events (and so they shouldnt be sent to our application code) diff --git a/framework/code/gui/imguiBase.cpp b/framework/code/gui/imguiBase.cpp index 7370968..1e4d7e6 100644 --- a/framework/code/gui/imguiBase.cpp +++ b/framework/code/gui/imguiBase.cpp @@ -1,15 +1,15 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "imguiBase.hpp" -#include "imgui.h" +#include "imgui/imgui.h" -bool GuiImguiBase::Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) +bool GuiImguiBase::Initialize(uintptr_t windowHandle, TextureFormat, uint32_t renderWidth, uint32_t renderHeight) { // Setup Dear ImGui context IMGUI_CHECKVERSION(); @@ -29,7 +29,6 @@ bool GuiImguiBase::Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint void GuiImguiBase::Update() { ImGui::NewFrame(); -// ImGui::ShowDemoWindow(); } bool GuiImguiBase::WantCaptureMouse() const diff --git a/framework/code/gui/imguiBase.hpp b/framework/code/gui/imguiBase.hpp index 5014153..46fd44f 100644 --- a/framework/code/gui/imguiBase.hpp +++ b/framework/code/gui/imguiBase.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -17,7 +17,7 @@ class GuiImguiBase : public Gui { public: - bool Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) override; + bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) override; void Update() override; bool WantCaptureMouse() const override; diff --git a/framework/code/gui/imguiDx12.cpp b/framework/code/gui/imguiDx12.cpp new file mode 100644 index 0000000..8d6828a --- /dev/null +++ b/framework/code/gui/imguiDx12.cpp @@ -0,0 +1,147 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "imguiDx12.hpp" +#include "imgui/imgui.h" +#include "imgui/backends/imgui_impl_dx12.h" +#include "imgui/backends/imgui_impl_win32.h" +#include "dx12/dx12.hpp" +#include "dx12/commandList.hpp" +#include "dx12/renderTarget.hpp" +#include "system/os_common.h" +#include + +// Forward declarations +using namespace Microsoft::WRL; // for ComPtr + + +//----------------------------------------------------------------------------- + +GuiImguiGfx::GuiImguiGfx(Dx12& gfxApi, const RenderPass&/*unused*/) + : GuiImguiPlatform() + , m_GfxApi(gfxApi) +{} + +//----------------------------------------------------------------------------- + +GuiImguiGfx::~GuiImguiGfx() +{ + ImGui_ImplDX12_Shutdown(); +} + +//----------------------------------------------------------------------------- + +bool GuiImguiGfx::Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) +{ + // Call the platform (windows/android) specific implementation initialize... + if (!GuiImguiPlatform::Initialize(windowHandle, renderFormat, m_GfxApi.GetSurfaceWidth(), m_GfxApi.GetSurfaceHeight(), renderWidth, renderHeight)) + { + return false; + } + + // Make a descriptor heap just for the GUI + ComPtr descriptorHeap = m_GfxApi.CreateDescriptorHeap( D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1, nullptr ); + if (!descriptorHeap) + return false; + + // Give imgui its own commandLists + { + m_CommandList.reserve( m_GfxApi.GetSwapchainBufferCount() ); + for (uint32_t imageIdx = 0; imageIdx < m_GfxApi.GetSwapchainBufferCount(); ++imageIdx) + { + if (!m_CommandList.emplace_back().Initialize( &m_GfxApi, "ImGui", CommandListBase::Type::Secondary )) + { + return false; + } + } + m_UploadCommandList = std::make_unique>(); + if (!m_UploadCommandList->Initialize( &m_GfxApi, "ImGui upload" )) + { + return false; + } + } + + // Create the ImGui Dx12 implementation! + { + const auto outputFormat = TextureFormatToDx( renderFormat ); + + D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle = descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle = descriptorHeap->GetGPUDescriptorHandleForHeapStart(); + + if (!ImGui_ImplDX12_Init(m_GfxApi.GetDevice(), + NUM_SWAPCHAIN_BUFFERS, + outputFormat, + m_GfxApi.GetShaderResourceViewDescriptorHeap().GetHeap() /*cbv_srv_heap*/, + font_srv_cpu_desc_handle, + font_srv_gpu_desc_handle)) + { + return false; + } + } + + m_DescriptorHeap = std::move( descriptorHeap ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiImguiGfx::Update() +{ + ImGui_ImplDX12_NewFrame(); + GuiImguiPlatform::Update(); +} + +//----------------------------------------------------------------------------- + +ID3D12GraphicsCommandList* GuiImguiGfx::Render(uint32_t frameIdx, RenderTarget& renderTarget) +{ + auto& commandList = m_CommandList[frameIdx]; + if (!commandList.Begin()) + { + return nullptr; + } + + //if (commandList.m_IsPrimary) + //{ + // VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; + // info.renderPass = m_RenderPass; + // info.framebuffer = frameBuffer; + // info.renderArea.extent.width = m_Vulkan.m_SurfaceWidth; + // info.renderArea.extent.height = m_Vulkan.m_SurfaceHeight; + // info.clearValueCount = 0; + // info.pClearValues = nullptr; + // vkCmdBeginRenderPass(m_CommandBuffer[frameIdx].m_VkCommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + //} + + Render(commandList.Get()); + + //if (m_CommandBuffer[frameIdx].m_IsPrimary) + //{ + // vkCmdEndRenderPass(m_CommandBuffer[frameIdx].m_VkCommandBuffer); + //} + + commandList.End(); + return commandList.Get(); +} + +//----------------------------------------------------------------------------- + +void GuiImguiGfx::Render(ID3D12GraphicsCommandList* commandList) +{ + auto* descriptorHeapPtr = m_DescriptorHeap.Get(); + commandList->SetDescriptorHeaps( 1, &descriptorHeapPtr); + + + ImGui::Render(); + ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList); +} + +//----------------------------------------------------------------------------- + +template class GuiImguiGfx; diff --git a/framework/code/gui/imguiDx12.hpp b/framework/code/gui/imguiDx12.hpp new file mode 100644 index 0000000..1d6eee3 --- /dev/null +++ b/framework/code/gui/imguiDx12.hpp @@ -0,0 +1,45 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "imguiPlatform.hpp" + +class Dx12; +struct ID3D12DescriptorHeap; +struct ID3D12GraphicsCommandList; +template class CommandList; +template class RenderPass; +template class RenderTarget; + +/// +/// @brief DirectX 12 specialized implementation of imgui rendering. +/// Derives from the Windows (or whatever) platform specific class implementation. +/// @ingroup GUI +/// +template<> +class GuiImguiGfx : public GuiImguiPlatform +{ +public: + GuiImguiGfx(Dx12&, const RenderPass&/*unused*/); + ~GuiImguiGfx(); + + bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) override; + void Update() override; + + ID3D12GraphicsCommandList* Render(uint32_t frameIdx, RenderTarget& renderTarget); + void Render( ID3D12GraphicsCommandList* cmdBuffer); + +private: + Dx12& m_GfxApi; + Microsoft::WRL::ComPtr m_DescriptorHeap; + std::vector> m_CommandList; + std::unique_ptr> m_UploadCommandList; +}; diff --git a/framework/code/gui/imguiPlatform.hpp b/framework/code/gui/imguiPlatform.hpp index efa98fe..78bc942 100644 --- a/framework/code/gui/imguiPlatform.hpp +++ b/framework/code/gui/imguiPlatform.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -14,7 +14,7 @@ class Vulkan; /// /// @brief Platform specific imgui class declaration -/// Implementation is in the Windows / Android (or whatever) subdirectory. +/// Implementation derives from this and is in the Windows / Android (or whatever) subdirectory. /// Does the input/screen (but not rendering) for whichever platform we are running on /// @ingroup GUI /// @@ -23,8 +23,8 @@ class GuiImguiPlatform : public GuiImguiBase public: GuiImguiPlatform(); ~GuiImguiPlatform(); - bool Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) override = 0; - bool Initialize(uintptr_t windowHandle, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight); + virtual bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) override = 0; + bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight); void Update() override; bool TouchDownEvent(int iPointerID, float xPos, float yPos) override; @@ -32,7 +32,6 @@ class GuiImguiPlatform : public GuiImguiBase bool TouchUpEvent(int iPointerID, float xPos, float yPos) override; }; - /// /// @brief Graphics API specific imgui rendering class template /// Actual implementations are implemented as a specialization of this class. @@ -41,6 +40,6 @@ class GuiImguiPlatform : public GuiImguiBase template class GuiImguiGfx : public GuiImguiPlatform { - + static_assert(sizeof( GuiImguiGfx ) > sizeof( GuiImguiPlatform )); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/gui/imguiVulkan.cpp b/framework/code/gui/imguiVulkan.cpp index 81d8eab..89545fa 100644 --- a/framework/code/gui/imguiVulkan.cpp +++ b/framework/code/gui/imguiVulkan.cpp @@ -1,13 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "imguiVulkan.hpp" #include "vulkan/vulkan.hpp" +#include "texture/vulkan/texture.hpp" #include "imgui/imgui.h" #include "imgui/backends/imgui_impl_vulkan.h" #include "imgui/backends/imgui_impl_win32.h" @@ -25,9 +26,9 @@ static void check_vk_result(VkResult err) //----------------------------------------------------------------------------- -GuiImguiGfx::GuiImguiGfx(Vulkan& vulkan, VkRenderPass renderPass) +GuiImguiGfx::GuiImguiGfx(Vulkan& vulkan, RenderPass renderPass) : GuiImguiPlatform() - , m_RenderPass(renderPass) + , m_RenderPass(std::move(renderPass)) , m_GfxApi(vulkan) {} @@ -43,14 +44,16 @@ GuiImguiGfx::~GuiImguiGfx() //----------------------------------------------------------------------------- -bool GuiImguiGfx::Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) +bool GuiImguiGfx::Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) { // Call the platform (windows/android) specific implementation initialize... - if (!GuiImguiPlatform::Initialize(windowHandle, m_GfxApi.GetSurfaceWidth(), m_GfxApi.GetSurfaceHeight(), renderWidth, renderHeight)) + if (!GuiImguiPlatform::Initialize(windowHandle, renderFormat, renderWidth, renderHeight, renderWidth, renderHeight )) { return false; } + const auto outputFormat = TextureFormatToVk( renderFormat ); + // Give Imgui its own descriptor pool { // Sizes from imgui example main.cpp @@ -68,12 +71,13 @@ bool GuiImguiGfx::Initialize(uintptr_t windowHandle, uint32_t renderWidt { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } }; - VkDescriptorPoolCreateInfo pool_info = {}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); - pool_info.pPoolSizes = pool_sizes; + const VkDescriptorPoolCreateInfo pool_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1000 * IM_ARRAYSIZE(pool_sizes), + .poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes), + .pPoolSizes = pool_sizes + }; VkResult err = vkCreateDescriptorPool(m_GfxApi.m_VulkanDevice, &pool_info, nullptr, &m_DescriptorPool); check_vk_result(err); if (err != VK_SUCCESS) @@ -87,7 +91,7 @@ bool GuiImguiGfx::Initialize(uintptr_t windowHandle, uint32_t renderWidt m_CommandBuffer.reserve(m_GfxApi.m_SwapchainImageCount); for (uint32_t imageIdx = 0; imageIdx < m_GfxApi.m_SwapchainImageCount; ++imageIdx) { - if (!m_CommandBuffer.emplace_back().Initialize(&m_GfxApi, "ImGui", VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_CommandBuffer.emplace_back().Initialize(&m_GfxApi, "ImGui", CommandListBase::Type::Secondary)) { return false; } @@ -111,8 +115,28 @@ bool GuiImguiGfx::Initialize(uintptr_t windowHandle, uint32_t renderWidt init_info.Allocator = nullptr;// g_Allocator; init_info.MinImageCount = m_GfxApi.m_SwapchainImageCount; //unused? init_info.ImageCount = m_GfxApi.m_SwapchainImageCount; - init_info.RenderPass = m_RenderPass; init_info.CheckVkResultFn = check_vk_result; + init_info.UseDynamicRendering = !m_RenderPass; + if (init_info.UseDynamicRendering) + { + init_info.PipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, + .viewMask = 0, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &outputFormat, + }; + } + else + { + // Non dynamic rendering + init_info.RenderPass = m_RenderPass.mRenderPass; + } + + ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void* vulkan_instance) + { + return vkGetInstanceProcAddr(*(reinterpret_cast(vulkan_instance)), function_name); + }, &init_info.Instance); + if (!ImGui_ImplVulkan_Init(&init_info)) { vkDestroyDescriptorPool(m_GfxApi.m_VulkanDevice, m_DescriptorPool, nullptr); @@ -163,35 +187,37 @@ void GuiImguiGfx::Update() VkCommandBuffer GuiImguiGfx::Render(uint32_t frameIdx, VkFramebuffer frameBuffer) { - if (!m_CommandBuffer[frameIdx].Begin(frameBuffer, m_RenderPass, false, 0, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) + assert(m_RenderPass); // cannot be called when doing dynamic rendering (use Render(VkCommandBuffer)) + if (!m_RenderPass) + return VK_NULL_HANDLE; + + auto& commandBuffer = m_CommandBuffer[frameIdx]; + if (!commandBuffer.Begin(frameBuffer, m_RenderPass, false, 0, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) { return VK_NULL_HANDLE; } - if (m_CommandBuffer[frameIdx].m_IsPrimary) + if (commandBuffer.m_Type == CommandListBase::Type::Primary) { - VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; - info.renderPass = m_RenderPass; - info.framebuffer = frameBuffer; - info.renderArea.extent.width = m_GfxApi.m_SurfaceWidth; - info.renderArea.extent.height = m_GfxApi.m_SurfaceHeight; - info.clearValueCount = 0; - info.pClearValues = nullptr; - vkCmdBeginRenderPass(m_CommandBuffer[frameIdx].m_VkCommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + fvk::VkRenderPassBeginInfo info; + info->renderPass = m_RenderPass.mRenderPass; + info->framebuffer = frameBuffer; + info->renderArea.extent.width = m_GfxApi.m_SurfaceWidth; + info->renderArea.extent.height = m_GfxApi.m_SurfaceHeight; + info->clearValueCount = 0; + info->pClearValues = nullptr; + commandBuffer.BeginRenderPass(*info, VK_SUBPASS_CONTENTS_INLINE); } - Render(m_CommandBuffer[frameIdx].m_VkCommandBuffer); + Render(commandBuffer); - if (m_CommandBuffer[frameIdx].m_IsPrimary) + if (commandBuffer.m_Type == CommandListBase::Type::Primary) { - vkCmdEndRenderPass(m_CommandBuffer[frameIdx].m_VkCommandBuffer); + commandBuffer.EndRenderPass(); } - { - VkResult err = vkEndCommandBuffer(m_CommandBuffer[frameIdx].m_VkCommandBuffer); - check_vk_result(err); - } - return m_CommandBuffer[frameIdx].m_VkCommandBuffer; + commandBuffer.End(); + return commandBuffer; } void GuiImguiGfx::Render(VkCommandBuffer cmdBuffer) diff --git a/framework/code/gui/imguiVulkan.hpp b/framework/code/gui/imguiVulkan.hpp index 0c4135e..f618c91 100644 --- a/framework/code/gui/imguiVulkan.hpp +++ b/framework/code/gui/imguiVulkan.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,6 +12,7 @@ #include "imguiPlatform.hpp" #include "vulkan/vulkan.hpp" #include "vulkan/commandBuffer.hpp" +#include "vulkan/renderPass.hpp" /// /// @brief Vulkan specialized implementation of imgui rendering. @@ -22,20 +23,20 @@ template<> class GuiImguiGfx : public GuiImguiPlatform { public: - GuiImguiGfx(Vulkan&, VkRenderPass renderPass); + GuiImguiGfx(Vulkan&, RenderPass renderPass = {}); ~GuiImguiGfx(); - bool Initialize(uintptr_t windowHandle, uint32_t renderWidth, uint32_t renderHeight) override; + bool Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) override; void Update() override; VkCommandBuffer Render(uint32_t frameIdx, VkFramebuffer frameBuffer); void Render(VkCommandBuffer cmdBuffer); private: - const VkRenderPass m_RenderPass; + const RenderPass m_RenderPass {}; // if null, use dynamic rendering VkDescriptorPool m_DescriptorPool = VK_NULL_HANDLE; - Wrap_VkCommandBuffer m_UploadCommandBuffer; - std::vector m_CommandBuffer; + CommandListVulkan m_UploadCommandBuffer; + std::vector m_CommandBuffer; Vulkan& m_GfxApi; }; diff --git a/framework/code/gui/linux/imguiLinux.cpp b/framework/code/gui/linux/imguiLinux.cpp new file mode 100644 index 0000000..5002ad1 --- /dev/null +++ b/framework/code/gui/linux/imguiLinux.cpp @@ -0,0 +1,58 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================================================ + +#include "imguiLinux.hpp" +#include +#include "imgui/backends/imgui_impl_glfw.h" + +// +// Implementation of GuiImguiPlatform (for glfw) +// + +GuiImguiPlatform::GuiImguiPlatform() +{} + +GuiImguiPlatform::~GuiImguiPlatform() +{ +} + +bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t renderWidth, uint32_t renderHeight) +{ + if (!GuiImguiBase::Initialize(windowHandle, renderFormat, renderWidth, renderHeight)) + { + return false; + } + + if (!ImGui_ImplGlfw_InitForVulkan((GLFWwindow*)windowHandle, true/*callbacks*/)) + { + return false; + } + + // Disable the IME callback, if IME is needed then we can revisit but this fixes a hang when entering text into an entry box. + // Remove if/when we upgrade IMGUI past 1.88. https://github.com/ocornut/imgui/issues/5535 + ImGui::GetIO().SetPlatformImeDataFn = nullptr; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiImguiPlatform::Update() +{ + ImGui_ImplGlfw_NewFrame(); + GuiImguiBase::Update(); +} + +//----------------------------------------------------------------------------- +// Windows inputs are all handled by ImGui_ImplGlfw_NewFrame +// No need to pass these events on to imGui (unlike on Android for instance!) +//----------------------------------------------------------------------------- + +bool GuiImguiPlatform::TouchDownEvent(int iPointerID, float xPos, float yPos) { return false; } +bool GuiImguiPlatform::TouchMoveEvent(int iPointerID, float xPos, float yPos) { return false; } +bool GuiImguiPlatform::TouchUpEvent(int iPointerID, float xPos, float yPos) { return false; } diff --git a/framework/code/gui/linux/imguiLinux.hpp b/framework/code/gui/linux/imguiLinux.hpp new file mode 100644 index 0000000..f1e752d --- /dev/null +++ b/framework/code/gui/linux/imguiLinux.hpp @@ -0,0 +1,17 @@ +//============================================================================= +// +// +// Copyright (c) 2025 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "../imguiPlatform.hpp" + +/// +/// @file imguiLinux.hpp +/// Linux specific implementation for imgui +/// Only Linux specific implementation files should include this header (eg imGuiLinux.cpp and linuxMain.cpp) +/// @ingroup GUI +/// diff --git a/framework/code/gui/windows/imguiWindows.cpp b/framework/code/gui/windows/imguiWindows.cpp index dc8ab3f..4f0edc0 100644 --- a/framework/code/gui/windows/imguiWindows.cpp +++ b/framework/code/gui/windows/imguiWindows.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -29,9 +29,9 @@ GuiImguiPlatform::~GuiImguiPlatform() PFN_Gui_WndProcHandler = nullptr; } -bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight) +bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, TextureFormat renderFormat, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight) { - if (!GuiImguiBase::Initialize(windowHandle, renderWidth, renderHeight)) + if (!GuiImguiBase::Initialize(windowHandle, renderFormat, renderWidth, renderHeight)) { return false; } @@ -43,15 +43,15 @@ bool GuiImguiPlatform::Initialize(uintptr_t windowHandle, uint32_t deviceWidth, PFN_Gui_WndProcHandler = &ImGui_ImplWin32_WndProcHandler; - // Disable the IME callback, if IME is needed then we can revisit but this fixes a hang when entering text into an entry box. - // Remove if/when we upgrade IMGUI past 1.88. https://github.com/ocornut/imgui/issues/5535 - ImGui::GetIO().SetPlatformImeDataFn = nullptr; - return true; } //----------------------------------------------------------------------------- +//bool Initialize( uintptr_t windowHandle, TextureFormat renderFormat, uint32_t deviceWidth, uint32_t deviceHeight, uint32_t renderWidth, uint32_t renderHeight ); + +//----------------------------------------------------------------------------- + void GuiImguiPlatform::Update() { ImGui_ImplWin32_NewFrame(); diff --git a/framework/code/gui/windows/imguiWindows.hpp b/framework/code/gui/windows/imguiWindows.hpp index 4a26d28..edf1e61 100644 --- a/framework/code/gui/windows/imguiWindows.hpp +++ b/framework/code/gui/windows/imguiWindows.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include "../imguiPlatform.hpp" diff --git a/framework/code/helper/postProcess.hpp b/framework/code/helper/postProcess.hpp index df3449c..4b8d4ff 100644 --- a/framework/code/helper/postProcess.hpp +++ b/framework/code/helper/postProcess.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,15 +13,21 @@ // Forward declarations class Vulkan; -class Drawable; -class Computable; +class ComputableBase; +template class Drawable; using Json = nlohmann::json; /// @brief Base class for Post Process classes. /// Roughly defines interface for a post processing class that does the final compositing of output image (generally using a fullscreen shader pass). -/// +/// +template class PostProcess { +protected: + using Drawable = Drawable; + PostProcess(const PostProcess&) = delete; + PostProcess& operator=(const PostProcess&) = delete; + PostProcess() noexcept = default; public: virtual ~PostProcess() = 0; @@ -32,7 +38,8 @@ class PostProcess virtual void UpdateGui() {}; virtual const Drawable* const GetDrawable() const { return nullptr; } - virtual const Computable* const GetComputable() const { return nullptr; } + virtual const ComputableBase* const GetComputable() const { return nullptr; } }; -inline PostProcess::~PostProcess() {} +template +inline PostProcess< T_GFXAPI>::~PostProcess() {} diff --git a/framework/code/helper/postProcessSMAA.cpp b/framework/code/helper/postProcessSMAA.cpp index aa13330..f8b065a 100644 --- a/framework/code/helper/postProcessSMAA.cpp +++ b/framework/code/helper/postProcessSMAA.cpp @@ -1,16 +1,18 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "postProcessSMAA.hpp" #include "imgui/imgui.h" -#include "material/materialManager.hpp" -#include "material/computable.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/shader.hpp" #include "system/os_common.h" +#include "texture/vulkan/texture.hpp" #include "texture/vulkan/textureManager.hpp" @@ -25,7 +27,7 @@ PostProcessSMAA::~PostProcessSMAA() ReleaseUniformBuffer(&m_Vulkan, m_Uniform); } -bool PostProcessSMAA::Init(const Shader& shader, MaterialManagerT& materialManager, TextureVulkan* diffuseRenderTarget, TextureVulkan* depthRenderTarget) +bool PostProcessSMAA::Init(const Shader& shader, MaterialManager& materialManager, TextureVulkan* diffuseRenderTarget, TextureVulkan* depthRenderTarget) { assert(diffuseRenderTarget); assert(depthRenderTarget); @@ -40,8 +42,8 @@ bool PostProcessSMAA::Init(const Shader& shader, MaterialManagerT& mater if (!CreateUniformBuffer(&m_Vulkan, m_Uniform, &m_UniformData)) return false; - auto blitShaderMaterial = materialManager.CreateMaterial(m_Vulkan, shader, (uint32_t) m_historyDiffuse.size(), - [&](const std::string& texName) -> const MaterialManagerT::tPerFrameTexInfo { + auto blitShaderMaterial = materialManager.CreateMaterial(shader, (uint32_t) m_historyDiffuse.size(), + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { return { diffuseRenderTarget }; } @@ -57,17 +59,18 @@ bool PostProcessSMAA::Init(const Shader& shader, MaterialManagerT& mater assert(0); return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_Uniform.vkBuffers.begin(), m_Uniform.vkBuffers.end() }; + [this](const std::string& bufferName) -> PerFrameBufferVulkan { + return { m_Uniform.bufferHandles }; } ); - m_Computable = std::make_unique(m_Vulkan, std::move(blitShaderMaterial)); - if (!m_Computable->Init()) + auto computable = std::make_unique>(m_Vulkan, std::move(blitShaderMaterial)); + if (!computable->Init()) { return false; } - m_Computable->SetDispatchGroupCount(0, { diffuseRenderTarget->Width/16, diffuseRenderTarget->Height/4, 1}); + computable->SetDispatchGroupCount(0, { diffuseRenderTarget->Width/16, diffuseRenderTarget->Height/4, 1}); + m_Computable = std::move(computable); return true; } @@ -98,7 +101,7 @@ void PostProcessSMAA::UpdateGui() { } -const Computable* const PostProcessSMAA::GetComputable() const +const ComputableBase* const PostProcessSMAA::GetComputable() const { return m_Computable.get(); } diff --git a/framework/code/helper/postProcessSMAA.hpp b/framework/code/helper/postProcessSMAA.hpp index abf0a53..f2e9d03 100644 --- a/framework/code/helper/postProcessSMAA.hpp +++ b/framework/code/helper/postProcessSMAA.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -17,12 +17,12 @@ #include // Forward declarations -class Computable; -class Shader; -template class MaterialManagerT; +template class Computable; +template class MaterialManager; +template class Shader; -class PostProcessSMAA : public PostProcess +class PostProcessSMAA : public PostProcess { PostProcessSMAA& operator=(const PostProcessSMAA&) = delete; PostProcessSMAA(const PostProcessSMAA&) = delete; @@ -30,18 +30,18 @@ class PostProcessSMAA : public PostProcess PostProcessSMAA(Vulkan& vulkan); ~PostProcessSMAA(); - bool Init(const Shader& shader, MaterialManagerT& materialManager, TextureVulkan* diffuseRenderTarget, TextureVulkan* depthRenderTarget); + bool Init(const Shader& shader, MaterialManager& materialManager, TextureVulkan* diffuseRenderTarget, TextureVulkan* depthRenderTarget); bool UpdateUniforms(uint32_t WhichFrame, float ElapsedTime) override { return UpdateUniforms(WhichFrame, ElapsedTime, glm::mat4(1.0f)); } bool UpdateUniforms(uint32_t WhichFrame, float ElapsedTime, const glm::mat4& clipToPrevClip); void UpdateGui() override; - const Computable* const GetComputable() const override; + const ComputableBase* const GetComputable() const override; std::span GetOutput() const; protected: Vulkan& m_Vulkan; - std::unique_ptr m_Computable; + std::unique_ptr m_Computable; std::array m_historyDiffuse; struct UniformData { diff --git a/framework/code/helper/postProcessStandard.cpp b/framework/code/helper/postProcessStandard.cpp index c1ca008..08c6a80 100644 --- a/framework/code/helper/postProcessStandard.cpp +++ b/framework/code/helper/postProcessStandard.cpp @@ -1,14 +1,16 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "postProcessStandard.hpp" #include "imgui/imgui.h" -#include "material/materialManager.hpp" -#include "material/drawable.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/material.hpp" +#include "material/vulkan/materialManager.hpp" +#include "vulkan/renderPass.hpp" #include "material/shader.hpp" #include "mesh/meshHelper.hpp" #include "system/os_common.h" @@ -25,7 +27,7 @@ PostProcessStandard::~PostProcessStandard() ReleaseUniformBuffer(&m_Vulkan, m_FragUniform); } -bool PostProcessStandard::Init(const Shader& shader, MaterialManagerT& materialManager, VkRenderPass blitRenderPass, std::span> diffuseRenderTargets, TextureT* bloomRenderTarget, TextureT* uiRenderTarget) +bool PostProcessStandard::Init(const Shader& shader, MaterialManager& materialManager, const RenderPass& blitRenderPass, std::span> diffuseRenderTargets, Texture* bloomRenderTarget, Texture* uiRenderTarget) { assert(!diffuseRenderTargets.empty()); assert(bloomRenderTarget); @@ -42,8 +44,8 @@ bool PostProcessStandard::Init(const Shader& shader, MaterialManagerT& m Mesh blitMesh; MeshHelper::CreateScreenSpaceMesh(m_Vulkan.GetMemoryManager(), 0, &blitMesh); - auto blitShaderMaterial = materialManager.CreateMaterial(m_Vulkan, shader, NUM_VULKAN_BUFFERS, - [&](const std::string& texName) -> const MaterialManagerT::tPerFrameTexInfo { + auto blitShaderMaterial = materialManager.CreateMaterial(shader, NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { return { diffuseRenderTargets.data() }; } @@ -56,14 +58,14 @@ bool PostProcessStandard::Init(const Shader& shader, MaterialManagerT& m assert(0); return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { + [this](const std::string& bufferName) -> PerFrameBufferVulkan { //BlitFragCB - return { m_FragUniform.vkBuffers.begin(), m_FragUniform.vkBuffers.end() }; + return { m_FragUniform.bufferHandles }; } ); m_Drawable = std::make_unique(m_Vulkan, std::move(blitShaderMaterial)); - if (!m_Drawable->Init(blitRenderPass, "RP_BLIT", std::move(blitMesh))) + if (!m_Drawable->Init( blitRenderPass, {}, "RP_BLIT", std::move( blitMesh ) )) { return false; } @@ -132,7 +134,7 @@ void PostProcessStandard::Save(Json& json) const json = Json(*this); } -const Drawable* const PostProcessStandard::GetDrawable() const +const Drawable* const PostProcessStandard::GetDrawable() const { return m_Drawable.get(); } diff --git a/framework/code/helper/postProcessStandard.hpp b/framework/code/helper/postProcessStandard.hpp index 5867034..193874f 100644 --- a/framework/code/helper/postProcessStandard.hpp +++ b/framework/code/helper/postProcessStandard.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -15,13 +15,15 @@ #include // Forward declarations -class Drawable; -class Shader; -template class MaterialManagerT; -template class TextureT; +class ShaderBase; +template class Drawable; +template class MaterialManager; +template class RenderPass; +template class Shader; +template class Texture; -class PostProcessStandard : public PostProcess +class PostProcessStandard : public PostProcess { PostProcessStandard& operator=(const PostProcessStandard&) = delete; PostProcessStandard(const PostProcessStandard&) = delete; @@ -29,7 +31,7 @@ class PostProcessStandard : public PostProcess PostProcessStandard(Vulkan& vulkan); ~PostProcessStandard(); - bool Init(const Shader& shader, MaterialManagerT& materialManager, VkRenderPass blitRenderPass, std::span> diffuseRenderTargets, TextureT* bloomRenderTarget, TextureT* uiRenderTarget); + bool Init(const Shader& shader, MaterialManager& materialManager, const RenderPass& blitRenderPass, std::span> diffuseRenderTargets, Texture* bloomRenderTarget, Texture* uiRenderTarget); bool UpdateUniforms(uint32_t WhichFrame, float ElapsedTime) override; void UpdateGui() override; @@ -55,7 +57,7 @@ class PostProcessStandard : public PostProcess int sRGB = 0; // 1 - apply srgb conversion in output blit shader, 0 passthrough color } m_FragUniformData; UniformArrayT m_FragUniform; - std::vector const*> m_DiffuseRenderTargets; + std::vector const*> m_DiffuseRenderTargets; size_t m_CurrentRenderTargetIdx = 0; private: diff --git a/framework/code/helper/zbufferReduce.cpp b/framework/code/helper/zbufferReduce.cpp new file mode 100644 index 0000000..f274313 --- /dev/null +++ b/framework/code/helper/zbufferReduce.cpp @@ -0,0 +1,234 @@ +//============================================================================= +// +// +// Copyright (c) 2024 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "zbufferReduce.hpp" +#include "vulkan/vulkan.hpp" +#include "vulkan/commandBuffer.hpp" +#include "texture/vulkan/texture.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/materialManager.hpp" +#include + +//----------------------------------------------------------------------------- +bool ZBufferReduce::Init( Vulkan* pVulkan, const Texture& srcDepth, const MaterialManager& materialManager, const Shader* const pReduce4x4Shader, MemoryPool* pPool ) +//----------------------------------------------------------------------------- +{ + using ImageInfo = ImageInfo; + + int numMips = -1; + { + uint32_t x = std::max( srcDepth.Width, srcDepth.Height ); + while (x >>= 1) + { + ++numMips; + } + } + assert( numMips > 1 ); + // Ensure an even number of mips, reduction shader generates 2 lod at a time + if ((numMips & 1) != 0) + --numMips; + const CreateTexObjectInfo reducedZBufferInfo{ + srcDepth.Width / 2, + srcDepth.Height / 2, + 1, // depth + (uint32_t)numMips, + 1, // faces + TextureFormat::R32_SFLOAT, + TT_COMPUTE_TARGET, + TEXTURE_FLAGS::None, + "ReducedZ", + Msaa::Samples1, + SamplerFilter::Nearest, + SamplerAddressMode::Undefined, //default to picking from a default dependant on the texture type + false // UnNormalizedCoordinates + }; + auto reducedZBuffer = CreateTextureObject( *pVulkan, reducedZBufferInfo, pPool ); + + // override the starting image layout because we are binding the 'entire' texture as read input (and sub-image views as outputs and inputs). + VkCommandBuffer setupCmdBuffer = pVulkan->StartSetupCommandBuffer(); + pVulkan->SetImageLayout( reducedZBuffer.GetVkImage(), setupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, reducedZBuffer.FirstMip, reducedZBuffer.MipLevels, 0, 1 ); + pVulkan->FinishSetupCommandBuffer( setupCmdBuffer ); + reducedZBuffer.ImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + std::map mipTextures = { + { "InputDepth", &srcDepth } + }; + constexpr int cMaxMips = 12; + numMips = std::min( cMaxMips, numMips ); + std::map mipImages = { + { "InputDepth", ImageInfo( srcDepth ) }, + { "0", ImageInfo( reducedZBuffer ) }, + { "1", ImageInfo( reducedZBuffer ) }, + { "2", ImageInfo( reducedZBuffer ) }, + { "3", ImageInfo( reducedZBuffer ) }, + { "4", ImageInfo( reducedZBuffer ) }, + { "5", ImageInfo( reducedZBuffer ) }, + { "6", ImageInfo( reducedZBuffer ) }, + { "7", ImageInfo( reducedZBuffer ) }, + { "8", ImageInfo( reducedZBuffer ) }, + { "9", ImageInfo( reducedZBuffer ) }, + { "10", ImageInfo( reducedZBuffer ) }, + { "11", ImageInfo( reducedZBuffer ) } + }; + // Create (and fill in) in the imageviews and 'textures' for each of the mip levels in m_ReducedZBuffer + assert( m_ReducedZBufferMipTexInfos.empty() ); + m_ReducedZBufferMipTexInfos.reserve( cMaxMips ); + + VkImageViewCreateInfo imageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + imageViewCreateInfo.image = reducedZBuffer.GetVkImage(); + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCreateInfo.format = TextureFormatToVk( reducedZBuffer.Format ); + imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCreateInfo.subresourceRange.levelCount = 1; + imageViewCreateInfo.subresourceRange.layerCount = 1; + + VkSamplerCreateInfo samplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + + int mipLevel = 0; + for (; mipLevel < numMips; ++mipLevel) + { + VkImageView imageView = VK_NULL_HANDLE; + VkSampler vkSampler = VK_NULL_HANDLE; + + // Create an imageview and sampler to point to this mip. Assigned/stored in a Texture per mip (all pointing to the same VkImage). + imageViewCreateInfo.subresourceRange.baseMipLevel = mipLevel; + samplerCreateInfo.minLod = float( mipLevel ); + samplerCreateInfo.maxLod = float( mipLevel ); + vkCreateImageView( pVulkan->m_VulkanDevice, &imageViewCreateInfo, nullptr, &imageView ); + vkCreateSampler( pVulkan->m_VulkanDevice, &samplerCreateInfo, nullptr, &vkSampler ); + SamplerVulkan sampler{pVulkan->m_VulkanDevice, vkSampler}; + + ImageViewVulkan imageViewVulkan{imageView,imageViewCreateInfo.viewType}; + m_ReducedZBufferMipTexInfos.emplace_back( reducedZBufferInfo.uiWidth, reducedZBufferInfo.uiHeight, reducedZBufferInfo.uiDepth, 1, mipLevel, 1, 0, reducedZBufferInfo.Format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VkClearValue{}, reducedZBuffer.GetVkImage(), (VkDeviceMemory)VK_NULL_HANDLE, std::move(sampler), std::move(imageViewVulkan)); + + const auto insertedTexture = mipTextures.insert( {std::to_string( mipLevel ), &m_ReducedZBufferMipTexInfos.back()} ); // map name to Texture* for this mip. + const auto insertedImage = mipImages.insert_or_assign( insertedTexture.first->first, *insertedTexture.first->second ); // map name to an ImageInfo for this mip. + insertedImage.first->second.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + for (; mipLevel < cMaxMips; ++mipLevel) + { + const auto insertedTexture = mipTextures.insert( {std::to_string( mipLevel ), &m_ReducedZBufferMipTexInfos.back()} ); // map name to Texture* for the last valid mip. + const auto insertedImage = mipImages.insert_or_assign( insertedTexture.first->first, *insertedTexture.first->second ); // map name to an ImageInfo for this mip. + insertedImage.first->second.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + assert( pReduce4x4Shader ); + auto material = materialManager.CreateMaterial( *pReduce4x4Shader, 1, + [&mipTextures, this]( const std::string& textureName ) -> const MaterialManagerBase::tPerFrameTexInfo { + return {mipTextures.find( textureName )->second}; + }, + nullptr, + [&mipImages]( const std::string& imageName ) -> const ImageInfo { + return mipImages.find( imageName )->second; + } + ); + + auto pComputable = std::make_unique>( *pVulkan, std::move( material ) ); + if (!pComputable->Init()) + return false; + for (uint32_t passIdx = 0; passIdx < (uint32_t)pComputable->GetPasses().size(); ++passIdx) + pComputable->SetDispatchThreadCount( passIdx, {srcDepth.Width, srcDepth.Height, 1} ); + m_ReduceComputable = std::move( pComputable ); + m_NumMips = numMips; + m_ReducedZBuffer = std::make_unique>( std::move( reducedZBuffer ) ); + + return true; +} + +//----------------------------------------------------------------------------- +void ZBufferReduce::Release( Vulkan* pVulkan ) +//----------------------------------------------------------------------------- +{ + for (auto& texInfo : m_ReducedZBufferMipTexInfos) + { + texInfo.Release( pVulkan ); + } + m_ReducedZBufferMipTexInfos.clear(); + if (m_ReducedZBuffer) + m_ReducedZBuffer->Release( pVulkan ); + m_ReducedZBuffer.reset(); + m_ReduceComputable.reset(); +} + +//----------------------------------------------------------------------------- +void ZBufferReduce::UpdateCommandBuffer( CommandList& vkCommandBuffer ) +//----------------------------------------------------------------------------- +{ + std::vector< VkImageMemoryBarrier> reduceInputImageMemoryBarrier; + reduceInputImageMemoryBarrier.push_back( {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + VK_IMAGE_LAYOUT_UNDEFINED, //oldLayout; + VK_IMAGE_LAYOUT_GENERAL, //newLayout; + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + m_ReducedZBuffer->GetVkImage(), //image; + { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; + m_ReducedZBuffer->FirstMip, //baseMipLevel; + m_ReducedZBuffer->MipLevels, //mipLevelCount; + 0, //baseLayer; + 1, //layerCount; + }//subresourceRange; + } ); + + // Barrier on input image, with correct layouts set. + vkCmdPipelineBarrier( vkCommandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, + 0, + 0, nullptr, + 0, nullptr, + (uint32_t)reduceInputImageMemoryBarrier.size(), + reduceInputImageMemoryBarrier.empty() ? nullptr : reduceInputImageMemoryBarrier.data() ); + + int mip = 0; + for (const auto& pass : m_ReduceComputable->GetPasses()) + { + m_ReduceComputable->DispatchPass( vkCommandBuffer, pass, 0 ); + mip += 2; + if (mip >= m_NumMips) // only using a subset of the computable passes + break; + } + + const auto& computableOutputBufferBarriers = m_ReduceComputable->GetBufferOutputMemoryBarriers(); + auto computableOutputImageBarriers = m_ReduceComputable->GetImageOutputMemoryBarriers(); // take a copy! + // transition the last mip too! + computableOutputImageBarriers.emplace_back( VkImageMemoryBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + VK_IMAGE_LAYOUT_GENERAL, //oldLayout; + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, //newLayout; + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + m_ReducedZBuffer->GetVkImage(), //image; + { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; + uint32_t( mip ) - 1, //baseMipLevel; + 1, //mipLevelCount; + 0, //baseLayer; + 1, //layerCount; + }//subresourceRange; + } ); + + // Barrier on output memory, with correct layouts set. + using tBufferBarrier = std::remove_reference::type::value_type::tBarrier; + using tImageBarrier = std::remove_reference::type::value_type::tBarrier; + vkCmdPipelineBarrier( vkCommandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, + 0, + 0, nullptr, + (uint32_t)computableOutputBufferBarriers.size(), + computableOutputBufferBarriers.empty() ? nullptr : (tBufferBarrier*)computableOutputBufferBarriers.data(), + (uint32_t)computableOutputImageBarriers.size(), + (tImageBarrier*)computableOutputImageBarriers.data() ); +} + diff --git a/framework/code/helper/zbufferReduce.hpp b/framework/code/helper/zbufferReduce.hpp new file mode 100644 index 0000000..aad3f6a --- /dev/null +++ b/framework/code/helper/zbufferReduce.hpp @@ -0,0 +1,40 @@ +//============================================================================= +// +// +// Copyright (c) 2024 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#pragma once + +#include +#include + +// Forward declarations +class Vulkan; +template class CommandList; +template class Computable; +template class MaterialManager; +template class MemoryPool; +template class Shader; +template class Texture; + +/// @brief Class to implement a fast zbuffer 'reduction' (pyramid/mipmap of depth images, each half size of previous level) +class ZBufferReduce +{ + ZBufferReduce( const ZBufferReduce& ) = delete; + ZBufferReduce& operator=( const ZBufferReduce& ) = delete; +public: + ZBufferReduce() = default; + bool Init( Vulkan* pVulkan, const Texture& srcDepth, const MaterialManager& materialManager, const Shader* const pReduce4x4Shader, MemoryPool* pPool = nullptr); + void Release( Vulkan* pVulkan ); + void UpdateCommandBuffer( CommandList& ); + auto& GetHierarchicalZTexture() const { return *m_ReducedZBuffer; } + +protected: + std::unique_ptr> m_ReducedZBuffer; ///< half sized 'depth' buffer (half in each dimension of original ZBuffer size) with mips. Populated by the reduce. + std::vector> m_ReducedZBufferMipTexInfos;///< Texture pointing to each of the mip levels in m_ReducedZBuffer. All points to Image owned by m_ReducedZBuffer + std::unique_ptr> m_ReduceComputable; ///< Computable that does the reduce (some passes may not be needed depending on mip count needed) + int m_NumMips = 0; +}; diff --git a/framework/code/light/light.cpp b/framework/code/light/light.cpp index b8149bf..6832b9c 100644 --- a/framework/code/light/light.cpp +++ b/framework/code/light/light.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/light.hpp b/framework/code/light/light.hpp index ad037f0..5947b44 100644 --- a/framework/code/light/light.hpp +++ b/framework/code/light/light.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightData.hpp b/framework/code/light/lightData.hpp index 6131e97..7fbd201 100644 --- a/framework/code/light/lightData.hpp +++ b/framework/code/light/lightData.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightGltfLoader.cpp b/framework/code/light/lightGltfLoader.cpp index 46a5a45..7ffd16a 100644 --- a/framework/code/light/lightGltfLoader.cpp +++ b/framework/code/light/lightGltfLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightGltfLoader.hpp b/framework/code/light/lightGltfLoader.hpp index b468ccc..c268c1a 100644 --- a/framework/code/light/lightGltfLoader.hpp +++ b/framework/code/light/lightGltfLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightList.cpp b/framework/code/light/lightList.cpp index 92deccf..0fa7005 100644 --- a/framework/code/light/lightList.cpp +++ b/framework/code/light/lightList.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightList.hpp b/framework/code/light/lightList.hpp index 49b0871..091a7e3 100644 --- a/framework/code/light/lightList.hpp +++ b/framework/code/light/lightList.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightListGui.cpp b/framework/code/light/lightListGui.cpp index 17dabd8..a6e9534 100644 --- a/framework/code/light/lightListGui.cpp +++ b/framework/code/light/lightListGui.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightListGui.hpp b/framework/code/light/lightListGui.hpp index 8e8862c..54275ba 100644 --- a/framework/code/light/lightListGui.hpp +++ b/framework/code/light/lightListGui.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightLoader.cpp b/framework/code/light/lightLoader.cpp index 44a4fa1..a11c532 100644 --- a/framework/code/light/lightLoader.cpp +++ b/framework/code/light/lightLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/light/lightLoader.hpp b/framework/code/light/lightLoader.hpp index aaecf70..abcbb50 100644 --- a/framework/code/light/lightLoader.hpp +++ b/framework/code/light/lightLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/main/android/androidMain.cpp b/framework/code/main/android/androidMain.cpp index 788895f..3c31a7c 100644 --- a/framework/code/main/android/androidMain.cpp +++ b/framework/code/main/android/androidMain.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -77,11 +77,23 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) // InitializeVulkan assert(engine->application); - assert(engine->application->GetGraphicsApi()); + assert(engine->application->GetGraphicsApiBase()); assert(engine->app->window); if (engine->application) - engine->application->SetWindowSize(ANativeWindow_getWidth(engine->app->window), ANativeWindow_getHeight(engine->app->window)); + { + // Tell the application the Window Size (likely the screen resolution, but may not be!) + engine->application->SetWindowSize( ANativeWindow_getWidth( engine->app->window ), ANativeWindow_getHeight( engine->app->window ) ); + + // If the surface sizes havent been set in app_config then set them to the wind size (so surface is at native device resolution) + const auto applicationWindowSize = engine->application->GetWindowSize(); + const auto* var = GetVariable( "gSurfaceWidth" ); + if ((nullptr == var || 0 == (var->GetFlags() & kVariableModified)) && applicationWindowSize.first > 0) + gSurfaceWidth = applicationWindowSize.first; + var = GetVariable( "gSurfaceHeight" ); + if ((nullptr == var || 0 == (var->GetFlags() & kVariableModified)) && applicationWindowSize.second > 0) + gSurfaceHeight = applicationWindowSize.second; + } if (engine->initialized) { diff --git a/framework/code/main/applicationEntrypoint.hpp b/framework/code/main/applicationEntrypoint.hpp index 040e8c9..a7d7d2c 100644 --- a/framework/code/main/applicationEntrypoint.hpp +++ b/framework/code/main/applicationEntrypoint.hpp @@ -1,10 +1,9 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #pragma once #include "frameworkApplicationBase.hpp" diff --git a/framework/code/main/applicationHelperBase.cpp b/framework/code/main/applicationHelperBase.cpp index 4f3e838..c56d97c 100644 --- a/framework/code/main/applicationHelperBase.cpp +++ b/framework/code/main/applicationHelperBase.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,11 +10,11 @@ #include "memory/vulkan/vertexBufferObject.hpp" #include "camera/cameraController.hpp" #include "camera/cameraControllerTouch.hpp" -#include "material/computable.hpp" -#include "material/drawable.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/material.hpp" +#include "material/vulkan/materialManager.hpp" #include "material/vertexFormat.hpp" -#include "material/materialManager.hpp" -#include "material/vulkan/specializationConstantsLayout.hpp" #include "material/shaderManagerT.hpp" #include "mesh/meshHelper.hpp" #include "texture/vulkan/texture.hpp" @@ -29,12 +29,15 @@ #include "vulkanRT/traceable.hpp" #endif // VK_KHR_ray_tracing_pipeline #include "applicationHelperBase.hpp" // last header to be included +using namespace std::string_literals; extern "C" { VAR(float, gCameraRotateSpeed, 0.25f, kVariableNonpersistent); VAR(float, gCameraMoveSpeed, 4.0f, kVariableNonpersistent); }; //extern "C" +static const uint32_t cClickTimeMs = 400; //max time we count as a single 'click' (finger/button down and up) +static const uint32_t cClickReleaseTimeMs = 400; //max time we count as a 'release' in the middle of a double-click (finger/button up and down) //----------------------------------------------------------------------------- ApplicationHelperBase::ApplicationHelperBase() noexcept : FrameworkApplicationBase() @@ -66,11 +69,18 @@ bool ApplicationHelperBase::Initialize(uintptr_t hWnd, uintptr_t hInstance) return false; } - CRenderTargetArray backbuffer; - if (!m_BackbufferRenderTarget.InitializeFromSwapchain(pVulkan)) - return false; + // Backbuffer/swapchain render target and render context + for(uint32_t frameIdx=0; frameIdxGetSwapchainBufferCount(); frameIdx++) + { + const TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; + const TextureFormat depthFormat = pVulkan->m_SwapchainDepth.format; + + if (!m_BackbufferRenderTarget[frameIdx].Initialize( pVulkan, pVulkan->m_SurfaceWidth, pVulkan->m_SurfaceHeight, {&surfaceFormat, 1}, depthFormat, Msaa::Samples1, "Swapchain" )) + return false; + m_BackbufferRenderContext[frameIdx] = {{}/*renderPass*/, {}/*pipeline*/, pVulkan->m_SwapchainBuffers[frameIdx].framebuffer, "Swapchain"s}; + } - auto textureManagerVulkan = std::make_unique(*pVulkan); + auto textureManagerVulkan = std::make_unique(*pVulkan, *m_AssetManager); if (!textureManagerVulkan->Initialize()) return false; m_TextureManager = std::move(textureManagerVulkan); @@ -85,9 +95,42 @@ bool ApplicationHelperBase::Initialize(uintptr_t hWnd, uintptr_t hInstance) if (m_SamplerMirroredRepeat.IsEmpty()) return false; - m_ShaderManager = std::make_unique>(*pVulkan); + m_ShaderManager = std::make_unique(*pVulkan); + + m_MaterialManager = std::make_unique(*pVulkan); + + return true; +} + +//----------------------------------------------------------------------------- +bool ApplicationHelperBase::ReInitialize( uintptr_t hWnd, uintptr_t hInstance ) +//----------------------------------------------------------------------------- +{ + if (!FrameworkApplicationBase::ReInitialize( hWnd, hInstance )) + return false; + + auto* const pVulkan = GetVulkan(); + + // Attempt to re-init vulkan surface etc with the new window + if (!pVulkan->ReInit( hWnd )) + return false; + + // Recreate the vulkan swapchain with the new vulkan surface. + if (!pVulkan->RecreateSwapChain()) + return false; + + // Remake the m_BackbufferRenderTarget helper object. + for (auto& renderTarget : m_BackbufferRenderTarget) + renderTarget.Release(); + for (uint32_t frameIdx = 0; frameIdx < pVulkan->GetSwapchainBufferCount(); frameIdx++) + { + const TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; + const TextureFormat depthFormat = pVulkan->m_SwapchainDepth.format; - m_MaterialManager = std::make_unique>(); + if (!m_BackbufferRenderTarget[frameIdx].Initialize( pVulkan, pVulkan->m_SurfaceWidth, pVulkan->m_SurfaceHeight, {&surfaceFormat, 1}, depthFormat, Msaa::Samples1, "Swapchain" )) + return false; + m_BackbufferRenderContext[frameIdx] = {{}/*renderPass*/, {}/*pipeline*/, pVulkan->m_SwapchainBuffers[frameIdx].framebuffer, "Swapchain"s}; + } return true; } @@ -98,18 +141,16 @@ void ApplicationHelperBase::Destroy() { auto* const pVulkan = GetVulkan(); - vkDestroySampler(pVulkan->m_VulkanDevice, m_SamplerMirroredRepeat.GetVkSampler(), nullptr); - //m_SamplerMirroredRepeat = VK_NULL_HANDLE; - vkDestroySampler(pVulkan->m_VulkanDevice, m_SamplerEdgeClamp.GetVkSampler(), nullptr); - //m_SamplerEdgeClamp = VK_NULL_HANDLE; - vkDestroySampler(pVulkan->m_VulkanDevice, m_SamplerRepeat.GetVkSampler(), nullptr); - //m_SamplerRepeat = VK_NULL_HANDLE; + ReleaseSampler(*pVulkan, &m_SamplerMirroredRepeat); + ReleaseSampler(*pVulkan, &m_SamplerEdgeClamp); + ReleaseSampler(*pVulkan, &m_SamplerRepeat); m_TextureManager.reset(); m_MaterialManager.reset(); m_ShaderManager.reset(); + for (auto& renderTarget : m_BackbufferRenderTarget) + renderTarget.Release(); - //m_BackbufferRenderTarget.HardReset(); // DO NOT destroy as this instance does not own its framebuffer (points to the vulkan backbuffers) FrameworkApplicationBase::Destroy(); } @@ -130,10 +171,19 @@ bool ApplicationHelperBase::SetWindowSize(uint32_t width, uint32_t height) } //----------------------------------------------------------------------------- -int ApplicationHelperBase::PreInitializeSelectSurfaceFormat(std::span) +int ApplicationHelperBase::PreInitializeSelectSurfaceFormat(std::span formats) //----------------------------------------------------------------------------- { - return -1; + // We want to select a SRGB output format (if one exists) unless running on HLM (does not support srgb output) + TextureFormat idealFormat = gRunOnHLM ? TextureFormat::B8G8R8A8_UNORM : TextureFormat::B8G8R8A8_SRGB; + int index = 0; + for (const auto& format : formats) + { + if (format.format == idealFormat) + return index; + ++index; + } + return -1; // let the driver decide } //----------------------------------------------------------------------------- @@ -168,9 +218,34 @@ bool ApplicationHelperBase::InitCamera() } //----------------------------------------------------------------------------- -void ApplicationHelperBase::AddDrawableToCmdBuffers(const Drawable& drawable, Wrap_VkCommandBuffer* cmdBuffers, uint32_t numRenderPasses, uint32_t numVulkanBuffers, uint32_t startDescriptorSetIdx) const +void ApplicationHelperBase::AddDrawableToCmdBuffers(const Drawable& drawable, CommandBufferT* cmdBuffers, uint32_t numRenderPasses, uint32_t numFrameBuffers, uint32_t startDescriptorSetIdx) const //----------------------------------------------------------------------------- { + const auto& drawablePasses = drawable.GetDrawablePasses(); + for (const auto& drawablePass : drawablePasses) + { + const auto passIdx = drawablePass.mPassIdx; + assert(passIdx < numRenderPasses); + + for (uint32_t bufferIdx = 0; bufferIdx < numFrameBuffers; ++bufferIdx) + { + CommandBufferT& buffer = cmdBuffers[bufferIdx * numRenderPasses + passIdx]; + assert(buffer.m_VkCommandBuffer != VK_NULL_HANDLE); + + // Add commands to bind the pipeline, buffers etc and issue the draw. + drawable.DrawPass(buffer, drawablePass, drawablePass.mDescriptorSet.empty() ? 0 : (startDescriptorSetIdx + bufferIdx) % drawablePass.mDescriptorSet.size() ); + + ++buffer.m_NumDrawCalls; + buffer.m_NumTriangles += drawablePass.mNumVertices / 3; + } + } +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::AddDrawableToCmdBuffers(const Drawable& drawable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t whichVulkanBuffer, uint32_t startDescriptorSetIdx, float dummy) const +//----------------------------------------------------------------------------- +{ + // Version of AddDrawableToCmdBuffers that doesn't add to every vulkan buffer Vulkan* pVulkan = GetVulkan(); const auto& drawablePasses = drawable.GetDrawablePasses(); @@ -179,23 +254,22 @@ void ApplicationHelperBase::AddDrawableToCmdBuffers(const Drawable& drawable, Wr const auto passIdx = drawablePass.mPassIdx; assert(passIdx < numRenderPasses); - for (uint32_t bufferIdx = 0; bufferIdx < numVulkanBuffers; ++bufferIdx) + uint32_t bufferIdx = whichVulkanBuffer; { - Wrap_VkCommandBuffer& buffer = cmdBuffers[bufferIdx * numRenderPasses + passIdx]; - VkCommandBuffer cmdBuffer = buffer.m_VkCommandBuffer; - assert(cmdBuffer != VK_NULL_HANDLE); + auto& cmdBuffer = cmdBuffers[bufferIdx * numRenderPasses + passIdx]; + assert(cmdBuffer.m_VkCommandBuffer != VK_NULL_HANDLE); // Add commands to bind the pipeline, buffers etc and issue the draw. drawable.DrawPass(cmdBuffer, drawablePass, drawablePass.mDescriptorSet.empty() ? 0 : (startDescriptorSetIdx + bufferIdx) % drawablePass.mDescriptorSet.size() ); - ++buffer.m_NumDrawCalls; - buffer.m_NumTriangles += drawablePass.mNumVertices / 3; + ++cmdBuffer.m_NumDrawCalls; + cmdBuffer.m_NumTriangles += drawablePass.mNumVertices / 3; } } } //----------------------------------------------------------------------------- -void ApplicationHelperBase::AddComputableToCmdBuffer(const Computable& computable, Wrap_VkCommandBuffer* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool) const +void ApplicationHelperBase::AddComputableToCmdBuffer( const Computable & computable, CommandList * cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx, TimerPoolBase * timerPool ) const //----------------------------------------------------------------------------- { // LOGI("AddComputableToCmdBuffer() Entered..."); @@ -204,46 +278,49 @@ void ApplicationHelperBase::AddComputableToCmdBuffer(const Computable& computabl for(uint32_t whichBuffer = 0; whichBuffer < numCmdBuffers; ++whichBuffer) { - for (uint32_t passIdx =0; const auto& computablePass : computable.GetPasses()) - { - const int timerIdx = timerPool ? cmdBuffers->StartGpuTimer( computable.GetPassName( passIdx )) : -1; - computable.DispatchPass(cmdBuffers->m_VkCommandBuffer, computablePass, (whichBuffer + startDescriptorSetIdx) % (uint32_t)computablePass.GetVkDescriptorSets().size()); - if (timerIdx>=0) - cmdBuffers->StopGpuTimer( timerIdx ); - ++passIdx; - } + computable.Dispatch(*cmdBuffers, whichBuffer + startDescriptorSetIdx, true/*timers*/); ++cmdBuffers; } } //----------------------------------------------------------------------------- -void ApplicationHelperBase::AddComputableOutputBarrierToCmdBuffer(const Computable& computable, Wrap_VkCommandBuffer* cmdBuffers, uint32_t numCmdBuffers) const +void ApplicationHelperBase::AddComputableToCmdBuffer(const ComputableBase& computable, CommandList* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool) const //----------------------------------------------------------------------------- { - const auto& computableOutputBufferBarriers = computable.GetBufferOutputMemoryBarriers(); - const auto& computableOutputImageBarriers = computable.GetImageOutputMemoryBarriers(); + assert( cmdBuffers != nullptr ); - if (computableOutputBufferBarriers.empty() && computableOutputImageBarriers.empty()) - return; + for (uint32_t whichBuffer = 0; whichBuffer < numCmdBuffers; ++whichBuffer) + { + computable.Dispatch( *cmdBuffers, whichBuffer + startDescriptorSetIdx, true/*timers*/ ); + ++cmdBuffers; + } +} - // Barrier on memory, with correct layouts set. - for (uint32_t WhichBuffer = 0; WhichBuffer < numCmdBuffers; ++WhichBuffer) +//----------------------------------------------------------------------------- +void ApplicationHelperBase::AddComputableOutputBarrierToCmdBuffer( const Computable& computable, CommandList* cmdBuffers, uint32_t numCmdBuffers ) const +//----------------------------------------------------------------------------- +{ + for (uint32_t whichBuffer = 0; whichBuffer < numCmdBuffers; ++whichBuffer) { - vkCmdPipelineBarrier(cmdBuffers[WhichBuffer].m_VkCommandBuffer, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, - 0, - 0, nullptr, - (uint32_t)computableOutputBufferBarriers.size(), - computableOutputBufferBarriers.empty() ? nullptr : computableOutputBufferBarriers.data(), - (uint32_t)computableOutputImageBarriers.size(), - computableOutputImageBarriers.empty() ? nullptr : computableOutputImageBarriers.data()); + computable.AddOutputBarriersToCmdList( *cmdBuffers ); + ++cmdBuffers; + } +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::AddComputableOutputBarrierToCmdBuffer(const ComputableBase& computable, CommandList* cmdBuffers, uint32_t numCmdBuffers) const +//----------------------------------------------------------------------------- +{ + for (uint32_t whichBuffer = 0; whichBuffer < numCmdBuffers; ++whichBuffer) + { + computable.AddOutputBarriersToCmdList(*cmdBuffers); + ++cmdBuffers; } } #if VK_KHR_ray_tracing_pipeline //----------------------------------------------------------------------------- -void ApplicationHelperBase::AddTraceableToCmdBuffer(const Traceable& traceable, Wrap_VkCommandBuffer* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool) const +void ApplicationHelperBase::AddTraceableToCmdBuffer(const Traceable& traceable, CommandList* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool) const //----------------------------------------------------------------------------- { // LOGI("AddTraceableToCmdBuffer() Entered..."); @@ -255,7 +332,7 @@ void ApplicationHelperBase::AddTraceableToCmdBuffer(const Traceable& traceable, for (uint32_t passIdx = 0; const auto& traceablePass : traceable.GetPasses()) { const int timerIdx = timerPool ? cmdBuffers->StartGpuTimer( traceable.GetPassName( passIdx ) ) : -1; - traceable.DispatchPass(cmdBuffers->m_VkCommandBuffer, traceablePass, (whichBuffer + startDescriptorSetIdx) % (uint32_t)traceablePass.GetVkDescriptorSets().size()); + traceable.DispatchPass(*cmdBuffers, traceablePass, (whichBuffer + startDescriptorSetIdx) % (uint32_t)traceablePass.GetVkDescriptorSets().size()); if (timerIdx >= 0) cmdBuffers->StopGpuTimer( timerIdx ); ++passIdx; @@ -270,10 +347,8 @@ bool ApplicationHelperBase::PresentQueue( const std::span Wai //----------------------------------------------------------------------------- { auto& vulkan = *GetVulkan(); - if (!vulkan.PresentQueue( WaitSemaphores, SwapchainPresentIndx )) return false; - #if OS_WINDOWS { static int gHLMFrameNumber = -1; @@ -282,111 +357,109 @@ bool ApplicationHelperBase::PresentQueue( const std::span Wai if (gHLMFrameNumber >= gHLMDumpFrame && gHLMFrameNumber < gHLMDumpFrame + gHLMDumpFrameCount && gHLMDumpFile!=nullptr && *gHLMDumpFile!='\0') { bool dumpResult = DumpImagePixelData( - vulkan, - vulkan.GetSwapchainImage(SwapchainPresentIndx), - vulkan.GetSurfaceFormat(), - vulkan.GetSurfaceWidth(), - vulkan.GetSurfaceHeight(), - true, - 0, - 0, - []( uint32_t width, uint32_t height, TextureFormat format, uint32_t spanBytes, const void* data ) - { - auto* dataBytes = static_cast(data); + vulkan, + vulkan.GetSwapchainImage( SwapchainPresentIndx ), + vulkan.GetSurfaceFormat(), + vulkan.GetSurfaceWidth(), + vulkan.GetSurfaceHeight(), + true, + 0, + 0, + []( uint32_t width, uint32_t height, TextureFormat format, uint32_t spanBytes, const void* data ) + { + auto* dataBytes = static_cast(data); - std::string fullFileName( gHLMDumpFile ); - const std::string cBmp( ".bmp" ); + std::string fullFileName( gHLMDumpFile ); + const std::string cBmp( ".bmp" ); - size_t extPos = fullFileName.rfind( '.' ); - if (extPos == -1) - { - extPos = fullFileName.size(); - fullFileName.append( cBmp ); - } - if (gHLMDumpFrameCount > 1) - { - // Number the frames (if asking for more than one frame to be saved) - const std::string frameIndex( std::to_string( gHLMFrameNumber ) ); - fullFileName.insert( extPos, frameIndex ); - extPos += frameIndex.size(); - } - if (extPos != -1 && !std::equal( std::begin( fullFileName ) + extPos, std::end( fullFileName ), std::begin( cBmp ), std::end( cBmp ), []( auto a, auto b )->bool { return std::tolower( a ) == std::tolower( b ); } )) - { - // save a non BMP image - SaveTextureData( fullFileName.c_str(), format, width, height, data ); - } - else - { - #pragma pack(push,2) - struct bmp_file_header { - uint16_t bfType; - uint32_t bfSize; - uint16_t bfReserved1; - uint16_t bfReserved2; - uint32_t bfOffBits; - }; - #pragma pack(pop) - struct bmp_v4_info_header { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; - uint32_t biRedMask; - uint32_t biGreenMask; - uint32_t biBlueMask; - uint32_t biAlphaMask; - uint32_t biCSType; - uint32_t biEndPoints[9]; - uint32_t biGammaRed; - uint32_t biGammaGreen; - uint32_t biGammaBlue; - }; - - FILE* stream; - struct bmp_file_header bmf; - struct bmp_v4_info_header bmi; - - memset( &bmf, 0, sizeof( bmf ) ); - bmf.bfType = 0x4d42; - bmf.bfSize = sizeof( bmf ) + sizeof( bmi ) + (height * width * 4); - bmf.bfOffBits = sizeof( bmf ) + sizeof( bmi ); - - memset( &bmi, 0, sizeof( bmi ) ); - bmi.biSize = sizeof( bmi ); - bmi.biWidth = width; - bmi.biHeight = /*-(int32_t)*/height; - bmi.biPlanes = 1; - bmi.biBitCount = 32; - bmi.biCompression = BI_BITFIELDS; - bmi.biSizeImage = 0; - bmi.biRedMask = 0xff; - bmi.biGreenMask = 0xff00; - bmi.biBlueMask = 0xff0000; - bmi.biAlphaMask = 0xff00000; - - stream = fopen( fullFileName.c_str(), "wb" ); - assert( stream ); - - fwrite( &bmf, sizeof( bmf ), 1, stream ); - fwrite( &bmi, sizeof( bmi ), 1, stream ); - - for (int y = height - 1; y >= 0; y--) { - fwrite( dataBytes + spanBytes * y, width * 4, 1, stream ); - } - fclose( stream ); + size_t extPos = fullFileName.rfind( '.' ); + if (extPos == -1) + { + extPos = fullFileName.size(); + fullFileName.append( cBmp ); + } + if (gHLMDumpFrameCount > 1) + { + // Number the frames (if asking for more than one frame to be saved) + const std::string frameIndex( std::to_string( gHLMFrameNumber ) ); + fullFileName.insert( extPos, frameIndex ); + extPos += frameIndex.size(); + } + if (extPos != -1 && !std::equal( std::begin( fullFileName ) + extPos, std::end( fullFileName ), std::begin( cBmp ), std::end( cBmp ), []( auto a, auto b )->bool { return std::tolower( a ) == std::tolower( b ); } )) + { + // save a non BMP image + SaveTextureData( fullFileName.c_str(), format, width, height, data ); + } + else + { +#pragma pack(push,2) + struct bmp_file_header { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; + }; +#pragma pack(pop) + struct bmp_v4_info_header { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; + uint32_t biRedMask; + uint32_t biGreenMask; + uint32_t biBlueMask; + uint32_t biAlphaMask; + uint32_t biCSType; + uint32_t biEndPoints[9]; + uint32_t biGammaRed; + uint32_t biGammaGreen; + uint32_t biGammaBlue; + }; + + FILE* stream; + struct bmp_file_header bmf; + struct bmp_v4_info_header bmi; + + memset( &bmf, 0, sizeof( bmf ) ); + bmf.bfType = 0x4d42; + bmf.bfSize = sizeof( bmf ) + sizeof( bmi ) + (height * width * 4); + bmf.bfOffBits = sizeof( bmf ) + sizeof( bmi ); + + memset( &bmi, 0, sizeof( bmi ) ); + bmi.biSize = sizeof( bmi ); + bmi.biWidth = width; + bmi.biHeight = /*-(int32_t)*/height; + bmi.biPlanes = 1; + bmi.biBitCount = 32; + bmi.biCompression = BI_BITFIELDS; + bmi.biSizeImage = 0; + bmi.biRedMask = 0xff; + bmi.biGreenMask = 0xff00; + bmi.biBlueMask = 0xff0000; + bmi.biAlphaMask = 0xff00000; + + stream = fopen( fullFileName.c_str(), "wb" ); + assert( stream ); + + fwrite( &bmf, sizeof( bmf ), 1, stream ); + fwrite( &bmi, sizeof( bmi ), 1, stream ); + + for (int y = height - 1; y >= 0; y--) { + fwrite( dataBytes + spanBytes * y, width * 4, 1, stream ); } - } ); - if (!dumpResult) - { - return false; - } + fclose( stream ); + } + + + } ); } } #endif // OS_WINDOWS @@ -409,10 +482,12 @@ bool ApplicationHelperBase::PresentQueue(const std::span Wa return PresentQueue( std::span(semaphores).subspan(0, numSemaphores), SwapchainPresentIndx); } +//----------------------------------------------------------------------------- bool ApplicationHelperBase::PresentQueue(const SemaphoreWait& WaitSemaphore, uint32_t SwapchainPresentIndx) +//----------------------------------------------------------------------------- { - auto WaitSemaphores = std::span(&WaitSemaphore, 1); - return PresentQueue(WaitSemaphores, SwapchainPresentIndx); + auto waitSemaphores = std::span(&WaitSemaphore, 1); + return PresentQueue(waitSemaphores, SwapchainPresentIndx); } //----------------------------------------------------------------------------- @@ -442,7 +517,7 @@ void ApplicationHelperBase::TouchDownEvent(int iPointerID, float xPos, float yPo //----------------------------------------------------------------------------- { // Make sure we are big enough for this ID - m_TouchStates.resize(std::max(m_TouchStates.size(), (size_t)(iPointerID + 1)), {}); + m_TouchStates.resize(std::max(m_TouchStates.size(), size_t(iPointerID + 1))); m_TouchStates[iPointerID].m_isDown = true; m_TouchStates[iPointerID].m_xPos = xPos; @@ -450,6 +525,16 @@ void ApplicationHelperBase::TouchDownEvent(int iPointerID, float xPos, float yPo m_TouchStates[iPointerID].m_xDownPos = xPos; m_TouchStates[iPointerID].m_yDownPos = yPos; + const uint32_t timeNowMS = uint32_t( OS_GetTimeUS() / uint64_t( 1000 ) ); + const uint32_t releasedTime = timeNowMS - m_TouchStates[iPointerID].m_lastDownChangeTimeMs; + m_TouchStates[iPointerID].m_lastDownChangeTimeMs = timeNowMS; + + if (releasedTime < cClickReleaseTimeMs && m_TouchStates[iPointerID].m_clicks > 0) + // We count a quick relase IF we already did a quick click (we are 2/3 of the way through a double click!) + m_TouchStates[iPointerID].m_clicks++; + else + m_TouchStates[iPointerID].m_clicks = 0; + if (m_CameraController) m_CameraController->TouchDownEvent(iPointerID, xPos, yPos); } @@ -459,11 +544,7 @@ void ApplicationHelperBase::TouchMoveEvent(int iPointerID, float xPos, float yPo //----------------------------------------------------------------------------- { // Make sure we are big enough for this ID - while (m_TouchStates.size() < iPointerID + 1) - { - TouchStatus NewEntry; - m_TouchStates.push_back(NewEntry); - } + m_TouchStates.resize(std::max(m_TouchStates.size(), size_t(iPointerID + 1))); m_TouchStates[iPointerID].m_isDown = true; m_TouchStates[iPointerID].m_xPos = xPos; @@ -478,25 +559,44 @@ void ApplicationHelperBase::TouchUpEvent(int iPointerID, float xPos, float yPos) //----------------------------------------------------------------------------- { // Make sure we are big enough for this ID - while (m_TouchStates.size() < iPointerID + 1) - { - TouchStatus NewEntry; - m_TouchStates.push_back(NewEntry); - } + m_TouchStates.resize(std::max(m_TouchStates.size(), size_t(iPointerID + 1))); m_TouchStates[iPointerID].m_isDown = false; m_TouchStates[iPointerID].m_xPos = xPos; m_TouchStates[iPointerID].m_yPos = yPos; + const uint32_t timeNowMS = uint32_t(OS_GetTimeUS() / uint64_t(1000)); + const uint32_t pressTime = timeNowMS - m_TouchStates[iPointerID].m_lastDownChangeTimeMs; + m_TouchStates[iPointerID].m_lastDownChangeTimeMs = timeNowMS; + + if (pressTime < cClickTimeMs) + { + if (m_TouchStates[iPointerID].m_clicks == 2) + { + // Double click! + TouchDoubleClickEvent(iPointerID); + m_TouchStates[iPointerID].m_clicks = 0; + } + else + m_TouchStates[iPointerID].m_clicks = 1; + } + else + m_TouchStates[iPointerID].m_clicks = 0; + if (m_CameraController) m_CameraController->TouchUpEvent(iPointerID, xPos, yPos); } //----------------------------------------------------------------------------- -TextureT ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, AssetManager& assetManager, const char* filename, SamplerAddressMode samplerMode) +void ApplicationHelperBase::TouchDoubleClickEvent(int iPointerID) +//----------------------------------------------------------------------------- +{} + +//----------------------------------------------------------------------------- +Texture ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, const char* filename, SamplerAddressMode samplerMode) //----------------------------------------------------------------------------- { - SamplerT sampler; + Sampler sampler; switch (samplerMode) { case SamplerAddressMode::Repeat: sampler = m_SamplerRepeat.Copy(); @@ -511,16 +611,16 @@ TextureT ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, AssetMana assert(0 && "Invalid sampler"); break; } - return LoadKTXTexture(vulkan, assetManager, filename, sampler); + return LoadKTXTexture(vulkan, filename, sampler); } //----------------------------------------------------------------------------- -TextureT ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, AssetManager& assetManager, const char* filename, Sampler& sampler) +Texture ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, const char* filename, SamplerBase& sampler) //----------------------------------------------------------------------------- { auto* pTextureManagerVulkan = apiCast(m_TextureManager.get()); - auto & samplerVulkan = static_cast&>(sampler); - return pTextureManagerVulkan->GetLoader()->LoadKtx(*vulkan, assetManager, filename, std::move(samplerVulkan)); + auto & samplerVulkan = apiCast(sampler); + return pTextureManagerVulkan->GetLoader()->LoadKtx(*vulkan, *m_AssetManager, filename, std::move(samplerVulkan)); } //----------------------------------------------------------------------------- @@ -529,11 +629,62 @@ TextureT ApplicationHelperBase::LoadKTXTexture(Vulkan* vulkan, AssetMana // positions, normals and threadColors. Vertices are held in TRIANGLE_LIST format. // // Vertex layout corresponds to ApplicationHelperBase::vertex_layout structure. -bool ApplicationHelperBase::LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject) +bool ApplicationHelperBase::LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject) { const auto meshObjects = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, filename); if (meshObjects.empty()) return false; - return MeshHelper::CreateMesh(GetVulkan()->GetMemoryManager(), meshObjects[0].CopyFlattened()/*remove indices*/, binding, { &MeshHelper::vertex_layout::sFormat, 1 }, meshObject); + return MeshHelper::CreateMesh(GetVulkan()->GetMemoryManager(), meshObjects[0].CopyFlattened()/*remove indices*/, binding, { &MeshHelper::vertex_layout::sFormat, 1 }, meshObject); +} + +//----------------------------------------------------------------------------- +std::unique_ptr> ApplicationHelperBase::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, const std::map& UniformsLookup, const std::map specializationConstants, const RenderPass& renderPass ) +//----------------------------------------------------------------------------- +{ + auto* const pVulkan = GetVulkan(); + + LOGI( "Creating %s mesh...", pShaderName ); + + const auto* pShader = m_ShaderManager->GetShader( pShaderName ); + assert( pShader ); + if (!pShader) + { + LOGE( "Error, %s shader is unknown (not loaded?)", pShaderName ); + return nullptr; + } + + Mesh mesh; + if (!MeshHelper::CreateMesh( pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pShader->m_shaderDescription->m_vertexFormats, &mesh )) + { + LOGE( "Error creating Fullscreen Mesh (for %s)", pShaderName ); + return nullptr; + } + + auto shaderMaterial = m_MaterialManager->CreateMaterial( *pShader, NUM_VULKAN_BUFFERS, + [&ColorAttachmentsLookup]( const std::string& texName, MaterialManagerBase::tPerFrameTexInfo& texInfo ) { + texInfo = {ColorAttachmentsLookup.find( texName )->second}; + }, + [&UniformsLookup]( const std::string& bufferName, PerFrameBufferBase& buffers ) { + buffers = {UniformsLookup.find( bufferName )->second}; + }, + [&ImageAttachmentsLookup]( const std::string& imageName, ImageInfoBase& image ) { + auto* imageT = static_cast(&image); + imageT->operator=({ImageAttachmentsLookup.find(imageName)->second}); + }, + nullptr, + [&specializationConstants]( const std::string& constantName ) -> const VertexElementData { + return {specializationConstants.find( constantName )->second}; + }); + + static const char* passName = "Fullscreen"; + + auto drawable = std::make_unique( *pVulkan, std::move( shaderMaterial ) ); + if (!drawable->Init( renderPass, {}, passName, std::move( mesh ) )) + { + LOGE( "Error creating Blit Drawable" ); + return nullptr; + } + + return std::move( drawable ); } diff --git a/framework/code/main/applicationHelperBase.hpp b/framework/code/main/applicationHelperBase.hpp index 7ee1e96..ef6cf7f 100644 --- a/framework/code/main/applicationHelperBase.hpp +++ b/framework/code/main/applicationHelperBase.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,25 +9,42 @@ #include "vulkan/vulkan.hpp" #include "frameworkApplicationBase.hpp" -#include "texture/vulkan/texture.hpp" -#include "vulkan/renderTarget.hpp" #include "camera/camera.hpp" +#include "vulkan/renderPass.hpp" +#include "material/vulkan/materialManager.hpp" +#include "memory/vulkan/uniform.hpp" #include "mesh/mesh.hpp" +#include "texture/vulkan/texture.hpp" +#include "vulkan/renderContext.hpp" +#include "vulkan/renderTarget.hpp" class CameraControllerBase; -class Computable; -class Drawable; -class Traceable; -class ShaderManager; +class CommandListBase; +class ComputableBase; class SemaphoreWait; -class TextureManager; +class ShaderManagerBase; +class TextureManagerBase; class TimerPoolBase; +class Traceable; +class VertexElementData; class VertexFormat; -template class CommandListT; +template class Buffer; +template class CommandList; +template class Computable; +template class Drawable; +template class DrawableLoader; +template class DrawIndirectBuffer; template class GuiImguiGfx; -template class MaterialManagerT; +template struct ImageInfo; +template class Material; +template class MaterialManager; +template class MaterialPass; template class Mesh; -template class TextureManagerT; +template struct PerFrameBuffer; +template class RenderPass; +template class RenderTarget; +template class ShaderManager; +template class TextureManager; template struct Uniform; template struct UniformT; template struct UniformArray; @@ -36,14 +53,20 @@ template struct UniformArra class TouchStatus { public: - TouchStatus() {m_isDown = false; m_xPos = 0.0f; m_yPos = 0.0f; m_xDownPos = 0.0f; m_yDownPos = 0.0f; }; + TouchStatus() = default; ~TouchStatus() { }; + TouchStatus( const TouchStatus& ) = delete; + TouchStatus& operator=( const TouchStatus& ) = delete; + TouchStatus( TouchStatus&& ) = default; + TouchStatus& operator=( TouchStatus&& ) = default; - bool m_isDown; - float m_xPos; - float m_yPos; - float m_xDownPos; - float m_yDownPos; + bool m_isDown = false; + uint8_t m_clicks = 0; // 1 if we just did a quick 'click', 2 if we did a quick click followed by a quick release + float m_xPos = 0.0f; + float m_yPos = 0.0f; + float m_xDownPos = 0.0f; + float m_yDownPos = 0.0f; + uint32_t m_lastDownChangeTimeMs = 0;// Timestamp (in ms) of last 'down' or 'up' }; /// Helper class that applications can be derived from. @@ -56,8 +79,30 @@ class ApplicationHelperBase : public FrameworkApplicationBase // This class is for Vulkan applications using tGfxApi = Vulkan; - using MeshObject = Mesh; + using CommandList = CommandList; + using CommandBufferT = CommandList; + using CommandBuffer = CommandBufferT; + using Computable = Computable; + using Drawable = Drawable; + using DrawableLoader = DrawableLoader; + using DrawIndirectBuffer = DrawIndirectBuffer; + using ImageInfo = ImageInfo; + using IndexBufferObject = IndexBuffer; + using Material = Material; + using MaterialPass = MaterialPass; + using MaterialManager = MaterialManager; + using Mesh = Mesh; + using PerFrameTexInfo = MaterialManager::tPerFrameTexInfo; + using PerFrameBuffer = PerFrameBuffer; + using RenderContext = RenderContext; + using RenderPass = RenderPass; + using RenderTarget = RenderTarget; + using ShaderManager = ShaderManager; using Uniform = Uniform; + using VertexBufferObject = VertexBuffer; + using BufferVulkan = Buffer; + using GuiImguiGfx = GuiImguiGfx; + template using UniformArray = UniformArray; template using UniformT = UniformT; template using UniformArrayT = UniformArrayT; @@ -67,6 +112,7 @@ class ApplicationHelperBase : public FrameworkApplicationBase virtual bool InitCamera(); bool Initialize(uintptr_t hWnd, uintptr_t hInstance) override; ///< Override FrameworkApplicationBase::Initialize + bool ReInitialize(uintptr_t hWnd, uintptr_t hInstance) override; ///< Override FrameworkApplicationBase::ReInitialize void Destroy() override; ///< Override FrameworkApplicationBase::Destroy bool SetWindowSize(uint32_t width, uint32_t height) override; ///< Override FrameworkApplicationBase::SetWindowSize. Passes new window size in to camera. @@ -77,7 +123,7 @@ class ApplicationHelperBase : public FrameworkApplicationBase /// May be called during initialize (from Vulkan.cpp, during inital Vulkan setup). /// @param configuration (if untouched, Vulkan will be setup using defaults). - virtual void PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration&); + virtual void PreInitializeSetVulkanConfiguration( tGfxApi::AppConfiguration&); void KeyDownEvent(uint32_t key) override; ///< Override FrameworkApplicationBase::KeyDownEvent void KeyRepeatEvent(uint32_t key) override; ///< Override FrameworkApplicationBase::KeyRepeatEvent @@ -85,21 +131,40 @@ class ApplicationHelperBase : public FrameworkApplicationBase void TouchDownEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchDownEvent void TouchMoveEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchMoveEvent void TouchUpEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchUpEvent + virtual void TouchDoubleClickEvent(int iPointerID); ///< Generated by ApplicationHelperBase (in ApplicationHelperBase::TouchUpEvent) /// @brief Add the commands to draw this drawable to the given commandbuffers. /// @param drawable the Drawable object we want to add commands for (may contain multiple DrawablePass) /// @param cmdBuffers pointer to array, assumed to be sized [numVulkanBuffers][numRenderPasses] /// @param numRenderPasses number of render passes in the array (the DrawablePass has the pass index that is written to) - /// @param numVulkanBuffers number of buffers in the array (number of frames to build the command buffers for) - void AddDrawableToCmdBuffers(const Drawable& drawable, CommandListT* cmdBuffers, uint32_t numRenderPasses, uint32_t numVulkanBuffers, uint32_t startDescriptorSetIdx = 0) const; + /// @param numFrameBuffers number of buffers in the array (number of frames to build the command buffers for) + void AddDrawableToCmdBuffers(const Drawable& drawable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t numFrameBuffers, uint32_t startDescriptorSetIdx = 0) const; + void AddDrawableToCmdBuffer( const Drawable& drawable, CommandList& cmdBuffer, uint32_t startDescriptorSetIdx = 0 ) const + { + AddDrawableToCmdBuffers( drawable, &cmdBuffer, 1, 1, startDescriptorSetIdx ); + } + + /// @brief Add the commands to draw this drawable to the given commandbuffers. + /// @param drawable the Drawable object we want to add commands for (may contain multiple DrawablePass) + /// @param cmdBuffers pointer to array, assumed to be sized [numVulkanBuffers][numRenderPasses] + /// @param numRenderPasses number of render passes in the array (the DrawablePass has the pass index that is written to) + /// @param whichVulkanBuffer which vulkan buffer to add to (not all vulkan buffers) + /// @param dummy to distinguish other version of AddDrawableToCmdBuffers(..., uint32_t startDescriptorSetIdx = 0) + void AddDrawableToCmdBuffers(const Drawable& drawable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t whichVulkanBuffer, uint32_t startDescriptorSetIdx, float dummy) const; + /// @brief Add the commands to dispatch this computable to a command buffer. /// Potentially adds multiple vkCmdDispatch (will make one per 'pass' in the Computable) and inserts appropiate memory barriers between passes. /// @param computable /// @param cmdBuffers pointer to array of commandbuffers we want to fill, assumed to be sized [numRenderPasses] /// @param numRenderPasses number of cmdBuffers to fill /// @param startDescriptorSetIdx index of the first descriptor set to add - void AddComputableToCmdBuffer(const Computable& computable, CommandListT* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; - void AddComputableToCmdBuffer( const Computable& computable, CommandListT& cmdBuffer, TimerPoolBase* timerPool = nullptr ) const + void AddComputableToCmdBuffer(const Computable& computable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; + void AddComputableToCmdBuffer(const Computable& computable, CommandList& cmdBuffer, TimerPoolBase* timerPool = nullptr) const + { + AddComputableToCmdBuffer( computable, &cmdBuffer, 1, 0, timerPool ); + } + void AddComputableToCmdBuffer(const ComputableBase& computable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; + void AddComputableToCmdBuffer(const ComputableBase& computable, CommandList& cmdBuffer, TimerPoolBase* timerPool = nullptr ) const { AddComputableToCmdBuffer( computable, &cmdBuffer, 1, 0, timerPool ); } @@ -108,8 +173,13 @@ class ApplicationHelperBase : public FrameworkApplicationBase /// @param computable /// @param cmdBuffers pointer to array of commandbuffers we want to add the barrier to, assumed to be sized [numRenderPasses] /// @param numRenderPasses number of cmdBuffers to fill - void AddComputableOutputBarrierToCmdBuffer(const Computable& computable, CommandListT* cmdBuffers, uint32_t numRenderPasses) const; - void AddComputableOutputBarrierToCmdBuffer(const Computable& computable, CommandListT& cmdBuffer) const + void AddComputableOutputBarrierToCmdBuffer(const Computable& computable, CommandList* cmdBuffers, uint32_t numRenderPasses) const; + void AddComputableOutputBarrierToCmdBuffer(const Computable& computable, CommandList& cmdBuffer) const + { + AddComputableOutputBarrierToCmdBuffer(computable, &cmdBuffer, 1); + } + void AddComputableOutputBarrierToCmdBuffer(const ComputableBase& computable, CommandList* cmdBuffers, uint32_t numRenderPasses) const; + void AddComputableOutputBarrierToCmdBuffer(const ComputableBase& computable, CommandList& cmdBuffer) const { AddComputableOutputBarrierToCmdBuffer(computable, &cmdBuffer, 1); } @@ -120,8 +190,8 @@ class ApplicationHelperBase : public FrameworkApplicationBase /// @param cmdBuffers pointer to array of commandbuffers we want to fill, assumed to be sized [numRenderPasses] /// @param numRenderPasses number of cmdBuffers to fill /// @param startDescriptorSetIdx index of the first descriptor set to add (use when Computable - void AddTraceableToCmdBuffer(const Traceable& traceable, CommandListT* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; - void AddTraceableToCmdBuffer(const Traceable& traceable, CommandListT& cmdBuffer, TimerPoolBase* timerPool = nullptr ) const + void AddTraceableToCmdBuffer(const Traceable& traceable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; + void AddTraceableToCmdBuffer(const Traceable& traceable, CommandList& cmdBuffer, TimerPoolBase* timerPool = nullptr ) const { AddTraceableToCmdBuffer(traceable, &cmdBuffer, 1, 0, timerPool); } @@ -141,18 +211,23 @@ class ApplicationHelperBase : public FrameworkApplicationBase bool PresentQueue(const SemaphoreWait& WaitSemaphore, uint32_t SwapchainPresentIndx); /// Texture loader helper to replace the (old) texture loading code - TextureT LoadKTXTexture(tGfxApi*, AssetManager&, const char* filename, SamplerAddressMode = SamplerAddressMode::ClampEdge); - TextureT LoadKTXTexture(tGfxApi*, AssetManager&, const char* filename, Sampler&); + Texture LoadKTXTexture(tGfxApi*, const char* filename, SamplerAddressMode = SamplerAddressMode::ClampEdge); + Texture LoadKTXTexture(tGfxApi*, const char* filename, SamplerBase&); /// Mesh loader helper to load the first shape in a .gltf file (no materials). /// Returned MeshObject does not have an index buffer (3 verts per triangle) and data is in the MeshHelper::vertex_layout format. /// @returns true on success - bool LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject); + bool LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject); + + /// @brief Helper to create a fullscreen (quad) drawable + std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, const std::map& UniformsLookup, const std::map specializationConstants, const RenderPass& renderPass ); /// @brief Get pointer to the framework Vulkan class Vulkan* GetVulkan() const { return static_cast(m_gfxBase.get()); } + /// @brief Get pointer to the framework graphicsApi Vulkan class (will get the Dx12 class when called from the Dx12 ApplicationHelperBase). Can use FrameworkApplicationBase::GetGraphicsApiBase if you want the base class only! + Vulkan* GetGfxApi() const { return static_cast(m_gfxBase.get()); } /// @brief Get pointer to the framework gui (Vulkan) class - GuiImguiGfx* GetGui() const { return (GuiImguiGfx*)(m_Gui.get()); } + auto* GetGui() const { return (GuiImguiGfx*)(m_Gui.get()); } protected: // Scene Camera @@ -164,19 +239,37 @@ class ApplicationHelperBase : public FrameworkApplicationBase std::vector m_TouchStates; // Shaders - std::unique_ptr m_ShaderManager; + std::unique_ptr m_ShaderManager; // Materials - std::unique_ptr> m_MaterialManager; + std::unique_ptr m_MaterialManager; // Output backbuffer (framebuffer) helper - CRenderTargetArray m_BackbufferRenderTarget; + std::array m_BackbufferRenderTarget; + std::array m_BackbufferRenderContext; // Texture manager - std::unique_ptr m_TextureManager; + std::unique_ptr m_TextureManager; // Default samplers - SamplerT m_SamplerRepeat; - SamplerT m_SamplerEdgeClamp; - SamplerT m_SamplerMirroredRepeat; + Sampler m_SamplerRepeat; + Sampler m_SamplerEdgeClamp; + Sampler m_SamplerMirroredRepeat; }; + +// +// Additional helpers for app_config.txt parsing +// + +template <> +inline void ReadFromText( VkSampleCountFlagBits* val, const char* const text ) +{ + ReadFromText((uint32_t*)(val), text); +} + +template <> +inline void ReadFromText( Msaa* val, const char* const text ) +{ + ReadFromText((uint32_t*)(val), text); +} + diff --git a/framework/code/main/applicationHelperBaseDx12.cpp b/framework/code/main/applicationHelperBaseDx12.cpp new file mode 100644 index 0000000..8a05283 --- /dev/null +++ b/framework/code/main/applicationHelperBaseDx12.cpp @@ -0,0 +1,184 @@ +//============================================================================= +// +// +// Copyright (c) 2021 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "applicationHelperBaseDx12.hpp" +#include "camera/cameraController.hpp" +#include "camera/cameraControllerTouch.hpp" +#include "material/vertexFormat.hpp" +#include "material/dx12/descriptorSetLayout.hpp" +#include "material/dx12/drawableDx12.hpp" +#include "material/dx12/material.hpp" +#include "material/dx12/materialManager.hpp" +#include "material/dx12/shader.hpp" +#include "material/dx12/shaderModule.hpp" +#include "material/shaderManagerT.hpp" +#include "mesh/meshHelper.hpp" +#include "memory/dx12/indexBufferObject.hpp" +#include "memory/dx12/vertexBufferObject.hpp" +#include "texture/dx12/textureManager.hpp" +#include "dx12/dx12.hpp" +#include "dx12/commandList.hpp" + + +/////////////////////////////////////////////////////////////////////////////// + +// Format of vertex_layout +VertexFormat ApplicationHelperBase::vertex_layout::sFormat{ + sizeof(ApplicationHelperBase::vertex_layout), + VertexFormat::eInputRate::Vertex, + { + { offsetof(ApplicationHelperBase::vertex_layout, pos), VertexElementType::Vec3 }, //float pos[3]; // SHADER_ATTRIB_LOC_POSITION + { offsetof(ApplicationHelperBase::vertex_layout, normal), VertexElementType::Vec3 }, //float normal[3]; // SHADER_ATTRIB_LOC_NORMAL + { offsetof(ApplicationHelperBase::vertex_layout, uv), VertexElementType::Vec2 }, //float uv[2]; // SHADER_ATTRIB_LOC_TEXCOORD0 + { offsetof(ApplicationHelperBase::vertex_layout, color), VertexElementType::Vec4 }, //float color[4]; // SHADER_ATTRIB_LOC_COLOR + { offsetof(ApplicationHelperBase::vertex_layout, tangent), VertexElementType::Vec3 }, //float tangent[3]; // SHADER_ATTRIB_LOC_TANGENT + //{ offsetof(ApplicationHelperBase::vertex_layout, bitangent), VertexElementType::Vec3 }, // float binormal[3]; // SHADER_ATTRIB_LOC_BITANGENT + }, + {"Position", "Normal", "UV", "Color", "Tangent" /*,"Bitangent"*/ } +}; + +//----------------------------------------------------------------------------- +ApplicationHelperBase::ApplicationHelperBase() +//----------------------------------------------------------------------------- + : FrameworkApplicationBase() +{ + m_gfxBase = std::make_unique(); +} + +//----------------------------------------------------------------------------- +ApplicationHelperBase::~ApplicationHelperBase() +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::PreInitializeSetVulkanConfiguration( ApplicationHelperBase::AppConfiguration& ) +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +bool ApplicationHelperBase::Initialize(uintptr_t hWnd, uintptr_t hInstance) +//----------------------------------------------------------------------------- +{ + Dx12* pDx12 = GetDx12(); + if (!pDx12->Init(hWnd, hInstance)) + { + LOGE("Unable to initialize DirectX12!!"); + return false; + } + + auto textureManager = std::make_unique(*pDx12, *m_AssetManager); + if (!textureManager->Initialize()) + return false; + m_TextureManager = std::move(textureManager); + + m_ShaderManager = std::make_unique(*pDx12); + + m_MaterialManager = std::make_unique(*pDx12); + + return true; +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::Destroy() +//----------------------------------------------------------------------------- +{ + m_MaterialManager.reset(); + + m_ShaderManager.reset(); + + m_TextureManager.reset(); + + FrameworkApplicationBase::Destroy(); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::KeyDownEvent(uint32_t key) +//----------------------------------------------------------------------------- +{ + if (m_CameraController) + m_CameraController->KeyDownEvent(key); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::KeyRepeatEvent(uint32_t key) +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::KeyUpEvent(uint32_t key) +//----------------------------------------------------------------------------- +{ + if (m_CameraController) + m_CameraController->KeyUpEvent(key); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::TouchDownEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + if (m_CameraController) + m_CameraController->TouchDownEvent(iPointerID, xPos, yPos); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::TouchMoveEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + if (m_CameraController) + m_CameraController->TouchMoveEvent(iPointerID, xPos, yPos); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::TouchUpEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + if (m_CameraController) + m_CameraController->TouchUpEvent(iPointerID, xPos, yPos); +} + +//----------------------------------------------------------------------------- +// Loads a .gltf file and builds a single Vertex Buffer containing relevant vertex. +// DOES not use (or populate) the index buffer although it does respect the gltf index buffer when loading the mesh +// positions, normals and threadColors. Vertices are held in TRIANGLE_LIST format. +// +// Vertex layout corresponds to ApplicationHelperBase::vertex_layout structure. +bool ApplicationHelperBase::LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject) +{ + const auto meshObjects = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, filename); + if (meshObjects.empty()) + return false; + + return MeshHelper::CreateMesh(GetDx12()->GetMemoryManager(), meshObjects[0].CopyFlattened()/*remove indices*/, binding, { &MeshHelper::vertex_layout::sFormat, 1 }, meshObject); +} + +//----------------------------------------------------------------------------- +void ApplicationHelperBase::AddDrawableToCmdBuffers(const Drawable& drawable, CommandList* cmdLists, uint32_t numRenderPasses, uint32_t numFrameBuffers, uint32_t startDescriptorSetIdx) const +//----------------------------------------------------------------------------- +{ + const auto& drawablePasses = drawable.GetDrawablePasses(); + for (const auto& drawablePass : drawablePasses) + { + const auto passIdx = drawablePass.mPassIdx; + assert(passIdx < numRenderPasses); + + for (uint32_t bufferIdx = 0; bufferIdx < numFrameBuffers; ++bufferIdx) + { + auto& cmdList = cmdLists[bufferIdx * numRenderPasses + passIdx]; + + // Add commands to bind the pipeline, buffers etc and issue the draw. + drawable.DrawPass(cmdList, drawablePass, /*drawablePass.mDescriptorSet.empty() ? 0 : (startDescriptorSetIdx + bufferIdx) % drawablePass.mDescriptorSet.size()*/ startDescriptorSetIdx + bufferIdx ); + + ++cmdList.m_NumDrawCalls; + cmdList.m_NumTriangles += drawablePass.mNumVertices / 3; + } + } +} + diff --git a/framework/code/main/applicationHelperBaseDx12.hpp b/framework/code/main/applicationHelperBaseDx12.hpp new file mode 100644 index 0000000..92bbd8d --- /dev/null +++ b/framework/code/main/applicationHelperBaseDx12.hpp @@ -0,0 +1,162 @@ +//============================================================================= +// +// +// Copyright (c) 2021 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "dx12/dx12.hpp" +#include "frameworkApplicationBase.hpp" +#include "camera/camera.hpp" +#include "texture/dx12/sampler.hpp" +#include "texture/dx12/texture.hpp" + +class CameraController; +class CameraControllerTouch; +class ComputableBase; +class MaterialManagerBase; +class ShaderManagerBase; +class TextureManagerBase; +class VertexFormat; +template class CommandList; +template class Computable; +template class Drawable; +template class DrawableLoader; +template class DrawIndirectBuffer; +template class GuiImguiGfx; +template class Material; +template class MaterialManager; +template class Mesh; +template struct PerFrameBuffer; +template class RenderPass; +template class RenderTarget; +template class ShaderManager; +template class TextureManager; +template struct Uniform; +template struct UniformT; +template struct UniformArray; +template struct UniformArrayT; + +using TimerPoolBase = void; ///TODO: implement Dx12 timers + +/// Helper class that applications can be derived from. +/// Provides Camera and Drawable functionality to reduce code duplication of more 'boilderplate' application features. +class ApplicationHelperBase : public FrameworkApplicationBase +{ +protected: + ApplicationHelperBase(); + ~ApplicationHelperBase() override; + + // This class is for Dx12 applications + using tGfxApi = Dx12; + using CommandList = CommandList; + using CommandBuffer = CommandList; + using Computable = Computable; + using Drawable = Drawable; + using DrawableLoader = DrawableLoader; + using DrawIndirectBuffer = DrawIndirectBuffer; + using Material = Material; + using MaterialManager = MaterialManager; + using Mesh = Mesh; + using PerFrameBuffer = PerFrameBuffer; + using RenderPass = RenderPass; + using RenderTarget = RenderTarget; + using ShaderManager = ShaderManager; + using Uniform = Uniform; + template using UniformArray = UniformArray; + template using UniformT = UniformT; + template using UniformArrayT = UniformArrayT; + using GuiImguiGfx = GuiImguiGfx; + + struct AppConfiguration + { + tGfxApi::AppConfiguration gfx; + }; + + virtual void PreInitializeSetVulkanConfiguration( AppConfiguration& ); + + bool Initialize(uintptr_t hWnd, uintptr_t hInstance) override; ///< Override FrameworkApplicationBase::Initialize + void Destroy() override; ///< Override FrameworkApplicationBase::Destroy + + void KeyDownEvent(uint32_t key) override; ///< Override FrameworkApplicationBase::KeyDownEvent + void KeyRepeatEvent(uint32_t key) override; ///< Override FrameworkApplicationBase::KeyRepeatEvent + void KeyUpEvent(uint32_t key) override; ///< Override FrameworkApplicationBase::KeyUpEvent + void TouchDownEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchDownEvent + void TouchMoveEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchMoveEvent + void TouchUpEvent(int iPointerID, float xPos, float yPos) override; ///< Override FrameworkApplicationBase::TouchUpEvent + + /// @brief Get pointer to the framework Dx12 class + Dx12* GetDx12() const { return static_cast(m_gfxBase.get()); } + /// @brief Get pointer to the framework graphicsApi Dx12 class (will get the Vulkan class when called from the Vulkan ApplicationHelperBase). Can use FrameworkApplicationBase::GetGraphicsApiBase if you want the base class only! + Dx12* GetGfxApi() const { return static_cast(m_gfxBase.get()); } + /// @brief Get pointer to the framework gui (Dx12) class + GuiImguiGfx* GetGui() const { return (GuiImguiGfx*)(m_Gui.get()); } + + // These MUST match the order of the attrib locations and sFormat must reflect the layout of this struct too! + struct vertex_layout + { + float pos[3]; // SHADER_ATTRIB_LOC_POSITION + float normal[3]; // SHADER_ATTRIB_LOC_NORMAL + float uv[2]; // SHADER_ATTRIB_LOC_TEXCOORD0 + float color[4]; // SHADER_ATTRIB_LOC_COLOR + float tangent[3]; // SHADER_ATTRIB_LOC_TANGENT + // float binormal[3]; // SHADER_ATTRIB_LOC_BITANGENT + static VertexFormat sFormat; + }; + + /// Mesh loader helper to load the first shape in a .gltf file (no materials). + /// Returned MeshObject does not have an index buffer (3 verts per triangle) and data is in the ApplicationHelperBase::vertex_layout format. + /// @returns true on success + bool LoadGLTF(const std::string& filename, uint32_t binding, Mesh* meshObject); + + /// @brief Add the commands to draw this drawable to the given commandbuffers. + /// @param drawable the Drawable object we want to add commands for (may contain multiple DrawablePass) + /// @param cmdBuffers pointer to array, assumed to be sized [numVulkanBuffers][numRenderPasses] + /// @param numRenderPasses number of render passes in the array (the DrawablePass has the pass index that is written to) + /// @param numVulkanBuffers number of buffers in the array (number of frames to build the command buffers for) + void AddDrawableToCmdBuffers(const Drawable& drawable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t numFrameBuffers, uint32_t startDescriptorSetIdx = 0) const; + void AddDrawableToCmdBuffer(const Drawable& drawable, CommandList& cmdBuffer, uint32_t startDescriptorSetIdx = 0) const + { + AddDrawableToCmdBuffers(drawable, &cmdBuffer, 1, 1, startDescriptorSetIdx); + } + /// @brief Add the commands to dispatch this computable to a command buffer. + /// Potentially adds multiple vkCmdDispatch (will make one per 'pass' in the Computable) and inserts appropiate memory barriers between passes. + /// @param computable + /// @param cmdBuffers pointer to array of commandbuffers we want to fill, assumed to be sized [numRenderPasses] + /// @param numRenderPasses number of cmdBuffers to fill + /// @param startDescriptorSetIdx index of the first descriptor set to add + void AddComputableToCmdBuffer(const Computable& computable, CommandList* cmdBuffers, uint32_t numRenderPasses, uint32_t startDescriptorSetIdx, TimerPoolBase* timerPool = nullptr ) const; + void AddComputableToCmdBuffer( const Computable& computable, CommandList& cmdBuffer, TimerPoolBase* timerPool = nullptr ) const + { + AddComputableToCmdBuffer( computable, &cmdBuffer, 1, 0, timerPool ); + } + + +protected: + // Scene Camera + Camera m_Camera; + + // Camera Controller +#if defined(OS_ANDROID) ///TODO: make this an option + std::unique_ptr m_CameraController; +#else + std::unique_ptr m_CameraController; +#endif + + // Shaders + std::unique_ptr m_ShaderManager; + + // Materials + std::unique_ptr m_MaterialManager; + + // Texture manager + std::unique_ptr> m_TextureManager; + + // Ready to use samplers + // Default samplers + Sampler m_SamplerRepeat; + Sampler m_SamplerEdgeClamp; + Sampler m_SamplerMirroredRepeat; +}; diff --git a/framework/code/main/frameworkApplicationBase.cpp b/framework/code/main/frameworkApplicationBase.cpp index ccaf786..656ee27 100644 --- a/framework/code/main/frameworkApplicationBase.cpp +++ b/framework/code/main/frameworkApplicationBase.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,14 +13,9 @@ #include "system/os_common.h" // Bring in the timestamp (and assign to a variable) -// Temporary fix for Android not building the timestamp, upstream cmake update will fix this in a future update -#if defined(_WIN32) -#include "../../project/buildtimestamp.h" -#else -#define BUILD_TIMESTAMP "UNDEFINED" -#endif - -const char* const FrameworkApplicationBase::sm_BuildTimestamp = BUILD_TIMESTAMP; +//#include "../../project/buildtimestamp.h" +//const char* const FrameworkApplicationBase::sm_BuildTimestamp = BUILD_TIMESTAMP; +const char* const FrameworkApplicationBase::sm_BuildTimestamp = ""; //######################################################### @@ -29,20 +24,20 @@ const char* const FrameworkApplicationBase::sm_BuildTimestamp = BUILD_TIMESTAMP; // ************************************ // General Settings // ************************************ -VAR(uint32_t, gSurfaceWidth, 1920, kVariableNonpersistent); -VAR(uint32_t, gSurfaceHeight, 1080, kVariableNonpersistent); +VAR(uint32_t, gSurfaceWidth, 1280, kVariableNonpersistent); +VAR(uint32_t, gSurfaceHeight, 720, kVariableNonpersistent); -VAR(uint32_t, gRenderWidth, 1920, kVariableNonpersistent); -VAR(uint32_t, gRenderHeight, 1080, kVariableNonpersistent); +VAR(uint32_t, gRenderWidth, 1280, kVariableNonpersistent); +VAR(uint32_t, gRenderHeight, 720, kVariableNonpersistent); -VAR(uint32_t, gReflectMapWidth, 1920, kVariableNonpersistent); -VAR(uint32_t, gReflectMapHeight, 1080, kVariableNonpersistent); +VAR(uint32_t, gReflectMapWidth, 1280/2, kVariableNonpersistent); +VAR(uint32_t, gReflectMapHeight, 720/2, kVariableNonpersistent); VAR(uint32_t, gShadowMapWidth, 1024, kVariableNonpersistent); VAR(uint32_t, gShadowMapHeight, 1024, kVariableNonpersistent); -VAR(uint32_t, gHudRenderWidth, 1920, kVariableNonpersistent); -VAR(uint32_t, gHudRenderHeight, 1080, kVariableNonpersistent); +VAR(uint32_t, gHudRenderWidth, 1280, kVariableNonpersistent); +VAR(uint32_t, gHudRenderHeight, 720, kVariableNonpersistent); VAR(float, gFixedFrameRate, 0.0f, kVariableNonpersistent); @@ -77,7 +72,7 @@ FrameworkApplicationBase::FrameworkApplicationBase() m_WindowWidth = 0; m_WindowHeight = 0; - LOGI("Application build time: %s", sm_BuildTimestamp); + //LOGI("Application build time: %s", sm_BuildTimestamp); } //----------------------------------------------------------------------------- @@ -111,7 +106,7 @@ void FrameworkApplicationBase::SetConfigFilename(const std::string& filename) bool FrameworkApplicationBase::LoadConfigFile() //----------------------------------------------------------------------------- { - const std::string ConfigFileFallbackPath = std::string("Media\\") + m_ConfigFilename; + const std::string ConfigFileFallbackPath = std::string("build\\Media\\") + m_ConfigFilename; LOGI("Loading Configuration File: %s", m_ConfigFilename.c_str()); std::string configFile; if (!m_AssetManager->LoadFileIntoMemory(m_ConfigFilename, configFile ) && diff --git a/framework/code/main/frameworkApplicationBase.hpp b/framework/code/main/frameworkApplicationBase.hpp index c7a3db9..710c882 100644 --- a/framework/code/main/frameworkApplicationBase.hpp +++ b/framework/code/main/frameworkApplicationBase.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -96,7 +96,10 @@ class FrameworkApplicationBase virtual void Destroy(); /// Called by framework whenever the screen (swap chain) size has changed. This is the WINDOW size (not necissarily the buffer size etc), mouse/touch input is expected to be in this coordinate space) - virtual bool SetWindowSize(uint32_t width, uint32_t height); + virtual bool SetWindowSize( uint32_t width, uint32_t height ); + + /// Return the WINDOW size (not necissarily the buffer size etc), mouse/touch input is expected to be in this coordinate space) + std::pair GetWindowSize() const { return {m_WindowWidth, m_WindowHeight}; }; /// Application Main thread 'render' loop (eg ALooper_pollAll loop on Android) /// Called every frame. @@ -125,9 +128,9 @@ class FrameworkApplicationBase bool Render(); // Accessors - GraphicsApiBase*GetGraphicsApi() const { return m_gfxBase.get(); } - Gui* GetGui() const { return m_Gui.get(); } - uint32_t GetFrameCount() const { return m_FrameCount; } + GraphicsApiBase*GetGraphicsApiBase() const { return m_gfxBase.get(); } + Gui* GetGui() const { return m_Gui.get(); } + uint32_t GetFrameCount() const { return m_FrameCount; } public: // Frame timings diff --git a/framework/code/main/framework_static_empty.cpp b/framework/code/main/framework_static_empty.cpp new file mode 100644 index 0000000..d114a92 --- /dev/null +++ b/framework/code/main/framework_static_empty.cpp @@ -0,0 +1,2 @@ +// Empty file used by Android build when creating static libraries + diff --git a/framework/code/main/linux/linuxMain.cpp b/framework/code/main/linux/linuxMain.cpp new file mode 100644 index 0000000..778c678 --- /dev/null +++ b/framework/code/main/linux/linuxMain.cpp @@ -0,0 +1,155 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +/// @file unixMain.cpp +/// @brief Unix 'main' entry point and event handler. +/// +/// Implements Unix specific wrapping of frameworkApplicationBase. +// There should be the minimum (possible) amount of code in here. + +#include +#include +#include +#include + +#include "system/os_common.h" + +#include "main/frameworkApplicationBase.hpp" +#include "main/applicationEntrypoint.hpp" +#include "gui/gui.hpp" + +#include + + + +// Based on config file loading, there is a window between the application being created, +// and it is intialized. A WM_PAINT can come in at this point and will crash because +// nothing has been initialized. +bool gAppInitialized = false; + +// Flag to indicate if the gui is currently 'eating' all the mouse input events (if false the events are passed on to the application) +static bool gGuiMouseActive = false; + +// If the mouse is down and being handled by the application (not the gui) +static bool gAppMouseActive = false; + +//------------------------------------------------------------------------------ +/// Create Window for rendering into +//------------------------------------------------------------------------------ +GLFWwindow* CreateWindow(int width, int height) { + GLFWwindow* window; + + // Initialize the library + if (!glfwInit()) + return nullptr; + + // Tel GLFW to not attach to OpenGL + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + // Create a windowed mode window + window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL); + if (!window) + { + glfwTerminate(); + return nullptr; + } + + /* Make the window's context current */ + glfwMakeContextCurrent(window); + + return window; +} + +//------------------------------------------------------------------------------ +// Destroy the Window +//------------------------------------------------------------------------------ +void DestroyWindow(GLFWwindow* window) +{ + glfwDestroyWindow(window); +} + + + +/// This is the main entry point of the Unix app. +/// initializes Vulkan and loops processing windows events (until the app is closed). +//----------------------------------------------------------------------------- +int main(int argc, const char*const* argv) +//----------------------------------------------------------------------------- +{ + // Check for a media directy (fatal to not have one). + constexpr auto mediaPath = "build/Media"; + if (!std::filesystem::exists(mediaPath) || !std::filesystem::is_directory(mediaPath)) + { + std::string errorMessage = "Cannot find 'build/Media' folder.\n You're likely not running from the correct directory or are missing files from the Media folder (check this sample's README.md for instructions).\n"; + LOGE(errorMessage.c_str()); + return EXIT_FAILURE; + } + + // Create the application + auto* gpApplication = Application_ConstructApplication(); + + // Do a very simple parse of the cmd line... + std::string sConfigFilenameOverride; + if (argc > 1) + gpApplication->SetConfigFilename(argv[1]); + + // Load the config file + // Need this here in order to get the window sizes + gpApplication->LoadConfigFile(); + + if (gSurfaceWidth == 0) + { + LOGI("gSurfaceWidth => %d", gRenderWidth); + gSurfaceWidth = gRenderWidth; + } + if (gSurfaceHeight == 0) + { + LOGI("gSurfaceHeight => %d", gRenderHeight); + gSurfaceHeight = gRenderHeight; + } + + auto* pWindow = CreateWindow(gSurfaceWidth, gSurfaceHeight); + + // Initialize the application class + if (!gpApplication->Initialize((uintptr_t)pWindow, (uintptr_t)0)) { + LOGE("Application initialization failed!!"); + return EXIT_FAILURE; + } + + gAppInitialized = true; + + if (!gpApplication->PostInitialize()) + return EXIT_FAILURE; + + // Loop on signals + bool fDone = false; + while (!fDone && !glfwWindowShouldClose(pWindow)) + { + glfwPollEvents(); + + if (!gpApplication->Render()) + fDone = true; + + OS_SleepMs(1); + } + // Release the application + if (gpApplication) + { + gpApplication->Destroy(); + + delete gpApplication; + gpApplication = NULL; + } + + DestroyWindow(pWindow); + pWindow = nullptr; + + glfwTerminate(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/framework/code/main/windows/winMain.cpp b/framework/code/main/windows/winMain.cpp index 0f1f910..4382417 100644 --- a/framework/code/main/windows/winMain.cpp +++ b/framework/code/main/windows/winMain.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -35,6 +35,8 @@ //============================================================================= // Declare the application global so can access it in WndProc FrameworkApplicationBase* gpApplication = nullptr; +// Define a funcion pointer to the GUI's winproc handler. Use this so the GUI can catch winproc events at the lowest level (eg imgui integration) +LRESULT(*PFN_Gui_WndProcHandler)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) = nullptr/*do nothing by default*/; #define KEY_THIS_FRAME_TIME 250 @@ -49,8 +51,8 @@ static bool gGuiMouseActive = false; // If the mouse is down and being handled by the application (not the gui) static bool gAppMouseActive = false; -// Define a funcion pointer to the GUI's winproc handler. Use this so the GUI can catch winproc events at the lowest level (eg imgui integration) -LRESULT (*PFN_Gui_WndProcHandler)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) = nullptr/*do nothing by default*/; +// From WinMain.cpp +extern LRESULT(*PFN_Gui_WndProcHandler)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); int DefaultCrashReport(int i, char* msg, int* p) { @@ -88,6 +90,7 @@ bool CreateConsoleWindow( bool forceNewConsole ) } _CrtSetReportHook( PFN_CrashReportHook ); + _set_error_mode( _OUT_TO_STDERR ); // Redirect stdout and stderr from our app to the new console window... @@ -142,19 +145,11 @@ LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_PAINT: if (gpApplication && gAppInitialized) { - static bool is_rendering = false; - if (is_rendering) - { - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - - is_rendering = true; if (!gpApplication->Render()) { // Exit requested. DestroyWindow(hWnd); } - is_rendering = false; } break; @@ -316,10 +311,10 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi bool allocatedNewConsoleWindow = CreateConsoleWindow(false); // Check for a media directy (fatal to not have one). - constexpr auto mediaPath = "Media"; + constexpr auto mediaPath = "build/Media"; if (!std::filesystem::exists(mediaPath) || !std::filesystem::is_directory(mediaPath)) { - std::string errorMessage = "Cannot find 'Media' folder.\n If you built this application maybe you didnt run the corresponding 02_PrepareMedia.bat script?\n If you did prepare the media (or are running a pre-built build) you're likely not running from the correct directory.\n"; + std::string errorMessage = "Cannot find 'build/Media' folder.\n You're likely not running from the correct directory or are missing files from the Media folder (check this sample's README.md for instructions).\n"; LOGE(errorMessage.c_str()); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); @@ -407,7 +402,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi // Initialize the application class if (!gpApplication->Initialize((uintptr_t)hWnd, (uintptr_t)hInstance)) { - LOGE("Application initialization failed!!"); + LOGE("\n\n===================================\nApplication initialization failed!!\n===================================\n\n"); return FALSE; } diff --git a/framework/code/material/computable.cpp b/framework/code/material/computable.cpp index b06255e..6a15e45 100644 --- a/framework/code/material/computable.cpp +++ b/framework/code/material/computable.cpp @@ -1,468 +1,35 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "computable.hpp" -#include "shader.hpp" -#include "vulkan/shaderModule.hpp" +#include "materialPass.hpp" #include "shaderDescription.hpp" -#include "vulkan/material.hpp" -#include "vulkan/vulkan.hpp" -#include "vulkan/TextureFuncts.h" -#include "texture/vulkan/texture.hpp" -#include "system/os_common.h" -#include -#include - -ComputablePass::ComputablePass(ComputablePass&& other) noexcept - : mMaterialPass(std::move(other.mMaterialPass)) - , mImageMemoryBarriers(std::move(other.mImageMemoryBarriers)) - , mBufferMemoryBarriers(std::move(other.mBufferMemoryBarriers)) - , mDispatchGroupCount(other.mDispatchGroupCount) - , mNeedsExecutionBarrier( other.mNeedsExecutionBarrier ) -{ - mPipeline = other.mPipeline; - other.mPipeline = VK_NULL_HANDLE; - mPipelineLayout = other.mPipelineLayout; - other.mPipelineLayout = VK_NULL_HANDLE; -} - -ComputablePass::~ComputablePass() -{ - assert(mPipeline == VK_NULL_HANDLE); -} - -Computable::Computable(Vulkan& vulkan, Material&& material) - : mMaterial(std::move(material)) - , mVulkan(vulkan) -{ -} - -Computable::Computable(Computable&& other) noexcept - : mMaterial(std::move(other.mMaterial)) - , mVulkan(other.mVulkan) - , mPasses(std::move(other.mPasses)) - , mImageInputMemoryBarriers(std::move(other.mImageInputMemoryBarriers)) - , mImageOutputMemoryBarriers(std::move(other.mImageOutputMemoryBarriers)) - , mBufferOutputMemoryBarriers(std::move(other.mBufferOutputMemoryBarriers)) +ComputablePassBase::ComputablePassBase( ComputablePassBase&& other) noexcept + : mDispatchGroupCount( other.mDispatchGroupCount ) + , mMaterialPass( other.mMaterialPass ) { + other.mDispatchGroupCount = {1,1,1}; } -void ComputablePass::SetDispatchThreadCount(const std::array threadCount) +void ComputablePassBase::SetDispatchThreadCount( const std::array threadCount ) { std::array groupCount; const auto& cWorkGroupLocalSize = mMaterialPass.mShaderPass.m_shaderPassDescription.m_workGroupSettings.localSize; if (cWorkGroupLocalSize[0] == 0 || cWorkGroupLocalSize[1] == 0 || cWorkGroupLocalSize[2] == 0) { // Workgroup local size must be defined in the shader definition if we want to use this function - assert(0); + assert( 0 ); return; } groupCount[0] = (threadCount[0] + cWorkGroupLocalSize[0] - 1) / cWorkGroupLocalSize[0]; groupCount[1] = (threadCount[1] + cWorkGroupLocalSize[1] - 1) / cWorkGroupLocalSize[1]; groupCount[2] = (threadCount[2] + cWorkGroupLocalSize[2] - 1) / cWorkGroupLocalSize[2]; - SetDispatchGroupCount(groupCount); -} - - -Computable::~Computable() -{ - for (auto& pass : mPasses) - { - vkDestroyPipeline(mVulkan.m_VulkanDevice, pass.mPipeline, nullptr); - pass.mPipeline = VK_NULL_HANDLE; - } -} - - -enum class BindingAccess { - ReadOnly, - WriteOnly, - ReadWrite -}; -static VkAccessFlags BindingAccessToBufferAccessMask(BindingAccess access) -{ - switch (access) { - case BindingAccess::ReadOnly: - return VK_ACCESS_SHADER_READ_BIT; - case BindingAccess::WriteOnly: - return VK_ACCESS_SHADER_WRITE_BIT; - case BindingAccess::ReadWrite: - default: - return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - } -} - -/// @brief Structure holding the 'use' information for one binding -/// @tparam VK_TYPE Vulkan buffer type for this list of bindings (eg VkImage, VkBuffer) -template -struct BindingUseData -{ - typedef VK_BUFFERTYPE buffer_type; - BindingUseData(uint32_t _passIdx, BindingAccess _access, VK_BUFFERTYPE _buffer) : passIdx(_passIdx), access(_access), buffer(_buffer) {} - uint32_t passIdx; - BindingAccess access; - VK_BUFFERTYPE buffer; -}; - - -bool Computable::Init() -{ - LOGI("Creating Computable"); - - const auto& materialPasses = mMaterial.GetMaterialPasses(); - mPasses.reserve(materialPasses.size() ); - - // - // Previour pass buffer usage data (for all passes before the current one). - // - struct ImageUsage { - VkImage image; - uint32_t numMips; - uint32_t firstMip; - VkImageLayout imageLayout; - ImageUsage( VkImage _image, uint32_t _numMips, uint32_t _firstMip, VkImageLayout _imageLayout ) noexcept : image( _image ), numMips( _numMips ), firstMip( _firstMip ), imageLayout(_imageLayout) {}; - ImageUsage( const ImageInfo& other ) noexcept : image( other.image ), numMips( other.imageViewNumMips ), firstMip( other.imageViewFirstMip ), imageLayout( other.imageLayout) {} - bool operator==( const ImageUsage& other ) const noexcept { - return image == other.image && ((firstMip < other.firstMip + other.numMips) && (firstMip + numMips > other.firstMip)); - } - }; - std::vector> prevImageUsages; - std::vector> prevBufferUsages; - prevImageUsages.reserve(64); - prevBufferUsages.reserve(64); - - // Lambda helper! Calls emitFn when the buffer passed to 'currentUsage' was last accessed in a differnet way by a previous pass (ie it is in prevPassUsages as a write and was not read from in a pass between current use and the previous write, also applies for read in a previous pass and now a write). - // Returns true if we need an execution barrier emitting. - // We want to buffer/image barrier on Write then Read patterns, and on Read then write patterns (we need to do the layout transition). - const auto& emitBarrier = [](const auto& currentUsage, const auto& prevPassUsages, const auto& emitFn) -> bool { - - // scan backwards looking for prior uses of this buffer/image... - int priorUseIdx = (int)prevPassUsages.size(); - while (priorUseIdx-- > 0) - { - const auto& priorUsage = prevPassUsages[priorUseIdx]; - if (currentUsage.buffer == priorUsage.buffer) - { - // if we found a prior use determine what kind of barrier we need to insert. - const auto& priorUsage = prevPassUsages[priorUseIdx]; - if (priorUsage.access == BindingAccess::ReadOnly) - { - if (currentUsage.access == BindingAccess::ReadOnly) - { - // Read followed by Read. - // do nothing... . Doesnt even need an execution barrier. - // HOWEVER DONT EARLY OUT BECAUSE WE MAY ALSO BIND AS A WRITE (albeit on a different frame - we need to sort that out, happens if we read the 'previous frame' as a texture and write the 'current frame' as an image in the same shader pass) - } - else - { - // Read followed by Write or ReadWrite. - // We used to only emit an execution barrier but we now emit a barrier to do the layout transition - emitFn(priorUsage, currentUsage); - return true; - } - } - else // priorUsage.access != BindingAccess::ReadOnly - { - // Write followed by something (read, write, readwrite). - // emit barrier. - emitFn(priorUsage, currentUsage); - return true; - } - } - } - return false; - }; - - - for (uint32_t materialPassIdx = 0; materialPassIdx < (uint32_t) materialPasses.size(); ++materialPassIdx) - { - const auto& materialPass = materialPasses[materialPassIdx]; - const auto& shaderPass = materialPass.mShaderPass; - assert(std::holds_alternative>(shaderPass.m_shaders.m_modules)); - - // Usually pipeline layout will be stored with the shader but if the descriptor set layout is 'dynamic' (and stored in the materialPass) the pipeline layout will also be in the materialPass. - VkPipelineLayout pipelineLayout = shaderPass.GetPipelineLayout().GetVkPipelineLayout(); - if (pipelineLayout == VK_NULL_HANDLE) - pipelineLayout = materialPass.GetPipelineLayout().GetVkPipelineLayout(); - - VkPipeline pipeline; - const ShaderModuleT& shaderModule = shaderPass.m_shaders.Get>(); - LOGI("CreateComputePipeline: %s\n", shaderPass.m_shaderPassDescription.m_computeName.c_str()); - - if (!mVulkan.CreateComputePipeline(VK_NULL_HANDLE, - pipelineLayout, - shaderModule.GetVkShaderModule(), - materialPass.GetSpecializationConstants().GetVkSpecializationInfo(), - &pipeline)) - { - // Error - return false; - } - - // - // Build any memory barriers between passes - // - bool passNeedsExecutionBarrier = false; // Set if the pass needs (at least) an execution barrier. Adds barriers for WAR (write after read) use cases. - std::vector imageMemoryBarriers; - std::vector bufferMemoryBarriers; - - // Barriers for Image bindings - for (const auto& passImageBindings : materialPass.GetImageBindings()) - { - for (const auto& passImageBinding : passImageBindings.first) // image binding can be an array of bindings - { - const auto passUsage = BindingUseData( materialPassIdx, passImageBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, ImageUsage(passImageBinding) ); - - passNeedsExecutionBarrier |= emitBarrier(passUsage, prevImageUsages, [&imageMemoryBarriers](auto& prevUsage, auto& currentUsage) { - const auto& image = currentUsage.buffer; - imageMemoryBarriers.push_back(VkImageMemoryBarrier{ - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - nullptr, - VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask - VK_ACCESS_SHADER_READ_BIT, //dstAccessMask - prevUsage.buffer.imageLayout,//oldLayout; - currentUsage.buffer.imageLayout,//newLayout; - VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; - VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - image.image, //image; - { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; - image.firstMip, //baseMipLevel; - image.numMips, //mipLevelCount; - 0, //baseLayer; - 1, //layerCount; - }//subresourceRange; - }); - }); - } - } - - // Barriers for texture bindings - for (const auto& passTextureBindings : materialPass.GetTextureBindings()) - { - for (const auto& passTextureBinding : passTextureBindings.first) // texture binding can be an array of bindings - { - const auto passUsage = BindingUseData(materialPassIdx, BindingAccess::ReadOnly/*always readonly*/, ImageUsage(*passTextureBinding)); - - passNeedsExecutionBarrier |= emitBarrier(passUsage, prevImageUsages, - [&imageMemoryBarriers](auto& prevUsage, auto& currentUsage) { - const auto& image = currentUsage.buffer; - imageMemoryBarriers.push_back(VkImageMemoryBarrier{ - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - nullptr, - VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask - VK_ACCESS_SHADER_READ_BIT, //dstAccessMask - prevUsage.buffer.imageLayout, //oldLayout; - currentUsage.buffer.imageLayout, //newLayout; - VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; - VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - image.image, //image; - { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; - image.firstMip, //baseMipLevel; - image.numMips, //mipLevelCount; - 0, //baseLayer; - 1, //layerCount; - }//subresourceRange; - }); - }); - } - } - - // Barriers for buffer bindings - for (const auto& passBufferBindings : materialPass.GetBufferBindings()) - { - for (const auto& passBufferBinding : passBufferBindings.first) // buffer binding can be an array of bindings - { - const auto passUsage = BindingUseData(materialPassIdx, passBufferBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, passBufferBinding.buffer); - - passNeedsExecutionBarrier |= emitBarrier(passUsage, prevBufferUsages, - [&](auto& prevUsage, auto& currentUsage) { - VkBuffer bufferUsage = currentUsage.buffer; - //LOGI("Pass %d: Buffer Barrier: %s", materialPassIdx, materialPass.mShaderPass.m_shaderPassDescription.m_sets[0].m_descriptorTypes[tmp].names[0].c_str()); - bufferMemoryBarriers.push_back(VkBufferMemoryBarrier{ - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - nullptr, - BindingAccessToBufferAccessMask(prevUsage.access), //srcAccessMask - BindingAccessToBufferAccessMask(currentUsage.access), //dstAccessMask - VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; - VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - bufferUsage, //buffer; - 0, //offset - VK_WHOLE_SIZE //size - }); - }); - } - } - - mPasses.push_back( ComputablePass{ materialPass, pipeline, pipelineLayout, std::move(imageMemoryBarriers), std::move(bufferMemoryBarriers), passNeedsExecutionBarrier } ); - - // - // Store out the arrays of buffers used by this pass - // - - for (const auto& imageBindings : materialPass.GetImageBindings()) - { - BindingAccess access = imageBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite; - for (const auto& imageBinding : imageBindings.first) - { - prevImageUsages.push_back( { materialPassIdx, access, ImageUsage{imageBinding.image, imageBinding.imageViewNumMips, imageBinding.imageViewFirstMip, imageBinding.imageLayout} } ); - } - } - for (const auto& textureBinding : materialPass.GetTextureBindings()) - { - for (const auto& texture : textureBinding.first) - { - const auto& textureVulkan = apiCast(texture); - if (!textureVulkan->Image.IsEmpty()) // Only consider for barriers if the texture has a buffer/image, texture objects containing only a sampler can be ignored. - { - prevImageUsages.push_back( {materialPassIdx, BindingAccess::ReadOnly, {textureVulkan->GetVkImage(), textureVulkan->MipLevels, textureVulkan->FirstMip, textureVulkan->GetVkImageLayout()}} ); - } - } - } - for (const auto& bufferBinding : materialPass.GetBufferBindings()) - { - LOGI("Buffer Binding: %s", materialPass.mShaderPass.m_shaderPassDescription.m_sets[bufferBinding.second.setIndex].m_descriptorTypes[bufferBinding.second.setBinding.index].names[0].c_str()); - - for (const auto& buffer : bufferBinding.first) - { - prevBufferUsages.push_back({ materialPassIdx, bufferBinding.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, buffer.buffer }); - } - } - } - - // Create the final set of barriers. - // Any bindings that (may) write and were not used as a (read only) input to a subsequent ComputePass will be assumed as being outputs from the Computable (and will get memory barriers built) - - // Helper to scan through passUsages and call emitFn on any bindings that have a write as their last access type (ie things that are being output from the Computable and haven't already been barriered). - // Mangles/clears passUsages. - const auto& emitOutputBarrier = [](auto&& passUsages, const auto& emitFn) -> void - { - while( !passUsages.empty() ) - { - std::optional::value_type::buffer_type> currentBuffer {}; - for (auto revIt = passUsages.rbegin(); revIt != passUsages.rend(); ) - { - if (!currentBuffer) - { - if (revIt->access == BindingAccess::ReadOnly) - { - // last use of this binding was a readonly - no need for any output barriers - currentBuffer = revIt->buffer; - } - else - { - // last use of this binding is write or read-write - currentBuffer = revIt->buffer; - // pushback - emitFn(revIt->buffer); - } - } - if (currentBuffer == revIt->buffer) - { - // Once we have found a candidate buffer remove all prior references to it (and itself). - revIt = std::reverse_iterator(passUsages.erase((++revIt).base())); - } - else - { - ++revIt; - } - } - } - }; - - emitOutputBarrier(prevBufferUsages, - [this](const VkBuffer& buffer) -> void { - mBufferOutputMemoryBarriers.push_back(VkBufferMemoryBarrier{ - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - nullptr, - VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask - VK_ACCESS_SHADER_READ_BIT, //dstAccessMask - VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; - VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - buffer, //buffer; - 0, //offset - VK_WHOLE_SIZE //size - }); - }); - - emitOutputBarrier(prevImageUsages, - [this](const ImageUsage& image) -> void { - mImageOutputMemoryBarriers.push_back(VkImageMemoryBarrier{ - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - nullptr, - VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask - VK_ACCESS_SHADER_READ_BIT, //dstAccessMask - VK_IMAGE_LAYOUT_GENERAL, //oldLayout; - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,//newLayout; - VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; - VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - image.image, //image; - { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; - image.firstMip, //baseMipLevel; - image.numMips, //mipLevelCount; - 0, //baseLayer; - 1, //layerCount; - }//subresourceRange; - }); - }); - - for (uint32_t whichBuffer = 0; whichBuffer < mMaterial.GetNumFrameBuffers(); ++whichBuffer) - { - mMaterial.UpdateDescriptorSets(whichBuffer); - } - return true; -} - -const std::string& Computable::GetPassName( uint32_t passIdx ) const -{ - const auto& passNames = mMaterial.m_shader.GetShaderPassIndicesToNames(); - assert( passIdx <= passNames.size() ); - return passNames[passIdx]; -} - -void Computable::SetDispatchGroupCount(uint32_t passIdx, const std::array& groupCount) -{ - mPasses[passIdx].SetDispatchGroupCount(groupCount); -} - -void Computable::SetDispatchThreadCount(uint32_t passIdx, const std::array& threadCount) -{ - mPasses[passIdx].SetDispatchThreadCount(threadCount); -} - -void Computable::DispatchPass(VkCommandBuffer cmdBuffer, const ComputablePass& computablePass, uint32_t bufferIdx) const -{ - // Add image barriers (if needed) - if (computablePass.NeedsBarrier()) - { - const auto& imageMemoryBarriers = computablePass.GetVkImageMemoryBarriers(); - const auto& bufferMemoryBarriers = computablePass.GetVkBufferMemoryBarriers(); - - // Barrier on memory, with correct layouts set. - vkCmdPipelineBarrier(cmdBuffer, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, - 0, - 0, nullptr, - (uint32_t)bufferMemoryBarriers.size(), - bufferMemoryBarriers.empty() ? nullptr : bufferMemoryBarriers.data(), - (uint32_t)imageMemoryBarriers.size(), - imageMemoryBarriers.empty() ? nullptr : imageMemoryBarriers.data()); - } - - // Bind the pipeline for this material - vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computablePass.mPipeline); - - // Bind everything the shader needs - const std::span descriptorSets = computablePass.mMaterialPass.GetVkDescriptorSets(bufferIdx); - vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computablePass.mPipelineLayout, 0, descriptorSets.size(), descriptorSets.data(), 0, nullptr); - - // Dispatch the compute task - vkCmdDispatch(cmdBuffer, computablePass.GetDispatchGroupCount()[0], computablePass.GetDispatchGroupCount()[1], computablePass.GetDispatchGroupCount()[2]); + SetDispatchGroupCount( groupCount ); } diff --git a/framework/code/material/computable.hpp b/framework/code/material/computable.hpp index a2d67e0..d7da3c6 100644 --- a/framework/code/material/computable.hpp +++ b/framework/code/material/computable.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,89 +12,211 @@ #include #include #include -#include "material/material.hpp" +#include "shader.hpp" +#include "material.hpp" + // Forward Declarations -template class ShaderT; -class Vulkan; +class CommandListBase; +class MaterialBase; +class MaterialPassBase; +template class Computable; +template class ComputablePass; +template class Material; +template class CommandList; +class ImageMemoryBarrierBase +{ +protected: + ImageMemoryBarrierBase() noexcept = default; + // ok to copy (default copy operators!) +}; + +template +class ImageMemoryBarrier final : public ImageMemoryBarrierBase +{ + ImageMemoryBarrier( const ImageMemoryBarrier& ) = delete; + ImageMemoryBarrier& operator=( const ImageMemoryBarrier& ) = delete; + ImageMemoryBarrier() = delete; + ~ImageMemoryBarrier() = delete; + static_assert(sizeof( ImageMemoryBarrier ) != sizeof( ImageMemoryBarrierBase )); // static assert if not specialized +}; + +class BufferMemoryBarrierBase +{ +protected: + BufferMemoryBarrierBase() noexcept = default; + // ok to copy (default copy operators!) +}; + +template +class BufferMemoryBarrier final : public BufferMemoryBarrierBase +{ + BufferMemoryBarrier( const BufferMemoryBarrier& ) = delete; + BufferMemoryBarrier& operator=( const BufferMemoryBarrier& ) = delete; + BufferMemoryBarrier() = delete; + ~BufferMemoryBarrier() = delete; + static_assert(sizeof( BufferMemoryBarrier ) != sizeof( BufferMemoryBarrierBase )); // static assert if not specialized +}; + /// Encapsulates a 'computable' pass, contains the materialpass, pipeline, etc). /// Similar to a DrawablePass but without the vertex buffer /// @ingroup Material -class ComputablePass +class ComputablePassBase { - ComputablePass(const ComputablePass&) = delete; - ComputablePass& operator=(const ComputablePass&) = delete; + ComputablePassBase(const ComputablePassBase&) = delete; + ComputablePassBase& operator=(const ComputablePassBase&) = delete; public: - ComputablePass(const MaterialPass& materialPass, VkPipeline pipeline, VkPipelineLayout pipelineLayout, std::vector imageMemoryBarriers, std::vector bufferMemoryBarriers, bool needsExecutionBarrier) + template using tApiDerived = ComputablePass; // make apiCast work! + /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) + void SetDispatchGroupCount( std::array count ) { mDispatchGroupCount = count; } + const auto& GetDispatchGroupCount() const { return mDispatchGroupCount; } + /// number of global workgroup threads to run (value before the local workgroup sizes are accounted for). Requires "WorkGroup" : { "LocalSize": {x,y,z} } in the shader definition json. + void SetDispatchThreadCount( std::array count ); + +protected: + ComputablePassBase( const MaterialPassBase& materialPass ) noexcept : mMaterialPass(materialPass) - , mPipeline(pipeline) - , mPipelineLayout(pipelineLayout) - , mImageMemoryBarriers(std::move(imageMemoryBarriers)) - , mBufferMemoryBarriers(std::move(bufferMemoryBarriers)) - , mNeedsExecutionBarrier(needsExecutionBarrier) {} - ComputablePass(ComputablePass&&) noexcept; - ~ComputablePass(); + ComputablePassBase( ComputablePassBase&& ) noexcept; - const std::vector& GetVkDescriptorSets() const { return mMaterialPass.GetVkDescriptorSets(); } - const auto& GetVkImageMemoryBarriers() const { return mImageMemoryBarriers; } - const auto& GetVkBufferMemoryBarriers() const { return mBufferMemoryBarriers; } - const bool NeedsBarrier() const { return mNeedsExecutionBarrier || (!mImageMemoryBarriers.empty()) || (!mBufferMemoryBarriers.empty()); }; ///< @return true if there needs to be a barrier before executing this compute pass + const MaterialPassBase& mMaterialPass; + std::array mDispatchGroupCount{1u,1u,1u}; +}; - /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) - void SetDispatchGroupCount(std::array count) { mDispatchGroupCount = count; } - const auto& GetDispatchGroupCount() const { return mDispatchGroupCount; } - /// number of global workgroup threads to run (value before the local workgroup sizes are accounted for). Requires "WorkGroup" : { "LocalSize": {x,y,z} } in the shader definition json. - void SetDispatchThreadCount(std::array count); - const MaterialPass& mMaterialPass; +template +class ComputablePass final : public ComputablePassBase +{ + ComputablePass() = delete; + ~ComputablePass() = delete; + static_assert(sizeof( ComputablePass ) != sizeof( ComputablePassBase )); // static assert if not specialized +}; - VkPipeline mPipeline = VK_NULL_HANDLE; // Owned by us - VkPipelineLayout mPipelineLayout; // Owned by ShaderPass or MaterialPass +/// Encapsulates a 'computable' object, contains the MaterialBase, computable passes. +/// Similar to a Drawable but without the vertex buffer +class ComputableBase +{ + ComputableBase(const ComputableBase&) = delete; + ComputableBase& operator=(const ComputableBase&) = delete; protected: - std::vector mImageMemoryBarriers; ///< Image barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho - std::vector mBufferMemoryBarriers; ///< Buffer barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho - std::array mDispatchGroupCount{ 1u,1u,1u }; - bool mNeedsExecutionBarrier = false;///< Denotes if we need an execution barrier for ENTRY to this pass (even if there are no image or buffer barriers). + ComputableBase() noexcept {} +public: + template using tApiDerived = Computable; // make apiCast work! + virtual ~ComputableBase() {} + + /// @brief Initialize the computable (call once, prior to dispatching) + /// @return true on success + virtual bool Init() = 0; + + /// Return the pass name for the given compute pass index + virtual const std::string& GetPassName( uint32_t passIdx ) const = 0; + + /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) + virtual void SetDispatchGroupCount(uint32_t passIdx, const std::array& groupCount) = 0; + /// number of global workgroup threads to run (value before the local workgroup sizes are accounted for). Requires "WorkGroup" : { "LocalSize": {x,y,z} } in the shader definition json. + virtual void SetDispatchThreadCount(uint32_t passIdx, const std::array& threadCount) = 0; + + /// Add all the commands to cmdList needed to 'dispatch' all the passes contained in this Computable + /// Will add necissary barriers before the first dispatch, between the passes and will bind the appropriate descriptor sets needed by each. + virtual void Dispatch(CommandListBase& commandList, uint32_t bufferIdx, bool addTimers) const = 0; + + /// Add the commands to barrier / syncronize all buffers written by the computable. + virtual void AddOutputBarriersToCmdList(CommandListBase& commandList) const = 0; }; -/// Encapsulates a 'computable' object, contains the Material, computable passes. -/// Similar to a Drawable but without the vertex buffer -class Computable +template +class Computable final : public ComputableBase { Computable(const Computable&) = delete; Computable& operator=(const Computable&) = delete; + public: - Computable(Vulkan& vulkan, Material&&); - ~Computable(); - Computable(Computable&&) noexcept; + Computable(T_GFXAPI&, Material&&) noexcept; + Computable(T_GFXAPI&, std::unique_ptr material) noexcept; + Computable(Computable&&) noexcept; + ~Computable() override; + + /// @brief Initialize the computable (call once, prior to dispatching) + /// @return true on success + bool Init() override; - bool Init(); const auto& GetPasses() const { return mPasses; } const auto& GetImageInputMemoryBarriers() const { return mImageInputMemoryBarriers; } const auto& GetImageOutputMemoryBarriers() const { return mImageOutputMemoryBarriers; } const auto& GetBufferOutputMemoryBarriers() const { return mBufferOutputMemoryBarriers; } - const std::string& GetPassName( uint32_t passIdx ) const; + /// Return the pass name for the given compute pass index + const std::string& GetPassName( uint32_t passIdx ) const override; /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) - void SetDispatchGroupCount(uint32_t passIdx, const std::array& groupCount); + void SetDispatchGroupCount(uint32_t passIdx, const std::array& groupCount) override; /// number of global workgroup threads to run (value before the local workgroup sizes are accounted for). Requires "WorkGroup" : { "LocalSize": {x,y,z} } in the shader definition json. - void SetDispatchThreadCount(uint32_t passIdx, const std::array& threadCount); + void SetDispatchThreadCount(uint32_t passIdx, const std::array& threadCount) override; /// Add all the commands to the cmdBuffer needed to 'dispatch' the given pass. /// Will add necissary barriers before the dispatch, along with binding the appropriate descriptor sets needed by the pass. - void DispatchPass(VkCommandBuffer cmdBuffer, const ComputablePass& computablePass, uint32_t bufferIdx) const; + void DispatchPass(CommandList& cmdList, const ComputablePass& computablePass, uint32_t bufferIdx) const; + + /// Add all the commands to cmdList needed to 'dispatch' all the passes contained in this Computable + /// Will add necissary barriers before the first dispatch, between the passes and will bind the appropriate descriptor sets needed by each. + void Dispatch(CommandListBase& commandList, uint32_t bufferIdx, bool addTimers) const override; + void Dispatch(CommandList& commandList, uint32_t bufferIdx, bool addTimers) const; + + /// Add the commands to barrier / syncronize all buffers written by the computable. + void AddOutputBarriersToCmdList(CommandListBase& commandList) const override; + void AddOutputBarriersToCmdList(CommandList& commandList) const; protected: - Material mMaterial; - Vulkan& mVulkan; - std::vector mPasses; - std::vector mImageInputMemoryBarriers; // barriers for ENTRY to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho - std::vector mImageOutputMemoryBarriers; // barriers for EXIT to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho - std::vector mBufferOutputMemoryBarriers; // barriers for EXIT to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + T_GFXAPI& mGfxApi; + Material mMaterial; + std::vector> mPasses; + std::vector> mImageInputMemoryBarriers; // barriers for ENTRY to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + std::vector> mImageOutputMemoryBarriers; // barriers for EXIT to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + std::vector> mBufferOutputMemoryBarriers; // barriers for EXIT to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho }; + + +template +Computable::Computable(T_GFXAPI& gfxApi, Material&& material) noexcept + : ComputableBase() + , mGfxApi(gfxApi) + , mMaterial(std::move(material)) +{ +} + +template +Computable::Computable(T_GFXAPI& gfxapi, std::unique_ptr material) noexcept + : ComputableBase() + , mGfxApi( gfxapi ) + , mMaterial(std::move( *apiCast( material.get() ) )) +{ +} + +template +Computable::Computable(Computable&& other) noexcept + : mGfxApi(other.mGfxApi) + , mMaterial(std::move(other.mMaterial)) + , mPasses(std::move(other.mPasses)) + , mImageInputMemoryBarriers(std::move(other.mImageInputMemoryBarriers)) + , mImageOutputMemoryBarriers(std::move(other.mImageOutputMemoryBarriers)) + , mBufferOutputMemoryBarriers(std::move(other.mBufferOutputMemoryBarriers)) +{ +} + +template +Computable::~Computable() +{ +} + +template +const std::string& Computable::GetPassName( uint32_t passIdx ) const +{ + const auto& passNames = static_cast(mMaterial).GetShader().GetShaderPassIndicesToNames(); + assert( passIdx <= passNames.size() ); + return passNames[passIdx]; +} + diff --git a/framework/code/material/descriptorSetDescription.hpp b/framework/code/material/descriptorSetDescription.hpp index 46eaba1..b7aadc8 100644 --- a/framework/code/material/descriptorSetDescription.hpp +++ b/framework/code/material/descriptorSetDescription.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -27,7 +27,8 @@ class DescriptorSetDescription ImageStorage, InputAttachment, DrawIndirectBuffer, - AccelerationStructure ///< Ray Tracing + AccelerationStructure, ///< Ray Tracing + DescriptorTable ///< Dx12 descriptor table (set) reference }; class StageFlag { @@ -65,16 +66,19 @@ class DescriptorSetDescription std::vector names; uint32_t count; bool readOnly; + int descriptorIndex; - DescriptorTypeAndCount(DescriptorType _type, StageFlag _stages, std::vector _names, int _count = -1 /* if un-set (-1), default to the number of names) */, bool _readOnly = false ) + DescriptorTypeAndCount(DescriptorType _type, StageFlag _stages, std::vector _names, int _count /*= -1*/ /* if un-set (-1), default to the number of names) */, bool _readOnly /*= false*/, int _descriptorIndex /*= -1*/) : type(_type) , stages(_stages) , names(_names) , count(_count) , readOnly(_readOnly) + , descriptorIndex(_descriptorIndex) { } - }; + std::string m_name; + uint32_t m_setIndex = 0; ///< numbered from 0 (0 is the root descriptor on DX12) std::vector m_descriptorTypes; }; diff --git a/framework/code/material/descriptorSetLayout.hpp b/framework/code/material/descriptorSetLayout.hpp index 383e939..eac0652 100644 --- a/framework/code/material/descriptorSetLayout.hpp +++ b/framework/code/material/descriptorSetLayout.hpp @@ -7,52 +7,106 @@ //============================================================================================================ #pragma once -#include -#include +#include #include #include -#include //TEMP -#include +#include "descriptorSetDescription.hpp" -// Forward declares -class DescriptorSetDescription; -class Vulkan; -/// Representation of the descriptor set layout. -/// Builds/owns the Vulkan Descriptor Set Layout that can be used to allocate the descriptor sets. -/// Also has a mapping from descriptor slots name (nice name) to the their shader binding index. +/// Base class for the descriptor set layout. +/// Graphics api independent. /// @ingroup Material -class DescriptorSetLayout +class DescriptorSetLayoutBase { - DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; - DescriptorSetLayout(const DescriptorSetLayout&) = delete; -public: - DescriptorSetLayout(); - ~DescriptorSetLayout(); - DescriptorSetLayout(DescriptorSetLayout&&) noexcept; - - bool Init(Vulkan& vulkan, const DescriptorSetDescription&); - void Destroy(Vulkan& vulkan); - - static VkDescriptorSetLayout CreateVkDescriptorSetLayout(Vulkan& vulkan, const std::span descriptorSetLayoutBindings); - static void CalculatePoolSizes(const std::span descriptorSetLayoutBindings, std::vector& descriptorPoolSizes/*output*/); + DescriptorSetLayoutBase& operator=(const DescriptorSetLayoutBase&) = delete; + DescriptorSetLayoutBase(const DescriptorSetLayoutBase&) = delete; +protected: + DescriptorSetLayoutBase() noexcept; + ~DescriptorSetLayoutBase(); + DescriptorSetLayoutBase(DescriptorSetLayoutBase && other) noexcept + : m_nameToBinding(std::move(other.m_nameToBinding)) + {} - const auto& GetVkDescriptorSetLayoutBinding() const { return m_descriptorSetLayoutBindings; } - const auto& GetVkDescriptorSetLayout() const { return m_descriptorSetLayout; } + bool Init(const DescriptorSetDescription&); +public: const auto& GetNameToBinding() const { return m_nameToBinding; } - const auto& GetDescriptorPoolSizes() const { return m_descriptorPoolSizes; } struct BindingTypeAndIndex { - VkDescriptorType type; - uint32_t index; - bool isArray; - bool isReadOnly; + DescriptorSetDescription::DescriptorType type; + uint32_t index; ///< index within the descriptor set + bool isArray; + bool isReadOnly; + }; + struct DescriptorBinding { + uint32_t setIndex; ///< descriptor set index + BindingTypeAndIndex setBinding; ///< binding within the descriptor set }; + private: std::map m_nameToBinding; ///< For each named descriptor slot store the relevant binding index (one name per binding for simplicity) ///< @TODO should this be in here, or in the materialPass. +}; + + +struct DescriptorTypeAndLocation { + DescriptorTypeAndLocation(uint32_t _setIndex, DescriptorSetLayoutBase::BindingTypeAndIndex _typeAndIndex) + : setIndex(_setIndex) + , index(_typeAndIndex.index) + , isArray(_typeAndIndex.isArray) + , isReadOnly(_typeAndIndex.isReadOnly) + {} + uint32_t setIndex; ///< index of descriptor set / table + uint32_t index; ///< index of descriptor within set / table + bool isArray; ///< is descriptor an array + bool isReadOnly; ///< is descriptor readonly +}; + + +inline DescriptorSetLayoutBase::DescriptorSetLayoutBase() noexcept +{ +} + +inline DescriptorSetLayoutBase::~DescriptorSetLayoutBase() +{ +} + + +inline bool DescriptorSetLayoutBase::Init(const DescriptorSetDescription& description) +{ + uint32_t index = 0; + for (const auto& it : description.m_descriptorTypes) + { + BindingTypeAndIndex bindingTypeAndIndex {}; + bindingTypeAndIndex.isReadOnly = it.readOnly; + bindingTypeAndIndex.isArray = it.count!=1; + bindingTypeAndIndex.type = it.type; + + if (it.descriptorIndex < 0) + // Index of < 0 denotes we want to use sequential descriptor binding indices. + ///TODO: look for collisions or determine how/if we want to handle out of order desciptor indices or enforce shaders that have an explicit binding index to define indices for all descriptors. + bindingTypeAndIndex.index = index++; + else + bindingTypeAndIndex.index = it.descriptorIndex; + + assert(it.names.size() <= 1); ///TODO: only one name supported, needs to store the index within the descriptor as well as the binding index if we want to support this! (the 'for' loop below is not the full implementation) + for (const auto& name : it.names) + { + auto nameToBindingEmplaced = m_nameToBinding.try_emplace(name, bindingTypeAndIndex); + assert(nameToBindingEmplaced.second); // name must be unique + } + } + return true; +} + + +/// Templated (by graphics API) derived version of DescriptorSetLayoutBase +/// Graphics api may want to specialize this. +template +class DescriptorSetLayout : public DescriptorSetLayoutBase +{ +public: + DescriptorSetLayout() noexcept = delete; // Expecting that this template be specialized + ~DescriptorSetLayout() = delete; // Expecting that this template be specialized + DescriptorSetLayout(DescriptorSetLayout&&) noexcept = delete; // Expecting that this template be specialized - // Vulkan objects - VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; ///< Vulkan descriptor set layout object. Can be VK_NULL_HANDLE after Init if there are bindings with 'dynamic' descriptorCount (0) - std::vector m_descriptorSetLayoutBindings; - std::vector m_descriptorPoolSizes; + static_assert(sizeof(DescriptorSetLayout) != sizeof(DescriptorSetLayoutBase)); // Expecting that this template be specialized }; diff --git a/framework/code/material/drawable.cpp b/framework/code/material/drawable.cpp deleted file mode 100644 index 380d45d..0000000 --- a/framework/code/material/drawable.cpp +++ /dev/null @@ -1,1005 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "drawable.hpp" -#include "vulkan/material.hpp" -#include "shader.hpp" -#include "shaderDescription.hpp" -#include "vulkan/shaderModule.hpp" -#include "system/os_common.h" -#include "mesh/meshHelper.hpp" -#include "mesh/instanceGenerator.hpp" -#include "vulkan/extensionHelpers.hpp" -#include -#include - -DrawablePass::~DrawablePass() -{ - assert(mPipeline == VK_NULL_HANDLE); -} - -const DrawablePass* Drawable::GetDrawablePass(const std::string& passName) const -{ - auto it = mPassNameToIndex.find(passName); - if (it != mPassNameToIndex.end()) - { - return &mPasses[it->second]; - } - return nullptr; -} - - -Drawable::Drawable(Vulkan& vulkan, Material&& material) - : mMaterial(std::move(material)) - , mVulkan(vulkan) -{ -} - -Drawable::Drawable(Drawable&& other) noexcept - : mMaterial(std::move(other.mMaterial)) - , mVulkan( other.mVulkan ) - , mMeshObject(std::move(other.mMeshObject)) - , mPasses(std::move(other.mPasses)) - , mPassNameToIndex(std::move(other.mPassNameToIndex)) - , mPassMask(other.mPassMask) - , mVertexInstanceBuffer(std::move(other.mVertexInstanceBuffer)) - , mDrawIndirectBuffer(std::move(other.mDrawIndirectBuffer)) -{ - other.mPassMask = 0; -} - -Drawable::~Drawable() -{ - for (auto& pass : mPasses) - { - vkDestroyPipeline(mVulkan.m_VulkanDevice, pass.mPipeline, nullptr); - pass.mPipeline = VK_NULL_HANDLE; - } -} - -static VkBlendFactor BlendFactorToVk( ShaderPassDescription::BlendFactor bf) -{ - switch( bf ) - { - case ShaderPassDescription::BlendFactor::Zero: - return VK_BLEND_FACTOR_ZERO; - case ShaderPassDescription::BlendFactor::One: - return VK_BLEND_FACTOR_ONE; - case ShaderPassDescription::BlendFactor::SrcAlpha: - return VK_BLEND_FACTOR_SRC_ALPHA; - case ShaderPassDescription::BlendFactor::OneMinusSrcAlpha: - return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case ShaderPassDescription::BlendFactor::DstAlpha: - return VK_BLEND_FACTOR_DST_ALPHA; - case ShaderPassDescription::BlendFactor::OneMinusDstAlpha: - return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - } - assert( 0 ); - return VK_BLEND_FACTOR_ZERO; -} - -bool Drawable::Init(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::optional drawIndirectBuffer, std::span passMultisample, std::span subpasses, int nodeId) -{ - mMeshObject = {}; - mVertexInstanceBuffer = {}; - mDrawIndirectBuffer = std::move(drawIndirectBuffer); - mNodeId = nodeId; - - return ReInit(vkRenderPasses, passNames, passMask, passMultisample, subpasses); -} - -bool Drawable::Init(VkRenderPass vkRenderPass, const char* passName, Mesh meshObject, std::optional vertexInstanceBuffer, std::optional drawIndirectBuffer, const VkSampleCountFlagBits* const passMultisample, const uint32_t* const subpasses, int nodeId) -{ - mMeshObject = std::move(meshObject); - mVertexInstanceBuffer = std::move(vertexInstanceBuffer); - mDrawIndirectBuffer = std::move(drawIndirectBuffer); - mNodeId = nodeId; - return ReInit( vkRenderPass, passName, passMultisample, subpasses ); -} - -bool Drawable::Init(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, Mesh meshObject, std::optional vertexInstanceBuffer, std::optional drawIndirectBuffer, std::span passMultisample, std::span subpasses, int nodeId) -{ - mMeshObject = std::move(meshObject); - mVertexInstanceBuffer = std::move(vertexInstanceBuffer); - mDrawIndirectBuffer = std::move(drawIndirectBuffer); - mNodeId = nodeId; - return ReInit(vkRenderPasses, passNames, passMask, passMultisample, subpasses); -} - -bool Drawable::Init(VkRenderPass vkRenderPass, const char* passName, Mesh meshObject, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom) -{ - mMeshObject = std::move(meshObject); - mVertexInstanceBuffer = std::nullopt; - mDrawIndirectBuffer = std::nullopt; - mNodeId = -1; - return ReInit(vkRenderPass, passName, nullptr, nullptr, ia_custom, rs_custom); -} - -bool Drawable::ReInit( VkRenderPass vkRenderPass, const char* passName, const VkSampleCountFlagBits* const passMultisample, const uint32_t* const subpasses ) -{ - auto multisampleSpan = passMultisample ? std::span(passMultisample, 1) : std::span{}; - auto subpassesSpan = subpasses ? std::span( subpasses, 1 ) : std::span{}; - - return ReInit( { &vkRenderPass,1 }, &passName, 1, multisampleSpan, subpassesSpan ); -} - -bool Drawable::ReInit( VkRenderPass vkRenderPass, const char* passName, const VkSampleCountFlagBits* const passMultisample, const uint32_t* const subpasses, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom) -{ - auto multisampleSpan = passMultisample ? std::span(passMultisample, 1) : std::span{}; - auto subpassesSpan = subpasses ? std::span( subpasses, 1 ) : std::span{}; - - return ReInit( { &vkRenderPass,1 }, &passName, 1, multisampleSpan, subpassesSpan, ia_custom, rs_custom); -} - -bool Drawable::ReInit(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses) -{ - // Need default values for InputAssembly and RasterizationState - - VkPipelineInputAssemblyStateCreateInfo ia_custom = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - ia_custom.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - // State for rasterization, such as polygon fill mode is defined. - VkPipelineRasterizationStateCreateInfo rs_custom = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - rs_custom.polygonMode = VK_POLYGON_MODE_FILL; - rs_custom.cullMode = VK_CULL_MODE_NONE; - rs_custom.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rs_custom.depthClampEnable = VK_FALSE; - rs_custom.rasterizerDiscardEnable = VK_FALSE; - rs_custom.depthBiasEnable = VK_FALSE; - rs_custom.lineWidth = 1.0f; - - return ReInit(vkRenderPasses, passNames, passMask, passMultisample, subpasses, ia_custom, rs_custom); -} - -bool Drawable::ReInit( std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom) -{ - assert( passMultisample.empty() || passMultisample.size() == vkRenderPasses.size() ); - mPassMask = passMask; - mPassNameToIndex.clear(); - for (auto& pass : mPasses) - { - vkDestroyPipeline(mVulkan.m_VulkanDevice, pass.mPipeline, nullptr); - pass.mPipeline = VK_NULL_HANDLE; - } - mPasses.clear(); - - const auto& shader = mMaterial.m_shader; - mPasses.reserve(shader.GetShaderPasses().size()); - for (uint32_t passIdx = 0; passIdx < sizeof(passMask) * 8 && passMask != 0; ++passIdx) - { - bool passMaskSet = ((passMask & 1) != 0); - passMask >>= 1; - if (passMaskSet) - { - // LOGI("Creating Mesh Object PipelineState and Pipeline for pass... %s", passNames[passIdx]); - MaterialPass* pMaterialPass = const_cast(mMaterial.GetMaterialPass(passNames[passIdx])); - if (!pMaterialPass) - { - LOGE(" Pass %s does not exist in shader", passNames[passIdx]); - continue; - } - assert(pMaterialPass); - const auto& shaderPass = pMaterialPass->mShaderPass; - - // Common to all pipelines - // State for rasterization, such as polygon fill mode is defined. - VkPipelineRasterizationStateCreateInfo rs = rs_custom; - const auto& fixedFunctionSettings = shaderPass.m_shaderPassDescription.m_fixedFunctionSettings; - // Don't stomp this value! rs.polygonMode = VK_POLYGON_MODE_FILL; - rs.cullMode = (fixedFunctionSettings.cullBackFace?VK_CULL_MODE_BACK_BIT:0) | (fixedFunctionSettings.cullFrontFace ? VK_CULL_MODE_FRONT_BIT : 0); - // Don't stomp this value! rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rs.depthClampEnable = fixedFunctionSettings.depthClampEnable ? VK_TRUE : VK_FALSE; - // Don't stomp this value! rs.rasterizerDiscardEnable = VK_FALSE; - rs.depthBiasEnable = fixedFunctionSettings.depthBiasEnable ? VK_TRUE : VK_FALSE; - if (fixedFunctionSettings.depthBiasEnable) - { - rs.depthBiasConstantFactor = fixedFunctionSettings.depthBiasConstant; - rs.depthBiasClamp = fixedFunctionSettings.depthBiasClamp; - rs.depthBiasSlopeFactor = fixedFunctionSettings.depthBiasSlope; - } - // Don't stomp this value! rs.lineWidth = 1.0f; - - const auto& outputSettings = shaderPass.m_shaderPassDescription.m_outputs; - - // Setup blending/transparency - std::vector BlendStates; - BlendStates.reserve(outputSettings.size()); - for (const auto& outputSetting : outputSettings) - { - VkPipelineColorBlendAttachmentState& cb = BlendStates.emplace_back(VkPipelineColorBlendAttachmentState{}); - if (outputSetting.blendEnable) - { - cb.blendEnable = VK_TRUE; - cb.srcColorBlendFactor = BlendFactorToVk(outputSetting.srcColorBlendFactor); - cb.dstColorBlendFactor = BlendFactorToVk(outputSetting.dstColorBlendFactor); - cb.colorBlendOp = VK_BLEND_OP_ADD; - cb.srcAlphaBlendFactor = BlendFactorToVk(outputSetting.srcAlphaBlendFactor); - cb.dstAlphaBlendFactor = BlendFactorToVk(outputSetting.dstAlphaBlendFactor); - cb.alphaBlendOp = VK_BLEND_OP_ADD; - } - cb.colorWriteMask = outputSetting.colorWriteMask & (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); - } - - VkPipelineColorBlendStateCreateInfo cb = {}; - cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - cb.attachmentCount = (uint32_t)BlendStates.size(); - cb.pAttachments = BlendStates.data(); - - // Setup depth testing - VkPipelineDepthStencilStateCreateInfo ds = {}; - ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - ds.depthTestEnable = fixedFunctionSettings.depthTestEnable ? VK_TRUE : VK_FALSE; - ds.depthWriteEnable = fixedFunctionSettings.depthWriteEnable ? VK_TRUE : VK_FALSE; - switch( fixedFunctionSettings.depthCompareOp ) { - case ShaderPassDescription::DepthCompareOp::LessEqual: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - case ShaderPassDescription::DepthCompareOp::Equal: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - case ShaderPassDescription::DepthCompareOp::Greater: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - } - ds.depthBoundsTestEnable = VK_FALSE; - ds.back.failOp = VK_STENCIL_OP_KEEP; - ds.back.passOp = VK_STENCIL_OP_KEEP; - ds.back.compareOp = VK_COMPARE_OP_ALWAYS; - ds.stencilTestEnable = VK_FALSE; - ds.front = ds.back; - - // Setup (multi) sampling - VkSampleMask sampleMask = 0; - VkPipelineMultisampleStateCreateInfo ms = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; - - ms.rasterizationSamples = passMultisample.empty() ? VK_SAMPLE_COUNT_1_BIT : passMultisample[passIdx]; - const auto& sampleShadingSettings = shaderPass.m_shaderPassDescription.m_sampleShadingSettings; - ms.sampleShadingEnable = sampleShadingSettings.sampleShadingEnable; - if (sampleShadingSettings.sampleShadingMask != 0) - { - assert(ms.rasterizationSamples <= VK_SAMPLE_COUNT_32_BIT ); // sampleMask is only 32bits currently! Easy fix if we want > 32x MSAA - sampleMask = sampleShadingSettings.sampleShadingMask & ((1 << ms.rasterizationSamples) -1); - ms.pSampleMask = &sampleMask; - } - - VkPipelineSampleLocationsStateCreateInfoEXT msLocations = { VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT }; - if( sampleShadingSettings.forceCenterSample ) - { - msLocations.sampleLocationsEnable = VK_TRUE; - msLocations.sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT; - msLocations.sampleLocationsInfo.sampleLocationsPerPixel = ms.rasterizationSamples; - msLocations.sampleLocationsInfo.sampleLocationsCount = (uint32_t) ms.rasterizationSamples; - msLocations.sampleLocationsInfo.sampleLocationGridSize = { 1,1 }; - std::vector msSampleLocations( msLocations.sampleLocationsInfo.sampleLocationsCount, { 0.5f,0.5f } ); - msLocations.sampleLocationsInfo.pSampleLocations = msSampleLocations.data(); - ms.pNext = &msLocations; - } - - mPassNameToIndex.try_emplace(passNames[passIdx], (uint32_t)mPasses.size()); // add the lookup (in to mPasses) - - // Build the passVertexBufferLookup so at runtime we can easily populate the vkBuffer array with the vertex and instance buffers in the order specified per pass by m_vertexFormatBindings. - // Could do this in a single loop; currently split into 2 so we can potentially add more flexibility in where we get the VKBuffers from (TODO) - std::vector tmp; - tmp.reserve(shader.m_shaderDescription->m_vertexFormats.size()); - int numVertexRateFormats = 0, numInstanceRateFormats = 0; - for (const auto& vertexFormat : shader.m_shaderDescription->m_vertexFormats) - { - switch (vertexFormat.inputRate) { - case VertexFormat::eInputRate::Vertex: - tmp.push_back(numVertexRateFormats++); - break; - case VertexFormat::eInputRate::Instance: - tmp.push_back(--numInstanceRateFormats); - break; - } - } - std::vector passVertexBufferLookup; // order of the vkBuffers for this pass (index is in to the vertex array if positive, or in to the instance array if negative (-1 is the 'first') - std::vector passVertexBuffers; - passVertexBufferLookup.reserve(shader.m_shaderDescription->m_vertexFormats.size()); - passVertexBuffers.reserve(shader.m_shaderDescription->m_vertexFormats.size()); - for (uint32_t formatBindingIdx : shaderPass.m_shaderPassDescription.m_vertexFormatBindings) - { - const int bufferIdx = tmp[formatBindingIdx]; - passVertexBufferLookup.push_back(bufferIdx); - if (bufferIdx >= 0) - { - // Vertex rate data (ie the mesh) - passVertexBuffers.push_back(mMeshObject.m_VertexBuffers[bufferIdx].GetVkBuffer()); - - // Double check the mesh is supplying the data we expect in the shader, may mismatch if the mesh was not built using the vertex format (eg Mesh::CreateScreenSpaceMesh) - // We could dive even deeper, for now check the span and the number of attributes match - assert(mMeshObject.m_VertexBuffers[bufferIdx].GetAttributes().size() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].elements.size()); - assert(mMeshObject.m_VertexBuffers[bufferIdx].GetSpan() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].span); - } - else - { - // Instance rate data (ie the instance buffer) - assert(bufferIdx == -1); - assert(mVertexInstanceBuffer.has_value()); - passVertexBuffers.push_back(mVertexInstanceBuffer->GetVkBuffer()); - } - } - std::vector passVertexBufferOffsets(passVertexBuffers.size(), 0); - - // Index buffer is optional - VkBuffer indexBuffer = VK_NULL_HANDLE; - VkIndexType indexBufferType = VK_INDEX_TYPE_MAX_ENUM; - size_t indexCount = 0; - if (mMeshObject.m_IndexBuffer) - { - indexBuffer = mMeshObject.m_IndexBuffer->GetVkBuffer(); - indexBufferType = mMeshObject.m_IndexBuffer->GetVkIndexType(); - indexCount = mMeshObject.m_IndexBuffer->GetNumIndices(); - } - - // Indirect Draw buffer is optional - VkBuffer drawIndirectBuffer = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetVkBuffer() : VK_NULL_HANDLE; - uint32_t drawIndirectCount = mDrawIndirectBuffer.has_value() ? (uint32_t)mDrawIndirectBuffer->GetNumDraws() : 0; - uint32_t drawIndirectOffset = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetBufferOffset() : 0; - // Indirect Draw Count (count buffer) set to be the beginning of the drawIndirectBuffer IF there is an offset in the mDrawIndirectBuffer. - VkBuffer drawIndirectCountBuffer = drawIndirectOffset>0 ? drawIndirectBuffer : VK_NULL_HANDLE; - - // Pipeline layout may come from the shaderPass or (if that fails) from the materialPass (if it was created late because of 'dynamic' descriptor set layout). - VkPipelineLayout pipelineLayout = shaderPass.GetPipelineLayout().GetVkPipelineLayout(); - if (pipelineLayout == VK_NULL_HANDLE) - pipelineLayout = pMaterialPass->GetPipelineLayout().GetVkPipelineLayout(); - - // add the DrawablePass - DrawablePass& pass = mPasses.emplace_back( *pMaterialPass, - (VkPipeline) VK_NULL_HANDLE, - pipelineLayout, - pMaterialPass->GetVkDescriptorSets(), - shaderPass.GetPipelineVertexInputState(), - DrawablePassVertexBuffers { std::move( passVertexBuffers ), - std::move( passVertexBufferOffsets ) }, - indexBuffer, - indexBufferType, - drawIndirectBuffer, - drawIndirectCountBuffer, - (uint32_t)mMeshObject.m_NumVertices, - (uint32_t)indexCount, - (uint32_t)drawIndirectCount, - (uint32_t)drawIndirectOffset, - passIdx - ); - VkShaderModule vkVertShader = VK_NULL_HANDLE; - VkShaderModule vkFragShader = VK_NULL_HANDLE; - VkShaderModule vkMeshShader = VK_NULL_HANDLE; - VkShaderModule vkTaskShader = VK_NULL_HANDLE; - - std::visit( [&](auto& m) - { - using T = std::decay_t; - if constexpr (std::is_same_v>) - { - vkVertShader = m.vert.GetVkShaderModule(); - vkFragShader = m.frag.GetVkShaderModule(); - } - else if constexpr (std::is_same_v>) - { - vkMeshShader = m.mesh.GetVkShaderModule(); - vkFragShader = m.frag.GetVkShaderModule(); - } - else if constexpr (std::is_same_v>) - { - vkTaskShader = m.task.GetVkShaderModule(); - vkMeshShader = m.mesh.GetVkShaderModule(); - vkFragShader = m.frag.GetVkShaderModule(); - } - else if constexpr (std::is_same_v>) - { - vkVertShader = m.vert.GetVkShaderModule(); - } - else - { - assert( 0 ); // unsupported shader module type (eg ComputeShaderModule) - } - }, shaderPass.m_shaders.m_modules ); - - if (!mVulkan.CreatePipeline(mVulkan.GetPipelineCache(), - &pass.mPipelineVertexInputState.GetVkPipelineVertexInputStateCreateInfo(), - pass.mPipelineLayout, - vkRenderPasses[passIdx], - subpasses.empty() ? 0 :subpasses[passIdx], - &rs, - &ds, - BlendStates.empty() ? nullptr : &cb, - &ms, - {},//dynamic states - nullptr, nullptr, - vkTaskShader, - vkMeshShader, - vkVertShader, - vkFragShader, - pMaterialPass->GetSpecializationConstants().GetVkSpecializationInfo(), - false, - VK_NULL_HANDLE, - &pass.mPipeline, - ia_custom)) - { - // Error - return false; - } - mVulkan.SetDebugObjectName(pass.mPipeline, pMaterialPass->mShaderPass.m_shaderPassDescription.m_vertexName.c_str()); - } - } - return true; -} - -bool Drawable::InitMeshShader(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::optional drawIndirectBuffer, std::span passMultisample, std::span subpasses, int nodeId) -{ - mMeshObject = {}; - mVertexInstanceBuffer = {}; - mDrawIndirectBuffer = std::move(drawIndirectBuffer); - mNodeId = nodeId; - - return ReInitMeshShader(vkRenderPasses, passNames, passMask, passMultisample, subpasses); -} -bool Drawable::ReInitMeshShader( std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses ) -{ - assert( passMultisample.empty() || passMultisample.size() == vkRenderPasses.size() ); - mPassMask = passMask; - mPassNameToIndex.clear(); - for (auto& pass : mPasses) - { - vkDestroyPipeline(mVulkan.m_VulkanDevice, pass.mPipeline, nullptr); - pass.mPipeline = VK_NULL_HANDLE; - } - mPasses.clear(); - - const auto& shader = mMaterial.m_shader; - mPasses.reserve(shader.GetShaderPasses().size()); - for (uint32_t passIdx = 0; passIdx < sizeof(passMask) * 8 && passMask != 0; ++passIdx) - { - bool passMaskSet = ((passMask & 1) != 0); - passMask >>= 1; - if (passMaskSet) - { - // LOGI("Creating Mesh Object PipelineState and Pipeline for pass... %s", passNames[passIdx]); - MaterialPass* pMaterialPass = const_cast(mMaterial.GetMaterialPass(passNames[passIdx])); - if (!pMaterialPass) - { - LOGE(" Pass %s does not exist in shader", passNames[passIdx]); - continue; - } - assert(pMaterialPass); - const auto& shaderPass = pMaterialPass->mShaderPass; - - // Common to all pipelines - // State for rasterization, such as polygon fill mode is defined. - VkPipelineRasterizationStateCreateInfo rs {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; - const auto& fixedFunctionSettings = shaderPass.m_shaderPassDescription.m_fixedFunctionSettings; - rs.polygonMode = VK_POLYGON_MODE_FILL; - rs.cullMode = (fixedFunctionSettings.cullBackFace?VK_CULL_MODE_BACK_BIT:0) | (fixedFunctionSettings.cullFrontFace ? VK_CULL_MODE_FRONT_BIT : 0); - rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rs.depthClampEnable = fixedFunctionSettings.depthClampEnable ? VK_TRUE : VK_FALSE; - rs.rasterizerDiscardEnable = VK_FALSE; - rs.depthBiasEnable = fixedFunctionSettings.depthBiasEnable ? VK_TRUE : VK_FALSE; - if (fixedFunctionSettings.depthBiasEnable) - { - rs.depthBiasConstantFactor = fixedFunctionSettings.depthBiasConstant; - rs.depthBiasClamp = fixedFunctionSettings.depthBiasClamp; - rs.depthBiasSlopeFactor = fixedFunctionSettings.depthBiasSlope; - } - rs.lineWidth = 1.0f; - - const auto& outputSettings = shaderPass.m_shaderPassDescription.m_outputs; - - // Setup blending/transparency - std::vector BlendStates; - BlendStates.reserve(outputSettings.size()); - for (const auto& outputSetting : outputSettings) - { - VkPipelineColorBlendAttachmentState& cb = BlendStates.emplace_back(VkPipelineColorBlendAttachmentState{}); - if (outputSetting.blendEnable) - { - cb.blendEnable = VK_TRUE; - cb.srcColorBlendFactor = BlendFactorToVk(outputSetting.srcColorBlendFactor); - cb.dstColorBlendFactor = BlendFactorToVk(outputSetting.dstColorBlendFactor); - cb.colorBlendOp = VK_BLEND_OP_ADD; - cb.srcAlphaBlendFactor = BlendFactorToVk(outputSetting.srcAlphaBlendFactor); - cb.dstAlphaBlendFactor = BlendFactorToVk(outputSetting.dstAlphaBlendFactor); - cb.alphaBlendOp = VK_BLEND_OP_ADD; - } - cb.colorWriteMask = outputSetting.colorWriteMask & (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); - } - - VkPipelineColorBlendStateCreateInfo cb = {}; - cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - cb.attachmentCount = (uint32_t)BlendStates.size(); - cb.pAttachments = BlendStates.data(); - - // Setup depth testing - VkPipelineDepthStencilStateCreateInfo ds = {}; - ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - ds.depthTestEnable = fixedFunctionSettings.depthTestEnable ? VK_TRUE : VK_FALSE; - ds.depthWriteEnable = fixedFunctionSettings.depthWriteEnable ? VK_TRUE : VK_FALSE; - switch( fixedFunctionSettings.depthCompareOp ) { - case ShaderPassDescription::DepthCompareOp::LessEqual: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - case ShaderPassDescription::DepthCompareOp::Equal: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - case ShaderPassDescription::DepthCompareOp::Greater: - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - break; - } - ds.depthBoundsTestEnable = VK_FALSE; - ds.back.failOp = VK_STENCIL_OP_KEEP; - ds.back.passOp = VK_STENCIL_OP_KEEP; - ds.back.compareOp = VK_COMPARE_OP_ALWAYS; - ds.stencilTestEnable = VK_FALSE; - ds.front = ds.back; - - // Setup (multi) sampling - VkSampleMask sampleMask = 0; - VkPipelineMultisampleStateCreateInfo ms = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; - - ms.rasterizationSamples = passMultisample.empty() ? VK_SAMPLE_COUNT_1_BIT : passMultisample[passIdx]; - const auto& sampleShadingSettings = shaderPass.m_shaderPassDescription.m_sampleShadingSettings; - ms.sampleShadingEnable = sampleShadingSettings.sampleShadingEnable; - if (sampleShadingSettings.sampleShadingMask != 0) - { - assert(ms.rasterizationSamples <= VK_SAMPLE_COUNT_32_BIT ); // sampleMask is only 32bits currently! Easy fix if we want > 32x MSAA - sampleMask = sampleShadingSettings.sampleShadingMask & ((1 << ms.rasterizationSamples) -1); - ms.pSampleMask = &sampleMask; - } - - VkPipelineSampleLocationsStateCreateInfoEXT msLocations = { VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT }; - if( sampleShadingSettings.forceCenterSample ) - { - msLocations.sampleLocationsEnable = VK_TRUE; - msLocations.sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT; - msLocations.sampleLocationsInfo.sampleLocationsPerPixel = ms.rasterizationSamples; - msLocations.sampleLocationsInfo.sampleLocationsCount = (uint32_t) ms.rasterizationSamples; - msLocations.sampleLocationsInfo.sampleLocationGridSize = { 1,1 }; - std::vector msSampleLocations( msLocations.sampleLocationsInfo.sampleLocationsCount, { 0.5f,0.5f } ); - msLocations.sampleLocationsInfo.pSampleLocations = msSampleLocations.data(); - ms.pNext = &msLocations; - } - - mPassNameToIndex.try_emplace(passNames[passIdx], (uint32_t)mPasses.size()); // add the lookup (in to mPasses) - - VkBuffer indexBuffer = VK_NULL_HANDLE; - VkIndexType indexBufferType = VK_INDEX_TYPE_MAX_ENUM; - - // Indirect Draw buffer is optional - VkBuffer drawIndirectBuffer = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetVkBuffer() : VK_NULL_HANDLE; - uint32_t drawIndirectCount = mDrawIndirectBuffer.has_value() ? (uint32_t)mDrawIndirectBuffer->GetNumDraws() : 0; - uint32_t drawIndirectOffset = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetBufferOffset() : 0; - // Indirect Draw Count (count buffer) set to be the beginning of the drawIndirectBuffer IF there is an offset in the mDrawIndirectBuffer. - VkBuffer drawIndirectCountBuffer = drawIndirectOffset>0 ? drawIndirectBuffer : VK_NULL_HANDLE; - - // Pipeline layout may come from the shaderPass or (if that fails) from the materialPass (if it was created late because of 'dynamic' descriptor set layout). - VkPipelineLayout pipelineLayout = shaderPass.GetPipelineLayout().GetVkPipelineLayout(); - if (pipelineLayout == VK_NULL_HANDLE) - pipelineLayout = pMaterialPass->GetPipelineLayout().GetVkPipelineLayout(); - - // add the DrawablePass - DrawablePass& pass = mPasses.emplace_back( *pMaterialPass, - (VkPipeline) VK_NULL_HANDLE, - pipelineLayout, - pMaterialPass->GetVkDescriptorSets(), - shaderPass.GetPipelineVertexInputState(), - DrawablePassVertexBuffers { }, //DrawablePassVertexBuffers { std::move( passVertexBuffers ), std::move( passVertexBufferOffsets ) }, - VK_NULL_HANDLE, //indexBuffer, - indexBufferType, - drawIndirectBuffer, - drawIndirectCountBuffer, - 0, //(uint32_t)mMeshObject.m_NumVertices, - 0, //(uint32_t)indexCount, - (uint32_t)drawIndirectCount, - (uint32_t)drawIndirectOffset, - passIdx - ); - VkShaderModule vkVertShader = VK_NULL_HANDLE; - VkShaderModule vkFragShader = VK_NULL_HANDLE; - VkShaderModule vkMeshShader = VK_NULL_HANDLE; - VkShaderModule vkTaskShader = VK_NULL_HANDLE; - - std::visit( [&](auto& m) - { - using T = std::decay_t; - if constexpr (std::is_same_v>) - { - vkMeshShader = m.mesh.GetVkShaderModule(); - vkFragShader = m.frag.GetVkShaderModule(); - } - else if constexpr (std::is_same_v>) - { - vkTaskShader = m.task.GetVkShaderModule(); - vkMeshShader = m.mesh.GetVkShaderModule(); - vkFragShader = m.frag.GetVkShaderModule(); - } - else - { - assert( 0 ); // unsupported shader module type (eg ComputeShaderModule, VertexShaderModule, ...) - } - }, shaderPass.m_shaders.m_modules ); - - if (!mVulkan.CreatePipeline(mVulkan.GetPipelineCache(), - &pass.mPipelineVertexInputState.GetVkPipelineVertexInputStateCreateInfo(), - pass.mPipelineLayout, - vkRenderPasses[passIdx], - subpasses.empty() ? 0 :subpasses[passIdx], - &rs, - &ds, - BlendStates.empty() ? nullptr : &cb, - &ms, - {},//dynamic states - nullptr, nullptr, - vkTaskShader, - vkMeshShader, - VK_NULL_HANDLE, // no vertex shader vkVertShader, - vkFragShader, - pMaterialPass->GetSpecializationConstants().GetVkSpecializationInfo(), - false, - VK_NULL_HANDLE, - &pass.mPipeline)) - { - // Error - return false; - } - mVulkan.SetDebugObjectName(pass.mPipeline, pMaterialPass->mShaderPass.m_shaderPassDescription.m_vertexName.c_str()); - } - } - return true; -} - -void Drawable::DrawPass(VkCommandBuffer cmdBuffer, const DrawablePass& drawablePass, uint32_t bufferIdx, const std::span vertexBufferOverrides) const -{ - // Bind the pipeline for this material - vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, drawablePass.mPipeline); - - // Bind everything the shader needs - if (!drawablePass.mDescriptorSet.empty()) - { - VkDescriptorSet vkDescriptorSet = drawablePass.mDescriptorSet.size() >= 1 ? drawablePass.mDescriptorSet[bufferIdx] : drawablePass.mDescriptorSet[0]; - vkCmdBindDescriptorSets(cmdBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - drawablePass.mPipelineLayout, - 0, - 1, - &vkDescriptorSet, - 0, - NULL); - } - - const auto& shaderPassDescription = drawablePass.mMaterialPass.mShaderPass.m_shaderPassDescription; - if (!shaderPassDescription.m_meshName.empty() && !shaderPassDescription.m_taskName.empty()) // Has a mesh shader (maybe not the ideal way to test this) - { - auto* meshExtension = mVulkan.GetExtension(); - if (!meshExtension || meshExtension->Status != VulkanExtensionStatus::eLoaded) - assert( 0 && "mesh shader extension not loader or supported" ); - else - { - - meshExtension->m_vkCmdDrawMeshTasksEXT(cmdBuffer, mDispatchGroupCount[0], mDispatchGroupCount[1], mDispatchGroupCount[2]); - return; - } - } - else if (!shaderPassDescription.m_meshName.empty() && shaderPassDescription.m_taskName.empty()) // Has a mesh shader (maybe not the ideal way to test this) - { - auto* meshExtension = mVulkan.GetExtension(); - if (!meshExtension || meshExtension->Status != VulkanExtensionStatus::eLoaded) - assert( 0 && "mesh shader extension not loader or supported" ); - else - { - if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) - { - if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) - { - // Draw the mesh using Mesh indirect count buffer (vkCmdDrawMeshTasksIndirectCountEXT command) - assert(meshExtension->m_vkCmdDrawMeshTasksIndirectCountEXT != nullptr); - meshExtension->m_vkCmdDrawMeshTasksIndirectCountEXT( - cmdBuffer, - drawablePass.mDrawIndirectBuffer, - drawablePass.mDrawIndirectOffset, - drawablePass.mDrawIndirectCountBuffer, - 0, - drawablePass.mNumDrawIndirect, - sizeof(VkDrawIndexedIndirectCommand)); - } - else - { - // Draw the mesh using Mesh indirect buffer (vkCmdDrawMeshTasksIndirectEXT) - assert(meshExtension->m_vkCmdDrawMeshTasksIndirectEXT!= nullptr); - meshExtension->m_vkCmdDrawMeshTasksIndirectEXT( - cmdBuffer, - drawablePass.mDrawIndirectBuffer, - drawablePass.mDrawIndirectOffset, - drawablePass.mNumDrawIndirect, - sizeof(VkDrawIndexedIndirectCommand)); - } - } - } - // RETURN here if mesh shader - return; - } - - const auto& vertexBuffers = vertexBufferOverrides.empty() ? drawablePass.mVertexBuffers : vertexBufferOverrides[bufferIdx % vertexBufferOverrides.size()]; - - if (!vertexBuffers.mVertexBuffers.empty()) - { - // Bind mesh vertex/instance buffer(s) - vkCmdBindVertexBuffers(cmdBuffer, - 0, - (uint32_t)vertexBuffers.mVertexBuffers.size(), - vertexBuffers.mVertexBuffers.data(), - vertexBuffers.mVertexBufferOffsets.data()); - } - if (!vertexBufferOverrides.empty()) - { - assert(vertexBuffers.mVertexBuffers.size() == vertexBuffers.mVertexBufferOffsets.size()); - assert(vertexBufferOverrides.empty() || vertexBuffers.mVertexBuffers.size() == drawablePass.mVertexBuffers.mVertexBuffers.size()); - } - - if (drawablePass.mIndexBuffer != VK_NULL_HANDLE) - { - assert( drawablePass.mIndexBufferType != VK_INDEX_TYPE_MAX_ENUM ); - - // Bind index buffer data - vkCmdBindIndexBuffer(cmdBuffer, - drawablePass.mIndexBuffer, - 0, - drawablePass.mIndexBufferType); - - if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) - { - if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) - { - // Draw the mesh using draw indirect count buffer (VkDrawIndexedIndirectCount command) - const auto* drawIndirectCountExt = mVulkan.GetExtension(); - assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr); - drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); - } - else - { - // Draw the mesh using draw indirect buffer (VkDrawIndexedIndirectCommand) - vkCmdDrawIndexedIndirect(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); - } - } - else - { - // Everything is set up, draw the mesh - vkCmdDrawIndexed(cmdBuffer, drawablePass.mNumIndices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0, 0); - } - } - else - { - if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) - { - if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) - { - // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) - const auto* drawIndirectCountExt = mVulkan.GetExtension(); - assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr ); - drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); - } - else - { - // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) - vkCmdDrawIndirect(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); - } - } - else - { - // Draw the mesh without index buffer - vkCmdDraw(cmdBuffer, drawablePass.mNumVertices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0); - } - } -} - -bool DrawableLoader::LoadDrawables(Vulkan& vulkan, AssetManager& assetManager, std::span vkRenderPasses, const char* const* renderPassNames, const std::string& meshFilename, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, std::span renderPassMultisample, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, std::span renderPassSubpasses, const glm::vec3 globalScale) -{ - LOGI("Loading Object mesh: %s...", meshFilename.c_str()); - - std::vector fatObjects; - if (meshFilename.size() > 4 && meshFilename.substr(meshFilename.size() - 4) == std::string(".obj")) - { - // Load .obj file - fatObjects = MeshObjectIntermediate::LoadObj(assetManager, meshFilename); - } - else - { - // Load .gltf file - fatObjects = MeshObjectIntermediate::LoadGLTF(assetManager, meshFilename, (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0, globalScale); - } - if (fatObjects.size() == 0) - { - LOGE("Error loading Object mesh: %s", meshFilename.c_str()); - return false; - } - // Print some debug - DrawableLoader::PrintStatistics(fatObjects); - - // Turn the intermediate mesh objects into Drawables (and load the materials) - if (!CreateDrawables(vulkan, std::move(fatObjects), vkRenderPasses, renderPassNames, materialLoader, drawables, renderPassMultisample, loaderFlags, renderPassSubpasses)) - { - LOGE("Error initializing Drawable: %s", meshFilename.c_str()); - return false; - } - return true; // success -} - -bool DrawableLoader::CreateDrawables(Vulkan& vulkan, std::vector&& intermediateMeshObjects, std::span vkRenderPasses, const char* const* renderPassNames, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, const std::span renderPassMultisample, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const std::span renderPassSubpasses) -{ - // See if we can find instances, we assume there is no instance information in the gltf! - auto instancedFatObjects = (loaderFlags & LoaderFlags::FindInstances) ? MeshInstanceGenerator::FindInstances(std::move(intermediateMeshObjects)) : MeshInstanceGenerator::NullFindInstances(std::move(intermediateMeshObjects)); - intermediateMeshObjects.clear(); - - return CreateDrawables(vulkan, std::move(instancedFatObjects), vkRenderPasses, renderPassNames, materialLoader, drawables, renderPassMultisample, loaderFlags, renderPassSubpasses); -} - -bool DrawableLoader::CreateDrawables(Vulkan& vulkan, std::vector&& intermediateMeshInstances, std::span vkRenderPasses, const char* const* renderPassNames, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, const std::span renderPassMultisample, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const std::span renderPassSubpasses) -{ - drawables.reserve(intermediateMeshInstances.size() ); - for (auto& [fatObject, instances] : intermediateMeshInstances) - { - // Get the material for this mesh - std::optional material; - if (fatObject.m_Materials.size() == 0) - { - // default material (materialId = 0) - auto loadedMaterial = materialLoader( MeshObjectIntermediate::MaterialDef{ "", 0, "textures/white_d.ktx"}); - if (loadedMaterial.has_value()) - material.emplace( std::move(loadedMaterial.value()) ); - } - else - { - if (fatObject.m_Materials.size() > 1) - { - LOGE(" Drawable loader does not support per-face materials, using first material"); - } - auto loadedMaterial = materialLoader( fatObject.m_Materials[0] ); - if (loadedMaterial.has_value()) - material.emplace( std::move(loadedMaterial.value()) ); - } - if (!material) - { - // It is valid for the material loader to return a null material - denotes we dont want to render this object! - continue; - } - const auto& shader = material->m_shader; - - uint32_t passMask = 0; // max 32 passes! - const ShaderPass* pFirstPass = nullptr; - for (uint32_t pass = 0; pass < vkRenderPasses.size(); ++pass) - { - const char* pRenderPassName = renderPassNames[pass]; - const auto* pShaderPass = shader.GetShaderPass(pRenderPassName); ///TODO: std::string generated here! - if (pShaderPass) - { - passMask |= 1 << pass; - } - if (!pFirstPass) - pFirstPass = pShaderPass; - } - - // If this drawable was in one or more passes (hopefully it was used for something) then initialize them - if (pFirstPass) - { - ///TODO: implement having different bindings and packing for different passes - - // Do the (optional) transform baking before creating the device mesh. - if (true && (loaderFlags & LoaderFlags::BakeTransforms) != 0) - { - if ((instances.size() > 1) || ((loaderFlags & LoaderFlags::FindInstances) != 0)) - { - // When we are using instancing dont bake the transform into the mesh - apply the transform to each of the instance transforms. - // It is quite likely the fatObject.m_Transform matrix will be 'identity' as it should have already been applied while instances were being found. - glm::mat4x3 objectTransform4x3 = fatObject.m_Transform; // convert 4x4 to 4x3 (keeps rotation and translation, lost column is unimportant if the transform is a simple TRS (translation/rotation/scale) matrix - for (MeshObjectIntermediate::FatInstance& instance : instances) - { - instance.transform = instance.transform * objectTransform4x3; - } - fatObject.m_Transform = glm::identity(); - fatObject.m_NodeId = -1; // object (may) point to multiple instances so m_NodeId is likely not valid at the mesh level - } - else - { - // Bake the object transform down into the mesh (instances[0] may well be identity but apply it to the transform incase it is not). - glm::mat4x3 combinedTransform = glm::transpose(instances[0].transform * glm::mat4x3(fatObject.m_Transform)); - fatObject.m_Transform = combinedTransform; - instances[0].transform = glm::identity(); - fatObject.BakeTransform(); - } - } - - const auto nodeId = fatObject.m_NodeId; // grab the nodeId before it goes away! - - Mesh meshObject; - const auto& vertexFormats = shader.m_shaderDescription->m_vertexFormats; - MeshHelper::CreateMesh(vulkan.GetMemoryManager(), fatObject, (uint32_t)pFirstPass->m_shaderPassDescription.m_vertexFormatBindings[0], vertexFormats, &meshObject); - - // We are done with the FatObject here, Release it to save some memory earlier. - fatObject.Release(); - - // Potentially the vertex format has some Instance bindings in here (will have been ignored by the CreateMesh). - std::optional vertexInstanceBuffer; - const auto instanceFormatIt = std::find_if(vertexFormats.cbegin(), vertexFormats.cend(), - [](const VertexFormat& f) { return f.inputRate == VertexFormat::eInputRate::Instance; }); - if (instanceFormatIt != vertexFormats.cend()) - { - if (instanceFormatIt != vertexFormats.cbegin() && instanceFormatIt != vertexFormats.end()-1) - { - LOGE(" Drawable loader (currently) only suports shaders with instance rate 'vertex' buffers at the beginning or end of their vertex layout"); - return false; - } - // Create the instance data - std::span instancesSpan = instances; - if( instancesSpan.empty() ) - { - // Even if we are not instancing there should be one instance per mesh - LOGE(" Drawable loader expected mesh to have (at least) one instance matrix"); - return false; - } - - const std::vector formattedVertexData = MeshObjectIntermediate::CopyFatInstanceToFormattedBuffer(instancesSpan, *instanceFormatIt); - - if (!vertexInstanceBuffer.emplace().Initialize(&vulkan.GetMemoryManager(), instanceFormatIt->span, instancesSpan.size(), formattedVertexData.data())) - { - return false; - } - } - else - { - if( instances.size() > 1) - { - LOGE(" Drawable loader found instances - expects shaders vertex layout to have instance data support"); - return false; - } - } - - // Create the drawable - if (!drawables.emplace_back(vulkan, std::move(material.value())).Init(vkRenderPasses, renderPassNames, passMask, std::move(meshObject), std::move(vertexInstanceBuffer), std::nullopt, renderPassMultisample, renderPassSubpasses, nodeId)) - { - return false; - } - } - } - return true; -} - -DrawableLoader::MeshStatistics DrawableLoader::GatherStatistics(const std::span meshObjects) -{ - MeshStatistics stats; - stats.totalVerts = 0; - stats.boundingBoxMin = glm::vec3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); - stats.boundingBoxMax = glm::vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); - for (const auto& mesh : meshObjects) - { - stats.totalVerts += mesh.m_VertexBuffer.size(); - - for (const auto& OneFat : mesh.m_VertexBuffer) - { - stats.boundingBoxMin[0] = std::min(stats.boundingBoxMin[0], OneFat.position[0]); - stats.boundingBoxMin[1] = std::min(stats.boundingBoxMin[1], OneFat.position[1]); - stats.boundingBoxMin[2] = std::min(stats.boundingBoxMin[2], OneFat.position[2]); - - stats.boundingBoxMax[0] = std::max(stats.boundingBoxMax[0], OneFat.position[0]); - stats.boundingBoxMax[1] = std::max(stats.boundingBoxMax[1], OneFat.position[1]); - stats.boundingBoxMax[2] = std::max(stats.boundingBoxMax[2], OneFat.position[2]); - } - } - if (stats.totalVerts == 0) - { - stats.boundingBoxMin = glm::vec3(0.0f, 0.0f, 0.0f); - stats.boundingBoxMax = glm::vec3(0.0f, 0.0f, 0.0f); - } - return stats; -} - -void DrawableLoader::PrintStatistics(const std::span meshObjects) -{ - MeshStatistics stats = GatherStatistics(meshObjects); - LOGI("Model total Vertices: %zu", stats.totalVerts); - LOGI("Model Bounding Box: (%0.2f, %0.2f, %0.2f) -> (%0.2f, %0.2f, %0.2f)", stats.boundingBoxMin[0], stats.boundingBoxMin[1], stats.boundingBoxMin[2], stats.boundingBoxMax[0], stats.boundingBoxMax[1], stats.boundingBoxMax[2]); - LOGI("Model Extent: (%0.2f, %0.2f, %0.2f)", stats.boundingBoxMax[0] - stats.boundingBoxMin[0], stats.boundingBoxMax[1] - stats.boundingBoxMin[1], stats.boundingBoxMax[2] - stats.boundingBoxMin[2]); -} diff --git a/framework/code/material/drawable.hpp b/framework/code/material/drawable.hpp index 47e4d3b..6c84799 100644 --- a/framework/code/material/drawable.hpp +++ b/framework/code/material/drawable.hpp @@ -1,225 +1,233 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once -#include -#include -#include -#include -#include #include -#include "vulkan/vulkan.hpp" -#include "mesh/mesh.hpp" -#include "material/material.hpp" -#include "memory/vulkan/indexBufferObject.hpp" -#include "memory/vulkan/vertexBufferObject.hpp" -#include "memory/vulkan/drawIndirectBufferObject.hpp" -#include "mesh/meshIntermediate.hpp" -#include "vulkan/pipelineVertexInputState.hpp" +#include +#include "texture/textureFormat.hpp" //for Msaa // Forward Declarations -class Material; -template class ShaderT; -class Vulkan; -struct MeshInstance; - - -/// Collection of VkBuffers for vertex buffer stream(s) (including instance buffers). -/// @ingroup Material -struct DrawablePassVertexBuffers +class MaterialBase; +template class CommandList; +template class DrawablePass; +template class DrawIndirectBuffer; +template class Material; +template class Mesh; +template class Pipeline; +template class RenderContext; +template class RenderPass; +template class VertexBuffer; + + +class DrawablePassVertexBuffersBase +{}; + +template +class DrawablePassVertexBuffers : public DrawablePassVertexBuffersBase { - std::vector mVertexBuffers; - std::vector mVertexBufferOffsets; + static_assert(sizeof( DrawablePassVertexBuffers ) != sizeof( DrawablePassVertexBuffersBase )); // static assert if not specialized }; -/// Encapsulates a drawable pass. -/// Owns the Vulkan pipeline, descriptor sets required by that pipeline. References vertex/index buffers etc from the parent Drawable. -/// Users are expected to use Drawable (which contains a vector of DrawablePasses and is more 'user friendly'). -/// @ingroup Material -class DrawablePass -{ - DrawablePass(const DrawablePass&) = delete; - DrawablePass& operator=(const DrawablePass&) = delete; -public: - DrawablePass(DrawablePass&&) = default; - DrawablePass() = delete; - ~DrawablePass(); - DrawablePass(const MaterialPass& MaterialPass, - VkPipeline Pipeline, - VkPipelineLayout PipelineLayout, - std::vector DescriptorSet, - const PipelineVertexInputState& PipelineVertexInputState, - DrawablePassVertexBuffers VertexBuffers, - VkBuffer IndexBuffer, - VkIndexType IndexBufferType, - VkBuffer DrawIndirectBuffer, - VkBuffer DrawIndirectCountBuffer, - uint32_t NumVertices, - uint32_t NumIndices, - uint32_t NumDrawIndirect, - uint32_t DrawIndirectOffset, - uint32_t PassIdx - ) : mMaterialPass( MaterialPass ), mPipeline( Pipeline ), mPipelineLayout( PipelineLayout ), mDescriptorSet( DescriptorSet ), mPipelineVertexInputState( PipelineVertexInputState ), mVertexBuffers( VertexBuffers ), mIndexBuffer( IndexBuffer ), mIndexBufferType( IndexBufferType ), mDrawIndirectBuffer( DrawIndirectBuffer ), mDrawIndirectCountBuffer( DrawIndirectCountBuffer ), mNumVertices( NumVertices ), - mNumIndices( NumIndices), mNumDrawIndirect( NumDrawIndirect), mDrawIndirectOffset( DrawIndirectOffset), mPassIdx( PassIdx) - { - } - - const MaterialPass& mMaterialPass; - VkPipeline mPipeline = VK_NULL_HANDLE; // Owned by us - VkPipelineLayout mPipelineLayout; // Owned by shader - std::vector mDescriptorSet; // one per NUM_VULKAN_BUFFERS (double/triple buffering) - const PipelineVertexInputState& mPipelineVertexInputState; // contains vertex binding and attribute descriptions - DrawablePassVertexBuffers mVertexBuffers; // contains vkbuffer/offsets; one per mVertexBuffersLookup entry. May be vertex rate or instance rate. - VkBuffer mIndexBuffer = VK_NULL_HANDLE; - VkIndexType mIndexBufferType = VK_INDEX_TYPE_MAX_ENUM; - VkBuffer mDrawIndirectBuffer = VK_NULL_HANDLE; - VkBuffer mDrawIndirectCountBuffer = VK_NULL_HANDLE; - uint32_t mNumVertices; - uint32_t mNumIndices; - uint32_t mNumDrawIndirect; - uint32_t mDrawIndirectOffset; // if non zero offset mDrawIndirectBuffer by this - uint32_t mPassIdx; // index of the bit in Drawable::m_passMask -}; /// Encapsulates a drawable object, owns the material (multiple passes, descriptor sets, etc) and the mesh (vertex list). /// @ingroup Material +template class Drawable { - Drawable(const Drawable&) = delete; - Drawable& operator=(const Drawable&) = delete; + Drawable( const Drawable& ) = delete; + Drawable& operator=( const Drawable& ) = delete; + using CommandList = CommandList; + using DrawablePass = DrawablePass; + using DrawIndirectBuffer = DrawIndirectBuffer; + using DrawablePassVertexBuffers = DrawablePassVertexBuffers; + using Mesh = Mesh; + using Material = Material; + using Pipeline = Pipeline; + using RenderContext = RenderContext; + using RenderPass = RenderPass; + using VertexBuffer = VertexBuffer; public: - Drawable(Vulkan& vulkan, Material&& material); - Drawable(Drawable&&) noexcept; + Drawable( T_GFXAPI&, Material&& material ) noexcept; + Drawable( T_GFXAPI&, std::unique_ptr material ) noexcept; + Drawable( Drawable&& ) noexcept; ~Drawable(); - /// Initialize this Drawable with the given mesh and render passes. - bool Init(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::optional indirectDrawBuffer = std::nullopt, std::span passMultisample = {}, std::span subpasses = {}, int nodeId = -1); + /// Initialize this Drawable with the given mesh and single render pass. + bool Init( const RenderPass& renderPass, const Pipeline&, const std::string& passName, Mesh meshObject, std::optional vertexInstanceBuffer = std::nullopt, std::optional indirectDrawBuffer = std::nullopt, int nodeId = -1 ); - /// Initialize this Drawable with the given mesh and (single) render pass. - bool Init(VkRenderPass vkRenderPass, const char* passNames, Mesh meshObject, std::optional vertexInstanceBuffer = std::nullopt, std::optional indirectDrawBuffer = std::nullopt, const VkSampleCountFlagBits* const passMultisample = nullptr, const uint32_t* const subpasses = nullptr, int nodeId = -1); - /// Initialize this Drawable with the given mesh and render passes. - bool Init( std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, Mesh meshObject, std::optional vertexInstanceBuffer = std::nullopt, std::optional indirectDrawBuffer = std::nullopt, std::span passMultisample = {}, std::span subpasses = {}, int nodeId = -1); - /// Initialize this Drawable with the given mesh, (single) render pass, and raster information (triangles, lines, points, etc.). - bool Init(VkRenderPass vkRenderPass, const char* passName, Mesh meshObject, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom); - bool InitMeshShader( std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::optional indirectDrawBuffer = std::nullopt, std::span passMultisample = {}, std::span subpasses = {}, int nodeId = -1); + /// Initialize this Drawable with the given mesh and single render pass. + bool Init( const RenderContext& renderPass, Mesh meshObject, std::optional vertexInstanceBuffer = std::nullopt, std::optional indirectDrawBuffer = std::nullopt, int nodeId = -1 ); + /// Initialize this Drawable with the given mesh and render passes. + bool Init( std::span renderPasses, uint32_t passMask, Mesh meshObject, std::optional vertexInstanceBuffer = std::nullopt, std::optional indirectDrawBuffer = std::nullopt, int nodeId = -1 ); + /// Initialize this Drawable without a mesh + bool Init( std::span renderPasses, uint32_t passMask, std::optional indirectDrawBuffer = std::nullopt, int nodeId = -1 ); + /// Initialize the mesh shader variant of the pipeline. + bool InitMeshShader( std::span renderPasses, uint32_t passMask, std::optional indirectDrawBuffer = std::nullopt, int nodeId = -1); /// Re-initialize the drawable. Used internally by Init but can be used by the user when the render pass has been modified. - bool ReInit( VkRenderPass vkRenderPass, const char* passNames, const VkSampleCountFlagBits* const passMultisample, const uint32_t* const subpasses ); - /// Re-initialize the drawable. Used internally by Init but can be used by the user when the render pass has been modified. - bool ReInit( VkRenderPass vkRenderPass, const char* passNames, const VkSampleCountFlagBits* const passMultisample, const uint32_t* const subpasses, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom); - /// Re-initialize the drawable. Used internally by Init but can be used by the user when the render passes have been modified. - bool ReInit(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses); + bool ReInit( const RenderContext& renderPass ); /// Re-initialize the drawable. Used internally by Init but can be used by the user when the render passes have been modified. - bool ReInit(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses, VkPipelineInputAssemblyStateCreateInfo ia_custom, VkPipelineRasterizationStateCreateInfo rs_custom); - + bool ReInit( std::span renderPasses, uint32_t passMask ); - bool ReInitMeshShader(std::span vkRenderPasses, const char* const* passNames, uint32_t passMask, std::span passMultisample, std::span subpasses); + bool ReInitMeshShader(std::span renderPasses, uint32_t passMask); - /// Issues the Vulkan commands needed to draw this DrawablePass. + /// Issues the commands needed to draw this DrawablePass. /// Binds the pipeline, descriptor sets, vertex buffers, index buffers, and issues the appropriate vkCmdDraw* /// @param vertexBindingsOverride allows user to replace @DrawablePass::mVertexBuffers with their own. Span is for each 'bufferIdx' (can be size()==1 if all buffers bind the same). DrawablePassVertexBuffers contains multiple buffers so all mesh and instance streams are overridden. - void DrawPass(VkCommandBuffer cmdBuffer, const DrawablePass& drawablePass, uint32_t bufferIdx, const std::span vertexBuffersOverride = {}) const; + void DrawPass(CommandList& cmdList, const DrawablePass& drawablePass, uint32_t bufferIdx, const std::span vertexBuffersOverride = {} ) const; - const DrawablePass* GetDrawablePass(const std::string& passName) const; - const auto& GetDrawablePasses() const { return mPasses; }; - uint32_t GetPassMask() const { return mPassMask; } - const auto& GetMaterial() const { return mMaterial; } + const DrawablePass* GetDrawablePass( const std::string& passName ) const; + const auto& GetDrawablePasses() const { return mPasses; }; + uint32_t GetPassMask() const { return mPassMask; } + const auto& GetMaterial() const { return mMaterial; } const auto& GetMeshObject() const { return mMeshObject; } - const auto& GetInstances() const { return mVertexInstanceBuffer; } - const auto& GetDrawIndirectBuffer() const { return mDrawIndirectBuffer; } - const int GetNodeId() const { return mNodeId; } + auto& GetMeshObject() { return mMeshObject; } + const auto& GetInstances() const { return mVertexInstanceBuffer; } + const auto& GetDrawIndirectBuffer() const { return mDrawIndirectBuffer; } + const int GetNodeId() const { return mNodeId; } /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) void SetDispatchGroupCount(std::array count) { mDispatchGroupCount = count; } - const auto& GetDispatchGroupCount() const { return mDispatchGroupCount; } + const auto& GetDispatchGroupCount() const { return mDispatchGroupCount; } - // Need non const access to mMeshObject but don't want to break anyone using GetMeshObject() public: - Mesh mMeshObject; + Material mMaterial; + Mesh mMeshObject; protected: - Material mMaterial; + std::vector mPasses; + std::map mPassNameToIndex; // Index in to mpasses ///TODO: allow for generation of a list of these - so each pass can iterate through the passes easily + uint32_t mPassMask = 0; + int mNodeId = -1; // Identifier used by application to determine what this drawable is attached to, eg for attaching to animations. Not used by Drawable. + std::array mDispatchGroupCount{1u,1u,1u}; - std::array mDispatchGroupCount{ 1u,1u,1u }; + std::optional mVertexInstanceBuffer; + std::optional mDrawIndirectBuffer; - std::vector mPasses; - std::map mPassNameToIndex; // Index in to mpasses ///TODO: allow for generation of a list of these - so each pass can iterate through the passes easily - uint32_t mPassMask = 0; - int mNodeId = -1; // Identifier used by application to determine what this drawable is attached to, eg for attaching to animations. Not used by Drawable. + T_GFXAPI& mGfxApi; +}; - std::optional mVertexInstanceBuffer; - std::optional mDrawIndirectBuffer; - Vulkan& mVulkan; +class DrawablePassBase +{ + DrawablePassBase( const DrawablePassBase& ) = delete; + DrawablePassBase& operator=( const DrawablePassBase& ) = delete; +protected: + DrawablePassBase() = default; + DrawablePassBase& operator=( DrawablePassBase&& ) noexcept = default; + DrawablePassBase( DrawablePassBase&& ) noexcept = default; }; - -/// Wrapper class for LoadDrawables (user entry point for loading a drawable mesh object). +/// Encapsulates a drawable pass. +/// We require that this template is specialized by the graphics api (and this class not used as-is, in it's non specialized form) /// @ingroup Material -class DrawableLoader +template +class DrawablePass : public DrawablePassBase { - DrawableLoader(const DrawableLoader&) = delete; - DrawableLoader& operator=(const DrawableLoader&) = delete; + DrawablePass( const DrawablePass& ) = delete; + DrawablePass& operator=( const DrawablePass& ) = delete; public: - /// @brief Flags controlling the behaviour of LoadDrawables - enum LoaderFlags : uint32_t { - None = 0, - FindInstances = 0x1, // useInstancing pass true if drawable loader should try to find duplicated instances of meshes(same MaterialDef, same vertex uv sets, vertex positions onlly differing by rotation and translation). Can take a little time to process. - BakeTransforms = 0x2, // bake world transform in to mesh data (and clear the m_Transform for all baked drawables) - IgnoreHierarchy = 0x4 // Ignore the gltf node hierarchy when loading model - }; - - /// @brief Load a mesh object and create the @Drawable(s) for rendering it. - /// This is the recommended way of loading meshes in to the Framework Material system. - /// @param vkRenderPasses span of Vulkan render passes that we may want to create DrawablePasses for (can be duplicated if we have subpasses) - /// @param renderPassNames names of the render passes, expected to match the names inside the Material - /// @param meshFilename name of the mesh filename to load (via assetManager) - /// @param materialLoader user supplied function that returns a Material for each mesh material (@ref MeshObjectIntermediate::MaterialDef) that the loaded mesh returns. Meshes can have multiple materials (possibly hundreds). - /// @param drawables output vector of @Drawable objects - /// @param renderPassMultisample optional multisample flags (if zero size assume no multisampling) - /// @param loaderFlags loader feature enables - /// @param renderPassSubpasses subpass indices for each render pass (0 for first subpass of if there are no subpasses). If empty treat everything as using subpass 0 - /// @param globalScale global scale applied to every loaded Drawable object - /// @return true on success - static bool LoadDrawables(Vulkan& vulkan, AssetManager& assetManager, std::span vkRenderPasses, const char* const* renderPassNames, const std::string& meshFilename, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, std::span renderPassMultisample, /*LoaderFlags*/uint32_t loaderFlags, std::span renderPassSubpasses, const glm::vec3 globalScale = glm::vec3(1.0f,1.0f,1.0f)); - - /// @brief Create @Drawable(s) for rendering a given vector of @MeshObjectIntermediate objects. - /// This is the recommended way of creating meshes in the Framework Material system and is used by the LoadDrawables function. - /// @param intermediateMeshObjects vector of the (intermediate format) mesh objects we are going to make drawables from. CreateDrawables takes ownership of this data. - /// @param vkRenderPasses span of Vulkan render passes that we may want to create DrawablePasses for (can be duplicated if we have subpasses) - /// @param renderPassNames names of the render passes, expected to match the names inside the Material - /// @param meshFilename name of the mesh filename to load (via assetManager) - /// @param materialLoader user supplied function that returns a Material for each mesh material (@ref MeshObjectIntermediate::MaterialDef) that the loaded mesh returns. Meshes can have multiple materials (possibly hundreds). - /// @param drawables output vector of @Drawable objects - /// @param renderPassMultisample optional multisample flags (if zero size assume no multisampling) - /// @param loaderFlags loader feature enables - /// @param RenderPassSubpasses subpass indices for each render pass (0 for first subpass of if there are no subpasses). If empty treat everything as using subpass 0 - /// @return true on success - static bool CreateDrawables(Vulkan& vulkan, std::vector&& intermediateMeshObjects, std::span vkRenderPasses, const char* const* renderPassNames, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, const std::span renderPassMultisample, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const std::span renderPassSubpasses); - - /// @brief Create @Drawables() for rendering the ficen @MeshInstance objects. - /// Identical to CreateDrawables but does not generate the MeshInstance data (is required to be already generated). - static bool CreateDrawables(Vulkan& vulkan, std::vector&& intermediateMeshInstances, std::span vkRenderPasses, const char* const* renderPassNames, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, const std::span renderPassMultisample, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const std::span renderPassSubpasses); - - /// @brief Print some combined statistics about the given meshObjects. - /// @param meshObjects span of the objects we want to gather the statistics for. - static void PrintStatistics(const std::span meshObjects); - - struct MeshStatistics { - size_t totalVerts; - glm::vec3 boundingBoxMin; - glm::vec3 boundingBoxMax; - }; - - /// @brief Collect some combined statistics about the given meshObjects. - /// @param meshObjects span of the objects we want to gather the statistics for. - /// @returns @DrawableLoaderMeshStatistics with statistics for the given objects (combined). - static MeshStatistics GatherStatistics(const std::span meshObjects); + DrawablePass() = delete; + static_assert(sizeof( DrawablePass ) != sizeof( DrawablePassBase )); // static assert if not specialized }; + + +template +Drawable::Drawable( T_GFXAPI& gfxapi, Material&& material ) noexcept + : mMaterial( std::move( material ) ) + , mGfxApi( gfxapi ) +{ +} + +template +Drawable::Drawable( T_GFXAPI& gfxapi, std::unique_ptr material ) noexcept + : mMaterial( std::move(*apiCast(material.get()) ) ) + , mGfxApi( gfxapi ) +{ +} + +template +Drawable::Drawable( Drawable&& other ) noexcept + : mMaterial( std::move( other.mMaterial ) ) + , mGfxApi( other.mGfxApi ) + , mMeshObject( std::move( other.mMeshObject ) ) + , mPasses( std::move( other.mPasses ) ) + , mPassNameToIndex( std::move( other.mPassNameToIndex ) ) + , mPassMask( other.mPassMask ) + , mVertexInstanceBuffer( std::move( other.mVertexInstanceBuffer ) ) + , mDrawIndirectBuffer( std::move( other.mDrawIndirectBuffer ) ) +{ + other.mPassMask = 0; +} + +template +bool Drawable::Init( const RenderPass& renderPass, const Pipeline& pipeline, const std::string& passName, Mesh meshObject, std::optional vertexInstanceBuffer, std::optional indirectDrawBuffer, int nodeId ) +{ + return Init( RenderContext{renderPass.Copy(), pipeline.Copy(), {}, passName}, std::move( meshObject ), std::move( vertexInstanceBuffer ), std::move( indirectDrawBuffer ), nodeId ); +} + +template +bool Drawable::Init( const RenderContext& renderPass, Mesh meshObject, std::optional vertexInstanceBuffer, std::optional drawIndirectBuffer, int nodeId ) +{ + mMeshObject = std::move( meshObject ); + mVertexInstanceBuffer = std::move( vertexInstanceBuffer ); + mDrawIndirectBuffer = std::move( drawIndirectBuffer ); + mNodeId = nodeId; + return ReInit( renderPass ); +} + +template +bool Drawable::Init( std::span renderPasses, uint32_t passMask, Mesh meshObject, std::optional vertexInstanceBuffer, std::optional drawIndirectBuffer, int nodeId ) +{ + mMeshObject = std::move( meshObject ); + mVertexInstanceBuffer = std::move( vertexInstanceBuffer ); + mDrawIndirectBuffer = std::move( drawIndirectBuffer ); + mNodeId = nodeId; + return ReInit( renderPasses, passMask ); +} + +template +bool Drawable::Init( std::span renderPasses, uint32_t passMask, std::optional drawIndirectBuffer, int nodeId ) +{ + mMeshObject = {}; + mVertexInstanceBuffer = {}; + mDrawIndirectBuffer = std::move( drawIndirectBuffer ); + mNodeId = nodeId; + + return ReInit( renderPasses, passMask ); +} + +template +bool Drawable::InitMeshShader( std::span renderPasses, uint32_t passMask, std::optional drawIndirectBuffer, int nodeId ) +{ + mMeshObject = {}; + mVertexInstanceBuffer = {}; + mDrawIndirectBuffer = std::move( drawIndirectBuffer ); + mNodeId = nodeId; + + return ReInitMeshShader( renderPasses, passMask ); +} + +template +bool Drawable::ReInit( const RenderContext& renderPass ) +{ + return ReInit( {&renderPass,1}, 1); +} + +template +const DrawablePass* Drawable::GetDrawablePass( const std::string& passName ) const +{ + auto it = mPassNameToIndex.find( passName ); + if (it != mPassNameToIndex.end()) + { + return &mPasses[it->second]; + } + return nullptr; +} diff --git a/framework/code/material/drawableLoader.cpp b/framework/code/material/drawableLoader.cpp new file mode 100644 index 0000000..91dd04a --- /dev/null +++ b/framework/code/material/drawableLoader.cpp @@ -0,0 +1,52 @@ +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "drawableLoader.hpp" +#include "drawable.hpp" +#include "mesh/meshIntermediate.hpp" +#include "system/os_common.h" +#include +#include + + +DrawableLoaderBase::MeshStatistics DrawableLoaderBase::GatherStatistics(const std::span meshObjects) +{ + MeshStatistics stats; + stats.totalVerts = 0; + stats.boundingBoxMin = glm::vec3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); + stats.boundingBoxMax = glm::vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); + for (const auto& mesh : meshObjects) + { + stats.totalVerts += mesh.m_VertexBuffer.size(); + + for (const auto& OneFat : mesh.m_VertexBuffer) + { + stats.boundingBoxMin[0] = std::min(stats.boundingBoxMin[0], OneFat.position[0]); + stats.boundingBoxMin[1] = std::min(stats.boundingBoxMin[1], OneFat.position[1]); + stats.boundingBoxMin[2] = std::min(stats.boundingBoxMin[2], OneFat.position[2]); + + stats.boundingBoxMax[0] = std::max(stats.boundingBoxMax[0], OneFat.position[0]); + stats.boundingBoxMax[1] = std::max(stats.boundingBoxMax[1], OneFat.position[1]); + stats.boundingBoxMax[2] = std::max(stats.boundingBoxMax[2], OneFat.position[2]); + } + } + if (stats.totalVerts == 0) + { + stats.boundingBoxMin = glm::vec3(0.0f, 0.0f, 0.0f); + stats.boundingBoxMax = glm::vec3(0.0f, 0.0f, 0.0f); + } + return stats; +} + +void DrawableLoaderBase::PrintStatistics(const std::span meshObjects) +{ + MeshStatistics stats = GatherStatistics(meshObjects); + LOGI("Model total Vertices: %zu", stats.totalVerts); + LOGI("Model Bounding Box: (%0.2f, %0.2f, %0.2f) -> (%0.2f, %0.2f, %0.2f)", stats.boundingBoxMin[0], stats.boundingBoxMin[1], stats.boundingBoxMin[2], stats.boundingBoxMax[0], stats.boundingBoxMax[1], stats.boundingBoxMax[2]); + LOGI("Model Extent: (%0.2f, %0.2f, %0.2f)", stats.boundingBoxMax[0] - stats.boundingBoxMin[0], stats.boundingBoxMax[1] - stats.boundingBoxMin[1], stats.boundingBoxMax[2] - stats.boundingBoxMin[2]); +} diff --git a/framework/code/material/drawableLoader.hpp b/framework/code/material/drawableLoader.hpp new file mode 100644 index 0000000..48b81db --- /dev/null +++ b/framework/code/material/drawableLoader.hpp @@ -0,0 +1,360 @@ +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include "mesh/instanceGenerator.hpp" +#include "mesh/mesh.hpp" +#include "mesh/meshHelper.hpp" +#include "mesh/meshIntermediate.hpp" +#include "pipeline.hpp" +#include "system/glm_common.hpp" +#include "system/os_common.h" +#include "texture/textureFormat.hpp" + +// Forward Declarations +class AssetManager; +class MaterialBase; +struct MeshInstance; +class MeshObjectIntermediate; +template class Material; +template class Pipeline; +template class RenderContext; +template class RenderPass; +template class ShaderPass; + +//class Dx12; +//class MaterialBase; +//enum class Msaa; +//template class Shader; + +template class Drawable; +template class DrawableLoader; + + +/// Base (platform agnostic) wrapper class for loading drawables +class DrawableLoaderBase +{ + DrawableLoaderBase( const DrawableLoaderBase& ) = delete; + DrawableLoaderBase& operator=( const DrawableLoaderBase& ) = delete; + template using tApiDerived = DrawableLoader; // make apiCast work! +public: + /// @brief Flags controlling the behaviour of LoadDrawables + enum LoaderFlags : uint32_t { + None = 0, + FindInstances = 0x1, // useInstancing pass true if drawable loader should try to find duplicated instances of meshes(same MaterialDef, same vertex uv sets, vertex positions onlly differing by rotation and translation). Can take a little time to process. + BakeTransforms = 0x2, // bake world transform in to mesh data (and clear the m_Transform for all baked drawables) + IgnoreHierarchy = 0x4 // Ignore the gltf node hierarchy when loading model + }; + + /// @brief Print some combined statistics about the given meshObjects. + /// @param meshObjects span of the objects we want to gather the statistics for. + static void PrintStatistics( const std::span meshObjects ); + + struct MeshStatistics { + size_t totalVerts; + glm::vec3 boundingBoxMin; + glm::vec3 boundingBoxMax; + }; + + /// @brief Collect some combined statistics about the given meshObjects. + /// @param meshObjects span of the objects we want to gather the statistics for. + /// @returns @DrawableLoaderMeshStatistics with statistics for the given objects (combined). + static MeshStatistics GatherStatistics( const std::span meshObjects ); +}; + + +/// Wrapper class for LoadDrawables (user entry point for loading a drawable mesh object). +/// @ingroup Material +template +class DrawableLoader : public DrawableLoaderBase +{ + DrawableLoader( const DrawableLoader& ) = delete; + DrawableLoader& operator=(const DrawableLoader&) = delete; + using Drawable = Drawable; + using Material = Material; + using RenderContext = RenderContext; + using RenderPass = RenderPass; + using PipelineRasterizationState = PipelineRasterizationState; +public: + /// @brief Load a mesh object and create the @Drawable(s) for rendering it. + /// This is the recommended way of loading meshes in to the Framework MaterialBase system. + /// @param renderPasses span of render pass contexts that we may want to create DrawablePasses for (can be duplicated if we have subpasses) + /// @param meshFilename name of the mesh filename to load (via assetManager) + /// @param materialLoader user supplied function that returns a MaterialBase for each mesh material (@ref MeshObjectIntermediate::MaterialDef) that the loaded mesh returns. Meshes can have multiple materials (possibly hundreds). + /// @param drawables output vector of @Drawable objects + /// @param renderPassMultisample optional multisample flags (if zero size assume no multisampling) + /// @param loaderFlags loader feature enables + /// @param globalScale global scale applied to every loaded Drawable object + /// @return true on success + static bool LoadDrawables(T_GFXAPI&, AssetManager& assetManager, std::span renderPasses, const std::string& meshFilename, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale = glm::vec3(1.0f,1.0f,1.0f)); + static bool LoadDrawables(T_GFXAPI&, AssetManager& assetManager, std::span renderPasses, const std::string& meshFilename, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale = glm::vec3(1.0f,1.0f,1.0f)); + + /// @brief Load a mesh object and create the @Drawable(s) for rendering it. + /// Identical to LoadDrawables but for a single pass only (helper to save end-user from creating spans with a single entry) + static bool LoadDrawables(T_GFXAPI&, AssetManager& assetManager, const RenderContext& renderPass, const std::string& meshFilename, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale = glm::vec3( 1.0f, 1.0f, 1.0f ) ); + static bool LoadDrawables(T_GFXAPI&, AssetManager& assetManager, const RenderContext& renderPass, const std::string& meshFilename, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale = glm::vec3( 1.0f, 1.0f, 1.0f ) ); + + /// @brief Create @Drawable(s) for rendering a given vector of @MeshObjectIntermediate objects. + /// This is the recommended way of creating meshes in the Framework MaterialBase system and is used by the LoadDrawables function. + /// @param intermediateMeshObjects vector of the (intermediate format) mesh objects we are going to make drawables from. CreateDrawables takes ownership of this data. + /// @param renderPasses span of render passes that we may want to create DrawablePasses for (can be duplicated if we have subpasses) + /// @param renderPassNames names of the render passes, expected to match the names inside the MaterialBase + /// @param meshFilename name of the mesh filename to load (via assetManager) + /// @param materialLoader user supplied function that returns a MaterialBase for each mesh material (@ref MeshObjectIntermediate::MaterialDef) that the loaded mesh returns. Meshes can have multiple materials (possibly hundreds). + /// @param drawables output vector of @Drawable objects + /// @param renderPassMultisample optional multisample flags (if zero size assume no multisampling) + /// @param loaderFlags loader feature enables + /// @param RenderPassSubpasses subpass indices for each render pass (0 for first subpass of if there are no subpasses). If empty treat everything as using subpass 0 + /// @return true on success + static bool CreateDrawables(T_GFXAPI&, std::vector&& intermediateMeshObjects, std::span renderPasses, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags); + + /// @brief Create @Drawables() for rendering the given @MeshInstance objects. + /// Identical to CreateDrawables but does not generate the MeshInstance data (is required to be already generated). + static bool CreateDrawables(T_GFXAPI&, std::vector&& intermediateMeshInstances, std::span renderPasses, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags); + + /// @brief Create @Drawables() for rendering the given @MeshInstance objects. + /// Identical to CreateDrawables but for a single pass only (helper to save end-user from creating spans with a single entry) + static bool CreateDrawables(T_GFXAPI&, std::vector&& intermediateMeshObjects, const RenderContext& renderPass, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags); +}; + + +template +bool DrawableLoader::LoadDrawables( T_GFXAPI& gfxapi, AssetManager& assetManager, std::span renderPasses, const std::string& meshFilename, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale ) +{ + LOGI( "Loading Object mesh: %s...", meshFilename.c_str() ); + + std::vector fatObjects; + if (meshFilename.size() > 4 && meshFilename.substr( meshFilename.size() - 4 ) == std::string( ".obj" )) + { + // Load .obj file + fatObjects = MeshObjectIntermediate::LoadObj( assetManager, meshFilename ); + } + else + { + // Load .gltf file + fatObjects = MeshObjectIntermediate::LoadGLTF( assetManager, meshFilename, (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0, globalScale ); + } + if (fatObjects.size() == 0) + { + LOGE( "Error loading Object mesh: %s", meshFilename.c_str() ); + return false; + } + // Print some debug + DrawableLoader::PrintStatistics( fatObjects ); + + // Turn the intermediate mesh objects into Drawables (and load the materials) + if (!CreateDrawables( gfxapi, std::move( fatObjects ), renderPasses, materialLoader, drawables, loaderFlags )) + { + LOGE( "Error initializing Drawable: %s", meshFilename.c_str() ); + return false; + } + return true; // success +} + +template +bool DrawableLoader::LoadDrawables( T_GFXAPI& gfxapi, AssetManager& assetManager, std::span renderPasses, const std::string& meshFilename, const std::function(const MeshObjectIntermediate::MaterialDef&)>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale ) +{ + auto materialLoader2 = [&materialLoader]( const MeshObjectIntermediate::MaterialDef& materialDef ) -> std::optional + { + auto pMaterial = materialLoader( materialDef ); + if (pMaterial) + { + return {std::move( *static_cast(pMaterial.get()) )}; + } + else + { + return std::nullopt; + } + }; + return LoadDrawables(gfxapi, assetManager, renderPasses, meshFilename, materialLoader2, drawables, loaderFlags, globalScale ); +} + +template +bool DrawableLoader::LoadDrawables( T_GFXAPI& gfxApi, AssetManager& assetManager, const RenderContext& renderPass, const std::string& meshFilename, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale ) +{ + return DrawableLoader::LoadDrawables( gfxApi, assetManager, {&renderPass, 1}, meshFilename, materialLoader, drawables, loaderFlags, globalScale ); +} + +template +bool DrawableLoader::LoadDrawables( T_GFXAPI& gfxApi, AssetManager& assetManager, const RenderContext& renderPass, const std::string& meshFilename, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*LoaderFlags*/uint32_t loaderFlags, const glm::vec3 globalScale ) +{ + return DrawableLoader::LoadDrawables( gfxApi, assetManager, {&renderPass, 1}, meshFilename, materialLoader, drawables, loaderFlags, globalScale ); +} + +template +bool DrawableLoader::CreateDrawables( T_GFXAPI& gfxapi, std::vector&& intermediateMeshObjects, std::span renderPasses, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags ) +{ + // See if we can find instances, we assume there is no instance information in the gltf! + auto instancedFatObjects = (loaderFlags & LoaderFlags::FindInstances) ? MeshInstanceGenerator::FindInstances( std::move( intermediateMeshObjects ) ) : MeshInstanceGenerator::NullFindInstances( std::move( intermediateMeshObjects ) ); + intermediateMeshObjects.clear(); + + return CreateDrawables( gfxapi, std::move( instancedFatObjects ), renderPasses, materialLoader, drawables, loaderFlags ); +} + +template +bool DrawableLoader::CreateDrawables( T_GFXAPI& gfxapi, std::vector&& intermediateMeshInstances, std::span renderPasses, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags ) +{ + // Create a pipeline container for each render pass, dont create the underlying pipeline until we know it is needed + std::vector> pipelines; + pipelines.resize( renderPasses.size() ); + + drawables.reserve( intermediateMeshInstances.size() ); + for (auto& [fatObject, instances] : intermediateMeshInstances) + { + // Get the material for this mesh + std::optional material; + if (fatObject.m_Materials.size() == 0) + { + // default material (materialId = 0) + auto loadedMaterial = materialLoader( MeshObjectIntermediate::MaterialDef{"", 0, "textures/white_d.ktx"} ); + if (loadedMaterial.has_value()) + material.emplace( std::move( loadedMaterial.value() ) ); + } + else + { + if (fatObject.m_Materials.size() > 1) + { + LOGE( " Drawable loader does not support per-face materials, using first material" ); + } + auto loadedMaterial = materialLoader( fatObject.m_Materials[0] ); + if (loadedMaterial.has_value()) + material.emplace( std::move( loadedMaterial.value() ) ); + } + if (!material) + { + // It is valid for the material loader to return a null material - denotes we dont want to render this object! + continue; + } + const auto& shader = material->GetShader(); + + // Determine which passes are being used + uint32_t passMask = 0; // max 32 passes! + const ShaderPass* pFirstPass = nullptr; + for (uint32_t pass = 0; pass < renderPasses.size(); ++pass) + { + const char* pRenderPassName = renderPasses[pass].name.c_str(); + const auto* pShaderPass = shader.GetShaderPass( pRenderPassName ); ///TODO: std::string generated here! + if (pShaderPass) + { + passMask |= 1 << pass; + // Make sure we have pipeline for this pass + if (!pipelines[pass]) + { + const auto* materialPass = material->GetMaterialPass( pRenderPassName ); + assert( materialPass ); + + PipelineRasterizationState rasterizationState{pShaderPass->m_shaderPassDescription}; + pipelines[pass] = ::CreatePipeline( gfxapi, + pShaderPass->m_shaderPassDescription, + pShaderPass->GetPipelineLayout(), + pShaderPass->GetPipelineVertexInputState(), + rasterizationState, + materialPass->GetSpecializationConstants(), + pShaderPass->m_shaders, + renderPasses[pass], + Msaa::Samples1 ); + } + } + if (!pFirstPass) + pFirstPass = pShaderPass; + } + + // If this drawable was in one or more passes (hopefully it was used for something) then initialize them + if (pFirstPass) + { + ///TODO: implement having different bindings and packing for different passes + + // Do the (optional) transform baking before creating the device mesh. + if (true && (loaderFlags & LoaderFlags::BakeTransforms) != 0) + { + if ((instances.size() > 1) || ((loaderFlags & LoaderFlags::FindInstances) != 0)) + { + // When we are using instancing dont bake the transform into the mesh - apply the transform to each of the instance transforms. + // It is quite likely the fatObject.m_Transform matrix will be 'identity' as it should have already been applied while instances were being found. + glm::mat4x3 objectTransform4x3 = fatObject.m_Transform; // convert 4x4 to 4x3 (keeps rotation and translation, lost column is unimportant if the transform is a simple TRS (translation/rotation/scale) matrix + for (MeshObjectIntermediate::FatInstance& instance : instances) + { + instance.transform = instance.transform * objectTransform4x3; + } + fatObject.m_Transform = glm::identity(); + fatObject.m_NodeId = -1; // object (may) point to multiple instances so m_NodeId is likely not valid at the mesh level + } + else + { + // Bake the object transform down into the mesh (instances[0] may well be identity but apply it to the transform incase it is not). + glm::mat4x3 combinedTransform = glm::transpose( instances[0].transform * glm::mat4x3( fatObject.m_Transform ) ); + fatObject.m_Transform = combinedTransform; + instances[0].transform = glm::identity(); + fatObject.BakeTransform(); + } + } + + const auto nodeId = fatObject.m_NodeId; // grab the nodeId before it goes away! + + Mesh meshObject; + const auto& vertexFormats = shader.m_shaderDescription->m_vertexFormats; + MeshHelper::CreateMesh( gfxapi.GetMemoryManager(), fatObject, (uint32_t)pFirstPass->m_shaderPassDescription.m_vertexFormatBindings[0], vertexFormats, &meshObject ); + + // We are done with the FatObject here, Release it to save some memory earlier. + fatObject.Release(); + + // Potentially the vertex format has some Instance bindings in here (will have been ignored by the CreateMesh). + std::optional> vertexInstanceBuffer; + const auto instanceFormatIt = std::find_if( vertexFormats.cbegin(), vertexFormats.cend(), + []( const VertexFormat& f ) { return f.inputRate == VertexFormat::eInputRate::Instance; } ); + if (instanceFormatIt != vertexFormats.cend()) + { + if (instanceFormatIt != vertexFormats.cbegin() && instanceFormatIt != vertexFormats.end() - 1) + { + LOGE( " Drawable loader (currently) only suports shaders with instance rate 'vertex' buffers at the beginning or end of their vertex layout" ); + return false; + } + // Create the instance data + std::span instancesSpan = instances; + if (instancesSpan.empty()) + { + // Even if we are not instancing there should be one instance per mesh + LOGE( " Drawable loader expected mesh to have (at least) one instance matrix" ); + return false; + } + + const std::vector formattedVertexData = MeshObjectIntermediate::CopyFatInstanceToFormattedBuffer( instancesSpan, *instanceFormatIt ); + + if (!vertexInstanceBuffer.emplace().Initialize( &gfxapi.GetMemoryManager(), instanceFormatIt->span, instancesSpan.size(), formattedVertexData.data() )) + { + return false; + } + } + else + { + if (instances.size() > 1) + { + LOGE( " Drawable loader found instances - expects shaders vertex layout to have instance data support" ); + return false; + } + } + + // Create the drawable + if (!drawables.emplace_back( gfxapi, std::move( material.value() ) ).Init( renderPasses, passMask, std::move( meshObject ), std::move( vertexInstanceBuffer ), std::nullopt, nodeId )) + { + return false; + } + } + } + return true; +} + +template +bool DrawableLoader::CreateDrawables( T_GFXAPI& gfxApi, std::vector&& intermediateMeshObjects, const RenderContext& renderPass, const std::function( const MeshObjectIntermediate::MaterialDef& )>& materialLoader, std::vector& drawables, /*DrawableLoader::LoaderFlags*/uint32_t loaderFlags ) +{ + return CreateDrawables( gfxApi, std::move( intermediateMeshObjects ), {&renderPass, 1}, std::move( materialLoader ), drawables, loaderFlags ); +} diff --git a/framework/code/material/dx12/computable.cpp b/framework/code/material/dx12/computable.cpp new file mode 100644 index 0000000..22b2554 --- /dev/null +++ b/framework/code/material/dx12/computable.cpp @@ -0,0 +1,99 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "computable.hpp" +#include "system/os_common.h" +#include "dx12/commandList.hpp" + + +ComputablePass::~ComputablePass() +{ +} + + +ComputablePass::ComputablePass( ComputablePass&& other ) noexcept + : ComputablePassBase(std::move(other)) + , mPipeline(std::move(other.mPipeline)) + , mImageMemoryBarriers(std::move(other.mImageMemoryBarriers)) + , mBufferMemoryBarriers(std::move(other.mBufferMemoryBarriers)) + , mNeedsExecutionBarrier(other.mNeedsExecutionBarrier) +{ +} + + +template<> +bool Computable::Init() +{ + LOGI( "Creating Computable" ); + + const auto& materialPasses = mMaterial.GetMaterialPasses(); + mPasses.reserve( materialPasses.size() ); + + return true; +} + + +template<> +void Computable::SetDispatchGroupCount( uint32_t passIdx, const std::array& groupCount ) +{ + mPasses[passIdx].SetDispatchGroupCount( groupCount ); +} + +template<> +void Computable::SetDispatchThreadCount( uint32_t passIdx, const std::array& threadCount ) +{ + mPasses[passIdx].SetDispatchThreadCount( threadCount ); +} + +template<> +void Computable::DispatchPass( CommandList& cmdList, const ComputablePass& computablePass, uint32_t bufferIdx ) const +{ + auto* cmdBuffer = cmdList.Get(); + + // Add image barriers (if needed) + if (computablePass.NeedsBarrier()) + { + } + + // Bind the pipeline for this material + + // Bind everything the shader needs + + // Dispatch the compute task +} + +template<> +void Computable::Dispatch( CommandList& cmdList, uint32_t bufferIdx, bool timers ) const +{ + for (uint32_t passIdx = 0; const auto & computablePass : GetPasses()) + { + DispatchPass( cmdList, computablePass, bufferIdx % (uint32_t)computablePass.GetMaterialPass().GetDescriptorTables().size()); + ++passIdx; + } +} + +template<> +void Computable::Dispatch( CommandListBase& cmdList, uint32_t bufferIdx, bool timers ) const /*override*/ +{ + Dispatch( apiCast( cmdList ), bufferIdx, timers ); +} + +template<> +void Computable::AddOutputBarriersToCmdList( CommandList& commandList ) const +{ + const auto& computableOutputBufferBarriers = GetBufferOutputMemoryBarriers(); + const auto& computableOutputImageBarriers = GetImageOutputMemoryBarriers(); + + if (computableOutputBufferBarriers.empty() && computableOutputImageBarriers.empty()) + return; +} + +template<> +void Computable::AddOutputBarriersToCmdList( CommandListBase& cmdList ) const /*override*/ +{ + AddOutputBarriersToCmdList( apiCast( cmdList ) ); +} diff --git a/framework/code/material/dx12/computable.hpp b/framework/code/material/dx12/computable.hpp new file mode 100644 index 0000000..e6e08b0 --- /dev/null +++ b/framework/code/material/dx12/computable.hpp @@ -0,0 +1,72 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "../computable.hpp" +#include "material.hpp" +#include "pipeline.hpp" + + +template<> +class ImageMemoryBarrier final : public ImageMemoryBarrierBase +{ +public: + typedef D3D12_RESOURCE_BARRIER tBarrier; + ImageMemoryBarrier( D3D12_RESOURCE_BARRIER&& barrier ) noexcept + : vkBarrier( std::move( barrier ) ) + {} + D3D12_RESOURCE_BARRIER vkBarrier; +}; +static_assert(sizeof( ImageMemoryBarrier ) == sizeof( D3D12_RESOURCE_BARRIER )); + +template<> +class BufferMemoryBarrier final : public BufferMemoryBarrierBase +{ +public: + typedef D3D12_RESOURCE_BARRIER tBarrier; + BufferMemoryBarrier( D3D12_RESOURCE_BARRIER&& barrier ) noexcept + : vkBarrier( std::move( barrier ) ) + {} + tBarrier vkBarrier; +}; +static_assert(sizeof( BufferMemoryBarrier ) == sizeof( D3D12_RESOURCE_BARRIER )); + + +template<> +class ComputablePass final : public ComputablePassBase +{ + ComputablePass( const ComputablePass& ) = delete; + ComputablePass& operator=( const ComputablePass& ) = delete; +public: + ComputablePass( const MaterialPass& materialPass, Pipeline pipeline, /*VkPipelineLayout pipelineLayout,*/ std::vector imageMemoryBarriers, std::vector bufferMemoryBarriers, bool needsExecutionBarrier) + : ComputablePassBase( materialPass ) + , mPipeline( std::move( pipeline ) ) + //, mPipelineLayout( pipelineLayout ) + , mImageMemoryBarriers( std::move( imageMemoryBarriers ) ) + , mBufferMemoryBarriers( std::move( bufferMemoryBarriers ) ) + , mNeedsExecutionBarrier( needsExecutionBarrier ) + {} + ComputablePass( ComputablePass&& ) noexcept; + ~ComputablePass(); + + const auto& GetMaterialPass() const { return apiCast( mMaterialPass ); } + + const auto& GetVkImageMemoryBarriers() const { return mImageMemoryBarriers; } + const auto& GetVkBufferMemoryBarriers() const { return mBufferMemoryBarriers; } + const bool NeedsBarrier() const { return mNeedsExecutionBarrier || (!mImageMemoryBarriers.empty()) || (!mBufferMemoryBarriers.empty()); }; ///< @return true if there needs to be a barrier before executing this compute pass + + Pipeline mPipeline; + //VkPipelineLayout mPipelineLayout; // Owned by ShaderPass or MaterialPass + +protected: + std::vector mImageMemoryBarriers; ///< Image barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + std::vector mBufferMemoryBarriers; ///< Buffer barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + bool mNeedsExecutionBarrier = false;///< Denotes if we need an execution barrier for ENTRY to this pass (even if there are no image or buffer barriers). +}; + diff --git a/framework/code/material/dx12/descriptorSetLayout.cpp b/framework/code/material/dx12/descriptorSetLayout.cpp new file mode 100644 index 0000000..511f62f --- /dev/null +++ b/framework/code/material/dx12/descriptorSetLayout.cpp @@ -0,0 +1,316 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "descriptorSetLayout.hpp" +#include "../descriptorSetDescription.hpp" +//#include "dx12/dx12.hpp" +#include +#include + +static D3D12_SHADER_VISIBILITY EnumToDx12(DescriptorSetDescription::StageFlag::t f) { + switch(f) + { + case DescriptorSetDescription::StageFlag::t::Vertex: + return D3D12_SHADER_VISIBILITY_VERTEX; + case DescriptorSetDescription::StageFlag::t::Fragment: + return D3D12_SHADER_VISIBILITY_PIXEL; + case DescriptorSetDescription::StageFlag::t::Geometry: + return D3D12_SHADER_VISIBILITY_GEOMETRY; + case DescriptorSetDescription::StageFlag::t::Compute: + return D3D12_SHADER_VISIBILITY_ALL; + case DescriptorSetDescription::StageFlag::t::None: + assert(0); + default: + // Mixing flags (or using any of the ray flags) just make the descriptor visible to all stages. + // DirectX 12 has a less finely grained control over this stuff (compared to other graphics api). + return D3D12_SHADER_VISIBILITY_ALL; + } + return D3D12_SHADER_VISIBILITY_ALL; +} + +DescriptorSetLayout::DescriptorSetLayout() noexcept + : DescriptorSetLayoutBase() +{ +} + +DescriptorSetLayout::~DescriptorSetLayout() +{ +// assert(m_descriptorSetLayout == VK_NULL_HANDLE); +} + +void DescriptorSetLayout::Destroy(Dx12& dx12) +{ +// if (m_descriptorSetLayout != VK_NULL_HANDLE) +// { +// vkDestroyDescriptorSetLayout(dx12.m_VulkanDevice, m_descriptorSetLayout, nullptr); +// m_descriptorSetLayout = VK_NULL_HANDLE; +// } +// m_descriptorSetLayoutBindings.clear(); +// m_descriptorPoolSizes.clear(); +} + +DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept + : DescriptorSetLayoutBase(std::move(other)) +// , m_descriptorSetLayoutBindings(std::move(other.m_descriptorSetLayoutBindings)) +// , m_descriptorPoolSizes(std::move(other.m_descriptorPoolSizes)) +{ +// m_descriptorSetLayout = other.m_descriptorSetLayout; +// other.m_descriptorSetLayout = VK_NULL_HANDLE; +} + +bool DescriptorSetLayout::Init(Dx12& dx12, const DescriptorSetDescription& description) +{ + if (!DescriptorSetLayoutBase::Init(description)) + return false; + + const size_t numDescriptors = description.m_descriptorTypes.size(); + uint32_t shaderRegisterIndex = 0; + if (description.m_setIndex == 0) + { + // Root signature + auto& [rootParams, rootSamplers] = m_Layout.emplace(); + + rootParams.reserve(numDescriptors); + rootSamplers.reserve(numDescriptors); + uint32_t rootParamCount = 0; + uint32_t rootSamplerCount = 0; + uint32_t shaderRegisterIndex = 0; + + for (const auto& it : description.m_descriptorTypes) + { + bool validRootParam = false; + bool validRootSampler = false; + D3D12_ROOT_PARAMETER rootParam {}; + D3D12_STATIC_SAMPLER_DESC rootSampler {}; + + if (it.descriptorIndex < 0) + { + // Index of < 0 denotes we want to use sequential parameter indices. + ///TODO: look for collisions or determine how/if we want to handle out of order desciptor indices or enforce shaders that have an explicit binding index to define indices for all descriptors. + } + else + assert(0); ///TODO: implement user defined indices for DX12 + + assert(it.count == 1); // do we need to support a count > 1 (or <=0) for the root signature + + bool readOnly = it.readOnly; + switch (it.type) { + case DescriptorSetDescription::DescriptorType::ImageSampler: + assert(0); //not supported in the root (currently) + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::ImageSampled: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + validRootParam = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::Sampler: +// assert(0);// Needs data setting into rootSampler + rootSampler = {}; + validRootSampler = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::UniformBuffer: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + validRootParam = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::StorageBuffer: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + validRootParam = true; + break; + case DescriptorSetDescription::DescriptorType::ImageStorage: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + validRootParam = true; + break; + case DescriptorSetDescription::DescriptorType::InputAttachment: + assert(0); ///TODO: implement DX12 equivalent to input attachements (if one exists with render passes) + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::AccelerationStructure: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + validRootParam = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::DrawIndirectBuffer: + assert(0 && "DrawIndirectBuffer is not a supported binding to a root signature"); + break; + case DescriptorSetDescription::DescriptorType::DescriptorTable: + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + validRootParam = true; + break; + } + + if (validRootParam) + { + switch (rootParam.ParameterType) { + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + rootParam.DescriptorTable = {}; // needs filling out when creating the root signature (in PipelineLayout::Init) + break; + case D3D12_ROOT_PARAMETER_TYPE_CBV: + case D3D12_ROOT_PARAMETER_TYPE_SRV: + case D3D12_ROOT_PARAMETER_TYPE_UAV: + rootParam.Descriptor = D3D12_ROOT_DESCRIPTOR { + .ShaderRegister = shaderRegisterIndex++, + .RegisterSpace = 0 + }; + break; + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + assert(0);//not currently supported + break; + } + rootParam.ShaderVisibility = EnumToDx12(it.stages); + rootParams.emplace_back(rootParam); + } + else if (validRootSampler) + { + rootSamplers.emplace_back(rootSampler); + } + } + } + else + { + // Descriptor table (non root) + auto& [descriptors] = m_Layout.emplace(); + descriptors.reserve(numDescriptors); + + for (const auto& it : description.m_descriptorTypes) + { + D3D12_DESCRIPTOR_RANGE descriptor {}; + bool validDescriptor = false; + + bool readOnly = it.readOnly; + switch (it.type) { + case DescriptorSetDescription::DescriptorType::ImageSampler: + assert(0); // not currently valid (needs 2 slots?) + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::ImageSampled: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + validDescriptor = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::Sampler: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + validDescriptor = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::UniformBuffer: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + validDescriptor = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::StorageBuffer: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + validDescriptor = true; + break; + case DescriptorSetDescription::DescriptorType::ImageStorage: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + validDescriptor = true; + break; + case DescriptorSetDescription::DescriptorType::InputAttachment: + assert(0); ///TODO: implement DX12 equivalent to input attachements (if one exists with render passes) + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::AccelerationStructure: + descriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + validDescriptor = true; + assert(readOnly); + break; + case DescriptorSetDescription::DescriptorType::DrawIndirectBuffer: + assert(0 && "DrawIndirectBuffer is not a supported binding to a descriptor set"); + break; + case DescriptorSetDescription::DescriptorType::DescriptorTable: + assert(0); // Descriptor table cannot reference another table + break; + } + + if (validDescriptor) + { + descriptor.NumDescriptors = it.count; + if (it.count <= 0) + { + // Count of 0 denotes that we dont (yet) know how many descriptors will go into this slot. + ///TODO: support variable descriptor counts for DX12 + assert(0); + } + + if (it.descriptorIndex < 0) + { + // Index of < 0 denotes we want to use sequential descriptor binding indices. + ///TODO: look for collisions or determine how/if we want to handle out of order desciptor indices or enforce shaders that have an explicit binding index to define indices for all descriptors. + ///TODO: also ensure D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND works in this case + descriptor.BaseShaderRegister = shaderRegisterIndex; + shaderRegisterIndex += descriptor.NumDescriptors; + } + else + assert(0); ///TODO: implement user defined descriptor indices for DX12 + + descriptor.RegisterSpace = 1; + descriptor.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + descriptors.emplace_back(descriptor); + } + } + } + + return true; +} + + +//VkDescriptorSetLayout DescriptorSetLayout::CreateVkDescriptorSetLayout(Dx12& dx12, const std::span descriptorSetLayoutBindings) +//{ +// // +// // Create the descriptor set layout +// // +// VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; +// layoutInfo.bindingCount = static_cast(descriptorSetLayoutBindings.size()); +// layoutInfo.pBindings = descriptorSetLayoutBindings.data(); +// +// VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; +// +// VkResult retVal = vkCreateDescriptorSetLayout(dx12.m_VulkanDevice, &layoutInfo, nullptr, &descriptorSetLayout); +// +// LOGI("vkCreateDescriptorSetLayout"); +// for (const auto& binding: descriptorSetLayoutBindings) +// { +// LOGI(" binding: %u\tdescriptorType: 0x%x descriptorCount: %d stageFlags: 0x%x pImmutableSamplers : %p", binding.binding, binding.descriptorType, binding.descriptorCount, binding.stageFlags, binding.pImmutableSamplers); +// } +// +// if (!CheckVkError("vkCreateDescriptorSetLayout()", retVal)) +// { +// return VK_NULL_HANDLE; +// } +// return descriptorSetLayout; +//} +// +// +//void DescriptorSetLayout::CalculatePoolSizes(const std::span descriptorSetLayoutBindings, std::vector& descriptorPoolSizes) +//{ +//#define MAX_USED_DESCRIPTOR_TYPES ((VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1) + 2) // Take a guess at how many unique descript types will be used +// +// descriptorPoolSizes.reserve(MAX_USED_DESCRIPTOR_TYPES); +// +// // +// // Calculate the descriptor set pool sizes +// // +// for (const auto& binding : descriptorSetLayoutBindings) +// { +// auto it = std::find_if(descriptorPoolSizes.begin(), descriptorPoolSizes.end(), [&binding](const VkDescriptorPoolSize& x) { return x.type == binding.descriptorType; }); +// if (it == descriptorPoolSizes.end()) +// { +// descriptorPoolSizes.push_back({ binding.descriptorType, binding.descriptorCount }); +// } +// else +// { +// it->descriptorCount += binding.descriptorCount; +// } +// } +//} + + + diff --git a/framework/code/material/dx12/descriptorSetLayout.hpp b/framework/code/material/dx12/descriptorSetLayout.hpp new file mode 100644 index 0000000..2834dc2 --- /dev/null +++ b/framework/code/material/dx12/descriptorSetLayout.hpp @@ -0,0 +1,54 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include "../descriptorSetLayout.hpp" + +// Forward declares +class DescriptorSetDescription; +class Dx12; + + +/// Representation of a descriptor table (set) for Dx12. +/// template specialization of DescriptSetLayout +/// Used to describe the root signature or a descriptor table pointed to by the root signature. +/// Also has a mapping from descriptor slots name (nice name) to the their table index. +/// @ingroup Material +template<> +class DescriptorSetLayout : public DescriptorSetLayoutBase +{ + DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; + DescriptorSetLayout(const DescriptorSetLayout&) = delete; +public: + DescriptorSetLayout() noexcept; + ~DescriptorSetLayout(); + DescriptorSetLayout(DescriptorSetLayout&&) noexcept; + + bool Init(Dx12& , const DescriptorSetDescription&); + void Destroy(Dx12&); + + struct RootSignatureParameters { + std::vector root; + std::vector samplers; + }; + struct DescriptorTableParameters { + std::vector ranges; + }; + const auto& GetLayout() const { return m_Layout; } + + template const auto& GetLayout() const; + template<> const auto& GetLayout() const { return std::get(m_Layout); } + template<> const auto& GetLayout() const { return std::get(m_Layout); } + +private: + std::variant < RootSignatureParameters, DescriptorTableParameters > m_Layout; +}; diff --git a/framework/code/material/dx12/drawableDx12.cpp b/framework/code/material/dx12/drawableDx12.cpp new file mode 100644 index 0000000..2c2ea9d --- /dev/null +++ b/framework/code/material/dx12/drawableDx12.cpp @@ -0,0 +1,271 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "drawableDx12.hpp" +#include "dx12/dx12.hpp" +#include "dx12/commandList.hpp" +#include "dx12/descriptorHeapManager.hpp" +#include "dx12/renderPass.hpp" +#include "material.hpp" +#include "pipeline.hpp" +#include "shader.hpp" +#include "../specializationConstants.hpp" +#include "material/shaderDescription.hpp" +#include "shaderModule.hpp" +#include "system/os_common.h" +#include "mesh/meshHelper.hpp" +#include "mesh/instanceGenerator.hpp" +#include +#include + +DrawablePass::~DrawablePass() +{ +} + +const DrawablePass* Drawable::GetDrawablePass(const std::string& passName) const +{ + auto it = mPassNameToIndex.find(passName); + if (it != mPassNameToIndex.end()) + { + return &mPasses[it->second]; + } + return nullptr; +} + + +Drawable::~Drawable() +{ +} + +bool Drawable::ReInit(std::span renderPasses, std::span pipelines, std::span passNames, uint32_t passMask, std::span passMultisample, std::span subpasses) +{ + assert( passMultisample.empty() || passMultisample.size() == passNames.size() ); + assert( renderPasses.size() == passNames.size() ); + + mPassMask = passMask; + mPassNameToIndex.clear(); + mPasses.clear(); + + const auto& shader = mMaterial.GetShader(); + mPasses.reserve(shader.GetShaderPasses().size()); + for (uint32_t passIdx = 0; passIdx < sizeof(passMask) * 8; ++passIdx) + { + if (passMask & (1 << passIdx)) + { + // LOGI("Creating Mesh Object PipelineState and Pipeline for pass... %s", passNames[passIdx]); + auto* pMaterialPass = const_cast*>(mMaterial.GetMaterialPass(passNames[passIdx])); + if (!pMaterialPass) + { + LOGE(" Pass does not exist in shader"); + continue; + } + assert(pMaterialPass); + const auto& shaderPass = pMaterialPass->GetShaderPass(); + PipelineRasterizationState rasterizationState; + SpecializationConstants specializationConstants; + + Pipeline pipeline = pipelines[passIdx] + ? pipelines[passIdx].Copy() + : CreatePipeline( mGfxApi, + shaderPass.m_shaderPassDescription, + shaderPass.GetPipelineLayout(), + shaderPass.GetPipelineVertexInputState(), + rasterizationState, + specializationConstants, + shaderPass.m_shaders, + renderPasses[passIdx], + subpasses.empty() ? 0 : subpasses[passIdx], + passMultisample.empty() ? Msaa::Samples1 : passMultisample[passIdx] ); + + // Common to all pipelines + mPassNameToIndex.try_emplace(passNames[passIdx], (uint32_t)mPasses.size()); // add the lookup (in to mPasses) + + // Build the passVertexBufferLookup so at runtime we can easily populate the vkBuffer array with the vertex and instance buffers in the order specified per pass by m_vertexFormatBindings. + // Could do this in a single loop; currently split into 2 so we can potentially add more flexibility in where we get the VKBuffers from (TODO) + std::vector tmp; + tmp.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + int numVertexRateFormats = 0, numInstanceRateFormats = 0; + for (const auto& vertexFormat : shader.m_shaderDescription->m_vertexFormats) + { + switch (vertexFormat.inputRate) { + case VertexFormat::eInputRate::Vertex: + tmp.push_back(numVertexRateFormats++); + break; + case VertexFormat::eInputRate::Instance: + tmp.push_back(--numInstanceRateFormats); + break; + } + } + std::vector passVertexBufferLookup; // order of the vkBuffers for this pass (index is in to the vertex array if positive, or in to the instance array if negative (-1 is the 'first') + DrawablePassVertexBuffers passVertexBuffers; + passVertexBufferLookup.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + passVertexBuffers.views.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + for (uint32_t formatBindingIdx : shaderPass.m_shaderPassDescription.m_vertexFormatBindings) + { + const int bufferIdx = tmp[formatBindingIdx]; + passVertexBufferLookup.push_back(bufferIdx); + if (bufferIdx >= 0) + { + // Vertex rate data (ie the mesh) + passVertexBuffers.views.push_back(mMeshObject.m_VertexBuffers[bufferIdx].GetVertexBufferView()); + + // Double check the mesh is supplying the data we expect in the shader, may mismatch if the mesh was not built using the vertex format (eg Mesh::CreateScreenSpaceMesh) + // We could dive even deeper, for now check the span and the number of attributes match + //assert(mMeshObject.m_VertexBuffers[bufferIdx].GetAttributes().size() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].elements.size()); + assert(mMeshObject.m_VertexBuffers[bufferIdx].GetSpan() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].span); + } + else + { + // Instance rate data (ie the instance buffer) + assert(bufferIdx == -1); + assert(mVertexInstanceBuffer.has_value()); + passVertexBuffers.views.push_back(mVertexInstanceBuffer->GetVertexBufferView()); + } + } + + // Index buffer is optional + D3D12_INDEX_BUFFER_VIEW indexBuffer = {}; + size_t indexCount = 0; + if (mMeshObject.m_IndexBuffer) + { + indexBuffer = mMeshObject.m_IndexBuffer->GetIndexBufferView(); + indexCount = mMeshObject.m_IndexBuffer->GetNumIndices(); + } + + // Indirect Draw buffer is optional +// VkBuffer drawIndirectBuffer = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetVkBuffer() : VK_NULL_HANDLE; +// uint32_t drawIndirectCount = mDrawIndirectBuffer.has_value() ? (uint32_t)mDrawIndirectBuffer->GetNumDraws() : 0; +// uint32_t drawIndirectOffset = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetBufferOffset() : 0; +// // Indirect Draw Count (count buffer) set to be the beginning of the drawIndirectBuffer IF there is an offset in the mDrawIndirectBuffer. +// VkBuffer drawIndirectCountBuffer = drawIndirectOffset>0 ? drawIndirectBuffer : VK_NULL_HANDLE; +// +// // Pipeline layout may come from the shaderPass or (if that fails) from the materialPass (if it was created late because of 'dynamic' descriptor set layout). +// VkPipelineLayout pipelineLayout = shaderPass.GetPipelineLayout().GetVkPipelineLayout(); +// if (pipelineLayout == VK_NULL_HANDLE) +// pipelineLayout = pMaterialPass->GetPipelineLayout().GetVkPipelineLayout(); + + // add the DrawablePass + DrawablePass& pass = mPasses.emplace_back( *pMaterialPass, + std::move( pipeline ), + pMaterialPass->GetRootData(), + pMaterialPass->GetDescriptorTables(), + std::move( passVertexBuffers ), + indexBuffer, + (uint32_t)mMeshObject.m_NumVertices, + (uint32_t)indexCount, + passIdx + ); + } + } + return true; +} + +void Drawable::DrawPass(CommandList& cmdList, const DrawablePass& drawablePass, uint32_t bufferIdx, const std::span vertexBufferOverrides) const +{ + ID3D12GraphicsCommandList* dxCmdList = cmdList.Get(); + const auto& shaderPass = drawablePass.mMaterialPass.GetShaderPass(); + + // Set the pipeline for this material + dxCmdList->SetPipelineState( drawablePass.mPipeline.GetPipeline() ); + + mGfxApi.SetDescriptorHeaps(dxCmdList); + + dxCmdList->SetGraphicsRootSignature( shaderPass.GetPipelineLayout().GetRootSignature() ); + + // Bind everything the shader needs + { + // Set the root parameters! + const auto& rootDescriptorTable = drawablePass.mDescriptorTables.begin(); + const auto& rootLayout = shaderPass.GetDescriptorSetLayouts()[0].GetLayout(); + uint32_t descriptorTableIdx = 0; // root is table 0 + for (size_t i = 0; i < rootLayout.root.size(); ++i) + { + switch (rootLayout.root[i].ParameterType) { + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + dxCmdList->SetGraphicsRootDescriptorTable(i, drawablePass.mDescriptorTables[++descriptorTableIdx].GetGpuHandle()); + break; + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + dxCmdList->SetGraphicsRoot32BitConstants(i, 0, nullptr, 0); + assert(0 && "unhandled root parameter"); + break; + case D3D12_ROOT_PARAMETER_TYPE_CBV: + dxCmdList->SetGraphicsRootConstantBufferView( i, drawablePass.mRootItems[i].gpuAddress ); + break; + case D3D12_ROOT_PARAMETER_TYPE_SRV: + dxCmdList->SetGraphicsRootShaderResourceView(i, 0); + assert(0 && "unhandled root parameter"); + break; + case D3D12_ROOT_PARAMETER_TYPE_UAV: + dxCmdList->SetGraphicsRootUnorderedAccessView(i, 0); + assert(0 && "unhandled root parameter"); + break; + } + } + } + + const auto& vertexBuffers = vertexBufferOverrides.empty() ? drawablePass.mVertexBuffers : vertexBufferOverrides[bufferIdx % vertexBufferOverrides.size()]; + + if (!vertexBuffers.views.empty()) + { + // Bind mesh vertex/instance buffer(s) + dxCmdList->IASetVertexBuffers( 0, (UINT) vertexBuffers.views.size(), vertexBuffers.views.data() ); + dxCmdList->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + } + if (!vertexBufferOverrides.empty()) + { + assert(vertexBufferOverrides.empty() || vertexBuffers.views.size() == drawablePass.mVertexBuffers.views.size()); + } + + if (drawablePass.mIndexBuffer.Format != DXGI_FORMAT_UNKNOWN) + { + // Bind index buffer data + dxCmdList->IASetIndexBuffer(&drawablePass.mIndexBuffer); + +// if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) +// { +// if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) +// { +// // Draw the mesh using draw indirect cont buffer (VkDrawIndexedIndirectCount command) +// const auto* drawIndirectCountExt = mVulkan.GetExtension(); +// assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr); +// drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); +// assert(0);//not yet implemented +// } +// else +// // Draw the mesh using draw indirect buffer (VkDrawIndexedIndirectCommand) +// vkCmdDrawIndexedIndirect(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); +// } +// else + { + // Everything is set up, draw the mesh + dxCmdList->DrawIndexedInstanced(drawablePass.mNumIndices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0, 0); + } + } + else + { +// if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) +// { +// if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) +// { +// // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) +// const auto* drawIndirectCountExt = mVulkan.GetExtension(); +// assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr ); +// drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); +// } +// else +// // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) +// vkCmdDrawIndirect(cmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); +// } +// else + { + // Draw the mesh without index buffer + dxCmdList->DrawInstanced(drawablePass.mNumVertices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0); + } + } +} + diff --git a/framework/code/material/dx12/drawableDx12.hpp b/framework/code/material/dx12/drawableDx12.hpp new file mode 100644 index 0000000..a4b74a8 --- /dev/null +++ b/framework/code/material/dx12/drawableDx12.hpp @@ -0,0 +1,111 @@ +//============================================================================= +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../drawable.hpp" +#include "dx12/dx12.hpp" +#include "mesh/mesh.hpp" +#include "material.hpp" +#include "pipeline.hpp" +#include "dx12/renderPass.hpp" +#include "material/shaderDescription.hpp" +#include "memory/dx12/indexBufferObject.hpp" +#include "memory/dx12/vertexBufferObject.hpp" +#include "memory/drawIndirectBuffer.hpp" +#include "mesh/instanceGenerator.hpp" +#include "mesh/meshHelper.hpp" +#include "mesh/meshIntermediate.hpp" +#include "system/os_common.h" +//#include "pipelineVertexInputState.hpp" + +// Forward Declarations +class Dx12; +class MaterialBase; +struct MeshInstance; +enum class Msaa; +template class Material; +template class Shader; + +template<> +struct DrawablePassVertexBuffers : public DrawablePassVertexBuffersBase +{ + std::vector views; +}; + + +/// Encapsulates a drawable pass. +/// References vertex/index buffers etc from the parent Drawable. +/// Users are expected to use Drawable (which contains a vector of DrawablePasses and is more 'user friendly'). +/// @ingroup Material +template<> +class DrawablePass : public DrawablePassBase +{ + DrawablePass(const DrawablePass&) = delete; + DrawablePass& operator=(const DrawablePass&) = delete; + using tGfxApi = Dx12; +public: + DrawablePass(DrawablePass&&) noexcept = default; + DrawablePass() = delete; + ~DrawablePass(); + DrawablePass(const MaterialPass& MaterialPass, + Pipeline Pipeline, + //VkPipelineLayout PipelineLayout, + std::vector RootDescriptorTableItems, + std::vector DescriptorTables, + //const PipelineVertexInputState& PipelineVertexInputState, + DrawablePassVertexBuffers VertexBuffers, + D3D12_INDEX_BUFFER_VIEW IndexBuffer, + //VkIndexType IndexBufferType, + //VkBuffer DrawIndirectBuffer, + //VkBuffer DrawIndirectCountBuffer, + uint32_t NumVertices, + uint32_t NumIndices, +// uint32_t NumDrawIndirect, +// uint32_t DrawIndirectOffset, + uint32_t PassIdx + ) noexcept + : mMaterialPass( MaterialPass ) + , mPipeline( std::move(Pipeline) ) + //, mPipelineLayout( PipelineLayout )*/ + , mRootItems( std::move(RootDescriptorTableItems) ) + , mDescriptorTables( std::move(DescriptorTables) ) + /*, mPipelineVertexInputState( PipelineVertexInputState )*/ + , mVertexBuffers( std::move(VertexBuffers) ) + , mIndexBuffer( IndexBuffer ) + /*, mIndexBufferType( IndexBufferType ), mDrawIndirectBuffer( DrawIndirectBuffer ), mDrawIndirectCountBuffer( DrawIndirectCountBuffer )*/ + , mNumVertices( NumVertices ) + , mNumIndices( NumIndices) + /*mNumDrawIndirect( NumDrawIndirect), mDrawIndirectOffset( DrawIndirectOffset),*/ + , mPassIdx( PassIdx) + { + } + + const MaterialPass& mMaterialPass; + Pipeline mPipeline; + //VkPipeline mPipeline = VK_NULL_HANDLE; // Owned by us + //VkPipelineLayout mPipelineLayout; // Owned by shader + std::vector mRootItems; + std::vector mDescriptorTables; // + //const PipelineVertexInputState& mPipelineVertexInputState; // contains vertex binding and attribute descriptions + //DrawablePassVertexBuffers mVertexBuffers; // contains vkbuffer/offsets; one per mVertexBuffersLookup entry. May be vertex rate or instance rate. + DrawablePassVertexBuffers mVertexBuffers; // one per mVertexBuffersLookup entry. May be vertex rate or instance rate + D3D12_INDEX_BUFFER_VIEW mIndexBuffer = {0,0,DXGI_FORMAT_UNKNOWN}; + //VkIndexType mIndexBufferType = VK_INDEX_TYPE_MAX_ENUM; + //VkBuffer mDrawIndirectBuffer = VK_NULL_HANDLE; + //VkBuffer mDrawIndirectCountBuffer = VK_NULL_HANDLE; + uint32_t mNumVertices; + uint32_t mNumIndices; + //uint32_t mNumDrawIndirect; + //uint32_t mDrawIndirectOffset; // if non zero offset mDrawIndirectBuffer by this + uint32_t mPassIdx; // index of the bit in Drawable::m_passMask +}; diff --git a/framework/code/material/dx12/material.cpp b/framework/code/material/dx12/material.cpp new file mode 100644 index 0000000..98c0b07 --- /dev/null +++ b/framework/code/material/dx12/material.cpp @@ -0,0 +1,15 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "material.hpp" +#include "shader.hpp" +//#include "vulkan/vulkan.hpp" +#include +#include "system/os_common.h" +//#include "vulkan/TextureFuncts.h" +#include "texture/dx12/texture.hpp" diff --git a/framework/code/material/dx12/material.hpp b/framework/code/material/dx12/material.hpp new file mode 100644 index 0000000..a03a635 --- /dev/null +++ b/framework/code/material/dx12/material.hpp @@ -0,0 +1,30 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "dx12/dx12.hpp" +#include "materialPass.hpp" +#include "../material.hpp" +#include "../materialT.hpp" + +using MaterialDx12 = Material; + + +/// @defgroup Material +/// Material and Shader loader. +/// Handles creation of descriptor sets, buffer binding, shader binaries (everything in Dx12 that describes and is used to render a shader). +/// +/// Typically user (application) writes a Json description for each 'Shader' that describes the inputs, outputs, and internal state of the shader, and the shader code (glsl). +/// The user then uses ShaderManagerBase::AddShader to register (and load) each Json file and the shader binaries. +/// +/// From there the user uses MaterialManagerBase::CreateMaterial to create a MaterialBase instance of the Shader (a MaterialBase contains bindings to the various texture/buffer inputs that a Shader requires - there can be many Materials using the same Shader (each MaterialBase with different textures, vertex buffers and/or uniform buffers etc) +/// +/// The MaterialBase returned by CreateMaterial can be used to Create a Drawable or Computable object that wraps everything together with one convenient interface! +/// +/// For more complex models the user should use DrawableLoader::LoadDrawable to load the mesh model file (and return a vector of Drawables). This api greatly simplifies the material creation and binding, splitting model meshes across material boundaries, automatically detecting instances (optionally). +/// diff --git a/framework/code/material/dx12/materialManager.cpp b/framework/code/material/dx12/materialManager.cpp new file mode 100644 index 0000000..95fe40c --- /dev/null +++ b/framework/code/material/dx12/materialManager.cpp @@ -0,0 +1,175 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "material.hpp" +#include "materialManager.hpp" +#include "../shaderDescription.hpp" +#include "../specializationConstants.hpp" +#include "descriptorSetLayout.hpp" +#include "shader.hpp" +#include "system/os_common.h" +#include "dx12/dx12.hpp" +#include "texture/Dx12/texture.hpp" + +#include +static_assert(GLM_HAS_CONSTEXPR); + + +MaterialManager::MaterialManager( Dx12& dx12 ) noexcept : MaterialManagerBase(dx12) +{} + +MaterialManager::~MaterialManager() +{} + +MaterialPass MaterialManager::CreateMaterialPassInternal( + const ShaderPass& shaderPass, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function(const std::string&)>& bufferLoader, + const std::function(const std::string&)>& imageLoader, + const std::function& accelerationStructureLoader, + const std::function& specializationStructureLoader, + const std::string& passDebugName) const +{ + using ImageInfo = ImageInfo; + auto& dx12 = static_cast(mGfxApi); + const std::vector>& descriptorSetLayouts = shaderPass.GetDescriptorSetLayouts(); + + // + // Gather the (named) texture and uniform buffer slots (for this material pass) + // + MaterialPass::tTextureBindings textureBindings; + MaterialPass::tImageBindings imageBindings; + MaterialPass::tBufferBindings bufferBindings; + MaterialPass::tAccelerationStructureBindings accelerationStructureBindings; + + for (uint32_t layoutIdx = 0; layoutIdx < (uint32_t) descriptorSetLayouts.size(); ++layoutIdx) + { + const auto& descSetLayout = descriptorSetLayouts[layoutIdx]; + std::vector dxBindings; + + for (const auto& bindingNames : descSetLayout.GetNameToBinding()) + { + uint32_t descriptorCount = 0; + const std::string& bindingName = bindingNames.first; + const DescriptorSetLayoutBase::BindingTypeAndIndex& binding = bindingNames.second; + using DescriptorType = DescriptorSetDescription::DescriptorType; + switch (binding.type) + { + case DescriptorType::ImageSampler: + case DescriptorType::ImageSampled: + case DescriptorType::Sampler: + { + MaterialManagerBase::tPerFrameTexInfo pTextures = textureLoader(bindingName); // Get the texture(s) and/or samplers from the callback + descriptorCount = (uint32_t) pTextures.size(); + textureBindings.push_back({std::move(pTextures), {layoutIdx, binding}}); + break; + } + case DescriptorType::InputAttachment: + case DescriptorType::ImageStorage: + { + if (imageLoader) + { + const ImageInfo imageInfo(imageLoader(bindingName)); + descriptorCount = 1; + imageBindings.push_back({{imageInfo}, {layoutIdx, binding}}); + } + else + { + // Fallback to using the texture callback + MaterialManagerBase::tPerFrameTexInfo pTextures = textureLoader(bindingName); + std::vector imageInfos; + descriptorCount = (uint32_t)pTextures.size(); + imageInfos.reserve(descriptorCount); + for (const auto* pTexture : pTextures) + imageInfos.push_back(ImageInfo(*pTexture)); + imageBindings.push_back({std::move(imageInfos), {layoutIdx, binding}}); + } + break; + } + + case DescriptorType::UniformBuffer: + case DescriptorType::StorageBuffer: + { + PerFrameBuffer dxBuffers = bufferLoader(bindingName); // Get the buffer(s) from the callback + descriptorCount = (uint32_t) dxBuffers.size(); + bufferBindings.push_back({std::move(dxBuffers), {layoutIdx, binding}}); + break; + } +#if VK_KHR_acceleration_structure +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch" +#endif // defined(__clang__) + case DescriptorType::AccelerationStructure: + { + assert(accelerationStructureLoader && "Needs accelerationStructureLoader function defining"); + MaterialPass::tPerFrameAccelerationStructure vkAS = accelerationStructureLoader(bindingName); // Get the buffer(s) from the callback + accelerationStructureBindings.push_back({ std::move(vkAS), binding }); + break; + } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // defined(__clang__) +#endif // VK_KHR_acceleration_structure + case DescriptorType::DescriptorTable: + { + // Skip past this (Name points to one of the other descriptor sets in descriptorSetLayouts) + break; + } + default: + assert(0); // needs implementing + break; + } + + // Fill in descriptor count if 'dynamically sized' + if (!dxBindings.empty() && dxBindings[binding.index].count == 0) + { + dxBindings[binding.index].count = descriptorCount; + } + } + } + + SpecializationConstants specializationConstants; + return MaterialPass(dx12, shaderPass/*, std::move(descriptorPool), std::move(descriptorTables), std::move(dynamicVkDescriptorSetLayouts)*/, + std::move(textureBindings), std::move(imageBindings), std::move(bufferBindings), std::move(accelerationStructureBindings), std::move(specializationConstants)); +} + +//Material MaterialManager::CreateMaterial(Dx12& dx12, const Shader& shader, uint32_t numFrameBuffers, +// const std::function::tPerFrameTexInfo(const std::string&)>& textureLoader, +// const std::function(const std::string&)>& bufferLoader, +// const std::function& imageLoader, +// const std::function::tPerFrameAccelerationStructure(const std::string&)>& accelerationStructureLoader, +// const std::function& specializationConstantLoader) const + +Material MaterialManager::CreateMaterial( const Shader& shader, + uint32_t numFrameBuffers, + const std::function::tPerFrameTexInfo( const std::string& )>& textureLoader, + const std::function( const std::string& )>& bufferLoader, + const std::function( const std::string& )>& imageLoader, + const std::function::tPerFrameAccelerationStructure( const std::string& )>& accelerationStructureLoader, + const std::function& specializationConstantLoader ) const +{ + Material material( shader, numFrameBuffers ); + + // iterate over the passes (in their index order - iterating std::map gaurantees to iterate in key order) + const auto& shaderPasses = shader.GetShaderPasses(); + for (uint32_t passIdx = 0; const auto& passName : shader.GetShaderPassIndicesToNames()) + { + const auto& shaderPass = shaderPasses[passIdx]; + material.AddMaterialPass(passName, CreateMaterialPassInternal(shaderPass, numFrameBuffers, textureLoader, bufferLoader, imageLoader, accelerationStructureLoader, specializationConstantLoader, passName)); + ++passIdx; + } + + for (uint32_t whichBuffer = 0; whichBuffer < numFrameBuffers; ++whichBuffer) + { + material.UpdateDescriptorSets(whichBuffer); + } + + return material; +} diff --git a/framework/code/material/dx12/materialManager.hpp b/framework/code/material/dx12/materialManager.hpp new file mode 100644 index 0000000..8d28cd3 --- /dev/null +++ b/framework/code/material/dx12/materialManager.hpp @@ -0,0 +1,46 @@ +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "../materialManager.hpp" + +class Dx12; + + +///// @brief Template specialization of MaterialManager container for Dx12 graphics api. +//template<> +//class MaterialManager final : public MaterialManagerBase +//{ +// using ImageInfo = ImageInfo; +// MaterialManager& operator=( const MaterialManager& ) = delete; +// MaterialManager( const MaterialManager& ) = delete; +//public: +// +// MaterialManager(); +// ~MaterialManager(); +// +// /// Create a material to be used for rendering (potentially contains multiple passes) +// /// If numFrameBuffers is not 1 there will be numFrameBuffers descriptor sets created (so different buffers can be bound on different 'frames', although the layout is fixed) +// Material CreateMaterial( Dx12& gfxApi, const Shader& shader, uint32_t numFrameBuffers, +// const std::function& textureLoader, +// const std::function( const std::string& )>& bufferLoader, +// const std::function& imageLoader = nullptr, +// const std::function& accelerationStructureLoader = nullptr, +// const std::function& specializationConstantLoader = nullptr ) const; +//protected: +// /// Internal material pass creation (does not UpdateDescriptorSets) +// MaterialPass CreateMaterialPassInternal( Dx12& gfxapi, +// const ShaderPass& shaderPass, +// uint32_t numFrameBuffers, +// const std::function& textureLoader, +// const std::function( const std::string& )>& bufferLoader, +// const std::function& imageLoader, +// const std::function& accelerationStructureLoader, +// const std::function& specializationStructureLoader, +// const std::string& passDebugName ) const; +//}; diff --git a/framework/code/material/dx12/materialPass.cpp b/framework/code/material/dx12/materialPass.cpp new file mode 100644 index 0000000..9ae65bc --- /dev/null +++ b/framework/code/material/dx12/materialPass.cpp @@ -0,0 +1,180 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "material.hpp" +#include "shader.hpp" +#include "../specializationConstants.hpp" +#include +#include "system/os_common.h" +#include "texture/dx12/texture.hpp" + + +MaterialPass::MaterialPass(Dx12& gfxapi, const ShaderPass& shaderPass/*, VkDescriptorPool descriptorPool, std::vector&& descriptorTables, std::vector dynamicDescriptorSetLayouts*/, tTextureBindings textureBindings, tImageBindings imageBindings, tBufferBindings bufferBindings, tAccelerationStructureBindings accelerationStructureBindings, SpecializationConstants specializationConstants ) noexcept + : MaterialPassBase(shaderPass) + , mGfxApi( gfxapi ) + , mTextureBindings(std::move(textureBindings)) + , mImageBindings(std::move(imageBindings)) + , mBufferBindings(std::move(bufferBindings)) + , mAccelerationStructureBindings( std::move( accelerationStructureBindings ) ) +{ +} + +MaterialPass::MaterialPass(MaterialPass&& other) noexcept + : MaterialPassBase(std::move(other)) + , mGfxApi( other.mGfxApi ) + , mRootData(std::move(other.mRootData)) + , mDescriptorTables(std::move(other.mDescriptorTables)) + , mTextureBindings(std::move(other.mTextureBindings)) + , mImageBindings(std::move(other.mImageBindings)) + , mBufferBindings(std::move(other.mBufferBindings)) + , mAccelerationStructureBindings( std::move( other.mAccelerationStructureBindings ) ) +{ +} + +MaterialPass::~MaterialPass() +{ + for (auto& descriptorTable: mDescriptorTables) + { + mGfxApi.FreeShaderResourceViewDescriptors(std::move(descriptorTable)); + } +} + +const SpecializationConstants& MaterialPass::GetSpecializationConstants() const +{ + static SpecializationConstants empty; //not available on Dx12 + return empty; +} + +ImageInfo::ImageInfo(const Texture& t) + : ImageInfoBase() + , imageViewNumMips(t.MipLevels) + , imageViewFirstMip(t.FirstMip) +{ +} + +ImageInfo::ImageInfo(const TextureBase& t) : ImageInfo( apiCast(t) ) +{ +} + +ImageInfo::ImageInfo(const ImageInfo& src) + : ImageInfoBase() + , imageViewNumMips(src.imageViewNumMips) + , imageViewFirstMip( src.imageViewFirstMip ) +{ +} + +ImageInfo::ImageInfo( ImageInfo&& src ) noexcept + : ImageInfoBase() + , imageViewNumMips( src.imageViewNumMips ) + , imageViewFirstMip( src.imageViewFirstMip ) +{ + src.imageViewNumMips = 0; + src.imageViewFirstMip = 0; +} + +ImageInfo& ImageInfo::operator=(ImageInfo&& src) noexcept +{ + if (this != &src) + { + src.imageViewNumMips = 0; + src.imageViewFirstMip = 0; + } + return *this; +} + +bool MaterialPass::UpdateDescriptorSets(uint32_t bufferIdx) +{ + const auto& descriptorTableLayouts = GetShaderPass().GetDescriptorSetLayouts(); + mDescriptorTables.reserve(descriptorTableLayouts.size()); + + for (bool root=true; auto& descriptorTableLayout : descriptorTableLayouts) + { + if (root) + { + // The 'root' descriptor table allocation isnt used (root descriptor table is handled differently), but we make a dummy allocation anyhow! ///TODO: remove this allocation + const auto& layout = descriptorTableLayout.GetLayout(); + mDescriptorTables.emplace_back(mGfxApi.AllocateShaderResourceViewDescriptors(layout.root.size())); + // Pre-size the rootdata array (so we can write 'out of order') + mRootData.resize(layout.root.size()); + } + else + { + const auto& layout = descriptorTableLayout.GetLayout(); + mDescriptorTables.emplace_back(mGfxApi.AllocateShaderResourceViewDescriptors(layout.ranges.size())); + } + root = false; + } + + // Go through the textures first + for (const auto& textureBinding : mTextureBindings) + { + uint32_t tableIndex = textureBinding.second.setIndex; + uint32_t bindingIndex = textureBinding.second.index; + uint32_t numTexToBind = textureBinding.second.isArray ? (uint32_t)textureBinding.first.size() : 1; + uint32_t texIndex = textureBinding.second.isArray ? 0 : (bufferIdx < textureBinding.first.size() ? bufferIdx : 0); + + if (tableIndex == 0) + { + // Add to root + for (uint32_t t = 0; t < numTexToBind; ++t, ++bindingIndex, ++texIndex) + { + const auto& texture = apiCast( textureBinding.first[texIndex] ); + mRootData[bindingIndex] = RootItem{.binding = {.descriptorType = DescriptorBinding::DescriptorType::SRV, + .bindingIndex = bindingIndex, + .count = 1}, + .gpuAddress = texture->GetResource()->GetGPUVirtualAddress()}; + } + } + else + { + for (uint32_t t = 0; t < numTexToBind; ++t, ++bindingIndex, ++texIndex) + { + // Create texture view + const auto& texture = apiCast( textureBinding.first[texIndex] ); + mGfxApi.GetDevice()->CreateShaderResourceView( texture->GetResource(), &texture->GetResourceViewDesc(), mDescriptorTables[tableIndex].GetCpuHandle( bindingIndex ) ); + } + } + } + + // Now do the buffers + for (const auto& bufferBinding : mBufferBindings) + { + uint32_t tableIndex = bufferBinding.second.setIndex; + uint32_t bindingIndex = bufferBinding.second.index; + uint32_t numBuffersToBind = bufferBinding.second.isArray ? (uint32_t)bufferBinding.first.size() : 1; + uint32_t bufferIndex = bufferBinding.second.isArray ? 0 : (bufferIdx < bufferBinding.first.size() ? bufferIdx : 0); + + if (tableIndex == 0) + { + // Add to root + for (uint32_t t = 0; t < numBuffersToBind; ++t, ++bufferIndex) + { + const auto& buffer = bufferBinding.first[bufferIndex]; + mRootData[bindingIndex] = RootItem{.binding = {.descriptorType = DescriptorBinding::DescriptorType::CBV, + .bindingIndex = bindingIndex, + .count = 1}, + .gpuAddress = buffer.buffer()->GetGPUVirtualAddress()}; + } + } + else + { + for (uint32_t t = 0; t < numBuffersToBind; ++t, ++bufferIndex) + { + const auto& buffer = bufferBinding.first[bufferIndex]; + assert(buffer.offset() == 0 && "Buffer offset not (currently) implemented for Dx12"); + D3D12_CONSTANT_BUFFER_VIEW_DESC viewDesc{}; + viewDesc.BufferLocation = buffer.buffer()->GetGPUVirtualAddress(); + viewDesc.SizeInBytes = buffer.buffer()->GetDesc().Width; + mGfxApi.GetDevice()->CreateConstantBufferView(&viewDesc, mDescriptorTables[tableIndex].GetCpuHandle(bindingIndex)); + } + } + } + + return true; +} + diff --git a/framework/code/material/dx12/materialPass.hpp b/framework/code/material/dx12/materialPass.hpp new file mode 100644 index 0000000..1937ecd --- /dev/null +++ b/framework/code/material/dx12/materialPass.hpp @@ -0,0 +1,108 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "shader.hpp" +#include "../materialManager.hpp" +#include "../materialPass.hpp" + +using MaterialPassDx12 = MaterialPass; + +// Forward declarations +class TextureBase; +struct DescriptorTableHandle; +template struct ImageInfo; +template class PipelineLayout; +template class ShaderPass; +template class SpecializationConstants; +template class Texture; + + +struct DescriptorBinding +{ + enum class DescriptorType { + Constant, + CBV, + SRV, + UAV, + Descriptor + } descriptorType; + UINT bindingIndex; + UINT count; +}; + +struct RootItem { + DescriptorBinding binding; + D3D12_GPU_VIRTUAL_ADDRESS gpuAddress; +}; + + +/// Reference to a texture image. +/// Does not have ownership over referenced image or view and lifetime of those object should be longer than the referencing @ImageInfo (no reference counting). +/// @ingroup Material +template<> +struct ImageInfo : public ImageInfoBase { + ImageInfo() noexcept : ImageInfoBase() {} + ImageInfo(ImageInfo&&) noexcept; + ImageInfo& operator=(ImageInfo&&) noexcept; + ImageInfo& operator=(const ImageInfo&) = delete; + ImageInfo(const ImageInfo&); + ImageInfo(const Texture&); + ImageInfo(const TextureBase&); + uint32_t imageViewNumMips = 0; + uint32_t imageViewFirstMip = 0; +}; + + +/// An instance of a ShaderPass material. +/// Template class specialization for Vulkan +/// @ingroup Material +template<> +class MaterialPass : public MaterialPassBase +{ + MaterialPass(const MaterialPass&) = delete; + MaterialPass& operator=(const MaterialPass&) = delete; + using ImageInfo = ImageInfo; +public: + + typedef std::vector tPerFrameImageInfo; + + typedef std::vector > tTextureBindings; + typedef std::vector > tImageBindings; + typedef std::vector , DescriptorTypeAndLocation>> tBufferBindings; + typedef std::vector > tAccelerationStructureBindings; + + MaterialPass() noexcept = delete; + MaterialPass(Dx12& gfxapi, const ShaderPass&/*, VkDescriptorPool, std::vector&&/*, std::vector,*/, tTextureBindings, tImageBindings, tBufferBindings, tAccelerationStructureBindings, SpecializationConstants) noexcept; + MaterialPass(MaterialPass&&) noexcept; + ~MaterialPass(); + + const auto& GetShaderPass() const { return apiCast( mShaderPass ); } + + const auto& GetRootData() const { return mRootData; } + const auto& GetDescriptorTables() const { return mDescriptorTables; } + const SpecializationConstants& GetSpecializationConstants() const; + + bool UpdateDescriptorSets(uint32_t bufferIdx); + +protected: + Dx12& mGfxApi; + + // Dx12 objects + std::vector mRootData; ///< Root descriptor table data + std::vector mDescriptorTables;///< Non root descriptor tables + + tTextureBindings mTextureBindings; ///< Images (textures) (with sampler) considered readonly + tImageBindings mImageBindings; ///< Images that may be bound as writable (or read/write). + tBufferBindings mBufferBindings; + tAccelerationStructureBindings mAccelerationStructureBindings; + +}; + + diff --git a/framework/code/material/dx12/pipeline.cpp b/framework/code/material/dx12/pipeline.cpp new file mode 100644 index 0000000..5aa102d --- /dev/null +++ b/framework/code/material/dx12/pipeline.cpp @@ -0,0 +1,208 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "dx12/dx12.hpp" +#include "dx12/renderPass.hpp" +#include "pipeline.hpp" +#include "pipelineLayout.hpp" +#include "pipelineVertexInputState.hpp" +#include "shader.hpp" +#include "shaderModule.hpp" +#include "../shaderDescription.hpp" + +// Forward declarations +class Dx12; + + +Pipeline::Pipeline() noexcept + : mPipeline() +{ +} + +Pipeline::Pipeline(ComPtr pipeline) noexcept + : mPipeline( std::move(pipeline) ) +{ + assert( mPipeline ); +} + +Pipeline::~Pipeline() +{ +} + +static D3D12_BLEND EnumToDx12( ShaderPassDescription::BlendFactor bf) +{ + switch( bf ) + { + case ShaderPassDescription::BlendFactor::Zero: + return D3D12_BLEND_ZERO; + case ShaderPassDescription::BlendFactor::One: + return D3D12_BLEND_ONE; + case ShaderPassDescription::BlendFactor::SrcAlpha: + return D3D12_BLEND_SRC_ALPHA; + case ShaderPassDescription::BlendFactor::OneMinusSrcAlpha: + return D3D12_BLEND_INV_SRC_ALPHA; + case ShaderPassDescription::BlendFactor::DstAlpha: + return D3D12_BLEND_DEST_ALPHA; + case ShaderPassDescription::BlendFactor::OneMinusDstAlpha: + return D3D12_BLEND_INV_DEST_ALPHA; + } + assert( 0 ); + return D3D12_BLEND_ZERO; +} + +static UINT EnumToDx12( Msaa msaa ) +{ + return (UINT) msaa; +} + + +template<> +Pipeline CreatePipeline( Dx12& dx12, + const ShaderPassDescription& shaderPassDescription, + const PipelineLayout& pipelineLayout, + const PipelineVertexInputState& inputLayout, + const PipelineRasterizationState& rasterizationState, + const SpecializationConstants&, + const ShaderModules& shaderModules, + const RenderPass& renderPass, + uint32_t subpass, + Msaa msaa) +{ + // State for rasterization, such as polygon fill mode is defined. + const auto& fixedFunctionSettings = shaderPassDescription.m_fixedFunctionSettings; + D3D12_CULL_MODE cullMode = (fixedFunctionSettings.cullBackFace == false) ? ((fixedFunctionSettings.cullFrontFace == false) ? D3D12_CULL_MODE_NONE : D3D12_CULL_MODE_FRONT) : D3D12_CULL_MODE_BACK; + D3D12_RASTERIZER_DESC rasterizerState{ + .FillMode = D3D12_FILL_MODE_SOLID, + .CullMode = cullMode, + .FrontCounterClockwise = FALSE, + .DepthBias = fixedFunctionSettings.depthBiasEnable ? (int)fixedFunctionSettings.depthBiasConstant : D3D12_DEFAULT_DEPTH_BIAS, + .DepthBiasClamp = fixedFunctionSettings.depthBiasEnable ? fixedFunctionSettings.depthBiasClamp : D3D12_DEFAULT_DEPTH_BIAS_CLAMP, + .SlopeScaledDepthBias = fixedFunctionSettings.depthBiasEnable ? fixedFunctionSettings.depthBiasSlope : D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, + .DepthClipEnable = fixedFunctionSettings.depthClampEnable ? TRUE : FALSE, + .MultisampleEnable = FALSE, + .AntialiasedLineEnable = FALSE, + .ForcedSampleCount = 0, + .ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF + }; + + // Setup blending/transparency + D3D12_BLEND_DESC blendState{ + .AlphaToCoverageEnable = FALSE, + .IndependentBlendEnable = FALSE, + }; + const auto& outputSettings = shaderPassDescription.m_outputs; + assert(outputSettings.size() <= 8/*max render targets currently in Dx12*/); + for (auto i=0; i < outputSettings.size(); ++i) + { + const auto& outputSetting = outputSettings[i]; + blendState.RenderTarget[i] = D3D12_RENDER_TARGET_BLEND_DESC{ + .BlendEnable = outputSetting.blendEnable ? TRUE : FALSE, + .LogicOpEnable = FALSE, + .SrcBlend = EnumToDx12(outputSetting.srcColorBlendFactor), + .DestBlend = EnumToDx12(outputSetting.dstColorBlendFactor), + .BlendOp = D3D12_BLEND_OP_ADD, + .SrcBlendAlpha = EnumToDx12(outputSetting.srcAlphaBlendFactor), + .DestBlendAlpha = EnumToDx12(outputSetting.dstAlphaBlendFactor), + .BlendOpAlpha = D3D12_BLEND_OP_ADD, + .LogicOp = D3D12_LOGIC_OP_NOOP, + .RenderTargetWriteMask = outputSetting.colorWriteMask & (D3D12_COLOR_WRITE_ENABLE_RED | D3D12_COLOR_WRITE_ENABLE_GREEN | D3D12_COLOR_WRITE_ENABLE_BLUE | D3D12_COLOR_WRITE_ENABLE_ALPHA) + }; + } + + // Setup depth testing + D3D12_COMPARISON_FUNC depthFunction; + switch( fixedFunctionSettings.depthCompareOp ) { + case ShaderPassDescription::DepthCompareOp::LessEqual: + depthFunction = D3D12_COMPARISON_FUNC_LESS_EQUAL; + break; + case ShaderPassDescription::DepthCompareOp::Equal: + depthFunction = D3D12_COMPARISON_FUNC_EQUAL; + break; + case ShaderPassDescription::DepthCompareOp::Greater: + depthFunction = D3D12_COMPARISON_FUNC_GREATER; + break; + } + D3D12_DEPTH_STENCIL_DESC depthState { + .DepthEnable = fixedFunctionSettings.depthTestEnable ? TRUE : FALSE, + .DepthWriteMask = fixedFunctionSettings.depthWriteEnable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO, + .DepthFunc = depthFunction, + .StencilEnable = FALSE, + .StencilReadMask = 0xff, + .StencilWriteMask = 0xff, + }; + + DXGI_SAMPLE_DESC sampleDesc { + .Count = EnumToDx12(msaa), + .Quality = 0 + }; + + uint32_t sampleMask = UINT_MAX; + const auto& sampleShadingSettings = shaderPassDescription.m_sampleShadingSettings; + assert(sampleShadingSettings.sampleShadingEnable == false); // not currently implemented + assert(sampleShadingSettings.forceCenterSample == false); // not currently implemented + if (sampleShadingSettings.sampleShadingMask != 0) + { + assert(sampleDesc.Count <= 32 ); // sampleMask is only 32bits currently! Easy fix if we want > 32x MSAA + sampleMask = sampleShadingSettings.sampleShadingMask & ((1u << sampleDesc.Count) -1u); + } + + using tShaderModuleData = ShaderModule::tData; + tShaderModuleData emptyData = {}; + auto [vertData, fragData] = std::visit( [&](auto& m) -> std::pair + { + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + return { m.vert.GetShaderData(), m.frag.GetShaderData() }; + } + else if constexpr (std::is_same_v>) + { + return { m.vert.GetShaderData(), emptyData }; + } + else + { + assert(0); // unsupported shader module type (eg ComputeShaderModule) + return { emptyData, emptyData }; + } + }, shaderModules.m_modules ); + + ComPtr pipelineState; + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc{}; + psoDesc.InputLayout = inputLayout.GetInputLayoutDesc(); + psoDesc.pRootSignature = const_cast( pipelineLayout.GetRootSignature() ); + psoDesc.VS.pShaderBytecode = vertData.data(); + psoDesc.VS.BytecodeLength = vertData.size(); + psoDesc.PS.pShaderBytecode = fragData.data(); + psoDesc.PS.BytecodeLength = fragData.size(); + psoDesc.RasterizerState = rasterizerState; + psoDesc.BlendState = blendState; + psoDesc.DepthStencilState = depthState; + psoDesc.SampleMask = sampleMask; + psoDesc.SampleDesc = sampleDesc; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = outputSettings.size(); + + const auto& renderPassFormats = renderPass.GetRenderTargetFormats(); + psoDesc.NumRenderTargets = (UINT) renderPassFormats.size(); + assert(psoDesc.NumRenderTargets <= 8); + psoDesc.NumRenderTargets = std::min(psoDesc.NumRenderTargets, 8u); + for (auto i=0; i < psoDesc.NumRenderTargets; ++i) + psoDesc.RTVFormats[i] = renderPassFormats[i]; + psoDesc.DSVFormat = renderPass.GetRenderTargetDepthFormat(); + + assert( psoDesc.BlendState.RenderTarget[0].RenderTargetWriteMask == 0xf ); + + if (S_OK != dx12.GetDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState))) + { + // Error + return {}; + } + return Pipeline(std::move(pipelineState)); +} diff --git a/framework/code/material/dx12/pipeline.hpp b/framework/code/material/dx12/pipeline.hpp new file mode 100644 index 0000000..33554c0 --- /dev/null +++ b/framework/code/material/dx12/pipeline.hpp @@ -0,0 +1,66 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "../pipeline.hpp" + +// Forward declarations +class Dx12; +class ShaderPassDescription; +enum class Msaa; +template class PipelineLayout; +template class GraphicsShaderModules; +template class PipelineVertexInputState; +template class ShaderModules; +template class RenderPass; +using namespace Microsoft::WRL; // for ComPtr + + +/// Simple wrapper around Pipeline. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// Specialization of Pipeline +/// @ingroup Material +template<> +class Pipeline final +{ + Pipeline& operator=( const Pipeline& ) = delete; +public: + Pipeline() noexcept; + ~Pipeline(); + Pipeline( Pipeline&& ) noexcept = default; + Pipeline& operator=( Pipeline&& ) noexcept = default; + Pipeline( ComPtr ) noexcept; + + Pipeline Copy() const { return Pipeline{*this}; } + + operator bool() const { return mPipeline; } + + ID3D12PipelineState* GetPipeline() const { return mPipeline.Get(); } + +private: + Pipeline( const Pipeline& src ) noexcept { + mPipeline = src.mPipeline; + } + ComPtr mPipeline; +}; + + +template<> +Pipeline CreatePipeline(Dx12&, + const ShaderPassDescription&, + const PipelineLayout&, + const PipelineVertexInputState& inputLayout, + const PipelineRasterizationState&, + const SpecializationConstants&, + const ShaderModules&, + const RenderPass&, + uint32_t subpass, + Msaa msaa); diff --git a/framework/code/material/dx12/pipelineLayout.cpp b/framework/code/material/dx12/pipelineLayout.cpp new file mode 100644 index 0000000..14a7bb7 --- /dev/null +++ b/framework/code/material/dx12/pipelineLayout.cpp @@ -0,0 +1,120 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "pipelineLayout.hpp" +#include "descriptorSetLayout.hpp" +#include +#include "dx12/dx12.hpp" +#include "texture/dx12/sampler.hpp" + +PipelineLayout::~PipelineLayout() +{ + assert(m_rootSignature == nullptr); // call Destroy prior to object being deleted +} + +void PipelineLayout::Destroy(Dx12&) +{ + m_rootSignature.Reset(); +} + +bool PipelineLayout::Init(Dx12& dx12, const std::span> descriptorTableLayouts, const std::span rootSamplers) +{ + assert(!descriptorTableLayouts.empty()); + + // Require that the first descriptor table (set) layout is for the root signature. + + // Get the root signature layout + const auto& rootSignatureLayout = std::get::RootSignatureParameters>(descriptorTableLayouts[0].GetLayout()); + + using tRangeSpan = std::span ; + std::vector pDescriptorTableRanges; + pDescriptorTableRanges.reserve(descriptorTableLayouts.size()-1); + + // Get the descriptor tables + for (auto i = 1; i < descriptorTableLayouts.size(); ++i) + { + const auto& descriptorTableParameters = std::get::DescriptorTableParameters>(descriptorTableLayouts[i].GetLayout()); + pDescriptorTableRanges.push_back( { std::begin(descriptorTableParameters.ranges), std::end(descriptorTableParameters.ranges) }); + } + + // We may need to copy the root descriptor table so we can modify it (patch in descriptor table pointers) + std::vector rootParametersWritable; + std::span pRootParameters { std::begin(rootSignatureLayout.root), std::end(rootSignatureLayout.root) }; + for (auto i=0; i < pRootParameters.size(); ++i) + { + if (pRootParameters[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + std::copy(pRootParameters.begin(), pRootParameters.end(), std::back_inserter(rootParametersWritable)); + pRootParameters = { rootParametersWritable.begin(), rootParametersWritable.end() }; + + size_t tableIdx = 0; + for (; i < rootParametersWritable.size(); ++i) + { + D3D12_ROOT_PARAMETER& rootParam = rootParametersWritable[i]; + if (rootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + rootParam.DescriptorTable.NumDescriptorRanges = pDescriptorTableRanges[tableIdx].size(); + rootParam.DescriptorTable.pDescriptorRanges = pDescriptorTableRanges[tableIdx].data(); + } + } + + break; + } + } + + D3D12_STATIC_SAMPLER_DESC staticSamplers[]{ { + .Filter = D3D12_FILTER_ANISOTROPIC, + .AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + .AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + .AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + .ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS, + .BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, + .MaxLOD = FLT_MAX + } }; + + std::vector rootSamplersDx12; + UINT i = 0; + std::transform(rootSamplers.begin(), rootSamplers.end(), std::back_inserter(rootSamplersDx12), [&i](const auto& a) -> D3D12_STATIC_SAMPLER_DESC { + auto samplerDesc = StructToDx12( a ); + + return D3D12_STATIC_SAMPLER_DESC{ + .Filter = samplerDesc.Filter, + .AddressU = samplerDesc.AddressU, + .AddressV = samplerDesc.AddressV, + .AddressW = samplerDesc.AddressW, + .MipLODBias = samplerDesc.MipLODBias, + .MaxAnisotropy = samplerDesc.MaxAnisotropy, + .ComparisonFunc = samplerDesc.ComparisonFunc, + .MinLOD = samplerDesc.MinLOD, + .MaxLOD = samplerDesc.MaxLOD, + .ShaderRegister = i++, + .RegisterSpace = 0, + .ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL + }; + }); + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc{ + .NumParameters = (UINT)pRootParameters.size(), + .pParameters = pRootParameters.data(), + .NumStaticSamplers = _countof(staticSamplers), + .pStaticSamplers = staticSamplers, + .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT }; + + ComPtr signatureData; + ComPtr error; + if (!Dx12::CheckError("D3D12SerializeRootSignature", D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signatureData, &error))) + { + return false; + } + + if (!Dx12::CheckError("CreateRootSignature", dx12.GetDevice()->CreateRootSignature(0, signatureData->GetBufferPointer(), signatureData->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)))) + return false; + + return true; +} + diff --git a/framework/code/material/dx12/pipelineLayout.hpp b/framework/code/material/dx12/pipelineLayout.hpp new file mode 100644 index 0000000..9086f8a --- /dev/null +++ b/framework/code/material/dx12/pipelineLayout.hpp @@ -0,0 +1,46 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "../pipelineLayout.hpp" + +// Forward declarations +using namespace Microsoft::WRL; // for ComPtr +class Dx12; +template class DescriptorSetLayout; +struct CreateSamplerObjectInfo; + + +/// Representation of the RootSignature (roughly the DirectX12 equivalent of a Vulkan pipeline layout). +/// template specialization of PipelineLayout +/// Builds/owns the root layout. +/// @ingroup Material +template<> +class PipelineLayout +{ + PipelineLayout& operator=(const PipelineLayout&) = delete; + PipelineLayout(const PipelineLayout&) = delete; +public: + PipelineLayout() noexcept {}; + PipelineLayout(PipelineLayout&&) noexcept = default; + ~PipelineLayout(); + + bool Init(Dx12&, const std::span>, const std::span); + void Destroy(Dx12&); + + operator bool() const { return m_rootSignature.Get() != nullptr; } + + /// Get the root signature + ID3D12RootSignature* GetRootSignature() const { return m_rootSignature.Get(); } + +private: + ComPtr m_rootSignature; +}; diff --git a/framework/code/material/dx12/pipelineVertexInputState.cpp b/framework/code/material/dx12/pipelineVertexInputState.cpp new file mode 100644 index 0000000..2c14863 --- /dev/null +++ b/framework/code/material/dx12/pipelineVertexInputState.cpp @@ -0,0 +1,94 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include +#include +#include "pipelineVertexInputState.hpp" +#include "material/shaderDescription.hpp" +#include "vertexDescription.hpp" + +DXGI_FORMAT EnumToDx12(VertexFormat::Element::ElementType elementType) +{ + switch (elementType.type) + { + case VertexFormat::Element::ElementType::t::Int32: + return DXGI_FORMAT_R32_SINT; + break; + case VertexFormat::Element::ElementType::t::Float: + return DXGI_FORMAT_R32_FLOAT; + break; + case VertexFormat::Element::ElementType::t::Boolean: + return DXGI_FORMAT_R32_UINT; + break; + case VertexFormat::Element::ElementType::t::Vec2: + return DXGI_FORMAT_R32G32_FLOAT; + break; + case VertexFormat::Element::ElementType::t::Vec3: + return DXGI_FORMAT_R32G32B32_FLOAT; + break; + case VertexFormat::Element::ElementType::t::Vec4: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + break; + case VertexFormat::Element::ElementType::t::Int16: + return DXGI_FORMAT_R16_SINT; + break; + case VertexFormat::Element::ElementType::t::Float16: + return DXGI_FORMAT_R16_FLOAT; + break; + case VertexFormat::Element::ElementType::t::F16Vec2: + return DXGI_FORMAT_R16G16_FLOAT; + break; + case VertexFormat::Element::ElementType::t::F16Vec3: + assert(0);//unsupported + return DXGI_FORMAT_UNKNOWN; + break; + case VertexFormat::Element::ElementType::t::F16Vec4: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + break; + case VertexFormat::Element::ElementType::t::Null: + return DXGI_FORMAT_UNKNOWN; + break; + } + assert(0);//unsupported + return DXGI_FORMAT_UNKNOWN; +} + + +PipelineVertexInputState::PipelineVertexInputState(const ShaderDescription& shaderDescription, const std::vector& buffersToBind) noexcept +{ + mInputElementDescs.reserve(buffersToBind.size() * 4/*rough estimate!*/); + + for (uint32_t binding = 0; binding < buffersToBind.size(); ++binding) + { + const auto& vertexFormat = shaderDescription.m_vertexFormats[buffersToBind[binding]]; + VertexDescription vertexDesc { vertexFormat, binding }; + std::copy( std::begin(vertexDesc.GetVertexElementDescs()), std::end(vertexDesc.GetVertexElementDescs()), std::back_inserter(mInputElementDescs)); + } + + /* + mInputElementDescs.reserve(buffersToBind.size()); + uint32_t location = 0; + for (uint32_t b = 0; b < buffersToBind.size(); ++b) + { + const auto& vertexFormat = shaderDescription.m_vertexFormats[buffersToBind[b]]; + for (uint32_t e = 0; e < vertexFormat.elements.size(); ++e) + { + const auto& elementFormat = vertexFormat.elements[e]; + + auto& elementDesc = mInputElementDescs.emplace_back(); + elementDesc.SemanticName = vertexFormat.elementIds[e].c_str(); + elementDesc.SemanticIndex = 0; + elementDesc.Format = EnumToDx12(elementFormat.type); + elementDesc.InputSlot = b; + elementDesc.AlignedByteOffset = elementFormat.offset; + elementDesc.InputSlotClass = vertexFormat.inputRate == VertexFormat::eInputRate::Vertex ? D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA : D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA; + elementDesc.InstanceDataStepRate = vertexFormat.inputRate == VertexFormat::eInputRate::Vertex ? 0 : 1; + } + } + */ +} diff --git a/framework/code/material/dx12/pipelineVertexInputState.hpp b/framework/code/material/dx12/pipelineVertexInputState.hpp new file mode 100644 index 0000000..59dd4ab --- /dev/null +++ b/framework/code/material/dx12/pipelineVertexInputState.hpp @@ -0,0 +1,39 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include "../pipelineVertexInputState.hpp" + + +// Forward declarations +class ShaderDescription; +class Dx12; + + +/// Template specialization for PipelineVertexInputState on Dx12. +/// Empty class since DX12 does not need to have anything equivalient defined. +/// @ingroup Material +template<> +class PipelineVertexInputState +{ + PipelineVertexInputState(const PipelineVertexInputState&) = delete; + PipelineVertexInputState& operator=(const PipelineVertexInputState&) = delete; +public: + PipelineVertexInputState(const ShaderDescription& shaderDescription, const std::vector& buffersToBind /*indices of buffers (in shaderDescription.m_vertexFormats) to bind in this input state*/ ) noexcept; + PipelineVertexInputState(PipelineVertexInputState&&) noexcept = default; + + const auto& GetInputElementDescs() const { return mInputElementDescs; } + D3D12_INPUT_LAYOUT_DESC GetInputLayoutDesc() const { return D3D12_INPUT_LAYOUT_DESC { mInputElementDescs.data(), (uint32_t) mInputElementDescs.size() }; } + +protected: + + std::vector mInputElementDescs; +}; + diff --git a/framework/code/material/dx12/shader.hpp b/framework/code/material/dx12/shader.hpp new file mode 100644 index 0000000..9eb8875 --- /dev/null +++ b/framework/code/material/dx12/shader.hpp @@ -0,0 +1,17 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "shaderModule.hpp" +#include "pipelineLayout.hpp" +#include "pipelineVertexInputState.hpp" +#include "descriptorSetLayout.hpp" +#include "specializationConstantsLayout.hpp" +#include "../shader.hpp" + +using ShaderDx12 = Shader; diff --git a/framework/code/material/dx12/shaderModule.cpp b/framework/code/material/dx12/shaderModule.cpp new file mode 100644 index 0000000..5381a78 --- /dev/null +++ b/framework/code/material/dx12/shaderModule.cpp @@ -0,0 +1,83 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "shaderModule.hpp" +#include +#include "material/shaderDescription.hpp" +//#include "material/vertexDescription.hpp" +#include "system/assetManager.hpp" + +class Dx12; + +ShaderModule::ShaderModule() noexcept : ShaderModuleBase() + , m_Shader{} +{ +} + +ShaderModule::~ShaderModule() +{ + assert(m_Shader.empty()); // expects that Destroy() called before destruction +} + +void ShaderModule::Destroy(Dx12& dx12) +{ + m_Shader = {}; + ShaderModuleBase::Destroy(); +} + +bool ShaderModule::Load(Dx12& dx12, AssetManager& assetManager, const std::string& filename) +{ + bool success = true; + Destroy(dx12); + + m_filename = filename; + + if (!m_filename.empty()) + { + if ( assetManager.LoadFileIntoMemory(m_filename.c_str(), m_Shader) ) + { + } + else + { + success = false; + } + } + return success; +} + +bool ShaderModule::Load(Dx12& dx12, AssetManager& assetManager, const ShaderPassDescription& shaderDescription, const ShaderType shaderType) +{ + const std::string* shaderFileName = nullptr; + switch (shaderType) + { + case ShaderType::Fragment: + shaderFileName = &shaderDescription.m_fragmentName; + break; + case ShaderType::Vertex: + shaderFileName = &shaderDescription.m_vertexName; + break; + case ShaderType::Compute: + shaderFileName = &shaderDescription.m_computeName; + break; + case ShaderType::RayGeneration: + shaderFileName = &shaderDescription.m_rayGenerationName; + break; + case ShaderType::RayClosestHit: + shaderFileName = &shaderDescription.m_rayClosestHitName; + break; + case ShaderType::RayAnyHit: + shaderFileName = &shaderDescription.m_rayAnyHitName; + break; + case ShaderType::RayMiss: + shaderFileName = &shaderDescription.m_rayMissName; + break; + } + + assert(shaderFileName!=nullptr); + return Load(dx12, assetManager, *shaderFileName); ///TODO: better return code / error handling +} diff --git a/framework/code/material/dx12/shaderModule.hpp b/framework/code/material/dx12/shaderModule.hpp new file mode 100644 index 0000000..d68477a --- /dev/null +++ b/framework/code/material/dx12/shaderModule.hpp @@ -0,0 +1,51 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include +#include "material/shaderModule.hpp" + +// Forward declarations +class AssetManager; +class DescriptorSetLayoutBase; +class ShaderPassDescription; +class VertexDescription; +class VertexFormat; +class Dx12; + +/// Wrapper around a Dx12 VkShaderModule. +/// @ingroup Material +template<> +class ShaderModule : public ShaderModuleBase +{ + ShaderModule(const ShaderModule&) = delete; + ShaderModule& operator=(const ShaderModule&) = delete; +public: + typedef std::vector tData; + ShaderModule() noexcept; + ~ShaderModule(); + + /// Free up the shader memory. + void Destroy(Dx12&); + + /// Load the shader binary with the given shader name + /// @returns true on success + bool Load(Dx12& vulkan, AssetManager& assetManager, const std::string& filename); + + /// Load the shader binary for the given shader type (using ShaderPassDescription to get the appropriate shader name). + /// @returns true on success + bool Load(Dx12&, AssetManager&, const ShaderPassDescription&, const ShaderType); + + const tData& GetShaderData() const { return m_Shader; } +private: + tData m_Shader; +}; diff --git a/framework/code/material/dx12/specializationConstantsLayout.hpp b/framework/code/material/dx12/specializationConstantsLayout.hpp new file mode 100644 index 0000000..3324785 --- /dev/null +++ b/framework/code/material/dx12/specializationConstantsLayout.hpp @@ -0,0 +1,31 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include "material/specializationConstantsLayout.hpp" + +// Forward declarations +class Dx12; + +/// Template specialization for specialization constants, not supported by dx12 (so this is just stub) +/// @ingroup Material +template<> +class SpecializationConstantsLayout +{ + SpecializationConstantsLayout( const SpecializationConstantsLayout& ) = delete; + SpecializationConstantsLayout& operator=( const SpecializationConstantsLayout& ) = delete; +public: + explicit SpecializationConstantsLayout( const std::span ) noexcept {}; + SpecializationConstantsLayout(SpecializationConstantsLayout && other) noexcept = default; + SpecializationConstantsLayout& operator=( SpecializationConstantsLayout&& ) noexcept = delete; + +private: +}; + diff --git a/framework/code/material/dx12/vertexDescription.cpp b/framework/code/material/dx12/vertexDescription.cpp new file mode 100644 index 0000000..8947818 --- /dev/null +++ b/framework/code/material/dx12/vertexDescription.cpp @@ -0,0 +1,147 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "vertexDescription.hpp" +#include +#include +#include +#include "system/os_common.h" + + +// Forward declaration +static std::pair ParseSemanticName(const std::string& name); + + +VertexDescription::VertexDescription(const VertexFormat& format, uint32_t binding) +{ + m_Stride = format.span; + m_Binding = binding; + + m_VertexElementDescs.reserve( format.elements.size() ); + + // THIS NEEDS TO BE FILLED IN FOR INSTANCE DATA TO WORK (FOR NOW JUST ASSERT) + uint32_t verticesPerInstance = 0; + assert(format.inputRate != VertexFormat::eInputRate::Instance); + // END + + uint32_t locationIdx = 0; + for( auto i = 0; i semantic = ParseSemanticName(elementId); + if (semantic.first == nullptr) + { + LOGE("Invalid semantic name (\"%s\") in vertex format.", elementId.c_str()); + } + + D3D12_INPUT_ELEMENT_DESC elementDesc{ + .SemanticName = semantic.first, + .SemanticIndex = semantic.second, + .Format = DxgiFormatFromElementType(element.type), + .InputSlot = binding, + .AlignedByteOffset = element.offset, + .InputSlotClass = format.inputRate == VertexFormat::eInputRate::Vertex ? D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA : D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, + .InstanceDataStepRate = format.inputRate == VertexFormat::eInputRate::Vertex ? 0 : verticesPerInstance, + }; + m_VertexElementDescs.push_back(elementDesc); + } +} + +DXGI_FORMAT VertexDescription::DxgiFormatFromElementType( const VertexFormat::Element::ElementType& elementType ) +{ + switch(elementType.type) { + case VertexFormat::Element::ElementType::t::Int32: + return DXGI_FORMAT_R32_SINT; + case VertexFormat::Element::ElementType::t::UInt32: + return DXGI_FORMAT_R32_UINT; + case VertexFormat::Element::ElementType::t::Float: + return DXGI_FORMAT_R32_FLOAT; + case VertexFormat::Element::ElementType::t::Boolean: + return DXGI_FORMAT_R32_UINT; + case VertexFormat::Element::ElementType::t::Vec2: + return DXGI_FORMAT_R32G32_FLOAT; + case VertexFormat::Element::ElementType::t::Vec3: + return DXGI_FORMAT_R32G32B32_FLOAT; + case VertexFormat::Element::ElementType::t::Vec4: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case VertexFormat::Element::ElementType::t::IVec2: + return DXGI_FORMAT_R32G32_SINT; + case VertexFormat::Element::ElementType::t::IVec3: + return DXGI_FORMAT_R32G32B32_SINT; + case VertexFormat::Element::ElementType::t::IVec4: + return DXGI_FORMAT_R32G32B32A32_SINT; + case VertexFormat::Element::ElementType::t::UVec2: + return DXGI_FORMAT_R32G32_UINT; + case VertexFormat::Element::ElementType::t::UVec3: + return DXGI_FORMAT_R32G32B32_UINT; + case VertexFormat::Element::ElementType::t::UVec4: + return DXGI_FORMAT_R32G32B32A32_UINT; + case VertexFormat::Element::ElementType::t::Int16: + return DXGI_FORMAT_R16_SINT; + case VertexFormat::Element::ElementType::t::UInt16: + return DXGI_FORMAT_R16_UINT; + case VertexFormat::Element::ElementType::t::Float16: + return DXGI_FORMAT_R16_FLOAT; + case VertexFormat::Element::ElementType::t::F16Vec2: + return DXGI_FORMAT_R16G16_FLOAT; + //case VertexFormat::Element::ElementType::t::F16Vec3: + // return DXGI_FORMAT_R16G16B16_FLOAT;// undefined in DXGI + case VertexFormat::Element::ElementType::t::F16Vec4: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case VertexFormat::Element::ElementType::t::I16Vec2: + return DXGI_FORMAT_R16G16_SINT; + //case VertexFormat::Element::ElementType::t::I16Vec3: + // return DXGI_FORMAT_R16G16B16_SINT;// undefined in DXGI + case VertexFormat::Element::ElementType::t::I16Vec4: + return DXGI_FORMAT_R16G16B16A16_SINT; + case VertexFormat::Element::ElementType::t::U16Vec2: + return DXGI_FORMAT_R16G16_UINT; + //case VertexFormat::Element::ElementType::t::U16Vec3: + // return DXGI_FORMAT_R16G16B16_UINT;// undefined in DXGI + case VertexFormat::Element::ElementType::t::U16Vec4: + return DXGI_FORMAT_R16G16B16A16_UINT; + + default: + assert(0); + return DXGI_FORMAT_UNKNOWN; + break; + } +} + +// Searchable set of supported vertex shader semantic names. +// Because we created the name stringviews from 'C' char strings we can be confident they are \0 terminated although this is not guaranteed by the string_view spec. +static std::set sSemanticNames{ + {"BINORMAL"}, + {"BLENDINDICES"}, + {"BLENDWEIGHT"}, + {"COLOR"}, + {"NORMAL"}, + {"POSITION"}, + {"POSITIONT"}, + {"PSIZE"}, + {"TANGENT"}, + {"TEXCOORD"}}; + +static std::pair ParseSemanticName(const std::string& name) +{ + uint32_t semanticIndex = 0; + + auto numStartIt = std::find_if(name.begin(), name.end(), [](char c)->bool { return isdigit(c) != 0; }); + if (numStartIt != name.end()) + { + semanticIndex = strtol(&(*numStartIt), nullptr, 10); + } + auto semanticIt = std::find(sSemanticNames.begin(), sSemanticNames.end(), std::string_view(name)); + if (semanticIt == sSemanticNames.end()) + return {}; + else + return { semanticIt->data(), semanticIndex}; +} + diff --git a/framework/code/material/dx12/vertexDescription.hpp b/framework/code/material/dx12/vertexDescription.hpp new file mode 100644 index 0000000..75eb9a8 --- /dev/null +++ b/framework/code/material/dx12/vertexDescription.hpp @@ -0,0 +1,31 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "material/vertexFormat.hpp" +#include + +/// Describes the Dx12 elements (layout) for a single vertex stream (not the buffer, just one of the the contained vertices). +/// @ingroup Material +class VertexDescription +{ + VertexDescription(const VertexDescription&) = delete; + VertexDescription& operator=(const VertexDescription&) = delete; +public: + VertexDescription(const VertexFormat&, uint32_t binding); + + size_t GetStride() const { return m_Stride; } + uint32_t GetBinding() const { return m_Binding; } + const std::vector& GetVertexElementDescs() const { return m_VertexElementDescs; } + + static DXGI_FORMAT DxgiFormatFromElementType(const VertexFormat::Element::ElementType& elementType); +protected: + size_t m_Stride; + uint32_t m_Binding; + std::vector m_VertexElementDescs; +}; diff --git a/framework/code/material/material.cpp b/framework/code/material/material.cpp index 2694527..00afff8 100644 --- a/framework/code/material/material.cpp +++ b/framework/code/material/material.cpp @@ -7,462 +7,25 @@ //============================================================================================================ #include "material.hpp" -#include "shader.hpp" -#include "vulkan/vulkan.hpp" -#include -#include "system/os_common.h" -#include "vulkan/TextureFuncts.h" -#include "vulkan/material.hpp" -#include "texture/vulkan/texture.hpp" - - -MaterialPass::MaterialPass(Vulkan& vulkan, const ShaderPass& shaderPass, VkDescriptorPool&& descriptorPool, std::vector&& descriptorSets, std::vector&& dynamicDescriptorSetLayouts, tTextureBindings&& textureBindings, tImageBindings&& imageBindings, tBufferBindings&& bufferBindings, tAccelerationStructureBindings&& accelerationStructureBindings, SpecializationConstants&& specializationConstants ) - : mShaderPass(shaderPass) - , mVulkan( vulkan ) - , mNumDescriptorSetsPerBuffer(uint32_t(shaderPass.GetDescriptorSetLayouts().size())) - , mNumBuffers(mNumDescriptorSetsPerBuffer>0 ? uint32_t(descriptorSets.size() / mNumDescriptorSetsPerBuffer) : 0) - , mDescriptorPool(descriptorPool) - , mDescriptorSets(std::move(descriptorSets)) - , mDynamicDescriptorSetLayouts(std::move(dynamicDescriptorSetLayouts)) - , mSpecializationConstants( std::move( specializationConstants ) ) - , mTextureBindings(std::move(textureBindings)) - , mImageBindings(std::move(imageBindings)) - , mBufferBindings(std::move(bufferBindings)) - , mAccelerationStructureBindings( std::move( accelerationStructureBindings ) ) -{ - if (!mDynamicDescriptorSetLayouts.empty()) - mDynamicPipelineLayout.Init(vulkan, mDynamicDescriptorSetLayouts); - assert( mDescriptorSets.size() == mNumBuffers*mNumDescriptorSetsPerBuffer ); - - descriptorPool = VK_NULL_HANDLE; // we took owenership -} - -MaterialPass::MaterialPass(MaterialPass&& other) noexcept - : mShaderPass(other.mShaderPass) - , mVulkan( other.mVulkan ) - , mNumDescriptorSetsPerBuffer( other.mNumDescriptorSetsPerBuffer ) - , mNumBuffers( other.mNumBuffers ) - , mDescriptorPool(other.mDescriptorPool) - , mDescriptorSets(std::move(other.mDescriptorSets)) - , mDynamicDescriptorSetLayouts(std::move(other.mDynamicDescriptorSetLayouts)) - , mDynamicPipelineLayout(std::move(other.mDynamicPipelineLayout)) - , mAccelerationStructureBindings( std::move( other.mAccelerationStructureBindings ) ) - , mTextureBindings(std::move(other.mTextureBindings)) - , mImageBindings(std::move(other.mImageBindings)) - , mBufferBindings(std::move(other.mBufferBindings)) - , mSpecializationConstants( std::move( other.mSpecializationConstants ) ) -{ - other.mDescriptorPool = VK_NULL_HANDLE; -} - -MaterialPass::~MaterialPass() -{ - if (mDescriptorPool != VK_NULL_HANDLE) - { - //if (mDescriptorSet != VK_NULL_HANDLE) - // vkFreeDescriptorSets(mVulkan.m_VulkanDevice, mDescriptorPool, 1, &mDescriptorSet); only if VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT - vkDestroyDescriptorPool(mVulkan.m_VulkanDevice, mDescriptorPool, nullptr); - } - - mDynamicPipelineLayout.Destroy(mVulkan); - - for(auto& layout: mDynamicDescriptorSetLayouts) - vkDestroyDescriptorSetLayout(mVulkan.m_VulkanDevice, layout, nullptr); -} - -ImageInfo::ImageInfo(const TextureT& t) - : image(t.GetVkImage()) - , imageView(t.GetVkImageView()) - , imageViewNumMips(t.MipLevels) - , imageViewFirstMip(t.FirstMip) - , imageLayout( t.GetVkImageLayout() ) -{ -} - -ImageInfo::ImageInfo(const Texture& t) : ImageInfo( apiCast(t) ) -{ -} - -ImageInfo::ImageInfo(const ImageInfo& src) - : image(src.image) - , imageView(src.imageView) - , imageLayout( src.imageLayout ) - , imageViewNumMips( src.imageViewNumMips ) - , imageViewFirstMip( src.imageViewFirstMip ) -{ -} - -ImageInfo::ImageInfo( ImageInfo&& src ) noexcept - : image( src.image ) - , imageView( src.imageView ) - , imageLayout( src.imageLayout ) - , imageViewNumMips( src.imageViewNumMips ) - , imageViewFirstMip( src.imageViewFirstMip ) -{ - src.image = VK_NULL_HANDLE; - src.imageView = VK_NULL_HANDLE; - src.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - src.imageViewNumMips = 0; - src.imageViewFirstMip = 0; -} - -ImageInfo& ImageInfo::operator=(ImageInfo&& src) noexcept -{ - if (this != &src) - { - image = src.image; - imageView = src.imageView; - imageLayout = src.imageLayout; - imageViewNumMips = src.imageViewNumMips; - imageViewFirstMip = src.imageViewFirstMip; - src.image = VK_NULL_HANDLE; - src.imageView = VK_NULL_HANDLE; - src.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - src.imageViewNumMips = 0; - src.imageViewFirstMip = 0; - } - return *this; -} - -bool MaterialPass::UpdateDescriptorSets(uint32_t bufferIdx) -{ - // TODO: All these need to become vectors that can dynamically grow - static const int cMAX_WRITES = 32; - static const int cMAX_IMAGE_INFOS = 1048; - static const int cMAX_BUFFER_INFOS = 1048; - static const int cMAX_ACCELERATIONSTRUCTURE_INFOS = 32; - std::array writeInfo{/*zero it*/ }; - std::array imageInfo{/*zero it*/ }; - std::array bufferInfoFixed/*not initialized*/; - std::vector bufferInfoDynamic; - - // std::vector - - uint32_t writeInfoIdx = 0; - uint32_t imageInfoCount = 0; - uint32_t bufferInfoCount = 0; - const size_t numDescriptorSetsPerFrame = mShaderPass.GetDescriptorSetLayouts().size(); - const auto descriptorSetBaseIdx = bufferIdx * numDescriptorSetsPerFrame; - - // Go through the textures first - for (const auto& textureBinding : mTextureBindings) - { - uint32_t setIndex = textureBinding.second.setIndex; - uint32_t bindingIndex = textureBinding.second.setBinding.index; - VkDescriptorType bindingType = textureBinding.second.setBinding.type; - - uint32_t numTexToBind = textureBinding.second.setBinding.isArray ? (uint32_t)textureBinding.first.size() : 1; - uint32_t texIndex = textureBinding.second.setBinding.isArray ? 0 : (bufferIdx % textureBinding.first.size()); - - writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeInfo[writeInfoIdx].descriptorType = bindingType; - writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; - writeInfo[writeInfoIdx].dstBinding = bindingIndex; - writeInfo[writeInfoIdx].dstArrayElement = 0; - writeInfo[writeInfoIdx].descriptorCount = numTexToBind; - writeInfo[writeInfoIdx].pBufferInfo = nullptr; - writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; - for (uint32_t t = 0; t < numTexToBind; ++t, ++imageInfoCount, ++texIndex) - { - if (imageInfoCount >= cMAX_IMAGE_INFOS) - { - LOGE("Max number (%d) of VkDescriptorImageInfo elements reached!", cMAX_IMAGE_INFOS); - assert(0); - return false; - } - - imageInfo[imageInfoCount] = apiCast(textureBinding.first[texIndex])->GetVkDescriptorImageInfo(); - } - - ++writeInfoIdx; - if (writeInfoIdx >= cMAX_WRITES) - { - LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); - assert(0); - return false; - } - } - - // Go through the images - for (const auto& imageBinding : mImageBindings) - { - uint32_t setIndex = imageBinding.second.setIndex; - uint32_t bindingIndex = imageBinding.second.setBinding.index; - VkDescriptorType bindingType = imageBinding.second.setBinding.type; - assert(bindingType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && bindingType != VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); // combined image sampler or sampled image should go through mTextureBindings - //assert(imageBinding.first.imageLayout == VK_IMAGE_LAYOUT_GENERAL); - - uint32_t numImgToBind = imageBinding.second.setBinding.isArray ? (uint32_t)imageBinding.first.size() : 1; - uint32_t imgIndex = imageBinding.second.setBinding.isArray ? 0 : (bufferIdx % imageBinding.first.size()); - - writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeInfo[writeInfoIdx].descriptorType = bindingType; - writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; - writeInfo[writeInfoIdx].dstBinding = bindingIndex; - writeInfo[writeInfoIdx].dstArrayElement = 0; - writeInfo[writeInfoIdx].descriptorCount = numImgToBind; - writeInfo[writeInfoIdx].pBufferInfo = nullptr; - writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; - for (uint32_t t = 0; t < numImgToBind; ++t, ++imageInfoCount, ++imgIndex) - { - if (imageInfoCount >= cMAX_IMAGE_INFOS) - { - LOGE("Max number (%d) of VkDescriptorImageInfo elements reached!", cMAX_IMAGE_INFOS); - assert(0); - return false; - } - - imageInfo[imageInfoCount].sampler = VK_NULL_HANDLE; - imageInfo[imageInfoCount].imageView = imageBinding.first[imgIndex].imageView; - imageInfo[imageInfoCount].imageLayout = imageBinding.first[imgIndex].imageLayout; - } - ++writeInfoIdx; - - if (writeInfoIdx >= cMAX_WRITES) - { - LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); - assert(0); - return false; - } - } - - // Now do the buffers - - for (const auto& bufferBinding : mBufferBindings) - { - bufferInfoCount += bufferBinding.second.setBinding.isArray ? (uint32_t) bufferBinding.first.size() : 1; - } - auto* pBufferInfo = bufferInfoFixed.data(); - if (bufferInfoCount > cMAX_BUFFER_INFOS) - { - bufferInfoDynamic.resize( bufferInfoCount ); - pBufferInfo = bufferInfoDynamic.data(); - } - - for (const auto& bufferBinding : mBufferBindings) - { - uint32_t setIndex = bufferBinding.second.setIndex; - uint32_t bindingIndex = bufferBinding.second.setBinding.index; - VkDescriptorType bindingType = bufferBinding.second.setBinding.type; - - uint32_t numBuffersToBind = bufferBinding.second.setBinding.isArray ? (uint32_t)bufferBinding.first.size() : 1; - uint32_t bufferIndex = bufferBinding.second.setBinding.isArray ? 0 : (bufferIdx % bufferBinding.first.size()); - - writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeInfo[writeInfoIdx].descriptorType = bindingType; - writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; - writeInfo[writeInfoIdx].dstBinding = bindingIndex; - writeInfo[writeInfoIdx].dstArrayElement = 0; - writeInfo[writeInfoIdx].descriptorCount = numBuffersToBind; - writeInfo[writeInfoIdx].pBufferInfo = pBufferInfo; - writeInfo[writeInfoIdx].pImageInfo = nullptr; - - for (uint32_t t = 0; t < numBuffersToBind; ++t, ++bufferIndex) - { - pBufferInfo->buffer = bufferBinding.first[bufferIndex].buffer; - pBufferInfo->offset = bufferBinding.first[bufferIndex].offset; - pBufferInfo->range = VK_WHOLE_SIZE; - ++pBufferInfo; - } - - ++writeInfoIdx; - if (writeInfoIdx >= cMAX_WRITES) - { - LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); - assert(0); - return false; - } - } - -#if VK_KHR_acceleration_structure - // And the acceleration structures - std::array accelerationStructureInfo{}; - uint32_t accelerationStructureCount = 0; - - for (const auto& accelerationBinding : mAccelerationStructureBindings) - { - uint32_t setIndex = accelerationBinding.second.setIndex; - uint32_t bindingIndex = accelerationBinding.second.setBinding.index; - VkDescriptorType bindingType = accelerationBinding.second.setBinding.type; - - uint32_t numAccelToBind = accelerationBinding.second.setBinding.isArray ? (uint32_t)accelerationBinding.first.size() : 1; - uint32_t accelIndex = accelerationBinding.second.setBinding.isArray ? 0 : (bufferIdx < accelerationBinding.first.size() ? bufferIdx : 0); - - if (writeInfoIdx >= cMAX_WRITES) - { - LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); - assert(0); - return false; - } - - if (accelerationStructureCount >= cMAX_ACCELERATIONSTRUCTURE_INFOS) - { - LOGE("Max number (%d) of VkWriteDescriptorSetAccelerationStructureKHR elements reached!", cMAX_ACCELERATIONSTRUCTURE_INFOS); - assert(0); - return false; - } - - const VkAccelerationStructureKHR * pAs = &accelerationBinding.first[accelIndex]; - accelerationStructureInfo[accelerationStructureCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; - accelerationStructureInfo[accelerationStructureCount].accelerationStructureCount = 1; - accelerationStructureInfo[accelerationStructureCount].pAccelerationStructures = pAs; - - writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeInfo[writeInfoIdx].descriptorType = bindingType;//VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR - writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; - writeInfo[writeInfoIdx].dstBinding = bindingIndex; - writeInfo[writeInfoIdx].dstArrayElement = 0; - writeInfo[writeInfoIdx].descriptorCount = accelerationStructureInfo[accelerationStructureCount].accelerationStructureCount; - writeInfo[writeInfoIdx].pBufferInfo = nullptr; - writeInfo[writeInfoIdx].pImageInfo = nullptr; - writeInfo[writeInfoIdx].pNext = &accelerationStructureInfo[accelerationStructureCount]; - ++writeInfoIdx; - ++accelerationStructureCount; - - } -#endif // VK_KHR_acceleration_structure - - // LOGI("Updating Descriptor Set (bufferIdx %d) with %d objects", bufferIdx, writeInfoIdx); - vkUpdateDescriptorSets(mVulkan.m_VulkanDevice, writeInfoIdx, writeInfo.data(), 0, NULL); - // LOGI("Descriptor Set Updated!"); - - return true; -} - -bool MaterialPass::UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const TextureT& newTexture) const -{ - std::array writeInfo{ {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET} }; - std::array imageInfo{/*zero it*/ }; - - uint32_t writeInfoIdx = 0; - uint32_t imageInfoCount = 0; - - for (int setIdx = 0; const auto & setLayout : mShaderPass.GetDescriptorSetLayouts()) - { - const auto& nameToBinding = setLayout.GetNameToBinding(); - const auto bindingIt = nameToBinding.find( bindingName ); - if (bindingIt != nameToBinding.end()) - { - const auto& descriptorSet = GetVkDescriptorSet( bufferIdx, setIdx ); - VkDescriptorType bindingType = bindingIt->second.type; - assert( bindingType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || bindingType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ); - - uint32_t bindingIndex = bindingIt->second.index; - - writeInfo[writeInfoIdx].descriptorType = bindingType; - writeInfo[writeInfoIdx].dstSet = descriptorSet; - writeInfo[writeInfoIdx].dstBinding = bindingIndex; - writeInfo[writeInfoIdx].dstArrayElement = 0; - writeInfo[writeInfoIdx].descriptorCount = 1; - writeInfo[writeInfoIdx].pBufferInfo = nullptr; - writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; - imageInfo[imageInfoCount] = newTexture.GetVkDescriptorImageInfo(); - - ++imageInfoCount; - ++writeInfoIdx; - - vkUpdateDescriptorSets( mVulkan.m_VulkanDevice, writeInfoIdx, writeInfo.data(), 0, NULL ); - return true; - } - ++setIdx; - } - assert( 0 && "Binding name not found" ); - return false; -} - +#include // -// Material class implementation +// MaterialBase base class implementation // -Material::Material(const ShaderT& shader, uint32_t numFramebuffers) - : m_shader(shader) - , m_numFramebuffers(numFramebuffers) +MaterialBase::MaterialBase(const ShaderBase& shader, uint32_t numFramebuffers) +: m_shader(shader) +, m_numFramebuffers(numFramebuffers) { - assert(m_numFramebuffers > 0); + assert(m_numFramebuffers > 0); } -Material::Material(Material&& other) noexcept - : m_shader(other.m_shader) - , m_materialPassNamesToIndex(std::move(other.m_materialPassNamesToIndex)) - , m_materialPasses(std::move(other.m_materialPasses)) - , m_numFramebuffers(other.m_numFramebuffers) +MaterialBase::MaterialBase(MaterialBase&& other) noexcept + : m_shader(other.m_shader) + , m_materialPassNamesToIndex(std::move(other.m_materialPassNamesToIndex)) + , m_numFramebuffers(other.m_numFramebuffers) { } -Material::~Material() +MaterialBase::~MaterialBase() {} - - -const MaterialPass* Material::GetMaterialPass(const std::string& passName) const -{ - auto it = m_materialPassNamesToIndex.find(passName); - if (it != m_materialPassNamesToIndex.end()) - { - return &m_materialPasses[it->second]; - } - return nullptr; -} - -bool Material::UpdateDescriptorSets(uint32_t bufferIdx) -{ - // Just go through and update the MaterialPass descriptor sets. - // Could be much smarter (and could handle failures better than just continuing and reporting something went wrong) - bool success = true; - for (auto& materialPass : m_materialPasses) - { - success &= materialPass.UpdateDescriptorSets(bufferIdx); - } - return success; -} - -bool Material::UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const TextureT& newTexture) const -{ - bool success = true; - for (auto& materialPass : m_materialPasses) - { - success &= materialPass.UpdateDescriptorSetBinding(bufferIdx, bindingName, newTexture); - } - return success; -} - -bool SpecializationConstants::Init( const SpecializationConstantsLayout& layout, const std::span constants ) -{ - const auto& layoutMapEntry = layout.GetVkSpecializationMapEntry(); - assert( layoutMapEntry.size() == constants.size() ); - if (layoutMapEntry.empty()) - { - mSpecializationData.reset(); - return true; - } - - std::span specializationConstantsRaw { new std::byte[layout.GetBufferSize()], layout.GetBufferSize() }; // unsafe - raw pointer in the span! - - for (auto constantIdx = 0; constantIdx < layoutMapEntry.size(); ++constantIdx) - { - const auto& constantLayout = layoutMapEntry[constantIdx]; - - // copy the loaded constant data into the constant buffer - std::span constantDataRaw = constants[constantIdx].getUnsafeData(); - assert( constantLayout.size == constantDataRaw.size() ); - std::copy( constantDataRaw.begin(), constantDataRaw.end(), specializationConstantsRaw.begin() + constantLayout.offset ); - } - - VkSpecializationInfo vkSpecializationInfo {}; - vkSpecializationInfo.mapEntryCount = (uint32_t) layout.GetVkSpecializationMapEntry().size(); - vkSpecializationInfo.pMapEntries = layout.GetVkSpecializationMapEntry().data(); - vkSpecializationInfo.dataSize = specializationConstantsRaw.size(); - vkSpecializationInfo.pData = specializationConstantsRaw.data(); // move ownership of the allocated buffer. - - mSpecializationData.emplace().specializationInfo = vkSpecializationInfo; - - return true; -} - -SpecializationConstants::VulkanSpecializationData::VulkanSpecializationData( SpecializationConstants::VulkanSpecializationData&& other ) noexcept : specializationInfo( other.specializationInfo )/*dumb move*/ -{ - other.specializationInfo.mapEntryCount = 0; - other.specializationInfo.pMapEntries = nullptr; - other.specializationInfo.dataSize = 0; - other.specializationInfo.pData = nullptr; -} diff --git a/framework/code/material/material.hpp b/framework/code/material/material.hpp index 30572ed..0190fe1 100644 --- a/framework/code/material/material.hpp +++ b/framework/code/material/material.hpp @@ -1,260 +1,41 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include -#include -#include #include -#include -#ifdef OS_WINDOWS -#define VK_ENABLE_BETA_EXTENSIONS -#endif -#include -#include "descriptorSetLayout.hpp" -#include "vulkan/pipelineLayout.hpp" -#include "vulkan/shader.hpp" -#include "vulkan/specializationConstantsLayout.hpp" -// Forward declarations -class Vulkan; -template class TextureT; -class Texture; -class VertexElementData; - -/// @defgroup Material -/// Material and Shader loader. -/// Handles creation of descriptor sets, buffer binding, shader binaries (everything in Vulkan that describes and is used to render a shader). -/// -/// Typically user (application) writes a Json description for each 'Shader' that describes the inputs, outputs, and internal state of the shader, and the shader code (glsl). -/// The user then uses ShaderManager::AddShader to register (and load) each Json file and the shader binaries. -/// -/// From there the user uses MaterialManager::CreateMaterial to create a Material instance of the Shader (a Material contains bindings to the various texture/buffer inputs that a Shader requires - there can be many Materials using the same Shader (each Material with different textures, vertex buffers and/or uniform buffers etc) -/// -/// The Material returned by CreateMaterial can be used to Create a Drawable or Computable object that wraps everything together with one convenient interface! -/// -/// For more complex models the user should use DrawableLoader::LoadDrawable to load the mesh model file (and return a vector of Drawables). This api greatly simplifies the material creation and binding, splitting model meshes across material boundaries, automatically detecting instances (optionally). -/// - -/// Reference to a VkImage. -/// Does not have ownership over referenced VkImage or VkImageView and lifetime of those object should be longer than the referencing @ImageInfo (no reference counting). -/// @ingroup Material -struct ImageInfo { - ImageInfo(ImageInfo&&) noexcept; - ImageInfo& operator=(ImageInfo&&) noexcept; - ImageInfo& operator=(const ImageInfo&) = delete; - ImageInfo(const ImageInfo&); - ImageInfo(const TextureT&); - ImageInfo(const Texture&); - VkImage image; - VkImageView imageView; - uint32_t imageViewNumMips; - uint32_t imageViewFirstMip; - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_GENERAL; -}; - -/// Reference to VkBuffer (with an offset). -/// Does not have ownership of the buffer (and so lifetime of the buffer should be longer than the referencing @VkBufferAndOffset) -struct VkBufferAndOffset { - VkBuffer buffer; - uint32_t offset = 0; - constexpr bool operator==( const VkBufferAndOffset& other ) const = default; -}; - -struct tPerFrameVkBuffer -{ - typedef std::vector tBuffers; - tBuffers buffers; - - template< typename InputIt> requires ( std::is_same_v::value_type, VkBuffer > ) - tPerFrameVkBuffer( InputIt first, InputIt last ) - { - while (first != last) - buffers.push_back( { *first++, 0 } ); - } - template< typename InputIt > requires (std::is_same_v) - tPerFrameVkBuffer( InputIt first, InputIt last ) : buffers( first, last ) - { - } - - template() ))>> - tPerFrameVkBuffer( const InputContainer& container ) - { - for (auto item : container) - buffers.push_back( { item } ); - } - tPerFrameVkBuffer( const VkBufferAndOffset& bufferAndOffset ) noexcept - { - buffers.push_back( bufferAndOffset ); - } - tPerFrameVkBuffer( VkBuffer buffer ) noexcept - { - buffers.push_back( { buffer, 0 } ); - } - explicit tPerFrameVkBuffer( tPerFrameVkBuffer&& other ) noexcept : buffers( std::move( other.buffers ) ) {} - tPerFrameVkBuffer& operator=( tPerFrameVkBuffer&& other ) noexcept { - if (this != &other) { - buffers = std::move( other.buffers ); - } - return *this; - } - tPerFrameVkBuffer( const tPerFrameVkBuffer& other ) noexcept : buffers( other.buffers ) {} - tPerFrameVkBuffer& operator=( const tPerFrameVkBuffer& other ) noexcept { - if (this != &other) { - buffers = other.buffers; - } - return *this; - } - tPerFrameVkBuffer() = default; - - constexpr size_t size() const noexcept { return buffers.size(); } - constexpr tBuffers::iterator begin() noexcept { return buffers.begin(); } - constexpr tBuffers::const_iterator begin() const noexcept { return buffers.begin(); } - constexpr tBuffers::const_iterator cbegin() const noexcept { return buffers.begin(); } - constexpr tBuffers::iterator end() { return buffers.end(); } - constexpr tBuffers::const_iterator end() const { return buffers.end(); } - constexpr tBuffers::const_iterator cend() const { return buffers.cend(); } - constexpr tBuffers::value_type& operator[]( const tBuffers::size_type x ) noexcept { return buffers[x]; } - constexpr const tBuffers::value_type& operator[]( const tBuffers::size_type x ) const noexcept { return buffers[x]; } -}; - -/// Specialization constant data for a MaterialPass. -/// @ingroup Material -class SpecializationConstants -{ - SpecializationConstants( const SpecializationConstants& ) = delete; - SpecializationConstants operator=( const SpecializationConstants& ) = delete; -public: - SpecializationConstants( SpecializationConstants&& ) noexcept = default; - SpecializationConstants() noexcept = default; - bool Init(const SpecializationConstantsLayout& layout, const std::span constants); - - const VkSpecializationInfo* GetVkSpecializationInfo() const { return mSpecializationData.has_value() ? &mSpecializationData.value().specializationInfo : nullptr; } - -private: - struct VulkanSpecializationData - { - VulkanSpecializationData( const VulkanSpecializationData& ) = delete; - VulkanSpecializationData operator=( const VulkanSpecializationData& ) = delete; - - VulkanSpecializationData() noexcept : specializationInfo() {} - ~VulkanSpecializationData() { delete[] (std::byte*) specializationInfo.pData/*we own just the data*/; specializationInfo.pData = nullptr; } - VulkanSpecializationData( VulkanSpecializationData&& ) noexcept; - VkSpecializationInfo specializationInfo; - }; - // container for specialization data prepared for vulkan use! - std::optional mSpecializationData; -}; - -/// An instance of a ShaderPass material. -/// @ingroup Material -class MaterialPass -{ - MaterialPass(const MaterialPass&) = delete; - MaterialPass& operator=(const MaterialPass&) = delete; -public: - MaterialPass(MaterialPass&&) noexcept; -#if VK_KHR_acceleration_structure - typedef VkAccelerationStructureKHR AccelerationStructureHandle; -#else - typedef void* AccelerationStructureHandle; // unused but defined to be void* -#endif - - typedef std::vector tPerFrameTexInfo; - typedef std::vector tPerFrameImageInfo; - - struct MaterialDescriptorBinding { - uint32_t setIndex; ///< descriptor set index - DescriptorSetLayout::BindingTypeAndIndex setBinding; ///< binding within the descriptor set - }; - - typedef std::vector tPerFrameVkAccelerationStructure; - typedef std::vector > tTextureBindings; - typedef std::vector > tImageBindings; - typedef std::vector > tBufferBindings; - typedef std::vector > tAccelerationStructureBindings; - - MaterialPass(Vulkan& vulkan, const ShaderPass&, VkDescriptorPool&&, std::vector&&, std::vector&&, tTextureBindings&&, tImageBindings&&, tBufferBindings&&, tAccelerationStructureBindings&&, SpecializationConstants&&); - ~MaterialPass(); - - /// Get the descriptor set for the (numbered) frame buffer index, allows for a single descriptor set identical for all frames if required. - const auto& GetVkDescriptorSet(uint32_t bufferIndex, int32_t setIndex) const { return mDescriptorSets[(bufferIndex % mNumBuffers)* mNumDescriptorSetsPerBuffer + setIndex]; } - const std::span GetVkDescriptorSets( uint32_t bufferIndex ) const { return {&mDescriptorSets[(bufferIndex % mNumBuffers) * mNumDescriptorSetsPerBuffer], mNumDescriptorSetsPerBuffer}; } - const auto& GetVkDescriptorSets() const { return mDescriptorSets; } - const auto& GetPipelineLayout() const { return mDynamicPipelineLayout; } - const auto& GetSpecializationConstants() const { return mSpecializationConstants; }; - - const auto& GetTextureBindings() const { return mTextureBindings; } - const auto& GetImageBindings() const { return mImageBindings; } - const auto& GetBufferBindings() const { return mBufferBindings; } - - bool UpdateDescriptorSets(uint32_t bufferIdx); - bool UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const TextureT& newTexture) const; - - const ShaderPass& mShaderPass; -protected: - Vulkan& mVulkan; - - // Helpers for size of mDescriptorSets - const uint32_t mNumDescriptorSetsPerBuffer; ///< number of descriptor sets needed by the shader(pass). Usually 1 but some shaders will use more then one secriptor set. - const uint32_t mNumBuffers; ///< Number of buffers worth of descriptors (may be 1, or number of framebuffers, or something else) - - // Vulkan objects - VkDescriptorPool mDescriptorPool; - std::vector mDescriptorSets; ///< array of descriptor sets (mNumDescriptorSetsPerBuffer * mNumBuffers)) - std::vector mDynamicDescriptorSetLayouts;///< array of descriptor set layouts specific for to this materialPass (usually they are shared across all materials with a specific shader, except in the case of descriptor sets that are 'dynamically' sized to fit the material specific contents) - PipelineLayout mDynamicPipelineLayout; ///< pipeline layout specific to this materiaPass (usually shaderPass contains the pipeline layout but for materials with 'dynamic' descriptor set layouts we have to have a unique pipeline per materialPass - SpecializationConstants mSpecializationConstants; ///< block of specialization constants for this material pass - - tTextureBindings mTextureBindings; ///< Images (textures) (with sampler) considered readonly - tImageBindings mImageBindings; ///< Images that may be bound as writable (or read/write). - tBufferBindings mBufferBindings; - tAccelerationStructureBindings mAccelerationStructureBindings; -}; +// Forward declarations +template class Material; +class ShaderBase; /// An instance of a Shader material. /// Container for MaterialPasses and reference to this material's Shader /// @ingroup Material -class Material +class MaterialBase { - Material(const Material&) = delete; - Material& operator=(const Material&) = delete; + MaterialBase(const MaterialBase&) = delete; + MaterialBase& operator=(const MaterialBase&) = delete; public: - Material(const ShaderT& shader, uint32_t numFramebuffers); - Material(Material&&) noexcept; - ~Material(); - - void AddMaterialPass(const std::string& passName, MaterialPass&& pass) - { - if (m_materialPassNamesToIndex.try_emplace(passName, (uint32_t)m_materialPasses.size()).second == true) - { - m_materialPasses.emplace_back(std::move(pass)); - } - // else pass name already exists - do nothing! - } + template using tApiDerived = Material; // make apiCast work! + MaterialBase(const ShaderBase& shader, uint32_t numFramebuffers); + MaterialBase(MaterialBase&&) noexcept; + ~MaterialBase(); - const MaterialPass* GetMaterialPass(const std::string& passName) const; - const auto& GetMaterialPasses() const { return m_materialPasses; } uint32_t GetNumFrameBuffers() const { return m_numFramebuffers; } - bool UpdateDescriptorSets(uint32_t bufferIdx); + const ShaderBase& m_shader; + const ShaderBase& GetShader() const { return m_shader; } - /// @brief Update a single value in a descriptor set - /// Not optimized for being called multiple times per set (per frame). Intended to be used sparingly. - /// @param bufferIdx - /// @return true on success - bool UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const TextureT& newTexture) const; - - const ShaderT& m_shader; protected: std::map m_materialPassNamesToIndex; /// pass name to index in m_materialPasses - std::vector m_materialPasses; const uint32_t m_numFramebuffers; /// more accurately 'number of frames of buffers'. }; + diff --git a/framework/code/material/materialManager.hpp b/framework/code/material/materialManager.hpp index 200a2cc..fd4381f 100644 --- a/framework/code/material/materialManager.hpp +++ b/framework/code/material/materialManager.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,97 +10,133 @@ #include #include #include -//#define VK_ENABLE_BETA_EXTENSIONS -#include +#include "material.hpp" // Forward declarations class GraphicsApiBase; class Vulkan; -class Material; -class MaterialPass; +class MaterialPassBase; class ShaderDescription; -class Shader; -template class ShaderT; +class ShaderBase; +template class Material; +template class MaterialManager; +template class MaterialPass; +template class Shader; template class ShaderPass; -class Texture; -struct ImageInfo; -struct tPerFrameVkBuffer; +class AccelerationStructureBase; +class TextureBase; +template struct ImageInfo; +struct ImageInfoBase; +struct PerFrameBufferBase; +template struct PerFrameBuffer; class VertexElementData; -/// Helper class for creating Material (base class, expected for there to be a graphics api specific derived class) +/// Helper class for creating MaterialBase (base class, expected for there to be a graphics api specific derived class) /// @ingroup Material -class MaterialManager +class MaterialManagerBase { -#if VK_KHR_acceleration_structure - typedef VkAccelerationStructureKHR AccelerationStructureHandle; -#else - typedef void* AccelerationStructureHandle; -#endif - - MaterialManager(const MaterialManager&) = delete; - MaterialManager& operator=(const MaterialManager&) = delete; + MaterialManagerBase(const MaterialManagerBase&) = delete; + MaterialManagerBase& operator=(const MaterialManagerBase&) = delete; protected: - MaterialManager() noexcept {}; + MaterialManagerBase(GraphicsApiBase& gfxApi) noexcept + : mGfxApi(gfxApi) + {} public: - virtual ~MaterialManager() {}; - typedef std::vector tPerFrameTexInfo; - typedef std::vector tPerFrameVkAccelerationStructure; + typedef std::vector tPerFrameTexInfo; + typedef std::vector tPerFrameAccelerationStructure; - virtual Material CreateMaterial(GraphicsApiBase&, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const = 0; + virtual std::unique_ptr CreateMaterial(const ShaderBase& shader, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function& bufferLoader, + const std::function& imageLoader = nullptr, + const std::function& accelerationStructureLoader = nullptr, + const std::function& specializationConstantLoader = nullptr) const = 0; + GraphicsApiBase &mGfxApi; }; -/// Helper class for creating Material +/// Helper class for creating MaterialBase /// @ingroup Material template -class MaterialManagerT : public MaterialManager +class MaterialManager : public MaterialManagerBase { -#if VK_KHR_acceleration_structure - typedef VkAccelerationStructureKHR AccelerationStructureHandle; -#else - typedef void* AccelerationStructureHandle; -#endif - - MaterialManagerT& operator=(const MaterialManagerT&) = delete; - MaterialManagerT(const MaterialManagerT&) = delete; + MaterialManager& operator=(const MaterialManager&) = delete; + MaterialManager(const MaterialManager&) = delete; public: - typedef std::vector tPerFrameTexInfo; - typedef std::vector tPerFrameVkAccelerationStructure; - MaterialManagerT(); - ~MaterialManagerT(); + MaterialManager( T_GFXAPI& gfxApi ) noexcept; + ~MaterialManager(); - /// Create a material to be used for rendering (potentially contains multiple passes) - /// If numFrameBuffers is not 1 there will be numFrameBuffers descriptor sets created (so different buffers can be bound on different 'frames', although the layout is fixed) - Material CreateMaterial(GraphicsApiBase&, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const override; + virtual std::unique_ptr CreateMaterial( const ShaderBase& shader, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function& bufferLoader, + const std::function& imageLoader = nullptr, + const std::function& accelerationStructureLoader = nullptr, + const std::function& specializationConstantLoader = nullptr ) const override final; - Material CreateMaterial(T_GFXAPI& gfxApi, const ShaderT& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const; + Material CreateMaterial( const Shader& shader, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function( const std::string& )>& bufferLoader, + const std::function( const std::string& )>& imageLoader = nullptr, + const std::function& accelerationStructureLoader = nullptr, + const std::function& specializationConstantLoader = nullptr ) const; + //{ + // static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"material//materialManager.hpp\""); + //} protected: + /// Internal material pass creation (does not UpdateDescriptorSets) - MaterialPass CreateMaterialPassInternal(T_GFXAPI& vulkan, - const ShaderPass& shaderPass, - uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function& accelerationStructureLoader, - const std::function& specializationStructureLoader, - const std::string& passDebugName) const; + MaterialPass CreateMaterialPassInternal( const ShaderPass& shaderPass, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function( const std::string& )>& bufferLoader, + const std::function( const std::string& )>& imageLoader, + const std::function& accelerationStructureLoader, + const std::function& specializationStructureLoader, + const std::string& passDebugName ) const; + //{ + // static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"material//materialManager.hpp\""); + //} + + //static_assert(sizeof( MaterialManager) != sizeof(MaterialManagerBase)); // Expecting that this template be specialized. If you get a compile error here maybe you didnt #include the materialManager.hpp for the gfxapi (eg MaterialBase\Vulkan\materialManager.hpp) }; + +template +std::unique_ptr MaterialManager::CreateMaterial( const ShaderBase& shader, + uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function& bufferLoader, + const std::function& imageLoader, + const std::function& accelerationStructureLoader, + const std::function& specializationConstantLoader ) const +{ + auto textureLoader2 = [&textureLoader]( const std::string& name ) ->tPerFrameTexInfo { + tPerFrameTexInfo texInfo; + textureLoader( name, texInfo ); + return texInfo; + }; + auto bufferLoader2 = [&bufferLoader]( const std::string& name ) -> PerFrameBuffer { + PerFrameBuffer buffers; + bufferLoader( name, buffers ); + return buffers; + }; + auto imageLoader2 = [&imageLoader]( const std::string& name ) ->ImageInfo { + ImageInfo imageInfo; + imageLoader( name, imageInfo ); + return imageInfo; + }; + + return std::make_unique< Material>( + MaterialManager::CreateMaterial( apiCast( shader ), + numFrameBuffers, + textureLoader2, + bufferLoader2, + imageLoader2, + accelerationStructureLoader, + specializationConstantLoader ) ); +} diff --git a/framework/code/material/materialManagerT.hpp b/framework/code/material/materialManagerT.hpp index 8fd45d7..c4808a9 100644 --- a/framework/code/material/materialManagerT.hpp +++ b/framework/code/material/materialManagerT.hpp @@ -1,118 +1,10 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once -#include -#include -#include -//#define VK_ENABLE_BETA_EXTENSIONS -#include - -// Forward declarations -class GraphicsApiBase; -class Vulkan; -class Material; -class MaterialPass; -class ShaderDescription; -class Shader; -template class ShaderT; -template class ShaderPass; -class Texture; -struct ImageInfo; -struct tPerFrameVkBuffer; -class VertexElementData; - - -/// Helper class for creating Material (base class, expected for there to be a graphics api specific derived class) -/// @ingroup Material -class MaterialManager -{ -#if VK_KHR_acceleration_structure - typedef VkAccelerationStructureKHR AccelerationStructureHandle; -#else - typedef void* AccelerationStructureHandle; -#endif - - MaterialManager(const MaterialManager&) = delete; - MaterialManager& operator=(const MaterialManager&) = delete; -protected: - MaterialManager() noexcept {}; - virtual ~MaterialManager() {} - -public: - typedef std::vector tPerFrameTexInfo; - typedef std::vector tPerFrameVkAccelerationStructure; - - virtual Material CreateMaterial(GraphicsApiBase&, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const = 0; -}; - - -/// Helper class for creating Material -/// @ingroup Material -template -class MaterialManagerT final : public MaterialManager -{ -#if VK_KHR_acceleration_structure - typedef VkAccelerationStructureKHR AccelerationStructureHandle; -#else - typedef void* AccelerationStructureHandle; -#endif - - MaterialManagerT& operator=(const MaterialManagerT&) = delete; - MaterialManagerT(const MaterialManagerT&) = delete; -public: - typedef std::vector tPerFrameTexInfo; - typedef std::vector tPerFrameVkAccelerationStructure; - - MaterialManagerT(); - ~MaterialManagerT(); - - /// Create a material to be used for rendering (potentially contains multiple passes) - /// If numFrameBuffers is not 1 there will be numFrameBuffers descriptor sets created (so different buffers can be bound on different 'frames', although the layout is fixed) - Material CreateMaterial(GraphicsApiBase&, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const override; - - Material CreateMaterial(T_GFXAPI& gfxApi, const ShaderT& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader = nullptr, - const std::function& accelerationStructureLoader = nullptr, - const std::function& specializationConstantLoader = nullptr) const; -protected: - /// Internal material pass creation (does not UpdateDescriptorSets) - MaterialPass CreateMaterialPassInternal(T_GFXAPI& vulkan, - const ShaderPass& shaderPass, - uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function& accelerationStructureLoader, - const std::function& specializationStructureLoader, - const std::string& passDebugName) const; -}; - -template -Material MaterialManagerT::CreateMaterial(GraphicsApiBase& gfxApi, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function& accelerationStructureLoader, - const std::function& specializationConstantLoader) const /*override*/ -{ - return CreateMaterial(static_cast(gfxApi), static_cast>(shader), numFrameBuffers, - textureLoader, bufferLoader, imageLoader, accelerationStructureLoader, specializationConstantLoader ); -} +#include "materialManager.hpp" diff --git a/framework/code/material/materialPass.hpp b/framework/code/material/materialPass.hpp new file mode 100644 index 0000000..ed5d6f6 --- /dev/null +++ b/framework/code/material/materialPass.hpp @@ -0,0 +1,175 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +//#include "memory/memoryMapped.hpp" +#include "shader.hpp" + +// Forward declarations +template struct ImageInfo; +template class MaterialPass; +template class MemoryAllocatedBuffer; + + +/// Base class for ImageInfo (different per graphics api) +/// @ingroup Material +struct ImageInfoBase +{ + ImageInfoBase() {} + ImageInfoBase( const ImageInfoBase& ) = delete; + ImageInfoBase& operator=( const ImageInfoBase& ) = delete; + ImageInfoBase( ImageInfoBase&& ) = default; + ImageInfoBase& operator=( ImageInfoBase&& ) = default; + template using tApiDerived = ImageInfo; // make apiCast work! +}; + +/// @ingroup Material +template +struct ImageInfo : public ImageInfoBase +{ + ImageInfo() = delete; // must be specialized + ~ImageInfo() = delete; // must be specialized + ImageInfo( const ImageInfo& ) = delete; + ImageInfo& operator=( const ImageInfo& ) = delete; + static_assert(sizeof( ImageInfo ) != sizeof( ImageInfoBase )); // static assert if not specialized +}; + +/// An instance of a ShaderPass material. +/// @ingroup Material +class MaterialPassBase +{ +protected: + MaterialPassBase( const ShaderPassBase& shaderPass ) noexcept + : mShaderPass( shaderPass) {} + MaterialPassBase(MaterialPassBase && other) noexcept + : mShaderPass( other.mShaderPass ) {} + ~MaterialPassBase() {} +public: + template using tApiDerived = MaterialPass; // make apiCast work! + + const ShaderPassBase& mShaderPass; +}; + +/// Templated MaterialPass (by the graphics api) +/// Expected to be specialized by graphics api and for this non-specialized template not be intantiated/used +/// @ingroup Material +template +class MaterialPass : public MaterialPassBase +{ + MaterialPass() = delete; // must be specialized + ~MaterialPass() = delete; // must be specialized + MaterialPass( const MaterialPass& ) = delete; + MaterialPass& operator=( const MaterialPass& ) = delete; + static_assert(sizeof(MaterialPass) != sizeof(MaterialPassBase)); // static assert if not specialized +}; + + +struct BufferAndOffsetVoid { + void* _buffer; + uint32_t _offset = 0; + constexpr bool operator==( const BufferAndOffsetVoid& other ) const = default; + void* buffer() const { return _buffer; } + auto offset() const { return _offset; } +}; + +/// Reference to VkBuffer or ID3D12Resource (with an offset). +/// Does not have ownership of the buffer (and so lifetime of the buffer should be longer than the referencing @VkBufferAndOffset) +template +struct BufferAndOffset : public BufferAndOffsetVoid { + using tApiBufferType = typename T_GFXAPI::BufferHandleType; + constexpr bool operator==( const BufferAndOffset& other ) const = default; + tApiBufferType buffer() const { + return static_cast(_buffer); + } +}; + + +struct PerFrameBufferBase +{ + /// Reference to VkBuffer or ID3D12Resource (with an offset). + /// Does not have ownership of the buffer (and so lifetime of the buffer should be longer than the referencing @VkBufferAndOffset) + + PerFrameBufferBase() = default; + + template< typename InputIt> requires (sizeof(std::iterator_traits::value_type)==sizeof(void*)) + PerFrameBufferBase( InputIt first, InputIt last ) noexcept + { + while (first != last) + buffers.push_back( {*first++, 0} ); + } + template() ))>> + PerFrameBufferBase( const InputContainer& container ) + { + for (auto item : container) + buffers.push_back( {item} ); + } + + using tBuffers = std::vector; + tBuffers buffers; +}; + +/// Reference to an array of BufferAndOffset (VkBuffer or ID3D12Resource, with an offset). +/// Does not have ownership of the buffers (and so lifetime of the buffers should be longer than this container) +template +struct PerFrameBuffer : public PerFrameBufferBase +{ + using tApiBufferType = typename T_GFXAPI::BufferHandleType; + using tBuffers = std::vector>; + + const auto& getBuffers() const { return (const tBuffers&)buffers; } + auto& getBuffers() { return (tBuffers&)buffers; } + + template< typename InputIt> requires ( std::is_same_v::value_type, tApiBufferType > ) + PerFrameBuffer( InputIt first, InputIt last ) : PerFrameBufferBase() + { + while (first != last) + getBuffers().push_back({*first++, 0}); + } + template< typename InputIt > requires (std::is_same_v) + PerFrameBuffer( InputIt first, InputIt last ) : PerFrameBufferBase( first, last ) + { + } + + template() ))>> requires (std::is_same_v>) + PerFrameBuffer( const InputContainer& container ) : PerFrameBufferBase() + { + for (auto& item : container) + buffers.push_back( {item.GetVkBuffer()}); + } + + template() ))>> + PerFrameBuffer( const InputContainer& container ) : PerFrameBufferBase() + { + for (auto item : container) + buffers.push_back( { item } ); + } + PerFrameBuffer( const BufferAndOffset& bufferAndOffset ) noexcept : PerFrameBufferBase() + { + buffers.push_back( bufferAndOffset ); + } + PerFrameBuffer( tApiBufferType buffer ) noexcept : PerFrameBufferBase() + { + buffers.push_back( { buffer, 0 } ); + } + explicit PerFrameBuffer( PerFrameBuffer&& other ) noexcept = default; + PerFrameBuffer& operator=( PerFrameBuffer&& other ) noexcept = default; + PerFrameBuffer( const PerFrameBuffer& other ) noexcept = default; + PerFrameBuffer& operator=( const PerFrameBuffer& other ) noexcept = default; + PerFrameBuffer() = default; + + constexpr size_t size() const noexcept { return buffers.size(); } + constexpr typename tBuffers::iterator begin() noexcept { getBuffers().begin(); } + constexpr typename tBuffers::const_iterator begin() const noexcept { return getBuffers().begin(); } + constexpr typename tBuffers::const_iterator cbegin() const noexcept { return getBuffers().begin(); } + constexpr typename tBuffers::iterator end() { return buffers.end(); } + constexpr typename tBuffers::const_iterator end() const { return getBuffers().end(); } + constexpr typename tBuffers::const_iterator cend() const { return getBuffers().cend(); } + constexpr typename tBuffers::value_type& operator[]( const typename tBuffers::size_type x ) noexcept { return buffers[x]; } + constexpr const typename tBuffers::value_type& operator[]( const typename tBuffers::size_type x ) const noexcept { return(typename tBuffers::value_type&)(buffers[x]); } +}; diff --git a/framework/code/material/materialProps.cpp b/framework/code/material/materialProps.cpp index 6d2d2e3..030dd0e 100644 --- a/framework/code/material/materialProps.cpp +++ b/framework/code/material/materialProps.cpp @@ -1,12 +1,13 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #include "materialProps.h" +#include #include "system/os_common.h" #include "memory/vulkan/indexBufferObject.hpp" #include "memory/vulkan/uniform.hpp" @@ -87,12 +88,9 @@ void MaterialProps::InitOneLayout(Vulkan* pVulkan) } //----------------------------------------------------------------------------- -bool MaterialProps::InitOnePipeline(Vulkan* pVulkan, Mesh* pMesh, uint32_t TargetWidth, uint32_t TargetHeight, VkRenderPass RenderPass) +bool MaterialProps::InitOnePipeline(Vulkan* pVulkan, Mesh* pMesh, uint32_t TargetWidth, uint32_t TargetHeight, const RenderContext& renderingPassContext) //----------------------------------------------------------------------------- { - // This is based on a specific mesh at this point - // TODO: How do I do multiple meshes? - // Raster State VkPipelineRasterizationStateCreateInfo RasterState{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; RasterState.flags = 0; @@ -204,18 +202,25 @@ bool MaterialProps::InitOnePipeline(Vulkan* pVulkan, Mesh* pMesh, uint32 // Grab the vertex input state for the vertex buffer we will be binding. VkPipelineVertexInputStateCreateInfo visci = pMesh->m_VertexBuffers[0].CreatePipelineState(); + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST + }; + if( !pVulkan->CreatePipeline( PipelineCache, &visci, PipelineLayout, - RenderPass, - 0,//subpass, + renderingPassContext, &RasterState, &DepthStencilInfo, &BlendStateInfo, nullptr,//default multisample state + &inputAssemblyState, dynamicStateEnables, &Viewport, &Scissor, + VK_NULL_HANDLE,//task shader + VK_NULL_HANDLE,//mesh shader pShader->VertShaderModule.GetVkShaderModule(), pShader->FragShaderModule.GetVkShaderModule(), nullptr,//specializationInfo diff --git a/framework/code/material/materialProps.h b/framework/code/material/materialProps.h index e36a08a..311c531 100644 --- a/framework/code/material/materialProps.h +++ b/framework/code/material/materialProps.h @@ -1,10 +1,9 @@ -//============================================================================================================ -// +//============================================================================= // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #ifndef _MATERIAL_MATERIALPROPS_H_ @@ -31,12 +30,12 @@ // Forward declarations struct ShaderInfo; -class Texture; +class TextureBase; template class Mesh; template struct Uniform; //============================================================================= -// Material Description +// MaterialBase Description //============================================================================= typedef struct _MaterialProps @@ -44,7 +43,7 @@ typedef struct _MaterialProps // Used by the application, not the engine. Nice to have it here uint32_t RenderPassMask; - // Material Flags + // MaterialBase Flags // Need one per render target layer. Usually this will be one, but not for MRTs uint32_t NumBlendStates; VkPipelineColorBlendAttachmentState BlendStates[MAX_GMEM_OUTPUT_LAYERS]; @@ -66,7 +65,7 @@ typedef struct _MaterialProps ShaderInfo* pShader; // Textures (responsibility of owner to clean up) - Texture* pTexture[MAX_MATERIAL_SAMPLERS]; + TextureBase* pTexture[MAX_MATERIAL_SAMPLERS]; // Constant Buffers uint32_t VertUniformOffset; @@ -90,7 +89,7 @@ typedef struct _MaterialProps // Helper Functions void InitOneLayout(Vulkan*); - bool InitOnePipeline(Vulkan*, Mesh* pMesh, uint32_t TargetWidth, uint32_t TargetHeight, VkRenderPass RenderPass); + bool InitOnePipeline(Vulkan*, Mesh* pMesh, uint32_t TargetWidth, uint32_t TargetHeight, const RenderContext& renderingPassContext); bool InitDescriptorPool(Vulkan*); bool InitDescriptorSet(Vulkan*); diff --git a/framework/code/material/materialShaderDefinition.cpp b/framework/code/material/materialShaderDefinition.cpp index d3a6f2c..8d1cc23 100644 --- a/framework/code/material/materialShaderDefinition.cpp +++ b/framework/code/material/materialShaderDefinition.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/material/materialShaderDefinition.hpp b/framework/code/material/materialShaderDefinition.hpp index b3db6a0..a64fbf4 100644 --- a/framework/code/material/materialShaderDefinition.hpp +++ b/framework/code/material/materialShaderDefinition.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/material/materialT.hpp b/framework/code/material/materialT.hpp new file mode 100644 index 0000000..558ebe8 --- /dev/null +++ b/framework/code/material/materialT.hpp @@ -0,0 +1,116 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "material.hpp" +#include "materialPass.hpp" + + +// Forward declarations +template class Shader; +template class Texture; + + +// Material (MaterialBase templated by the graphics api) +template +class Material : public MaterialBase +{ + Material (const Material&) = delete; + Material& operator=(const Material&) = delete; +public: + Material(const Shader& shader, uint32_t numFramebuffers); + Material(Material&&) noexcept; + ~Material(); + + const Shader& GetShader() const { return apiCast(m_shader); } + + void AddMaterialPass(const std::string& passName, MaterialPass&& pass) + { + if (m_materialPassNamesToIndex.try_emplace(passName, (uint32_t)m_materialPasses.size()).second == true) + { + m_materialPasses.emplace_back(std::move(pass)); + } + // else pass name already exists - do nothing! + } + + const MaterialPass* GetMaterialPass(const std::string& passName) const; + const MaterialPass* GetMaterialPass(uint32_t passIdx) const; + const auto& GetMaterialPasses() const { return m_materialPasses; } + + bool UpdateDescriptorSets(uint32_t bufferIdx); + + /// @brief Update a single value in a descriptor set + /// Not optimized for being called multiple times per set (per frame). Intended to be used sparingly. + /// @param bufferIdx + /// @return true on success + bool UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const Texture& newTexture) const; + +protected: + std::vector> m_materialPasses; +}; + + +template +Material::Material(Material && other) noexcept +: MaterialBase( std::move(other) ) +, m_materialPasses(std::move(other.m_materialPasses)) +{ +} + +template +Material::~Material() +{ +} + +template +Material::Material(const Shader& shader, uint32_t numFramebuffers) + :MaterialBase(shader, numFramebuffers) +{ +} + + +template +const MaterialPass* Material::GetMaterialPass(const std::string& passName) const +{ + auto it = m_materialPassNamesToIndex.find(passName); + if (it != m_materialPassNamesToIndex.end()) + { + return &m_materialPasses[it->second]; + } + return nullptr; +} + +template +const MaterialPass* Material::GetMaterialPass(uint32_t passIdx) const +{ + return &m_materialPasses[passIdx]; +} + +template +bool Material::UpdateDescriptorSets(uint32_t bufferIdx) +{ + // Just go through and update the MaterialPass descriptor sets. + // Could be much smarter (and could handle failures better than just continuing and reporting something went wrong) + bool success = true; + for (auto& materialPass : m_materialPasses) + { + success &= materialPass.UpdateDescriptorSets(bufferIdx); + } + return success; +} + +template +bool Material::UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const Texture& newTexture) const +{ + bool success = true; + for (auto& materialPass : m_materialPasses) + { + success &= materialPass.UpdateDescriptorSetBinding(bufferIdx, bindingName, newTexture); + } + return success; +} diff --git a/framework/code/material/pipeline.hpp b/framework/code/material/pipeline.hpp new file mode 100644 index 0000000..4f7c650 --- /dev/null +++ b/framework/code/material/pipeline.hpp @@ -0,0 +1,72 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +// Forward declarations +class ShaderPassDescription; +template class SpecializationConstants; +template class PipelineLayout; +template class PipelineRasterizationState; +template class PipelineVertexInputState; +template class ShaderModules; +template class RenderPass; +template class RenderContext; +enum class Msaa; + +/// @brief Settings for vertex input assembly +struct PipelineInputAssemblySettings +{ + enum PrimitiveTopology { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + } PrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + bool primitiveRestartEnable = false; +}; + + +/// Simple wrapper around VkPipeline or ID3D12PipelineState. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// This template class expected to be specialized (if this template throws compiler errors then the code is not using the specialization classes which is an issue!) +/// @ingroup Material +template +class Pipeline +{ + Pipeline& operator=(const Pipeline&) = delete; + Pipeline(const Pipeline&) = delete; +public: + Pipeline() noexcept = delete; + Pipeline(Pipeline&&) noexcept = delete; + ~Pipeline() = delete; + + static_assert(sizeof( Pipeline ) >= 1, "Must use the specialized version of this class. Your are likely missing #include \"material//pipeline.hpp\""); +}; + +template +Pipeline CreatePipeline( T_GFXAPI&, + const ShaderPassDescription& shaderPassDescription, + const PipelineLayout& pipelineLayout, + const PipelineVertexInputState& pipelineVertexInputState, + const PipelineRasterizationState& pipelineRasterizationState, + const SpecializationConstants& specializationConstants, + const ShaderModules& shaderModules, + const RenderContext& renderContext, + Msaa msaa ) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"material//pipeline.hpp\""); + assert( 0 && "Expecting CreatePipeline (per graphics api) to be used" ); + return {}; +} \ No newline at end of file diff --git a/framework/code/material/pipelineLayout.hpp b/framework/code/material/pipelineLayout.hpp index 34d6a4c..79276eb 100644 --- a/framework/code/material/pipelineLayout.hpp +++ b/framework/code/material/pipelineLayout.hpp @@ -1,14 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once // Forward declarations -class DescriptorSetLayout; +class DescriptorSetLayoutBase; /// Simple wrapper around VkPipelineLayout or RootSignature. @@ -25,5 +25,5 @@ class PipelineLayout PipelineLayout(PipelineLayout&&) noexcept = delete; ~PipelineLayout() = delete; - static_assert(sizeof(PipelineLayout) >= 1); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof(PipelineLayout) >= 1, "Must use the specialized version of this class. Your are likely missing #include \"material//pipelineLayout.hpp\""); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/material/pipelineVertexInputState.hpp b/framework/code/material/pipelineVertexInputState.hpp index 10babac..897099b 100644 --- a/framework/code/material/pipelineVertexInputState.hpp +++ b/framework/code/material/pipelineVertexInputState.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,18 +9,47 @@ // Forward declarations class ShaderDescription; +class ShaderPassDescription; + +class PipelineVertexInputStateBase { + PipelineVertexInputStateBase( const PipelineVertexInputStateBase& ) = delete; + PipelineVertexInputStateBase& operator=( const PipelineVertexInputStateBase& ) = delete; +public: + PipelineVertexInputStateBase() noexcept = default; +}; /// Helper for making VkPipelineVertexInputStateCreateInfo. /// @ingroup Material template -class PipelineVertexInputState +class PipelineVertexInputState : public PipelineVertexInputStateBase { - PipelineVertexInputState(const PipelineVertexInputState&) = delete; - PipelineVertexInputState& operator=(const PipelineVertexInputState&) = delete; public: PipelineVertexInputState(const ShaderDescription& shaderDescription, const std::vector& buffersToBind /*indices of buffers (in shaderDescription.m_vertexFormats) to bind in this input state*/) noexcept; - PipelineVertexInputState(PipelineVertexInputState&&) noexcept; - static_assert(sizeof(PipelineVertexInputState) >= 1); // Ensure this class template is specialized (and not used as-is) }; + + +class PipelineRasterizationStateBase +{ + PipelineRasterizationStateBase( const PipelineRasterizationStateBase& ) = delete; + PipelineRasterizationStateBase& operator=( const PipelineRasterizationStateBase& ) = delete; +public: + PipelineRasterizationStateBase() noexcept = default; +}; + +/// Helper for making VkPipelineRasterizationStateCreateInfo. +/// @ingroup Material +template +class PipelineRasterizationState : public PipelineRasterizationStateBase +{ +public: + PipelineRasterizationState() noexcept + {} + PipelineRasterizationState( PipelineRasterizationState&& ) noexcept + {} + PipelineRasterizationState( const ShaderPassDescription& shaderPassDescription ) noexcept {} + + //ok to use the base class (eg for Dx12) when there is no rasterization state + //static_assert(sizeof( PipelineRasterizationState ) >= 1); // Ensure this class template is specialized (and not used as-is) +}; diff --git a/framework/code/material/shader.hpp b/framework/code/material/shader.hpp index 3656b9d..43f92bc 100644 --- a/framework/code/material/shader.hpp +++ b/framework/code/material/shader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -22,8 +22,10 @@ // forward declarations class GraphicsApiBase; class ShaderDescription; -template class ShaderT; -template class ShaderModuleT; +template class Shader; +template class ShaderModule; +template class ShaderPass; +template class DescriptorSetLayout; class ShaderPassDescription; /// Reference to a 'normal' graphics pipeline pairing of a vertex and fragment shader @@ -31,15 +33,15 @@ class ShaderPassDescription; template struct GraphicsShaderModules { - ShaderModuleT& vert; - ShaderModuleT& frag; + ShaderModule& vert; + ShaderModule& frag; }; /// Reference to a 'vertex only' graphics pipeline pairing of a vertex shader (no fragment, typically used when laying down a depth buffer) /// @ingroup Material template struct GraphicsShaderModuleVertOnly { - ShaderModuleT& vert; + ShaderModule& vert; operator const auto& () const { return vert; } }; /// Reference to a 'mesh' graphics pipeline pairing of a mesh and fragment shader @@ -47,17 +49,17 @@ struct GraphicsShaderModuleVertOnly template struct GraphicsMeshShaderModules { - ShaderModuleT& mesh; - ShaderModuleT& frag; + ShaderModule& mesh; + ShaderModule& frag; }; /// Reference to a 'task mesh' graphics pipeline pairing of a task, mesh and fragment shader /// @ingroup Material template struct GraphicsTaskMeshShaderModules { - ShaderModuleT& task; - ShaderModuleT& mesh; - ShaderModuleT& frag; + ShaderModule& task; + ShaderModule& mesh; + ShaderModule& frag; }; /// Reference to a compute shader @@ -65,18 +67,18 @@ struct GraphicsTaskMeshShaderModules template struct ComputeShaderModule { - ShaderModuleT& compute; - operator const ShaderModuleT& () const { return compute; } + ShaderModule& compute; + operator const ShaderModule& () const { return compute; } }; /// Reference to a ray tracing pipeline with a ray-generation shader and some combination of ray-miss, ray-hit, ray-first-hit /// @ingroup Material template struct RayTracingShaderModules { - ShaderModuleT& rayGeneration; - const ShaderModuleT* rayClosestHit = nullptr; - const ShaderModuleT* rayAnyHit = nullptr; - const ShaderModuleT* rayMiss = nullptr; + ShaderModule& rayGeneration; + const ShaderModule* rayClosestHit = nullptr; + const ShaderModule* rayAnyHit = nullptr; + const ShaderModule* rayMiss = nullptr; }; /// Container for a reference to one of the allowed shader module types. /// @ingroup Material @@ -97,34 +99,55 @@ class ShaderModules }; +class ShaderPassBase +{ + ShaderPassBase( const ShaderPassBase& ) = delete; + ShaderPassBase& operator=( const ShaderPassBase& ) = delete; + ShaderPassBase() = delete; +protected: + ShaderPassBase( const ShaderPassDescription& shaderPassDescription ) noexcept : m_shaderPassDescription( shaderPassDescription ) + {} + ShaderPassBase( ShaderPassBase&& other ) noexcept;///< @note currently implemented as an assert, so compiler does not complain (eg std::vector::push_back) but implementation expects this is never hit at runtime! +public: + template using tApiDerived = ShaderPass; // make apiCast work! + + const ShaderPassDescription& m_shaderPassDescription; +}; + + /// Container for the gpu objects needed for a shader pass (described by ShaderPassDescription). /// Typically used to create MaterialPass (s). /// @ingroup Material template -class ShaderPass +class ShaderPass final : public ShaderPassBase { + using DescriptorSetLayout = DescriptorSetLayout; + using PipelineLayout = PipelineLayout; + using PipelineVertexInputState = PipelineVertexInputState; + using ShaderModules = ShaderModules; + using SpecializationConstantsLayout = SpecializationConstantsLayout; + ShaderPass(const ShaderPass& src) = delete; ShaderPass& operator=(const ShaderPass& src) = delete; ShaderPass() = delete; public: - ShaderPass(const ShaderPassDescription& shaderPassDescription, ShaderModules shaders, std::vector descriptorSetLayouts, PipelineLayout, PipelineVertexInputState, SpecializationConstantsLayout, uint32_t numOutputs) noexcept; + + ShaderPass(const ShaderPassDescription& shaderPassDescription, ShaderModules shaders, std::vector descriptorSetLayouts, PipelineLayout, PipelineVertexInputState, SpecializationConstantsLayout, uint32_t numOutputs) noexcept; ShaderPass(ShaderPass&& other) noexcept;///< @note currently implemented as an assert, so compiler does not complain (eg std::vector::push_back) but implementation expects this is never hit at runtime! void Destroy(T_GFXAPI&); - ShaderModules m_shaders; + ShaderModules m_shaders; const auto& GetPipelineLayout() const { return m_pipelineLayout; } const auto& GetDescriptorSetLayouts() const { return m_descriptorSetLayouts; } const auto& GetPipelineVertexInputState() const { return m_pipelineVertexInputState; } const auto& GetSpecializationConstantsLayout()const { return m_specializationConstantsLayout; } - - const ShaderPassDescription& m_shaderPassDescription; private: std::vector m_descriptorSetLayouts; - PipelineLayout m_pipelineLayout; - PipelineVertexInputState m_pipelineVertexInputState; - SpecializationConstantsLayout m_specializationConstantsLayout; + PipelineLayout m_pipelineLayout; + PipelineVertexInputState m_pipelineVertexInputState; + SpecializationConstantsLayout m_specializationConstantsLayout; uint32_t m_numOutputs = 0; static_assert(sizeof(T_GFXAPI) > 1); @@ -132,41 +155,45 @@ class ShaderPass /// @brief Base class for shaders (graphics api agnostic, expected to have a derived API specific implementation) -class Shader +class ShaderBase { - Shader(const Shader&) = delete; - Shader& operator=(const Shader&) = delete; + ShaderBase(const ShaderBase&) = delete; + ShaderBase& operator=(const ShaderBase&) = delete; public: - template using tApiDerived = ShaderT; // make apiCast work! - Shader(const ShaderDescription* pShaderDescription) noexcept : m_shaderDescription(pShaderDescription) {} - virtual ~Shader() {} + template using tApiDerived = Shader; // make apiCast work! + ShaderBase(const ShaderDescription* pShaderDescription) noexcept : m_shaderDescription(pShaderDescription) {} + virtual ~ShaderBase() {} virtual void Destroy(GraphicsApiBase&) = 0; + const auto& GetShaderPassIndicesToNames() const { return m_passIndexToName; } + const ShaderDescription* m_shaderDescription = nullptr; + +protected: + std::map m_passNameToIndex; // maps shader pass name to m_shaderPasses[x] + std::vector> m_passIndexToName; // names in index order, eg reverse of m_passNameToIndex. string referenced is m_passNameToIndex.key }; /// Container for a vector of ShaderPass (with name lookup) -/// Typically used to create Material (s). +/// Typically used to create MaterialBase (s). /// @ingroup Material template -class ShaderT : public Shader +class Shader final : public ShaderBase { - ShaderT(const ShaderT&) = delete; - ShaderT& operator=(const ShaderT&) = delete; + Shader(const Shader&) = delete; + Shader& operator=(const Shader&) = delete; + using ShaderPass = ShaderPass; public: - ShaderT( const ShaderDescription* pShaderDescription, std::vector> shaderPasses, const std::vector& passNames) noexcept; - ~ShaderT() override { assert(m_shaderPasses.empty()); } + Shader( const ShaderDescription* pShaderDescription, std::vector shaderPasses, const std::vector& passNames) noexcept; + ~Shader() override { assert(m_shaderPasses.empty()); } void Destroy(GraphicsApiBase&) override; - const ShaderPass* GetShaderPass(const std::string& passName) const; - const auto& GetShaderPasses() const { return m_shaderPasses; } - const auto& GetShaderPassIndicesToNames() const { return m_passIndexToName; } + const ShaderPass*const GetShaderPass(const std::string& passName) const; + const auto& GetShaderPasses() const { return m_shaderPasses; } protected: - std::vector> m_shaderPasses; // in order of calls to AddShaderPass, concievably we could have shaders who share passes (eg shadow) with other Shaders, not currently supported though! - std::map m_passNameToIndex; // maps shader pass name to m_shaderPasses[x] - std::vector> m_passIndexToName; // names in index order, eg reverse of m_passNameToIndex. string referenced is m_passNameToIndex.key + std::vector m_shaderPasses; // in order of calls to AddShaderPass, concievably we could have shaders who share passes (eg shadow) with other Shaders, not currently supported though! }; // @@ -174,9 +201,9 @@ class ShaderT : public Shader // template -ShaderPass::ShaderPass(const ShaderPassDescription& shaderPassDescription, ShaderModules shaders, std::vector descriptorSetLayouts, PipelineLayout pipelineLayout, PipelineVertexInputState pipelineVertexInputState, SpecializationConstantsLayout specializationConstantsLayout, uint32_t numOutputs) noexcept - : m_shaders(std::move(shaders)) - , m_shaderPassDescription(shaderPassDescription) +ShaderPass::ShaderPass(const ShaderPassDescription& shaderPassDescription, ShaderModules shaders, std::vector descriptorSetLayouts, PipelineLayout pipelineLayout, PipelineVertexInputState pipelineVertexInputState, SpecializationConstantsLayout specializationConstantsLayout, uint32_t numOutputs) noexcept + : ShaderPassBase(shaderPassDescription) + , m_shaders(std::move(shaders)) , m_descriptorSetLayouts(std::move(descriptorSetLayouts)) , m_pipelineLayout(std::move(pipelineLayout)) , m_pipelineVertexInputState(std::move(pipelineVertexInputState)) @@ -187,8 +214,8 @@ ShaderPass::ShaderPass(const ShaderPassDescription& shaderPassDescript template ShaderPass::ShaderPass(ShaderPass&& other) noexcept - : m_shaders(std::move(other.m_shaders)) - , m_shaderPassDescription(other.m_shaderPassDescription) + : ShaderPassBase(other.m_shaderPassDescription) + , m_shaders(std::move(other.m_shaders)) , m_pipelineVertexInputState(std::move(other.m_pipelineVertexInputState)) , m_specializationConstantsLayout(std::move(other.m_specializationConstantsLayout)) { @@ -206,8 +233,8 @@ void ShaderPass::Destroy(T_GFXAPI& gfxapi) } template -ShaderT::ShaderT(const ShaderDescription* pShaderDescription, std::vector> shaderPasses, const std::vector& passNames) noexcept - : Shader(pShaderDescription) +Shader::Shader(const ShaderDescription* pShaderDescription, std::vector shaderPasses, const std::vector& passNames) noexcept + : ShaderBase(pShaderDescription) , m_shaderPasses(std::move(shaderPasses)) { uint32_t idx = 0; @@ -221,7 +248,7 @@ ShaderT::ShaderT(const ShaderDescription* pShaderDescription, std::vec } template -void ShaderT::Destroy(GraphicsApiBase& gfxapi) +void Shader::Destroy(GraphicsApiBase& gfxapi) { for (auto& sp : m_shaderPasses) sp.Destroy(static_cast(gfxapi)); @@ -229,7 +256,7 @@ void ShaderT::Destroy(GraphicsApiBase& gfxapi) } template -const ShaderPass* ShaderT::GetShaderPass(const std::string& passName) const +const ShaderPass* const Shader::GetShaderPass(const std::string& passName) const { auto it = m_passNameToIndex.find(passName); if (it != m_passNameToIndex.end()) diff --git a/framework/code/material/shaderDescription.cpp b/framework/code/material/shaderDescription.cpp index 11b732e..d5caa39 100644 --- a/framework/code/material/shaderDescription.cpp +++ b/framework/code/material/shaderDescription.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,13 +10,13 @@ #include "nlohmann/json.hpp" #include "system/assetManager.hpp" #include "system/os_common.h" -#include +#include "texture/sampler.hpp" #include -#include +using namespace std::string_literals; using Json = nlohmann::json; -ShaderPassDescription::ShaderPassDescription(std::vector setDescriptions, std::vector outputs, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, ShaderPassDescription::FixedFunctionSettings fixedFunctionSettings, ShaderPassDescription::SampleShadingSettings sampleShadingSettings, ShaderPassDescription::WorkGroupSettings workGroupSettings, ShaderPassDescription::RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants ) +ShaderPassDescription::ShaderPassDescription(std::vector setDescriptions, std::vector outputs, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, ShaderPassDescription::FixedFunctionSettings fixedFunctionSettings, ShaderPassDescription::SampleShadingSettings sampleShadingSettings, ShaderPassDescription::WorkGroupSettings workGroupSettings, ShaderPassDescription::RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants, std::vector rootSamplers ) : m_sets(std::move(setDescriptions)) , m_outputs(std::move(outputs)) , m_computeName(std::move(computeName)) @@ -32,13 +32,12 @@ ShaderPassDescription::ShaderPassDescription(std::vector setDescriptions, std::vector outputs, std::string taskName, std::string meshName, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, ShaderPassDescription::FixedFunctionSettings fixedFunctionSettings, ShaderPassDescription::SampleShadingSettings sampleShadingSettings, ShaderPassDescription::WorkGroupSettings workGroupSettings, ShaderPassDescription::RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants ) +ShaderPassDescription::ShaderPassDescription(std::vector setDescriptions, std::vector outputs, std::string taskName, std::string meshName, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, ShaderPassDescription::FixedFunctionSettings fixedFunctionSettings, ShaderPassDescription::SampleShadingSettings sampleShadingSettings, ShaderPassDescription::WorkGroupSettings workGroupSettings, ShaderPassDescription::RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants, std::vector rootSamplers ) : m_sets(std::move(setDescriptions)) , m_outputs(std::move(outputs)) , m_taskName(std::move(taskName)) @@ -56,26 +55,40 @@ ShaderPassDescription::ShaderPassDescription(std::vector cElementTypeByName{ - {"Int32", VertexFormat::Element::ElementType::t::Int32}, - {"Float", VertexFormat::Element::ElementType::t::Float}, - {"Boolean", VertexFormat::Element::ElementType::t::Boolean}, - {"Vec2", VertexFormat::Element::ElementType::t::Vec2}, - {"Vec3", VertexFormat::Element::ElementType::t::Vec3}, - {"Vec4", VertexFormat::Element::ElementType::t::Vec4}, - {"Int16", VertexFormat::Element::ElementType::t::Int16}, - {"Float16", VertexFormat::Element::ElementType::t::Float16}, - {"F16Vec2", VertexFormat::Element::ElementType::t::F16Vec2}, - {"F16Vec3", VertexFormat::Element::ElementType::t::F16Vec3}, - {"F16Vec4", VertexFormat::Element::ElementType::t::F16Vec4} + {"Int32"s, VertexFormat::Element::ElementType::t::Int32}, + {"UInt32"s, VertexFormat::Element::ElementType::t::UInt32}, + {"Float"s, VertexFormat::Element::ElementType::t::Float}, + {"Boolean"s, VertexFormat::Element::ElementType::t::Boolean}, + {"Vec2"s, VertexFormat::Element::ElementType::t::Vec2}, + {"Vec3"s, VertexFormat::Element::ElementType::t::Vec3}, + {"Vec4"s, VertexFormat::Element::ElementType::t::Vec4}, + {"IVec2"s, VertexFormat::Element::ElementType::t::IVec2}, + {"IVec3"s, VertexFormat::Element::ElementType::t::IVec3}, + {"IVec4"s, VertexFormat::Element::ElementType::t::IVec4}, + {"UVec2"s, VertexFormat::Element::ElementType::t::UVec2}, + {"UVec3"s, VertexFormat::Element::ElementType::t::UVec3}, + {"UVec4"s, VertexFormat::Element::ElementType::t::UVec4}, + {"Int16"s, VertexFormat::Element::ElementType::t::Int16}, + {"UInt16"s, VertexFormat::Element::ElementType::t::UInt16}, + {"Float16"s, VertexFormat::Element::ElementType::t::Float16}, + {"F16Vec2"s, VertexFormat::Element::ElementType::t::F16Vec2}, + {"F16Vec3"s, VertexFormat::Element::ElementType::t::F16Vec3}, + {"F16Vec4"s, VertexFormat::Element::ElementType::t::F16Vec4}, + {"I16Vec2"s, VertexFormat::Element::ElementType::t::I16Vec2}, + {"I16Vec3"s, VertexFormat::Element::ElementType::t::I16Vec3}, + {"I16Vec4"s, VertexFormat::Element::ElementType::t::I16Vec4}, + {"U16Vec2"s, VertexFormat::Element::ElementType::t::U16Vec2}, + {"U16Vec3"s, VertexFormat::Element::ElementType::t::U16Vec3}, + {"U16Vec4"s, VertexFormat::Element::ElementType::t::U16Vec4}, }; const static std::map cBufferRateByName{ - {"Vertex", VertexFormat::eInputRate::Vertex}, - {"Instance", VertexFormat::eInputRate::Instance} + {"Vertex"s, VertexFormat::eInputRate::Vertex}, + {"Instance"s, VertexFormat::eInputRate::Instance} }; struct DescriptorTypeAndReadOnly { @@ -83,43 +96,44 @@ struct DescriptorTypeAndReadOnly { bool readOnly = false; }; const static std::map cBufferTypeByName { - {"Unused", {DescriptorSetDescription::DescriptorType::Unused, true}}, - {"ImageSampler", {DescriptorSetDescription::DescriptorType::ImageSampler, true}}, - {"ImageSampled", {DescriptorSetDescription::DescriptorType::ImageSampled, true}}, - {"Sampler", {DescriptorSetDescription::DescriptorType::Sampler, true}}, - {"UniformBuffer", {DescriptorSetDescription::DescriptorType::UniformBuffer, true}}, - {"StorageBuffer", {DescriptorSetDescription::DescriptorType::StorageBuffer, false}}, - {"ImageStorage", {DescriptorSetDescription::DescriptorType::ImageStorage, false}}, - {"InputAttachment", {DescriptorSetDescription::DescriptorType::InputAttachment, true}}, - {"AccelerationStructure", {DescriptorSetDescription::DescriptorType::AccelerationStructure, true}} + {"Unused"s, {DescriptorSetDescription::DescriptorType::Unused, true}}, + {"ImageSampler"s, {DescriptorSetDescription::DescriptorType::ImageSampler, true}}, + {"ImageSampled"s, {DescriptorSetDescription::DescriptorType::ImageSampled, true}}, + {"Sampler"s, {DescriptorSetDescription::DescriptorType::Sampler, true}}, + {"UniformBuffer"s, {DescriptorSetDescription::DescriptorType::UniformBuffer, true}}, + {"StorageBuffer"s, {DescriptorSetDescription::DescriptorType::StorageBuffer, false}}, + {"ImageStorage"s, {DescriptorSetDescription::DescriptorType::ImageStorage, false}}, + {"InputAttachment"s, {DescriptorSetDescription::DescriptorType::InputAttachment, true}}, + {"AccelerationStructure"s, {DescriptorSetDescription::DescriptorType::AccelerationStructure, true}}, + {"DescriptorTable"s, {DescriptorSetDescription::DescriptorType::DescriptorTable, true}} }; const static std::map cStageFlagBitsByName{ - {"Vertex", DescriptorSetDescription::StageFlag::t::Vertex}, - {"Fragment", DescriptorSetDescription::StageFlag::t::Fragment}, - {"Geometry", DescriptorSetDescription::StageFlag::t::Geometry}, - {"Compute", DescriptorSetDescription::StageFlag::t::Compute}, - {"RayGeneration", DescriptorSetDescription::StageFlag::t::RayGeneration}, - {"RayClosestHit", DescriptorSetDescription::StageFlag::t::RayClosestHit}, - {"RayAnyHit", DescriptorSetDescription::StageFlag::t::RayAnyHit}, - {"RayMiss", DescriptorSetDescription::StageFlag::t::RayMiss}, - {"Task", DescriptorSetDescription::StageFlag::t::Task}, - {"Mesh", DescriptorSetDescription::StageFlag::t::Mesh} + {"Vertex"s, DescriptorSetDescription::StageFlag::t::Vertex}, + {"Fragment"s, DescriptorSetDescription::StageFlag::t::Fragment}, + {"Geometry"s, DescriptorSetDescription::StageFlag::t::Geometry}, + {"Compute"s, DescriptorSetDescription::StageFlag::t::Compute}, + {"RayGeneration"s, DescriptorSetDescription::StageFlag::t::RayGeneration}, + {"RayClosestHit"s, DescriptorSetDescription::StageFlag::t::RayClosestHit}, + {"RayAnyHit"s, DescriptorSetDescription::StageFlag::t::RayAnyHit}, + {"RayMiss"s, DescriptorSetDescription::StageFlag::t::RayMiss}, + {"Task"s, DescriptorSetDescription::StageFlag::t::Task}, + {"Mesh"s, DescriptorSetDescription::StageFlag::t::Mesh} }; const static std::map cDepthCompareOpByName{ - {"LessEqual", ShaderPassDescription::DepthCompareOp::LessEqual}, - {"Equal", ShaderPassDescription::DepthCompareOp::Equal}, - {"Greater", ShaderPassDescription::DepthCompareOp::Greater}, + {"LessEqual"s, ShaderPassDescription::DepthCompareOp::LessEqual}, + {"Equal"s, ShaderPassDescription::DepthCompareOp::Equal}, + {"Greater"s, ShaderPassDescription::DepthCompareOp::Greater}, }; const static std::map cBlendFactorByName{ - {"Zero", ShaderPassDescription::BlendFactor::Zero}, - {"One", ShaderPassDescription::BlendFactor::One}, - {"SrcAlpha", ShaderPassDescription::BlendFactor::SrcAlpha}, - {"OneMinusSrcAlpha", ShaderPassDescription::BlendFactor::OneMinusSrcAlpha}, - {"DstAlpha", ShaderPassDescription::BlendFactor::DstAlpha}, - {"OneMinusDstAlpha", ShaderPassDescription::BlendFactor::OneMinusDstAlpha}, + {"Zero"s, ShaderPassDescription::BlendFactor::Zero}, + {"One"s, ShaderPassDescription::BlendFactor::One}, + {"SrcAlpha"s, ShaderPassDescription::BlendFactor::SrcAlpha}, + {"OneMinusSrcAlpha"s, ShaderPassDescription::BlendFactor::OneMinusSrcAlpha}, + {"DstAlpha"s, ShaderPassDescription::BlendFactor::DstAlpha}, + {"OneMinusDstAlpha"s, ShaderPassDescription::BlendFactor::OneMinusDstAlpha}, }; static void from_json(const Json& j, ShaderPassDescription::DepthCompareOp& op) { @@ -128,7 +142,7 @@ static void from_json(const Json& j, ShaderPassDescription::DepthCompareOp& op) op = foundIt->second; } else { - throw std::invalid_argument("Unknown species"); + throw std::invalid_argument("Unknown DepthCompareOp"s); } } @@ -138,85 +152,86 @@ static void from_json(const Json& j, ShaderPassDescription::BlendFactor& op) { op = foundIt->second; } else { - throw std::invalid_argument("Unknown species"); + throw std::invalid_argument("Unknown BlendFactor"s); } } static void from_json(const Json& j, ShaderPassDescription::Output& output) { - auto it = j.find("BlendEnable"); + auto it = j.find("BlendEnable"s); if (it != j.end()) it->get_to(output.blendEnable); - it = j.find("SrcColorBlendFactor"); + it = j.find("SrcColorBlendFactor"s); if (it != j.end()) it->get_to(output.srcColorBlendFactor); - it = j.find("DstColorBlendFactor"); + it = j.find("DstColorBlendFactor"s); if (it != j.end()) it->get_to(output.dstColorBlendFactor); - it = j.find("SrcAlphaBlendFactor"); + it = j.find("SrcAlphaBlendFactor"s); if (it != j.end()) it->get_to(output.srcAlphaBlendFactor); - it = j.find("DstAlphaBlendFactor"); + it = j.find("DstAlphaBlendFactor"s); if (it != j.end()) it->get_to(output.dstAlphaBlendFactor); - it = j.find("ColorWriteMask"); + it = j.find("ColorWriteMask"s); if (it != j.end()) it->get_to(output.colorWriteMask); } static void from_json(const Json& j, ShaderPassDescription::FixedFunctionSettings& ffs) { - auto it = j.find("DepthTestEnable"); + auto it = j.find("DepthTestEnable"s); if (it != j.end()) it->get_to(ffs.depthTestEnable); - it = j.find("DepthWriteEnable"); + it = j.find("DepthWriteEnable"s); if (it != j.end()) it->get_to(ffs.depthWriteEnable); - it = j.find("DepthCompareOp"); + it = j.find("DepthCompareOp"s); if (it != j.end()) it->get_to(ffs.depthCompareOp); - it = j.find("DepthClampEnable"); + it = j.find("DepthClampEnable"s); if (it != j.end()) it->get_to(ffs.depthClampEnable); - it = j.find("DepthBiasEnable"); + it = j.find("DepthBiasEnable"s); if (it != j.end()) it->get_to(ffs.depthBiasEnable); - it = j.find("DepthBiasConstant"); + it = j.find("DepthBiasConstant"s); if (it != j.end()) it->get_to(ffs.depthBiasConstant); - it = j.find("DepthBiasClamp"); + it = j.find("DepthBiasClamp"s); if (it != j.end()) it->get_to(ffs.depthBiasClamp); - it = j.find("DepthBiasSlope"); + it = j.find("DepthBiasSlope"s); if (it != j.end()) it->get_to(ffs.depthBiasSlope); - it = j.find("CullBackFace"); + it = j.find("CullBackFace"s); if (it != j.end()) it->get_to(ffs.cullBackFace); - it = j.find("CullFrontFace"); + it = j.find("CullFrontFace"s); if (it != j.end()) it->get_to(ffs.cullFrontFace); } static void from_json(const Json& j, ShaderPassDescription::SampleShadingSettings& sss) { - auto it = j.find("Enable"); + auto it = j.find("Enable"s); if (it != j.end()) it->get_to(sss.sampleShadingEnable); - it = j.find("ForceCenterSample"); + it = j.find("ForceCenterSample"s); if (it != j.end()) it->get_to(sss.forceCenterSample); - it = j.find("Mask"); + it = j.find("Mask"s); if (it != j.end()) it->get_to(sss.sampleShadingMask); } static void from_json(const Json& j, ShaderPassDescription::WorkGroupSettings& ws) { - auto it = j.find("LocalSize"); + auto it = j.find("LocalSize"s); if (it != j.end()) it->get_to(ws.localSize); + it = j.find("PerTileDispatch"s); + if (it != j.end()) it->get_to(ws.perTileDispatch ); } static void from_json(const Json& j, ShaderPassDescription::RayTracingSettings& rts) { - auto it = j.find("MaxRecursionDepth"); + auto it = j.find("MaxRecursionDepth"s); if (it != j.end()) it->get_to(rts.maxRayRecursionDepth); } std::optional ShaderDescriptionLoader::Load(AssetManager& assetManager, const std::string& filename) { Json json; -#if true { std::vector data; if (!assetManager.LoadFileIntoMemory(filename, data)) @@ -225,61 +240,49 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass } json = Json::parse(data); } -#else - // Load using file streams - { - std::ifstream file(filename); - if (!file.is_open()) - { - return std::nullopt; - //throw std::runtime_error("failed to open file!"); - } - file >> json; - } -#endif std::vector vertexFormats; std::map vertexFormatLookup; // lookup in to the vertexFormats vector (by vertex format name) - if (json.contains("Vertex")) + if (json.contains("Vertex"s)) { // Array of 'Vertex" objects - for (const auto& el : json["Vertex"]) + for (const auto& el : json["Vertex"s]) { std::vector elements; std::vector elementIds; VertexFormat::eInputRate rate = VertexFormat::eInputRate::Vertex; - uint32_t span = el["Span"]; + uint32_t span = el["Span"s]; uint32_t offset = 0; - for (auto& ar : el["Elements"]) + for (auto& ar : el["Elements"s]) { - const std::string& elementType = ar["Type"]; + const std::string& elementType = ar["Type"s]; const auto typeIt = cElementTypeByName.find(elementType); assert(typeIt != cElementTypeByName.end()); - const uint32_t elementOffset = ar.contains("Offset") ? (uint32_t)ar["Offset"] : offset; + const uint32_t elementOffset = ar.contains("Offset"s) ? (uint32_t)ar["Offset"s] : offset; elements.emplace_back(VertexFormat::Element{ elementOffset, typeIt->second }); - if (ar.contains("Name")) + if (ar.contains("Name"s)) { - elementIds.emplace_back(ar["Name"]); + elementIds.emplace_back(ar["Name"s]); } offset = elementOffset + typeIt->second.size(); if (offset > span) { - throw std::runtime_error("Element outside of span range"); + throw std::runtime_error(filename + " : Element outside of span range"s); } } if (elementIds.size() > 0 && elementIds.size() != elements.size()) { - throw std::runtime_error("all vertex elements must all be named or none named!"); + throw std::runtime_error(filename + " : all vertex elements must all be named or none named!"s); } - if (el.contains("Name")) + if (el.contains("Name"s)) { - vertexFormatLookup.try_emplace(el["Name"], (uint32_t)vertexFormats.size()); // put in the lookup before vertexFormats.emplace_back so correctly indexed (from 0) + vertexFormatLookup.try_emplace(el["Name"s], (uint32_t)vertexFormats.size()); // put in the lookup before vertexFormats.emplace_back so correctly indexed (from 0) } - if (el.contains("Rate")) + if (el.contains("Rate"s)) { - const auto rateIt = cBufferRateByName.find(el["Rate"]); + const auto rateIt = cBufferRateByName.find(el["Rate"s]); assert(rateIt != cBufferRateByName.end()); rate = rateIt->second; } @@ -288,7 +291,13 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass } std::vector passNames; std::vector descriptions; - for(const auto& ar: json["Passes"] ) + auto jtileShading = json["TileShading"s]; + bool tileShading = false; + if (!jtileShading.is_null()) + { + jtileShading.get_to( tileShading ); + } + for(const auto& ar: json["Passes"s] ) { std::string taskShader; std::string meshShader; @@ -309,95 +318,120 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass ShaderPassDescription::WorkGroupSettings workGroupSettings; ShaderPassDescription::RayTracingSettings rayTracingSettings; std::vector specializationConstants; + std::vector rootSamplers; for (const auto& el : ar.items()) { - if (el.key().compare( "Name" ) == 0) + if (el.key().compare("Name"s ) == 0) { passname = el.value(); } - else if (el.key().compare( "Shaders" ) == 0) + else if (el.key().compare("Shaders"s ) == 0) { // Either Compute or Vertex shader has to be defined, but Fragment is optional. // Or none of the above and Ray Generation has to be defined, but any hit, closest hit and miss are optional - taskShader = el.value().contains( "Task" ) ? (std::string) el.value()["Task"] : std::string(); - meshShader = el.value().contains( "Mesh" ) ? (std::string) el.value()["Mesh"] : std::string(); - computeShader = el.value().contains( "Compute" ) ? (std::string) el.value()["Compute"] : std::string(); - vertexShader = el.value().contains( "Vertex" ) ? (std::string) el.value()["Vertex"] : std::string(); - fragmentShader = el.value().contains( "Fragment" ) ? (std::string) el.value()["Fragment"] : std::string(); - rayGenerationShader = el.value().contains( "RayGeneration" ) ? (std::string) el.value()["RayGeneration"] : std::string(); - rayClosestHitShader = el.value().contains( "RayClosestHit" ) ? (std::string) el.value()["RayClosestHit"] : std::string(); - rayAnyHitShader = el.value().contains( "RayAnyHit" ) ? (std::string) el.value()["RayAnyHit"] : std::string(); - rayMissShader = el.value().contains( "RayMiss" ) ? (std::string) el.value()["RayMiss"] : std::string(); + taskShader = el.value().contains( "Task"s) ? (std::string) el.value()["Task"s] : std::string(); + meshShader = el.value().contains( "Mesh"s) ? (std::string) el.value()["Mesh"s] : std::string(); + computeShader = el.value().contains( "Compute"s) ? (std::string) el.value()["Compute"s] : std::string(); + vertexShader = el.value().contains( "Vertex"s) ? (std::string) el.value()["Vertex"s] : std::string(); + fragmentShader = el.value().contains( "Fragment"s) ? (std::string) el.value()["Fragment"s] : std::string(); + rayGenerationShader = el.value().contains( "RayGeneration"s) ? (std::string) el.value()["RayGeneration"s] : std::string(); + rayClosestHitShader = el.value().contains( "RayClosestHit"s) ? (std::string) el.value()["RayClosestHit"s] : std::string(); + rayAnyHitShader = el.value().contains( "RayAnyHit"s) ? (std::string) el.value()["RayAnyHit"s] : std::string(); + rayMissShader = el.value().contains( "RayMiss"s) ? (std::string) el.value()["RayMiss"s] : std::string(); if (meshShader.empty() && computeShader.empty() && fragmentShader.empty() && vertexShader.empty() && rayGenerationShader.empty()) { - throw std::runtime_error( "must have a Vertex, Compute, Mesh or RayGeneration shader name!" ); + throw std::runtime_error(filename + " : must have a Vertex, Compute, Mesh or RayGeneration shader name!"s); } if (!taskShader.empty() && meshShader.empty()) { - throw std::runtime_error( "must have a Mesh shader when a Task is provided !" ); + throw std::runtime_error(filename + " : must have a Mesh shader when a Task is provided !"s); + } + if (!fragmentShader.empty() && (meshShader.empty() && vertexShader.empty())) + { + throw std::runtime_error(filename + " : must have a Vertex or Mesh shader when a Fragment is provided !"s); + } + const bool rayGen = !rayGenerationShader.empty(); + const bool rayProcess = !(rayClosestHitShader.empty() && rayAnyHitShader.empty() && rayMissShader.empty()); + if (rayGen && !rayProcess) + { + throw std::runtime_error(filename + " : must have a Ray hit, miss or closest hit shader when a RayGeneration is provided!"s); + } + else if (!rayGen && rayProcess) + { + throw std::runtime_error(filename + " : must have a RayGeneration shader when a Ray hit, miss or closest hit shader is provided!"s); } } - else if (el.key().compare( "DescriptorSets" ) == 0) + else if (el.key().compare("DescriptorSets"s) == 0) { - for (const auto& ar : el.value()) + for (uint32_t setIndex = 0; const auto& ar : el.value()) { std::vector descriptors; - if (ar.contains( "Buffers" )) + if (ar.contains( "Buffers"s)) { - auto& buffel = ar["Buffers"]; + auto& buffel = ar["Buffers"s]; for (const auto& ar : buffel) { - const std::string& bufferType = ar["Type"]; - uint32_t count = ar.contains( "Count" ) ? (uint32_t) ar["Count"] : 1; - bool readOnly = ar.contains( "ReadOnly" ) ? (bool) ar["ReadOnly"] : false; // may be overridden if the 'Type' is intrinsically read-only + const std::string& bufferType = ar["Type"s]; + uint32_t count = ar.contains( "Count"s) ? (uint32_t) ar["Count"s] : 1; + bool readOnly = ar.contains( "ReadOnly"s) ? (bool) ar["ReadOnly"s] : false; // may be overridden if the 'Type' is intrinsically read-only DescriptorSetDescription::StageFlag stageBindingFlags = { DescriptorSetDescription::StageFlag::t::None }; - for (const auto& ar2 : ar["Stages"]) + for (const auto& ar2 : ar["Stages"s]) { stageBindingFlags = stageBindingFlags | cStageFlagBitsByName.find( ar2 )->second; } std::vector names; - if (ar.contains( "Names" )) + if (ar.contains( "Names"s)) { - for (auto& ar2 : ar["Names"]) + for (auto& ar2 : ar["Names"s]) { names.push_back( ar2 ); } } + int registerIndex = -1; // default, ascending register indices + if (ar.contains( "Register"s)) + { + registerIndex = (int) ar["Register"s]; + } assert( names.size() <= 1 ); ///TODO: array of names does not currently work (especially since dynmamic 'Count' was added) //count = std::max(count, (uint32_t)names.size()); const auto& bufferTypeData = cBufferTypeByName.find( bufferType )->second; - descriptors.emplace_back( DescriptorSetDescription::DescriptorTypeAndCount { bufferTypeData.type, stageBindingFlags, names, (int) count, readOnly || bufferTypeData.readOnly } ); + descriptors.emplace_back( DescriptorSetDescription::DescriptorTypeAndCount { bufferTypeData.type, stageBindingFlags, names, (int) count, readOnly || bufferTypeData.readOnly, registerIndex } ); } } - sets.push_back( { std::move( descriptors ) } ); + std::string name; + if (ar.contains("Name"s)) + { + name = ar["Name"s]; + } + sets.push_back( { name, setIndex++, std::move( descriptors ) } ); } } - else if (el.key().compare( "Outputs" ) == 0) + else if (el.key().compare("Outputs"s) == 0) { for (const auto& ar2 : el.value()) { outputs.push_back( ar2 ); } } - else if (el.key().compare( "FixedFunction" ) == 0) + else if (el.key().compare("FixedFunction"s) == 0) { el.value().get_to( fixedFunctionSettings );// (); } - else if (el.key().compare( "SampleShading" ) == 0) + else if (el.key().compare("SampleShading"s) == 0) { el.value().get_to( sampleShadingSettings );// (); } - else if (el.key().compare( "WorkGroup" ) == 0) + else if (el.key().compare("WorkGroup"s) == 0) { el.value().get_to( workGroupSettings );// (); } - else if (el.key().compare( "RayTracing" ) == 0) + else if (el.key().compare("RayTracing"s) == 0) { el.value().get_to( rayTracingSettings );// (); } - else if (el.key().compare( "VertexBindings" ) == 0) + else if (el.key().compare("VertexBindings"s) == 0) { // Array of VertexBinding names for (const auto& vertexFormatName : el.value()) @@ -409,16 +443,16 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass } else { - throw std::runtime_error( "VertexBinding does not match a Name in the Vertex array" ); + throw std::runtime_error(filename + " : VertexBinding does not match a Name in the Vertex array"s); } } } - else if (el.key().compare( "SpecializationConstants" ) == 0) + else if (el.key().compare("SpecializationConstants"s) == 0) { for (const auto& ar : el.value()) { - const std::string& constantName = ar["Name"]; - const std::string& constantType = ar["Type"]; + const std::string& constantName = ar["Name"s]; + const std::string& constantType = ar["Type"s]; const auto typeIt = cElementTypeByName.find( constantType ); assert( typeIt != cElementTypeByName.end() ); assert( typeIt->second == VertexFormat::Element::ElementType::t::Int32 || typeIt->second == VertexFormat::Element::ElementType::t::Float || typeIt->second == VertexFormat::Element::ElementType::t::Boolean ); @@ -426,6 +460,14 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass specializationConstants.push_back( SpecializationConstantDescription { constantName, (uint32_t) specializationConstants.size(), typeIt->second } ); } } + else if (el.key().compare("RootSamplers"s) == 0) + { + for (const auto& ar : el.value()) + { + CreateSamplerObjectInfo& samplerInfo = rootSamplers.emplace_back( CreateSamplerObjectInfo{} ); + ar.get_to( samplerInfo ); + } + } } descriptions.emplace_back( std::move(sets), @@ -442,10 +484,30 @@ std::optional ShaderDescriptionLoader::Load(AssetManager& ass std::move(fixedFunctionSettings), std::move(sampleShadingSettings), std::move(workGroupSettings), - std::move(rayTracingSettings), + std::move(rayTracingSettings), std::move(vertexFormatBindings), - std::move(specializationConstants) ); + std::move(specializationConstants), + std::move(rootSamplers) + ); passNames.push_back(passname); } - return std::make_optional(ShaderDescription{ std::move(vertexFormats), std::move(descriptions), passNames }); + + // Run sanity checks on the shader description to catch common errors. + bool sane = std::all_of( descriptions.begin(), descriptions.end(), [&filename]( const ShaderPassDescription& desc ) -> bool { + if (!desc.m_vertexName.empty()) + { + if (desc.m_outputs.empty()) + { + //throw std::runtime_error(filename + " : must have outputs (in json) when vertex shader (rasterization) provided !"s); + //return false; // not hit + } + } + return true; + } ); + if (!sane) + { + return std::nullopt; + } + + return std::make_optional(ShaderDescription{ std::move(vertexFormats), std::move(descriptions), passNames, tileShading }); } diff --git a/framework/code/material/shaderDescription.hpp b/framework/code/material/shaderDescription.hpp index 411d37b..d04c378 100644 --- a/framework/code/material/shaderDescription.hpp +++ b/framework/code/material/shaderDescription.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,6 +10,7 @@ #include "vertexFormat.hpp" #include "descriptorSetDescription.hpp" #include "specializationConstantDescription.hpp" +#include "texture/sampler.hpp" #include #include #include @@ -18,6 +19,7 @@ // Forward declarations class AssetManager; +class CreateSamplerObjectInfo; /// Describes a 'ShaderPass'. /// Is platform agnostic. @@ -82,44 +84,42 @@ class ShaderPassDescription BlendFactor srcAlphaBlendFactor = BlendFactor::One; BlendFactor dstAlphaBlendFactor = BlendFactor::Zero; uint32_t colorWriteMask = 0x7fffffff; - //VkBlendOp colorBlendOp; - //VkBlendOp alphaBlendOp; }; struct WorkGroupSettings { std::array localSize = {}; + bool perTileDispatch = false; }; struct RayTracingSettings { uint32_t maxRayRecursionDepth = 1; }; - ShaderPassDescription( std::vector sets, std::vector outputs, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, FixedFunctionSettings fixedFunctionSettings, SampleShadingSettings sampleShadingSettings, WorkGroupSettings workGroupSettings, RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants); - ShaderPassDescription( std::vector sets, std::vector outputs, std::string taskName, std::string meshName, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, FixedFunctionSettings fixedFunctionSettings, SampleShadingSettings sampleShadingSettings, WorkGroupSettings workGroupSettings, RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants); - + ShaderPassDescription( std::vector sets, std::vector outputs, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, FixedFunctionSettings fixedFunctionSettings, SampleShadingSettings sampleShadingSettings, WorkGroupSettings workGroupSettings, RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants, std::vector rootSamplers ); + ShaderPassDescription( std::vector sets, std::vector outputs, std::string taskName, std::string meshName, std::string computeName, std::string vertexName, std::string fragmentName, std::string rayGenerationName, std::string rayClosestHitName, std::string rayAnyHitName, std::string rayMissName, FixedFunctionSettings fixedFunctionSettings, SampleShadingSettings sampleShadingSettings, WorkGroupSettings workGroupSettings, RayTracingSettings rayTracingSettings, std::vector vertexFormatBindings, std::vector specializationConstants, std::vector rootSamplers ); ShaderPassDescription(ShaderPassDescription&&) = default; - std::vector m_sets; - std::vector m_outputs; - std::string m_taskName; ///< Name of the task shader (optional, not valid if m_vertexName or m_fragmentName are set) - std::string m_meshName; ///< Name of the mesh shader (optional, not valid if m_vertexName or m_fragmentName are set), mandatory if task shader is set - std::string m_computeName; ///< Name of the compute shader (optional, not valid if m_vertexName or m_fragmentName are set) - std::string m_vertexName; ///< Name of the vertex shader used by this shader pass (optional, not valid if m_computeName set) - std::string m_fragmentName; ///< Name of the fragment shader used by this shader pass (optional, only valid if m_vertexName set) - std::string m_rayGenerationName; - std::string m_rayClosestHitName; - std::string m_rayAnyHitName; - std::string m_rayMissName; - FixedFunctionSettings m_fixedFunctionSettings; - SampleShadingSettings m_sampleShadingSettings; - WorkGroupSettings m_workGroupSettings; - RayTracingSettings m_rayTracingSettings; - std::vector m_vertexFormatBindings; //< Indices of the vertex buffers bound by this shader pass (index in to ShaderDescription::m_vertexFormats) - std::vector m_constants; - //std::vector m_vertexInstanceFormatBindings; ///TODO: support more than one buffer of instance rate data + std::vector m_sets; + std::vector m_outputs; + std::string m_taskName; ///< Name of the task shader (optional, not valid if m_vertexName or m_fragmentName are set) + std::string m_meshName; ///< Name of the mesh shader (optional, not valid if m_vertexName or m_fragmentName are set), mandatory if task shader is set + std::string m_computeName; ///< Name of the compute shader (optional, not valid if m_vertexName or m_fragmentName are set) + std::string m_vertexName; ///< Name of the vertex shader used by this shader pass (optional, not valid if m_computeName set) + std::string m_fragmentName; ///< Name of the fragment shader used by this shader pass (optional, only valid if m_vertexName set) + std::string m_rayGenerationName; + std::string m_rayClosestHitName; + std::string m_rayAnyHitName; + std::string m_rayMissName; + FixedFunctionSettings m_fixedFunctionSettings; + SampleShadingSettings m_sampleShadingSettings; + WorkGroupSettings m_workGroupSettings; + RayTracingSettings m_rayTracingSettings; + std::vector m_vertexFormatBindings; //< Indices of the vertex buffers bound by this shader pass (index in to ShaderDescription::m_vertexFormats) + std::vector m_constants; + std::vector m_rootSamplers; }; -/// Describes a 'Shader'. +/// Describes a 'ShaderBase'. /// May contain multiple shader passes (vector of ShaderPassDescription and a pass name lookup). /// Also contain description (VertexFormat) of the all the vertex buffers that can be bound to this set of shaders. /// Is platform agnostic. @@ -134,7 +134,7 @@ class ShaderDescription , m_descriptionPerPass(std::move(other.m_descriptionPerPass)) , m_passNameToIndex(std::move(other.m_passNameToIndex)) {} - ShaderDescription(std::vector vertexFormats, std::vector passes, const std::vector& passNames) + ShaderDescription(std::vector vertexFormats, std::vector passes, const std::vector& passNames, bool tileShading) : m_vertexFormats(std::move(vertexFormats)) , m_descriptionPerPass(std::move(passes)) { diff --git a/framework/code/material/shaderManager.cpp b/framework/code/material/shaderManager.cpp index 6f5fc58..74f7f81 100644 --- a/framework/code/material/shaderManager.cpp +++ b/framework/code/material/shaderManager.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,28 +9,21 @@ #include "shaderManager.hpp" #include "shaderDescription.hpp" #include "shaderModule.hpp" -#include "descriptorSetLayout.hpp" #include "shader.hpp" -#include "vulkan/specializationConstantsLayout.hpp" -#include "vulkan/shader.hpp" -#include "vulkan/shaderModule.hpp" -#include "vulkan/pipelineLayout.hpp" -#include "vulkan/vulkan.hpp" #include "system/os_common.h" -#include -ShaderManager::ShaderManager(GraphicsApiBase& graphicsApi) +ShaderManagerBase::ShaderManagerBase(GraphicsApiBase& graphicsApi) : m_GraphicsApi(graphicsApi) {} -ShaderManager::~ShaderManager() +ShaderManagerBase::~ShaderManagerBase() { assert(m_shadersByName.empty()); // Expected that the derived (playform specific) class destroys the shaders assert(m_shaderModulesByName.empty()); } -void ShaderManager::RegisterRenderPassNames(const std::span passNames) +void ShaderManagerBase::RegisterRenderPassNames(const std::span passNames) { assert(m_shaderPassIndexByName.size() == 0); // only expected to be called once uint32_t passIndex = 0; @@ -43,9 +36,13 @@ void ShaderManager::RegisterRenderPassNames(const std::span pa } } -bool ShaderManager::AddShader(AssetManager& assetManager, const std::string& shaderName, const std::string& filename) +bool ShaderManagerBase::AddShader( + AssetManager& assetManager, + const std::string& shaderName, + const std::string& filename, + const std::filesystem::path& source_dir) { - auto shaderDescription = ShaderDescriptionLoader::Load(assetManager, filename); + auto shaderDescription = ShaderDescriptionLoader::Load(assetManager, source_dir.empty() ? filename : (source_dir / filename).string()); if (!shaderDescription) { return false; @@ -53,7 +50,16 @@ bool ShaderManager::AddShader(AssetManager& assetManager, const std::string& sha return AddShader(assetManager, shaderName, std::move(*shaderDescription)); } -//const ShaderDescription* ShaderManager::GetShaderDescription(const std::string& shaderName) const +bool ShaderManagerBase::AddShader( + AssetManager& assetManager, + const std::string& shaderName, + const std::filesystem::path& filename, + const std::filesystem::path& source_dir) +{ + return AddShader(assetManager, shaderName, filename.string(), source_dir); +} + +//const ShaderDescription* ShaderManagerBase::GetShaderDescription(const std::string& shaderName) const //{ // auto it = m_shaderDescriptionsByName.find(shaderName); // if (it != m_shaderDescriptionsByName.end()) @@ -61,7 +67,7 @@ bool ShaderManager::AddShader(AssetManager& assetManager, const std::string& sha // return nullptr; //} -const Shader* ShaderManager::GetShader(const std::string& shaderName) const +const ShaderBase* ShaderManagerBase::GetShader(const std::string& shaderName) const { auto it = m_shadersByName.find(shaderName); if (it != m_shadersByName.end()) diff --git a/framework/code/material/shaderManager.hpp b/framework/code/material/shaderManager.hpp index acc2531..14bb7a6 100644 --- a/framework/code/material/shaderManager.hpp +++ b/framework/code/material/shaderManager.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,29 +11,28 @@ #include #include #include +#include // Forward declarations class AssetManager; -class ShaderDescription; -class Shader; -class ShaderModule; -//template class ShaderT; -//template class ShaderModuleT; -template class ShaderManagerT; class GraphicsApiBase; +class ShaderDescription; +class ShaderBase; +class ShaderModuleBase; +template class ShaderManager; -/// Manages Shader loading (and lookup). +/// Manages ShaderBase loading (and lookup). /// This is the entry point for applications to register (AddShader) the shaders they want to use and access the loaded data. /// @ingroup Material -class ShaderManager +class ShaderManagerBase { - ShaderManager() = delete; - ShaderManager& operator=(const ShaderManager&) = delete; - ShaderManager(const ShaderManager&) = delete; + ShaderManagerBase() = delete; + ShaderManagerBase& operator=(const ShaderManagerBase&) = delete; + ShaderManagerBase(const ShaderManagerBase&) = delete; public: - template using tApiDerived = ShaderManagerT; // make apiCast work! - ShaderManager(GraphicsApiBase& graphicsApi); - virtual ~ShaderManager(); + template using tApiDerived = ShaderManager; // make apiCast work! + ShaderManagerBase(GraphicsApiBase& graphicsApi); + virtual ~ShaderManagerBase(); /// User registration of render passes. Passes must be registered before shaders referencing these passes can be loaded. /// Order of pass names is important (passes can be requested by index). @@ -43,11 +42,17 @@ class ShaderManager /// @param shaderName name to be given to this shader for lookup within the application code (user determined name). /// @param filename name of the (json formatted) shader desciption file to load and parse. Filename should include extension. /// @return true if everything loaded correctly. - bool AddShader(AssetManager& assetManager, const std::string& shaderName, const std::string& filename); + bool AddShader(AssetManager& assetManager, const std::string& shaderName, const std::string& filename, const std::filesystem::path& source_dir = std::filesystem::path()); + + /// @brief Load a given shader definition (json file) and load all referenced shaders modules. Makes shader ready for material to be created from it. + /// @param shaderName name to be given to this shader for lookup within the application code (user determined name). + /// @param filename path of the (json formatted) shader desciption file to load and parse. Filename should include extension. + /// @return true if everything loaded correctly. + bool AddShader(AssetManager& assetManager, const std::string& shaderName, const std::filesystem::path& filename, const std::filesystem::path& source_dir = std::filesystem::path()); - // Get Shader for the given shader name + // Get ShaderBase for the given shader name /// @returns nullptr if shaderName unknown - virtual const Shader* GetShader(const std::string& shaderName) const; + const ShaderBase* GetShader(const std::string& shaderName) const; protected: virtual bool AddShader(AssetManager& assetManager, const std::string& shaderName, ShaderDescription shaderDescription) = 0; @@ -55,7 +60,7 @@ class ShaderManager protected: GraphicsApiBase& m_GraphicsApi; std::map m_shaderDescriptionsByName; // Contains descriptions (module names, inputs etc) for all the passes of a given 'shader' - std::map> m_shaderModulesByName; // Contains the ShaderModule (one per hardware shader program) - std::map> m_shadersByName; // Contains all the passes of a given Shader (this contains what is described by m_shaderDescriptionsByName although is not the material) + std::map> m_shaderModulesByName; // Contains the ShaderModule (one per hardware shader program) + std::map> m_shadersByName; // Contains all the passes of a given ShaderBase (this contains what is described by m_shaderDescriptionsByName although is not the material) std::map < std::string, uint32_t> m_shaderPassIndexByName; // Contains the registered render pass names (and their indices) }; diff --git a/framework/code/material/shaderManagerT.hpp b/framework/code/material/shaderManagerT.hpp index 6d101de..2c4ac55 100644 --- a/framework/code/material/shaderManagerT.hpp +++ b/framework/code/material/shaderManagerT.hpp @@ -1,12 +1,13 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once +#include #include #include "shaderManager.hpp" #include "shader.hpp" @@ -16,33 +17,41 @@ /// Tempated (by graphics api) ShaderManager. /// @ingroup Material template -class ShaderManagerT : public ShaderManager +class ShaderManager : public ShaderManagerBase { - ShaderManagerT() = delete; - ShaderManagerT& operator=(const ShaderManager&) = delete; - ShaderManagerT(const ShaderManagerT&) = delete; + ShaderManager() = delete; + ShaderManager& operator=(const ShaderManagerBase&) = delete; + ShaderManager(const ShaderManager&) = delete; public: - ShaderManagerT(T_GFXAPI& gpxApi); - ~ShaderManagerT(); + ShaderManager(T_GFXAPI& gpxApi); + ~ShaderManager(); + // Get ShaderBase for the given shader name + /// @returns nullptr if shaderName unknown + const Shader* GetShader(const std::string& shaderName) const; + + using ShaderManagerBase::AddShader; // bring into name resolution scope protected: - /// @brief Load all referenced shaders modules from the given description. Makes shader ready for material to be created from it. + /// @brief Load all referenced shaders modules from the given description for all passes. Makes shader ready for material to be created from it. /// @param shaderName name to be given to this shader for lookup within the application code (user determined name). /// @param shaderDescription description of this shader /// @return true if everything loaded correctly. bool AddShader(AssetManager& assetManager, const std::string& shaderName, ShaderDescription shaderDescription) override; -protected: + /// @brief Load all referenced shaders modules from the given description (called by AddShader) + std::optional > LoadShaderModules(AssetManager& assetManager, const ShaderPassDescription& shaderPassDescription); + + T_GFXAPI& GetGraphicsApi() const { return static_cast(m_GraphicsApi); } }; template -ShaderManagerT::ShaderManagerT(T_GFXAPI& gfxApi) : ShaderManager(gfxApi) +ShaderManager::ShaderManager(T_GFXAPI& gfxApi) : ShaderManagerBase(gfxApi) {} template -ShaderManagerT::~ShaderManagerT() +ShaderManager::~ShaderManager() { // Destroy the Shaders (which destroys the owned ShaderPasses etc) auto& rVulkan = static_cast(m_GraphicsApi); @@ -50,7 +59,7 @@ ShaderManagerT::~ShaderManagerT() s.second->Destroy(rVulkan); m_shadersByName.clear(); - // ShaderModules (do after Shaders, as the Shader(Pass) points to the ShaderModule) + // ShaderModules (do after Shaders, as the ShaderBase(Pass) points to the ShaderModule) // If you get an error here then maybe you haven't included the 'ShaderModule.hpp' for your graphics API ! for (auto& sm : m_shaderModulesByName) apiCast(sm.second.get())->Destroy(rVulkan); @@ -58,7 +67,7 @@ ShaderManagerT::~ShaderManagerT() } template -bool ShaderManagerT::AddShader(AssetManager& assetManager, const std::string& shaderName, ShaderDescription shaderDescription) +bool ShaderManager::AddShader(AssetManager& assetManager, const std::string& shaderName, ShaderDescription shaderDescription) { bool success = true; // Create the ShaderDescription @@ -86,7 +95,7 @@ bool ShaderManagerT::AddShader(AssetManager& assetManager, const std:: orderedPassNames[passNameIndex.second] = passNameIndex; } - T_GFXAPI& rGfxApi = static_cast(m_GraphicsApi); + T_GFXAPI& rGfxApi = GetGraphicsApi(); for (const auto& passIt : orderedPassNames) { @@ -98,319 +107,324 @@ bool ShaderManagerT::AddShader(AssetManager& assetManager, const std:: auto passIdxIt = m_shaderPassIndexByName.find(passName); if (passIdxIt == m_shaderPassIndexByName.end()) { - //LOGI("ShaderManager::AddShader - pass name \"%s\" not registered with RegisterRenderPassNames", passName.c_str()); + //LOGI("ShaderManagerBase::AddShader - pass name \"%s\" not registered with RegisterRenderPassNames", passName.c_str()); // This may not be an error (if we are manually grabbing or iterating passes from within a shader/material). } - std::optional> shaderModules; + auto shaderModules = LoadShaderModules(assetManager, shaderPassDescription); + if (!shaderModules) + { + return false; + } - if (shaderPassDescription.m_vertexName.empty() && !shaderPassDescription.m_taskName.empty() && !shaderPassDescription.m_meshName.empty() && !shaderPassDescription.m_fragmentName.empty()) + // + // Create the descriptor set layout (for the ShaderPass) + // + std::vector> descriptorSetLayouts; + + descriptorSetLayouts.reserve(shaderPassDescription.m_sets.size()); + for (const auto& set : shaderPassDescription.m_sets) { - // Create the task ShaderModule - auto taskShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_taskName); - if (taskShader.second == true) + if (!descriptorSetLayouts.emplace_back().Init(rGfxApi, set)) { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Task)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_taskName); - success = false; - break; - } - taskShader.first->second = std::move(pShaderModule); + // Error + return false; } - auto* pTaskShaderModule = apiCast(taskShader.first->second.get()); + } + + // + // Create the pipeline layout (for the ShaderPass) + // + PipelineLayout pipelineLayout; + if (!pipelineLayout.Init(rGfxApi, descriptorSetLayouts, shaderPassDescription.m_rootSamplers)) + { + // Error. Is ok (just means we didnt have a valid descriptor set layout yet) + } + + PipelineVertexInputState pipelineVertexInputState{ *pShaderDescription, shaderPassDescription.m_vertexFormatBindings }; + + SpecializationConstantsLayout specializationConstants{ shaderPassDescription.m_constants }; + + // + // Add the ShaderPass to the ShaderBase + // + + shaderPasses.emplace_back(shaderPassDescription, std::move(shaderModules.value()), std::move(descriptorSetLayouts), std::move(pipelineLayout), std::move(pipelineVertexInputState), std::move(specializationConstants), (uint32_t)shaderPassDescription.m_outputs.size()); + shaderPassNames.push_back(passName); + } + + if (!success) + { + // if we failed to load something then remove all knowledge of this shader + m_shaderDescriptionsByName.erase(shaderName); + return false; + } + + // Create the ShaderBase class + auto shader = m_shadersByName.try_emplace(shaderName, std::make_unique>( pShaderDescription, std::move(shaderPasses), shaderPassNames )); + if (!shader.second) + { + // Duplicate shader name + return false; + } - // Create the mesh ShaderModule - auto meshShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_meshName); - if (meshShader.second == true) + return true; +} + +template +std::optional> ShaderManager::LoadShaderModules(AssetManager& assetManager, const ShaderPassDescription& shaderPassDescription) +{ + // TEMPORARY FIX FOR RECENT MEDIA FOLDER CHANGES // + + if (shaderPassDescription.m_fragmentName.starts_with("Media/Shaders/")) + { + LOGW("Project uses old 'Media/Shaders/' path its shaders (fragment), the new one is 'build/Media/Shaders', please update your shader json accordingly, temporary fix will be applied"); + const_cast(shaderPassDescription).m_fragmentName = std::string("build/Media/Shaders/") + shaderPassDescription.m_fragmentName.substr(std::string("Media/Shaders/").size()); + } + + if (shaderPassDescription.m_vertexName.starts_with("Media/Shaders/")) + { + LOGW("Project uses old 'Media/Shaders/' path its shaders (vertex), the new one is 'build/Media/Shaders', please update your shader json accordingly, temporary fix will be applied"); + const_cast(shaderPassDescription).m_vertexName = std::string("build/Media/Shaders/") + shaderPassDescription.m_vertexName.substr(std::string("Media/Shaders/").size()); + } + + if (shaderPassDescription.m_computeName.starts_with("Media/Shaders/")) + { + LOGW("Project uses old 'Media/Shaders/' path its shaders (compute), the new one is 'build/Media/Shaders', please update your shader json accordingly, temporary fix will be applied"); + const_cast(shaderPassDescription).m_computeName = std::string("build/Media/Shaders/") + shaderPassDescription.m_computeName.substr(std::string("Media/Shaders/").size()); + } + + /////////////////////////////////////////////////// + + std::optional> shaderModules; + auto& rGfxApi = GetGraphicsApi(); + + if (!shaderPassDescription.m_meshName.empty()) + { + // Create the mesh ShaderModule + auto meshShader = m_shaderModulesByName.try_emplace( shaderPassDescription.m_meshName ); + if (meshShader.second == true) + { + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load( rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Mesh )) { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Mesh)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_meshName); - success = false; - break; - } - meshShader.first->second = std::move(pShaderModule); + // Failed to load, remove the unloaded Shader class! + m_shaderModulesByName.erase( shaderPassDescription.m_meshName ); + return {}; } - auto* pMeshShaderModule = apiCast(meshShader.first->second.get()); + meshShader.first->second = std::move( pShaderModule ); + } + auto* pMeshShaderModule = apiCast( meshShader.first->second.get() ); - // Create the fragment ShaderModule - auto fragShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_fragmentName); - if (fragShader.second == true) + // Create the fragment ShaderModule + if (shaderPassDescription.m_fragmentName.empty()) + { + assert( shaderPassDescription.m_fragmentName.empty() ); + return {}; + } + auto fragShader = m_shaderModulesByName.try_emplace( shaderPassDescription.m_fragmentName ); + if (fragShader.second == true) + { + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load( rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Fragment )) { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Fragment)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_fragmentName); - success = false; - break; - } - fragShader.first->second = std::move(pShaderModule); + // Failed to load, remove the unloaded Shader class! + m_shaderModulesByName.erase( shaderPassDescription.m_fragmentName ); + return {}; } - auto* pFragShaderModule = apiCast(fragShader.first->second.get()); - - shaderModules.emplace(ShaderModules(GraphicsTaskMeshShaderModules{ *pTaskShaderModule, *pMeshShaderModule, *pFragShaderModule })); + fragShader.first->second = std::move( pShaderModule ); } - else if (shaderPassDescription.m_vertexName.empty() && shaderPassDescription.m_taskName.empty() && !shaderPassDescription.m_meshName.empty() && !shaderPassDescription.m_fragmentName.empty()) + auto* pFragShaderModule = apiCast( fragShader.first->second.get() ); + + // Create the (optional) task shader module + if (!shaderPassDescription.m_taskName.empty()) { - // Create the mesh ShaderModule - auto meshShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_meshName); - if (meshShader.second == true) + auto taskShader = m_shaderModulesByName.try_emplace( shaderPassDescription.m_taskName ); + if (taskShader.second == true) { // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); + auto pShaderModule = std::make_unique>(); // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Mesh)) + if (!pShaderModule->Load( rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Task )) { // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_meshName); - success = false; - break; + m_shaderModulesByName.erase( shaderPassDescription.m_taskName ); + return {}; } - meshShader.first->second = std::move(pShaderModule); + taskShader.first->second = std::move( pShaderModule ); } - auto* pMeshShaderModule = apiCast(meshShader.first->second.get()); - + auto* pTaskShaderModule = apiCast( taskShader.first->second.get() ); + shaderModules.emplace( ShaderModules( GraphicsTaskMeshShaderModules{ *pTaskShaderModule, * pMeshShaderModule, * pFragShaderModule } ) ); + } + else + { + shaderModules.emplace( ShaderModules( GraphicsMeshShaderModules{ * pMeshShaderModule, * pFragShaderModule } ) ); + } + } + else if (!shaderPassDescription.m_taskName.empty()) + { + assert( 0 && "task shader without a mesh shader"); + return {}; + } + else if (!shaderPassDescription.m_vertexName.empty()) + { + // Create the vertex ShaderModule + auto vertShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_vertexName); + if (vertShader.second == true) + { + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Vertex)) + { + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_vertexName); + return {}; + } + vertShader.first->second = std::move(pShaderModule); + } + auto* pVertShaderModule = apiCast(vertShader.first->second.get()); + if (shaderPassDescription.m_fragmentName.empty()) // Dont have to have a fragment shader! + { + // Vertex only Module + shaderModules.emplace(ShaderModules(GraphicsShaderModuleVertOnly{ *pVertShaderModule })); + } + else + { // Create the fragment ShaderModule auto fragShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_fragmentName); if (fragShader.second == true) { // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); + auto pShaderModule = std::make_unique>(); // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Fragment)) + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Fragment)) { - // Failed to load, remove the unloaded Shader class! + // Failed to load, remove the unloaded ShaderBase class! m_shaderModulesByName.erase(shaderPassDescription.m_fragmentName); - success = false; - break; + m_shaderModulesByName.erase(shaderPassDescription.m_vertexName); + return {}; } fragShader.first->second = std::move(pShaderModule); } auto* pFragShaderModule = apiCast(fragShader.first->second.get()); - - shaderModules.emplace(ShaderModules(GraphicsMeshShaderModules{ *pMeshShaderModule, *pFragShaderModule })); + shaderModules.emplace(ShaderModules(GraphicsShaderModules{ *pVertShaderModule, *pFragShaderModule })); } - else if (!shaderPassDescription.m_vertexName.empty()) + } + else if (!shaderPassDescription.m_computeName.empty()) + { + // Create the compute ShaderModule + auto computeShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_computeName); + if (computeShader.second == true) { - // Create the vertex ShaderModule - auto vertShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_vertexName); - if (vertShader.second == true) - { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Vertex)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_vertexName); - success = false; - break; - } - vertShader.first->second = std::move(pShaderModule); - } - auto* pVertShaderModule = apiCast(vertShader.first->second.get()); - if (shaderPassDescription.m_fragmentName.empty()) // Dont have to have a fragment shader! + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::Compute)) { - // Vertex only Module - shaderModules.emplace(ShaderModules(GraphicsShaderModuleVertOnly{ *pVertShaderModule })); + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_computeName); + return {}; } - else + computeShader.first->second = std::move(pShaderModule); + } + auto* pComputeShaderModule = apiCast(computeShader.first->second.get()); + shaderModules.emplace(ShaderModules(ComputeShaderModule{*pComputeShaderModule})); + } + else if (!shaderPassDescription.m_rayGenerationName.empty()) + { + // Create the ray generation ShaderModule + auto rayGenerationShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayGenerationName); + if (rayGenerationShader.second == true) + { + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::RayGeneration)) { - // Create the fragment ShaderModule - auto fragShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_fragmentName); - if (fragShader.second == true) - { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Fragment)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_fragmentName); - m_shaderModulesByName.erase(shaderPassDescription.m_vertexName); - success = false; - break; - } - fragShader.first->second = std::move(pShaderModule); - } - auto* pFragShaderModule = apiCast(fragShader.first->second.get()); - shaderModules.emplace(ShaderModules(GraphicsShaderModules{ *pVertShaderModule, *pFragShaderModule })); + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_rayGenerationName); + return {}; } + rayGenerationShader.first->second = std::move(pShaderModule); } - else if (!shaderPassDescription.m_computeName.empty()) + auto* pRayGenerationShaderModule = apiCast(rayGenerationShader.first->second.get()); + + const ShaderModule* pRayClosestHitShaderModule = nullptr; + const ShaderModule* pRayAnyHitShaderModule = nullptr; + const ShaderModule* pRayMissShaderModule = nullptr; + + if (!shaderPassDescription.m_rayClosestHitName.empty()) { - // Create the compute ShaderModule - auto computeShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_computeName); - if (computeShader.second == true) + auto rayClosestHitShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayClosestHitName); + if (rayClosestHitShader.second == true) { // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); + auto pShaderModule = std::make_unique>(); // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::Compute)) + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::RayClosestHit)) { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_computeName); - success = false; - break; + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_rayClosestHitName); + return {}; } - computeShader.first->second = std::move(pShaderModule); + rayClosestHitShader.first->second = std::move(pShaderModule); } - auto* pComputeShaderModule = apiCast(computeShader.first->second.get()); - shaderModules.emplace(ShaderModules(ComputeShaderModule{*pComputeShaderModule})); + pRayClosestHitShaderModule = apiCast (rayClosestHitShader.first->second.get()); } - else if (!shaderPassDescription.m_rayGenerationName.empty()) + if (!shaderPassDescription.m_rayAnyHitName.empty()) { - // Create the ray generation ShaderModule - auto rayGenerationShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayGenerationName); - if (rayGenerationShader.second == true) + auto rayAnyHitShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayAnyHitName); + if (rayAnyHitShader.second == true) { // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); + auto pShaderModule = std::make_unique>(); // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::RayGeneration)) + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::RayAnyHit)) { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_rayGenerationName); - success = false; - break; + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_rayAnyHitName); + return {}; } - rayGenerationShader.first->second = std::move(pShaderModule); + rayAnyHitShader.first->second = std::move(pShaderModule); } - auto* pRayGenerationShaderModule = apiCast(rayGenerationShader.first->second.get()); - - const ShaderModuleT* pRayClosestHitShaderModule = nullptr; - const ShaderModuleT* pRayAnyHitShaderModule = nullptr; - const ShaderModuleT* pRayMissShaderModule = nullptr; - - if (!shaderPassDescription.m_rayClosestHitName.empty()) - { - auto rayClosestHitShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayClosestHitName); - if (rayClosestHitShader.second == true) - { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::RayClosestHit)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_rayClosestHitName); - success = false; - break; - } - rayClosestHitShader.first->second = std::move(pShaderModule); - } - pRayClosestHitShaderModule = apiCast (rayClosestHitShader.first->second.get()); - } - if (!shaderPassDescription.m_rayAnyHitName.empty()) - { - auto rayAnyHitShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayAnyHitName); - if (rayAnyHitShader.second == true) - { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::RayAnyHit)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_rayAnyHitName); - success = false; - break; - } - rayAnyHitShader.first->second = std::move(pShaderModule); - } - pRayAnyHitShaderModule = apiCast(rayAnyHitShader.first->second.get()); - } - if (!shaderPassDescription.m_rayMissName.empty()) - { - auto rayMissShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayMissName); - if (rayMissShader.second == true) - { - // Create the unique_ptr object (is not already loaded) - auto pShaderModule = std::make_unique>(); - // Load the physical shader file - if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModule::ShaderType::RayMiss)) - { - // Failed to load, remove the unloaded Shader class! - m_shaderModulesByName.erase(shaderPassDescription.m_rayMissName); - success = false; - break; - } - rayMissShader.first->second = std::move(pShaderModule); - } - pRayMissShaderModule = apiCast(rayMissShader.first->second.get()); - } - - shaderModules.emplace(ShaderModules(RayTracingShaderModules{ *pRayGenerationShaderModule, pRayClosestHitShaderModule, pRayAnyHitShaderModule, pRayMissShaderModule })); + pRayAnyHitShaderModule = apiCast(rayAnyHitShader.first->second.get()); } - else + if (!shaderPassDescription.m_rayMissName.empty()) { - // Error - must have a vertex shader or a compute shader - success = false; - } - - - // - // Add the ShaderPass to the Shader - // - - // - // Create the descriptor set layout (for the ShaderPass) - // - std::vector descriptorSetLayouts; - - descriptorSetLayouts.reserve(shaderPassDescription.m_sets.size()); - for (const auto& set : shaderPassDescription.m_sets) - { - if (!descriptorSetLayouts.emplace_back().Init(rGfxApi, set)) + auto rayMissShader = m_shaderModulesByName.try_emplace(shaderPassDescription.m_rayMissName); + if (rayMissShader.second == true) { - // Error - return false; + // Create the unique_ptr object (is not already loaded) + auto pShaderModule = std::make_unique>(); + // Load the physical shader file + if (!pShaderModule->Load(rGfxApi, assetManager, shaderPassDescription, ShaderModuleBase::ShaderType::RayMiss)) + { + // Failed to load, remove the unloaded ShaderBase class! + m_shaderModulesByName.erase(shaderPassDescription.m_rayMissName); + return {}; + } + rayMissShader.first->second = std::move(pShaderModule); } + pRayMissShaderModule = apiCast(rayMissShader.first->second.get()); } - // - // Create the pipeline layout (for the ShaderPass) - // - PipelineLayout pipelineLayout; - if (!pipelineLayout.Init(rGfxApi, descriptorSetLayouts)) - { - // Error. Is ok (just means we didnt have a valid descriptor set layout yet) - } - - PipelineVertexInputState pipelineVertexInputState{ *pShaderDescription, shaderPassDescription.m_vertexFormatBindings }; - - SpecializationConstantsLayout specializationConstants{ shaderPassDescription.m_constants }; - - shaderPasses.emplace_back(shaderPassDescription, std::move(shaderModules.value()), std::move(descriptorSetLayouts), std::move(pipelineLayout), std::move(pipelineVertexInputState), std::move(specializationConstants), (uint32_t)shaderPassDescription.m_outputs.size()); - shaderPassNames.push_back(passName); + shaderModules.emplace(ShaderModules(RayTracingShaderModules{ *pRayGenerationShaderModule, pRayClosestHitShaderModule, pRayAnyHitShaderModule, pRayMissShaderModule })); } - - if (!success) + else { - // if we failed to load something then remove all knowledge of this shader - m_shaderDescriptionsByName.erase(shaderName); - return false; + // Error - must have a vertex shader or a compute shader } + return shaderModules; +} - // Create the Shader class - auto shader = m_shadersByName.try_emplace(shaderName, std::make_unique>( pShaderDescription, std::move(shaderPasses), shaderPassNames )); - if (!shader.second) - { - // Duplicate shader name - return false; - } - return true; +template +const Shader* ShaderManager::GetShader(const std::string& shaderName) const +{ + return apiCast(ShaderManagerBase::GetShader(shaderName)); } diff --git a/framework/code/material/shaderModule.hpp b/framework/code/material/shaderModule.hpp index 69b4089..5ea2948 100644 --- a/framework/code/material/shaderModule.hpp +++ b/framework/code/material/shaderModule.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -15,20 +15,20 @@ // Forward declarations class AssetManager; -class DescriptorSetLayout; +class DescriptorSetLayoutBase; class ShaderPassDescription; class VertexDescription; class VertexFormat; -template class ShaderModuleT; +template class ShaderModule; -class ShaderModule +class ShaderModuleBase { - ShaderModule(const ShaderModule&) = delete; - ShaderModule& operator=(const ShaderModule&) = delete; + ShaderModuleBase(const ShaderModuleBase&) = delete; + ShaderModuleBase& operator=(const ShaderModuleBase&) = delete; public: - template using tApiDerived = ShaderModuleT; // make apiCast work! - ShaderModule() noexcept {} - ~ShaderModule() {} + template using tApiDerived = ShaderModule; // make apiCast work! + ShaderModuleBase() noexcept {} + ~ShaderModuleBase() {} enum class ShaderType { Fragment, Vertex, Compute, RayGeneration, RayClosestHit, RayAnyHit, RayMiss, Task, Mesh @@ -43,16 +43,16 @@ class ShaderModule std::string m_filename; }; -/// Templated Shader module (container for shader code) +/// Templated ShaderBase module (container for shader code) /// Expected to be specialized (by the graphics api) /// @ingroup Material template -class ShaderModuleT : private ShaderModule +class ShaderModule : private ShaderModuleBase { - ShaderModuleT(const ShaderModuleT&) = delete; - ShaderModuleT& operator=(const ShaderModuleT&) = delete; + ShaderModule(const ShaderModule&) = delete; + ShaderModule& operator=(const ShaderModule&) = delete; public: - ShaderModuleT() noexcept = delete; // this template class must be specialized + ShaderModule() noexcept = delete; // this template class must be specialized /// Free up the vkShaderModule resource. void Destroy(T_GFXAPI&) = delete; @@ -65,5 +65,5 @@ class ShaderModuleT : private ShaderModule /// @returns true on success bool Load(T_GFXAPI&, AssetManager&, const ShaderPassDescription&, const ShaderType); - static_assert(sizeof(ShaderModuleT) != sizeof(ShaderModule)); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof(ShaderModule) != sizeof(ShaderModuleBase)); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/material/specializationConstantDescription.hpp b/framework/code/material/specializationConstantDescription.hpp index 80949cb..0d4075a 100644 --- a/framework/code/material/specializationConstantDescription.hpp +++ b/framework/code/material/specializationConstantDescription.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/material/specializationConstants.hpp b/framework/code/material/specializationConstants.hpp new file mode 100644 index 0000000..be9f7e5 --- /dev/null +++ b/framework/code/material/specializationConstants.hpp @@ -0,0 +1,27 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + + +/// Templated wrapper for collection of specialization constants (eg VkSpecializationMapEntry for Vulkan VkSpecializationInfo::pMapEntries). +/// Expected to be specialized (compile will fail if this template is used rather than a specialization of it) +/// @ingroup Material +template +class SpecializationConstants +{ + SpecializationConstants( const SpecializationConstants& ) = delete; + SpecializationConstants operator=( const SpecializationConstants& ) = delete; +public: + SpecializationConstants( SpecializationConstants&& ) noexcept = default; + SpecializationConstants() noexcept = default; + //bool Init( const SpecializationConstantsLayout& layout, const std::span constants ) {} + + //we are ok with this generic class being used as-is (eg by Dx12 who does not have specialization conatants!) + //static_assert(sizeof(SpecializationConstantsLayout ) >= 1); // Ensure this class template is specialized (and not used as-is) +}; + diff --git a/framework/code/material/specializationConstantsLayout.hpp b/framework/code/material/specializationConstantsLayout.hpp index 6bd7349..5699a5f 100644 --- a/framework/code/material/specializationConstantsLayout.hpp +++ b/framework/code/material/specializationConstantsLayout.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,17 +11,12 @@ #include "specializationConstantDescription.hpp" #include -//#include "vulkan/specializationConstantsLayout.hpp" - - -// Forward declarations -class Vulkan; /// Templated wrapper for collection of specialization constant layout data (eg VkSpecializationMapEntry for Vulkan VkSpecializationInfo::pMapEntries). /// Expected to be specialized (compile will fail if this template is used rather than a specialization of it) /// @ingroup Material template -class SpecializationConstantsLayout +class SpecializationConstantsLayout final { SpecializationConstantsLayout( const SpecializationConstantsLayout& ) = delete; SpecializationConstantsLayout& operator=( const SpecializationConstantsLayout& ) = delete; diff --git a/framework/code/material/vertexFormat.hpp b/framework/code/material/vertexFormat.hpp index 57276a9..b0a09a5 100644 --- a/framework/code/material/vertexFormat.hpp +++ b/framework/code/material/vertexFormat.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -28,16 +28,30 @@ class VertexFormat public: enum class t { Int32, + UInt32, Float, Boolean, Vec2, Vec3, Vec4, + IVec2, + IVec3, + IVec4, + UVec2, + UVec3, + UVec4, Int16, + UInt16, Float16, F16Vec2, F16Vec3, F16Vec4, + I16Vec2, + I16Vec3, + I16Vec4, + U16Vec2, + U16Vec3, + U16Vec4, Null }; constexpr ElementType( const ElementType& ) noexcept = default; @@ -49,79 +63,123 @@ class VertexFormat constexpr uint32_t elements() const noexcept { switch (type) { case t::Int32: + case t::UInt32: case t::Float: case t::Boolean: case t::Int16: + case t::UInt16: case t::Float16: return 1; case t::Vec2: case t::F16Vec2: + case t::I16Vec2: + case t::U16Vec2: + case t::IVec2: + case t::UVec2: return 2; case t::Vec3: case t::F16Vec3: + case t::I16Vec3: + case t::U16Vec3: + case t::IVec3: + case t::UVec3: return 3; case t::Vec4: case t::F16Vec4: + case t::I16Vec4: + case t::U16Vec4: + case t::IVec4: + case t::UVec4: return 4; default: + // Assert if runtime eval and arithmetic error if const (compiletime) evaluated + do { if (std::is_constant_evaluated()) { int error = 1 / (int(type) - int(type)); } else { assert(0); } } while (0); // constexpr friendly assert! case t::Null: return 0; } } constexpr uint32_t size() const noexcept { switch(type) { - case t::Int32: - return 4; case t::Float: + case t::Int32: + case t::UInt32: return 4; case t::Boolean: return 4; case t::Vec2: + case t::IVec2: + case t::UVec2: return 8; case t::Vec3: + case t::IVec3: + case t::UVec3: return 12; case t::Vec4: + case t::IVec4: + case t::UVec4: return 16; - case t::Int16: - return 2; case t::Float16: + case t::Int16: + case t::UInt16: return 2; case t::F16Vec2: + case t::I16Vec2: + case t::U16Vec2: return 4; case t::F16Vec3: + case t::I16Vec3: + case t::U16Vec3: return 6; case t::F16Vec4: + case t::I16Vec4: + case t::U16Vec4: return 8; default: + // Assert if runtime eval and arithmetic error if const (compiletime) evaluated + do { if (std::is_constant_evaluated()) { int error = 1 / (int(type) - int(type)); } else { assert(0); } } while (0); // constexpr friendly assert! case t::Null: return 0; } } constexpr uint32_t alignment() const noexcept { switch(type) { - case t::Int32: - return 4; case t::Float: + case t::Int32: + case t::UInt32: return 4; case t::Boolean: return 4; case t::Vec2: + case t::IVec2: + case t::UVec2: return 8; case t::Vec3: + case t::IVec3: + case t::UVec3: return 16; case t::Vec4: + case t::IVec4: + case t::UVec4: return 16; - case t::Int16: - return 2; case t::Float16: + case t::Int16: + case t::UInt16: return 2; case t::F16Vec2: + case t::I16Vec2: + case t::U16Vec2: return 4; case t::F16Vec3: + case t::I16Vec3: + case t::U16Vec3: return 8; case t::F16Vec4: + case t::I16Vec4: + case t::U16Vec4: return 8; default: + // Assert if runtime eval and arithmetic error if const (compiletime) evaluated + do { if (std::is_constant_evaluated()) { int error = 1 / (int(type) - int(type)); } else { assert(0); } } while (0); // constexpr friendly assert! case t::Null: return 0; } diff --git a/framework/code/material/vulkan/computable.cpp b/framework/code/material/vulkan/computable.cpp new file mode 100644 index 0000000..0eb0c10 --- /dev/null +++ b/framework/code/material/vulkan/computable.cpp @@ -0,0 +1,502 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "computable.hpp" +#include "material.hpp" +#include "pipeline.hpp" +#include "shader.hpp" +#include "shaderModule.hpp" +#include "../shaderDescription.hpp" +#include "vulkan/vulkan.hpp" +#include "vulkan/TextureFuncts.h" +#include "texture/vulkan/texture.hpp" +#include "vulkan/commandBuffer.hpp" +#include "vulkan/extensionLib.hpp" +#include "system/os_common.h" +#include +#include +#include + +//template<> +ComputablePass::ComputablePass(ComputablePass&& other) noexcept + : ComputablePassBase(std::move(other)) + , mPipeline(std::move(other.mPipeline)) + , mImageMemoryBarriers(std::move(other.mImageMemoryBarriers)) + , mBufferMemoryBarriers(std::move(other.mBufferMemoryBarriers)) + , mNeedsExecutionBarrier( other.mNeedsExecutionBarrier ) +{ + mPipelineLayout = other.mPipelineLayout; + other.mPipelineLayout = VK_NULL_HANDLE; +} + +ComputablePass::~ComputablePass() +{ +} + +void ComputablePass::SetDispatchThreadCount(const std::array threadCount) +{ + std::array groupCount; + const auto& cWorkGroupLocalSize = mMaterialPass.mShaderPass.m_shaderPassDescription.m_workGroupSettings.localSize; + if (cWorkGroupLocalSize[0] == 0 || cWorkGroupLocalSize[1] == 0 || cWorkGroupLocalSize[2] == 0) + { + // Workgroup local size must be defined in the shader definition if we want to use this function + assert(0); + return; + } + groupCount[0] = (threadCount[0] + cWorkGroupLocalSize[0] - 1) / cWorkGroupLocalSize[0]; + groupCount[1] = (threadCount[1] + cWorkGroupLocalSize[1] - 1) / cWorkGroupLocalSize[1]; + groupCount[2] = (threadCount[2] + cWorkGroupLocalSize[2] - 1) / cWorkGroupLocalSize[2]; + + SetDispatchGroupCount(groupCount); +} + + +enum class BindingAccess { + ReadOnly, + WriteOnly, + ReadWrite +}; +static VkAccessFlags BindingAccessToBufferAccessMask(BindingAccess access) +{ + switch (access) { + case BindingAccess::ReadOnly: + return VK_ACCESS_SHADER_READ_BIT; + case BindingAccess::WriteOnly: + return VK_ACCESS_SHADER_WRITE_BIT; + case BindingAccess::ReadWrite: + default: + return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } +} + +/// @brief Structure holding the 'use' information for one binding +/// @tparam VK_TYPE Vulkan buffer type for this list of bindings (eg VkImage, VkBuffer) +template +struct BindingUseData +{ + typedef VK_BUFFERTYPE buffer_type; + BindingUseData(uint32_t _passIdx, BindingAccess _access, VK_BUFFERTYPE _buffer) : passIdx(_passIdx), access(_access), buffer(_buffer) {} + uint32_t passIdx; + BindingAccess access; + VK_BUFFERTYPE buffer; +}; + +template<> +bool Computable::Init() +{ + LOGI("Creating Computable"); + + const auto& materialPasses = mMaterial.GetMaterialPasses(); + mPasses.reserve(materialPasses.size() ); + + // + // Previous pass buffer usage data (for all passes before the current one). + // + struct ImageUsage { + VkImage image; + uint32_t numMips; + uint32_t firstMip; + VkImageLayout imageLayout; + ImageUsage( VkImage _image, uint32_t _numMips, uint32_t _firstMip, VkImageLayout _imageLayout ) noexcept : image( _image ), numMips( _numMips ), firstMip( _firstMip ), imageLayout(_imageLayout) {}; + ImageUsage( const ImageInfo& other ) noexcept : image( other.image ), numMips( other.imageViewNumMips ), firstMip( other.imageViewFirstMip ), imageLayout( other.imageLayout) {} + bool operator==( const ImageUsage& other ) const noexcept { + return image == other.image && ((firstMip < other.firstMip + other.numMips) && (firstMip + numMips > other.firstMip)); + } + }; + std::vector> prevImageUsages; + std::vector> prevBufferUsages; + prevImageUsages.reserve(64); + prevBufferUsages.reserve(64); + + // Lambda helper! Calls emitFn when the buffer passed to 'currentUsage' was last accessed in a differnet way by a previous pass (ie it is in prevPassUsages as a write and was not read from in a pass between current use and the previous write, also applies for read in a previous pass and now a write). + // Returns true if we need an execution barrier emitting. + // We want to buffer/image barrier on Write then Read patterns, and on Read then write patterns (we need to do the layout transition). + const auto& emitBarrier = [](const auto& currentUsage, const auto& prevPassUsages, const auto& emitFn) -> bool { + + // scan backwards looking for prior uses of this buffer/image... + int priorUseIdx = (int)prevPassUsages.size(); + while (priorUseIdx-- > 0) + { + const auto& priorUsage = prevPassUsages[priorUseIdx]; + if (currentUsage.buffer == priorUsage.buffer) + { + // if we found a prior use determine what kind of barrier we need to insert. + const auto& priorUsage = prevPassUsages[priorUseIdx]; + if (priorUsage.access == BindingAccess::ReadOnly) + { + if (currentUsage.access == BindingAccess::ReadOnly) + { + // Read followed by Read. + // do nothing... . Doesnt even need an execution barrier. + // HOWEVER DONT EARLY OUT BECAUSE WE MAY ALSO BIND AS A WRITE (albeit on a different frame - we need to sort that out, happens if we read the 'previous frame' as a texture and write the 'current frame' as an image in the same shader pass) + } + else + { + // Read followed by Write or ReadWrite. + // We used to only emit an execution barrier but we now emit a barrier to do the layout transition + emitFn(priorUsage, currentUsage); + return true; + } + } + else // priorUsage.access != BindingAccess::ReadOnly + { + // Write followed by something (read, write, readwrite). + // emit barrier. + emitFn(priorUsage, currentUsage); + return true; + } + } + } + return false; + }; + + + for (uint32_t materialPassIdx = 0; materialPassIdx < (uint32_t) materialPasses.size(); ++materialPassIdx) + { + const auto& materialPass = materialPasses[materialPassIdx]; + const auto& shaderPass = materialPass.GetShaderPass(); + assert(std::holds_alternative>(shaderPass.m_shaders.m_modules)); + + // Usually pipeline layout will be stored with the shader but if the descriptor set layout is 'dynamic' (and stored in the materialPass) the pipeline layout will also be in the materialPass. + VkPipelineLayout pipelineLayout = shaderPass.GetPipelineLayout().GetVkPipelineLayout(); + if (pipelineLayout == VK_NULL_HANDLE) + pipelineLayout = materialPass.GetPipelineLayout().GetVkPipelineLayout(); + + VkPipeline vkPipeline; + const ShaderModule& shaderModule = shaderPass.m_shaders.Get>(); + LOGI("CreateComputePipeline: %s", shaderPass.m_shaderPassDescription.m_computeName.c_str()); + + if (!mGfxApi.CreateComputePipeline(VK_NULL_HANDLE, + pipelineLayout, + shaderModule.GetVkShaderModule(), + materialPass.GetSpecializationConstants().GetVkSpecializationInfo(), + &vkPipeline)) + { + // Error + return false; + } + Pipeline pipeline {mGfxApi.m_VulkanDevice, vkPipeline}; + + // + // Build any memory barriers between passes + // + bool passNeedsExecutionBarrier = false; // Set if the pass needs (at least) an execution barrier. Adds barriers for WAR (write after read) use cases. + std::vector imageMemoryBarriers; + std::vector bufferMemoryBarriers; + + // Barriers for Image bindings + for (const auto& passImageBindings : materialPass.GetImageBindings()) + { + for (const auto& passImageBinding : passImageBindings.first) // image binding can be an array of bindings + { + const auto passUsage = BindingUseData( materialPassIdx, passImageBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, ImageUsage(passImageBinding) ); + + passNeedsExecutionBarrier |= emitBarrier(passUsage, prevImageUsages, [&imageMemoryBarriers](auto& prevUsage, auto& currentUsage) { + const auto& image = currentUsage.buffer; + imageMemoryBarriers.push_back(VkImageMemoryBarrier{ + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + prevUsage.buffer.imageLayout,//oldLayout; + currentUsage.buffer.imageLayout,//newLayout; + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + image.image, //image; + { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; + image.firstMip, //baseMipLevel; + image.numMips, //mipLevelCount; + 0, //baseLayer; + 1, //layerCount; + }//subresourceRange; + }); + }); + } + } + + // Barriers for texture bindings + for (const auto& passTextureBindings : materialPass.GetTextureBindings()) + { + for (const auto& passTextureBinding : passTextureBindings.first) // texture binding can be an array of bindings + { + const auto passUsage = BindingUseData(materialPassIdx, BindingAccess::ReadOnly/*always readonly*/, ImageUsage(*passTextureBinding)); + + passNeedsExecutionBarrier |= emitBarrier(passUsage, prevImageUsages, + [&imageMemoryBarriers](auto& prevUsage, auto& currentUsage) { + const auto& image = currentUsage.buffer; + imageMemoryBarriers.push_back(VkImageMemoryBarrier{ + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + prevUsage.buffer.imageLayout, //oldLayout; + currentUsage.buffer.imageLayout, //newLayout; + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + image.image, //image; + { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; + image.firstMip, //baseMipLevel; + image.numMips, //mipLevelCount; + 0, //baseLayer; + 1, //layerCount; + }//subresourceRange; + }); + }); + } + } + + // Barriers for buffer bindings + for (const auto& passBufferBindings : materialPass.GetBufferBindings()) + { + for (const auto& passBufferBinding : passBufferBindings.first) // buffer binding can be an array of bindings + { + const auto passUsage = BindingUseData(materialPassIdx, passBufferBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, passBufferBinding.buffer()); + + passNeedsExecutionBarrier |= emitBarrier(passUsage, prevBufferUsages, + [&](auto& prevUsage, auto& currentUsage) { + VkBuffer bufferUsage = currentUsage.buffer; + //LOGI("Pass %d: Buffer Barrier: %s", materialPassIdx, materialPass.mShaderPass.m_shaderPassDescription.m_sets[0].m_descriptorTypes[tmp].names[0].c_str()); + bufferMemoryBarriers.push_back(VkBufferMemoryBarrier{ + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + nullptr, + BindingAccessToBufferAccessMask(prevUsage.access), //srcAccessMask + BindingAccessToBufferAccessMask(currentUsage.access), //dstAccessMask + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + bufferUsage, //buffer; + 0, //offset + VK_WHOLE_SIZE //size + }); + }); + } + } + + mPasses.push_back( ComputablePass{ materialPass, std::move(pipeline), pipelineLayout, std::move(imageMemoryBarriers), std::move(bufferMemoryBarriers), passNeedsExecutionBarrier } ); + + // + // Store out the arrays of buffers used by this pass + // + + for (const auto& imageBindings : materialPass.GetImageBindings()) + { + BindingAccess access = imageBindings.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite; + for (const auto& imageBinding : imageBindings.first) + { + prevImageUsages.push_back( { materialPassIdx, access, ImageUsage{imageBinding.image, imageBinding.imageViewNumMips, imageBinding.imageViewFirstMip, imageBinding.imageLayout} } ); + } + } + for (const auto& textureBinding : materialPass.GetTextureBindings()) + { + for (const auto& texture : textureBinding.first) + { + const auto& textureVulkan = apiCast(texture); + if (!textureVulkan->Image.IsEmpty()) // Only consider for barriers if the texture has a buffer/image, texture objects containing only a sampler can be ignored. + { + prevImageUsages.push_back( {materialPassIdx, BindingAccess::ReadOnly, {textureVulkan->GetVkImage(), textureVulkan->MipLevels, textureVulkan->FirstMip, textureVulkan->GetVkImageLayout()}} ); + } + } + } + for (const auto& bufferBinding : materialPass.GetBufferBindings()) + { +// LOGI("Buffer Binding: %s", materialPass.mShaderPass.m_shaderPassDescription.m_sets[0].m_descriptorTypes[bufferBinding.second.index].names[0].c_str()); + + for (const auto& buffer : bufferBinding.first) + { + prevBufferUsages.push_back({ materialPassIdx, bufferBinding.second.setBinding.isReadOnly ? BindingAccess::ReadOnly : BindingAccess::ReadWrite, buffer.buffer()}); + } + } + } + + // Create the final set of barriers. + // Any bindings that (may) write and were not used as a (read only) input to a subsequent ComputePass will be assumed as being outputs from the Computable (and will get memory barriers built) + + // Helper to scan through passUsages and call emitFn on any bindings that have a write as their last access type (ie things that are being output from the Computable and haven't already been barriered). + // Mangles/clears passUsages. + const auto& emitOutputBarrier = [](auto&& passUsages, const auto& emitFn) -> void + { + while( !passUsages.empty() ) + { + std::optional::value_type::buffer_type> currentBuffer {}; + for (auto revIt = passUsages.rbegin(); revIt != passUsages.rend(); ) + { + if (!currentBuffer) + { + if (revIt->access == BindingAccess::ReadOnly) + { + // last use of this binding was a readonly - no need for any output barriers + currentBuffer = revIt->buffer; + } + else + { + // last use of this binding is write or read-write + currentBuffer = revIt->buffer; + // pushback + emitFn(revIt->buffer); + } + } + if (currentBuffer == revIt->buffer) + { + // Once we have found a candidate buffer remove all prior references to it (and itself). + revIt = std::reverse_iterator(passUsages.erase((++revIt).base())); + } + else + { + ++revIt; + } + } + } + }; + + emitOutputBarrier(prevBufferUsages, + [this](const VkBuffer& buffer) -> void { + mBufferOutputMemoryBarriers.emplace_back(VkBufferMemoryBarrier{ + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + buffer, //buffer; + 0, //offset + VK_WHOLE_SIZE //size + }); + }); + + emitOutputBarrier(prevImageUsages, + [this](const ImageUsage& image) -> void { + mImageOutputMemoryBarriers.emplace_back(VkImageMemoryBarrier{ + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + nullptr, + VK_ACCESS_SHADER_WRITE_BIT, //srcAccessMask + VK_ACCESS_SHADER_READ_BIT, //dstAccessMask + VK_IMAGE_LAYOUT_GENERAL, //oldLayout; + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,//newLayout; + VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; + VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; + image.image, //image; + { VK_IMAGE_ASPECT_COLOR_BIT,//aspect; + image.firstMip, //baseMipLevel; + image.numMips, //mipLevelCount; + 0, //baseLayer; + 1, //layerCount; + }//subresourceRange; + }); + }); + + for (uint32_t whichBuffer = 0; whichBuffer < mMaterial.GetNumFrameBuffers(); ++whichBuffer) + { + mMaterial.UpdateDescriptorSets(whichBuffer); + } + return true; +} + +template<> +void Computable::SetDispatchGroupCount(uint32_t passIdx, const std::array& groupCount) +{ + mPasses[passIdx].SetDispatchGroupCount(groupCount); +} + +template<> +void Computable::SetDispatchThreadCount(uint32_t passIdx, const std::array& threadCount) +{ + mPasses[passIdx].SetDispatchThreadCount(threadCount); +} + +template<> +void Computable::DispatchPass(CommandList& cmdList, const ComputablePass& computablePass, uint32_t bufferIdx) const +{ + VkCommandBuffer vkCmdBuffer = cmdList; + + // Add image barriers (if needed) + if (computablePass.NeedsBarrier()) + { + const auto& imageMemoryBarriers = computablePass.GetVkImageMemoryBarriers(); + const auto& bufferMemoryBarriers = computablePass.GetVkBufferMemoryBarriers(); + + // Barrier on memory, with correct layouts set. + vkCmdPipelineBarrier(vkCmdBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, + 0, + 0, nullptr, + (uint32_t)bufferMemoryBarriers.size(), + bufferMemoryBarriers.empty() ? nullptr : bufferMemoryBarriers.data(), + (uint32_t)imageMemoryBarriers.size(), + imageMemoryBarriers.empty() ? nullptr : imageMemoryBarriers.data()); + } + + // Bind the pipeline for this material + vkCmdBindPipeline(vkCmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computablePass.mPipeline.GetVkPipeline()); + + // Bind everything the shader needs + const std::span descriptorSets = computablePass.GetMaterialPass().GetVkDescriptorSets(bufferIdx); + vkCmdBindDescriptorSets(vkCmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computablePass.mPipelineLayout, 0, descriptorSets.size(), descriptorSets.data(), 0, nullptr); + + // Dispatch the compute task + const auto& workgroupSettings = computablePass.GetMaterialPass().mShaderPass.m_shaderPassDescription.m_workGroupSettings; + if (workgroupSettings.perTileDispatch) + { + mGfxApi.GetExtension()->m_vkCmdDispatchTileQCOM(vkCmdBuffer, nullptr); + } + else + { + vkCmdDispatch(vkCmdBuffer, computablePass.GetDispatchGroupCount()[0], computablePass.GetDispatchGroupCount()[1], computablePass.GetDispatchGroupCount()[2] ); + } +} + +template<> +void Computable::Dispatch(CommandList& cmdList, uint32_t bufferIdx, bool timers) const +{ + assert(!GetPasses().empty() && "No Computable passes, did you call Init() for the computable?"); + + // Determine if we want per-tile shader mode enabling (can either dispatch per tile or just run a compute shader with user defined workgroup sizes) + + for (uint32_t passIdx =0; const auto& computablePass : GetPasses()) + { + const int timerIdx = timers ? cmdList.StartGpuTimer( GetPassName( passIdx ) ) : -1; + DispatchPass(cmdList, computablePass, bufferIdx % (uint32_t)computablePass.GetMaterialPass().GetVkDescriptorSets().size()); + if (timerIdx>=0) + cmdList.StopGpuTimer( timerIdx ); + ++passIdx; + } +} + +template<> +void Computable::Dispatch(CommandListBase& cmdList, uint32_t bufferIdx, bool timers) const /*override*/ +{ + Dispatch(apiCast(cmdList), bufferIdx, timers); +} + +template<> +void Computable::AddOutputBarriersToCmdList(CommandList& commandList) const +{ + const auto& computableOutputBufferBarriers = GetBufferOutputMemoryBarriers(); + const auto& computableOutputImageBarriers = GetImageOutputMemoryBarriers(); + + if (computableOutputBufferBarriers.empty() && computableOutputImageBarriers.empty()) + return; + + static_assert(std::is_same_v> ); + static_assert(std::is_same_v> ); + + vkCmdPipelineBarrier(commandList, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcMask, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstMask, + 0, + 0, nullptr, + (uint32_t)computableOutputBufferBarriers.size(), + computableOutputBufferBarriers.empty() ? nullptr : (const VkBufferMemoryBarrier*) computableOutputBufferBarriers.data(), + (uint32_t)computableOutputImageBarriers.size(), + computableOutputImageBarriers.empty() ? nullptr : (const VkImageMemoryBarrier*) computableOutputImageBarriers.data()); +} + +template<> +void Computable::AddOutputBarriersToCmdList(CommandListBase& cmdList) const /*override*/ +{ + AddOutputBarriersToCmdList(apiCast(cmdList)); +} diff --git a/framework/code/material/vulkan/computable.hpp b/framework/code/material/vulkan/computable.hpp new file mode 100644 index 0000000..9db1726 --- /dev/null +++ b/framework/code/material/vulkan/computable.hpp @@ -0,0 +1,79 @@ +//============================================================================= +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "material.hpp" +#include "pipeline.hpp" +#include "../computable.hpp" + + +template<> +class ImageMemoryBarrier final : public ImageMemoryBarrierBase +{ +public: + typedef VkImageMemoryBarrier tBarrier; + ImageMemoryBarrier( VkImageMemoryBarrier&& barrier ) noexcept + : ImageMemoryBarrierBase() + , vkBarrier( std::move( barrier ) ) + {} + VkImageMemoryBarrier vkBarrier; +}; +static_assert(sizeof( ImageMemoryBarrier ) == sizeof( VkImageMemoryBarrier )); + +template<> +class BufferMemoryBarrier final : public BufferMemoryBarrierBase +{ +public: + typedef VkBufferMemoryBarrier tBarrier; + BufferMemoryBarrier( VkBufferMemoryBarrier&& barrier ) noexcept + : BufferMemoryBarrierBase() + , vkBarrier( std::move( barrier ) ) + {} + tBarrier vkBarrier; +}; +static_assert(sizeof( BufferMemoryBarrier ) == sizeof( VkBufferMemoryBarrier )); + + +template<> +class ComputablePass final : public ComputablePassBase +{ + ComputablePass( const ComputablePass& ) = delete; + ComputablePass& operator=( const ComputablePass& ) = delete; +public: + ComputablePass( const MaterialPass& materialPass, Pipeline pipeline, VkPipelineLayout pipelineLayout, std::vector imageMemoryBarriers, std::vector bufferMemoryBarriers, bool needsExecutionBarrier ) + : ComputablePassBase( materialPass ) + , mPipeline( std::move( pipeline ) ) + , mPipelineLayout( pipelineLayout ) + , mImageMemoryBarriers( std::move( imageMemoryBarriers ) ) + , mBufferMemoryBarriers( std::move( bufferMemoryBarriers ) ) + , mNeedsExecutionBarrier( needsExecutionBarrier ) + {} + ComputablePass( ComputablePass&& ) noexcept; + ~ComputablePass(); + + const auto& GetMaterialPass() const { return apiCast( mMaterialPass ); } + + const auto& GetVkImageMemoryBarriers() const { return mImageMemoryBarriers; } + const auto& GetVkBufferMemoryBarriers() const { return mBufferMemoryBarriers; } + const bool NeedsBarrier() const { return mNeedsExecutionBarrier || (!mImageMemoryBarriers.empty()) || (!mBufferMemoryBarriers.empty()); }; ///< @return true if there needs to be a barrier before executing this compute pass + + /// number of workgroup dispatches to execute (value after the local workgroup sizes are accounted for) + void SetDispatchGroupCount( std::array count ) { mDispatchGroupCount = count; } + const auto& GetDispatchGroupCount() const { return mDispatchGroupCount; } + /// number of global workgroup threads to run (value before the local workgroup sizes are accounted for). Requires "WorkGroup" : { "LocalSize": {x,y,z} } in the shader definition json. + void SetDispatchThreadCount( std::array count ); + + Pipeline mPipeline; // Owned by us + VkPipelineLayout mPipelineLayout; // Owned by ShaderPass or MaterialPass + +protected: + std::vector mImageMemoryBarriers; ///< Image barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + std::vector mBufferMemoryBarriers; ///< Buffer barriers for ENTRY in to this pass. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho + bool mNeedsExecutionBarrier = false;///< Denotes if we need an execution barrier for ENTRY to this pass (even if there are no image or buffer barriers). +}; + + diff --git a/framework/code/material/descriptorSetLayout.cpp b/framework/code/material/vulkan/descriptorSetLayout.cpp similarity index 79% rename from framework/code/material/descriptorSetLayout.cpp rename to framework/code/material/vulkan/descriptorSetLayout.cpp index c6b427a..e075e1c 100644 --- a/framework/code/material/descriptorSetLayout.cpp +++ b/framework/code/material/vulkan/descriptorSetLayout.cpp @@ -1,27 +1,28 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "descriptorSetLayout.hpp" -#include "descriptorSetDescription.hpp" +#include "../descriptorSetDescription.hpp" #include "vulkan/vulkan.hpp" #include #include -DescriptorSetLayout::DescriptorSetLayout() +DescriptorSetLayout::DescriptorSetLayout() noexcept + : DescriptorSetLayoutBase() { } -DescriptorSetLayout::~DescriptorSetLayout() +DescriptorSetLayout::~DescriptorSetLayout() { assert(m_descriptorSetLayout == VK_NULL_HANDLE); } -void DescriptorSetLayout::Destroy(Vulkan& vulkan) +void DescriptorSetLayout::Destroy(Vulkan& vulkan) { if (m_descriptorSetLayout != VK_NULL_HANDLE) { @@ -32,8 +33,8 @@ void DescriptorSetLayout::Destroy(Vulkan& vulkan) m_descriptorPoolSizes.clear(); } -DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept - : m_nameToBinding( std::move(other.m_nameToBinding)) +DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept + : DescriptorSetLayoutBase(std::move(other)) , m_descriptorSetLayoutBindings(std::move(other.m_descriptorSetLayoutBindings)) , m_descriptorPoolSizes(std::move(other.m_descriptorPoolSizes)) { @@ -41,8 +42,11 @@ DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept other.m_descriptorSetLayout = VK_NULL_HANDLE; } -bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& description) +bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& description) { + if (!DescriptorSetLayoutBase::Init(description)) + return false; + const size_t numBindings = description.m_descriptorTypes.size(); uint32_t index = 0; bool dynamicDescriptorCount = false; @@ -59,7 +63,12 @@ bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& d continue; } auto& binding = m_descriptorSetLayoutBindings.emplace_back(); - binding.binding = index++; + if (it.descriptorIndex < 0) + // Index of < 0 denotes we want to use sequential descriptor binding indices. + ///TODO: look for collisions or determine how/if we want to handle out of order desciptor indices or enforce shaders that have an explicit binding index to define indices for all descriptors. + binding.binding = index++; + else + binding.binding = it.descriptorIndex; binding.descriptorCount = it.count; if (it.count <= 0) // Count of 0 denotes that we dont (yet) know how many descriptors will go into this slot - layout will be created alongside the descriptor set. @@ -106,6 +115,9 @@ bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& d case DescriptorSetDescription::DescriptorType::Unused: // Handled before switch, never hit! break; + case DescriptorSetDescription::DescriptorType::DescriptorTable: + assert( 0 && "Descriptor tables are a DirectX12 feature, not supported on Vulkan" ); + break; } VkShaderStageFlagBits stageFlags = (VkShaderStageFlagBits)0; if (it.stages & DescriptorSetDescription::StageFlag::t::Task) @@ -131,13 +143,6 @@ bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& d binding.stageFlags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR; #endif binding.pImmutableSamplers = nullptr; - - assert(it.names.size() <= 1); ///TODO: only one name supported, needs to store the index within the descriptor as well as the binding index if we want to support this! (the 'for' loop below is not the full implementation) - for (const auto& name : it.names) - { - auto nameToBindingEmplaced = m_nameToBinding.try_emplace(name, BindingTypeAndIndex{ binding.descriptorType, binding.binding, binding.descriptorCount!=1/*isArray*/, readOnly }); - assert(nameToBindingEmplaced.second); // name must be unique - } } m_descriptorPoolSizes.clear(); @@ -157,7 +162,7 @@ bool DescriptorSetLayout::Init(Vulkan& vulkan, const DescriptorSetDescription& d } -VkDescriptorSetLayout DescriptorSetLayout::CreateVkDescriptorSetLayout(Vulkan& vulkan, const std::span descriptorSetLayoutBindings) +VkDescriptorSetLayout DescriptorSetLayout::CreateVkDescriptorSetLayout(Vulkan& vulkan, const std::span descriptorSetLayoutBindings) { // // Create the descriptor set layout @@ -169,6 +174,13 @@ VkDescriptorSetLayout DescriptorSetLayout::CreateVkDescriptorSetLayout(Vulkan& v VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; VkResult retVal = vkCreateDescriptorSetLayout(vulkan.m_VulkanDevice, &layoutInfo, nullptr, &descriptorSetLayout); + + LOGI("vkCreateDescriptorSetLayout"); + for (const auto& binding: descriptorSetLayoutBindings) + { + LOGI(" binding: %u\tdescriptorType: 0x%x descriptorCount: %d stageFlags: 0x%x pImmutableSamplers : %p", binding.binding, binding.descriptorType, binding.descriptorCount, binding.stageFlags, binding.pImmutableSamplers); + } + if (!CheckVkError("vkCreateDescriptorSetLayout()", retVal)) { return VK_NULL_HANDLE; @@ -177,7 +189,7 @@ VkDescriptorSetLayout DescriptorSetLayout::CreateVkDescriptorSetLayout(Vulkan& v } -void DescriptorSetLayout::CalculatePoolSizes(const std::span descriptorSetLayoutBindings, std::vector& descriptorPoolSizes) +void DescriptorSetLayout::CalculatePoolSizes(const std::span descriptorSetLayoutBindings, std::vector& descriptorPoolSizes) { #define MAX_USED_DESCRIPTOR_TYPES ((VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1) + 2) // Take a guess at how many unique descript types will be used diff --git a/framework/code/material/vulkan/descriptorSetLayout.hpp b/framework/code/material/vulkan/descriptorSetLayout.hpp new file mode 100644 index 0000000..c7c8238 --- /dev/null +++ b/framework/code/material/vulkan/descriptorSetLayout.hpp @@ -0,0 +1,51 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +//#include //TEMP +#include +#include "../descriptorSetLayout.hpp" + +// Forward declares +class DescriptorSetDescription; +class Vulkan; + + +/// Representation of the descriptor set layout. +/// template specialization of DescriptSetLayout +/// Builds/owns the Vulkan Descriptor Set Layout that can be used to allocate the descriptor sets. +/// Also has a mapping from descriptor slots name (nice name) to the their shader binding index. +/// @ingroup Material +template<> +class DescriptorSetLayout : public DescriptorSetLayoutBase +{ + DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; + DescriptorSetLayout(const DescriptorSetLayout&) = delete; +public: + DescriptorSetLayout() noexcept; + ~DescriptorSetLayout(); + DescriptorSetLayout(DescriptorSetLayout&&) noexcept; + + bool Init(Vulkan& vulkan, const DescriptorSetDescription&); + void Destroy(Vulkan& vulkan); + + static VkDescriptorSetLayout CreateVkDescriptorSetLayout(Vulkan& vulkan, const std::span descriptorSetLayoutBindings); + static void CalculatePoolSizes(const std::span descriptorSetLayoutBindings, std::vector& descriptorPoolSizes/*output*/); + + const auto& GetVkDescriptorSetLayoutBinding() const { return m_descriptorSetLayoutBindings; } + const auto& GetVkDescriptorSetLayout() const { return m_descriptorSetLayout; } + const auto& GetDescriptorPoolSizes() const { return m_descriptorPoolSizes; } + +private: + // Vulkan objects + VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; ///< Vulkan descriptor set layout object. Can be VK_NULL_HANDLE after Init if there are bindings with 'dynamic' descriptorCount (0) + std::vector m_descriptorSetLayoutBindings; + std::vector m_descriptorPoolSizes; +}; diff --git a/framework/code/material/vulkan/drawable.cpp b/framework/code/material/vulkan/drawable.cpp new file mode 100644 index 0000000..0417360 --- /dev/null +++ b/framework/code/material/vulkan/drawable.cpp @@ -0,0 +1,389 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "drawable.hpp" +#include "vulkan/vulkan.hpp" +#include "material.hpp" +#include "shader.hpp" +#include "../shaderDescription.hpp" +#include "vulkan/commandBuffer.hpp" +#include "vulkan/extensionLib.hpp" + +namespace +{ + inline VkSampleCountFlagBits EnumToVk(Msaa msaa) + { + return (VkSampleCountFlagBits)msaa; + } +} + +DrawablePass::~DrawablePass() +{ +} + +template<> +Drawable::~Drawable() +{ +} + +template<> +bool Drawable::ReInit( std::span renderContexts, uint32_t passMask ) +{ + mPassMask = passMask; + mPassNameToIndex.clear(); + mPasses.clear(); + + const auto& shader = mMaterial.GetShader(); + mPasses.reserve(shader.GetShaderPasses().size()); + + // we allow either no pass names or a single empty passName to denote that we want to just process the passes in order, otherwise lookup by name +// const bool lookupPassNames = !passNames.empty() && (passNames.size() > 1 || (passNames[0] != nullptr && passNames[0][0] != '\0')); + + for (uint32_t passIdx = 0; passIdx < sizeof(passMask) * 8 && passMask != 0 && passIdx < renderContexts.size(); ++passIdx) + { + bool passMaskSet = ((passMask & 1) != 0); + passMask >>= 1; + if (passMaskSet) + { + const auto& renderContext = renderContexts[passIdx]; + // LOGI("Creating Mesh Object PipelineState and Pipeline for pass... %s", renderContext.name.c_str()); + auto* pMaterialPass = renderContext.name.empty() ? mMaterial.GetMaterialPass(passIdx) : mMaterial.GetMaterialPass(renderContext.name); + if (!pMaterialPass) + { + LOGE(" Pass %s does not exist in shader", renderContext.name.c_str()); + continue; + } + assert(pMaterialPass); + const auto& shaderPass = pMaterialPass->GetShaderPass(); + + mPassNameToIndex.try_emplace(renderContext.name, (uint32_t)mPasses.size()); // add the lookup (in to mPasses) + + // Build the passVertexBufferLookup so at runtime we can easily populate the vkBuffer array with the vertex and instance buffers in the order specified per pass by m_vertexFormatBindings. + // Could do this in a single loop; currently split into 2 so we can potentially add more flexibility in where we get the VKBuffers from (TODO) + std::vector tmp; + tmp.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + int numVertexRateFormats = 0, numInstanceRateFormats = 0; + for (const auto& vertexFormat : shader.m_shaderDescription->m_vertexFormats) + { + switch (vertexFormat.inputRate) { + case VertexFormat::eInputRate::Vertex: + tmp.push_back(numVertexRateFormats++); + break; + case VertexFormat::eInputRate::Instance: + tmp.push_back(--numInstanceRateFormats); + break; + } + } + + std::vector passVertexBufferLookup; // order of the vkBuffers for this pass (index is in to the vertex array if positive, or in to the instance array if negative (-1 is the 'first') + std::vector passVertexBuffers; + passVertexBufferLookup.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + passVertexBuffers.reserve(shader.m_shaderDescription->m_vertexFormats.size()); + for (uint32_t formatBindingIdx : shaderPass.m_shaderPassDescription.m_vertexFormatBindings) + { + const int bufferIdx = tmp[formatBindingIdx]; + passVertexBufferLookup.push_back(bufferIdx); + if (bufferIdx >= 0) + { + // Vertex rate data (ie the mesh) + passVertexBuffers.push_back(mMeshObject.m_VertexBuffers[bufferIdx].GetVkBuffer()); + + // Double check the mesh is supplying the data we expect in the shader, may mismatch if the mesh was not built using the vertex format (eg Mesh::CreateScreenSpaceMesh) + // We could dive even deeper, for now check the span and the number of attributes match + assert(mMeshObject.m_VertexBuffers[bufferIdx].GetAttributes().size() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].elements.size()); + assert(mMeshObject.m_VertexBuffers[bufferIdx].GetSpan() == shader.m_shaderDescription->m_vertexFormats[formatBindingIdx].span); + } + else + { + // Instance rate data (ie the instance buffer) + assert(bufferIdx == -1); + assert(mVertexInstanceBuffer.has_value()); + passVertexBuffers.push_back(mVertexInstanceBuffer->GetVkBuffer()); + } + } + std::vector passVertexBufferOffsets(passVertexBuffers.size(), 0); + + // Index buffer is optional + VkBuffer indexBuffer = VK_NULL_HANDLE; + VkIndexType indexBufferType = VK_INDEX_TYPE_MAX_ENUM; + size_t indexCount = 0; + if (mMeshObject.m_IndexBuffer) + { + indexBuffer = mMeshObject.m_IndexBuffer->GetVkBuffer(); + indexBufferType = mMeshObject.m_IndexBuffer->GetVkIndexType(); + indexCount = mMeshObject.m_IndexBuffer->GetNumIndices(); + } + + // Indirect Draw buffer is optional + VkBuffer drawIndirectBuffer = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetVkBuffer() : VK_NULL_HANDLE; + uint32_t drawIndirectCount = mDrawIndirectBuffer.has_value() ? (uint32_t)mDrawIndirectBuffer->GetNumDraws() : 0; + uint32_t drawIndirectOffset = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetBufferOffset() : 0; + // Indirect Draw Count (count buffer) set to be the beginning of the drawIndirectBuffer IF there is an offset in the mDrawIndirectBuffer. + VkBuffer drawIndirectCountBuffer = drawIndirectOffset>0 ? drawIndirectBuffer : VK_NULL_HANDLE; + + Pipeline pipeline; + + // Pipeline (and layout) may come from the shaderPass or (if that fails) from the materialPass (if it was created late because of 'dynamic' descriptor set layout). + bool materialSpecificPipeline = !shaderPass.GetPipelineLayout(); + const PipelineLayout& pipelineLayout = materialSpecificPipeline ? pMaterialPass->GetPipelineLayout() : shaderPass.GetPipelineLayout(); + if (!materialSpecificPipeline) + { + // We (probably) have a valid pipeline we can (re)use + pipeline = !renderContext.IsDynamic() ? renderContext.GetOverridePipeline() : Pipeline(); + } + if (!pipeline) + { + PipelineRasterizationState pipelineRasterizationState { shaderPass.m_shaderPassDescription }; + pipeline = CreatePipeline( mGfxApi, shaderPass.m_shaderPassDescription, pipelineLayout, shaderPass.GetPipelineVertexInputState(), pipelineRasterizationState, pMaterialPass->GetSpecializationConstants(), shaderPass.m_shaders, renderContext, renderContext.msaa); + } + + // add the DrawablePass + DrawablePass& pass = mPasses.emplace_back( *pMaterialPass, + std::move(pipeline), + pipelineLayout.GetVkPipelineLayout(), + pMaterialPass->GetVkDescriptorSets(), + shaderPass.GetPipelineVertexInputState(), + DrawablePassVertexBuffers { .mVertexBuffers = std::move( passVertexBuffers ), + .mVertexBufferOffsets = std::move( passVertexBufferOffsets ) }, + indexBuffer, + indexBufferType, + drawIndirectBuffer, + drawIndirectCountBuffer, + (uint32_t)mMeshObject.m_NumVertices, + (uint32_t)indexCount, + (uint32_t)drawIndirectCount, + (uint32_t)drawIndirectOffset, + passIdx + ); + } + } + return true; +} + +template<> +bool Drawable::ReInitMeshShader(std::span renderContexts, uint32_t passMask) +{ + mPassMask = passMask; + mPassNameToIndex.clear(); + mPasses.clear(); + + const auto& shader = mMaterial.GetShader(); + mPasses.reserve( shader.GetShaderPasses().size() ); + for (uint32_t passIdx = 0; passIdx < sizeof( passMask ) * 8 && passMask != 0; ++passIdx) + { + bool passMaskSet = ((passMask & 1) != 0); + passMask >>= 1; + if (passMaskSet) + { + const auto& renderContext = renderContexts[passIdx]; + // LOGI("Creating Mesh Object PipelineState and Pipeline for pass... %s", passNames[passIdx]); + auto* pMaterialPass = renderContext.name.empty() ? mMaterial.GetMaterialPass(passIdx) : mMaterial.GetMaterialPass(renderContext.name); + if (!pMaterialPass) + { + LOGE(" Pass %s does not exist in shader", renderContext.name.c_str()); + continue; + } + assert(pMaterialPass); + assert( pMaterialPass ); + const auto& shaderPass = pMaterialPass->GetShaderPass(); + + mPassNameToIndex.try_emplace( renderContext.name, (uint32_t)mPasses.size() ); // add the lookup (in to mPasses) + + VkBuffer indexBuffer = VK_NULL_HANDLE; + VkIndexType indexBufferType = VK_INDEX_TYPE_MAX_ENUM; + + // Indirect Draw buffer is optional + VkBuffer drawIndirectBuffer = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetVkBuffer() : VK_NULL_HANDLE; + uint32_t drawIndirectCount = mDrawIndirectBuffer.has_value() ? (uint32_t)mDrawIndirectBuffer->GetNumDraws() : 0; + uint32_t drawIndirectOffset = mDrawIndirectBuffer.has_value() ? mDrawIndirectBuffer->GetBufferOffset() : 0; + // Indirect Draw Count (count buffer) set to be the beginning of the drawIndirectBuffer IF there is an offset in the mDrawIndirectBuffer. + VkBuffer drawIndirectCountBuffer = drawIndirectOffset > 0 ? drawIndirectBuffer : VK_NULL_HANDLE; + + Pipeline pipeline; + + // Pipeline (and layout) may come from the shaderPass or (if that fails) from the materialPass (if it was created late because of 'dynamic' descriptor set layout). + bool materialSpecificPipeline = !shaderPass.GetPipelineLayout(); + const PipelineLayout& pipelineLayout = materialSpecificPipeline ? pMaterialPass->GetPipelineLayout() : shaderPass.GetPipelineLayout(); + if (!renderContext.IsDynamic()) + { + if (!materialSpecificPipeline && !renderContext.IsDynamic()) + { + // We (probably) have a valid pipeline we can (re)use + pipeline = renderContext.GetOverridePipeline(); + } + if (!pipeline) + { + PipelineRasterizationState pipelineRasterizationState{shaderPass.m_shaderPassDescription}; + pipeline = CreatePipeline( mGfxApi, shaderPass.m_shaderPassDescription, pipelineLayout, shaderPass.GetPipelineVertexInputState(), pipelineRasterizationState, pMaterialPass->GetSpecializationConstants(), shaderPass.m_shaders, renderContext, renderContext.msaa); + } + } + + // add the DrawablePass + DrawablePass& pass = mPasses.emplace_back( *pMaterialPass, + std::move(pipeline), + pipelineLayout.GetVkPipelineLayout(), + pMaterialPass->GetVkDescriptorSets(), + shaderPass.GetPipelineVertexInputState(), + DrawablePassVertexBuffers { }, //{ .mVertexBuffers = std::move( passVertexBuffers ), .mVertexBufferOffsets = std::move( passVertexBufferOffsets ) }, + VK_NULL_HANDLE, //indexBuffer, + indexBufferType, + drawIndirectBuffer, + drawIndirectCountBuffer, + 0, //(uint32_t)mMeshObject.m_NumVertices, + 0, //(uint32_t)indexCount, + (uint32_t)drawIndirectCount, + (uint32_t)drawIndirectOffset, + passIdx + ); + } + } + return true; +} + +template<> +void Drawable::DrawPass(CommandList& cmdBuffer, const DrawablePass& drawablePass, uint32_t bufferIdx, const std::span vertexBufferOverrides) const +{ + VkCommandBuffer vkCmdBuffer = cmdBuffer; + + // Bind the pipeline for this material + vkCmdBindPipeline(vkCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, drawablePass.mPipeline.GetVkPipeline()); + + // Bind everything the shader needs + if (!drawablePass.mDescriptorSet.empty()) + { + VkDescriptorSet vkDescriptorSet = drawablePass.mDescriptorSet.size() >= 1 ? drawablePass.mDescriptorSet[bufferIdx] : drawablePass.mDescriptorSet[0]; + vkCmdBindDescriptorSets(vkCmdBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + drawablePass.mPipelineLayout, + 0, + 1, + &vkDescriptorSet, + 0, + NULL); + } + + const auto& shaderPassDescription = drawablePass.mMaterialPass.mShaderPass.m_shaderPassDescription; + if (shaderPassDescription.m_meshName.empty()) + { + // + // Traditional vert (and maybe frag) shader rasterization pipeline + // + const auto& vertexBuffers = vertexBufferOverrides.empty() ? drawablePass.mVertexBuffers : vertexBufferOverrides[bufferIdx % vertexBufferOverrides.size()]; + + if (!vertexBuffers.mVertexBuffers.empty()) + { + // Bind mesh vertex/instance buffer(s) + vkCmdBindVertexBuffers( vkCmdBuffer, + 0, + (uint32_t)vertexBuffers.mVertexBuffers.size(), + vertexBuffers.mVertexBuffers.data(), + vertexBuffers.mVertexBufferOffsets.data() ); + } + if (!vertexBufferOverrides.empty()) + { + assert( vertexBuffers.mVertexBuffers.size() == vertexBuffers.mVertexBufferOffsets.size() ); + assert( vertexBufferOverrides.empty() || vertexBuffers.mVertexBuffers.size() == drawablePass.mVertexBuffers.mVertexBuffers.size() ); + } + + if (drawablePass.mIndexBuffer != VK_NULL_HANDLE) + { + assert( drawablePass.mIndexBufferType != VK_INDEX_TYPE_MAX_ENUM ); + + // Bind index buffer data + vkCmdBindIndexBuffer(vkCmdBuffer, + drawablePass.mIndexBuffer, + 0, + drawablePass.mIndexBufferType); + + if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) + { + if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) + { + // Draw the mesh using draw indirect cont buffer (VkDrawIndexedIndirectCount command) + const auto* drawIndirectCountExt = mGfxApi.GetExtension(); + assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr); + drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(vkCmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); + } + else + // Draw the mesh using draw indirect buffer (VkDrawIndexedIndirectCommand) + vkCmdDrawIndexedIndirect(vkCmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndexedIndirectCommand)); + } + else + { + // Everything is set up, draw the mesh + vkCmdDrawIndexed(vkCmdBuffer, drawablePass.mNumIndices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0, 0); + } + } + else + { + if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) + { + if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) + { + // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) + const auto* drawIndirectCountExt = mGfxApi.GetExtension(); + assert( drawIndirectCountExt != nullptr && drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR != nullptr ); + drawIndirectCountExt->m_vkCmdDrawIndexedIndirectCountKHR(vkCmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mDrawIndirectCountBuffer, 0, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); + } + else + // Draw the mesh using draw indirect buffer (VkDrawIndirectCommand - no index buffer) + vkCmdDrawIndirect(vkCmdBuffer, drawablePass.mDrawIndirectBuffer, drawablePass.mDrawIndirectOffset, drawablePass.mNumDrawIndirect, sizeof(VkDrawIndirectCommand)); + } + else + { + // Draw the mesh without index buffer + vkCmdDraw(vkCmdBuffer, drawablePass.mNumVertices, GetInstances() ? (uint32_t)GetInstances()->GetNumVertices() : 1, 0, 0); + } + } + } + else + { + // + // Mesh shader pipeline + // + auto* meshExtension = mGfxApi.GetExtension(); + if (!meshExtension || meshExtension->Status != VulkanExtensionStatus::eLoaded) + assert( 0 && "mesh shader extension not loader or supported" ); + else if (!shaderPassDescription.m_taskName.empty()) + { + // Task, mesh (and frag) shader + meshExtension->m_vkCmdDrawMeshTasksEXT( vkCmdBuffer, mDispatchGroupCount[0], mDispatchGroupCount[1], mDispatchGroupCount[2] ); + } + else + { + // Mesh (and frag) only, no task shader + if (drawablePass.mDrawIndirectBuffer != VK_NULL_HANDLE) + { + if (drawablePass.mDrawIndirectCountBuffer != VK_NULL_HANDLE) + { + // Draw the mesh using Mesh indirect count buffer (vkCmdDrawMeshTasksIndirectCountEXT command) + assert(meshExtension->m_vkCmdDrawMeshTasksIndirectCountEXT != nullptr); + meshExtension->m_vkCmdDrawMeshTasksIndirectCountEXT( + vkCmdBuffer, + drawablePass.mDrawIndirectBuffer, + drawablePass.mDrawIndirectOffset, + drawablePass.mDrawIndirectCountBuffer, + 0, + drawablePass.mNumDrawIndirect, + sizeof(VkDrawMeshTasksIndirectCommandEXT)); + } + else + { + // Draw the mesh using Mesh indirect buffer (vkCmdDrawMeshTasksIndirectEXT) + assert(meshExtension->m_vkCmdDrawMeshTasksIndirectEXT!= nullptr); + meshExtension->m_vkCmdDrawMeshTasksIndirectEXT( + vkCmdBuffer, + drawablePass.mDrawIndirectBuffer, + drawablePass.mDrawIndirectOffset, + drawablePass.mNumDrawIndirect, + sizeof(VkDrawMeshTasksIndirectCommandEXT)); + } + } + } + } +} diff --git a/framework/code/material/vulkan/drawable.hpp b/framework/code/material/vulkan/drawable.hpp new file mode 100644 index 0000000..bae16d5 --- /dev/null +++ b/framework/code/material/vulkan/drawable.hpp @@ -0,0 +1,99 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + + +#include "../drawableLoader.hpp" + +#include "../drawable.hpp" +#include "vulkan/vulkan.hpp" +#include "memory/vulkan/drawIndirectBufferObject.hpp" +#include "memory/vulkan/indexBufferObject.hpp" +#include "memory/vulkan/vertexBufferObject.hpp" +#include "pipeline.hpp" +#include "pipelineVertexInputState.hpp" +#include "vulkan/renderContext.hpp" + + +// Forward Declarations +class MaterialBase; +template class IndexBuffer; +template class Material; +template class MaterialPass; +template class Shader; +template class RenderContext; + +class Vulkan; +struct MeshInstance; + + +/// Collection of VkBuffers for vertex buffer stream(s) (including instance buffers). +/// @ingroup Material +template<> +struct DrawablePassVertexBuffers : public DrawablePassVertexBuffersBase +{ + std::vector mVertexBuffers; + std::vector mVertexBufferOffsets; +}; + +/// Encapsulates a drawable pass. Specialized for Vulkan. +/// Owns the Vulkan pipeline, descriptor sets required by that pipeline. References vertex/index buffers etc from the parent Drawable. +/// Users are expected to use Drawable (which contains a vector of DrawablePasses and is more 'user friendly'). +/// @ingroup Material +template<> +class DrawablePass : public DrawablePassBase +{ + DrawablePass(const DrawablePass&) = delete; + DrawablePass& operator=(const DrawablePass&) = delete; + DrawablePass& operator=( DrawablePass&& ) noexcept = delete; +public: + DrawablePass( DrawablePass&& ) noexcept = default; + DrawablePass() = delete; + ~DrawablePass(); + DrawablePass(const MaterialPass& MaterialPass, + Pipeline Pipeline, + VkPipelineLayout PipelineLayout, + std::vector DescriptorSet, + const PipelineVertexInputState& PipelineVertexInputState, + DrawablePassVertexBuffers VertexBuffers, + VkBuffer IndexBuffer, + VkIndexType IndexBufferType, + VkBuffer DrawIndirectBuffer, + VkBuffer DrawIndirectCountBuffer, + uint32_t NumVertices, + uint32_t NumIndices, + uint32_t NumDrawIndirect, + uint32_t DrawIndirectOffset, + uint32_t PassIdx + ) : mMaterialPass( MaterialPass ), mPipeline( std::move(Pipeline) ), mPipelineLayout( PipelineLayout ), mDescriptorSet( DescriptorSet ), mPipelineVertexInputState( PipelineVertexInputState ), mVertexBuffers( VertexBuffers ), mIndexBuffer( IndexBuffer ), mIndexBufferType( IndexBufferType ), mDrawIndirectBuffer( DrawIndirectBuffer ), mDrawIndirectCountBuffer( DrawIndirectCountBuffer ), mNumVertices( NumVertices ), + mNumIndices( NumIndices), mNumDrawIndirect( NumDrawIndirect), mDrawIndirectOffset( DrawIndirectOffset), mPassIdx( PassIdx) + { + } + + const MaterialPass& mMaterialPass; + Pipeline mPipeline; // Owned by us + VkPipelineLayout mPipelineLayout; // Owned by shader + std::vector mDescriptorSet; // one per NUM_VULKAN_BUFFERS (double/triple buffering) + const PipelineVertexInputState& mPipelineVertexInputState; // contains vertex binding and attribute descriptions + DrawablePassVertexBuffers mVertexBuffers; // contains vkbuffer/offsets; one per mVertexBuffersLookup entry. May be vertex rate or instance rate. + VkBuffer mIndexBuffer = VK_NULL_HANDLE; + VkIndexType mIndexBufferType = VK_INDEX_TYPE_MAX_ENUM; + VkBuffer mDrawIndirectBuffer = VK_NULL_HANDLE; + VkBuffer mDrawIndirectCountBuffer = VK_NULL_HANDLE; + uint32_t mNumVertices; + uint32_t mNumIndices; + uint32_t mNumDrawIndirect; + uint32_t mDrawIndirectOffset; // if non zero offset mDrawIndirectBuffer by this + uint32_t mPassIdx; // index of the bit in Drawable::m_passMask +}; + + +class PipelineRasterizationInfo { + // contents of D3D12_GRAPHICS_PIPELINE_STATE_DESC or VkPipelineRasterizationStateCreateInfo +}; + diff --git a/framework/code/material/vulkan/material.cpp b/framework/code/material/vulkan/material.cpp new file mode 100644 index 0000000..60e506c --- /dev/null +++ b/framework/code/material/vulkan/material.cpp @@ -0,0 +1,17 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "material.hpp" +#include "shader.hpp" +#include "vulkan/vulkan.hpp" +#include +#include "system/os_common.h" +#include "vulkan/TextureFuncts.h" +#include "texture/vulkan/texture.hpp" + + diff --git a/framework/code/material/vulkan/material.hpp b/framework/code/material/vulkan/material.hpp index cd97e65..b113ce9 100644 --- a/framework/code/material/vulkan/material.hpp +++ b/framework/code/material/vulkan/material.hpp @@ -1,12 +1,31 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once +#include "vulkan/vulkan.hpp" +#include "materialPass.hpp" +#include "descriptorSetLayout.hpp" #include "../material.hpp" +#include "../materialT.hpp" #include "specializationConstantsLayout.hpp" -#include "pipelineVertexInputState.hpp" + + +/// @defgroup Material +/// Material and Shader loader. +/// Handles creation of descriptor sets, buffer binding, shader binaries (everything in Vulkan that describes and is used to render a shader). +/// +/// Typically user (application) writes a Json description for each 'Shader' that describes the inputs, outputs, and internal state of the shader, and the shader code (glsl). +/// The user then uses ShaderManagerBase::AddShader to register (and load) each Json file and the shader binaries. +/// +/// From there the user uses MaterialManagerBase::CreateMaterial to create a MaterialBase instance of the Shader (a MaterialBase contains bindings to the various texture/buffer inputs that a Shader requires - there can be many Materials using the same Shader (each MaterialBase with different textures, vertex buffers and/or uniform buffers etc) +/// +/// The MaterialBase returned by CreateMaterial can be used to Create a Drawable or Computable object that wraps everything together with one convenient interface! +/// +/// For more complex models the user should use DrawableLoader::LoadDrawable to load the mesh model file (and return a vector of Drawables). This api greatly simplifies the material creation and binding, splitting model meshes across material boundaries, automatically detecting instances (optionally). +/// + diff --git a/framework/code/material/materialManager.cpp b/framework/code/material/vulkan/materialManager.cpp similarity index 68% rename from framework/code/material/materialManager.cpp rename to framework/code/material/vulkan/materialManager.cpp index 26d44b9..0c2838a 100644 --- a/framework/code/material/materialManager.cpp +++ b/framework/code/material/vulkan/materialManager.cpp @@ -1,45 +1,45 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "materialManager.hpp" #include "descriptorSetLayout.hpp" -#include "vulkan/material.hpp" +#include "material.hpp" #include "shader.hpp" -#include "shaderDescription.hpp" +#include "../shaderDescription.hpp" #include "system/os_common.h" #include "vulkan/vulkan.hpp" #include "texture/vulkan/texture.hpp" -//#include "material/vulkan/specializationConstantsLayout.hpp" #include static_assert(GLM_HAS_CONSTEXPR); template<> -MaterialManagerT::MaterialManagerT() +MaterialManager::MaterialManager(Vulkan& gfxApi) noexcept + : MaterialManagerBase(gfxApi) {} template<> -MaterialManagerT::~MaterialManagerT() +MaterialManager::~MaterialManager() {} template<> -MaterialPass MaterialManagerT::CreateMaterialPassInternal( - Vulkan& vulkan, +MaterialPass MaterialManager::CreateMaterialPassInternal( const ShaderPass& shaderPass, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function& accelerationStructureLoader, + const std::function& textureLoader, + const std::function& bufferLoader, + const std::function(const std::string&)>& imageLoader, + const std::function& accelerationStructureLoader, const std::function& specializationStructureLoader, const std::string& passDebugName) const { - const std::vector& descriptorSetLayouts = shaderPass.GetDescriptorSetLayouts(); + auto& vulkan = static_cast(mGfxApi); + const std::vector>& descriptorSetLayouts = shaderPass.GetDescriptorSetLayouts(); // Copy the Vulkan descriptor set layout handles from our descriptor set layout class. // There may be 'null' layouts in here - denoting descriptor set layouts with 'dynamic' descriptorCount that needs to be calculated from the textureLoader functions. @@ -56,10 +56,10 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( // // Gather the (named) texture and uniform buffer slots (for this material pass) // - MaterialPass::tTextureBindings textureBindings; - MaterialPass::tImageBindings imageBindings; - MaterialPass::tBufferBindings bufferBindings; - MaterialPass::tAccelerationStructureBindings accelerationStructureBindings; + MaterialPass::tTextureBindings textureBindings; + MaterialPass::tImageBindings imageBindings; + MaterialPass::tBufferBindings bufferBindings; + MaterialPass::tAccelerationStructureBindings accelerationStructureBindings; for (size_t layoutIdx = 0; layoutIdx < descriptorSetLayouts.size(); ++layoutIdx) { @@ -77,20 +77,21 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( { uint32_t descriptorCount = 0; const std::string& bindingName = bindingNames.first; - const MaterialPass::MaterialDescriptorBinding binding{uint32_t(layoutIdx)/*set index*/, bindingNames.second}; + const DescriptorSetLayoutBase::DescriptorBinding binding{uint32_t(layoutIdx)/*set index*/, bindingNames.second}; + using DescriptorType = DescriptorSetDescription::DescriptorType; switch (binding.setBinding.type) { - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_SAMPLER: + case DescriptorType::ImageSampler: + case DescriptorType::ImageSampled: + case DescriptorType::Sampler: { - MaterialPass::tPerFrameTexInfo pTextures = textureLoader(bindingName); // Get the texture(s) and/or samplers from the callback + MaterialManagerBase::tPerFrameTexInfo pTextures = textureLoader(bindingName); // Get the texture(s) and/or samplers from the callback descriptorCount = (uint32_t) pTextures.size(); textureBindings.push_back({ std::move(pTextures), binding }); break; } - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case DescriptorType::InputAttachment: + case DescriptorType::ImageStorage: { if (imageLoader) { @@ -101,20 +102,21 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( else { // Fallback to using the texture callback - MaterialPass::tPerFrameTexInfo pTextures = textureLoader(bindingName); - std::vector imageInfos; + MaterialManagerBase::tPerFrameTexInfo pTextures = textureLoader(bindingName); + std::vector> imageInfos; descriptorCount = (uint32_t)pTextures.size(); imageInfos.reserve(descriptorCount); for (const auto* pTexture : pTextures) - imageInfos.push_back(ImageInfo(*pTexture)); + imageInfos.push_back( ImageInfo(*pTexture)); imageBindings.push_back({ std::move(imageInfos), binding }); } break; } - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + + case DescriptorType::UniformBuffer: + case DescriptorType::StorageBuffer: { - tPerFrameVkBuffer vkBuffers = bufferLoader(bindingName); // Get the buffer(s) from the callback + PerFrameBufferVulkan vkBuffers = bufferLoader(bindingName); // Get the buffer(s) from the callback descriptorCount = (uint32_t) vkBuffers.size(); bufferBindings.push_back({ std::move(vkBuffers), binding }); break; @@ -124,10 +126,10 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wswitch" #endif // defined(__clang__) - case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + case DescriptorType::AccelerationStructure: { assert(accelerationStructureLoader && "Needs accelerationStructureLoader function defining"); - MaterialPass::tPerFrameVkAccelerationStructure vkAS = accelerationStructureLoader(bindingName); // Get the buffer(s) from the callback + tPerFrameAccelerationStructure vkAS = accelerationStructureLoader(bindingName); // Get the buffer(s) from the callback accelerationStructureBindings.push_back({ std::move(vkAS), binding }); break; } @@ -135,6 +137,8 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( #pragma clang diagnostic pop #endif // defined(__clang__) #endif // VK_KHR_acceleration_structure + case DescriptorType::Unused: + break; default: assert(0); // needs implementing break; @@ -150,10 +154,10 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( if (vkDescSetLayout == VK_NULL_HANDLE) { // Create the descriptor set layout that is unique to this descriptor set (ideally we share the layouts but in the case of 'dynamic' descriptorCounts we cannot) - vkDescSetLayout = dynamicVkDescriptorSetLayouts.emplace_back(DescriptorSetLayout::CreateVkDescriptorSetLayout(vulkan, vkBindings)); + vkDescSetLayout = dynamicVkDescriptorSetLayouts.emplace_back(DescriptorSetLayout::CreateVkDescriptorSetLayout(vulkan, vkBindings)); // Add counts directly into pool sizes array! - DescriptorSetLayout::CalculatePoolSizes(vkBindings, poolSizes); + DescriptorSetLayout::CalculatePoolSizes(vkBindings, poolSizes); } } @@ -247,21 +251,22 @@ MaterialPass MaterialManagerT::CreateMaterialPassInternal( for (const auto& constantDescription : shaderPassConstantDescriptions) shaderConstantDatas.push_back( specializationStructureLoader( constantDescription.name ) ); - SpecializationConstants specializationConstants; + SpecializationConstants specializationConstants; specializationConstants.Init( shaderPass.GetSpecializationConstantsLayout(), { shaderConstantDatas } ); - return MaterialPass(vulkan, shaderPass, std::move(descriptorPool), std::move(descriptorSets), std::move(dynamicVkDescriptorSetLayouts), std::move(textureBindings), std::move(imageBindings), std::move(bufferBindings), std::move(accelerationStructureBindings), std::move(specializationConstants)); + return MaterialPass(vulkan, shaderPass, std::move(descriptorPool), std::move(descriptorSets), std::move(dynamicVkDescriptorSetLayouts), std::move(textureBindings), std::move(imageBindings), std::move(bufferBindings), std::move(accelerationStructureBindings), std::move(specializationConstants)); } template<> -Material MaterialManagerT::CreateMaterial(Vulkan& vulkan, const ShaderT& shader, uint32_t numFrameBuffers, - const std::function::tPerFrameTexInfo(const std::string&)>& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function::tPerFrameVkAccelerationStructure(const std::string&)>& accelerationStructureLoader, +Material MaterialManager::CreateMaterial(const Shader& shader, uint32_t numFrameBuffers, + const std::function& textureLoader, + const std::function(const std::string&)>& bufferLoader, + const std::function(const std::string&)>& imageLoader, + const std::function& accelerationStructureLoader, const std::function& specializationConstantLoader) const { - Material material( shader, numFrameBuffers ); + auto& vulkan = static_cast(mGfxApi); + Material material( shader, numFrameBuffers ); // We do each pass seperately. const ShaderDescription* pShaderDescription = shader.m_shaderDescription; @@ -271,7 +276,7 @@ Material MaterialManagerT::CreateMaterial(Vulkan& vulkan, const ShaderT< for (uint32_t passIdx = 0; const auto& passName : shader.GetShaderPassIndicesToNames()) { const auto& shaderPass = shaderPasses[passIdx]; - material.AddMaterialPass(passName, CreateMaterialPassInternal(vulkan, shaderPass, numFrameBuffers, textureLoader, bufferLoader, imageLoader, accelerationStructureLoader, specializationConstantLoader, passName)); + material.AddMaterialPass(passName, CreateMaterialPassInternal(shaderPass, numFrameBuffers, textureLoader, bufferLoader, imageLoader, accelerationStructureLoader, specializationConstantLoader, passName)); ++passIdx; } @@ -283,14 +288,20 @@ Material MaterialManagerT::CreateMaterial(Vulkan& vulkan, const ShaderT< return material; } -template<> -Material MaterialManagerT::CreateMaterial(GraphicsApiBase& gfxApi, const Shader& shader, uint32_t numFrameBuffers, - const std::function& textureLoader, - const std::function& bufferLoader, - const std::function& imageLoader, - const std::function& accelerationStructureLoader, - const std::function& specializationConstantLoader) const /*override*/ -{ - return CreateMaterial(static_cast(gfxApi), static_cast&>(shader), numFrameBuffers, - textureLoader, bufferLoader, imageLoader, accelerationStructureLoader, specializationConstantLoader); -} +//template<> +//MaterialBase MaterialManager::CreateMaterial(GraphicsApiBase& gfxApi, const Shader& shader, uint32_t numFrameBuffers, +// const std::function& textureLoader, +// const std::function& bufferLoader, +// const std::function& imageLoader, +// const std::function& accelerationStructureLoader, +// const std::function& specializationConstantLoader) const /*override*/ +//{ +// return CreateMaterial(static_cast(gfxApi), +// static_cast&>(shader), +// numFrameBuffers, +// textureLoader, +// bufferLoader, +// imageLoader, +// accelerationStructureLoader, +// specializationConstantLoader); +//} diff --git a/samples/sdp-cli/code/main/helpers/numerical_aggregator.cpp b/framework/code/material/vulkan/materialManager.hpp similarity index 84% rename from samples/sdp-cli/code/main/helpers/numerical_aggregator.cpp rename to framework/code/material/vulkan/materialManager.hpp index 3199802..f3df88e 100644 --- a/samples/sdp-cli/code/main/helpers/numerical_aggregator.cpp +++ b/framework/code/material/vulkan/materialManager.hpp @@ -5,5 +5,8 @@ // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ +#pragma once + +#include "material.hpp" +#include "../materialManager.hpp" -#include "numerical_aggregator.hpp" diff --git a/framework/code/material/vulkan/materialPass.cpp b/framework/code/material/vulkan/materialPass.cpp new file mode 100644 index 0000000..17c84f1 --- /dev/null +++ b/framework/code/material/vulkan/materialPass.cpp @@ -0,0 +1,409 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "material.hpp" +#include "shader.hpp" +#include "vulkan/vulkan.hpp" +#include +#include "system/os_common.h" +#include "vulkan/TextureFuncts.h" +#include "vulkanRT/accelerationStructure.hpp" +#include "texture/vulkan/texture.hpp" + +static constexpr VkDescriptorType cDescriptorTypeToVk[] { + VK_DESCRIPTOR_TYPE_MAX_ENUM, // unused + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // UniformBuffer + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // StorageBuffer + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // ImageSampler + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, // ImageSampled + VK_DESCRIPTOR_TYPE_SAMPLER, // Sampler + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, // ImageStorage, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, // InputAttachment, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // DrawIndirectBuffer ///TODO: is this the correct conversion (should we have this descriptor type?) + VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, // AccelerationStructure + VK_DESCRIPTOR_TYPE_MAX_ENUM, // descriptor table (not supported by Vk) +}; +static_assert(cDescriptorTypeToVk[(int)DescriptorSetDescription::DescriptorType::AccelerationStructure] == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); +static_assert(cDescriptorTypeToVk[(int)DescriptorSetDescription::DescriptorType::Sampler] == VK_DESCRIPTOR_TYPE_SAMPLER); + +static constexpr VkDescriptorType EnumToVk(const DescriptorSetDescription::DescriptorType t) { + return cDescriptorTypeToVk[(int)t]; +} + + + +MaterialPass::MaterialPass(Vulkan& vulkan, const ShaderPass& shaderPass, VkDescriptorPool descriptorPool, std::vector descriptorSets, std::vector dynamicDescriptorSetLayouts, tTextureBindings textureBindings, tImageBindings imageBindings, tBufferBindings bufferBindings, tAccelerationStructureBindings accelerationStructureBindings, SpecializationConstants specializationConstants ) noexcept + : MaterialPassBase(shaderPass) + , mVulkan( vulkan ) + , mNumDescriptorSetsPerBuffer(uint32_t(shaderPass.GetDescriptorSetLayouts().size())) + , mNumBuffers(mNumDescriptorSetsPerBuffer>0 ? uint32_t(descriptorSets.size() / mNumDescriptorSetsPerBuffer) : 0) + , mDescriptorPool(descriptorPool) + , mDescriptorSets(std::move(descriptorSets)) + , mDynamicDescriptorSetLayouts(std::move(dynamicDescriptorSetLayouts)) + , mSpecializationConstants( std::move( specializationConstants ) ) + , mTextureBindings(std::move(textureBindings)) + , mImageBindings(std::move(imageBindings)) + , mBufferBindings(std::move(bufferBindings)) + , mAccelerationStructureBindings( std::move( accelerationStructureBindings ) ) +{ + if (!mDynamicDescriptorSetLayouts.empty()) + mDynamicPipelineLayout.Init(vulkan, mDynamicDescriptorSetLayouts); + assert( mDescriptorSets.size() == mNumBuffers*mNumDescriptorSetsPerBuffer ); + + descriptorPool = VK_NULL_HANDLE; // we took owenership +} + +MaterialPass::MaterialPass(MaterialPass&& other) noexcept + : MaterialPassBase(std::move(other)) + , mVulkan( other.mVulkan ) + , mNumDescriptorSetsPerBuffer( other.mNumDescriptorSetsPerBuffer ) + , mNumBuffers( other.mNumBuffers ) + , mDescriptorPool(other.mDescriptorPool) + , mDescriptorSets(std::move(other.mDescriptorSets)) + , mDynamicDescriptorSetLayouts(std::move(other.mDynamicDescriptorSetLayouts)) + , mDynamicPipelineLayout(std::move(other.mDynamicPipelineLayout)) + , mAccelerationStructureBindings( std::move( other.mAccelerationStructureBindings ) ) + , mTextureBindings(std::move(other.mTextureBindings)) + , mImageBindings(std::move(other.mImageBindings)) + , mBufferBindings(std::move(other.mBufferBindings)) + , mSpecializationConstants( std::move( other.mSpecializationConstants ) ) +{ + other.mDescriptorPool = VK_NULL_HANDLE; +} + +MaterialPass::~MaterialPass() +{ + if (mDescriptorPool != VK_NULL_HANDLE) + { + //if (mDescriptorSet != VK_NULL_HANDLE) + // vkFreeDescriptorSets(mVulkan.m_VulkanDevice, mDescriptorPool, 1, &mDescriptorSet); only if VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT + vkDestroyDescriptorPool(mVulkan.m_VulkanDevice, mDescriptorPool, nullptr); + } + + mDynamicPipelineLayout.Destroy(mVulkan); + + for(auto& layout: mDynamicDescriptorSetLayouts) + vkDestroyDescriptorSetLayout(mVulkan.m_VulkanDevice, layout, nullptr); +} + +bool MaterialPass::UpdateDescriptorSets(uint32_t bufferIdx) +{ + // TODO: All these need to become vectors that can dynamically grow + static const int cMAX_WRITES = 32; + static const int cMAX_IMAGE_INFOS = 1048; + static const int cMAX_BUFFER_INFOS = 1048; + static const int cMAX_ACCELERATIONSTRUCTURE_INFOS = 32; + std::array writeInfo{/*zero it*/ }; + std::array imageInfo{/*zero it*/ }; + std::array bufferInfoFixed/*not initialized*/; + std::vector bufferInfoDynamic; + + // std::vector + + uint32_t writeInfoIdx = 0; + uint32_t imageInfoCount = 0; + uint32_t bufferInfoCount = 0; + const size_t numDescriptorSetsPerFrame = GetShaderPass().GetDescriptorSetLayouts().size(); + const auto descriptorSetBaseIdx = bufferIdx * numDescriptorSetsPerFrame; + + // Go through the textures first + for (const auto& textureBinding : mTextureBindings) + { + uint32_t setIndex = textureBinding.second.setIndex; + uint32_t bindingIndex = textureBinding.second.setBinding.index; + VkDescriptorType bindingType = EnumToVk(textureBinding.second.setBinding.type); + + uint32_t numTexToBind = textureBinding.second.setBinding.isArray ? (uint32_t)textureBinding.first.size() : 1; + uint32_t texIndex = textureBinding.second.setBinding.isArray ? 0 : (bufferIdx % textureBinding.first.size()); + + writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo[writeInfoIdx].descriptorType = bindingType; + writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; + writeInfo[writeInfoIdx].dstBinding = bindingIndex; + writeInfo[writeInfoIdx].dstArrayElement = 0; + writeInfo[writeInfoIdx].descriptorCount = numTexToBind; + writeInfo[writeInfoIdx].pBufferInfo = nullptr; + writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; + for (uint32_t t = 0; t < numTexToBind; ++t, ++imageInfoCount, ++texIndex) + { + if (imageInfoCount >= cMAX_IMAGE_INFOS) + { + LOGE("Max number (%d) of VkDescriptorImageInfo elements reached!", cMAX_IMAGE_INFOS); + assert(0); + return false; + } + + imageInfo[imageInfoCount] = apiCast(textureBinding.first[texIndex])->GetVkDescriptorImageInfo(); + assert(imageInfo[imageInfoCount].imageView != VK_NULL_HANDLE); + } + + ++writeInfoIdx; + if (writeInfoIdx >= cMAX_WRITES) + { + LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); + assert(0); + return false; + } + } + + // Go through the images + for (const auto& imageBinding : mImageBindings) + { + uint32_t setIndex = imageBinding.second.setIndex; + uint32_t bindingIndex = imageBinding.second.setBinding.index; + VkDescriptorType bindingType = EnumToVk(imageBinding.second.setBinding.type); + assert(bindingType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && bindingType != VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); // combined image sampler or sampled image should go through mTextureBindings + //assert(imageBinding.first.imageLayout == VK_IMAGE_LAYOUT_GENERAL); + + uint32_t numImgToBind = imageBinding.second.setBinding.isArray ? (uint32_t)imageBinding.first.size() : 1; + uint32_t imgIndex = imageBinding.second.setBinding.isArray ? 0 : (bufferIdx % imageBinding.first.size()); + + writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo[writeInfoIdx].descriptorType = bindingType; + writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; + writeInfo[writeInfoIdx].dstBinding = bindingIndex; + writeInfo[writeInfoIdx].dstArrayElement = 0; + writeInfo[writeInfoIdx].descriptorCount = numImgToBind; + writeInfo[writeInfoIdx].pBufferInfo = nullptr; + writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; + for (uint32_t t = 0; t < numImgToBind; ++t, ++imageInfoCount, ++imgIndex) + { + if (imageInfoCount >= cMAX_IMAGE_INFOS) + { + LOGE("Max number (%d) of VkDescriptorImageInfo elements reached!", cMAX_IMAGE_INFOS); + assert(0); + return false; + } + + imageInfo[imageInfoCount].sampler = VK_NULL_HANDLE; + imageInfo[imageInfoCount].imageView = imageBinding.first[imgIndex].imageView; + imageInfo[imageInfoCount].imageLayout = imageBinding.first[imgIndex].imageLayout; + assert(imageBinding.first[imgIndex].imageView != VK_NULL_HANDLE); + } + ++writeInfoIdx; + + if (writeInfoIdx >= cMAX_WRITES) + { + LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); + assert(0); + return false; + } + } + + // Now do the buffers + + for (const auto& bufferBinding : mBufferBindings) + { + bufferInfoCount += bufferBinding.second.setBinding.isArray ? (uint32_t) bufferBinding.first.size() : 1; + } + auto* pBufferInfo = bufferInfoFixed.data(); + if (bufferInfoCount > cMAX_BUFFER_INFOS) + { + bufferInfoDynamic.resize( bufferInfoCount ); + pBufferInfo = bufferInfoDynamic.data(); + } + + for (const auto& bufferBinding : mBufferBindings) + { + uint32_t setIndex = bufferBinding.second.setIndex; + uint32_t bindingIndex = bufferBinding.second.setBinding.index; + VkDescriptorType bindingType = EnumToVk(bufferBinding.second.setBinding.type); + + uint32_t numBuffersToBind = bufferBinding.second.setBinding.isArray ? (uint32_t)bufferBinding.first.size() : 1; + uint32_t bufferIndex = bufferBinding.second.setBinding.isArray ? 0 : (bufferIdx % bufferBinding.first.size()); + + writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo[writeInfoIdx].descriptorType = bindingType; + writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; + writeInfo[writeInfoIdx].dstBinding = bindingIndex; + writeInfo[writeInfoIdx].dstArrayElement = 0; + writeInfo[writeInfoIdx].descriptorCount = numBuffersToBind; + writeInfo[writeInfoIdx].pBufferInfo = pBufferInfo; + writeInfo[writeInfoIdx].pImageInfo = nullptr; + + for (uint32_t t = 0; t < numBuffersToBind; ++t, ++bufferIndex) + { + pBufferInfo->buffer = bufferBinding.first[bufferIndex].buffer(); + pBufferInfo->offset = bufferBinding.first[bufferIndex].offset(); + pBufferInfo->range = VK_WHOLE_SIZE; + ++pBufferInfo; + } + + ++writeInfoIdx; + if (writeInfoIdx >= cMAX_WRITES) + { + LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); + assert(0); + return false; + } + } + +#if VK_KHR_acceleration_structure + // And the acceleration structures + std::array accelerationStructureInfo{}; + uint32_t accelerationStructureCount = 0; + + for (const auto& accelerationBinding : mAccelerationStructureBindings) + { + uint32_t setIndex = accelerationBinding.second.setIndex; + uint32_t bindingIndex = accelerationBinding.second.setBinding.index; + VkDescriptorType bindingType = EnumToVk(accelerationBinding.second.setBinding.type); + + uint32_t numAccelToBind = accelerationBinding.second.setBinding.isArray ? (uint32_t)accelerationBinding.first.size() : 1; + uint32_t accelIndex = accelerationBinding.second.setBinding.isArray ? 0 : (bufferIdx < accelerationBinding.first.size() ? bufferIdx : 0); + + if (writeInfoIdx >= cMAX_WRITES) + { + LOGE("Max number (%d) of VkWriteDescriptorSet elements reached!", cMAX_WRITES); + assert(0); + return false; + } + + if (accelerationStructureCount >= cMAX_ACCELERATIONSTRUCTURE_INFOS) + { + LOGE("Max number (%d) of VkWriteDescriptorSetAccelerationStructureKHR elements reached!", cMAX_ACCELERATIONSTRUCTURE_INFOS); + assert(0); + return false; + } + + const auto* pAs = apiCast(accelerationBinding.first[accelIndex]); + accelerationStructureInfo[accelerationStructureCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; + accelerationStructureInfo[accelerationStructureCount].accelerationStructureCount = 1; + accelerationStructureInfo[accelerationStructureCount].pAccelerationStructures = &pAs->GetVkAccelerationStructure(); + + writeInfo[writeInfoIdx].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo[writeInfoIdx].descriptorType = bindingType;//VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR + writeInfo[writeInfoIdx].dstSet = mDescriptorSets[descriptorSetBaseIdx + setIndex]; + writeInfo[writeInfoIdx].dstBinding = bindingIndex; + writeInfo[writeInfoIdx].dstArrayElement = 0; + writeInfo[writeInfoIdx].descriptorCount = accelerationStructureInfo[accelerationStructureCount].accelerationStructureCount; + writeInfo[writeInfoIdx].pBufferInfo = nullptr; + writeInfo[writeInfoIdx].pImageInfo = nullptr; + writeInfo[writeInfoIdx].pNext = &accelerationStructureInfo[accelerationStructureCount]; + ++writeInfoIdx; + ++accelerationStructureCount; + + } +#endif // VK_KHR_acceleration_structure + + // LOGI("Updating Descriptor Set (bufferIdx %d) with %d objects", bufferIdx, writeInfoIdx); + vkUpdateDescriptorSets(mVulkan.m_VulkanDevice, writeInfoIdx, writeInfo.data(), 0, NULL); + // LOGI("Descriptor Set Updated!"); + + return true; +} + +bool MaterialPass::UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const Texture& newTexture) const +{ + std::array writeInfo{ {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET} }; + std::array imageInfo{/*zero it*/ }; + + uint32_t writeInfoIdx = 0; + uint32_t imageInfoCount = 0; + + for (int setIdx = 0; const auto & setLayout : GetShaderPass().GetDescriptorSetLayouts()) + { + const auto& nameToBinding = setLayout.GetNameToBinding(); + const auto bindingIt = nameToBinding.find( bindingName ); + if (bindingIt != nameToBinding.end()) + { + const auto& descriptorSet = GetVkDescriptorSet( bufferIdx, setIdx ); + VkDescriptorType bindingType = EnumToVk(bindingIt->second.type); + assert( bindingType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || bindingType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || bindingType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); + + uint32_t bindingIndex = bindingIt->second.index; + + writeInfo[writeInfoIdx].descriptorType = bindingType; + writeInfo[writeInfoIdx].dstSet = descriptorSet; + writeInfo[writeInfoIdx].dstBinding = bindingIndex; + writeInfo[writeInfoIdx].dstArrayElement = 0; + writeInfo[writeInfoIdx].descriptorCount = 1; + writeInfo[writeInfoIdx].pBufferInfo = nullptr; + writeInfo[writeInfoIdx].pImageInfo = &imageInfo[imageInfoCount]; + imageInfo[imageInfoCount] = newTexture.GetVkDescriptorImageInfo(); + + ++imageInfoCount; + ++writeInfoIdx; + + vkUpdateDescriptorSets( mVulkan.m_VulkanDevice, writeInfoIdx, writeInfo.data(), 0, NULL ); + return true; + } + ++setIdx; + } + assert( 0 && "Binding name not found" ); + return false; +} + + +ImageInfo::ImageInfo(const Texture& t) +: image(t.GetVkImage()) +, imageView(t.GetVkImageView()) +, imageViewNumMips(t.MipLevels) +, imageViewFirstMip(t.FirstMip) +, imageLayout( t.GetVkImageLayout() ) +{ +} + +ImageInfo::ImageInfo(const TextureBase& t) : ImageInfo( apiCast(t) ) +{ +} + +ImageInfo::ImageInfo(const ImageInfo& src) +: image(src.image) +, imageView(src.imageView) +, imageLayout( src.imageLayout ) +, imageViewNumMips( src.imageViewNumMips ) +, imageViewFirstMip( src.imageViewFirstMip ) +{ +} + +ImageInfo::ImageInfo( ImageInfo&& src ) noexcept + : ImageInfoBase() + , image( src.image ) + , imageView( src.imageView ) + , imageLayout( src.imageLayout ) + , imageViewNumMips( src.imageViewNumMips ) + , imageViewFirstMip( src.imageViewFirstMip ) +{ + src.image = VK_NULL_HANDLE; + src.imageView = VK_NULL_HANDLE; + src.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + src.imageViewNumMips = 0; + src.imageViewFirstMip = 0; +} + +ImageInfo& ImageInfo::operator=(ImageInfo&& src) noexcept +{ + if (this != &src) + { + image = src.image; + imageView = src.imageView; + imageLayout = src.imageLayout; + imageViewNumMips = src.imageViewNumMips; + imageViewFirstMip = src.imageViewFirstMip; + src.image = VK_NULL_HANDLE; + src.imageView = VK_NULL_HANDLE; + src.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + src.imageViewNumMips = 0; + src.imageViewFirstMip = 0; + } + return *this; +} + +ImageInfo& ImageInfo::operator=( const ImageInfo& src) noexcept +{ + if (this != &src) + { + image = src.image; + imageView = src.imageView; + imageLayout = src.imageLayout; + imageViewNumMips = src.imageViewNumMips; + imageViewFirstMip = src.imageViewFirstMip; + } + return *this; +} diff --git a/framework/code/material/vulkan/materialPass.hpp b/framework/code/material/vulkan/materialPass.hpp new file mode 100644 index 0000000..d8d8ef6 --- /dev/null +++ b/framework/code/material/vulkan/materialPass.hpp @@ -0,0 +1,108 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "vulkan/vulkan.hpp" +#include "../descriptorSetLayout.hpp" +#include "shader.hpp" +#include "pipelineLayout.hpp" +#include "specializationConstants.hpp" +#include "specializationConstantsLayout.hpp" +#include "../materialManager.hpp" +#include "../materialPass.hpp" + + +// Forward declarations +class TextureBase; +template struct ImageInfo; +template class PipelineLayout; +template class ShaderPass; +template class Texture; + +using VkBufferAndOffset = BufferAndOffset; +using PerFrameBufferVulkan = PerFrameBuffer; + + +/// Reference to a VkImage. +/// Does not have ownership over referenced VkImage or VkImageView and lifetime of those object should be longer than the referencing @ImageInfo (no reference counting). +/// @ingroup Material +template<> +struct ImageInfo : public ImageInfoBase { + ImageInfo() noexcept : ImageInfoBase() {} + ImageInfo( ImageInfo&&) noexcept; + ImageInfo& operator=(ImageInfo&&) noexcept; + ImageInfo& operator=(const ImageInfo&) noexcept; + ImageInfo(const ImageInfo&); + ImageInfo(const Texture&); + ImageInfo(const TextureBase&); + VkImage image = VK_NULL_HANDLE; + VkImageView imageView = VK_NULL_HANDLE; + uint32_t imageViewNumMips = 0; + uint32_t imageViewFirstMip = 0; + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_GENERAL; +}; + + + +/// An instance of a ShaderPass material. +/// Template class specialization for Vulkan +/// @ingroup Material +template<> +class MaterialPass final : public MaterialPassBase +{ + MaterialPass(const MaterialPass&) = delete; + MaterialPass& operator=(const MaterialPass&) = delete; +public: + + typedef std::vector > tPerFrameImageInfo; + + typedef std::vector > tTextureBindings; + typedef std::vector > tImageBindings; + typedef std::vector > tBufferBindings; + typedef std::vector > tAccelerationStructureBindings; + + MaterialPass(Vulkan& vulkan, const ShaderPass&, VkDescriptorPool, std::vector, std::vector dynamicDescriptorSetLayouts, tTextureBindings, tImageBindings, tBufferBindings, tAccelerationStructureBindings, SpecializationConstants) noexcept; + MaterialPass(MaterialPass&&) noexcept; + ~MaterialPass(); + + const auto& GetShaderPass() const { return apiCast( mShaderPass ); } + + /// Get the descriptor set for the (numbered) frame buffer index, allows for a single descriptor set identical for all frames if required. + const auto& GetVkDescriptorSet(uint32_t bufferIndex, int32_t setIndex) const { return mDescriptorSets[mDescriptorSets.size() > 1 ? bufferIndex : 0]; } + const std::span GetVkDescriptorSets( uint32_t bufferIndex ) const { return {&mDescriptorSets[(bufferIndex % mNumBuffers) * mNumDescriptorSetsPerBuffer], mNumDescriptorSetsPerBuffer}; } + const auto& GetVkDescriptorSets() const { return mDescriptorSets; } + const auto& GetPipelineLayout() const { return mDynamicPipelineLayout; } + const auto& GetSpecializationConstants() const { return mSpecializationConstants; }; + + const auto& GetTextureBindings() const { return mTextureBindings; } + const auto& GetImageBindings() const { return mImageBindings; } + const auto& GetBufferBindings() const { return mBufferBindings; } + + bool UpdateDescriptorSets(uint32_t bufferIdx); + bool UpdateDescriptorSetBinding(uint32_t bufferIdx, const std::string& bindingName, const Texture& newTexture) const; + +protected: + Vulkan& mVulkan; + + // Helpers for size of mDescriptorSets + const uint32_t mNumDescriptorSetsPerBuffer; ///< number of descriptor sets needed by the shader(pass). Usually 1 but some shaders will use more then one secriptor set. + const uint32_t mNumBuffers; ///< Number of buffers worth of descriptors (may be 1, or number of framebuffers, or something else) + + // Vulkan objects + VkDescriptorPool mDescriptorPool; + std::vector mDescriptorSets; ///< array of descriptor sets (mNumDescriptorSetsPerBuffer * mNumBuffers)) + std::vector mDynamicDescriptorSetLayouts;///< array of descriptor set layouts specific for to this materialPass (usually they are shared across all materials with a specific shader, except in the case of descriptor sets that are 'dynamically' sized to fit the material specific contents) + PipelineLayout mDynamicPipelineLayout; ///< pipeline layout specific to this materiaPass (usually shaderPass contains the pipeline layout but for materials with 'dynamic' descriptor set layouts we have to have a unique pipeline per materialPass + SpecializationConstants mSpecializationConstants; ///< block of specialization constants for this material pass + + tTextureBindings mTextureBindings; ///< Images (textures) (with sampler) considered readonly + tImageBindings mImageBindings; ///< Images that may be bound as writable (or read/write). + tBufferBindings mBufferBindings; + tAccelerationStructureBindings mAccelerationStructureBindings; +}; diff --git a/framework/code/material/vulkan/pipeline.cpp b/framework/code/material/vulkan/pipeline.cpp new file mode 100644 index 0000000..b002e17 --- /dev/null +++ b/framework/code/material/vulkan/pipeline.cpp @@ -0,0 +1,209 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "vulkan/vulkan.hpp" +#include "vulkan/renderContext.hpp" +#include "pipeline.hpp" +#include "pipelineLayout.hpp" +#include "pipelineVertexInputState.hpp" +#include "shader.hpp" +#include "shaderModule.hpp" +#include "specializationConstants.hpp" +#include "../shaderDescription.hpp" + +// Forward declarations +class Vulkan; + + +Pipeline::Pipeline() noexcept + : mPipeline{} +{ +} + +Pipeline::Pipeline(VkDevice device, VkPipeline pipeline) noexcept + : mPipeline( device, pipeline ) +{ +} + +Pipeline::~Pipeline() +{} + +static VkBlendFactor EnumToVk( ShaderPassDescription::BlendFactor bf) +{ + switch( bf ) + { + case ShaderPassDescription::BlendFactor::Zero: + return VK_BLEND_FACTOR_ZERO; + case ShaderPassDescription::BlendFactor::One: + return VK_BLEND_FACTOR_ONE; + case ShaderPassDescription::BlendFactor::SrcAlpha: + return VK_BLEND_FACTOR_SRC_ALPHA; + case ShaderPassDescription::BlendFactor::OneMinusSrcAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case ShaderPassDescription::BlendFactor::DstAlpha: + return VK_BLEND_FACTOR_DST_ALPHA; + case ShaderPassDescription::BlendFactor::OneMinusDstAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + } + assert( 0 ); + return VK_BLEND_FACTOR_ZERO; +} + +template<> +Pipeline CreatePipeline( Vulkan& vulkan, + const ShaderPassDescription& shaderPassDescription, + const PipelineLayout& pipelineLayout, + const PipelineVertexInputState& pipelineVertexInputState, + const PipelineRasterizationState& pipelineRasterizationState, + const SpecializationConstants& specializationConstants, + const ShaderModules& shaderModules, + const RenderContext& renderingPassContext, + Msaa msaa) +{ + //// State for rasterization, such as polygon fill mode is defined. + const auto& fixedFunctionSettings = shaderPassDescription.m_fixedFunctionSettings; + + const auto& outputSettings = shaderPassDescription.m_outputs; + + // Setup blending/transparency + std::vector BlendStates; + BlendStates.reserve(outputSettings.size()); + for (const auto& outputSetting : outputSettings) + { + VkPipelineColorBlendAttachmentState& cb = BlendStates.emplace_back(VkPipelineColorBlendAttachmentState{}); + if (outputSetting.blendEnable) + { + cb.blendEnable = VK_TRUE; + cb.srcColorBlendFactor = EnumToVk(outputSetting.srcColorBlendFactor); + cb.dstColorBlendFactor = EnumToVk(outputSetting.dstColorBlendFactor); + cb.colorBlendOp = VK_BLEND_OP_ADD; + cb.srcAlphaBlendFactor = EnumToVk(outputSetting.srcAlphaBlendFactor); + cb.dstAlphaBlendFactor = EnumToVk(outputSetting.dstAlphaBlendFactor); + cb.alphaBlendOp = VK_BLEND_OP_ADD; + } + cb.colorWriteMask = outputSetting.colorWriteMask & (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); + } + + VkPipelineColorBlendStateCreateInfo cb = {}; + cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + cb.attachmentCount = (uint32_t)BlendStates.size(); + cb.pAttachments = BlendStates.data(); + + // Setup depth testing + VkPipelineDepthStencilStateCreateInfo ds = {}; + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = fixedFunctionSettings.depthTestEnable ? VK_TRUE : VK_FALSE; + ds.depthWriteEnable = fixedFunctionSettings.depthWriteEnable ? VK_TRUE : VK_FALSE; + switch( fixedFunctionSettings.depthCompareOp ) { + case ShaderPassDescription::DepthCompareOp::LessEqual: + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + break; + case ShaderPassDescription::DepthCompareOp::Equal: + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + break; + case ShaderPassDescription::DepthCompareOp::Greater: + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + break; + } + ds.depthBoundsTestEnable = VK_FALSE; + ds.back.failOp = VK_STENCIL_OP_KEEP; + ds.back.passOp = VK_STENCIL_OP_KEEP; + ds.back.compareOp = VK_COMPARE_OP_ALWAYS; + ds.stencilTestEnable = VK_FALSE; + ds.front = ds.back; + + // Setup (multi) sampling + const auto& sampleShadingSettings = shaderPassDescription.m_sampleShadingSettings; + VkSampleMask sampleMask = 0; + VkPipelineMultisampleStateCreateInfo ms = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; + ms.rasterizationSamples = EnumToVk(msaa); + ms.sampleShadingEnable = sampleShadingSettings.sampleShadingEnable; + if (sampleShadingSettings.sampleShadingMask != 0) + { + assert(ms.rasterizationSamples <= VK_SAMPLE_COUNT_32_BIT ); // sampleMask is only 32bits currently! Easy fix if we want > 32x MSAA + sampleMask = sampleShadingSettings.sampleShadingMask & ((1 << ms.rasterizationSamples) -1); + ms.pSampleMask = &sampleMask; + } + + VkPipelineSampleLocationsStateCreateInfoEXT msLocations = { VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT }; + if( sampleShadingSettings.forceCenterSample ) + { + msLocations.sampleLocationsEnable = VK_TRUE; + msLocations.sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT; + msLocations.sampleLocationsInfo.sampleLocationsPerPixel = ms.rasterizationSamples; + msLocations.sampleLocationsInfo.sampleLocationsCount = (uint32_t) ms.rasterizationSamples; + msLocations.sampleLocationsInfo.sampleLocationGridSize = { 1,1 }; + std::vector msSampleLocations( msLocations.sampleLocationsInfo.sampleLocationsCount, { 0.5f,0.5f } ); + msLocations.sampleLocationsInfo.pSampleLocations = msSampleLocations.data(); + ms.pNext = &msLocations; + } + + VkShaderModule vkVertShader = VK_NULL_HANDLE; + VkShaderModule vkFragShader = VK_NULL_HANDLE; + VkShaderModule vkTaskShader = VK_NULL_HANDLE; + VkShaderModule vkMeshShader = VK_NULL_HANDLE; + std::visit( [&]( auto& m ) + { + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + vkVertShader = m.vert.GetVkShaderModule(); + vkFragShader = m.frag.GetVkShaderModule(); + } + else if constexpr (std::is_same_v>) + { + vkVertShader = m.vert.GetVkShaderModule(); + } + else if constexpr (std::is_same_v>) + { + vkMeshShader = m.mesh.GetVkShaderModule(); + vkFragShader = m.frag.GetVkShaderModule(); + } + else if constexpr (std::is_same_v>) + { + vkTaskShader = m.task.GetVkShaderModule(); + vkMeshShader = m.mesh.GetVkShaderModule(); + vkFragShader = m.frag.GetVkShaderModule(); + } + else + { + assert( 0 ); // unsupported shader module type (eg ComputeShaderModule) + } + }, shaderModules.m_modules ); + + VkPipeline pipeline = VK_NULL_HANDLE; + if (!vulkan.CreatePipeline( vulkan.GetPipelineCache(), + &pipelineVertexInputState.GetVkPipelineVertexInputStateCreateInfo(), + pipelineLayout.GetVkPipelineLayout(), + renderingPassContext, + &pipelineRasterizationState.mPipelineRasterizationStateCreateInfo, + &ds, + BlendStates.empty() ? nullptr : &cb, + &ms, + nullptr,//input assembly state + {},//dynamic states + nullptr/*viewport*/, nullptr/*scissor*/, + vkTaskShader, + vkMeshShader, + vkVertShader, + vkFragShader, + specializationConstants.GetVkSpecializationInfo(), + false, + VK_NULL_HANDLE, + &pipeline)) + { + // Error + return {}; + } + return Pipeline(vulkan.m_VulkanDevice, pipeline); +} + +void ReleasePipeline(Vulkan& vulkan, Pipeline& pipeline) +{ + pipeline = {}; +} \ No newline at end of file diff --git a/framework/code/material/vulkan/pipeline.hpp b/framework/code/material/vulkan/pipeline.hpp new file mode 100644 index 0000000..d866dcc --- /dev/null +++ b/framework/code/material/vulkan/pipeline.hpp @@ -0,0 +1,61 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "vulkan/refHandle.hpp" +#include "pipelineVertexInputState.hpp" +#include "../pipeline.hpp" + +// Forward declarations +class ShaderPassDescription; +class Vulkan; +enum class Msaa; +template class PipelineLayout; +template class ShaderModules; +template class SpecializationConstants; + + +/// Simple wrapper around VkPipeline. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// Specialization of Pipeline +/// @ingroup Material +template<> +class Pipeline final +{ + Pipeline& operator=(const Pipeline&) = delete; +public: + Pipeline() noexcept; + ~Pipeline(); + Pipeline( VkDevice device, VkPipeline pipeline ) noexcept; + Pipeline( Pipeline&& ) noexcept = default; + Pipeline& operator=( Pipeline&& ) noexcept = default; + + Pipeline Copy() const { return Pipeline{*this}; } + + operator bool() const { return mPipeline != VK_NULL_HANDLE; } + + VkPipeline GetVkPipeline() const { return mPipeline.get(); } + +private: + Pipeline( const Pipeline& src ) noexcept { + mPipeline = src.mPipeline; + } + RefHandle mPipeline; +}; + +template<> +Pipeline CreatePipeline( Vulkan& vulkan, + const ShaderPassDescription& shaderPassDescription, + const PipelineLayout& pipelineLayout, + const PipelineVertexInputState& pipelineVertexInputState, + const PipelineRasterizationState& pipelineRasterizationState, + const SpecializationConstants& specializationConstants, + const ShaderModules& shaderModules, + const RenderContext& renderContext, + Msaa msaa ); \ No newline at end of file diff --git a/framework/code/material/vulkan/pipelineLayout.cpp b/framework/code/material/vulkan/pipelineLayout.cpp index c2f92d2..2258b68 100644 --- a/framework/code/material/vulkan/pipelineLayout.cpp +++ b/framework/code/material/vulkan/pipelineLayout.cpp @@ -1,14 +1,15 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "pipelineLayout.hpp" -#include "material/descriptorSetLayout.hpp" +#include "descriptorSetLayout.hpp" #include "vulkan/vulkan.hpp" +#include "texture/sampler.hpp" PipelineLayout::PipelineLayout() noexcept {} @@ -33,8 +34,9 @@ void PipelineLayout::Destroy(Vulkan& vulkan) } } -bool PipelineLayout::Init(Vulkan& vulkan, const std::span descriptorSetLayouts) +bool PipelineLayout::Init(Vulkan& vulkan, const std::span> descriptorSetLayouts, const std::span rootSamplers) { + assert(rootSamplers.empty()); // not supported in Vulkan std::vector vkDescriptorSetLayouts; vkDescriptorSetLayouts.reserve(descriptorSetLayouts.size()); for (const auto& descriptorSetLayout : descriptorSetLayouts) diff --git a/framework/code/material/vulkan/pipelineLayout.hpp b/framework/code/material/vulkan/pipelineLayout.hpp index 4db4ba5..3ae9838 100644 --- a/framework/code/material/vulkan/pipelineLayout.hpp +++ b/framework/code/material/vulkan/pipelineLayout.hpp @@ -1,20 +1,21 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include -#include +#include #include #include "material/pipelineLayout.hpp" // Forward declarations class Vulkan; -class DescriptorSetLayout; +template class DescriptorSetLayout; +struct CreateSamplerObjectInfo; /// Simple wrapper around VkPipelineLayout. @@ -31,7 +32,9 @@ class PipelineLayout PipelineLayout(PipelineLayout&&) noexcept; ~PipelineLayout(); - bool Init(Vulkan& vulkan, const std::span); + operator bool() const { return m_pipelineLayout != VK_NULL_HANDLE; } + + bool Init(Vulkan& vulkan, const std::span>, const std::span); bool Init(Vulkan& vulkan, const std::span vkDescriptorSetLayouts); void Destroy(Vulkan& vulkan); diff --git a/framework/code/material/vulkan/pipelineVertexInputState.cpp b/framework/code/material/vulkan/pipelineVertexInputState.cpp index 543f19b..80a92c0 100644 --- a/framework/code/material/vulkan/pipelineVertexInputState.cpp +++ b/framework/code/material/vulkan/pipelineVertexInputState.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,7 +11,8 @@ #include "vertexDescription.hpp" -PipelineVertexInputState::PipelineVertexInputState(const ShaderDescription& shaderDescription, const std::vector& buffersToBind) noexcept : mVisci{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO } +PipelineVertexInputState::PipelineVertexInputState(const ShaderDescription& shaderDescription, const std::vector& buffersToBind) noexcept + : mVisci{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO } { mBindings.reserve(buffersToBind.size()); uint32_t location = 0; @@ -44,3 +45,26 @@ PipelineVertexInputState::PipelineVertexInputState(PipelineVertexInputSt mVisci = other.mVisci; other.mVisci = {}; } + + +PipelineRasterizationState::PipelineRasterizationState( const ShaderPassDescription& shaderPassDescription ) noexcept + : mPipelineRasterizationStateCreateInfo() +{ + const auto& fixedFunctionSettings = shaderPassDescription.m_fixedFunctionSettings; + + //// State for rasterization, such as polygon fill mode is defined. + mPipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + mPipelineRasterizationStateCreateInfo.polygonMode = VK_POLYGON_MODE_FILL; + mPipelineRasterizationStateCreateInfo.cullMode = (fixedFunctionSettings.cullBackFace ? VK_CULL_MODE_BACK_BIT : 0) | (fixedFunctionSettings.cullFrontFace ? VK_CULL_MODE_FRONT_BIT : 0); + mPipelineRasterizationStateCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + mPipelineRasterizationStateCreateInfo.depthClampEnable = fixedFunctionSettings.depthClampEnable ? VK_TRUE : VK_FALSE; + mPipelineRasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE; + mPipelineRasterizationStateCreateInfo.depthBiasEnable = fixedFunctionSettings.depthBiasEnable ? VK_TRUE : VK_FALSE; + if (fixedFunctionSettings.depthBiasEnable) + { + mPipelineRasterizationStateCreateInfo.depthBiasConstantFactor = fixedFunctionSettings.depthBiasConstant; + mPipelineRasterizationStateCreateInfo.depthBiasClamp = fixedFunctionSettings.depthBiasClamp; + mPipelineRasterizationStateCreateInfo.depthBiasSlopeFactor = fixedFunctionSettings.depthBiasSlope; + } + mPipelineRasterizationStateCreateInfo.lineWidth = 1.0f; +} diff --git a/framework/code/material/vulkan/pipelineVertexInputState.hpp b/framework/code/material/vulkan/pipelineVertexInputState.hpp index a10e692..45d84d4 100644 --- a/framework/code/material/vulkan/pipelineVertexInputState.hpp +++ b/framework/code/material/vulkan/pipelineVertexInputState.hpp @@ -1,17 +1,18 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include -#include -#include "material/pipelineVertexInputState.hpp" +#include +#include "../pipelineVertexInputState.hpp" // Forward declarations +class ShaderPassDescription; class ShaderDescription; class Vulkan; @@ -19,7 +20,7 @@ class Vulkan; /// Helper for making VkPipelineVertexInputStateCreateInfo. /// @ingroup Material template<> -class PipelineVertexInputState +class PipelineVertexInputState : public PipelineVertexInputStateBase { PipelineVertexInputState(const PipelineVertexInputState&) = delete; PipelineVertexInputState& operator=(const PipelineVertexInputState&) = delete; @@ -33,3 +34,16 @@ class PipelineVertexInputState std::vector mAttributes; }; + +template<> +class PipelineRasterizationState : public PipelineRasterizationStateBase +{ + PipelineRasterizationState( const PipelineRasterizationState& ) = delete; + PipelineRasterizationState& operator=( const PipelineRasterizationState& ) = delete; +public: + PipelineRasterizationState( const ShaderPassDescription& shaderPassDescription ) noexcept; + + VkPipelineRasterizationStateCreateInfo mPipelineRasterizationStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO + }; +}; diff --git a/framework/code/material/vulkan/shader.cpp b/framework/code/material/vulkan/shader.cpp index 4393b94..0d94ad6 100644 --- a/framework/code/material/vulkan/shader.cpp +++ b/framework/code/material/vulkan/shader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,3 +11,9 @@ #include "material/descriptorSetLayout.hpp" #include "pipelineLayout.hpp" #include + +ShaderPassBase::ShaderPassBase( ShaderPassBase&& other ) noexcept + : m_shaderPassDescription( other.m_shaderPassDescription ) +{ + assert( 0 ); // should not use this function +} diff --git a/framework/code/material/vulkan/shader.hpp b/framework/code/material/vulkan/shader.hpp index 79232ec..edeb425 100644 --- a/framework/code/material/vulkan/shader.hpp +++ b/framework/code/material/vulkan/shader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,14 +12,15 @@ #include #include #include -#include -#include "material/shader.hpp" +#include #include "pipelineLayout.hpp" #include "pipelineVertexInputState.hpp" -#include "material/specializationConstantsLayout.hpp" +#include "specializationConstantsLayout.hpp" +#include "shaderModule.hpp" +#include "../shader.hpp" // forward declarations -class DescriptorSetLayout; +class DescriptorSetLayoutBase; class ShaderDescription; -template class ShaderModuleT; +template class ShaderModule; class ShaderPassDescription; diff --git a/framework/code/material/vulkan/shaderManager.hpp b/framework/code/material/vulkan/shaderManager.hpp new file mode 100644 index 0000000..dbb2fdb --- /dev/null +++ b/framework/code/material/vulkan/shaderManager.hpp @@ -0,0 +1,11 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "shader.hpp" +#include "../shaderManagerT.hpp" diff --git a/framework/code/material/vulkan/shaderModule.cpp b/framework/code/material/vulkan/shaderModule.cpp index 2aa8cb0..5522143 100644 --- a/framework/code/material/vulkan/shaderModule.cpp +++ b/framework/code/material/vulkan/shaderModule.cpp @@ -1,43 +1,41 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "shaderModule.hpp" -#include #include -#include #include "material/shaderDescription.hpp" #include "vertexDescription.hpp" #include "vulkan/vulkan.hpp" #include "system/assetManager.hpp" -ShaderModuleT::ShaderModuleT() noexcept : ShaderModule() +ShaderModule::ShaderModule() noexcept : ShaderModuleBase() , m_shader(VK_NULL_HANDLE) { } /// Destructor. Will assert if a shader was loaded and the matching Destroy was not called. -ShaderModuleT::~ShaderModuleT() +ShaderModule::~ShaderModule() { assert(m_shader == VK_NULL_HANDLE); } -void ShaderModuleT::Destroy(Vulkan& vulkan) +void ShaderModule::Destroy(Vulkan& vulkan) { if (m_shader != VK_NULL_HANDLE) { vkDestroyShaderModule(vulkan.m_VulkanDevice, m_shader, nullptr); m_shader = VK_NULL_HANDLE; } - ShaderModule::Destroy(); + ShaderModuleBase::Destroy(); } -bool ShaderModuleT::Load(Vulkan& vulkan, AssetManager& assetManager, const std::string& filename) +bool ShaderModule::Load(Vulkan& vulkan, AssetManager& assetManager, const std::string& filename) { bool success = true; Destroy(vulkan); @@ -74,7 +72,7 @@ bool ShaderModuleT::Load(Vulkan& vulkan, AssetManager& assetManager, con return success; } -bool ShaderModuleT::Load(Vulkan& vulkan, AssetManager& assetManager, const ShaderPassDescription& shaderDescription, const ShaderType shaderType) +bool ShaderModule::Load(Vulkan& vulkan, AssetManager& assetManager, const ShaderPassDescription& shaderDescription, const ShaderType shaderType) { const std::string* shaderFileName = nullptr; switch (shaderType) diff --git a/framework/code/material/vulkan/shaderModule.hpp b/framework/code/material/vulkan/shaderModule.hpp index 4c28559..9109148 100644 --- a/framework/code/material/vulkan/shaderModule.hpp +++ b/framework/code/material/vulkan/shaderModule.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,12 +12,12 @@ #include #include #include -#include +#include #include "material/shaderModule.hpp" // Forward declarations class AssetManager; -class DescriptorSetLayout; +class DescriptorSetLayoutBase; class ShaderPassDescription; class VertexDescription; class VertexFormat; @@ -26,13 +26,13 @@ class Vulkan; /// Wrapper around a Vulkan VkShaderModule. /// @ingroup Material template<> -class ShaderModuleT : public ShaderModule +class ShaderModule : public ShaderModuleBase { - ShaderModuleT(const ShaderModuleT&) = delete; - ShaderModuleT& operator=(const ShaderModuleT&) = delete; + ShaderModule(const ShaderModule&) = delete; + ShaderModule& operator=(const ShaderModule&) = delete; public: - ShaderModuleT() noexcept; - ~ShaderModuleT(); + ShaderModule() noexcept; + ~ShaderModule(); /// Free up the vkShaderModule resource. void Destroy(Vulkan&); diff --git a/framework/code/material/vulkan/specializationConstants.cpp b/framework/code/material/vulkan/specializationConstants.cpp new file mode 100644 index 0000000..abe2803 --- /dev/null +++ b/framework/code/material/vulkan/specializationConstants.cpp @@ -0,0 +1,51 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "specializationConstants.hpp" + + +bool SpecializationConstants::Init( const SpecializationConstantsLayout& layout, const std::span constants ) +{ + const auto& layoutMapEntry = layout.GetVkSpecializationMapEntry(); + assert( layoutMapEntry.size() == constants.size() ); + if (layoutMapEntry.empty()) + { + mSpecializationData.reset(); + return true; + } + + std::span specializationConstantsRaw { new std::byte[layout.GetBufferSize()], layout.GetBufferSize() }; // unsafe - raw pointer in the span! + + for (auto constantIdx = 0; constantIdx < layoutMapEntry.size(); ++constantIdx) + { + const auto& constantLayout = layoutMapEntry[constantIdx]; + + // copy the loaded constant data into the constant buffer + std::span constantDataRaw = constants[constantIdx].getUnsafeData(); + assert( constantLayout.size == constantDataRaw.size() ); + std::copy( constantDataRaw.begin(), constantDataRaw.end(), specializationConstantsRaw.begin() + constantLayout.offset ); + } + + VkSpecializationInfo vkSpecializationInfo {}; + vkSpecializationInfo.mapEntryCount = (uint32_t) layout.GetVkSpecializationMapEntry().size(); + vkSpecializationInfo.pMapEntries = layout.GetVkSpecializationMapEntry().data(); + vkSpecializationInfo.dataSize = specializationConstantsRaw.size(); + vkSpecializationInfo.pData = specializationConstantsRaw.data(); // move ownership of the allocated buffer. + + mSpecializationData.emplace().specializationInfo = vkSpecializationInfo; + + return true; +} + +SpecializationConstants::VulkanSpecializationData::VulkanSpecializationData( SpecializationConstants::VulkanSpecializationData&& other ) noexcept : specializationInfo( other.specializationInfo )/*dumb move*/ +{ + other.specializationInfo.mapEntryCount = 0; + other.specializationInfo.pMapEntries = nullptr; + other.specializationInfo.dataSize = 0; + other.specializationInfo.pData = nullptr; +} diff --git a/framework/code/material/vulkan/specializationConstants.hpp b/framework/code/material/vulkan/specializationConstants.hpp new file mode 100644 index 0000000..c5c245f --- /dev/null +++ b/framework/code/material/vulkan/specializationConstants.hpp @@ -0,0 +1,45 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include "../specializationConstants.hpp" +#include "specializationConstantsLayout.hpp" + +// Forward declarations + + +/// Specialization constant data for a pipeline. +/// @ingroup Material +template<> +class SpecializationConstants final +{ + SpecializationConstants( const SpecializationConstants& ) = delete; + SpecializationConstants operator=( const SpecializationConstants& ) = delete; +public: + SpecializationConstants( SpecializationConstants&& ) noexcept = default; + SpecializationConstants() noexcept = default; + bool Init(const SpecializationConstantsLayout& layout, const std::span constants); + + const VkSpecializationInfo* GetVkSpecializationInfo() const { return mSpecializationData.has_value() ? &mSpecializationData.value().specializationInfo : nullptr; } + +private: + struct VulkanSpecializationData + { + VulkanSpecializationData( const VulkanSpecializationData& ) = delete; + VulkanSpecializationData operator=( const VulkanSpecializationData& ) = delete; + + VulkanSpecializationData() noexcept : specializationInfo() {} + ~VulkanSpecializationData() { delete[] (std::byte*) specializationInfo.pData/*we own just the data*/; specializationInfo.pData = nullptr; } + VulkanSpecializationData( VulkanSpecializationData&& ) noexcept; + VkSpecializationInfo specializationInfo; + }; + // container for specialization data prepared for vulkan use! + std::optional mSpecializationData; +}; diff --git a/framework/code/material/vulkan/specializationConstantsLayout.cpp b/framework/code/material/vulkan/specializationConstantsLayout.cpp index 6235d68..773942b 100644 --- a/framework/code/material/vulkan/specializationConstantsLayout.cpp +++ b/framework/code/material/vulkan/specializationConstantsLayout.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/material/vulkan/specializationConstantsLayout.hpp b/framework/code/material/vulkan/specializationConstantsLayout.hpp index 2f6762b..37e8f5e 100644 --- a/framework/code/material/vulkan/specializationConstantsLayout.hpp +++ b/framework/code/material/vulkan/specializationConstantsLayout.hpp @@ -1,15 +1,15 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include -#include -#include "material/specializationConstantsLayout.hpp" +#include +#include "../specializationConstantsLayout.hpp" #include // Forward declarations @@ -18,7 +18,7 @@ class Vulkan; /// Simple wrapper around collection of VkSpecializationMapEntry (for VkSpecializationInfo::pMapEntries). /// @ingroup Material template<> -class SpecializationConstantsLayout +class SpecializationConstantsLayout final { SpecializationConstantsLayout( const SpecializationConstantsLayout& ) = delete; SpecializationConstantsLayout& operator=( const SpecializationConstantsLayout& ) = delete; diff --git a/framework/code/material/vulkan/vertexDescription.cpp b/framework/code/material/vulkan/vertexDescription.cpp index ce00a7e..6457160 100644 --- a/framework/code/material/vulkan/vertexDescription.cpp +++ b/framework/code/material/vulkan/vertexDescription.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -33,6 +33,8 @@ VkFormat VertexDescription::VkFormatFromElementType( const VertexFormat::Element switch(elementType.type) { case VertexFormat::Element::ElementType::t::Int32: return VK_FORMAT_R32_SINT; + case VertexFormat::Element::ElementType::t::UInt32: + return VK_FORMAT_R32_UINT; case VertexFormat::Element::ElementType::t::Float: return VK_FORMAT_R32_SFLOAT; case VertexFormat::Element::ElementType::t::Boolean: @@ -45,6 +47,8 @@ VkFormat VertexDescription::VkFormatFromElementType( const VertexFormat::Element return VK_FORMAT_R32G32B32A32_SFLOAT; case VertexFormat::Element::ElementType::t::Int16: return VK_FORMAT_R16_SINT; + case VertexFormat::Element::ElementType::t::UInt16: + return VK_FORMAT_R16_UINT; case VertexFormat::Element::ElementType::t::Float16: return VK_FORMAT_R16_SFLOAT; case VertexFormat::Element::ElementType::t::F16Vec2: @@ -53,6 +57,30 @@ VkFormat VertexDescription::VkFormatFromElementType( const VertexFormat::Element return VK_FORMAT_R16G16B16_SFLOAT; case VertexFormat::Element::ElementType::t::F16Vec4: return VK_FORMAT_R16G16B16A16_SFLOAT; + case VertexFormat::Element::ElementType::t::I16Vec2: + return VK_FORMAT_R16G16_SINT; + case VertexFormat::Element::ElementType::t::I16Vec3: + return VK_FORMAT_R16G16B16_SINT; + case VertexFormat::Element::ElementType::t::I16Vec4: + return VK_FORMAT_R16G16B16A16_SINT; + case VertexFormat::Element::ElementType::t::U16Vec2: + return VK_FORMAT_R16G16_UINT; + case VertexFormat::Element::ElementType::t::U16Vec3: + return VK_FORMAT_R16G16B16_UINT; + case VertexFormat::Element::ElementType::t::U16Vec4: + return VK_FORMAT_R16G16B16A16_UINT; + case VertexFormat::Element::ElementType::t::IVec2: + return VK_FORMAT_R32G32_SINT; + case VertexFormat::Element::ElementType::t::IVec3: + return VK_FORMAT_R32G32B32_SINT; + case VertexFormat::Element::ElementType::t::IVec4: + return VK_FORMAT_R32G32B32A32_SINT; + case VertexFormat::Element::ElementType::t::UVec2: + return VK_FORMAT_R32G32_UINT; + case VertexFormat::Element::ElementType::t::UVec3: + return VK_FORMAT_R32G32B32_UINT; + case VertexFormat::Element::ElementType::t::UVec4: + return VK_FORMAT_R32G32B32A32_UINT; default: assert(0); diff --git a/framework/code/material/vulkan/vertexDescription.hpp b/framework/code/material/vulkan/vertexDescription.hpp index b982bc9..b2f34f0 100644 --- a/framework/code/material/vulkan/vertexDescription.hpp +++ b/framework/code/material/vulkan/vertexDescription.hpp @@ -1,14 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include "material/vertexFormat.hpp" -#include +#include /// Describes the Vulkan layout of a single vertex stream (not the buffer, just one of the the contained vertices). /// @ingroup Material diff --git a/framework/code/memory/androidHardwareBuffer.cpp b/framework/code/memory/androidHardwareBuffer.cpp index 47ae108..a265363 100644 --- a/framework/code/memory/androidHardwareBuffer.cpp +++ b/framework/code/memory/androidHardwareBuffer.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/memory/androidHardwareBuffer.hpp b/framework/code/memory/androidHardwareBuffer.hpp index def8754..28fba1c 100644 --- a/framework/code/memory/androidHardwareBuffer.hpp +++ b/framework/code/memory/androidHardwareBuffer.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/memory/buffer.hpp b/framework/code/memory/buffer.hpp index f97d812..4e67f35 100644 --- a/framework/code/memory/buffer.hpp +++ b/framework/code/memory/buffer.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -16,23 +16,23 @@ struct AHardwareBuffer_Desc; struct AHardwareBuffer; template class MemoryManager; template class MemoryCpuMappedUntyped; -template class BufferT; +template class Buffer; template class MemoryCpuMapped; /// @brief Base class (virtual) for memory buffers. Platform agnostic. -class Buffer +class BufferBase { - Buffer(const Buffer&) = delete; - Buffer& operator=(const Buffer&) = delete; + BufferBase(const BufferBase&) = delete; + BufferBase& operator=(const BufferBase&) = delete; protected: - Buffer() noexcept {}; + BufferBase() noexcept {}; public: - virtual ~Buffer() {} + virtual ~BufferBase() {} virtual void Destroy() = 0; - Buffer(Buffer&&) noexcept = default; - Buffer& operator=(Buffer&&) noexcept = default; + BufferBase(BufferBase&&) noexcept = default; + BufferBase& operator=(BufferBase&&) noexcept = default; }; @@ -43,7 +43,7 @@ class MapGuard : public MemoryCpuMapped MapGuard& operator=(const MapGuard&) = delete; MapGuard& operator=(MapGuard&& other) = delete; public: - MapGuard( BufferT< T_GFXAPI>& buffer, MemoryCpuMapped mapped) + MapGuard( Buffer< T_GFXAPI>& buffer, MemoryCpuMapped mapped) : MemoryCpuMapped(std::move(mapped)) // Only ever one owner of a MemoryCpuMapped , mBuffer(&buffer) // not passed as a pointer so we know it's never initialized null {} @@ -67,7 +67,7 @@ class MapGuard : public MemoryCpuMapped other.mBuffer = nullptr; } private: - BufferT< T_GFXAPI>* mBuffer; + Buffer< T_GFXAPI>* mBuffer; }; @@ -75,16 +75,16 @@ class MapGuard : public MemoryCpuMapped /// Expected for only the specialized version to be used! /// @ingroup Memory template -class BufferT : public Buffer +class Buffer : public BufferBase { - BufferT& operator=(const BufferT&) = delete; - BufferT(const BufferT&) = delete; + Buffer& operator=(const Buffer&) = delete; + Buffer(const Buffer&) = delete; protected: public: - BufferT() noexcept = delete; // this template class must be specialized - virtual ~BufferT() = delete; // this template class must be specialized - BufferT(BufferT&&) noexcept = delete; // this template class must be specialized - BufferT& operator=(BufferT&&) noexcept = delete;// this template class must be specialized + Buffer() noexcept = delete; // this template class must be specialized + virtual ~Buffer() = delete; // this template class must be specialized + Buffer(Buffer&&) noexcept = delete; // this template class must be specialized + Buffer& operator=(Buffer&&) noexcept = delete;// this template class must be specialized /// destroy buffer and leave in a state where it could be re-initialized void Destroy() override = delete; // this template class must be specialized @@ -93,6 +93,6 @@ class BufferT : public Buffer template MapGuard Map() = delete; // this template class must be specialized - static_assert(sizeof(BufferT) != sizeof(Buffer)); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof(Buffer) != sizeof(BufferBase)); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/memory/drawIndirectBuffer.hpp b/framework/code/memory/drawIndirectBuffer.hpp index 0f58f17..6a19179 100644 --- a/framework/code/memory/drawIndirectBuffer.hpp +++ b/framework/code/memory/drawIndirectBuffer.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,18 +12,25 @@ /// Templated (by graphics api) buffer containing draw indirect commands /// @ingroup Memory template -class DrawIndirectBuffer: public BufferT +class DrawIndirectBuffer: public Buffer { DrawIndirectBuffer(const DrawIndirectBuffer&) = delete; DrawIndirectBuffer& operator=(const DrawIndirectBuffer&) = delete; public: - DrawIndirectBuffer(bool indexed) noexcept; + enum class eType { + Draw, + IndexedDraw, + MeshTasks + }; + + DrawIndirectBuffer(DrawIndirectBuffer::eType indirectBufferType) noexcept; DrawIndirectBuffer(DrawIndirectBuffer&&) noexcept; DrawIndirectBuffer& operator=(DrawIndirectBuffer&&) noexcept; ~DrawIndirectBuffer(); - /// Initialization + /// Buffer initialization with a prequel block (and then a block of type T_DRAW data for each potential draw) template bool Initialize(MemoryManager* pManager, size_t numDraws, const T_DRAW* initialData, const T_PREQUEL* initialPrequelData, const BufferUsageFlags usage = BufferUsageFlags::Indirect); + /// Buffer initialization with a block of type T_DRAW data for each potential draw template bool Initialize(MemoryManager* pManager, size_t numDraws, const T_DRAW* initialData, const BufferUsageFlags usage = BufferUsageFlags::Indirect); /// destroy buffer and leave in a state where it could be re-initialized @@ -39,8 +46,9 @@ class DrawIndirectBuffer: public BufferT /// Map this buffer to the cpu and return a guard object (automatically unmaps when it goes out of scope) template MapGuard Map(); - bool IsIndexed() const { return mIndexed; } - uint32_t GetDrawCommandBytes() const;// { return (mIndexed ? sizeof(VkDrawIndexedIndirectCommand) : sizeof(VkDrawIndirectCommand)); } + //bool IsIndexed() const { return mIndexed; } + eType GetIndirectBufferType() const { return mIndirectBufferType; } + uint32_t GetDrawCommandBytes() const; auto GetNumDraws() const { return mNumDraws; } auto GetBufferOffset() const { return mPrequelBytes; } @@ -49,18 +57,18 @@ class DrawIndirectBuffer: public BufferT bool Initialize(MemoryManager* pManager, size_t numDraws, const void* initialData, const BufferUsageFlags usage, const void* prequelData, uint32_t prequelBytes); private: - size_t mNumDraws = 0; - uint32_t mPrequelBytes = 0; // buffer bytes before the index data begins. - const bool mIndexed; + size_t mNumDraws = 0; + uint32_t mPrequelBytes = 0; // buffer bytes before the index data begins. + const eType mIndirectBufferType; }; template -DrawIndirectBuffer::DrawIndirectBuffer(bool indexed) noexcept : BufferT(), mIndexed(indexed) +DrawIndirectBuffer::DrawIndirectBuffer(eType indirectBufferType) noexcept : Buffer(), mIndirectBufferType(indirectBufferType) {} template -DrawIndirectBuffer::DrawIndirectBuffer(DrawIndirectBuffer&& other) noexcept : mIndexed(other.mIndexed) +DrawIndirectBuffer::DrawIndirectBuffer(DrawIndirectBuffer&& other) noexcept : mIndirectBufferType(other.mIndirectBufferType ) { *this = std::move(other); } @@ -68,10 +76,10 @@ DrawIndirectBuffer::DrawIndirectBuffer(DrawIndirectBuffer&& other) noe template DrawIndirectBuffer& DrawIndirectBuffer::operator=(DrawIndirectBuffer&& other) noexcept { - BufferT::operator=(std::move(other)); + Buffer::operator=(std::move(other)); if (&other != this) { - assert(mIndexed == other.mIndexed); + assert(mIndirectBufferType == other.mIndirectBufferType); mNumDraws = other.mNumDraws; other.mNumDraws = 0; mPrequelBytes = other.mPrequelBytes; @@ -97,16 +105,68 @@ bool DrawIndirectBuffer::Initialize(MemoryManager* pManager, return Initialize(pManager, numDraws, (const void*)initialData, usage, prequelData, sizeof(T_PREQUEL)); } +template +bool DrawIndirectBuffer::Initialize(MemoryManager* pManager, size_t numDraws, const void* initialData, const BufferUsageFlags usage, const void* prequelData, uint32_t prequelBytes ) +{ + mNumDraws = numDraws; + mPrequelBytes = prequelBytes; + + MemoryUsage memoryUsage = initialData ? MemoryUsage::CpuToGpu : MemoryUsage::GpuExclusive; + if ((usage & BufferUsageFlags::TransferSrc) != 0) + memoryUsage = MemoryUsage::CpuToGpu; + + if (!Buffer::Initialize( pManager, GetDrawCommandBytes() * mNumDraws + mPrequelBytes, usage, memoryUsage )) + { + return false; + } + + if (initialData) + { + auto mappedGuard = Buffer::template Map(); + auto initialDataSize = GetDrawCommandBytes() * mNumDraws; + if (prequelBytes) + memcpy( mappedGuard.data(), prequelData, prequelBytes ); + else + memset( mappedGuard.data(), 0, prequelBytes ); + memcpy( mappedGuard.data() + prequelBytes, initialData, initialDataSize ); + } + return true; +} + +template +void DrawIndirectBuffer::Destroy() +{ + mNumDraws = 0; + Buffer::Destroy(); +} + +template +DrawIndirectBuffer DrawIndirectBuffer::Copy( BufferUsageFlags newUsageFlags ) +{ + if (newUsageFlags == BufferUsageFlags::Unknown) + newUsageFlags = Buffer::template mBufferUsageFlags<>; + DrawIndirectBuffer copy( GetIndirectBufferType() ); + auto data = MapVoid(); + if (copy.Initialize( Buffer::template mManager<>, GetNumDraws(), static_cast(data.data()) + mPrequelBytes, newUsageFlags, data.data(), mPrequelBytes )) + { + return copy; + } + else + { + return {GetIndirectBufferType()}; + } +} + template template MapGuard DrawIndirectBuffer::Map() { - assert(mPrequelBytes == 0); // prequel bytes not yet supported in map - assert(sizeof(T) == GetDrawCommandBytes()); - return BufferT::template Map(); + assert( mPrequelBytes == 0 ); // prequel bytes not yet supported in map + assert( sizeof( T ) == GetDrawCommandBytes() ); + return Buffer::template Map(); } template MapGuard DrawIndirectBuffer::MapVoid() { - return BufferT::template Map(); + return Buffer::template Map(); } diff --git a/framework/code/memory/dx12/bufferObject.cpp b/framework/code/memory/dx12/bufferObject.cpp new file mode 100644 index 0000000..4f925e8 --- /dev/null +++ b/framework/code/memory/dx12/bufferObject.cpp @@ -0,0 +1,120 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "bufferObject.hpp" +#include "memoryManager.hpp" +#include +//#include + +Buffer::Buffer() noexcept +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +Buffer::Buffer(Buffer&& other) noexcept +{ + *this = std::move(other); +} + +/////////////////////////////////////////////////////////////////////////////// + +Buffer& Buffer::operator=(Buffer&& other) noexcept +{ + if (this != &other) + { + mManager = other.mManager; + other.mManager = nullptr; + mAllocatedBuffer = std::move(other.mAllocatedBuffer); + mBufferUsageFlags = other.mBufferUsageFlags; + other.mBufferUsageFlags = BufferUsageFlags::Unknown; + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + +Buffer::~Buffer() +{ + Destroy(); +} + +/////////////////////////////////////////////////////////////////////////////// + +Buffer::operator bool() const +{ + return !!mAllocatedBuffer; +} + +/////////////////////////////////////////////////////////////////////////////// +template< class Enum > +constexpr std::underlying_type_t to_underlying(Enum e) noexcept { return static_cast>(e); }; + +bool Buffer::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, const void* initialData) +{ + MemoryUsage memoryUsage = initialData ? MemoryUsage::CpuToGpu : MemoryUsage::GpuExclusive; + if ((to_underlying(bufferUsageFlags) & to_underlying(BufferUsageFlags::TransferSrc)) != 0) + memoryUsage = MemoryUsage::CpuToGpu; + + if (!Initialize(pManager, size, bufferUsageFlags, memoryUsage)) + { + return false; + } + + if (initialData) + { + auto mappedGuard = Map(); + memcpy(mappedGuard.data(), initialData, size); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Buffer::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, MemoryUsage memoryUsage) +{ + assert(!mManager); + if (!pManager) + { + return false; + } + mManager = pManager; + + mAllocatedBuffer = mManager->CreateBuffer(size, bufferUsageFlags, memoryUsage); + mBufferUsageFlags = bufferUsageFlags; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Buffer::Destroy() +{ + if (!mManager) + { + assert(!mAllocatedBuffer); // ensure we don't have an orphaned buffer (somehow) + return; + } + mManager->Destroy( std::move(mAllocatedBuffer) ); + mManager = nullptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Buffer::Unmap(MemoryCpuMappedUntyped buffer) +{ + mManager->Unmap(mAllocatedBuffer, std::move(buffer)); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint64_t Buffer::GetResourceGPUVirtualAddress() +{ + return mAllocatedBuffer.GetResource()->GetGPUVirtualAddress(); /*GetGPUVirtualAddress is not const, so this function cannot be either */ +} diff --git a/framework/code/memory/dx12/bufferObject.hpp b/framework/code/memory/dx12/bufferObject.hpp new file mode 100644 index 0000000..2354d9e --- /dev/null +++ b/framework/code/memory/dx12/bufferObject.hpp @@ -0,0 +1,58 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include "memoryManager.hpp" // dx12 +#include "memoryMapped.hpp" // dx12 +#include "memory/buffer.hpp" // agnostic +#include "memory/memory.hpp" // agnostic + + +/// Template specialization for creating and holding Dx12 graphics memory buffer objects. +/// @ingroup Memory +template<> +class Buffer : public BufferBase +{ +public: + Buffer() noexcept; + virtual ~Buffer(); + Buffer(Buffer&&) noexcept; + Buffer& operator=(Buffer&&) noexcept; + + using MemoryManager = MemoryManager; + using MemoryCpuMappedUntyped = MemoryCpuMappedUntyped; + + explicit operator bool() const; + + bool Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, const void* initialData); + bool Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, MemoryUsage memoryUsage); + + /// destroy buffer and leave in a state where it could be re-initialized + void Destroy() override; + + /// Map this buffer to the cpu and return a guard object (automatically unmaps when it goes out of scope) + template + MapGuard Map() + { + assert(mManager); + return { *this, std::move(mManager->Map(mAllocatedBuffer)) }; + } + + uint64_t GetResourceGPUVirtualAddress(); + +protected: + template friend class MapGuard; + void Unmap(MemoryCpuMappedUntyped buffer); + +protected: + using MemoryAllocatedBuffer = MemoryAllocatedBuffer; + MemoryManager* mManager = nullptr; + MemoryAllocatedBuffer mAllocatedBuffer; + BufferUsageFlags mBufferUsageFlags = BufferUsageFlags::Unknown; +}; diff --git a/framework/code/memory/dx12/indexBufferObject.cpp b/framework/code/memory/dx12/indexBufferObject.cpp new file mode 100644 index 0000000..3eca5a4 --- /dev/null +++ b/framework/code/memory/dx12/indexBufferObject.cpp @@ -0,0 +1,35 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "indexBufferObject.hpp" + + +D3D12_INDEX_BUFFER_VIEW IndexBuffer::GetIndexBufferView() const +{ + DXGI_FORMAT format = DXGI_FORMAT_R16_UINT; + UINT formatBytes = 2; + switch (mIndexType) { + case IndexType::IndexU16: + format = DXGI_FORMAT_R16_UINT; + formatBytes = 2; + break; + case IndexType::IndexU32: + format = DXGI_FORMAT_R32_UINT; + formatBytes = 4; + break; + default: + assert(0); + break; + } + + return D3D12_INDEX_BUFFER_VIEW{ + .BufferLocation = mAllocatedBuffer.GetResource()->GetGPUVirtualAddress(), + .SizeInBytes = (UINT) GetNumIndices() * formatBytes, + .Format = format + }; +} diff --git a/framework/code/memory/dx12/indexBufferObject.hpp b/framework/code/memory/dx12/indexBufferObject.hpp new file mode 100644 index 0000000..9af8720 --- /dev/null +++ b/framework/code/memory/dx12/indexBufferObject.hpp @@ -0,0 +1,29 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +class Dx12; + +#include "memoryMapped.hpp" +#include "bufferObject.hpp" +#include "memory/indexBuffer.hpp" + + +// Template specialization for Dx12 Index buffer +template<> +class IndexBuffer final : public IndexBufferT +{ +public: + IndexBuffer(IndexType i) noexcept : IndexBufferT(i) {} + //IndexBuffer(VkIndexType) noexcept; + IndexBuffer(IndexBuffer&& o) noexcept = default; + IndexBuffer& operator=(IndexBuffer&& o) noexcept = default; + ~IndexBuffer() {} + + D3D12_INDEX_BUFFER_VIEW GetIndexBufferView() const; +}; diff --git a/framework/code/memory/dx12/memoryManager.cpp b/framework/code/memory/dx12/memoryManager.cpp new file mode 100644 index 0000000..d2467e1 --- /dev/null +++ b/framework/code/memory/dx12/memoryManager.cpp @@ -0,0 +1,174 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "memoryManager.hpp" +#include "memoryMapped.hpp" +#include "dx12/dx12.hpp" +#include "D3D12MemoryAllocator/include/D3D12MemAlloc.h" // we should be the only file to include this header! +#include + + +MemoryManager::MemoryManager() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryManager::~MemoryManager() +{ + Destroy(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MemoryManager::Initialize(IDXGIAdapter* adapter, ID3D12Device* device) +{ + assert(mDxmaAllocator == nullptr); + D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; + allocatorDesc.pDevice = device; + allocatorDesc.pAdapter = adapter; + + HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &mDxmaAllocator); + if (hr != S_OK) + { + mDxmaAllocator = nullptr; + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MemoryManager::Destroy() +{ + auto* const tmp = mDxmaAllocator; + mDxmaAllocator = nullptr; + if (tmp!=nullptr) + tmp->Release(); +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryManager::MemoryAllocatedBuffer MemoryManager::CreateBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage) +{ + assert(memoryUsage != MemoryUsage::Unknown); + assert(bufferUsage != BufferUsageFlags::Unknown); + + auto heapType = D3D12_HEAP_TYPE_DEFAULT; + switch (memoryUsage) { + case MemoryUsage::CpuToGpu: + heapType = D3D12_HEAP_TYPE_UPLOAD; + break; + case MemoryUsage::GpuToCpu: + heapType = D3D12_HEAP_TYPE_READBACK; + break; + case MemoryUsage::GpuExclusive: + heapType = D3D12_HEAP_TYPE_DEFAULT; + break; + default: + assert(0); + break; + } + if ((bufferUsage&BufferUsageFlags::Uniform) != 0) + size = (size + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1) & ~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT-1); + + const D3D12_RESOURCE_DESC resourceDesc = { .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = 0, + .Width = size, + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = {.Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE }; + D3D12MA::ALLOCATION_DESC allocationDesc = {.HeapType = heapType}; + MemoryAllocatedBuffer allocatedBuffer; + HRESULT hr = mDxmaAllocator->CreateResource(&allocationDesc, + &resourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + NULL, + &allocatedBuffer.allocation.allocation, + IID_PPV_ARGS(&allocatedBuffer.resource)); + if (hr != S_OK) + return {}; + + return allocatedBuffer; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryManager::MemoryAllocatedBuffer MemoryManager::CreateImage(const D3D12_RESOURCE_DESC& imageDesc, MemoryUsage memoryUsage, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE& clearValue) +{ + assert(memoryUsage != MemoryUsage::Unknown); + + auto heapType = D3D12_HEAP_TYPE_DEFAULT; + switch (memoryUsage) { + case MemoryUsage::CpuToGpu: + heapType = D3D12_HEAP_TYPE_UPLOAD; + break; + case MemoryUsage::GpuToCpu: + heapType = D3D12_HEAP_TYPE_READBACK; + break; + case MemoryUsage::GpuExclusive: + heapType = D3D12_HEAP_TYPE_DEFAULT; + break; + default: + assert(0); + break; + } + + D3D12MA::ALLOCATION_DESC allocationDesc = { .HeapType = heapType }; + MemoryAllocatedBuffer allocatedBuffer; + HRESULT hr = mDxmaAllocator->CreateResource(&allocationDesc, + &imageDesc, + initialResourceState, + clearValue.Format != DXGI_FORMAT_UNKNOWN ? &clearValue : nullptr, + &allocatedBuffer.allocation.allocation, + IID_PPV_ARGS(&allocatedBuffer.resource)); + if (hr != S_OK) + return {}; + + return allocatedBuffer; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MemoryManager::Destroy(MemoryAllocatedBuffer allocatedBuffer) +{ + if (allocatedBuffer) + { + allocatedBuffer.resource->Release(); + allocatedBuffer.resource = nullptr; + allocatedBuffer.allocation.allocation->Release(); + allocatedBuffer.allocation.allocation = nullptr; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MemoryManager::MapInternal(D3D12MA::Allocation* allocation, void** outCpuLocation) +{ + assert(outCpuLocation != nullptr); + assert(*outCpuLocation == nullptr); // mapped twice? + assert(allocation != nullptr); // Likely caused by a double mapping of this data + if (allocation->GetResource()->Map(0, nullptr, outCpuLocation) != S_OK) + { + outCpuLocation = nullptr; + assert(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MemoryManager::UnmapInternal(D3D12MA::Allocation* allocation, void* cpuLocation) +{ + assert(cpuLocation); + allocation->GetResource()->Unmap(0, nullptr); +} diff --git a/framework/code/memory/dx12/memoryManager.hpp b/framework/code/memory/dx12/memoryManager.hpp new file mode 100644 index 0000000..2e7001d --- /dev/null +++ b/framework/code/memory/dx12/memoryManager.hpp @@ -0,0 +1,95 @@ +//============================================================================= +// +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include "memory/memory.hpp" +#include "memory/memoryManager.hpp" +#include "memory/dx12/memoryMapped.hpp" +#include "material/materialPass.hpp" + +// forward declarations +class Dx12; +struct IDXGIAdapter; +struct ID3D12Device; +struct D3D12_CLEAR_VALUE; +struct D3D12_RESOURCE_DESC; +enum D3D12_RESOURCE_STATES; +namespace D3D12MA { + class Allocator; + class Allocation; +}; + + +/// Top level API for allocating memory buffers for use by DX12 +/// template specialization of MemoryManager. +/// @ingroup Memory +template<> +class MemoryManager +{ + MemoryManager(const MemoryManager&) = delete; + MemoryManager& operator=(const MemoryManager&) = delete; +public: + MemoryManager(); + ~MemoryManager(); + using tGfxApi = Dx12; + using MemoryAllocatedBuffer = MemoryAllocatedBuffer; + + /// Initialize the memory manager (must be initialized before using CreateBuffer etc) + /// @param EnableBufferDeviceAddress enable ability to call GetBufferDeviceAddress + /// @return true if successfully initialized + bool Initialize(IDXGIAdapter* adapter, ID3D12Device* device); + /// Destroy the memory manager (do before you destroy the Dx12 device) + void Destroy(); + + /// Create buffer in memory and create the associated Dx12 objects + MemoryAllocatedBuffer CreateBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage); + /// Create buffer in memory + MemoryAllocatedBuffer CreateImage(const D3D12_RESOURCE_DESC& imageDesc, MemoryUsage memoryUsage, D3D12_RESOURCE_STATES initialResourceState, const D3D12_CLEAR_VALUE& clearValue); + + /// Destruction of created buffer + void Destroy(MemoryAllocatedBuffer); + + /// Map a buffer to cpu memory + template + MemoryCpuMapped Map(MemoryAllocatedBuffer& buffer); + + /// Unmap a buffer from cpu memory + void Unmap(MemoryAllocatedBuffer& buffer, MemoryCpuMappedUntyped allocation); + +protected: + MemoryCpuMappedUntyped MapInt(MemoryAllocation allocation); +private: + void MapInternal(D3D12MA::Allocation*, void** outCpuLocation); + void UnmapInternal(D3D12MA::Allocation*, void* cpuLocation); + +private: + D3D12MA::Allocator* mDxmaAllocator = nullptr; +}; + +template +MemoryCpuMapped MemoryManager::Map(MemoryAllocatedBuffer& buffer) +{ + return MapInt(std::move(buffer.allocation)); +} + +inline void MemoryManager::Unmap(MemoryAllocatedBuffer& buffer, MemoryCpuMappedUntyped allocation) +{ + assert(allocation.mCpuLocation); + UnmapInternal(allocation.mAllocation.allocation, allocation.mCpuLocation); + allocation.mCpuLocation = nullptr; // clear ownership + buffer.allocation = std::move(allocation.mAllocation); +} + +inline MemoryCpuMappedUntyped MemoryManager::MapInt(MemoryAllocation allocation) +{ + MemoryCpuMappedUntyped guard(std::move(allocation)); + MapInternal(guard.mAllocation.allocation, &guard.mCpuLocation); + return guard; +} + diff --git a/framework/code/memory/dx12/memoryMapped.hpp b/framework/code/memory/dx12/memoryMapped.hpp new file mode 100644 index 0000000..8000ace --- /dev/null +++ b/framework/code/memory/dx12/memoryMapped.hpp @@ -0,0 +1,132 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include "../memoryMapped.hpp" + +// forward declarations +class Dx12; +struct ID3D12Resource; +namespace D3D12MA { + class Allocation; +}; + +/// Wraps the D3D12MA Allocation handle. Represents an allocation out of 'dx12' memory. Cannot be copied (only moved) - single owner. +/// Passed around between whomever is 'owner' of the allocation (when mapping etc) +/// @ingroup Memory +template<> +class MemoryAllocation final +{ + friend class MemoryManager; + friend class MemoryDx12AllocatedBuffer; +public: + MemoryAllocation(const MemoryAllocation&) = delete; + MemoryAllocation& operator=(const MemoryAllocation&) = delete; + MemoryAllocation() {} + MemoryAllocation(MemoryAllocation&& other) noexcept; + MemoryAllocation& operator=(MemoryAllocation&& other) noexcept; + ~MemoryAllocation() { assert(allocation == nullptr); } // protect accidental deletion (leak) + explicit operator bool() const { return allocation != nullptr; } +private: + //void clear() { allocation = nullptr; } + D3D12MA::Allocation* allocation = nullptr; +}; + + +/// Represents a memory resource allocated by D3D12MA +/// @ingroup Memory +template +class MemoryAllocatedBuffer final +{ + MemoryAllocatedBuffer(const MemoryAllocatedBuffer&) = delete; + MemoryAllocatedBuffer& operator=(const MemoryAllocatedBuffer&) = delete; +public: + friend class MemoryManager; + // Restrict MemoryAllocatedBuffer to not be duplicated and not accidentally deleted (leaking memory). + MemoryAllocatedBuffer(MemoryAllocatedBuffer&& other) noexcept; + MemoryAllocatedBuffer& operator=(MemoryAllocatedBuffer&& other) noexcept; + MemoryAllocatedBuffer() noexcept {} + ~MemoryAllocatedBuffer() { assert(resource == nullptr); } + auto* GetResource() const { return resource; } + auto* GetResource() { return resource; } + explicit operator bool() const { return static_cast(allocation); } +private: + MemoryAllocation allocation; + static_assert( std::is_pointer_v ); // expecting T_RESOURCETYPE is a pointer, eg ID3D12Resource* + T_RESOURCETYPE/*ID3D12Resource* */ resource = nullptr; +}; + + +// +// MemoryAllocation implementation +// +inline MemoryAllocation::MemoryAllocation(MemoryAllocation&& other) noexcept +{ + allocation = other.allocation; + other.allocation = nullptr; +} + +inline MemoryAllocation& MemoryAllocation::operator=(MemoryAllocation&& other) noexcept +{ + if (&other != this) { + assert(allocation==nullptr); + allocation = other.allocation; + other.allocation = nullptr; + } + return *this; +} + + +// +// MemoryDx12AllocatedBuffer implementation +// +template +inline MemoryAllocatedBuffer::MemoryAllocatedBuffer(MemoryAllocatedBuffer&& other) noexcept + : allocation(std::move(other.allocation)) +{ + resource = other.resource; + other.resource = 0; +} + +template +inline MemoryAllocatedBuffer& MemoryAllocatedBuffer::operator=(MemoryAllocatedBuffer&& other) noexcept +{ + if (this != &other) { + allocation = std::move(other.allocation); + assert(resource==nullptr); + resource = other.resource; + other.resource = 0; + } + return *this; +} + + +#if 0 +// +// MemoryCpuMappedUntyped implementation +// +inline MemoryCpuMappedUntyped::MemoryCpuMappedUntyped(MemoryAllocation&& memoryToMap) + : mAllocation(std::move(memoryToMap)) + , mCpuLocation(nullptr) +{ +} + +inline MemoryCpuMappedUntyped::MemoryCpuMappedUntyped(MemoryCpuMappedUntyped&& other) noexcept + : mAllocation(std::move(other.mAllocation)) + , mCpuLocation(other.mCpuLocation) +{ + other.mCpuLocation = nullptr; +} + +inline MemoryCpuMappedUntyped::~MemoryCpuMappedUntyped() +{ + assert(mCpuLocation == nullptr); // should have been unmapped through the memorymanager +} +#endif // 0 diff --git a/framework/code/memory/dx12/uniform.cpp b/framework/code/memory/dx12/uniform.cpp new file mode 100644 index 0000000..7f97349 --- /dev/null +++ b/framework/code/memory/dx12/uniform.cpp @@ -0,0 +1,98 @@ +//============================================================================= +// +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#include +#include "memory/memoryMapped.hpp" +#include "dx12/dx12.hpp" +#include "uniform.hpp" + + +//----------------------------------------------------------------------------- +bool CreateUniformBuffer(Dx12* pDx12, Uniform* pNewUniform, size_t dataSize, const void* const pData, BufferUsageFlags usage) +//----------------------------------------------------------------------------- +{ + auto& memoryManager = pDx12->GetMemoryManager(); + + // Create the memory buffer... + pNewUniform->buf = memoryManager.CreateBuffer(dataSize, usage, MemoryUsage::CpuToGpu); + + // If we have initial data, add it now + if (pData != nullptr) + { + UpdateUniformBuffer( pDx12, pNewUniform, dataSize, pData); + } + + return true; +} + +//----------------------------------------------------------------------------- +void UpdateUniformBuffer(Dx12* pDx12, Uniform* pUniform, size_t dataSize, const void* const pNewData) +//----------------------------------------------------------------------------- +{ + void* pMappedData = NULL; + auto mapped = pDx12->GetMemoryManager().Map(pUniform->buf); + if (mapped.data() == nullptr) + { + return; + } + + memcpy(mapped.data(), pNewData, dataSize); + + pDx12->GetMemoryManager().Unmap(pUniform->buf, std::move(mapped)); +} + + +//----------------------------------------------------------------------------- +void ReleaseUniformBuffer(Dx12* pDx12, Uniform* pUniform) +//----------------------------------------------------------------------------- +{ + if (pUniform->buf) + { + pDx12->GetMemoryManager().Destroy(std::move(pUniform->buf)); + } +} + + +//----------------------------------------------------------------------------- +bool CreateUniformBuffer(Dx12* pDx12, MemoryAllocatedBuffer& rNewUniformBuffer, size_t dataSize, const void* const pData, BufferUsageFlags usage) +//----------------------------------------------------------------------------- +{ + auto& memoryManager = pDx12->GetMemoryManager(); + + // Create the memory buffer... + rNewUniformBuffer = memoryManager.CreateBuffer(dataSize, usage, MemoryUsage::CpuToGpu); + + // If we have initial data, add it now + if (pData != nullptr) + { + UpdateUniformBuffer(pDx12, rNewUniformBuffer, dataSize, pData); + } + + return true; +} + +//----------------------------------------------------------------------------- +void UpdateUniformBuffer(Dx12* pDx12, MemoryAllocatedBuffer& rUniform, size_t dataSize, const void* const pNewData) +//----------------------------------------------------------------------------- +{ + auto mapped = pDx12->GetMemoryManager().Map(rUniform); + if (mapped.data() == nullptr) + { + return; + } + + memcpy(mapped.data(), pNewData, dataSize); + + pDx12->GetMemoryManager().Unmap(rUniform, std::move(mapped)); +} + +//----------------------------------------------------------------------------- +void ReleaseUniformBuffer(Dx12* pDx12, MemoryAllocatedBuffer& rUniform) +//----------------------------------------------------------------------------- +{ + pDx12->GetMemoryManager().Destroy(std::move(rUniform)); +} diff --git a/framework/code/memory/dx12/uniform.hpp b/framework/code/memory/dx12/uniform.hpp new file mode 100644 index 0000000..8d5fe25 --- /dev/null +++ b/framework/code/memory/dx12/uniform.hpp @@ -0,0 +1,116 @@ +//============================================================================= +// +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +/// @file uniform.hpp +/// @brief Dx12 Uniform buffer containers and support functions. + +#include +#include "memoryMapped.hpp" +#include "memory/uniform.hpp" + +#include + +// forward defines +class Dx12; +struct ID3D12Resource; + + +/// All the variables needed to create a uniform buffer +/// Template specialization of Uniform<> for Dx12 +template<> +struct Uniform +{ + Uniform() = default; + Uniform(const Uniform&) = delete; + Uniform& operator=(const Uniform&) = delete; + Uniform(Uniform&&) noexcept = default; + MemoryAllocatedBuffer buf; + //VkDescriptorBufferInfo bufferInfo; +}; + +/// Uniform buffer array that can be updated every frame without stomping the in-flight uniform buffers. +template +struct UniformArray +{ + UniformArray() = default; + UniformArray(const UniformArray&) = delete; + UniformArray& operator=(const UniformArray&) = delete; + UniformArray(UniformArray&&) noexcept { assert(0); } + std::array, T_NUM_BUFFERS> buf; + std::array bufferHandles{}; ///< copy of ID3D12Resource handles (for easy use in bindings etc) + constexpr size_t size() const { return T_NUM_BUFFERS; } +}; + +bool CreateUniformBuffer(Dx12*, Uniform* pNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform); +void UpdateUniformBuffer(Dx12*, Uniform* pUniform, size_t dataSize, const void* const pNewData); +void ReleaseUniformBuffer(Dx12*, Uniform* pUniform); + +template +bool CreateUniformBuffer(Dx12* pGfxApi, UniformT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) +{ + return CreateUniformBuffer(pGfxApi, &newUniform, sizeof(T), pData, usage); +} +template +void UpdateUniformBuffer(Dx12* pGfxApi, Uniform& uniform, const T& newData) +{ + return UpdateUniformBuffer(pGfxApi, &uniform, sizeof(T), &newData); +} +template +void UpdateUniformBuffer(Dx12* pGfxApi, UniformT& uniform, const TT& newData) +{ + static_assert(std::is_same::value, "UpdateUniformBuffer, uniform is different type to newData"); + return UpdateUniformBuffer(pGfxApi, static_cast&>(uniform), newData); +} + +bool CreateUniformBuffer(Dx12* pGfxApi, MemoryAllocatedBuffer& rNewUniformBuffer, size_t dataSize, const void* const pData, BufferUsageFlags usage); +void UpdateUniformBuffer(Dx12* pGfxApi, MemoryAllocatedBuffer& rUniform, size_t dataSize, const void* const pNewData); +void ReleaseUniformBuffer(Dx12* pGfxApi, MemoryAllocatedBuffer& rUniform); + +template +bool CreateUniformBuffer(Dx12* pGfxApi, UniformArray& rNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform) +{ + for (size_t i = 0; i < T_NUM_BUFFERS; ++i) + { + if (!CreateUniformBuffer(pGfxApi, rNewUniform.buf[i], dataSize, pData, usage)) + return false; + rNewUniform.bufferHandles[i] = rNewUniform.buf[i].GetResource(); + } + return true; +} +template +void UpdateUniformBuffer(Dx12* pGfxApi, UniformArray& rUniform, size_t dataSize, const void* const pNewData, uint32_t bufferIdx) +{ + UpdateUniformBuffer(pGfxApi, rUniform.buf[bufferIdx], dataSize, pNewData); +} +template +void ReleaseUniformBuffer(Dx12* pGfxApi, UniformArray& rUniform) +{ + for (size_t i = 0; i < T_NUM_BUFFERS; ++i) + { + rUniform.bufferHandles[i] = nullptr; + ReleaseUniformBuffer(pGfxApi, rUniform.buf[i]); + } +} + +template +bool CreateUniformBuffer(Dx12* pGfxApi, UniformArrayT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) +{ + return CreateUniformBuffer(pGfxApi, newUniform, sizeof(T), pData, usage); +} +template +void UpdateUniformBuffer(Dx12* pGfxApi, UniformArrayT& uniform, const T& newData, uint32_t bufferIdx) +{ + return UpdateUniformBuffer(pGfxApi, uniform, sizeof(T), &newData, bufferIdx); +} +template +void UpdateUniformBuffer(Dx12* pGfxApi, UniformArrayT& uniform, const TT& newData, uint32_t bufferIdx) +{ + static_assert(std::is_same::value, "UpdateUniformBuffer, uniform is different type to newData"); + return UpdateUniformBuffer(pGfxApi, static_cast&>(uniform), newData, bufferIdx); +} diff --git a/framework/code/memory/dx12/vertexBufferObject.cpp b/framework/code/memory/dx12/vertexBufferObject.cpp new file mode 100644 index 0000000..d098589 --- /dev/null +++ b/framework/code/memory/dx12/vertexBufferObject.cpp @@ -0,0 +1,153 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "vertexBufferObject.hpp" +#include "material/dx12/vertexDescription.hpp" + + +/////////////////////////////////////////////////////////////////////////////// + +VertexBuffer::~VertexBuffer() +{ + //mBindings.clear(); + //mAttributes.clear(); +} + + +/////////////////////////////////////////////////////////////////////////////// + +VertexBuffer::VertexBuffer(VertexBuffer&& other) noexcept +{ + *this = std::move(other); +} + +/////////////////////////////////////////////////////////////////////////////// + +VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other) noexcept +{ + Buffer::operator=(std::move(other)); + if (&other != this) + { + mSpan = other.mSpan; + other.mSpan = 0; + mNumVertices = other.mNumVertices; + other.mNumVertices = 0; + mDspUsable = other.mDspUsable; + mView = other.mView; + other.mView = {}; + //mBindings = std::move(other.mBindings); + //mAttributes = std::move(other.mAttributes); + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VertexBuffer::Initialize(MemoryManager* pManager, size_t span, size_t numVerts, const void* initialData, const bool dspUsable, const BufferUsageFlags usage ) +{ + mNumVertices = numVerts; + mSpan = span; + mDspUsable = dspUsable; + + mView = {}; + + if (dspUsable) + { + mManager = pManager; + assert(0); // dsp currently unsupported + return false; + } + else + { + if (!Buffer::Initialize(pManager, static_cast(mSpan * mNumVertices), usage, initialData)) + return false; + + mView.BufferLocation = mAllocatedBuffer.GetResource()->GetGPUVirtualAddress(); + mView.SizeInBytes = mNumVertices * mSpan; + mView.StrideInBytes = mSpan; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void VertexBuffer::Destroy() +{ + mView = {}; + //mBindings.clear(); + //mAttributes.clear(); + mNumVertices = 0; + Buffer::Destroy(); +} + +/////////////////////////////////////////////////////////////////////////////// + +VertexBuffer VertexBuffer::Copy() +{ + VertexBuffer copy; + if (copy.Initialize(mManager, GetSpan(), GetNumVertices(), Map().data(), mDspUsable, mBufferUsageFlags )) + { + //copy.mBindings = mBindings; + //copy.mAttributes = mAttributes; + copy.mElements = mElements; + return copy; + } + else + { + return {}; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void VertexBuffer::AddBindingAndAtributes(uint32_t binding, const VertexFormat& vertexFormat) +{ + VertexDescription v(vertexFormat, binding); + mElements.insert(mElements.end(), v.GetVertexElementDescs().begin(), v.GetVertexElementDescs().end()); +} +// +///////////////////////////////////////////////////////////////////////////////// +// +//void VertexBuffer::AddBinding(uint32_t binding, uint32_t stride, VkVertexInputRate inputRate) +//{ +// VkVertexInputBindingDescription vibd = {}; +// vibd.binding = binding; +// vibd.stride = stride; +// vibd.inputRate = inputRate; +// mBindings.push_back(vibd); +//} +// +///////////////////////////////////////////////////////////////////////////////// +// +//void VertexBuffer::AddAttribute(uint32_t binding, uint32_t location, uint32_t offset, VkFormat format) +//{ +// VkVertexInputAttributeDescription viad = {}; +// viad.binding = binding; +// viad.location = location; +// viad.offset = offset; +// viad.format = format; +// mAttributes.push_back(viad); +//} +// +///////////////////////////////////////////////////////////////////////////////// +// +//VkPipelineVertexInputStateCreateInfo VertexBuffer::CreatePipelineState() const +//{ +// assert(mBindings.size() > 0); +// assert(mAttributes.size() > 0); +// VkPipelineVertexInputStateCreateInfo visci = {}; +// visci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; +// visci.pNext = nullptr; +// visci.vertexBindingDescriptionCount = (uint32_t)mBindings.size(); +// visci.pVertexBindingDescriptions = &mBindings[0]; +// visci.vertexAttributeDescriptionCount = (uint32_t)mAttributes.size(); +// visci.pVertexAttributeDescriptions = &mAttributes[0]; +// return visci; +//} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/framework/code/memory/dx12/vertexBufferObject.hpp b/framework/code/memory/dx12/vertexBufferObject.hpp new file mode 100644 index 0000000..f58c2de --- /dev/null +++ b/framework/code/memory/dx12/vertexBufferObject.hpp @@ -0,0 +1,86 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "dx12/dx12.hpp" +#include "bufferObject.hpp" +#include "memory/vertexBuffer.hpp" +#include + +class Dx12; +using VertexBufferDx12 = VertexBuffer; + + +/// Buffer containing vertex data (specialized for DX12). +/// @ingroup Memory +template<> +class VertexBuffer : public Buffer +{ + VertexBuffer& operator=(const VertexBuffer&) = delete; + VertexBuffer(const VertexBuffer&) = delete; +public: + VertexBuffer() noexcept = default; + VertexBuffer(VertexBuffer&&) noexcept; + VertexBuffer& operator=(VertexBuffer&&) noexcept; + virtual ~VertexBuffer(); + + bool Initialize(MemoryManager* pManager, size_t span, size_t numVerts, const void* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex ); + template + bool Initialize(MemoryManager* pManager, const std::span initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex); + + /// destroy buffer and leave in a state where it could be re-initialized + virtual void Destroy() override; + + /// create a copy of this vertex buffer (including a new copy of the data) + VertexBuffer Copy(); + + /// get number of bytes allocated + size_t GetAllocationSize() const { return GetSpan() * GetNumVertices(); } + + /// get span (in bytes) + size_t GetSpan() const { return mSpan; } + + /// get number of vertices allocated + size_t GetNumVertices() const { return mNumVertices; } + + // Dx12 vertex attributes + //void AddBinding(uint32_t binding, uint32_t stride, VkVertexInputRate inputRate); + //void AddAttribute(uint32_t binding, uint32_t location, uint32_t offset, VkFormat format); + void AddBindingAndAtributes(uint32_t binding, const VertexFormat& vertexFormat); // helper, does AddBinding and AddAttribute(s) + //const auto& GetBindings() const { return mBindings; } + //const auto& GetAttributes() const { return mAttributes; } + //VkPipelineVertexInputStateCreateInfo CreatePipelineState() const; + const auto& GetVertexBufferView() const { return mView; } + const auto& GetElementDescs() const { return mElements; } + + /// Access (cpu mapped) vertex data using attribute information + //template + //const T* GetAttributeData(const MemoryCpuMappedUntyped& pMappedVertData, uint32_t location, uint32_t vertIndex, VkFormat expectedFormat) const + //{ + // assert(mAttributes[location].location == location); // going to assume locations are in order within mAttributes and contiguous. May need to revisit this code if not! + // assert(mAttributes[location].format == expectedFormat); // in the future we could convert, for now just expect it to match + // assert(vertIndex < mNumVertices); + // return (const T*)((static_cast(pMappedVertData.data())) + mAttributes[location].offset + mSpan * vertIndex); + //} + +protected: + size_t mSpan = 0; + size_t mNumVertices = 0; + bool mDspUsable = false; + //std::vector mBindings; + //std::vector mAttributes; + D3D12_VERTEX_BUFFER_VIEW mView{}; + std::vector mElements; + +}; + +template +bool VertexBuffer::Initialize(MemoryManager* pManager, const std::span initialData, const bool dspUsable, const BufferUsageFlags usage ) +{ + return Initialize(pManager, sizeof(T), initialData.size(), initialData.data(), dspUsable, usage); +} diff --git a/framework/code/memory/indexBuffer.hpp b/framework/code/memory/indexBuffer.hpp index ed0f8a4..52cb077 100644 --- a/framework/code/memory/indexBuffer.hpp +++ b/framework/code/memory/indexBuffer.hpp @@ -1,33 +1,34 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include "buffer.hpp" +#include /// Templated (by graphics api) buffer containing vertex indices /// DO NOT specialize this template (use IndexBuffer for that) /// @ingroup Memory template -class IndexBufferT: public BufferT +class IndexBufferT: public Buffer { IndexBufferT(const IndexBufferT&) = delete; IndexBufferT& operator=(const IndexBufferT&) = delete; public: IndexBufferT(IndexType) noexcept; - //IndexBufferObject(VkIndexType) noexcept; + //IndexBuffer(VkIndexType) noexcept; IndexBufferT(IndexBufferT&&) noexcept; IndexBufferT& operator=(IndexBufferT&&) noexcept; ~IndexBufferT(); using MemoryManager = MemoryManager; /// Initialization - template bool Initialize( MemoryManager* pManager, size_t numIndices, const T* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Index); + template bool Initialize( MemoryManager* pManager, const std::span initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Index); /// Initialization bool Initialize( MemoryManager* pManager, size_t numIndices, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Index ); @@ -70,7 +71,7 @@ class IndexBuffer : public IndexBufferT { public: IndexBuffer(IndexType i) noexcept : IndexBufferT(i) {} - //IndexBufferObject(VkIndexType) noexcept; + //IndexBuffer(VkIndexType) noexcept; IndexBuffer(IndexBuffer&& o) noexcept : IndexBufferT(std::move(o)) {} IndexBuffer& operator=(IndexBuffer&& o) noexcept { return IndexBufferT::operator=(o); }; ~IndexBuffer() {} @@ -80,14 +81,14 @@ class IndexBuffer : public IndexBufferT template -IndexBufferT::IndexBufferT(IndexType indexType) noexcept : BufferT(), mIndexType(indexType) +IndexBufferT::IndexBufferT(IndexType indexType) noexcept : Buffer(), mIndexType(indexType) {} //template //IndexBufferT::IndexBufferT(VkIndexType) noexcept; template -IndexBufferT::IndexBufferT(IndexBufferT&& other) noexcept : BufferT(std::move(other)), mIndexType(other.mIndexType) +IndexBufferT::IndexBufferT(IndexBufferT&& other) noexcept : Buffer(std::move(other)), mIndexType(other.mIndexType) { assert(mIndexType == other.mIndexType); mNumIndices = other.mNumIndices; @@ -98,7 +99,7 @@ IndexBufferT::IndexBufferT(IndexBufferT&& other) noexcept : template IndexBufferT& IndexBufferT::operator=(IndexBufferT&& other) noexcept { - BufferT::operator=(std::move(other)); + Buffer::operator=(std::move(other)); if (&other != this) { assert(mIndexType == other.mIndexType); @@ -113,10 +114,10 @@ template IndexBufferT::~IndexBufferT() {} template template -bool IndexBufferT::Initialize(MemoryManager* pManager, size_t numIndices, const T* initialData, const bool dspUsable, const BufferUsageFlags usage) +bool IndexBufferT::Initialize(MemoryManager* pManager, const std::span initialData, const bool dspUsable, const BufferUsageFlags usage) { assert(sizeof(T) == GetIndexTypeBytes()); - return Initialize(pManager, numIndices, (const void*)initialData, dspUsable, usage); + return Initialize(pManager, initialData.size(), initialData.data(), dspUsable, usage); } template @@ -138,14 +139,14 @@ bool IndexBufferT::Initialize(MemoryManager* pManager, size_t numIndic } else { - return BufferT::Initialize(pManager, GetIndexTypeBytes() * mNumIndices, usage, initialData); + return Buffer::Initialize(pManager, GetIndexTypeBytes() * mNumIndices, usage, initialData); } } template void IndexBufferT::Destroy() { - BufferT::Destroy(); + Buffer::Destroy(); } template @@ -153,11 +154,11 @@ template MapGuard IndexBufferT::Map() { assert( sizeof(T) == GetIndexTypeBytes() ); - return BufferT::template Map(); + return Buffer::template Map(); } template MapGuard IndexBufferT::MapVoid() { - return BufferT::template Map(); + return Buffer::template Map(); } diff --git a/framework/code/memory/memory.hpp b/framework/code/memory/memory.hpp index 0a1a20f..d6e0b36 100644 --- a/framework/code/memory/memory.hpp +++ b/framework/code/memory/memory.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once /// Shared definitions used across all graphics APIs (Dx12, Vulkan) diff --git a/framework/code/memory/memoryManager.hpp b/framework/code/memory/memoryManager.hpp index a2065c8..a27e649 100644 --- a/framework/code/memory/memoryManager.hpp +++ b/framework/code/memory/memoryManager.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/memory/memoryMapped.hpp b/framework/code/memory/memoryMapped.hpp index d5f896b..5d968cf 100644 --- a/framework/code/memory/memoryMapped.hpp +++ b/framework/code/memory/memoryMapped.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -17,38 +17,32 @@ template class MemoryAllocation; /// Wraps the VMA or D3D12MA allocation handle. Represents an allocation out of 'gpu' memory. Cannot be copied (only moved) - single owner. /// Passed around between whomever is 'owner' of the allocation (when mapping etc) +/// Expected for this class to be specialized for each graphics api. /// @ingroup Memory template class MemoryAllocation final { - friend class MemoryManager; public: - MemoryAllocation(const MemoryAllocation&) = delete; - MemoryAllocation& operator=(const MemoryAllocation&) = delete; - MemoryAllocation() noexcept {} - MemoryAllocation(MemoryAllocation&& other) noexcept; - MemoryAllocation& operator=(MemoryAllocation&& other) noexcept; - ~MemoryAllocation() { assert(!allocation); } // protect accidental deletion (leak) - explicit operator bool() const { return allocation != nullptr; } -private: - void clear() { allocation = nullptr; } - void* allocation = nullptr; // anonymous handle (gpx api specific) + MemoryAllocation() noexcept {} // This class is expected to be specialized! + ~MemoryAllocation() = delete; // This class is expected to be specialized! +protected: + static_assert(sizeof( MemoryAllocation ) >= 1); // Ensure this class template is specialized (and not used as-is). Maybe the correct #include is not being pulled in! }; /// Represents a memory block allocated on the gfx device and that has an associated buffer/resource handle alongside the allocation -/// @tparam T_VKTYPE underlying buffer/resource type - eg VkImage or VkBuffer on Vulkan +/// @tparam T_BUFFERTYPE underlying buffer/resource type - eg VkImage or VkBuffer on Vulkan /// @ingroup Memory -template +template class MemoryAllocatedBuffer { - MemoryAllocatedBuffer(const MemoryAllocatedBuffer&) = delete; - MemoryAllocatedBuffer& operator=(const MemoryAllocatedBuffer&) = delete; + MemoryAllocatedBuffer(const MemoryAllocatedBuffer&) = delete; + MemoryAllocatedBuffer& operator=(const MemoryAllocatedBuffer&) = delete; public: friend class MemoryManager; // Restrict MemoryAllocatedBuffer to not be duplicated and not accidentally deleted (leaking memory). - MemoryAllocatedBuffer(MemoryAllocatedBuffer&& other) noexcept; - MemoryAllocatedBuffer& operator=(MemoryAllocatedBuffer&& other) noexcept; + MemoryAllocatedBuffer(MemoryAllocatedBuffer&& other) noexcept; + MemoryAllocatedBuffer& operator=(MemoryAllocatedBuffer&& other) noexcept; MemoryAllocatedBuffer() noexcept {} explicit operator bool() const { return static_cast(allocation); } private: @@ -94,26 +88,6 @@ MemoryCpuMappedUntyped::MemoryCpuMappedUntyped(MemoryCpuMappedUntyped< other.mCpuLocation = nullptr; } -// -// MemoryAllocation implementation -// -template -MemoryAllocation::MemoryAllocation(MemoryAllocation&& other) noexcept -{ - allocation = other.allocation; - other.allocation = nullptr; -} - -template -MemoryAllocation& MemoryAllocation::operator=(MemoryAllocation&& other) noexcept -{ - if (&other != this) { - allocation = other.allocation; - other.allocation = nullptr; - } - return *this; -} - // // MemoryCpuMappedUntyped implementation // diff --git a/framework/code/memory/uniform.hpp b/framework/code/memory/uniform.hpp index 09ff392..c48f684 100644 --- a/framework/code/memory/uniform.hpp +++ b/framework/code/memory/uniform.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once /// @file uniform.hpp @@ -43,6 +43,8 @@ struct UniformArray UniformArray(const UniformArray &) = delete; UniformArray& operator=(const UniformArray&) = delete; UniformArray(UniformArray&&) noexcept = delete; // template class expected to be specialized + + static_assert(sizeof( UniformArray ) > 1); // Ensure this class template is specialized (and not used as-is) }; /// @brief UniformArray buffer helper template @@ -55,77 +57,77 @@ struct UniformArrayT : public UniformArray template -bool CreateUniformBuffer(T_GFXAPI* pVulkan, Uniform* pNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform); +bool CreateUniformBuffer( T_GFXAPI*, Uniform* pNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform ) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"memory//uniform.hpp\""); +} template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, Uniform* pUniform, size_t dataSize, const void* const pNewData); +void UpdateUniformBuffer(T_GFXAPI* , Uniform* pUniform, size_t dataSize, const void* const pNewData) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"memory//uniform.hpp\""); +} template -void ReleaseUniformBuffer(T_GFXAPI* pVulkan, Uniform* pUniform); +void ReleaseUniformBuffer(T_GFXAPI* , Uniform* pUniform) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"memory//uniform.hpp\""); +} template -bool CreateUniformBuffer(T_GFXAPI* pVulkan, UniformT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) +bool CreateUniformBuffer(T_GFXAPI* pGfxApi, UniformT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) { - return CreateUniformBuffer(pVulkan, &newUniform, sizeof(T), pData, usage); + return CreateUniformBuffer(pGfxApi, &newUniform, sizeof(T), pData, usage); } template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, Uniform& uniform, const T& newData) +void UpdateUniformBuffer(T_GFXAPI* pGfxApi, Uniform& uniform, const T& newData) { - return UpdateUniformBuffer(pVulkan, &uniform, sizeof(T), &newData); + return UpdateUniformBuffer(pGfxApi, &uniform, sizeof(T), &newData); } template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, UniformT& uniform, const TT& newData) +void UpdateUniformBuffer(T_GFXAPI* pGfxApi, UniformT& uniform, const TT& newData) { static_assert(std::is_same::value, "UpdateUniformBuffer, uniform is different type to newData"); - return UpdateUniformBuffer(pVulkan, static_cast&>(uniform), newData); + return UpdateUniformBuffer(pGfxApi, static_cast&>(uniform), newData); } -#if 0 -template -bool CreateUniformBuffer(T_GFXAPI* pVulkan, MemoryAllocatedBuffer& rNewUniformBuffer, size_t dataSize, const void* const pData, BufferUsageFlags usage) = delete/* expected to be specialized*/; -template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, MemoryAllocatedBuffer& rUniform, size_t dataSize, const void* const pNewData) = delete/* expected to be specialized*/; -template -void ReleaseUniformBuffer(T_GFXAPI* pVulkan, MemoryAllocatedBuffer& rUniform) = delete/* expected to be specialized*/; -#endif - template -bool CreateUniformBuffer(T_GFXAPI* pVulkan, UniformArray& rNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform) +bool CreateUniformBuffer(T_GFXAPI* pGfxApi, UniformArray& rNewUniform, size_t dataSize, const void* const pData = NULL, BufferUsageFlags usage = BufferUsageFlags::Uniform) { for (size_t i = 0; i < T_NUM_BUFFERS; ++i) { - if (!CreateUniformBuffer(pVulkan, rNewUniform.buf[i], dataSize, pData, usage)) + if (!CreateUniformBuffer(pGfxApi, rNewUniform.buf[i], dataSize, pData, usage)) return false; //rNewUniform.vkBuffers[i] = rNewUniform.buf[i].GetVkBuffer(); } return true; } template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, UniformArray& rUniform, size_t dataSize, const void* const pNewData, uint32_t bufferIdx) +void UpdateUniformBuffer(T_GFXAPI* pGfxApi, UniformArray& rUniform, size_t dataSize, const void* const pNewData, uint32_t bufferIdx) { - UpdateUniformBuffer(pVulkan, rUniform.buf[bufferIdx], dataSize, pNewData); + UpdateUniformBuffer(pGfxApi, rUniform.buf[bufferIdx], dataSize, pNewData); } template -void ReleaseUniformBuffer(T_GFXAPI* pVulkan, UniformArray& rUniform) +void ReleaseUniformBuffer(T_GFXAPI* pGfxApi, UniformArray& rUniform) { for (size_t i = 0; i < T_NUM_BUFFERS; ++i) { - //rUniform.vkBuffers[i] = VK_NULL_HANDLE; - ReleaseUniformBuffer(pVulkan, rUniform.buf[i]); + rUniform.bufferHandles[i] = {}; + ReleaseUniformBuffer(pGfxApi, rUniform.buf[i]); } } template -bool CreateUniformBuffer(T_GFXAPI* pVulkan, UniformArrayT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) +bool CreateUniformBuffer(T_GFXAPI* pGfxApi, UniformArrayT& newUniform, const T* const pData = nullptr, BufferUsageFlags usage = BufferUsageFlags::Uniform) { - return CreateUniformBuffer(pVulkan, newUniform, sizeof(T), pData, usage); + return CreateUniformBuffer(pGfxApi, newUniform, sizeof(T), pData, usage); } template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, UniformArrayT& uniform, const T& newData, uint32_t bufferIdx) +void UpdateUniformBuffer(T_GFXAPI* pGfxApi, UniformArrayT& uniform, const T& newData, uint32_t bufferIdx) { - return UpdateUniformBuffer(pVulkan, uniform, sizeof(T), &newData, bufferIdx); + return UpdateUniformBuffer(pGfxApi, uniform, sizeof(T), &newData, bufferIdx); } template -void UpdateUniformBuffer(T_GFXAPI* pVulkan, UniformArrayT& uniform, const TT& newData, uint32_t bufferIdx) +void UpdateUniformBuffer(T_GFXAPI* pGfxApi, UniformArrayT& uniform, const TT& newData, uint32_t bufferIdx) { static_assert(std::is_same::value, "UpdateUniformBuffer, uniform is different type to newData"); - return UpdateUniformBuffer(pVulkan, static_cast&>(uniform), newData, bufferIdx); + return UpdateUniformBuffer(pGfxApi, static_cast&>(uniform), newData, bufferIdx); } diff --git a/framework/code/memory/vertexBuffer.hpp b/framework/code/memory/vertexBuffer.hpp index f078ef3..359a118 100644 --- a/framework/code/memory/vertexBuffer.hpp +++ b/framework/code/memory/vertexBuffer.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -18,59 +18,11 @@ // forward declarations class VertexFormat; -#if 1 - /// Buffer containing vertex data. /// @ingroup Memory template -class VertexBuffer : public BufferT +class VertexBuffer : public Buffer { ~VertexBuffer() noexcept = delete; // Ensure this class template is specialized (and not used as-is) - static_assert(sizeof(VertexBuffer) != sizeof(BufferT)); // Ensure this class template is specialized (and not used as-is) -}; - -#else - -template -class VertexBuffer : public BufferT -{ -public: - VertexBuffer() noexcept; - VertexBuffer(VertexBuffer&&) noexcept; - VertexBuffer& operator=(VertexBuffer&&) noexcept; - virtual ~VertexBuffer(); - - bool Initialize(MemoryManager* pManager, size_t span, size_t numVerts, const void* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex ); - template - bool Initialize(MemoryManager* pManager, size_t numVerts, const T* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex); - - /// destroy buffer and leave in a state where it could be re-initialized - virtual void Destroy() override; - - /// create a copy of this vertex buffer (including a new copy of the data) - VertexBuffer Copy(); - - /// get number of bytes allocated - size_t GetAllocationSize() const { return GetSpan() * GetNumVertices(); } - - /// get span (in bytes) - size_t GetSpan() const { return mSpan; } - - /// get number of vertices allocated - size_t GetNumVertices() const { return mNumVertices; } - - /// Vertex attributes - void AddBindingAndAtributes(uint32_t binding, const VertexFormat& vertexFormat) {} - -protected: - size_t mSpan; - size_t mNumVertices; - bool mDspUsable; + static_assert(sizeof(VertexBuffer) != sizeof(Buffer)); // Ensure this class template is specialized (and not used as-is) }; - -template template -bool VertexBuffer::Initialize(MemoryManager* pManager, size_t numVerts, const T* initialData, const bool dspUsable, const BufferUsageFlags usage ) -{ - return false;//Initialize(pManager, sizeof(T), numVerts, initialData, dspUsable, usage); -} -#endif diff --git a/framework/code/memory/vulkan/bufferObject.cpp b/framework/code/memory/vulkan/bufferObject.cpp index a06d8ed..a584081 100644 --- a/framework/code/memory/vulkan/bufferObject.cpp +++ b/framework/code/memory/vulkan/bufferObject.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,24 +9,25 @@ #include "bufferObject.hpp" #include "memoryManager.hpp" #include +#include /////////////////////////////////////////////////////////////////////////////// -BufferT::BufferT() +Buffer::Buffer() { } /////////////////////////////////////////////////////////////////////////////// -BufferT::BufferT(BufferT&& other) noexcept +Buffer::Buffer(Buffer&& other) noexcept { *this = std::move(other); } /////////////////////////////////////////////////////////////////////////////// -BufferT& BufferT::operator=(BufferT&& other) noexcept +Buffer& Buffer::operator=(Buffer&& other) noexcept { if (this != &other) { @@ -41,21 +42,21 @@ BufferT& BufferT::operator=(BufferT&& other) noexcept /////////////////////////////////////////////////////////////////////////////// -BufferT::~BufferT() +Buffer::~Buffer() { Destroy(); } /////////////////////////////////////////////////////////////////////////////// -BufferT::operator bool() const +Buffer::operator bool() const { return !!mAllocatedBuffer; } /////////////////////////////////////////////////////////////////////////////// -bool BufferT::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, const void* initialData) +bool Buffer::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, const void* initialData) { MemoryUsage memoryUsage = initialData ? MemoryUsage::CpuToGpu : MemoryUsage::GpuExclusive; if ((bufferUsageFlags & BufferUsageFlags::TransferSrc) != 0) @@ -77,7 +78,7 @@ bool BufferT::Initialize(MemoryManager* pManager, size_t size, BufferUsa /////////////////////////////////////////////////////////////////////////////// -bool BufferT::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, MemoryUsage memoryUsage) +bool Buffer::Initialize(MemoryManager* pManager, size_t size, BufferUsageFlags bufferUsageFlags, MemoryUsage memoryUsage) { assert(!mManager); if (!pManager) @@ -94,7 +95,34 @@ bool BufferT::Initialize(MemoryManager* pManager, size_t size, BufferUsa /////////////////////////////////////////////////////////////////////////////// -void BufferT::Destroy() +bool Buffer::Update(MemoryManager* pManager, size_t dataSize, const void* newData) +{ + if (newData) + { + auto mappedGuard = Map(); + memcpy(mappedGuard.data(), newData, dataSize); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Buffer::GetMeshData(MemoryManager* pManager, size_t dataSize, void* outputData) const +{ + auto* pThisNonConst = const_cast*>(this); // we remove the const-ness of 'this' so we can do the map/unmap. Treating this as "no harm no foul" - our class on exit is identical to what it is on entry! + if (outputData) + { + auto mappedGuard = pThisNonConst->Map(); + memcpy(outputData, mappedGuard.data(), dataSize); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Buffer::Destroy() { if (!mManager) { @@ -107,7 +135,7 @@ void BufferT::Destroy() /////////////////////////////////////////////////////////////////////////////// -void BufferT::Unmap(MemoryCpuMappedUntyped buffer) +void Buffer::Unmap(MemoryCpuMappedUntyped buffer) { mManager->Unmap(mAllocatedBuffer, std::move(buffer)); } diff --git a/framework/code/memory/vulkan/bufferObject.hpp b/framework/code/memory/vulkan/bufferObject.hpp index 94913b1..e4a24e6 100644 --- a/framework/code/memory/vulkan/bufferObject.hpp +++ b/framework/code/memory/vulkan/bufferObject.hpp @@ -1,13 +1,13 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once -#include +#include #include "memory/buffer.hpp" #include "memory/memory.hpp" #include "memoryManager.hpp" @@ -16,23 +16,22 @@ // Forward declarations struct AHardwareBuffer_Desc; struct AHardwareBuffer; -template class BufferT; +template class Buffer; -using BufferVulkan = BufferT; /// Defines a simple (vulkan specialized) object for creating and holding Vulkan memory buffer objects. /// @ingroup Memory template<> -class BufferT : public Buffer +class Buffer : public BufferBase { - BufferT& operator=(const BufferT&) = delete; - BufferT(const BufferT&) = delete; + Buffer& operator=(const Buffer&) = delete; + Buffer(const Buffer&) = delete; protected: public: - BufferT(); - virtual ~BufferT(); - BufferT(BufferT&&) noexcept; - BufferT& operator=(BufferT&&) noexcept; + Buffer(); + virtual ~Buffer(); + Buffer(Buffer&&) noexcept; + Buffer& operator=(Buffer&&) noexcept; using MemoryManager = MemoryManager; using MemoryCpuMappedUntyped = MemoryCpuMappedUntyped; @@ -44,6 +43,9 @@ class BufferT : public Buffer //bool Initialize(MemoryManager* pManager, BufferUsageFlags usageFlags, const AHardwareBuffer_Desc& hardwareBufferDesc, const void* initialData); //bool Initialize(MemoryManager* pManagere, BufferUsageFlags usageFlags, const AHardwareBuffer* pAHardwareBuffer); + bool Update(MemoryManager* pManager, size_t dataSize, const void* newData); + bool GetMeshData(MemoryManager* pManager, size_t dataSize, void* outputData) const; + /// destroy buffer and leave in a state where it could be re-initialized virtual void Destroy(); diff --git a/framework/code/memory/vulkan/drawIndirectBufferObject.cpp b/framework/code/memory/vulkan/drawIndirectBufferObject.cpp index 99fa076..789d47e 100644 --- a/framework/code/memory/vulkan/drawIndirectBufferObject.cpp +++ b/framework/code/memory/vulkan/drawIndirectBufferObject.cpp @@ -1,105 +1,27 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "drawIndirectBufferObject.hpp" - -DrawIndirectBuffer::DrawIndirectBuffer(bool indexed) noexcept : BufferT(), mIndexed(indexed) -{ -} - -/////////////////////////////////////////////////////////////////////////////// - -DrawIndirectBuffer::~DrawIndirectBuffer() -{ -} - -/////////////////////////////////////////////////////////////////////////////// - -DrawIndirectBuffer::DrawIndirectBuffer(DrawIndirectBuffer&& other) noexcept : mIndexed(other.mIndexed) -{ - *this = std::move(other); -} - -/////////////////////////////////////////////////////////////////////////////// - -DrawIndirectBuffer& DrawIndirectBuffer::operator=(DrawIndirectBuffer&& other) noexcept -{ - BufferT::operator=(std::move(other)); - if (&other != this) - { - assert(mIndexed == other.mIndexed); - mNumDraws = other.mNumDraws; - other.mNumDraws = 0; - mPrequelBytes = other.mPrequelBytes; - other.mPrequelBytes = 0; - } - return *this; -} - -/////////////////////////////////////////////////////////////////////////////// - -bool DrawIndirectBuffer::Initialize(MemoryManager* pManager, size_t numDraws, const void* initialData, const BufferUsageFlags usage, const void* prequelData, uint32_t prequelBytes) -{ - mNumDraws = numDraws; - mPrequelBytes = prequelBytes; - - MemoryUsage memoryUsage = initialData ? MemoryUsage::CpuToGpu : MemoryUsage::GpuExclusive; - if ((usage & BufferUsageFlags::TransferSrc) != 0) - memoryUsage = MemoryUsage::CpuToGpu; - - if (!BufferT::Initialize(pManager, (VkDeviceSize)(GetDrawCommandBytes() * mNumDraws + mPrequelBytes), usage, memoryUsage)) - { - return false; - } - - if (initialData) - { - auto mappedGuard = BufferT::Map(); - auto initialDataSize = GetDrawCommandBytes() * mNumDraws; - if (prequelBytes) - memcpy(mappedGuard.data(), prequelData, prequelBytes); - else - memset(mappedGuard.data(), 0, prequelBytes); - memcpy(mappedGuard.data() + prequelBytes, initialData, initialDataSize); - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -void DrawIndirectBuffer::Destroy() -{ - mNumDraws = 0; - BufferT::Destroy(); -} - -/////////////////////////////////////////////////////////////////////////////// - -DrawIndirectBuffer DrawIndirectBuffer::Copy(BufferUsageFlags newUsageFlags) -{ - if (newUsageFlags == BufferUsageFlags::Unknown) - newUsageFlags = mBufferUsageFlags; - DrawIndirectBuffer copy( IsIndexed() ); - auto data = MapVoid(); - if (copy.Initialize(mManager, GetNumDraws(), static_cast(data.data()) + mPrequelBytes, newUsageFlags, data.data(), mPrequelBytes)) - { - return copy; - } - else - { - return { IsIndexed() }; +template<> +uint32_t DrawIndirectBuffer::GetDrawCommandBytes() const +{ + switch (mIndirectBufferType) { + case eType::Draw: + return sizeof( VkDrawIndirectCommand ); + case eType::IndexedDraw: + return sizeof( VkDrawIndexedIndirectCommand ); + case eType::MeshTasks: + return sizeof( VkDrawMeshTasksIndirectCommandEXT ); + break; + default: + assert(0); + return 0; } } -/////////////////////////////////////////////////////////////////////////////// - -MapGuard DrawIndirectBuffer::MapVoid() -{ - return BufferT::Map(); -} diff --git a/framework/code/memory/vulkan/drawIndirectBufferObject.hpp b/framework/code/memory/vulkan/drawIndirectBufferObject.hpp index fcede01..16e4ee0 100644 --- a/framework/code/memory/vulkan/drawIndirectBufferObject.hpp +++ b/framework/code/memory/vulkan/drawIndirectBufferObject.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,75 +10,3 @@ #include "vulkan/vulkan.hpp" #include "bufferObject.hpp" #include "memory/drawIndirectBuffer.hpp" - -class Vulkan; -using DrawIndirectBufferObject = DrawIndirectBuffer; - - -/// Buffer containing VkDrawIndirectCommand or VkDrawIndexedIndirectCommand (specialized for Vulkan) -/// @ingroup Memory -template<> -class DrawIndirectBuffer : public BufferT -{ - DrawIndirectBuffer(const DrawIndirectBuffer&) = delete; - DrawIndirectBuffer& operator=(const DrawIndirectBuffer&) = delete; -public: - DrawIndirectBuffer(bool indexed) noexcept; - DrawIndirectBuffer(DrawIndirectBuffer&&) noexcept; - DrawIndirectBuffer& operator=(DrawIndirectBuffer&&) noexcept; - ~DrawIndirectBuffer(); - - /// Initialization - template bool Initialize(MemoryManager* pManager, size_t numDraws, const T_DRAW* initialData, const T_PREQUEL* initialPrequelData, const BufferUsageFlags usage = BufferUsageFlags::Indirect); - template bool Initialize(MemoryManager* pManager, size_t numDraws, const T_DRAW* initialData, const BufferUsageFlags usage = BufferUsageFlags::Indirect); - - /// destroy buffer and leave in a state where it could be re-initialized - void Destroy(); - - /// create a copy of this buffer (including a new copy of the data) - /// @param newUsageFlags make the copy with new usage flags (default to copying the source usage flags) - DrawIndirectBuffer Copy(BufferUsageFlags newUsageFlags = BufferUsageFlags::Unknown); - - /// get number of bytes allocated - size_t GetAllocationSize() const { return mPrequelBytes + GetDrawCommandBytes() * mNumDraws; } - - /// Map this buffer to the cpu and return a guard object (automatically unmaps when it goes out of scope) - template MapGuard Map(); - - bool IsIndexed() const { return mIndexed; } - uint32_t GetDrawCommandBytes() const { return (mIndexed ? sizeof(VkDrawIndexedIndirectCommand) : sizeof(VkDrawIndirectCommand)); } - auto GetNumDraws() const { return mNumDraws; } - auto GetBufferOffset() const { return mPrequelBytes; } - -protected: - MapGuard MapVoid(); - bool Initialize(MemoryManager* pManager, size_t numDraws, const void* initialData, const BufferUsageFlags usage, const void* prequelData, uint32_t prequelBytes); - -private: - size_t mNumDraws = 0; - uint32_t mPrequelBytes = 0; // buffer bytes before the index data begins. - const bool mIndexed; -}; - - -template -bool DrawIndirectBuffer::Initialize(MemoryManager* pManager, size_t numDraws, const T* initialData, const BufferUsageFlags usage) -{ - assert(sizeof(T) == GetDrawCommandBytes()); - return Initialize(pManager, numDraws, (const void*)initialData, usage, nullptr, 0); -} - -template -bool DrawIndirectBuffer::Initialize(MemoryManager* pManager, size_t numDraws, const T_DRAW* initialData, const T_PREQUEL* prequelData, const BufferUsageFlags usage) -{ - assert(sizeof(T_DRAW) == GetDrawCommandBytes()); - return Initialize(pManager, numDraws, (const void*)initialData, usage, prequelData, sizeof(T_PREQUEL)); -} - -template -MapGuard DrawIndirectBuffer::Map() -{ - assert(mPrequelBytes == 0); // prequel bytes not yet supported in map - assert(sizeof(T) == GetDrawCommandBytes()); - return BufferT::template Map(); -} diff --git a/framework/code/memory/vulkan/indexBufferObject.cpp b/framework/code/memory/vulkan/indexBufferObject.cpp index a7df2d7..9af2596 100644 --- a/framework/code/memory/vulkan/indexBufferObject.cpp +++ b/framework/code/memory/vulkan/indexBufferObject.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -51,7 +51,7 @@ IndexBuffer IndexBuffer::Copy( VkCommandBuffer vkCommandBuffer, // Create the buffer we are copying into. size_t size = GetIndexTypeBytes() * mNumIndices; - if (!copy.BufferT::Initialize( mManager, size, bufferUsage, memoryUsage )) + if (!copy.Buffer::Initialize( mManager, size, bufferUsage, memoryUsage )) return copy; // Use gpu copy commands to copy buffer data diff --git a/framework/code/memory/vulkan/indexBufferObject.hpp b/framework/code/memory/vulkan/indexBufferObject.hpp index 9fdc4a1..2c4aad3 100644 --- a/framework/code/memory/vulkan/indexBufferObject.hpp +++ b/framework/code/memory/vulkan/indexBufferObject.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,7 +13,6 @@ class Vulkan; #include "bufferObject.hpp" #include "memory/indexBuffer.hpp" -using IndexBufferObject = IndexBuffer; // Template specialization for Vulkan Index buffer template<> @@ -21,7 +20,7 @@ class IndexBuffer final : public IndexBufferT { public: IndexBuffer(IndexType i) noexcept : IndexBufferT(i), mVkIndexType( IndexTypeToVk(i) ) {} - //IndexBufferObject(VkIndexType) noexcept; + //IndexBuffer(VkIndexType) noexcept; IndexBuffer(IndexBuffer&& o) noexcept : IndexBufferT(std::move(static_cast&&>(o))), mVkIndexType( o.mVkIndexType ) {} IndexBuffer& operator=(IndexBuffer&& o) noexcept { assert(mVkIndexType == o.mVkIndexType); IndexBufferT::operator=(std::move(static_cast &&>(o))); return *this; }; ~IndexBuffer() {} diff --git a/framework/code/memory/vulkan/memoryManager.cpp b/framework/code/memory/vulkan/memoryManager.cpp index ab118ac..6d660bf 100644 --- a/framework/code/memory/vulkan/memoryManager.cpp +++ b/framework/code/memory/vulkan/memoryManager.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -14,8 +14,9 @@ // Include the VulkanMemoryAllocator AND compile the implementation in this cpp (is a header-only library) #define VMA_IMPLEMENTATION #define VMA_STATIC_VULKAN_FUNCTIONS 0 -#include -#include "VulkanMemoryAllocator/src/vk_mem_alloc.h" +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#include +#include "VulkanMemoryAllocator/include/vk_mem_alloc.h" // Ensure our enum matches the one in VMA. This way we can pass out enum straight through to VMA (and not have to include the 700kb vma header everywhere) @@ -34,53 +35,61 @@ MemoryManager::MemoryManager() MemoryManager::~MemoryManager() { - Destroy(); + Destroy(); } /////////////////////////////////////////////////////////////////////////////// bool MemoryManager::Initialize(VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkInstance vkInstance, bool EnableBufferDeviceAddress) { - assert(!mVmaAllocator); - mGpuDevice = vkDevice; - - VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.physicalDevice = vkPhysicalDevice; - allocatorInfo.device = vkDevice; - allocatorInfo.instance = vkInstance; - allocatorInfo.flags = EnableBufferDeviceAddress ? VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT : 0; - - VkResult result = vmaCreateAllocator(&allocatorInfo, &mVmaAllocator); - if (result != VK_SUCCESS) - { - mVmaAllocator = nullptr; - return false; - } - - if ((allocatorInfo.flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0) - { - PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(vkInstance, "vkGetDeviceProcAddr"); + assert(!mVmaAllocator); + mGpuDevice = vkDevice; + + VmaVulkanFunctions vulkanFunctions{ + .vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( vkInstance, "vkGetDeviceProcAddr" ) + }; + auto* fpGetDeviceProcAddr = vulkanFunctions.vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo allocatorInfo = { + .flags = (VmaAllocatorCreateFlags) ( EnableBufferDeviceAddress ? VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT : 0 ), + .physicalDevice = vkPhysicalDevice, + .device = vkDevice, + .pVulkanFunctions = &vulkanFunctions, + .instance = vkInstance, + .vulkanApiVersion = VK_MAKE_API_VERSION( 0,1,1,0 ), + }; + + VkResult result = vmaCreateAllocator(&allocatorInfo, &mVmaAllocator); + if (result != VK_SUCCESS) + { + mVmaAllocator = nullptr; + return false; + } + + if ((allocatorInfo.flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0) + { #if VK_KHR_buffer_device_address - mFpGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddressKHR)fpGetDeviceProcAddr(vkDevice, "vkGetBufferDeviceAddressKHR"); + mFpGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddressKHR)fpGetDeviceProcAddr(vkDevice, "vkGetBufferDeviceAddressKHR"); #elif VK_EXT_buffer_device_address - mFpGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddressEXT)fpGetDeviceProcAddr(vkDevice, "vkGetBufferDeviceAddressEXT"); + mFpGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddressEXT)fpGetDeviceProcAddr(vkDevice, "vkGetBufferDeviceAddressEXT"); #endif - if (!mFpGetBufferDeviceAddress) - { - // When we go to Vulkan 1.2 switch to use vkGetBufferDeviceAddress - assert(0 && "vkGetBufferDeviceAddressKHR/vkGetBufferDeviceAddressEXT not available, needed for VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT"); - return false; - } - } - return true; + if (!mFpGetBufferDeviceAddress) + { + // When we go to Vulkan 1.2 switch to use vkGetBufferDeviceAddress + assert(0 && "vkGetBufferDeviceAddressKHR/vkGetBufferDeviceAddressEXT not available, needed for VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT"); + return false; + } + } + return true; } /////////////////////////////////////////////////////////////////////////////// void MemoryManager::Destroy() { - vmaDestroyAllocator(mVmaAllocator); // safe to pass nullptr - mVmaAllocator = nullptr; + vmaDestroyAllocator(mVmaAllocator); // safe to pass nullptr + mVmaAllocator = nullptr; } static VkBufferUsageFlags BufferUsageToVk(BufferUsageFlags usage) @@ -101,10 +110,7 @@ static VkBufferUsageFlags BufferUsageToVk(BufferUsageFlags usage) if ((usage & BufferUsageFlags::Indirect) != 0) vkUsage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; if ((usage & BufferUsageFlags::AccelerationStructure) != 0) - { - vkUsage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR; - vkUsage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; - } + vkUsage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; if ((usage & BufferUsageFlags::AccelerationStructureBuild) != 0) vkUsage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; if ((usage & BufferUsageFlags::ShaderBindingTable) != 0) @@ -123,102 +129,226 @@ static VkBufferUsageFlags BufferUsageToVk(BufferUsageFlags usage) MemoryAllocatedBuffer MemoryManager::CreateBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage, VkDescriptorBufferInfo* pDescriptorBufferInfo) { - assert(memoryUsage != MemoryUsage::Unknown); - VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - bufferInfo.size = (VkDeviceSize)size; - bufferInfo.usage = BufferUsageToVk(bufferUsage); - - VmaAllocationCreateInfo allocInfo = {}; - allocInfo.usage = static_cast(memoryUsage); - + assert(memoryUsage != MemoryUsage::Unknown); + VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferInfo.size = (VkDeviceSize)size; + bufferInfo.usage = BufferUsageToVk(bufferUsage); + + VmaAllocationCreateInfo allocInfo = {}; + allocInfo.usage = static_cast(memoryUsage); + MemoryAllocatedBuffer vmaAllocatedBuffer; - VmaAllocationInfo vmaAllocationInfo; - VkResult result = vmaCreateBuffer(mVmaAllocator, &bufferInfo, &allocInfo, &vmaAllocatedBuffer.buffer, (VmaAllocation*)&vmaAllocatedBuffer.allocation.allocation, &vmaAllocationInfo); - if (result != VK_SUCCESS) - { - return {}; - } - if (pDescriptorBufferInfo) - { - *pDescriptorBufferInfo = { vmaAllocatedBuffer.GetVkBuffer(), 0, size }; - } - return vmaAllocatedBuffer; + VmaAllocationInfo vmaAllocationInfo; + VkResult result = vmaCreateBuffer(mVmaAllocator, &bufferInfo, &allocInfo, &vmaAllocatedBuffer.buffer, (VmaAllocation*)&vmaAllocatedBuffer.allocation.allocation, &vmaAllocationInfo); + if (result != VK_SUCCESS) + { + return {}; + } + if (pDescriptorBufferInfo) + { + *pDescriptorBufferInfo = { vmaAllocatedBuffer.GetVkBuffer(), 0, size }; + } + return vmaAllocatedBuffer; } /////////////////////////////////////////////////////////////////////////////// MemoryAbhAllocatedBuffer MemoryManager::CreateAndroidHardwareBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage) { - assert(0 && "unimplemented"); - return {}; + assert(0 && "unimplemented"); + return {}; +} + +/////////////////////////////////////////////////////////////////////////////// + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImageQCOM( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo ) +{ + VMA_ASSERT( allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation ); + + if (pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG( "vmaCreateImageQCOM" ); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + * pImage = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if (res >= 0) + { + VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; + + // 2. Allocate memory using allocator. + VkMemoryRequirements vkMemReq = {}; + + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + + allocator->GetImageMemoryRequirements( *pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation ); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + pImageCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation ); + + if (res >= 0) + { + // 3. Bind image with memory. + if ((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindImageMemory( *pAllocation, 0, *pImage, VMA_NULL ); + } + if (res >= 0) + { + // All steps succeeded. +#if VMA_STATS_STRING_ENABLED + ( *pAllocation )->InitBufferImageUsage( pImageCreateInfo->usage ); +#endif + if (pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo( *pAllocation, pAllocationInfo ); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation ); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +static PFN_vkGetImageMemoryRequirements2KHR s_oldGetImageMemReq = nullptr; +void vkGetImageMemoryRequirements2KHR_Qcom( VkDevice device, const VkImageMemoryRequirementsInfo2KHR* memReqInfo, VkMemoryRequirements2KHR* memReq2 ) +{ + VkTileMemoryRequirementsQCOM memReqQCOM { + .sType = VK_STRUCTURE_TYPE_TILE_MEMORY_REQUIREMENTS_QCOM, + .pNext = memReq2->pNext + }; + memReq2->pNext = &memReqQCOM; + s_oldGetImageMemReq(device, memReqInfo, memReq2); + + if (true) + { + LOGI("vkGetImageMemoryRequirements2KHR_Qcom image size: %zu (vs %zu for non tiled); alignment : %zu (vs %zu) memory type bits: %d", memReqQCOM.size, memReq2->memoryRequirements.size, memReqQCOM.alignment, memReq2->memoryRequirements.alignment, memReq2->memoryRequirements.memoryTypeBits); + //// Round alignment to next power of 2 (Qualcomm driver may not return a power of 2 alignment) + uint32_t alignment = memReqQCOM.alignment; + assert((memReqQCOM.size % alignment ) == 0); // make sure the buffer's size is aligned, we are going to have to assume everything can be laid out in order! + memReq2->memoryRequirements.alignment = 1;// alignment; + memReq2->memoryRequirements.size = memReqQCOM.size; + + // memReq2->memoryRequirements.memoryTypeBits = memReqQCOM.memoryTypeBits; + } + memReq2->pNext = const_cast(memReqQCOM.pNext); } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::CreateImage(const VkImageCreateInfo& imageInfo, MemoryUsage memoryUsage) { - assert(memoryUsage != MemoryUsage::Unknown); - VmaAllocationCreateInfo allocInfo = {}; - allocInfo.usage = static_cast(memoryUsage); - - MemoryAllocatedBuffer vmaAllocatedImage; - VkResult result = vmaCreateImage(mVmaAllocator, &imageInfo, &allocInfo, &vmaAllocatedImage.buffer, (VmaAllocation*)&vmaAllocatedImage.allocation.allocation, nullptr); - if (result != VK_SUCCESS) - { - return {}; - } - return vmaAllocatedImage; + assert(memoryUsage != MemoryUsage::Unknown); + VmaAllocationCreateInfo allocInfo = {}; + allocInfo.usage = static_cast(memoryUsage); + + MemoryAllocatedBuffer vmaAllocatedImage; + + VkResult result = vmaCreateImage(mVmaAllocator, &imageInfo, &allocInfo, &vmaAllocatedImage.buffer, (VmaAllocation*)&vmaAllocatedImage.allocation.allocation, nullptr); + + if (result != VK_SUCCESS) + { + return {}; + } + return vmaAllocatedImage; } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::AllocateMemory(size_t size, uint32_t memoryTypeBits) { - VkMemoryRequirements memoryRequirements{ .size = size, .memoryTypeBits = memoryTypeBits }; - - VmaAllocationCreateInfo allocInfo = {}; - allocInfo.usage = VMA_MEMORY_USAGE_UNKNOWN; - allocInfo.memoryTypeBits = memoryTypeBits; - allocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; - - MemoryAllocatedBuffer vmaAllocatedMemory; - VkResult result = vmaAllocateMemory(mVmaAllocator, &memoryRequirements, &allocInfo, (VmaAllocation*)&vmaAllocatedMemory.allocation.allocation, nullptr); - if (result != VK_SUCCESS) - { - return {}; - } - vmaAllocatedMemory.buffer = static_cast(vmaAllocatedMemory.allocation.allocation)->GetMemory(); - return vmaAllocatedMemory; + VkMemoryRequirements memoryRequirements{ .size = size, .memoryTypeBits = memoryTypeBits }; + + VmaAllocationCreateInfo allocInfo = {}; + allocInfo.usage = VMA_MEMORY_USAGE_UNKNOWN; + allocInfo.memoryTypeBits = memoryTypeBits; + allocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + + MemoryAllocatedBuffer vmaAllocatedMemory; + VkResult result = vmaAllocateMemory(mVmaAllocator, &memoryRequirements, &allocInfo, (VmaAllocation*)&vmaAllocatedMemory.allocation.allocation, nullptr); + if (result != VK_SUCCESS) + { + return {}; + } + vmaAllocatedMemory.buffer = static_cast(vmaAllocatedMemory.allocation.allocation)->GetMemory(); + return vmaAllocatedMemory; } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::BindImageToMemory(VkImage image, MemoryAllocatedBuffer&& memory) const { - VkResult result = vkBindImageMemory(mGpuDevice, image, static_cast(memory.allocation.allocation)->GetMemory(), static_cast(memory.allocation.allocation)->GetOffset()); - if (result != VK_SUCCESS) - { - return {}; - } - MemoryAllocatedBuffer imageMemory; - imageMemory.allocation = std::move(memory.allocation); - imageMemory.buffer = image; - return imageMemory; + VkResult result = vkBindImageMemory(mGpuDevice, image, static_cast(memory.allocation.allocation)->GetMemory(), static_cast(memory.allocation.allocation)->GetOffset()); + if (result != VK_SUCCESS) + { + return {}; + } + MemoryAllocatedBuffer imageMemory; + imageMemory.allocation = std::move(memory.allocation); + imageMemory.buffer = image; + return imageMemory; } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::BindBufferToMemory(VkBuffer buffer, MemoryAllocatedBuffer&& memory) const { - VkResult result = vkBindBufferMemory(mGpuDevice, buffer, static_cast(memory.allocation.allocation)->GetMemory(), static_cast(memory.allocation.allocation)->GetOffset()); - if (result != VK_SUCCESS) - { - return {}; - } - MemoryAllocatedBuffer bufferMemory; - bufferMemory.allocation = std::move(memory.allocation); - bufferMemory.buffer = buffer; - return bufferMemory; + VkResult result = vkBindBufferMemory(mGpuDevice, buffer, static_cast(memory.allocation.allocation)->GetMemory(), static_cast(memory.allocation.allocation)->GetOffset()); + if (result != VK_SUCCESS) + { + return {}; + } + MemoryAllocatedBuffer bufferMemory; + bufferMemory.allocation = std::move(memory.allocation); + bufferMemory.buffer = buffer; + return bufferMemory; } /////////////////////////////////////////////////////////////////////////////// @@ -237,91 +367,208 @@ bool MemoryManager::CopyData( VkCommandBuffer vkCommandBuffer, const Mem void MemoryManager::Destroy(MemoryAllocatedBuffer vmaAllocatedBuffer) { - assert(mVmaAllocator); - vmaDestroyBuffer(mVmaAllocator, vmaAllocatedBuffer.buffer, static_cast(vmaAllocatedBuffer.allocation.allocation)); - // Set the allocated buffer to a clean (deletable) state. - vmaAllocatedBuffer.allocation.clear(); - vmaAllocatedBuffer.buffer = VK_NULL_HANDLE; - // no error code from vmaDestroyBuffer so assume it worked (is also safe to pass an empty vmaAllocatedBuffer) + assert(mVmaAllocator); + vmaDestroyBuffer(mVmaAllocator, vmaAllocatedBuffer.buffer, static_cast(vmaAllocatedBuffer.allocation.allocation)); + // Set the allocated buffer to a clean (deletable) state. + vmaAllocatedBuffer.allocation.clear(); + vmaAllocatedBuffer.buffer = VK_NULL_HANDLE; + // no error code from vmaDestroyBuffer so assume it worked (is also safe to pass an empty vmaAllocatedBuffer) } /////////////////////////////////////////////////////////////////////////////// void MemoryManager::Destroy(MemoryAllocatedBuffer vmaAllocatedImage) { - assert(mVmaAllocator); - vmaDestroyImage(mVmaAllocator, vmaAllocatedImage.buffer, static_cast(vmaAllocatedImage.allocation.allocation)); - // Set the allocated buffer to a clean (deletable) state. - vmaAllocatedImage.allocation.clear(); - vmaAllocatedImage.buffer = VK_NULL_HANDLE; - // no error code from vmaDestroyImage so assume it worked (is also safe to pass an empty vmaAllocatedImage) + assert(mVmaAllocator); + vmaDestroyImage(mVmaAllocator, vmaAllocatedImage.buffer, static_cast(vmaAllocatedImage.allocation.allocation)); + // Set the allocated buffer to a clean (deletable) state. + vmaAllocatedImage.allocation.clear(); + vmaAllocatedImage.buffer = VK_NULL_HANDLE; + // no error code from vmaDestroyImage so assume it worked (is also safe to pass an empty vmaAllocatedImage) } /////////////////////////////////////////////////////////////////////////////// void MemoryManager::Destroy(MemoryAllocatedBuffer vmaAllocatedMemory) { - assert(mVmaAllocator); + assert(mVmaAllocator); vmaFreeMemory(mVmaAllocator, (VmaAllocation_T*)vmaAllocatedMemory.allocation.allocation); - // Set the allocated buffer to a clean (deletable) state. - vmaAllocatedMemory.allocation.clear(); - vmaAllocatedMemory.buffer = VK_NULL_HANDLE; + // Set the allocated buffer to a clean (deletable) state. + vmaAllocatedMemory.allocation.clear(); + vmaAllocatedMemory.buffer = VK_NULL_HANDLE; } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::DestroyBufferOnly(MemoryAllocatedBuffer vmaAllocatedBuffer) { - assert(mVmaAllocator); - vkDestroyBuffer(mGpuDevice, vmaAllocatedBuffer.buffer, nullptr); - MemoryAllocatedBuffer allocatedMemory; - allocatedMemory.buffer = static_cast(vmaAllocatedBuffer.allocation.allocation)->GetMemory(); - allocatedMemory.allocation = std::move(vmaAllocatedBuffer.allocation); - return allocatedMemory; + assert(mVmaAllocator); + vkDestroyBuffer(mGpuDevice, vmaAllocatedBuffer.buffer, nullptr); + MemoryAllocatedBuffer allocatedMemory; + allocatedMemory.buffer = static_cast(vmaAllocatedBuffer.allocation.allocation)->GetMemory(); + allocatedMemory.allocation = std::move(vmaAllocatedBuffer.allocation); + return allocatedMemory; } /////////////////////////////////////////////////////////////////////////////// MemoryAllocatedBuffer MemoryManager::DestroyBufferOnly(MemoryAllocatedBuffer vmaAllocatedImage) { - assert(mVmaAllocator); - vkDestroyImage(mGpuDevice, vmaAllocatedImage.buffer, nullptr); - MemoryAllocatedBuffer allocatedMemory; - allocatedMemory.buffer = static_cast(vmaAllocatedImage.allocation.allocation)->GetMemory(); - allocatedMemory.allocation = std::move(vmaAllocatedImage.allocation); - return allocatedMemory; + assert(mVmaAllocator); + vkDestroyImage(mGpuDevice, vmaAllocatedImage.buffer, nullptr); + MemoryAllocatedBuffer allocatedMemory; + allocatedMemory.buffer = static_cast(vmaAllocatedImage.allocation.allocation)->GetMemory(); + allocatedMemory.allocation = std::move(vmaAllocatedImage.allocation); + return allocatedMemory; } /////////////////////////////////////////////////////////////////////////////// VkDeviceAddress MemoryManager::GetBufferDeviceAddressInternal(VkBuffer buffer) const { - assert(mFpGetBufferDeviceAddress != nullptr); // need EnableBufferDeviceAddress parameter to be set on Initialize + assert(mFpGetBufferDeviceAddress != nullptr); // need EnableBufferDeviceAddress parameter to be set on Initialize - VkBufferDeviceAddressInfoEXT bufferAddressInfo = {}; - bufferAddressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT; - bufferAddressInfo.buffer = buffer; - return mFpGetBufferDeviceAddress(mGpuDevice, &bufferAddressInfo); + VkBufferDeviceAddressInfoEXT bufferAddressInfo = {}; + bufferAddressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT; + bufferAddressInfo.buffer = buffer; + return mFpGetBufferDeviceAddress(mGpuDevice, &bufferAddressInfo); } /////////////////////////////////////////////////////////////////////////////// void MemoryManager::MapInternal(void* vmaAllocation, void** outCpuLocation) { - assert(outCpuLocation != nullptr); - assert(*outCpuLocation == nullptr); // mapped twice? - assert(vmaAllocation != nullptr); // Likely caused by a double mapping of this data - if (vmaMapMemory(mVmaAllocator, static_cast(vmaAllocation), outCpuLocation) != VK_SUCCESS) - { - outCpuLocation = nullptr; - assert(0); - } + assert(outCpuLocation != nullptr); + assert(*outCpuLocation == nullptr); // mapped twice? + assert(vmaAllocation != nullptr); // Likely caused by a double mapping of this data + if (vmaMapMemory(mVmaAllocator, static_cast(vmaAllocation), outCpuLocation) != VK_SUCCESS) + { + outCpuLocation = nullptr; + assert(0); + } } /////////////////////////////////////////////////////////////////////////////// void MemoryManager::UnmapInternal(void* vmaAllocation, void* cpuLocation) { - assert(cpuLocation); - vmaUnmapMemory(mVmaAllocator, static_cast(vmaAllocation)); + assert(cpuLocation); + vmaUnmapMemory(mVmaAllocator, static_cast(vmaAllocation)); } + +/////////////////////////////////////////////////////////////////////////////// + +MemoryPool MemoryManager::CreateCustomPool( uint32_t typeIndex, uint32_t poolAllocationSize, uint32_t maxPoolAllocations, uint32_t bufferUsageFlag, uint32_t imageUsageFlag ) const +{ + VmaPoolCreateInfo poolCreateInfo{ + .memoryTypeIndex = typeIndex, + .blockSize = poolAllocationSize, + //.flags, + .maxBlockCount = maxPoolAllocations, + .pMemoryAllocateNext = nullptr + }; + + VmaPool vmaPool{}; + if (vmaCreatePool( mVmaAllocator, &poolCreateInfo, &vmaPool ) != VK_SUCCESS) + return {}; + return {vmaPool, bufferUsageFlag, imageUsageFlag}; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryAllocatedBuffer MemoryManager::AllocateMemory( MemoryPool& pool, size_t size, uint32_t memoryTypeBits ) +{ + VkMemoryRequirements memoryRequirements{.size = size, .memoryTypeBits = memoryTypeBits}; + + VmaAllocationCreateInfo allocInfo{ + .pool = (VmaPool)pool.pool + }; + + MemoryAllocatedBuffer vmaAllocatedMemory; + VmaAllocationInfo retInfo{}; + VkResult result = vmaAllocateMemory( mVmaAllocator, &memoryRequirements, &allocInfo, (VmaAllocation*)&vmaAllocatedMemory.allocation.allocation, &retInfo ); + if (result != VK_SUCCESS) + { + return {}; + } + vmaAllocatedMemory.buffer = static_cast(vmaAllocatedMemory.allocation.allocation)->GetMemory(); + + LOGI("Allocated memory from pool. size=%zu offset=%zu memoryType=0x%x", (size_t) retInfo.size, (size_t) retInfo.offset, (uint32_t) retInfo.memoryType); + return vmaAllocatedMemory; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryAllocatedBuffer MemoryManager::CreateBuffer( MemoryPool& pool, size_t size, BufferUsageFlags bufferUsage, VkDescriptorBufferInfo* pDescriptorBufferInfo ) +{ + assert( pool ); + static_assert(sizeof( VkBufferUsageFlags ) == sizeof( decltype(pool.bufferUsageFlag) )); + const VkBufferCreateInfo bufferInfo { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = (VkDeviceSize)size, + .usage = BufferUsageToVk( bufferUsage ) | pool.bufferUsageFlag + }; + + const VmaAllocationCreateInfo allocInfo = { + .pool = (VmaPool)pool.pool + }; + + MemoryAllocatedBuffer vmaAllocatedBuffer; + VmaAllocationInfo vmaAllocationInfo; + VkResult result = vmaCreateBuffer( mVmaAllocator, &bufferInfo, &allocInfo, &vmaAllocatedBuffer.buffer, (VmaAllocation*)&vmaAllocatedBuffer.allocation.allocation, &vmaAllocationInfo ); + if (result != VK_SUCCESS) + { + return {}; + } + if (pDescriptorBufferInfo) + { + *pDescriptorBufferInfo = {vmaAllocatedBuffer.GetVkBuffer(), 0, size}; + } + LOGI( "Successfully called vmaCreateBuffer with pool. size=%zu offset=%zu memoryType=0x%x", (size_t)vmaAllocationInfo.size, (size_t)vmaAllocationInfo.offset, (uint32_t)vmaAllocationInfo.memoryType ); + return vmaAllocatedBuffer; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemoryAllocatedBuffer MemoryManager::CreateImage( MemoryPool& pool, const VkImageCreateInfo& imageInfo ) +{ + assert( pool ); + const VmaAllocationCreateInfo allocInfo { + .pool = (VmaPool) pool.pool + }; + // Add in any flags specified with this pool + auto imageInfoCopy = imageInfo; + static_assert(sizeof( VkImageUsageFlags ) == sizeof( decltype(pool.imageUsageFlag) )); + imageInfoCopy.usage |= (VkImageUsageFlags) pool.imageUsageFlag; + + + + + + MemoryAllocatedBuffer vmaAllocatedImage; + VmaAllocationInfo vmaAllocationInfo; + + // Swap in our stub of vkGetImageMemoryRequirements2KHR + VmaVulkanFunctions& vmaVulkanFunctions = const_cast(mVmaAllocator->GetVulkanFunctions()); + s_oldGetImageMemReq = vmaVulkanFunctions.vkGetImageMemoryRequirements2KHR; + vmaVulkanFunctions.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2KHR_Qcom; + // Call vmaCreateImage (will use our stub to get memory requirements) + VkResult result = vmaCreateImage( mVmaAllocator, &imageInfoCopy, &allocInfo, &vmaAllocatedImage.buffer, (VmaAllocation*)&vmaAllocatedImage.allocation.allocation, &vmaAllocationInfo ); + // Put vkGetImageMemoryRequirements2KHR back to how it was! + vmaVulkanFunctions.vkGetImageMemoryRequirements2KHR = s_oldGetImageMemReq; + + if (result != VK_SUCCESS) + { + return {}; + } + LOGI( "Successfully called vmaCreateImageQCOM with pool. size=%zu offset=%zu memoryType=0x%x format=%s", (size_t)vmaAllocationInfo.size, (size_t)vmaAllocationInfo.offset, (uint32_t)vmaAllocationInfo.memoryType, Vulkan::VulkanFormatString(imageInfo.format) ); + return vmaAllocatedImage; +} + +/////////////////////////////////////////////////////////////////////////////// + +VkDeviceMemory MemoryManager::GetVkDeviceMemory( const MemoryAllocation& alloc ) const +{ + return static_cast(alloc.allocation)->GetMemory(); +} + diff --git a/framework/code/memory/vulkan/memoryManager.hpp b/framework/code/memory/vulkan/memoryManager.hpp index 0fe3b1e..8d0bec2 100644 --- a/framework/code/memory/vulkan/memoryManager.hpp +++ b/framework/code/memory/vulkan/memoryManager.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -16,7 +16,7 @@ #include #include -#include +#include #ifdef OS_WINDOWS #include #endif // OS_WINDOWS @@ -33,19 +33,65 @@ struct AHardwareBuffer; //template class MemoryAbhAllocatedBuffer { - MemoryAbhAllocatedBuffer(const MemoryAbhAllocatedBuffer&) = delete; - MemoryAbhAllocatedBuffer& operator=(const MemoryAbhAllocatedBuffer&) = delete; + MemoryAbhAllocatedBuffer(const MemoryAbhAllocatedBuffer&) = delete; + MemoryAbhAllocatedBuffer& operator=(const MemoryAbhAllocatedBuffer&) = delete; public: - friend class MemoryManager; - // Restrict MemoryAbhAllocatedBuffer to not be duplicated and not accidentally deleted (leaking memory). - MemoryAbhAllocatedBuffer(MemoryAbhAllocatedBuffer&& other); - MemoryAbhAllocatedBuffer& operator=(MemoryAbhAllocatedBuffer&& other); - MemoryAbhAllocatedBuffer() {} -// const T_VKTYPE& GetVkBuffer() const { return buffer; } - explicit operator bool() const { return static_cast(allocation); } + friend class MemoryManager; + // Restrict MemoryAbhAllocatedBuffer to not be duplicated and not accidentally deleted (leaking memory). + MemoryAbhAllocatedBuffer(MemoryAbhAllocatedBuffer&& other); + MemoryAbhAllocatedBuffer& operator=(MemoryAbhAllocatedBuffer&& other); + MemoryAbhAllocatedBuffer() {} +// const T_VKTYPE& GetVkBuffer() const { return buffer; } + explicit operator bool() const { return static_cast(allocation); } private: - AHardwareBuffer* allocation = nullptr; -// T_VKTYPE buffer = VK_NULL_HANDLE; // the allocated vulkan buffer (or VK_NULL_HANDLE) + AHardwareBuffer* allocation = nullptr; +// T_VKTYPE buffer = VK_NULL_HANDLE; // the allocated vulkan buffer (or VK_NULL_HANDLE) +}; + + +/// Wraps the VMA custom pool handle. Represents a pool allocator for 'gpu' memory. Cannot be copied (only moved) - single owner. +/// @ingroup Memory +template +class MemoryPool final +{ + friend class MemoryManager; + //MemoryPool( void* _pool ) noexcept : pool( _pool ) {} + MemoryPool( void* _pool, uint32_t _bufferUsageFlag, uint32_t _imageUsageFlag ) noexcept + : pool( _pool ) + , bufferUsageFlag(_bufferUsageFlag) + , imageUsageFlag(_imageUsageFlag) + {} + +public: + MemoryPool( const MemoryPool& ) = delete; + MemoryPool& operator=( const MemoryPool& ) = delete; + MemoryPool() noexcept {} + MemoryPool( MemoryPool&& other ) noexcept + : pool( other.pool ) + , bufferUsageFlag( other.bufferUsageFlag ) + , imageUsageFlag( other.imageUsageFlag ) + { + other.pool = nullptr; + }; + MemoryPool& operator=( MemoryPool&& other ) noexcept { + if (this != &other) + { + pool = other.pool; + other.pool = nullptr; + bufferUsageFlag = other.bufferUsageFlag; + other.bufferUsageFlag = 0; + imageUsageFlag = other.imageUsageFlag; + other.imageUsageFlag = 0; + } + return *this; + } + ~MemoryPool() { assert( !pool ); } // protect accidental deletion (leak) + explicit operator bool() const { return pool != nullptr; } +private: + void clear() { pool = nullptr; } + void* pool = nullptr; // anonymous handle (gpx api specific) + uint32_t bufferUsageFlag = 0; // Additional flag that should be applied (or'ed) to creation parameters when buffers created out of this heap (eg VkBufferCreateInfo::usage) + uint32_t imageUsageFlag = 0; // Additional flag that should be applied (or'ed) to creation parameters when images created out of this heap (eg VkImageCreateInfo::usage) }; @@ -54,77 +100,92 @@ class MemoryAbhAllocatedBuffer template<> class MemoryManager { - MemoryManager(const MemoryManager&) = delete; - MemoryManager& operator=(const MemoryManager&) = delete; + MemoryManager(const MemoryManager&) = delete; + MemoryManager& operator=(const MemoryManager&) = delete; public: - MemoryManager(); - ~MemoryManager(); - using tGfxApi = Vulkan; - - /// Initialize the memory manager (must be initialized before using CreateBuffer etc) - /// @param EnableBufferDeviceAddress enable ability to call GetBufferDeviceAddress - /// @return true if successfully initialized - bool Initialize(VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkInstance vkInstance, bool EnableBufferDeviceAddress); - /// Destroy the memory manager (do before you destroy the Vulkan device) - void Destroy(); - - /// Create buffer in memory and create the associated Vulkan objects - MemoryAllocatedBuffer CreateBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage, VkDescriptorBufferInfo* /*output, optional*/ = nullptr); - /// Create image in memory and create the associated Vulkan objects - MemoryAllocatedBuffer CreateImage(const VkImageCreateInfo& imageInfo, MemoryUsage memoryUsage); - /// Allocation with 'unknown' use type. Typically you would use CreateBuffer or CreateImage, this is for special cases! - MemoryAllocatedBuffer AllocateMemory(size_t size, uint32_t memoryTypeBits); - /// Bind the provided image to already allocated memory (assumes the memory was allocated to be compatible) - /// Memory allocation is transfered from the input MemoryVmaAllocatedBuffer to the returned MemoryVmaAllocatedBuffer (ownership transfer of memory and image) - MemoryAllocatedBuffer BindImageToMemory(VkImage image, MemoryAllocatedBuffer&& memory) const; - /// Bind the provided buffer to already allocated memory (assumes the memory was allocated to be compatible) - /// Memory allocation is transfered from the input MemoryVmaAllocatedBuffer to the returned MemoryVmaAllocatedBuffer (ownership transfer of memory and buffer) - MemoryAllocatedBuffer BindBufferToMemory(VkBuffer buffer, MemoryAllocatedBuffer&& memory) const; - - /// Destruction of created buffer - void Destroy(MemoryAllocatedBuffer); - /// Destruction of created image - void Destroy(MemoryAllocatedBuffer); - /// Destruction of a memory buffer (without a bound vkImage or vkBuffer) - void Destroy(MemoryAllocatedBuffer vmaAllocatedMemory); - /// Destruction of just the bound buffer (not the underlying device memory) - MemoryAllocatedBuffer DestroyBufferOnly(MemoryAllocatedBuffer); - /// Destruction of just the bound image (not the underlying device memory) - MemoryAllocatedBuffer DestroyBufferOnly(MemoryAllocatedBuffer); - - // Creation of Android Hardware buffer (with a Vulkan object) - MemoryAbhAllocatedBuffer CreateAndroidHardwareBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage); - - /// Map a buffer to cpu memory - template - MemoryCpuMapped Map(MemoryAllocatedBuffer& buffer); - - /// Unmap a buffer from cpu memory - template - void Unmap(MemoryAllocatedBuffer& buffer, MemoryCpuMappedUntyped allocation); + MemoryManager(); + ~MemoryManager(); + + /// Initialize the memory manager (must be initialized before using CreateBuffer etc) + /// @param EnableBufferDeviceAddress enable ability to call GetBufferDeviceAddress + /// @return true if successfully initialized + bool Initialize(VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, VkInstance vkInstance, bool EnableBufferDeviceAddress); + /// Destroy the memory manager (do before you destroy the Vulkan device) + void Destroy(); + + /// Create buffer in memory and create the associated Vulkan objects + MemoryAllocatedBuffer CreateBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage, VkDescriptorBufferInfo* /*output, optional*/ = nullptr); + /// Create image in memory and create the associated Vulkan objects + MemoryAllocatedBuffer CreateImage(const VkImageCreateInfo& imageInfo, MemoryUsage memoryUsage); + /// Allocation with 'unknown' use type. Typically you would use CreateBuffer or CreateImage, this is for special cases! + MemoryAllocatedBuffer AllocateMemory(size_t size, uint32_t memoryTypeBits); + /// Bind the provided image to already allocated memory (assumes the memory was allocated to be compatible) + /// Memory allocation is transfered from the input MemoryVmaAllocatedBuffer to the returned MemoryVmaAllocatedBuffer (ownership transfer of memory and image) + MemoryAllocatedBuffer BindImageToMemory(VkImage image, MemoryAllocatedBuffer&& memory) const; + /// Bind the provided buffer to already allocated memory (assumes the memory was allocated to be compatible) + /// Memory allocation is transfered from the input MemoryVmaAllocatedBuffer to the returned MemoryVmaAllocatedBuffer (ownership transfer of memory and buffer) + MemoryAllocatedBuffer BindBufferToMemory(VkBuffer buffer, MemoryAllocatedBuffer&& memory) const; + + /// Destruction of created buffer + void Destroy(MemoryAllocatedBuffer); + /// Destruction of created image + void Destroy(MemoryAllocatedBuffer); + /// Destruction of a memory buffer (without a bound vkImage or vkBuffer) + void Destroy(MemoryAllocatedBuffer vmaAllocatedMemory); + /// Destruction of just the bound buffer (not the underlying device memory) + MemoryAllocatedBuffer DestroyBufferOnly(MemoryAllocatedBuffer); + /// Destruction of just the bound image (not the underlying device memory) + MemoryAllocatedBuffer DestroyBufferOnly(MemoryAllocatedBuffer); + + // Creation of Android Hardware buffer (with a Vulkan object) + MemoryAbhAllocatedBuffer CreateAndroidHardwareBuffer(size_t size, BufferUsageFlags bufferUsage, MemoryUsage memoryUsage); + + /// Map a buffer to cpu memory + template + MemoryCpuMapped Map(MemoryAllocatedBuffer& buffer); + + /// Unmap a buffer from cpu memory + template + void Unmap(MemoryAllocatedBuffer& buffer, MemoryCpuMappedUntyped allocation); /// Copy data in one buffer into another. Assumes buffers created with appropriate VK_BUFFER_USAGE_TRANSFER_SRC_BIT and VK_BUFFER_USAGE_TRANSFER_DST_BIT bool CopyData(VkCommandBuffer vkCommandBuffer, const MemoryAllocatedBuffer& src, MemoryAllocatedBuffer& dst, size_t copySize, size_t srcOffset = 0, size_t dstOffset = 0); - /// Query the device address (assuming that Vulkan extension was enabled) - VkDeviceAddress GetBufferDeviceAddress(VkBuffer b) const { return GetBufferDeviceAddressInternal(b); } - /// Query the device address (assuming that Vulkan extension was enabled) - template - VkDeviceAddress GetBufferDeviceAddress(const T& b) const { return GetBufferDeviceAddress(b.GetVkBuffer()); } + /// Query the device address (assuming that Vulkan extension was enabled) + VkDeviceAddress GetBufferDeviceAddress(VkBuffer b) const { return GetBufferDeviceAddressInternal(b); } + /// Query the device address (assuming that Vulkan extension was enabled) + template + VkDeviceAddress GetBufferDeviceAddress(const T& b) const { return GetBufferDeviceAddress(b.GetVkBuffer()); } + + /// @brief Create a custom memory pool using the given memory heap + /// @param heapIndex index of the Vulkan heap to use for this pool + /// @param poolAllocationSize size of each 'chunk' allocation made by this pool + /// @param maxPoolAllocations maximum number of 'chunk' allocations that can be made by this pool (NOT the number of buffers/iamges that can be allocated from those chunks) + /// @param bufferUsageFlag value to | into VkBufferCreateInfo::usage + /// @param imageUsageFlag value to | into VkImageCreateInfo::usage + /// @return + MemoryPool CreateCustomPool( uint32_t typeIndex, uint32_t poolAllocationSize, uint32_t maxPoolAllocations, uint32_t bufferUsageFlag, uint32_t imageUsageFlag ) const; + + /// @brief Allocate memory out of a pool + MemoryAllocatedBuffer AllocateMemory( MemoryPool& pool, size_t size, uint32_t memoryTypeBits ); + MemoryAllocatedBuffer CreateBuffer( MemoryPool& pool, size_t size, BufferUsageFlags bufferUsage, VkDescriptorBufferInfo* pDescriptorBufferInfo ); + MemoryAllocatedBuffer CreateImage( MemoryPool& pool, const VkImageCreateInfo& imageInfo ); + + VkDeviceMemory GetVkDeviceMemory(const MemoryAllocation& ) const; protected: - MemoryCpuMappedUntyped MapInt(MemoryAllocation allocation); + MemoryCpuMappedUntyped MapInt(MemoryAllocation allocation); private: - VkDeviceAddress GetBufferDeviceAddressInternal(VkBuffer buffer) const; - void MapInternal(void* vmaAllocation, void** outCpuLocation); - void UnmapInternal(void* vmaAllocation, void* cpuLocation); + VkDeviceAddress GetBufferDeviceAddressInternal(VkBuffer buffer) const; + void MapInternal(void* vmaAllocation, void** outCpuLocation); + void UnmapInternal(void* vmaAllocation, void* cpuLocation); private: - VmaAllocator_T* mVmaAllocator = nullptr; - VkDevice mGpuDevice = VK_NULL_HANDLE; + VmaAllocator_T* mVmaAllocator = nullptr; + VkDevice mGpuDevice = VK_NULL_HANDLE; #if VK_KHR_buffer_device_address - PFN_vkGetBufferDeviceAddressKHR mFpGetBufferDeviceAddress = nullptr; + PFN_vkGetBufferDeviceAddressKHR mFpGetBufferDeviceAddress = nullptr; #elif VK_EXT_buffer_device_address - PFN_vkGetBufferDeviceAddressEXT mFpGetBufferDeviceAddress = nullptr; + PFN_vkGetBufferDeviceAddressEXT mFpGetBufferDeviceAddress = nullptr; #endif // VK_KHR_buffer_device_address | VK_EXT_buffer_device_address }; @@ -132,21 +193,21 @@ class MemoryManager template MemoryCpuMapped MemoryManager::Map(MemoryAllocatedBuffer& buffer) { - return MapInt(std::move(buffer.allocation)); + return MapInt(std::move(buffer.allocation)); } template void MemoryManager::Unmap(MemoryAllocatedBuffer& buffer, MemoryCpuMappedUntyped allocation) { - assert(allocation.mCpuLocation); - UnmapInternal(allocation.mAllocation.allocation, allocation.mCpuLocation); - allocation.mCpuLocation = nullptr; // clear ownership - buffer.allocation = std::move(allocation.mAllocation); + assert(allocation.mCpuLocation); + UnmapInternal(allocation.mAllocation.allocation, allocation.mCpuLocation); + allocation.mCpuLocation = nullptr; // clear ownership + buffer.allocation = std::move(allocation.mAllocation); } inline MemoryCpuMappedUntyped MemoryManager::MapInt(MemoryAllocation allocation) { - MemoryCpuMappedUntyped guard(std::move(allocation)); - MapInternal(guard.mAllocation.allocation, &guard.mCpuLocation); - return guard; + MemoryCpuMappedUntyped guard(std::move(allocation)); + MapInternal(guard.mAllocation.allocation, &guard.mCpuLocation); + return guard; } diff --git a/framework/code/memory/vulkan/memoryMapped.hpp b/framework/code/memory/vulkan/memoryMapped.hpp index a27de42..12398e5 100644 --- a/framework/code/memory/vulkan/memoryMapped.hpp +++ b/framework/code/memory/vulkan/memoryMapped.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -17,6 +17,27 @@ class Vulkan; template class MemoryAllocatedBuffer; + +/// Wraps the Vk Allocation handle. Represents an allocation out of 'vulkan' memory. Cannot be copied (only moved) - single owner. +/// Passed around between whomever is 'owner' of the allocation (when mapping etc) +/// @ingroup Memory +template<> +class MemoryAllocation final +{ + friend class MemoryManager; +public: + MemoryAllocation( const MemoryAllocation& ) = delete; + MemoryAllocation& operator=( const MemoryAllocation& ) = delete; + MemoryAllocation() {} + MemoryAllocation( MemoryAllocation&& other ) noexcept; + MemoryAllocation& operator=( MemoryAllocation&& other ) noexcept; + ~MemoryAllocation() { assert( allocation == nullptr ); } // protect accidental deletion (leak) + explicit operator bool() const { return allocation != nullptr; } +private: + void clear() { allocation = nullptr; } + void* allocation = nullptr; // anonymous handle (gpx api specific) +}; + /// Vulkan template specialization of MemoryAllocatedBuffer /// Represents a memory block allocated by VMA and that has either a VkImage or VkBuffer /// @tparam T_VKTYPE underlying Vulkan buffer type - VkImage or VkBuffer @@ -33,12 +54,31 @@ class MemoryAllocatedBuffer MemoryAllocatedBuffer& operator=(MemoryAllocatedBuffer&& other) noexcept; MemoryAllocatedBuffer() noexcept {} const T_VKTYPE& GetVkBuffer() const { return buffer; } + const MemoryAllocation& GetMemoryAllocation() const { return allocation; } explicit operator bool() const { return static_cast(allocation); } private: MemoryAllocation allocation; T_VKTYPE buffer = 0; // the allocated vulkan buffer (or VK_NULL_HANDLE) }; +// +// MemoryAllocation implementation +// +inline MemoryAllocation::MemoryAllocation( MemoryAllocation&& other ) noexcept +{ + allocation = other.allocation; + other.allocation = nullptr; +} + +inline MemoryAllocation& MemoryAllocation::operator=( MemoryAllocation&& other ) noexcept +{ + if (&other != this) { + allocation = other.allocation; + other.allocation = nullptr; + } + return *this; +} + // // MemoryAllocatedBuffer vulkan specialization implementation // diff --git a/framework/code/memory/vulkan/uniform.cpp b/framework/code/memory/vulkan/uniform.cpp index df9820e..59de4b9 100644 --- a/framework/code/memory/vulkan/uniform.cpp +++ b/framework/code/memory/vulkan/uniform.cpp @@ -1,10 +1,11 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== +#include #include #include "memory/memoryMapped.hpp" #include "vulkan/vulkan.hpp" diff --git a/framework/code/memory/vulkan/uniform.hpp b/framework/code/memory/vulkan/uniform.hpp index 33c8848..2e0dab9 100644 --- a/framework/code/memory/vulkan/uniform.hpp +++ b/framework/code/memory/vulkan/uniform.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once /// @file uniform.hpp @@ -14,12 +14,13 @@ #include "memoryMapped.hpp" #include "memory/memory.hpp" #include "memory/uniform.hpp" -#include +#include // forward defines class Vulkan; template struct Uniform; using UniformVulkan = Uniform; +template using UniformVulkanArray = UniformArray; /// All the variables needed to create a uniform buffer template<> @@ -31,6 +32,7 @@ struct Uniform Uniform(Uniform&&) noexcept; MemoryAllocatedBuffer buf; VkDescriptorBufferInfo bufferInfo; + bool IsEmpty() const { return !buf; } }; /// Uniform buffer array that can be updated every frame without stomping the in-flight uniform buffers. @@ -42,8 +44,9 @@ struct UniformArray UniformArray& operator=(const UniformArray&) = delete; UniformArray(UniformArray&&) noexcept { assert(0); } std::array, T_NUM_BUFFERS> buf; - std::array vkBuffers{}; ///< copy of VkBuffer handles (for easy use in bindings etc) + std::array bufferHandles{}; ///< copy of VkBuffer handles (for easy use in bindings etc) constexpr size_t size() const { return T_NUM_BUFFERS; } + bool IsEmpty() const { return !buf[0]; } }; @@ -82,7 +85,7 @@ bool CreateUniformBuffer(Vulkan* pVulkan, UniformArray& r { if (!CreateUniformBuffer(pVulkan, rNewUniform.buf[i], dataSize, pData, usage)) return false; - rNewUniform.vkBuffers[i] = rNewUniform.buf[i].GetVkBuffer(); + rNewUniform.bufferHandles[i] = rNewUniform.buf[i].GetVkBuffer(); } return true; } @@ -96,7 +99,7 @@ void ReleaseUniformBuffer(Vulkan* pVulkan, UniformArray& { for (size_t i = 0; i < T_NUM_BUFFERS; ++i) { - rUniform.vkBuffers[i] = VK_NULL_HANDLE; + rUniform.bufferHandles[i] = VK_NULL_HANDLE; ReleaseUniformBuffer(pVulkan, rUniform.buf[i]); } } @@ -117,3 +120,8 @@ void UpdateUniformBuffer(Vulkan* pVulkan, UniformArrayT::value, "UpdateUniformBuffer, uniform is different type to newData"); return UpdateUniformBuffer(pVulkan, static_cast&>(uniform), newData, bufferIdx); } +template +void ReleaseUniformBuffer(Vulkan* pVulkan, UniformArrayT& uniform) +{ + ReleaseUniformBuffer(pVulkan, static_cast&>(uniform)); +} diff --git a/framework/code/memory/vulkan/vertexBufferObject.cpp b/framework/code/memory/vulkan/vertexBufferObject.cpp index 2915772..11d115a 100644 --- a/framework/code/memory/vulkan/vertexBufferObject.cpp +++ b/framework/code/memory/vulkan/vertexBufferObject.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,7 +11,7 @@ VertexBuffer::VertexBuffer() noexcept - : BufferT() + : Buffer() , mSpan(0) , mNumVertices(0) , mDspUsable(false) @@ -38,7 +38,7 @@ VertexBuffer::VertexBuffer(VertexBuffer&& other) noexcept VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other) noexcept { - BufferT::operator=(std::move(other)); + Buffer::operator=(std::move(other)); if (&other != this) { mSpan = other.mSpan; @@ -68,18 +68,32 @@ bool VertexBuffer::Initialize(MemoryManager* pManager, size_t span, size } else { - return BufferT::Initialize(pManager, static_cast(mSpan * mNumVertices), usage, initialData); + return Buffer::Initialize(pManager, static_cast(mSpan * mNumVertices), usage, initialData); } } /////////////////////////////////////////////////////////////////////////////// +bool VertexBuffer::Update(MemoryManager* pManager, size_t dataSize, const void* newData) +{ + return Buffer::Update(pManager, dataSize, newData); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VertexBuffer::GetMeshData(MemoryManager* pManager, size_t dataSize, void* outputData) const +{ + return Buffer::GetMeshData(pManager, dataSize, outputData); +} + +/////////////////////////////////////////////////////////////////////////////// + void VertexBuffer::Destroy() { mBindings.clear(); mAttributes.clear(); mNumVertices = 0; - BufferT::Destroy(); + Buffer::Destroy(); } /////////////////////////////////////////////////////////////////////////////// @@ -117,7 +131,7 @@ VertexBuffer VertexBuffer::Copy( VkCommandBuffer vkCommandBuffer // Create the buffer we are copying into. size_t size = GetSpan() * GetNumVertices(); - if (!copy.BufferT::Initialize( mManager, size, bufferUsage, memoryUsage )) + if (!copy.Buffer::Initialize( mManager, size, bufferUsage, memoryUsage )) return {}; if (((mBufferUsageFlags & BufferUsageFlags::TransferSrc) == 0) || ((bufferUsage & BufferUsageFlags::TransferDst) == 0)) diff --git a/framework/code/memory/vulkan/vertexBufferObject.hpp b/framework/code/memory/vulkan/vertexBufferObject.hpp index b7eef5f..17e7558 100644 --- a/framework/code/memory/vulkan/vertexBufferObject.hpp +++ b/framework/code/memory/vulkan/vertexBufferObject.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,15 +10,15 @@ #include "vulkan/vulkan.hpp" #include "bufferObject.hpp" #include "memory/vertexBuffer.hpp" +#include class Vulkan; -using VertexBufferObject = VertexBuffer; /// Buffer containing vertex data (specialized for Vulkan). /// @ingroup Memory template<> -class VertexBuffer : public BufferT +class VertexBuffer : public Buffer { VertexBuffer& operator=(const VertexBuffer&) = delete; VertexBuffer(const VertexBuffer&) = delete; @@ -30,7 +30,13 @@ class VertexBuffer : public BufferT bool Initialize(MemoryManager* pManager, size_t span, size_t numVerts, const void* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex ); template - bool Initialize(MemoryManager* pManager, size_t numVerts, const T* initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex); + bool Initialize(MemoryManager* pManager, const std::span initialData, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex); + template + bool Initialize(MemoryManager* pManager, size_t numVerts, const bool dspUsable = false, const BufferUsageFlags usage = BufferUsageFlags::Vertex); + + bool Update(MemoryManager* pManager, size_t dataSize, const void* newData); + + bool GetMeshData(MemoryManager* pManager, size_t dataSize, void* outputData) const; /// destroy buffer and leave in a state where it could be re-initialized virtual void Destroy() override; @@ -77,7 +83,13 @@ class VertexBuffer : public BufferT }; template -bool VertexBuffer::Initialize(MemoryManager* pManager, size_t numVerts, const T* initialData, const bool dspUsable, const BufferUsageFlags usage ) +bool VertexBuffer::Initialize(MemoryManager* pManager, const std::span initialData, const bool dspUsable, const BufferUsageFlags usage) +{ + return Initialize(pManager, sizeof(T), initialData.size(), initialData.data(), dspUsable, usage); +} + +template +bool VertexBuffer::Initialize(MemoryManager* pManager, size_t numVertices, const bool dspUsable, const BufferUsageFlags usage) { - return Initialize(pManager, sizeof(T), numVerts, initialData, dspUsable, usage); + return Initialize(pManager, sizeof(T), numVertices, nullptr, dspUsable, usage); } diff --git a/framework/code/mesh/instanceGenerator.cpp b/framework/code/mesh/instanceGenerator.cpp index 4eba4fc..f9f0325 100644 --- a/framework/code/mesh/instanceGenerator.cpp +++ b/framework/code/mesh/instanceGenerator.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,11 +11,6 @@ #include #define EIGEN_INITIALIZE_MATRICES_BY_ZERO #define EIGEN_MPL2_ONLY - -#if defined(_WIN32) -#define EIGEN_HAS_STD_RESULT_OF 0 -#endif - #include #include #include diff --git a/framework/code/mesh/instanceGenerator.hpp b/framework/code/mesh/instanceGenerator.hpp index 1dad295..70d7483 100644 --- a/framework/code/mesh/instanceGenerator.hpp +++ b/framework/code/mesh/instanceGenerator.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/mesh/mesh.hpp b/framework/code/mesh/mesh.hpp index 7672919..24e37e4 100644 --- a/framework/code/mesh/mesh.hpp +++ b/framework/code/mesh/mesh.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include diff --git a/framework/code/mesh/meshHelper.cpp b/framework/code/mesh/meshHelper.cpp index 81b4127..9d6427a 100644 --- a/framework/code/mesh/meshHelper.cpp +++ b/framework/code/mesh/meshHelper.cpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #include "meshHelper.hpp" #include "material/vertexFormat.hpp" diff --git a/framework/code/mesh/meshHelper.hpp b/framework/code/mesh/meshHelper.hpp index fcaac2b..a3e91d5 100644 --- a/framework/code/mesh/meshHelper.hpp +++ b/framework/code/mesh/meshHelper.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include "mesh.hpp" @@ -45,8 +45,8 @@ struct MeshHelper template static bool CreateMesh(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, uint32_t bindingIndex, const std::span pVertexFormat, Mesh* meshObjectOut); - /// Helper to create a IndexBufferObject, IF the mesh object has index buffer data. - /// @returns true on success (including no index buffer data existing in IndexBufferObject), false on error. + /// Helper to create a IndexBuffer object, IF the mesh object has index buffer data. + /// @returns true on success (including no index buffer data existing in IndexBuffer object), false on error. template static bool CreateIndexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, std::optional>& indexBufferOut, BufferUsageFlags usage = BufferUsageFlags::Index); @@ -59,6 +59,29 @@ struct MeshHelper /// @return true on success template static bool CreateScreenSpaceMesh(MemoryManager& memoryManager, glm::vec4 PosLLRadius, glm::vec4 UVLLRadius, uint32_t binding, Mesh* meshObjectOut); + + /// @brief Templated function to update a renderable Mesh + /// @tparam T_GFXAPI Graphics API class. + /// @param memoryManager + /// @param meshObject + /// @param whichBuffer + /// @param dataSize + /// @param newData + /// @return true on success + template + static bool UpdateMesh(MemoryManager& memoryManager, Mesh& meshObject, uint32_t whichBuffer, size_t dataSize, void *newData); + + /// @brief Templated function to update a renderable Mesh + /// @tparam T_GFXAPI Graphics API class. + /// @param memoryManager + /// @param meshObject + /// @param whichBuffer + /// @param dataSize + /// @param outputData + /// @return true on success + template + static bool GetMeshData(MemoryManager& memoryManager, const Mesh& meshObject, uint32_t whichBuffer, size_t dataSize, void *outputData); + }; /////////////////////////////////////////////////////////////////////////////// @@ -84,7 +107,7 @@ bool MeshHelper::CreateMesh(MemoryManager& memoryManager, const MeshOb const auto& vertexFormat = pVertexFormat[vertexBufferIdx]; if (vertexFormat.inputRate == VertexFormat::eInputRate::Vertex) { - const std::vector formattedVertexData = MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(meshObject.m_VertexBuffer, pVertexFormat[vertexBufferIdx]); + const std::vector formattedVertexData = MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(meshObject.m_VertexBuffer, meshObject.m_WeightBuffer, pVertexFormat[vertexBufferIdx]); if (!meshObjectOut->m_VertexBuffers.emplace_back().Initialize(&memoryManager, vertexFormat.span, numVertices, formattedVertexData.data())) { @@ -106,6 +129,40 @@ bool MeshHelper::CreateMesh(MemoryManager& memoryManager, const MeshOb /////////////////////////////////////////////////////////////////////////////// +template +bool MeshHelper::UpdateMesh(MemoryManager& memoryManager, Mesh& meshObject, uint32_t whichBuffer, size_t dataSize, void* newData) +{ + // Make sure the vertex buffer is in range + if (whichBuffer >= meshObject.m_VertexBuffers.size()) + { + LOGE("MeshHelper::UpdateMesh: Error! Buffer index (%d) out of range (%d vertex buffers)", whichBuffer, (int)meshObject.m_VertexBuffers.size()); + return false; + } + + meshObject.m_VertexBuffers[0].Update(&memoryManager, dataSize, newData); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +bool MeshHelper::GetMeshData(MemoryManager& memoryManager, const Mesh& meshObject, uint32_t whichBuffer, size_t dataSize, void* outputData) +{ + // Make sure the vertex buffer is in range + if (whichBuffer >= meshObject.m_VertexBuffers.size()) + { + LOGE("MeshHelper::GetMeshData: Error! Buffer index (%d) out of range (%d vertex buffers)", whichBuffer, (int)meshObject.m_VertexBuffers.size()); + return false; + } + + meshObject.m_VertexBuffers[0].GetMeshData(&memoryManager, dataSize, outputData); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + template bool MeshHelper::CreateIndexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, std::optional>& indexBufferOut, BufferUsageFlags usage) { @@ -118,7 +175,7 @@ bool MeshHelper::CreateIndexBuffer(MemoryManager& memoryManager, const if constexpr (std::is_same_v>) { auto& indexBuffer = indexBufferOut.emplace(IndexType::IndexU32); const auto& vec32 = std::get>(meshObject.m_IndexBuffer); - if (!indexBuffer.Initialize(&memoryManager, vec32.size(), vec32.data(), false, usage)) + if (!indexBuffer.Initialize(&memoryManager, std::span(vec32), false, usage)) { return false; } @@ -126,7 +183,7 @@ bool MeshHelper::CreateIndexBuffer(MemoryManager& memoryManager, const else if constexpr (std::is_same_v>) { auto& indexBuffer = indexBufferOut.emplace(IndexType::IndexU16); const auto& vec16 = std::get>(meshObject.m_IndexBuffer); - if (!indexBuffer.Initialize(&memoryManager, vec16.size(), vec16.data(), false, usage)) + if (!indexBuffer.Initialize(&memoryManager, std::span(vec16), false, usage)) { return false; } @@ -147,7 +204,7 @@ bool MeshHelper::CreateIndexBuffer(MemoryManager& memoryManager, const template bool MeshHelper::CreateScreenSpaceMesh( MemoryManager& memoryManager, uint32_t binding, Mesh* meshObjectOut ) { - return MeshHelper::CreateMesh( memoryManager, MeshObjectIntermediate::CreateScreenSpaceMesh(), binding, { &vertex_layout::sFormat,1 }, meshObjectOut ); + return MeshHelper::CreateMesh( memoryManager, MeshObjectIntermediate::CreateScreenSpaceMesh(), binding, { &vertex_layout::sFormat,1 }, meshObjectOut ); } /////////////////////////////////////////////////////////////////////////////// @@ -155,5 +212,5 @@ bool MeshHelper::CreateScreenSpaceMesh( MemoryManager& memoryManager, template bool MeshHelper::CreateScreenSpaceMesh( MemoryManager& memoryManager, glm::vec4 PosLLRadius, glm::vec4 UVLLRadius, uint32_t binding, Mesh* meshObjectOut ) { - return MeshHelper::CreateMesh( memoryManager, MeshObjectIntermediate::CreateScreenSpaceMesh(PosLLRadius, UVLLRadius), binding, { &vertex_layout::sFormat,1 }, meshObjectOut ); + return MeshHelper::CreateMesh( memoryManager, MeshObjectIntermediate::CreateScreenSpaceMesh(PosLLRadius, UVLLRadius), binding, { &vertex_layout::sFormat,1 }, meshObjectOut ); } diff --git a/framework/code/mesh/meshIntermediate.cpp b/framework/code/mesh/meshIntermediate.cpp index 507adc9..f003128 100644 --- a/framework/code/mesh/meshIntermediate.cpp +++ b/framework/code/mesh/meshIntermediate.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -162,6 +162,7 @@ static void CalculateTangentAndBitangent(const float* objNormal, const glm::vec3 void MeshObjectIntermediate::Release() { std::vector().swap(m_VertexBuffer); // use swap so we know the memory disappears (clear may leave the memory 'reserved'). + std::vector().swap(m_WeightBuffer); std::vector().swap(m_Materials); m_Transform = glm::identity(); m_NodeId = -1; @@ -212,22 +213,30 @@ MeshObjectIntermediate MeshObjectIntermediate::CopyFlattened() const std::visit([&](auto& m) { using T = std::decay_t; - if constexpr (std::is_same_v> || std::is_same_v>) + if constexpr (std::is_same_v> || std::is_same_v> || std::is_same_v>) { - dst.m_VertexBuffer.reserve(m.size()); - for (const auto index : m ) + dst.m_VertexBuffer.resize(m.size()); + std::transform(std::begin(m), std::end(m), std::begin(dst.m_VertexBuffer), [this](const auto index) { + return this->m_VertexBuffer[index]; + }); + if (!this->m_WeightBuffer.empty()) { - dst.m_VertexBuffer.push_back(this->m_VertexBuffer[index]); + dst.m_WeightBuffer.resize(m.size()); + std::transform(std::begin(m), std::end(m), std::begin(dst.m_WeightBuffer), [this](const auto index) { + return this->m_WeightBuffer[index]; + }); } } else { dst.m_VertexBuffer = m_VertexBuffer; + dst.m_WeightBuffer = m_WeightBuffer; } }, this->m_IndexBuffer); dst.m_Materials = m_Materials; dst.m_Transform = m_Transform; dst.m_NodeId = m_NodeId; + dst.m_WeightsPerVertex = m_WeightsPerVertex; return dst; } @@ -338,6 +347,7 @@ std::vector MeshObjectIntermediate::LoadObj(AssetManager materials[faceMaterialId].specular_texname, 0.5f, // metallicFactor 0.5f, // roughnessFactor + glm::vec3(0.0f), // emissiveFactor !materials[faceMaterialId].alpha_texname.empty(), materials[faceMaterialId].illum == 7 }); @@ -497,7 +507,7 @@ MeshObjectIntermediate MeshObjectIntermediate::CreateScreenSpaceTriMesh() // String literal lower case hash (constexpr) constexpr FnvHashLower operator "" _h(const char* str, size_t) { return FnvHashLower(str); } -std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(const std::span& fatVertexBuffer, const VertexFormat& vertexFormat) +std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(const std::span& fatVertexBuffer, const std::span& fatWeightBuffer, const VertexFormat& vertexFormat) { // // Determine the arrangement of the output data (based on the vertexFormat) @@ -509,6 +519,8 @@ std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(con int colorIndex = -1; int tangentIndex = -1; int bitangentIndex = -1; + int jointIndex = -1; + int weightIndex = -1; for (int i = 0; i < vertexFormat.elementIds.size(); ++i) { @@ -535,30 +547,46 @@ std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(con case "Bitangent"_h: bitangentIndex = i; break; + case "Joint"_h: + jointIndex = i; + break; + case "Weight"_h: + weightIndex = i; + break; default: LOGE("Cannot map vertex elementId %s to the mesh data", elementId.c_str()); break; } } - const uint32_t destSpan32 = (uint32_t)vertexFormat.span / 4; // span of output data (in 32bit words) - assert((vertexFormat.span & 3) == 0); // does not support spans that are not a multiple of 4 + const size_t numVertices = fatVertexBuffer.size(); - // Determine the mapping between the FatVertex (input) data and the output VertexFormat items - std::array copyOffsets; - memset(copyOffsets.data(), -1, sizeof(copyOffsets)); - for (const std::pair& srcOffset_DestIndex : { - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, position) / 4, positionIndex), - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, normal) / 4, normalIndex), - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, color) / 4, colorIndex), - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, uv0) / 4, uv0Index), - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, tangent) / 4, tangentIndex), - std::pair((uint32_t)offsetof(MeshObjectIntermediate::FatVertex, bitangent) / 4, bitangentIndex) }) + // Determine the mapping between the FatVertex / FatWeight (input) data and the output VertexFormat items + struct SrcToDstMapping { + size_t srcOffset; + int destIndex; + int* srcOffsetArray; + }; + std::array copyOffsets; + std::array copyWeightOffsets; + copyOffsets.fill(-1); + copyWeightOffsets.fill(-1); + for (const auto& srcOffset_DestIndex : { + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, position) / 4, positionIndex, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, normal) / 4, normalIndex, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, color) / 4, colorIndex, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, uv0) / 4, uv0Index, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, tangent) / 4, tangentIndex, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatVertex, bitangent) / 4, bitangentIndex, copyOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatWeight, joint) / 4, jointIndex, copyWeightOffsets.data()}, + SrcToDstMapping{offsetof(MeshObjectIntermediate::FatWeight, weight) / 4, weightIndex, copyWeightOffsets.data()}, + }) { - int destIndex = srcOffset_DestIndex.second; + const int destIndex = srcOffset_DestIndex.destIndex; if (destIndex != -1) { - uint32_t srcOffset32 = srcOffset_DestIndex.first; + uint32_t srcOffset32 = srcOffset_DestIndex.srcOffset; + int* srcOffsetArray = srcOffset_DestIndex.srcOffsetArray; uint32_t destOffset = vertexFormat.elements[destIndex].offset; assert((destOffset & 3) == 0); // cannot handle non 4 byte aligned data destOffset /= 4; @@ -566,13 +594,13 @@ std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(con switch (destSize) { case 16: - copyOffsets[srcOffset32++] = destOffset++; + srcOffsetArray[srcOffset32++] = destOffset++; case 12: - copyOffsets[srcOffset32++] = destOffset++; + srcOffsetArray[srcOffset32++] = destOffset++; case 8: - copyOffsets[srcOffset32++] = destOffset++; + srcOffsetArray[srcOffset32++] = destOffset++; case 4: - copyOffsets[srcOffset32++] = destOffset++; + srcOffsetArray[srcOffset32++] = destOffset++; break; default: assert(0); @@ -581,17 +609,19 @@ std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(con } } - const size_t numVertices = fatVertexBuffer.size(); + const uint32_t destSpan32 = (uint32_t)vertexFormat.span / 4; // span of output data (in 32bit words) + assert((vertexFormat.span & 3) == 0); // does not support spans that are not a multiple of 4 std::vector outputData; outputData.resize(destSpan32 * numVertices, 0/*zero buffer*/); - uint32_t* pSrc = (uint32_t*)fatVertexBuffer.data(); + const uint32_t* pSrc = (const uint32_t*)fatVertexBuffer.data(); + const uint32_t* pSrcWeight = fatWeightBuffer.empty() ? nullptr : (const uint32_t*)fatWeightBuffer.data(); uint32_t* pDst = outputData.data(); for (size_t i = 0; i < numVertices; ++i) { - // Copy from tinyObjVertex to our buffer vertex format (compiler may decide to unroll this since copyOffsets.size() is known at compile time + // Copy vertex data from tinyObjVertex to our buffer vertex format (compiler may decide to unroll this since copyOffsets.size() is known at compile time for (uint32_t srcOffset = 0; srcOffset < copyOffsets.size(); ++srcOffset) { if (copyOffsets[srcOffset] >= 0) @@ -600,6 +630,15 @@ std::vector MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(con } ++pSrc; } + // Copy vertex weights and joint idxs from tinyObjVertex to our buffer vertex format (compiler may decide to unroll this since copyWeightOffsets.size() is known at compile time + for (uint32_t srcOffset = 0; srcOffset < copyWeightOffsets.size(); ++srcOffset) + { + if (copyWeightOffsets[srcOffset] >= 0) + { + pDst[copyWeightOffsets[srcOffset]] = *pSrcWeight; + } + ++pSrcWeight; + } pDst += destSpan32; } // WhichVert @@ -718,7 +757,7 @@ size_t MeshObjectIntermediate::CalcNumTriangles() const return std::visit( [&]( const auto& m ) { using T = std::decay_t; - if constexpr (std::is_same_v> || std::is_same_v>) + if constexpr (std::is_same_v> || std::is_same_v> || std::is_same_v>) { return m.size() / 3; } @@ -741,7 +780,9 @@ size_t MeshObjectIntermediate::CalcNumTriangles() const if (!material.diffuseFilename.empty()) textureNames.insert(material.diffuseFilename); if (!material.bumpFilename.empty()) - textureNames.insert(material.bumpFilename); + textureNames.insert( material.bumpFilename ); + if (!material.specMapFilename.empty()) + textureNames.insert( material.specMapFilename ); } } std::vector textureNamesVector; @@ -841,11 +882,11 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode // We now know the ModelData.accessors[] index for all our data. - for (uint32_t WhichAttrib = 0; WhichAttrib < NUM_GLTF_ATTRIBS; WhichAttrib++) + for (auto& attrib: AttribInfo) { - if (AttribInfo[WhichAttrib].AccessorIndx >= 0) + if (attrib.AccessorIndx >= 0) { - const tinygltf::Accessor& AccessorData = ModelData.accessors[AttribInfo[WhichAttrib].AccessorIndx]; + const tinygltf::Accessor& AccessorData = ModelData.accessors[attrib.AccessorIndx]; const tinygltf::BufferView& ViewData = ModelData.bufferViews[AccessorData.bufferView]; int Stride = AccessorData.ByteStride(ViewData); if (Stride < 0) @@ -853,12 +894,12 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode printf("\nError loading %s: Cannot calculate data stride", m_filename.c_str()); return false; } - AttribInfo[WhichAttrib].BytesPerElem = Stride; - AttribInfo[WhichAttrib].BytesTotal = ViewData.byteLength; - AttribInfo[WhichAttrib].Count = (uint32_t) AccessorData.count; + attrib.BytesPerElem = Stride; + attrib.BytesTotal = ViewData.byteLength; + attrib.Count = (uint32_t) AccessorData.count; const tinygltf::Buffer& BufferData = ModelData.buffers[ViewData.buffer]; - AttribInfo[WhichAttrib].pData = (void*)(&BufferData.data.at(ViewData.byteOffset + AccessorData.byteOffset)); + attrib.pData = (void*)(&BufferData.data.at(ViewData.byteOffset + AccessorData.byteOffset)); } } @@ -901,6 +942,7 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode auto& meshObject = meshObjects.emplace_back(); // Pass up the mesh name + meshObject.m_NodeName = NodeData.name; meshObject.m_MeshName = MeshData.name; // Set the object transform. @@ -908,6 +950,9 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode meshObject.m_Transform[3] *= glm::vec4(m_globalScale, 1.0f);// Transform position needs scale applying, dont scale entire transform as the vertex data is scaled independantly (below). meshObject.m_NodeId = (int)NodeIdx; + // Want the vertex color to be the base color from the material + glm::vec4 materialColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + if (materialIdx >= 0)/*-1 is valid*/ { // Was seeing invalid material index on some GLTF exports. Protect from that. @@ -928,6 +973,9 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode material.pbrMetallicRoughness.baseColorFactor[2], material.pbrMetallicRoughness.baseColorFactor[3]); + // Want the vertex color to be the base color from the material + materialColor = baseColorFactor; + int normalIndex = material.normalTexture.index; normalIndex = normalIndex >= 0 ? ModelData.textures[normalIndex].source : -1; @@ -952,6 +1000,8 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode (float)material.pbrMetallicRoughness.roughnessFactor, + glm::vec3(material.emissiveFactor[0], material.emissiveFactor[1], material.emissiveFactor[2]), + material.alphaMode == "MASK", material.alphaMode == "BLEND" }); @@ -969,8 +1019,14 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode else if (AttribInfo[ATTRIB_INDICES].BytesPerElem == 2) { uint16_t* p16 = (uint16_t*)AttribInfo[ATTRIB_INDICES].pData; - std::span indicesSpan{ p16, NumIndices }; - meshObject.m_IndexBuffer.emplace< std::vector>(indicesSpan.begin(), indicesSpan.end()); + std::span indicesSpan{p16, NumIndices}; + meshObject.m_IndexBuffer.emplace< std::vector>( indicesSpan.begin(), indicesSpan.end() ); + } + else if (AttribInfo[ATTRIB_INDICES].BytesPerElem == 1) + { + uint8_t* p8 = (uint8_t*)AttribInfo[ATTRIB_INDICES].pData; + std::span indicesSpan{p8, NumIndices}; + meshObject.m_IndexBuffer.emplace< std::vector>( indicesSpan.begin(), indicesSpan.end() ); } else { @@ -978,14 +1034,33 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode return false; } - float* pPosition = (float*)AttribInfo[ATTRIB_POSITION].pData; + const float* pPosition = (const float*)AttribInfo[ATTRIB_POSITION].pData; uint32_t positionIncr = AttribInfo[ATTRIB_POSITION].BytesPerElem / sizeof(float); - float* pNormals = (float*)AttribInfo[ATTRIB_NORMAL].pData; + const float* pNormals = (const float*)AttribInfo[ATTRIB_NORMAL].pData; uint32_t normalIncr = AttribInfo[ATTRIB_NORMAL].BytesPerElem / sizeof(float); - float* pTangents = (float*)AttribInfo[ATTRIB_TANGENT].pData; + const float* pTangents = (const float*)AttribInfo[ATTRIB_TANGENT].pData; uint32_t tangentIncr = AttribInfo[ATTRIB_TANGENT].BytesPerElem / sizeof(float); - float* pTexCoords = (float*)AttribInfo[ATTRIB_TEXCOORD_0].pData; + const float* pTexCoords = (const float*)AttribInfo[ATTRIB_TEXCOORD_0].pData; uint32_t texCoordsIncr = AttribInfo[ATTRIB_TEXCOORD_0].BytesPerElem / sizeof(float); + const uint8_t* pJoints = (const uint8_t*)AttribInfo[ATTRIB_JOINTS_0].pData; + uint32_t jointsIncr = AttribInfo[ATTRIB_JOINTS_0].BytesPerElem / sizeof(uint8_t); + const float* pWeights = (const float*)AttribInfo[ATTRIB_WEIGHTS_0].pData; + uint32_t weightsIncr = AttribInfo[ATTRIB_WEIGHTS_0].BytesPerElem / sizeof(float); + + if (pJoints) + { + if (AttribInfo[ATTRIB_JOINTS_0].BytesPerElem != 4) + { + printf("\nError loading %s: Mesh has invalid BytesPerElem (%d) for joints", m_filename.c_str(), AttribInfo[ATTRIB_JOINTS_0].BytesPerElem); + return false; + } + if (AttribInfo[ATTRIB_WEIGHTS_0].BytesPerElem != 16) + { + printf("\nError loading %s: Mesh has invalid BytesPerElem (%d) for weights", m_filename.c_str(), AttribInfo[ATTRIB_WEIGHTS_0].BytesPerElem); + return false; + } + meshObject.m_WeightBuffer.reserve(NumPositions); + } // The problem is that color can come in as unsigned short (not floats), so // can't access as a float pointer @@ -994,7 +1069,8 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode for (uint32_t WhichVert = 0; WhichVert < NumPositions; ++WhichVert) { - MeshObjectIntermediate::FatVertex vertex = {}; + MeshObjectIntermediate::FatVertex vertex {}; + MeshObjectIntermediate::FatWeight jointWeights {.weight = {1.0f,0.0f,0.0f,0.0f}}; if (pPosition != nullptr) { vertex.position[0] = pPosition[0] * m_globalScale.x; @@ -1077,10 +1153,11 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode } else { - vertex.color[0] = 1.0f; - vertex.color[1] = 1.0f; - vertex.color[2] = 1.0f; - vertex.color[3] = 1.0f; + // Want the vertex color to be the base color from the material + vertex.color[0] = materialColor.x; + vertex.color[1] = materialColor.y; + vertex.color[2] = materialColor.z; + vertex.color[3] = materialColor.w; } if (AttribInfo[ATTRIB_NORMAL].pData != nullptr) @@ -1103,6 +1180,25 @@ bool MeshObjectIntermediateGltfProcessor::operator()(const tinygltf::Model& Mode vertex.material = materialIdx; meshObject.m_VertexBuffer.push_back(vertex); + + if (pWeights != nullptr) + { + jointWeights.weight[0] = pWeights[0]; + jointWeights.weight[1] = pWeights[1]; + jointWeights.weight[2] = pWeights[2]; + jointWeights.weight[3] = pWeights[3]; + pWeights += weightsIncr; + } + if (pJoints) + { + jointWeights.joint[0] = pJoints[0]; + jointWeights.joint[1] = pJoints[1]; + jointWeights.joint[2] = pJoints[2]; + jointWeights.joint[3] = pJoints[3]; + pJoints += jointsIncr; + + meshObject.m_WeightBuffer.push_back(jointWeights); + } } } } diff --git a/framework/code/mesh/meshIntermediate.hpp b/framework/code/mesh/meshIntermediate.hpp index 0f7d1d1..e5f1b99 100644 --- a/framework/code/mesh/meshIntermediate.hpp +++ b/framework/code/mesh/meshIntermediate.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -74,6 +74,13 @@ class MeshObjectIntermediate int material; ///< indexc in to m_Materials }; + /// Data for a vertex weight, at least one per animated vertex. 4 joint influences per vert follows gltf capabilities + struct FatWeight + { + int joint[4]; // index into skeleton joints + float weight[4]; // weight (influence) of joint + }; + /// All the data that could be associuated with an object instance struct FatInstance { @@ -86,8 +93,9 @@ class MeshObjectIntermediate }; /// Creates a 'raw' array of data from a 'fat' MeshObjectIntermediate object, with the returned data being formatted in the way described by vertexFormat + /// fatWeightBuffer may be empty if not required/supported, if not empty must have the same number of vertices as fatVertexBuffer. /// @returns data in the requested vertexFormat - static std::vector CopyFatVertexToFormattedBuffer(const std::span& fatVertexBuffer, const VertexFormat& vertexFormat); + static std::vector CopyFatVertexToFormattedBuffer(const std::span& fatVertexBuffer, const std::span& fatWeightBuffer, const VertexFormat& vertexFormat); /// Creates a 'raw' array of data from a 'fat instance' MeshObjectIntermediate object, with the returned data being formatted in the way described by vertexFormat /// Same functionality as @CopyFatVertexToFormattedBuffer but for instance rate data. @@ -116,6 +124,7 @@ class MeshObjectIntermediate std::string specMapFilename; // Texture version of roughnessFactor float metallicFactor = 0.0f; float roughnessFactor = 0.0f; + glm::vec3 emissiveFactor = glm::vec3(0.0f); bool alphaCutout = false; bool transparent = false; @@ -124,12 +133,16 @@ class MeshObjectIntermediate }; public: - typedef std::vector tVertexBuffer; - typedef std::variant, std::vector> tIndexBuffer; + using tVertexBuffer = std::vector; + using tWeightBuffer = std::vector; + using tIndexBuffer = std::variant, std::vector, std::vector>; std::string m_MeshName; + std::string m_NodeName; + tVertexBuffer m_VertexBuffer; + tWeightBuffer m_WeightBuffer; //< vertex weights (or empty) /// Index buffer can be 16bit or 32bit (or not exist; in which case every 3 vertices in m_VertexBuffer are the verts of a triangle). tIndexBuffer m_IndexBuffer; std::vector m_Materials; @@ -138,6 +151,8 @@ class MeshObjectIntermediate glm::mat4 m_Transform = glm::identity(); /// Node id (child node that this node is attached to) from gltf, can be used to lookup animations on this node (non skinned animation) int m_NodeId = -1; + /// Number of joint weights per vertex (or 0) + uint32_t m_WeightsPerVertex = 0; }; diff --git a/framework/code/mesh/meshLoader.cpp b/framework/code/mesh/meshLoader.cpp index 54b5cc6..0f18372 100644 --- a/framework/code/mesh/meshLoader.cpp +++ b/framework/code/mesh/meshLoader.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -36,12 +36,7 @@ static bool Gltf_WriteWholeFile(std::string*, const std::string&, const std::vec { return false; } -static bool Gltf_GetFileSize(size_t* output, std::string* error, const std::string& filename, void* vAssetManager) -{ - AssetManager* pAssetManager = (AssetManager*)vAssetManager; - *output = pAssetManager->GetFileSize(filename); - return output != 0; -} + bool StubGltfLoadImageDataFunction(tinygltf::Image*, const int, std::string*, std::string*, int, int, const unsigned char*, int, void*) @@ -70,12 +65,12 @@ bool MeshLoader::LoadGlftModel(AssetManager& assetManager, const std::string& fi bool RetVal = ModelLoader.LoadASCIIFromFile(&ModelData, &err, &warn, filename); if (!warn.empty()) { - printf("\nWarning loading %s: %s", filename.c_str(), warn.c_str()); + LOGE("\nWarning loading %s: %s", filename.c_str(), warn.c_str()); } if (!err.empty()) { - printf("\nError loading %s: %s", filename.c_str(), err.c_str()); + LOGE("\nError loading %s: %s", filename.c_str(), err.c_str()); } if (!RetVal) diff --git a/framework/code/mesh/meshLoader.hpp b/framework/code/mesh/meshLoader.hpp index c1831cb..6b665c6 100644 --- a/framework/code/mesh/meshLoader.hpp +++ b/framework/code/mesh/meshLoader.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/mesh/octree.cpp b/framework/code/mesh/octree.cpp index be65b28..5146299 100644 --- a/framework/code/mesh/octree.cpp +++ b/framework/code/mesh/octree.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/mesh/octree.hpp b/framework/code/mesh/octree.hpp index 956363b..29bc303 100644 --- a/framework/code/mesh/octree.hpp +++ b/framework/code/mesh/octree.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -384,7 +384,7 @@ struct SphereTest /// View frustum -/// Builds the +/// Builds a view frustum form camera matrixes and implements frustum test queries /// @ingroup Mesh class ViewFrustum { diff --git a/framework/code/rayTracing/accelerationStructure.hpp b/framework/code/rayTracing/accelerationStructure.hpp new file mode 100644 index 0000000..1d771c2 --- /dev/null +++ b/framework/code/rayTracing/accelerationStructure.hpp @@ -0,0 +1,44 @@ +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +// Forward declarations +class GraphicsApiBase; +template class AccelerationStructure; + + +/// @brief Base class for a acceleration structure object. +/// Is subclassed for each graphics API. +class AccelerationStructureBase +{ + AccelerationStructureBase( const AccelerationStructureBase& ) = delete; + AccelerationStructureBase& operator=( const AccelerationStructureBase& ) = delete; +public: + template using tApiDerived = AccelerationStructure; // make apiCast work! + + AccelerationStructureBase() noexcept {} + virtual ~AccelerationStructureBase() noexcept = 0; +}; +inline AccelerationStructureBase::~AccelerationStructureBase() noexcept {} + + +template +class AccelerationStructure final : public AccelerationStructureBase +{ + AccelerationStructure( const AccelerationStructure& ) = delete; + AccelerationStructure& operator=( const AccelerationStructure& ) = delete; +public: + AccelerationStructure() noexcept = delete; // template class expected to be specialized + AccelerationStructure( AccelerationStructure&& ) noexcept = delete; // template class expected to be specialized + AccelerationStructure& operator=( AccelerationStructure&& ) noexcept = delete;// template class expected to be specialized + + bool IsEmpty() const { return true; } + +protected: + static_assert(sizeof( AccelerationStructure ) != sizeof( AccelerationStructureBase )); // Ensure this class template is specialized (and not used as-is). Include the vulkan / dx12 (or whatever gfx api) specific header file +}; diff --git a/framework/code/shadow/shadow.cpp b/framework/code/shadow/shadow.cpp index 2758e72..43ecbe3 100644 --- a/framework/code/shadow/shadow.cpp +++ b/framework/code/shadow/shadow.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/shadow/shadow.hpp b/framework/code/shadow/shadow.hpp index f7c3efd..c67ba8f 100644 --- a/framework/code/shadow/shadow.hpp +++ b/framework/code/shadow/shadow.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -49,6 +49,6 @@ class Shadow template class ShadowT : public Shadow { - ~ShadowT() noexcept = delete; // Enforce specialization of this class + ~ShadowT() = delete; // Enforce specialization of this class static_assert(sizeof(ShadowT) != sizeof(Shadow)); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/shadow/shadowVsm.cpp b/framework/code/shadow/shadowVsm.cpp index 5c1eff1..73aed52 100644 --- a/framework/code/shadow/shadowVsm.cpp +++ b/framework/code/shadow/shadowVsm.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,9 +9,9 @@ #include "shadowVsm.hpp" #include "shadow.hpp" #include "shadowVulkan.hpp" -#include "material/computable.hpp" -#include "material/materialManager.hpp" -#include "material/shaderManager.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/shaderManagerT.hpp" #include "vulkan/commandBuffer.hpp" #include @@ -19,7 +19,8 @@ ShadowVSM::ShadowVSM() { } -bool ShadowVSM::Initialize(GraphicsApiBase& gfxApi, const ShaderManager& shaderManager, const MaterialManager& materialManager, const Shadow& shadow ) +template<> +bool ShadowVSM::Initialize(Vulkan& gfxApi, const ShaderManager& shaderManager, const MaterialManager& materialManager, const ShadowT& shadow ) { // Variant Shadow Map Computable // Computables @@ -43,10 +44,10 @@ bool ShadowVSM::Initialize(GraphicsApiBase& gfxApi, const ShaderManager& shaderM auto vsmTargetIntermediate = CreateTextureObject(vulkan, width / 2, height, TextureFormat::R16G16B16A16_SFLOAT, TEXTURE_TYPE::TT_COMPUTE_TARGET, "VSM Intermediate"); // Make a material (from the vsm shader) - auto material = materialManager.CreateMaterial(gfxApi, *pComputeShader, 1, - [this, &shadowVulkan, &vsmTarget, &vsmTargetIntermediate](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { + Material material = materialManager.CreateMaterial(*pComputeShader, 1, + [this, &shadowVulkan, &vsmTarget, &vsmTargetIntermediate](const std::string& texName) -> MaterialManagerBase::tPerFrameTexInfo { if (texName == "ShadowDepth") - return { &shadowVulkan.GetDepthTexture(0) }; + return { &shadowVulkan.GetDepthTexture() }; else if (texName == "VarianceShadowMap") return { &vsmTarget }; else if (texName == "VarianceShadowMapIntermediate") @@ -56,17 +57,17 @@ bool ShadowVSM::Initialize(GraphicsApiBase& gfxApi, const ShaderManager& shaderM }, nullptr); // Create the computable to execute the material - m_Computable = std::make_unique(vulkan, std::move(material)); - if (!m_Computable->Init()) + auto computable = std::make_unique>(vulkan, std::move(material)); + if (!computable->Init()) { LOGE("Error Creating VSM computable..."); - m_Computable.reset(); } else { - m_Computable->SetDispatchThreadCount(0, { vsmTarget.Width, vsmTarget.Height, 1 }); - m_Computable->SetDispatchThreadCount(1, { vsmTarget.Width, vsmTarget.Height, 1 }); + computable->SetDispatchThreadCount(0, { vsmTarget.Width, vsmTarget.Height, 1 }); + computable->SetDispatchThreadCount(1, { vsmTarget.Width, vsmTarget.Height, 1 }); } + m_Computable = std::move(computable); m_VsmTarget = std::make_unique( std::move(vsmTarget) ); m_VsmTargetIntermediate = std::make_unique( std::move(vsmTargetIntermediate) ); @@ -84,12 +85,7 @@ void ShadowVSM::Release(GraphicsApiBase& gfxApi) m_VsmTargetIntermediate.reset(); } -void ShadowVSM::AddComputableToCmdBuffer(CommandList& cmdBuffer) +void ShadowVSM::AddComputableToCmdBuffer(CommandListBase& cmdBuffer) { - const auto& computable = *m_Computable; - for (const auto& computablePass : computable.GetPasses()) - { - auto& cmdBufferVulkan = apiCast(cmdBuffer); - computable.DispatchPass(cmdBufferVulkan.m_VkCommandBuffer, computablePass, 0); - } + m_Computable->Dispatch(cmdBuffer, 0, false/*timers*/); } diff --git a/framework/code/shadow/shadowVsm.hpp b/framework/code/shadow/shadowVsm.hpp index c350fd3..f844d7e 100644 --- a/framework/code/shadow/shadowVsm.hpp +++ b/framework/code/shadow/shadowVsm.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,12 +10,16 @@ #include class GraphicsApiBase; -class Computable; -class MaterialManager; -class ShaderManager; +class ComputableBase; +class MaterialManagerBase; +class ShaderManagerBase; class Shadow; -class Texture; -class CommandList; +class TextureBase; +class CommandListBase; + +template class MaterialManager; +template class ShaderManager; +template class ShadowT; /// Variance Shadow Map class ShadowVSM @@ -25,14 +29,15 @@ class ShadowVSM public: ShadowVSM(); - bool Initialize(GraphicsApiBase& , const ShaderManager& , const MaterialManager& , const Shadow& ); + template + bool Initialize(T_GFXAPI& , const ShaderManager& , const MaterialManager& , const ShadowT& ); void Release(GraphicsApiBase&); - const Texture* const GetVSMTexture() const { return m_VsmTarget.get(); } - void AddComputableToCmdBuffer(CommandList& cmdBuffer); + const TextureBase* const GetVSMTexture() const { return m_VsmTarget.get(); } + void AddComputableToCmdBuffer(CommandListBase& cmdBuffer); protected: - std::unique_ptr m_Computable; - std::unique_ptr m_VsmTarget; - std::unique_ptr m_VsmTargetIntermediate; + std::unique_ptr m_Computable; + std::unique_ptr m_VsmTarget; + std::unique_ptr m_VsmTargetIntermediate; }; diff --git a/framework/code/shadow/shadowVulkan.cpp b/framework/code/shadow/shadowVulkan.cpp index ae4a22c..d53295c 100644 --- a/framework/code/shadow/shadowVulkan.cpp +++ b/framework/code/shadow/shadowVulkan.cpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #include "shadowVulkan.hpp" #include "vulkan/vulkan_support.hpp" @@ -30,19 +30,40 @@ bool ShadowT::Initialize(Vulkan& rGfxApi, uint32_t shadowMapWidth, uint3 m_Scissor.extent.width = shadowMapWidth; m_Scissor.extent.height = shadowMapHeight; - const TextureFormat colorFormat[]{ TextureFormat::R8G8B8A8_UNORM }; - const std::span emptyColorFormat{}; + TextureFormat colorFormat = TextureFormat::R8G8B8A8_UNORM; TextureFormat depthFormat = TextureFormat::D32_SFLOAT; - if (!m_ShadowMapRT.Initialize(&rGfxApi, shadowMapWidth, shadowMapHeight, addColorTarget ? colorFormat : emptyColorFormat, depthFormat, VK_SAMPLE_COUNT_1_BIT, "Shadow RT")) + m_ShadowDepthBuffer = CreateTextureObject( rGfxApi, shadowMapWidth, shadowMapHeight, depthFormat, TEXTURE_TYPE::TT_RENDER_TARGET, "Shadow Depth", Msaa::Samples1 ); + + std::span colorBufferSpan{}; + std::span colorFormatSpan{}; + if (addColorTarget) + { + m_ShadowColorBuffer = CreateTextureObject( rGfxApi, shadowMapWidth, shadowMapHeight, colorFormat, TEXTURE_TYPE::TT_RENDER_TARGET, "Shadow Depth", Msaa::Samples1 ); + colorBufferSpan ={ &m_ShadowColorBuffer, 1 }; + colorFormatSpan = {&colorFormat, 1}; + } + + RenderPass shadowRenderPass; + if (!rGfxApi.CreateRenderPass( + colorFormatSpan, + depthFormat, + Msaa::Samples1, + RenderPassInputUsage::Clear, // color input + RenderPassOutputUsage::StoreReadOnly, // color output + true, // depth clear + RenderPassOutputUsage::StoreReadOnly, // depth output + shadowRenderPass )) { - LOGE("Unable to create shadow render target"); return false; } + + Framebuffer framebuffer; + framebuffer.Initialize( rGfxApi, shadowRenderPass, colorBufferSpan, &m_ShadowDepthBuffer, "Shadow framebuffer" ); + m_ShadowMapRC = {std::move(shadowRenderPass), {}/*pipeline*/, framebuffer, "Shadow Context"}; return true; } void ShadowT::Release() { - m_ShadowMapRT.Release(); } diff --git a/framework/code/shadow/shadowVulkan.hpp b/framework/code/shadow/shadowVulkan.hpp index 29f7e9b..538d954 100644 --- a/framework/code/shadow/shadowVulkan.hpp +++ b/framework/code/shadow/shadowVulkan.hpp @@ -1,14 +1,15 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include "system/glm_common.hpp" #include "vulkan/vulkan_support.hpp" +#include "vulkan/renderContext.hpp" #include "vulkan/renderTarget.hpp" #include "shadow.hpp" @@ -31,45 +32,43 @@ class ShadowT : public Shadow return m_Scissor; } - auto GetRenderPass() const + const auto& GetRenderContext() const { - return m_ShadowMapRT.m_RenderPass; + return m_ShadowMapRC; } - auto GetFramebuffer(uint32_t idx = 0) const + VkFramebuffer GetFramebuffer(uint32_t idx = 0) const { - return m_ShadowMapRT[idx].m_FrameBuffer; + return m_ShadowMapRC.GetFramebuffer()->m_FrameBuffer; } void GetTargetSize(uint32_t& width, uint32_t& height) const { - width = m_ShadowMapRT[0].m_Width; - height = m_ShadowMapRT[0].m_Height; + width = m_ShadowDepthBuffer.Width; + height = m_ShadowDepthBuffer.Height; } auto GetDepthFormat(uint32_t frameBufferIndex = 0) const { - return m_ShadowMapRT[frameBufferIndex].m_DepthAttachment.Format; + return m_ShadowDepthBuffer.Format; } - const auto& GetDepthTexture(uint32_t frameBufferIndex = 0) const + const auto& GetDepthTexture() const { - return m_ShadowMapRT[frameBufferIndex].m_DepthAttachment; + return m_ShadowDepthBuffer; } - const TextureT* GetColorTexture(uint32_t frameBufferIndex = 0) const + const Texture* GetColorTexture() const { - if (frameBufferIndex < m_ShadowMapRT[frameBufferIndex].m_ColorAttachments.size()) - return &m_ShadowMapRT[frameBufferIndex].m_ColorAttachments[frameBufferIndex]; - return nullptr; - } - const auto& GetRenderTarget(uint32_t frameBufferIndex = 0) const - { - return m_ShadowMapRT[frameBufferIndex]; + return &m_ShadowColorBuffer; } protected: VkViewport m_Viewport = {}; VkRect2D m_Scissor = {}; - // Render targets and renderpass - CRenderTargetArray<1> m_ShadowMapRT; + // Render targets (images) + Texture m_ShadowDepthBuffer; + Texture m_ShadowColorBuffer; + + // Render context (renderpass and framebuffer) + RenderContext m_ShadowMapRC; }; using ShadowVulkan = ShadowT; diff --git a/framework/code/system/Worker.cpp b/framework/code/system/Worker.cpp index 7294c75..54a000c 100644 --- a/framework/code/system/Worker.cpp +++ b/framework/code/system/Worker.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,12 +13,12 @@ // ********************************************** // ********************************************** -// CWorker +// ThreadWorker // ********************************************** // ********************************************** //----------------------------------------------------------------------------- -void CWorker::WorkerThreadProc() +void ThreadWorker::WorkerThreadProc() //----------------------------------------------------------------------------- { // @@ -39,7 +39,7 @@ void CWorker::WorkerThreadProc() m_WorkAvailable.Wait(); // Get the work (will wait until work is available). - Work work; + ThreadWork work; { std::lock_guard lock( m_WaitingWorkQueueMutex ); work = m_WaitingWorkQueue.front(); @@ -71,21 +71,21 @@ void CWorker::WorkerThreadProc() } //----------------------------------------------------------------------------- -CWorker::CWorker() : m_WorkAvailable(0), m_WorkersRunning(0), m_WorkInFlight(0) +ThreadWorker::ThreadWorker() : m_WorkAvailable(0), m_WorkersRunning(0), m_WorkInFlight(0) //----------------------------------------------------------------------------- { m_Name = "Worker"; } //----------------------------------------------------------------------------- -CWorker::~CWorker() +ThreadWorker::~ThreadWorker() //----------------------------------------------------------------------------- { Terminate(); } //----------------------------------------------------------------------------- -uint32_t CWorker::Initialize(const char *pName, uint32_t uiDesiredThreads) +uint32_t ThreadWorker::Initialize(const char *pName, uint32_t uiDesiredThreads) //----------------------------------------------------------------------------- { if (pName != nullptr) @@ -134,24 +134,24 @@ uint32_t CWorker::Initialize(const char *pName, uint32_t uiDesiredThreads) // Create and startup the worker threads for(uint32_t uiIndx = 0; uiIndx < uiNumWorkers; uiIndx++) { - m_Workers.emplace_back( std::thread{ &CWorker::WorkerThreadProc, this } ); + m_Workers.emplace_back( std::thread{ &ThreadWorker::WorkerThreadProc, this } ); } return uiNumWorkers; } //----------------------------------------------------------------------------- -void CWorker::Terminate() +void ThreadWorker::Terminate() //----------------------------------------------------------------------------- { for( size_t i = 0; i < m_Workers.size(); ++i ) { // Send an 'empty' job for each of the workers (signals to stop running). - DoWork( Work{}, 10 * 1000 ); + DoWork( ThreadWork{}, 10 * 1000 ); } // Potentially the threads pushed jobs on to the m_WaitingWorkQueue between us pushing the 'empty' jobs and them getting processed. - // What do we do with that? We could either run the jobs here (single threaded) or just punt. What happens if another CWorker is + // What do we do with that? We could either run the jobs here (single threaded) or just punt. What happens if another ThreadWorker is // processing jobs that push work on to our queue? // Wait for all the threads to be done. @@ -167,7 +167,7 @@ void CWorker::Terminate() } //----------------------------------------------------------------------------- -void CWorker::FinishAllWork() +void ThreadWorker::FinishAllWork() //----------------------------------------------------------------------------- { if(m_Workers.empty()) @@ -183,7 +183,7 @@ void CWorker::FinishAllWork() } //----------------------------------------------------------------------------- -bool CWorker::IsAllWorkDone() +bool ThreadWorker::IsAllWorkDone() //----------------------------------------------------------------------------- { if (m_Workers.empty()) @@ -204,14 +204,14 @@ bool CWorker::IsAllWorkDone() //----------------------------------------------------------------------------- -void CWorker::DoWork(void (*lpStartAddress) (void *), void *pParam, uint32_t WaitTimeMS) +void ThreadWorker::DoWork(void (*lpStartAddress) (void *), void *pParam, uint32_t WaitTimeMS) //----------------------------------------------------------------------------- { DoWork({lpStartAddress, pParam}, WaitTimeMS); } //----------------------------------------------------------------------------- -void CWorker::DoWork(Work&& work, uint32_t WaitTimeMS) +void ThreadWorker::DoWork(ThreadWork&& work, uint32_t WaitTimeMS) //----------------------------------------------------------------------------- { if(m_Workers.empty()) diff --git a/framework/code/system/Worker.h b/framework/code/system/Worker.h index 0dde799..eff57b8 100644 --- a/framework/code/system/Worker.h +++ b/framework/code/system/Worker.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,11 +11,13 @@ #include #include #include +#include #include #include +#include #if !defined(MAX_CPU_CORES) - #define MAX_CPU_CORES 4 + #define MAX_CPU_CORES 12 #endif // !defined(MAX_CPU_CORES) /// Sempahore. Post increases the counter, Wait allows a thread through if the counter is greater than zero and then decreases the counter. @@ -128,7 +130,7 @@ class ReverseSemaphore // Some work that we want the worker to do -struct Work +struct ThreadWork { // Funtion pointer void (*lpStartAddress) (void *) = nullptr; @@ -142,12 +144,12 @@ struct Work /// Creates a number of worker threads that can then be given work to do (via DoWork / DoWork2) /// Holds the threads, the work queue, and the associated syncronization primitives. /// @ingroup System -class CWorker +class ThreadWorker { public: // Constructor/Destructor - CWorker(); - ~CWorker(); + ThreadWorker(); + ~ThreadWorker(); /// Initialize this worker with the given number of threads uint32_t Initialize(const char *pName, uint32_t uiDesiredThreads = 0); @@ -197,10 +199,27 @@ class CWorker DoWork( +lambdaWrap, pParams, 1000 ); } + struct pWrapper { + std::function m_function; + }; + + using tWork3Fn = std::function; + void DoWork3( tWork3Fn&& work ) { + + void* pWork = new tWork3Fn( std::forward( work ) ); + + auto lambdaWrap = []( void* voidParams ) { + tWork3Fn* pWork = static_cast(voidParams); + pWork->operator()(); + delete pWork; + }; + DoWork( +lambdaWrap, pWork, 1000 ); + } + void Terminate(); protected: - void DoWork(Work&& work, uint32_t WaitTimeMS); + void DoWork(ThreadWork&& work, uint32_t WaitTimeMS); /// Function run by the WorkerInfo::m_Thread, loops. void WorkerThreadProc(); @@ -212,7 +231,7 @@ class CWorker std::vector m_Workers; /// Queue that holds the work we have been asked to do (protected by a mutex). - std::queue m_WaitingWorkQueue; + std::queue m_WaitingWorkQueue; std::mutex m_WaitingWorkQueueMutex; /// Semaphore to control when work is available. diff --git a/framework/code/system/android/androidAssetManager.cpp b/framework/code/system/android/androidAssetManager.cpp index cc7c0db..27e0c9a 100644 --- a/framework/code/system/android/androidAssetManager.cpp +++ b/framework/code/system/android/androidAssetManager.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -80,8 +80,29 @@ AssetHandle* AssetManager::OpenFile(const std::string& portableFilename, Mode mo // Asset name needs to have / seperators and no ./ preamble! std::string aAssetFilename; const auto skipPreambleOffset = std::max(portableFilename.find_first_not_of("./\\"), (size_t)0); - std::transform(portableFilename.begin() + skipPreambleOffset, portableFilename.end(), std::back_inserter(aAssetFilename), [](char c) { return c == '\\' ? '/' : c; }); + // Convert to backslashes and remove double backslashes + aAssetFilename.resize(portableFilename.length() - skipPreambleOffset, ' '); + size_t outputIdx = 0, slashCount = 0; + for (size_t inputIdx = skipPreambleOffset; inputIdx < portableFilename.length(); ++inputIdx) + { + char c = portableFilename[inputIdx]; + c = c=='\\' ? '/' : c; + if (c == '/') + { + ++slashCount; + if (slashCount > 1) + continue; + } + else + { + slashCount = 0; + } + aAssetFilename[outputIdx++] = c; + } + aAssetFilename.resize(outputIdx); + + // Open the file AAsset* pAAsset = AAssetManager_open(m_AAssetManager, aAssetFilename.c_str(), AASSET_MODE_STREAMING); if (pAAsset != nullptr) { diff --git a/framework/code/system/assetManager.hpp b/framework/code/system/assetManager.hpp index 45b5b70..2b31780 100644 --- a/framework/code/system/assetManager.hpp +++ b/framework/code/system/assetManager.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -34,7 +34,7 @@ class AssetMemStream : public std::basic_istream { AssetMemStream& operator=(const AssetMemStream&) = delete; public: AssetMemStream() - : std::istream(&mBuffer) + : std::basic_istream(&mBuffer) , mBuffer() { this->set_rdbuf(&mBuffer); @@ -174,17 +174,6 @@ class AssetManager return true; } - size_t GetFileSize(const std::string& portableFileName) - { - AssetHandle* handle = OpenFile(portableFileName, Mode::Read); - if (!handle) - { - return 0; - } - - return FileSize(handle); - } - AssetHandleGuard OpenFile( const std::string& portableFilename ) { auto* fileHandle = OpenFile( portableFilename, Mode::Read ); diff --git a/framework/code/system/config.cpp b/framework/code/system/config.cpp index 4a6ec86..419a36f 100644 --- a/framework/code/system/config.cpp +++ b/framework/code/system/config.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/config.h b/framework/code/system/config.h index e8e8949..2df5466 100644 --- a/framework/code/system/config.h +++ b/framework/code/system/config.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -23,6 +23,7 @@ #include #include +#include #include #include #include "containers.h" @@ -689,4 +690,4 @@ Type RegisterVariable(const char* name, Type* ptr, Type init, unsigned long flag /// @} // End doxygen group -#endif //_SYS_CONFIG_H_ \ No newline at end of file +#endif //_SYS_CONFIG_H_ diff --git a/framework/code/system/containers.cpp b/framework/code/system/containers.cpp index ee7e5fb..24fd828 100644 --- a/framework/code/system/containers.cpp +++ b/framework/code/system/containers.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/containers.h b/framework/code/system/containers.h index b1f619e..b6fe588 100644 --- a/framework/code/system/containers.h +++ b/framework/code/system/containers.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/crc32c.hpp b/framework/code/system/crc32c.hpp index b21bfa5..d356d60 100644 --- a/framework/code/system/crc32c.hpp +++ b/framework/code/system/crc32c.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/glm_common.hpp b/framework/code/system/glm_common.hpp index b89daca..0020fe1 100644 --- a/framework/code/system/glm_common.hpp +++ b/framework/code/system/glm_common.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -22,5 +22,6 @@ #include #include #include +#include #include #include diff --git a/framework/code/system/linux/linuxAssetManager.cpp b/framework/code/system/linux/linuxAssetManager.cpp new file mode 100644 index 0000000..741a687 --- /dev/null +++ b/framework/code/system/linux/linuxAssetManager.cpp @@ -0,0 +1,110 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================================================ + +/// @file linuxAssetManager.cpp +/// Platform specific implementation of AssetManager class. +/// @ingroup System + +#include "../assetManager.hpp" +#include "system/os_common.h" +#include +#include +#include + + +//----------------------------------------------------------------------------- +// Define a class to hold the file handle pointers. +class AssetHandle +{ +public: + AssetHandle(FILE* fp, size_t fileSize) + : mFp(fp) + , mFileSize(fileSize) + {} + ~AssetHandle() + { + assert(mFp==nullptr); // expecting the fp to be cleared by Fileclose() + } + FILE* mFp; + size_t mFileSize; +}; + + +//----------------------------------------------------------------------------- +AssetHandle* AssetManager::OpenFile(const std::string& pPortableFileName, Mode mode) +//----------------------------------------------------------------------------- +{ + if (pPortableFileName.empty()) + return nullptr; + + // Fix the filename + const auto deviceFilename = PortableFilenameToDevicePath(pPortableFileName); + + // Open the file and see what is to be seen + FILE* fp = fopen(deviceFilename.c_str(), (mode == Mode::Write) ? "wb" : "rb"); + if (fp == nullptr) + { + LOGE("Unable to open file: %s (errno=%d)", deviceFilename.c_str(), (int)errno); + return nullptr; + } + + size_t fileSize = 0; + if (mode == Mode::Read) + { + // Get the file length + fseek(fp, 0, SEEK_END); + fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + } + + return new AssetHandle(fp, fileSize); +} + +//----------------------------------------------------------------------------- +size_t AssetManager::FileSize(AssetHandle* pHandle) const +//----------------------------------------------------------------------------- +{ + return pHandle->mFileSize; +} + +//----------------------------------------------------------------------------- +size_t AssetManager::ReadFile(void* pDest, size_t bytes, AssetHandle* pHandle) +//----------------------------------------------------------------------------- +{ + return fread(pDest, sizeof(char), bytes, pHandle->mFp); +} + +//----------------------------------------------------------------------------- +size_t AssetManager::WriteFile(const void* pSrc, size_t bytes, AssetHandle* pHandle) +//----------------------------------------------------------------------------- +{ + size_t bytesWritten = fwrite(pSrc, sizeof(char), bytes, pHandle->mFp); + pHandle->mFileSize += (bytesWritten > 0 ? bytesWritten : 0); + return bytesWritten; +} + +//----------------------------------------------------------------------------- +void AssetManager::CloseFile(AssetHandle* pHandle) +//----------------------------------------------------------------------------- +{ + fclose(pHandle->mFp); + pHandle->mFp = nullptr; + delete pHandle; +} + + +//----------------------------------------------------------------------------- +std::string AssetManager::PortableFilenameToDevicePath(const std::string& portableFileName) +//----------------------------------------------------------------------------- +{ + std::string output; + output.reserve(portableFileName.length()); + // change backslash \ to forwardslash / . + std::transform(portableFileName.begin(), portableFileName.end(), std::back_inserter(output), [](char c) { return c == '\\' ? '/' : c; }); + return output; +} \ No newline at end of file diff --git a/framework/code/system/math_common.hpp b/framework/code/system/math_common.hpp index 3030d4d..b5bdbf1 100644 --- a/framework/code/system/math_common.hpp +++ b/framework/code/system/math_common.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/os_common.cpp b/framework/code/system/os_common.cpp index c83692c..9e1b746 100644 --- a/framework/code/system/os_common.cpp +++ b/framework/code/system/os_common.cpp @@ -1,19 +1,20 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "os_common.h" #include "assetManager.hpp" +#include #include -#if defined (OS_ANDROID) +#if defined (OS_ANDROID) || defined(OS_LINUX) #include #include -#endif // defined (OS_ANDROID) +#endif // defined (OS_ANDROID) || defined (OS_LINUX) #if defined (OS_WINDOWS) #define NOMINMAX @@ -62,6 +63,15 @@ uint32_t OS_GetNumCores() else return (uint32_t) iNumCores; +#elif defined(OS_LINUX) + + // sysconf can return a negative number! + int iNumCores = sysconf( _SC_NPROCESSORS_ONLN ); // Number of processors online + if( iNumCores <= 0 ) + return 0; + else + return (uint32_t) iNumCores; + #else #error Must define an OS! @@ -85,11 +95,10 @@ uint32_t OS_GetTimeMS() return (uint32_t)(((double)nTime.QuadPart / (double)nFrequency.QuadPart) * 1000.0f); -#elif defined (OS_ANDROID) +#elif defined (OS_ANDROID) || defined (OS_LINUX) struct timeval t; t.tv_sec = t.tv_usec = 0; - if (gettimeofday(&t, NULL) == -1) { return 0; @@ -97,10 +106,9 @@ uint32_t OS_GetTimeMS() return (uint32_t)(t.tv_sec * 1000LL + t.tv_usec / 1000LL); -#endif // defined (OS_WINDOWS|OS_ANDROID) +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) } - //----------------------------------------------------------------------------- uint64_t OS_GetTimeUS() //----------------------------------------------------------------------------- @@ -117,7 +125,7 @@ uint64_t OS_GetTimeUS() return (uint64_t)((double)nTime.QuadPart / (((double)nFrequency.QuadPart) / 1000000.0)); -#elif defined (OS_ANDROID) +#elif defined (OS_ANDROID) || defined(OS_LINUX) struct timeval t; t.tv_sec = t.tv_usec = 0; @@ -129,10 +137,9 @@ uint64_t OS_GetTimeUS() return (uint64_t)(t.tv_sec * 1000000LL + t.tv_usec); -#endif // defined (OS_WINDOWS|OS_ANDROID) +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) } - //----------------------------------------------------------------------------- void OS_SleepMs(uint32_t ms) //----------------------------------------------------------------------------- @@ -141,14 +148,13 @@ void OS_SleepMs(uint32_t ms) Sleep(ms); -#elif defined (OS_ANDROID) +#elif defined (OS_ANDROID) || defined (OS_LINUX) usleep(ms*1000); -#endif // defined (OS_WINDOWS|OS_ANDROID) +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) } - #if defined (OS_WINDOWS) //----------------------------------------------------------------------------- static void LOG_(WORD ConsoleColor, const char* pszFormat, va_list args) @@ -172,9 +178,6 @@ static void LOG_(WORD ConsoleColor, const char* pszFormat, va_list args) OutputDebugString(szBuffer); } } -#endif // defined (OS_WINDOWS) - -#if defined (OS_WINDOWS) //----------------------------------------------------------------------------- void LOGE(const char* pszFormat, ...) //----------------------------------------------------------------------------- @@ -184,9 +187,6 @@ void LOGE(const char* pszFormat, ...) LOG_(FOREGROUND_RED | FOREGROUND_INTENSITY, pszFormat, args); va_end(args); } -#endif // defined (OS_WINDOWS) - -#if defined (OS_WINDOWS) //----------------------------------------------------------------------------- void LOGW(const char* pszFormat, ...) //----------------------------------------------------------------------------- @@ -196,9 +196,6 @@ void LOGW(const char* pszFormat, ...) LOG_(FOREGROUND_BLUE | FOREGROUND_GREEN, pszFormat, args); va_end(args); } -#endif // defined (OS_WINDOWS) - -#if defined (OS_WINDOWS) //----------------------------------------------------------------------------- void LOGI(const char* pszFormat, ...) //----------------------------------------------------------------------------- @@ -210,3 +207,36 @@ void LOGI(const char* pszFormat, ...) } #endif // defined (OS_WINDOWS) +#if defined (OS_LINUX) +//----------------------------------------------------------------------------- +void LOGE(const char* pszFormat, ...) +//----------------------------------------------------------------------------- +{ + va_list args; + va_start(args, pszFormat); + vfprintf(stderr, pszFormat, args); + va_end(args); + fprintf(stderr, "\n"); +} +//----------------------------------------------------------------------------- +void LOGW(const char* pszFormat, ...) +//----------------------------------------------------------------------------- +{ + va_list args; + va_start(args, pszFormat); + vfprintf(stdout, pszFormat, args); + va_end(args); + fprintf(stdout, "\n"); +} +//----------------------------------------------------------------------------- +void LOGI(const char* pszFormat, ...) +//----------------------------------------------------------------------------- +{ + va_list args; + va_start(args, pszFormat); + vfprintf(stdout, pszFormat, args); + va_end(args); + fprintf(stdout, "\n"); +} +#endif // defined (OS_LINUX) + diff --git a/framework/code/system/os_common.h b/framework/code/system/os_common.h index 1ba9259..09f3d93 100644 --- a/framework/code/system/os_common.h +++ b/framework/code/system/os_common.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -53,6 +53,12 @@ void LOGI(const char* pszFormat, ...); void LOGW(const char* pszFormat, ...); void LOGE(const char* pszFormat, ...); +#elif OS_LINUX + +void LOGI(const char* pszFormat, ...); +void LOGW(const char* pszFormat, ...); +void LOGE(const char* pszFormat, ...); + #else #error "Must define an OS_xxx!" diff --git a/framework/code/system/profile.cpp b/framework/code/system/profile.cpp index 4599816..606e77a 100644 --- a/framework/code/system/profile.cpp +++ b/framework/code/system/profile.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/profile.h b/framework/code/system/profile.h index 99c3da4..e477636 100644 --- a/framework/code/system/profile.h +++ b/framework/code/system/profile.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/timer.cpp b/framework/code/system/timer.cpp index 62fb808..d947607 100644 --- a/framework/code/system/timer.cpp +++ b/framework/code/system/timer.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/timer.hpp b/framework/code/system/timer.hpp index b3db6a0..a64fbf4 100644 --- a/framework/code/system/timer.hpp +++ b/framework/code/system/timer.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/system/windows/windowsAssetManager.cpp b/framework/code/system/windows/windowsAssetManager.cpp index c8e7ebc..63e8fa4 100644 --- a/framework/code/system/windows/windowsAssetManager.cpp +++ b/framework/code/system/windows/windowsAssetManager.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -104,6 +104,7 @@ std::string AssetManager::PortableFilenameToDevicePath(const std::string& portab { std::string output; output.reserve(portableFileName.length()); + // change forwardslash / to backslash \ . std::transform(portableFileName.begin(), portableFileName.end(), std::back_inserter(output), [](char c) { return c == '/' ? '\\' : c; }); return output; } diff --git a/framework/code/texture/dx12/loaderKtx.cpp b/framework/code/texture/dx12/loaderKtx.cpp new file mode 100644 index 0000000..82ce357 --- /dev/null +++ b/framework/code/texture/dx12/loaderKtx.cpp @@ -0,0 +1,1149 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "loaderKtx.hpp" +#include "sampler.hpp" +#include "texture.hpp" +#include "dx12/dx12.hpp" +#include // KTX-Software +#include +#include // for GL_* defines + +#undef min +#undef max + +// Forward declarations +static uint32_t WidthToPitch(DXGI_FORMAT dxgiFormat, uint32_t width); +static uint32_t AreaToBytes(DXGI_FORMAT dxgiFormat, uint32_t width, uint32_t height); + + +TextureKtx::TextureKtx(Dx12& dx12) noexcept : TextureKtxBase(), m_Dx12(dx12) +{ +} + +TextureKtx::~TextureKtx() +{} + +bool TextureKtx::Initialize() +{ + if (!TextureKtxBase::Initialize()) + return false; + return true; +} + +void TextureKtx::Release() +{ + TextureKtxBase::Release(); +} + + +static constexpr std::array sTextureFormatToVk{ + VK_FORMAT_UNDEFINED, + VK_FORMAT_R4G4_UNORM_PACK8, + VK_FORMAT_R4G4B4A4_UNORM_PACK16, + VK_FORMAT_B4G4R4A4_UNORM_PACK16, + VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_FORMAT_B5G6R5_UNORM_PACK16, + VK_FORMAT_R5G5B5A1_UNORM_PACK16, + VK_FORMAT_B5G5R5A1_UNORM_PACK16, + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + VK_FORMAT_R8_UNORM, + VK_FORMAT_R8_SNORM, + VK_FORMAT_R8_USCALED, + VK_FORMAT_R8_SSCALED, + VK_FORMAT_R8_UINT, + VK_FORMAT_R8_SINT, + VK_FORMAT_R8_SRGB, + VK_FORMAT_R8G8_UNORM, + VK_FORMAT_R8G8_SNORM, + VK_FORMAT_R8G8_USCALED, + VK_FORMAT_R8G8_SSCALED, + VK_FORMAT_R8G8_UINT, + VK_FORMAT_R8G8_SINT, + VK_FORMAT_R8G8_SRGB, + VK_FORMAT_R8G8B8_UNORM, + VK_FORMAT_R8G8B8_SNORM, + VK_FORMAT_R8G8B8_USCALED, + VK_FORMAT_R8G8B8_SSCALED, + VK_FORMAT_R8G8B8_UINT, + VK_FORMAT_R8G8B8_SINT, + VK_FORMAT_R8G8B8_SRGB, + VK_FORMAT_B8G8R8_UNORM, + VK_FORMAT_B8G8R8_SNORM, + VK_FORMAT_B8G8R8_USCALED, + VK_FORMAT_B8G8R8_SSCALED, + VK_FORMAT_B8G8R8_UINT, + VK_FORMAT_B8G8R8_SINT, + VK_FORMAT_B8G8R8_SRGB, + VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R8G8B8A8_SNORM, + VK_FORMAT_R8G8B8A8_USCALED, + VK_FORMAT_R8G8B8A8_SSCALED, + VK_FORMAT_R8G8B8A8_UINT, + VK_FORMAT_R8G8B8A8_SINT, + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_B8G8R8A8_SNORM, + VK_FORMAT_B8G8R8A8_USCALED, + VK_FORMAT_B8G8R8A8_SSCALED, + VK_FORMAT_B8G8R8A8_UINT, + VK_FORMAT_B8G8R8A8_SINT, + VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_A8B8G8R8_UNORM_PACK32, + VK_FORMAT_A8B8G8R8_SNORM_PACK32, + VK_FORMAT_A8B8G8R8_USCALED_PACK32, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32, + VK_FORMAT_A8B8G8R8_UINT_PACK32, + VK_FORMAT_A8B8G8R8_SINT_PACK32, + VK_FORMAT_A8B8G8R8_SRGB_PACK32, + VK_FORMAT_A2R10G10B10_UNORM_PACK32, + VK_FORMAT_A2R10G10B10_SNORM_PACK32, + VK_FORMAT_A2R10G10B10_USCALED_PACK32, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32, + VK_FORMAT_A2R10G10B10_UINT_PACK32, + VK_FORMAT_A2R10G10B10_SINT_PACK32, + VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_FORMAT_A2B10G10R10_SNORM_PACK32, + VK_FORMAT_A2B10G10R10_USCALED_PACK32, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32, + VK_FORMAT_A2B10G10R10_UINT_PACK32, + VK_FORMAT_A2B10G10R10_SINT_PACK32, + VK_FORMAT_R16_UNORM, + VK_FORMAT_R16_SNORM, + VK_FORMAT_R16_USCALED, + VK_FORMAT_R16_SSCALED, + VK_FORMAT_R16_UINT, + VK_FORMAT_R16_SINT, + VK_FORMAT_R16_SFLOAT, + VK_FORMAT_R16G16_UNORM, + VK_FORMAT_R16G16_SNORM, + VK_FORMAT_R16G16_USCALED, + VK_FORMAT_R16G16_SSCALED, + VK_FORMAT_R16G16_UINT, + VK_FORMAT_R16G16_SINT, + VK_FORMAT_R16G16_SFLOAT, + VK_FORMAT_R16G16B16_UNORM, + VK_FORMAT_R16G16B16_SNORM, + VK_FORMAT_R16G16B16_USCALED, + VK_FORMAT_R16G16B16_SSCALED, + VK_FORMAT_R16G16B16_UINT, + VK_FORMAT_R16G16B16_SINT, + VK_FORMAT_R16G16B16_SFLOAT, + VK_FORMAT_R16G16B16A16_UNORM, + VK_FORMAT_R16G16B16A16_SNORM, + VK_FORMAT_R16G16B16A16_USCALED, + VK_FORMAT_R16G16B16A16_SSCALED, + VK_FORMAT_R16G16B16A16_UINT, + VK_FORMAT_R16G16B16A16_SINT, + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_FORMAT_R32_UINT, + VK_FORMAT_R32_SINT, + VK_FORMAT_R32_SFLOAT, + VK_FORMAT_R32G32_UINT, + VK_FORMAT_R32G32_SINT, + VK_FORMAT_R32G32_SFLOAT, + VK_FORMAT_R32G32B32_UINT, + VK_FORMAT_R32G32B32_SINT, + VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32B32A32_UINT, + VK_FORMAT_R32G32B32A32_SINT, + VK_FORMAT_R32G32B32A32_SFLOAT, + VK_FORMAT_R64_UINT, + VK_FORMAT_R64_SINT, + VK_FORMAT_R64_SFLOAT, + VK_FORMAT_R64G64_UINT, + VK_FORMAT_R64G64_SINT, + VK_FORMAT_R64G64_SFLOAT, + VK_FORMAT_R64G64B64_UINT, + VK_FORMAT_R64G64B64_SINT, + VK_FORMAT_R64G64B64_SFLOAT, + VK_FORMAT_R64G64B64A64_UINT, + VK_FORMAT_R64G64B64A64_SINT, + VK_FORMAT_R64G64B64A64_SFLOAT, + VK_FORMAT_B10G11R11_UFLOAT_PACK32, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + VK_FORMAT_D16_UNORM, + VK_FORMAT_X8_D24_UNORM_PACK32, + VK_FORMAT_D32_SFLOAT, + VK_FORMAT_S8_UINT, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_BC1_RGB_UNORM_BLOCK, + VK_FORMAT_BC1_RGB_SRGB_BLOCK, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + VK_FORMAT_BC2_UNORM_BLOCK, + VK_FORMAT_BC2_SRGB_BLOCK, + VK_FORMAT_BC3_UNORM_BLOCK, + VK_FORMAT_BC3_SRGB_BLOCK, + VK_FORMAT_BC4_UNORM_BLOCK, + VK_FORMAT_BC4_SNORM_BLOCK, + VK_FORMAT_BC5_UNORM_BLOCK, + VK_FORMAT_BC5_SNORM_BLOCK, + VK_FORMAT_BC6H_UFLOAT_BLOCK, + VK_FORMAT_BC6H_SFLOAT_BLOCK, + VK_FORMAT_BC7_UNORM_BLOCK, + VK_FORMAT_BC7_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11_SNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK, //184 + + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, //1000054000 + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, + + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT, //1000066000 + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT, + + VK_FORMAT_G8B8G8R8_422_UNORM, //1000156000 + VK_FORMAT_B8G8R8G8_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + VK_FORMAT_R10X6_UNORM_PACK16, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_R12X4_UNORM_PACK16, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_G16B16G16R16_422_UNORM, + VK_FORMAT_B16G16R16G16_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + (VkFormat)1000330000, //VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, + (VkFormat)1000330001, //VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, + (VkFormat)1000330002, //VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, + (VkFormat)1000330003, //VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT, + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT, +}; +#include +// Ensure the sTextureFormatToVk array is in strict numerical order (so we can quickly std::find +consteval bool CheckFormatOrdering() { + return std::is_sorted( std::begin( sTextureFormatToVk ), std::end( sTextureFormatToVk ) ); +} +static_assert(CheckFormatOrdering()); + + +// do some rough checks to ensure our formats are in the order we expect (or else the above lookup doesnt work!) +static_assert(int( TextureFormat::B8G8R8A8_SRGB ) == 50); +static_assert(sTextureFormatToVk[int( TextureFormat::B8G8R8A8_SRGB )] == int( VK_FORMAT_B8G8R8A8_SRGB )); +static_assert(int( TextureFormat::R32_SFLOAT ) == 100); +static_assert(int( TextureFormat::ETC2_R8G8B8A1_SRGB_BLOCK ) == 150); +static_assert(sTextureFormatToVk[int( TextureFormat::ETC2_R8G8B8A1_SRGB_BLOCK )] == int( VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK )); +static_assert(int( TextureFormat::ASTC_12x12_SRGB_BLOCK ) == 184); +static_assert(int( TextureFormat::PVRTC1_2BPP_UNORM_BLOCK_IMG ) == 185); +static_assert(sTextureFormatToVk[int( TextureFormat::PVRTC1_2BPP_UNORM_BLOCK_IMG )] == int( VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG )); +static_assert(int( TextureFormat::ASTC_4x4_SFLOAT_BLOCK ) == 193); +static_assert(sTextureFormatToVk[int( TextureFormat::ASTC_4x4_SFLOAT_BLOCK )] == int( VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT )); +static_assert(int( TextureFormat::G8B8G8R8_422_UNORM ) == 207); +static_assert(sTextureFormatToVk[int( TextureFormat::G8B8G8R8_422_UNORM )] == int( VK_FORMAT_G8B8G8R8_422_UNORM )); +static_assert(int( TextureFormat::EndCount ) == 247); + +static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type ) +{ + switch (type) + { + // + // 8 bits per component + // + case GL_UNSIGNED_BYTE: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R8_UNORM; + case GL_RG: return VK_FORMAT_R8G8_UNORM; + case GL_RGB: return VK_FORMAT_R8G8B8_UNORM; + case GL_BGR: return VK_FORMAT_B8G8R8_UNORM; + case GL_RGBA: return VK_FORMAT_R8G8B8A8_UNORM; + case GL_BGRA: return VK_FORMAT_B8G8R8A8_UNORM; + case GL_RED_INTEGER: return VK_FORMAT_R8_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R8G8_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R8G8B8_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_B8G8R8_UINT; + case GL_RGBA_INTEGER: return VK_FORMAT_R8G8B8A8_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_B8G8R8A8_UINT; + case GL_STENCIL_INDEX: return VK_FORMAT_S8_UINT; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_BYTE: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R8_SNORM; + case GL_RG: return VK_FORMAT_R8G8_SNORM; + case GL_RGB: return VK_FORMAT_R8G8B8_SNORM; + case GL_BGR: return VK_FORMAT_B8G8R8_SNORM; + case GL_RGBA: return VK_FORMAT_R8G8B8A8_SNORM; + case GL_BGRA: return VK_FORMAT_B8G8R8A8_SNORM; + case GL_RED_INTEGER: return VK_FORMAT_R8_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R8G8_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R8G8B8_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_B8G8R8_SINT; + case GL_RGBA_INTEGER: return VK_FORMAT_R8G8B8A8_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_B8G8R8A8_SINT; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 16 bits per component + // + case GL_UNSIGNED_SHORT: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R16_UNORM; + case GL_RG: return VK_FORMAT_R16G16_UNORM; + case GL_RGB: return VK_FORMAT_R16G16B16_UNORM; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_UNORM; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R16_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R16G16_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R16G16B16_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R16G16B16A16_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_D16_UNORM; + case GL_DEPTH_STENCIL: return VK_FORMAT_D16_UNORM_S8_UINT; + } + break; + } + case GL_SHORT: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R16_SNORM; + case GL_RG: return VK_FORMAT_R16G16_SNORM; + case GL_RGB: return VK_FORMAT_R16G16B16_SNORM; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_SNORM; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R16_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R16G16_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R16G16B16_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R16G16B16A16_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_HALF_FLOAT: + case GL_HALF_FLOAT_OES: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R16_SFLOAT; + case GL_RG: return VK_FORMAT_R16G16_SFLOAT; + case GL_RGB: return VK_FORMAT_R16G16B16_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R16G16B16A16_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 32 bits per component + // + case GL_UNSIGNED_INT: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R32_UINT; + case GL_RG: return VK_FORMAT_R32G32_UINT; + case GL_RGB: return VK_FORMAT_R32G32B32_UINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R32_UINT; + case GL_RG_INTEGER: return VK_FORMAT_R32G32_UINT; + case GL_RGB_INTEGER: return VK_FORMAT_R32G32B32_UINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_STENCIL: return VK_FORMAT_D24_UNORM_S8_UINT; + } + break; + } + case GL_INT: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R32_SINT; + case GL_RG: return VK_FORMAT_R32G32_SINT; + case GL_RGB: return VK_FORMAT_R32G32B32_SINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R32_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R32G32_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R32G32B32_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_FLOAT: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R32_SFLOAT; + case GL_RG: return VK_FORMAT_R32G32_SFLOAT; + case GL_RGB: return VK_FORMAT_R32G32B32_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R32G32B32A32_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_STENCIL: return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + break; + } + + // + // 64 bits per component + // + case GL_UNSIGNED_INT64: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R64_UINT; + case GL_RG: return VK_FORMAT_R64G64_UINT; + case GL_RGB: return VK_FORMAT_R64G64B64_UINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_UINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_INT64: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R64_SINT; + case GL_RG: return VK_FORMAT_R64G64_SINT; + case GL_RGB: return VK_FORMAT_R64G64B64_SINT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R64_SINT; + case GL_RG_INTEGER: return VK_FORMAT_R64G64_SINT; + case GL_RGB_INTEGER: return VK_FORMAT_R64G64B64_SINT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_DOUBLE: + { + switch (format) + { + case GL_RED: return VK_FORMAT_R64_SFLOAT; + case GL_RG: return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB: return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR: return VK_FORMAT_UNDEFINED; + case GL_RGBA: return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA: return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: return VK_FORMAT_R64_SFLOAT; + case GL_RG_INTEGER: return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB_INTEGER: return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA_INTEGER: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // Packed + // + case GL_UNSIGNED_BYTE_3_3_2: + assert( format == GL_RGB || format == GL_RGB_INTEGER ); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_BYTE_2_3_3_REV: + assert( format == GL_BGR || format == GL_BGR_INTEGER ); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_SHORT_5_6_5: + assert( format == GL_RGB || format == GL_RGB_INTEGER ); + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_6_5_REV: + assert( format == GL_BGR || format == GL_BGR_INTEGER ); + return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_5_5_1: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GL_UNSIGNED_INT_8_8_8_8: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_UNORM; + case GL_UNSIGNED_INT_8_8_8_8_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VK_FORMAT_A8B8G8R8_UINT_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + case GL_UNSIGNED_INT_10_10_10_2: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VK_FORMAT_A2R10G10B10_UINT_PACK32 : VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case GL_UNSIGNED_INT_2_10_10_10_REV: + assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VK_FORMAT_A2B10G10R10_UINT_PACK32 : VK_FORMAT_A2B10G10R10_UNORM_PACK32; + case GL_UNSIGNED_INT_10F_11F_11F_REV: + assert( format == GL_RGB || format == GL_BGR ); + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case GL_UNSIGNED_INT_5_9_9_9_REV: + assert( format == GL_RGB || format == GL_BGR ); + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case GL_UNSIGNED_INT_24_8: + assert( format == GL_DEPTH_STENCIL ); + return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + assert( format == GL_DEPTH_STENCIL ); + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + + return VK_FORMAT_UNDEFINED; +} + +static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat ) +{ + switch (internalFormat) + { + // + // 8 bits per component + // + case GL_R8: return VK_FORMAT_R8_UNORM; // 1-component, 8-bit unsigned normalized + case GL_RG8: return VK_FORMAT_R8G8_UNORM; // 2-component, 8-bit unsigned normalized + case GL_RGB8: return VK_FORMAT_R8G8B8_UNORM; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: return VK_FORMAT_R8_SNORM; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: return VK_FORMAT_R8G8_SNORM; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: return VK_FORMAT_R8G8B8_SNORM; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; // 4-component, 8-bit signed normalized + + case GL_R8UI: return VK_FORMAT_R8_UINT; // 1-component, 8-bit unsigned integer + case GL_RG8UI: return VK_FORMAT_R8G8_UINT; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: return VK_FORMAT_R8G8B8_UINT; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; // 4-component, 8-bit unsigned integer + + case GL_R8I: return VK_FORMAT_R8_SINT; // 1-component, 8-bit signed integer + case GL_RG8I: return VK_FORMAT_R8G8_SINT; // 2-component, 8-bit signed integer + case GL_RGB8I: return VK_FORMAT_R8G8B8_SINT; // 3-component, 8-bit signed integer + case GL_RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; // 4-component, 8-bit signed integer + + case GL_SR8: return VK_FORMAT_R8_SRGB; // 1-component, 8-bit sRGB + case GL_SRG8: return VK_FORMAT_R8G8_SRGB; // 2-component, 8-bit sRGB + case GL_SRGB8: return VK_FORMAT_R8G8B8_SRGB; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: return VK_FORMAT_R8G8B8A8_SRGB; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: return VK_FORMAT_R16_UNORM; // 1-component, 16-bit unsigned normalized + case GL_RG16: return VK_FORMAT_R16G16_UNORM; // 2-component, 16-bit unsigned normalized + case GL_RGB16: return VK_FORMAT_R16G16B16_UNORM; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: return VK_FORMAT_R16G16B16A16_UNORM; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: return VK_FORMAT_R16_SNORM; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: return VK_FORMAT_R16G16_SNORM; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: return VK_FORMAT_R16G16B16_SNORM; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: return VK_FORMAT_R16G16B16A16_SNORM; // 4-component, 16-bit signed normalized + + case GL_R16UI: return VK_FORMAT_R16_UINT; // 1-component, 16-bit unsigned integer + case GL_RG16UI: return VK_FORMAT_R16G16_UINT; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: return VK_FORMAT_R16G16B16_UINT; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT; // 4-component, 16-bit unsigned integer + + case GL_R16I: return VK_FORMAT_R16_SINT; // 1-component, 16-bit signed integer + case GL_RG16I: return VK_FORMAT_R16G16_SINT; // 2-component, 16-bit signed integer + case GL_RGB16I: return VK_FORMAT_R16G16B16_SINT; // 3-component, 16-bit signed integer + case GL_RGBA16I: return VK_FORMAT_R16G16B16A16_SINT; // 4-component, 16-bit signed integer + + case GL_R16F: return VK_FORMAT_R16_SFLOAT; // 1-component, 16-bit floating-point + case GL_RG16F: return VK_FORMAT_R16G16_SFLOAT; // 2-component, 16-bit floating-point + case GL_RGB16F: return VK_FORMAT_R16G16B16_SFLOAT; // 3-component, 16-bit floating-point + case GL_RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: return VK_FORMAT_R32_UINT; // 1-component, 32-bit unsigned integer + case GL_RG32UI: return VK_FORMAT_R32G32_UINT; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: return VK_FORMAT_R32G32B32_UINT; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT; // 4-component, 32-bit unsigned integer + + case GL_R32I: return VK_FORMAT_R32_SINT; // 1-component, 32-bit signed integer + case GL_RG32I: return VK_FORMAT_R32G32_SINT; // 2-component, 32-bit signed integer + case GL_RGB32I: return VK_FORMAT_R32G32B32_SINT; // 3-component, 32-bit signed integer + case GL_RGBA32I: return VK_FORMAT_R32G32B32A32_SINT; // 4-component, 32-bit signed integer + + case GL_R32F: return VK_FORMAT_R32_SFLOAT; // 1-component, 32-bit floating-point + case GL_RG32F: return VK_FORMAT_R32G32_SFLOAT; // 2-component, 32-bit floating-point + case GL_RGB32F: return VK_FORMAT_R32G32B32_SFLOAT; // 3-component, 32-bit floating-point + case GL_RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: return VK_FORMAT_UNDEFINED; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: return VK_FORMAT_UNDEFINED; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: return VK_FORMAT_UNDEFINED; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: return VK_FORMAT_UNDEFINED; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: return VK_FORMAT_UNDEFINED; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: return VK_FORMAT_A1R5G5B5_UNORM_PACK16; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: return VK_FORMAT_A2R10G10B10_UINT_PACK32; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGB_UNORM_BLOCK; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return VK_FORMAT_BC2_UNORM_BLOCK; // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return VK_FORMAT_BC3_UNORM_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGB_SRGB_BLOCK; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return VK_FORMAT_BC2_SRGB_BLOCK; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return VK_FORMAT_BC3_SRGB_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: return VK_FORMAT_BC6H_UFLOAT_BLOCK; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: return VK_FORMAT_BC6H_SFLOAT_BLOCK; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: return VK_FORMAT_BC7_UNORM_BLOCK; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: return VK_FORMAT_BC7_SRGB_BLOCK; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC1, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_RGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: return VK_FORMAT_EAC_R11_UNORM_BLOCK; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: return VK_FORMAT_EAC_R11_SNORM_BLOCK; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: return VK_FORMAT_ASTC_6x5_UNORM_BLOCK; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: return VK_FORMAT_ASTC_6x6_UNORM_BLOCK; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: return VK_FORMAT_ASTC_8x5_UNORM_BLOCK; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: return VK_FORMAT_ASTC_8x6_UNORM_BLOCK; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: return VK_FORMAT_ASTC_8x8_UNORM_BLOCK; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: return VK_FORMAT_ASTC_10x5_UNORM_BLOCK; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: return VK_FORMAT_ASTC_10x6_UNORM_BLOCK; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: return VK_FORMAT_ASTC_10x8_UNORM_BLOCK; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: return VK_FORMAT_ASTC_10x10_UNORM_BLOCK; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: return VK_FORMAT_ASTC_12x10_UNORM_BLOCK; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: return VK_FORMAT_ASTC_12x12_UNORM_BLOCK; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: return VK_FORMAT_ASTC_5x4_SRGB_BLOCK; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: return VK_FORMAT_ASTC_5x5_SRGB_BLOCK; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: return VK_FORMAT_ASTC_6x5_SRGB_BLOCK; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: return VK_FORMAT_ASTC_6x6_SRGB_BLOCK; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: return VK_FORMAT_ASTC_8x5_SRGB_BLOCK; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: return VK_FORMAT_ASTC_8x6_SRGB_BLOCK; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: return VK_FORMAT_ASTC_8x8_SRGB_BLOCK; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: return VK_FORMAT_ASTC_10x5_SRGB_BLOCK; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: return VK_FORMAT_ASTC_10x6_SRGB_BLOCK; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: return VK_FORMAT_ASTC_10x8_SRGB_BLOCK; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: return VK_FORMAT_ASTC_10x10_SRGB_BLOCK; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: return VK_FORMAT_ASTC_12x10_SRGB_BLOCK; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: return VK_FORMAT_ASTC_12x12_SRGB_BLOCK; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: return VK_FORMAT_UNDEFINED; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: return VK_FORMAT_D16_UNORM; + case GL_DEPTH_COMPONENT24: return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_COMPONENT32: return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT32F: return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_COMPONENT32F_NV: return VK_FORMAT_D32_SFLOAT; + case GL_STENCIL_INDEX1: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX4: return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX8: return VK_FORMAT_S8_UINT; + case GL_STENCIL_INDEX16: return VK_FORMAT_UNDEFINED; + case GL_DEPTH24_STENCIL8: return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_DEPTH32F_STENCIL8: return VK_FORMAT_D32_SFLOAT_S8_UINT; + case GL_DEPTH32F_STENCIL8_NV: return VK_FORMAT_D32_SFLOAT_S8_UINT; + + default: return VK_FORMAT_UNDEFINED; + } +} + + + +VkFormat +ktxTexture1_GetVkFormat( ktxTexture1* This ) +{ + VkFormat vkFormat; + + vkFormat = vkGetFormatFromOpenGLInternalFormat( This->glInternalformat ); + if (vkFormat == VK_FORMAT_UNDEFINED) { + vkFormat = vkGetFormatFromOpenGLFormat( This->glFormat, + This->glType ); + } + return vkFormat; +} + +//----------------------------------------------------------------------------- +TextureFormat VkToTextureFormat( VkFormat f ) +//----------------------------------------------------------------------------- +{ + // Use the fact we have ensured the TextureFormats are sorted with respect to the values of VkFormat, so we can binary search. + auto foundIt = std::lower_bound( std::begin( sTextureFormatToVk ), std::end( sTextureFormatToVk ), f ); + if (foundIt != std::end( sTextureFormatToVk ) && *foundIt == f) + return TextureFormat( std::distance( std::begin( sTextureFormatToVk ), foundIt ) ); + return TextureFormat::UNDEFINED; +} + +//----------------------------------------------------------------------------- +VkFormat ktxTexture2_GetVkFormat( ktxTexture2* This ) +//----------------------------------------------------------------------------- +{ + return (VkFormat) This->vkFormat; +} + +//----------------------------------------------------------------------------- +VkFormat ktxTexture_GetVkFormat( ktxTexture* This ) +//----------------------------------------------------------------------------- +{ + if (This->classId == ktxTexture2_c) + return (VkFormat) ktxTexture2_GetVkFormat( (ktxTexture2*)This ); + else + return (VkFormat) ktxTexture1_GetVkFormat( (ktxTexture1*)This ); +} + +//----------------------------------------------------------------------------- +TextureFormat ktxTexture_GetTextureFormat( ktxTexture* This ) +//----------------------------------------------------------------------------- +{ + return VkToTextureFormat( ktxTexture_GetVkFormat( This ) ); +} + +//----------------------------------------------------------------------------- +TextureDx12 TextureKtx::LoadKtx(Dx12& graphicsApi, const TextureKtxFileWrapper& fileData, Sampler sampler) +//----------------------------------------------------------------------------- +{ + auto* const pKtxData = GetKtxTexture(fileData); + if (!pKtxData) + return {}; + TextureFormat textureFormat = TextureFormat::UNDEFINED; + if (ktxTexture_NeedsTranscoding(pKtxData) && pKtxData->classId == class_id::ktxTexture2_c) + { + auto pKtx2Data = (ktxTexture2* const)pKtxData; + if (KTX_SUCCESS != ktxTexture2_TranscodeBasis(pKtx2Data, KTX_TTF_RGBA32, (ktx_transcode_flag_bits_e)0)) + { + return {}; + } + + textureFormat = TextureFormat::R8G8B8A8_UNORM; + } + else + { + textureFormat = ktxTexture_GetTextureFormat( pKtxData ); + ktxTexture_LoadImageData(pKtxData, nullptr, 0); + } + + auto& memoryManager = graphicsApi.GetMemoryManager(); + //bool Buffer::Initialize(MemoryManager * pManager, size_t size, BufferUsageFlags bufferUsageFlags, MemoryUsage memoryUsage) + + auto dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D; + switch (pKtxData->numDimensions) { + case 2: + dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + break; + case 3: + dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D; + break; + default: break; + } + const uint8_t* pSrcData = ktxTexture_GetData(pKtxData); + + bool rgbTorgba = false; + switch (textureFormat) { + case TextureFormat::R8G8B8_UNORM: + case TextureFormat::R8G8B8_SNORM: + case TextureFormat::R8G8B8_USCALED: + case TextureFormat::R8G8B8_SSCALED: + case TextureFormat::R8G8B8_UINT: + case TextureFormat::R8G8B8_SINT: + case TextureFormat::R8G8B8_SRGB: + case TextureFormat::B8G8R8_UNORM: + case TextureFormat::B8G8R8_SNORM: + case TextureFormat::B8G8R8_USCALED: + case TextureFormat::B8G8R8_SSCALED: + case TextureFormat::B8G8R8_UINT: + case TextureFormat::B8G8R8_SINT: + case TextureFormat::B8G8R8_SRGB: + static_assert(uint32_t( TextureFormat::R8G8B8A8_UNORM ) == uint32_t( TextureFormat::R8G8B8_UNORM ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_SNORM ) == uint32_t( TextureFormat::R8G8B8_SNORM ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_USCALED ) == uint32_t( TextureFormat::R8G8B8_USCALED ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_SSCALED ) == uint32_t( TextureFormat::R8G8B8_SSCALED ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_UINT ) == uint32_t( TextureFormat::R8G8B8_UINT ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_SINT ) == uint32_t( TextureFormat::R8G8B8_SINT ) + 14); + static_assert(uint32_t( TextureFormat::R8G8B8A8_SRGB ) == uint32_t( TextureFormat::R8G8B8_SRGB ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_UNORM ) == uint32_t( TextureFormat::B8G8R8_UNORM ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_SNORM ) == uint32_t( TextureFormat::B8G8R8_SNORM ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_USCALED ) == uint32_t( TextureFormat::B8G8R8_USCALED ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_SSCALED ) == uint32_t( TextureFormat::B8G8R8_SSCALED ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_UINT ) == uint32_t( TextureFormat::B8G8R8_UINT ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_SINT ) == uint32_t( TextureFormat::B8G8R8_SINT ) + 14); + static_assert(uint32_t( TextureFormat::B8G8R8A8_SRGB ) == uint32_t( TextureFormat::B8G8R8_SRGB ) + 14); + textureFormat = TextureFormat( uint32_t( textureFormat ) + 14 ); + rgbTorgba = true; + break; + default: + break; + } + + const DXGI_FORMAT dxgiFormat = TextureFormatToDx( textureFormat ); + const D3D12_RESOURCE_DESC textureDesc = { .Dimension = dimension, + .Alignment = 0, + .Width = pKtxData->baseWidth, + .Height = pKtxData->baseHeight, + .DepthOrArraySize = (UINT16) pKtxData->baseDepth, + .MipLevels = (uint16_t)pKtxData->numLevels, + .Format = dxgiFormat, + .SampleDesc = {.Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + .Flags = D3D12_RESOURCE_FLAG_NONE + }; + + D3D12_CLEAR_VALUE clearColor{}; + auto gpuBuffer = memoryManager.CreateImage(textureDesc, MemoryUsage::GpuExclusive, D3D12_RESOURCE_STATE_COPY_DEST, clearColor); + + { + const size_t numSubresources = textureDesc.DepthOrArraySize * textureDesc.MipLevels; + auto gpuBufferDesc = gpuBuffer.GetResource()->GetDesc(); + std::vector resourceLayouts; + resourceLayouts.resize(numSubresources); + size_t resourceDataSize = 0; + graphicsApi.GetDevice()->GetCopyableFootprints(&gpuBufferDesc, 0/*startSubresource*/, numSubresources, 0, resourceLayouts.data(), nullptr, nullptr, &resourceDataSize); + + //auto stagingBuffer = memoryManager.CreateImage(textureDesc, MemoryUsage::CpuToGpu, D3D12_RESOURCE_STATE_COPY_SOURCE); + auto stagingBuffer = memoryManager.CreateBuffer(resourceDataSize, BufferUsageFlags::TransferSrc, MemoryUsage::CpuToGpu); + { + assert( pKtxData->dataSize <= resourceDataSize ); + auto stagingCpu = memoryManager.Map( stagingBuffer ); + + for (size_t mip = 0; mip < pKtxData->numLevels; ++mip) + { + ktx_size_t srcOffset = 0; + ktxTexture_GetImageOffset(pKtxData, mip, 0/*layer*/, 0/*faceSlice*/, &srcOffset); + auto* pSrc = pSrcData + srcOffset; + + auto pDestMip = stagingCpu.data() + resourceLayouts[mip].Offset; + auto pitch = resourceLayouts[mip].Footprint.RowPitch; + auto mipWidth = resourceLayouts[mip].Footprint.Width; + auto mipHeight = resourceLayouts[mip].Footprint.Height; + auto mipDepth = resourceLayouts[mip].Footprint.Depth; + + auto srcPitch = WidthToPitch(dxgiFormat, mipWidth); + + uint32_t slicePitch = pitch * mipHeight; + for (size_t z = 0; z < mipDepth; ++z) + { + auto pDest = pDestMip + slicePitch * z; + { + for (auto y = 0; y < mipHeight; ++y) + { + if (rgbTorgba) + { + auto pS = pSrc; + auto pD = pDest; + for (auto x = 0; x < mipWidth; ++x) + { + *pD++ = *pS++; + *pD++ = *pS++; + *pD++ = *pS++; + *pD++ = 255; // fill alpha + } + } + else + memcpy(pDest, pSrc, pitch/*we may only want to copy the actual width (in bytes), for now copy the entire pitch*/); + pDest += pitch; + pSrc += srcPitch; + } + } + } + } + memoryManager.Unmap(stagingBuffer, std::move(stagingCpu)); + } + ID3D12GraphicsCommandList* cmdList = graphicsApi.StartSetupCommandBuffer(); + assert(cmdList); + + for(UINT subResourceIdx = 0; subResourceIdx < numSubresources; ++subResourceIdx) + { + const D3D12_TEXTURE_COPY_LOCATION gpuDest{ .pResource = gpuBuffer.GetResource(), + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = subResourceIdx + }; + const D3D12_TEXTURE_COPY_LOCATION stagingSrc{ .pResource = stagingBuffer.GetResource(), + .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, + .PlacedFootprint = resourceLayouts[subResourceIdx] + }; + cmdList->CopyTextureRegion(&gpuDest, 0, 0, 0, &stagingSrc, nullptr); + } + //cmdList->CopyResource(gpuBuffer.GetResource(), stagingBuffer.GetResource()); + graphicsApi.FinishSetupCommandBuffer(cmdList); + + memoryManager.Destroy(std::move(stagingBuffer)); + } + + // Return the fully formed texture object + return TextureDx12{ (uint32_t)textureDesc.Width, textureDesc.Height, (uint32_t)textureDesc.DepthOrArraySize, (uint32_t)textureDesc.MipLevels, textureFormat, textureDesc.Layout, std::move(gpuBuffer)/*, sampler*/}; +} + +TextureDx12 TextureKtx::LoadKtx(Dx12& graphicsApi, AssetManager& assetManager, const char* const pFileName, Sampler sampler) +{ + auto ktxData = LoadFile(assetManager, pFileName); + if (!ktxData) + return {}; + return LoadKtx(graphicsApi, ktxData, sampler); +} + +TextureKtxFileWrapper TextureKtx::Transcode(TextureKtxFileWrapper&& fileData) +{ + auto* const pKtxData = GetKtxTexture(fileData); + if (pKtxData != nullptr && ktxTexture_NeedsTranscoding(pKtxData) && pKtxData->classId == class_id::ktxTexture2_c) + { + auto pKtx2Data = (ktxTexture2* const)pKtxData; + if (KTX_SUCCESS != ktxTexture2_TranscodeBasis(pKtx2Data, KTX_TTF_RGBA32, (ktx_transcode_flag_bits_e)0)) + { + return {}; + } + } + return std::move(fileData); +} + +static uint32_t WidthToPitch( DXGI_FORMAT dxgiFormat, uint32_t width ) +{ + return Dx12::FormatBytesPerPixel( dxgiFormat ) * width; +} + +static uint32_t AreaToBytes( DXGI_FORMAT dxgiFormat, uint32_t width, uint32_t height ) +{ + return WidthToPitch( dxgiFormat, width) * height; +} + diff --git a/framework/code/texture/dx12/loaderKtx.hpp b/framework/code/texture/dx12/loaderKtx.hpp new file mode 100644 index 0000000..3e021e0 --- /dev/null +++ b/framework/code/texture/dx12/loaderKtx.hpp @@ -0,0 +1,82 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +/// +/// KTX image file loading for Dx12 +/// + +#include "../loaderKtx.hpp" +#include "memory/dx12/memoryMapped.hpp" +#include +#include +#include + +// Forward declarations +class GraphicsApiBase; + +struct ktxTexture; +class Dx12; +template class Texture; +template class Sampler; + + +/// @brief Class to handle loading KTX textures in to Vulkan memory +/// Generally applications will want a singleton of this class. +template<> +class TextureKtx : public TextureKtxBase +{ + TextureKtx(const TextureKtx&) = delete; + TextureKtx& operator=(const TextureKtx&) = delete; +public: + TextureKtx(Dx12&) noexcept; + ~TextureKtx() noexcept override; + + /// @brief Initialize this loader class + /// @return true on success + bool Initialize() override; + + /// @brief Release (de-initialize) back to a clean (initializable) state + void Release() override; + + /// @brief Do the necessary upload etc to go from a cpu texture representation to Vulkan format + /// @param textureFile ktx file data we want to load as a vulkan texture + /// @param sampler sampler that loaded texture will take OWNERSHIP of, may be VK_NULL_HANDLE (in which case LoadKtx creates an appropriate sampler) + /// @returns a &TextureDx12, will be empty on failure + Texture LoadKtx(Dx12& dx12, const TextureKtxFileWrapper& textureFile, Sampler sampler); + + /// @brief Load a ktx file and do the necessary upload etc to go from a cpu texture representation to Vulkan format + /// @param filename of ktx (or ktx2) format file we want to load as a vulkan texture + /// @param sampler sampler that loaded texture will take OWNERSHIP of, may be VK_NULL_HANDLE (in which case LoadKtx creates an appropriate sampler) + /// @returns a &TextureDx12, will be empty on failure + Texture LoadKtx(Dx12& dx12, AssetManager& assetManager, const char* const pFileName, Sampler sampler); + + /// @brief Run the Ktx2 transcoding step (if needed) + /// Will do nothing for textures that do not need transcoding. + /// Performance will depend on ktx2 texture size and intermediate encoding format. + /// @param fileData ktx file data loaded by TextureKtxBase::LoadData or similar. + /// @return transcoded Ktx texture. + TextureKtxFileWrapper Transcode(TextureKtxFileWrapper&& fileData); + +protected: + Dx12& m_Dx12; +}; + + +///// @brief Function specialization +//template<> +//Texture TextureKtxBase::LoadKtx( Dx12& vulkan, const TextureKtxFileWrapper& textureFile, const Sampler& sampler ) +//{ +// return apiCast( this )->LoadKtx( vulkan, textureFile, sampler ); +//} +// +///// @brief Function specialization +//template<> +//Texture TextureKtxBase::LoadKtx( Dx12& vulkan, AssetManager& assetManager, const char* const pFileName, const Sampler& sampler ) +//{ +// return apiCast( this )->LoadKtx( vulkan, assetManager, pFileName, sampler ); +//} diff --git a/framework/code/texture/dx12/sampler.cpp b/framework/code/texture/dx12/sampler.cpp new file mode 100644 index 0000000..f325467 --- /dev/null +++ b/framework/code/texture/dx12/sampler.cpp @@ -0,0 +1,68 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "sampler.hpp" + + +constexpr D3D12_FILTER EnumToDx12( SamplerFilter s ) { + assert(s != SamplerFilter::Undefined && s <= SamplerFilter::Anisotropic); + if (s <= SamplerFilter::Linear) + return D3D12_FILTER( int( s ) - 1 ); + return D3D12_FILTER_ANISOTROPIC; +} +static_assert(D3D12_FILTER_MIN_MAG_MIP_POINT == int(SamplerFilter::Nearest) - 1); +static_assert(D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR == int(SamplerFilter::Linear) - 1); + +constexpr D3D12_TEXTURE_ADDRESS_MODE EnumToDx12(SamplerAddressMode s) { return D3D12_TEXTURE_ADDRESS_MODE(int(s) + 1); } +static_assert(D3D12_TEXTURE_ADDRESS_MODE_WRAP == int(SamplerAddressMode::Repeat)); +static_assert(D3D12_TEXTURE_ADDRESS_MODE_MIRROR == int(SamplerAddressMode::MirroredRepeat)); +static_assert(D3D12_TEXTURE_ADDRESS_MODE_CLAMP == int(SamplerAddressMode::ClampEdge)); +static_assert(D3D12_TEXTURE_ADDRESS_MODE_BORDER == int(SamplerAddressMode::ClampBorder)); +static_assert(D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE == int(SamplerAddressMode::MirroredClampEdge)); + + +//----------------------------------------------------------------------------- +// Implementation of template function specialization +template<> +Sampler CreateSampler(Dx12& dx12, const CreateSamplerObjectInfo& createInfo) +//----------------------------------------------------------------------------- +{ + return Sampler{ StructToDx12( createInfo ) }; +} + +//----------------------------------------------------------------------------- +D3D12_SAMPLER_DESC StructToDx12( const CreateSamplerObjectInfo& createInfo ) +//----------------------------------------------------------------------------- +{ + const D3D12_TEXTURE_ADDRESS_MODE SamplerModeDx12 = EnumToDx12( createInfo.Mode ); + D3D12_SAMPLER_DESC samplerDesc{ + .Filter = EnumToDx12( createInfo.Filter ), + .AddressU = SamplerModeDx12, + .AddressV = SamplerModeDx12, + .AddressW = SamplerModeDx12, + .MipLODBias = createInfo.MipBias, + .MaxAnisotropy = std::max( (UINT)1, (UINT)createInfo.Anisotropy ), + .ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS, + .BorderColor = {}, + .MinLOD = createInfo.MinLod, + .MaxLOD = createInfo.MaxLod + }; + switch (createInfo.BorderColor) { + case SamplerBorderColor::TransparentBlackFloat: + case SamplerBorderColor::TransparentBlackInt: + break; + case SamplerBorderColor::OpaqueBlackFloat: + case SamplerBorderColor::OpaqueBlackInt: + samplerDesc.BorderColor[3] = 1.0f; + break; + case SamplerBorderColor::OpaqueWhiteFloat: + case SamplerBorderColor::OpaqueWhiteInt: + std::fill( std::begin( samplerDesc.BorderColor ), std::end( samplerDesc.BorderColor ), 1.0f ); + break; + } + return samplerDesc; +} diff --git a/framework/code/texture/dx12/sampler.hpp b/framework/code/texture/dx12/sampler.hpp new file mode 100644 index 0000000..e2a5e40 --- /dev/null +++ b/framework/code/texture/dx12/sampler.hpp @@ -0,0 +1,43 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "../sampler.hpp" +#define NOMINMAX +#include + +// Forward declarations +class Dx12; +struct D3D12_SAMPLER_DESC; + +using SamplerDx12 = Sampler; + +D3D12_SAMPLER_DESC StructToDx12( const CreateSamplerObjectInfo& createInfo ); + + +/// @brief Template specialization of sampler container for Dx12 graphics api. +template<> +class Sampler final : public SamplerBase +{ +public: + Sampler() noexcept {}; + Sampler(const Sampler& src) noexcept : m_SamplerDesc(src.m_SamplerDesc) {}; + Sampler(const D3D12_SAMPLER_DESC& desc) noexcept : m_SamplerDesc(desc) {}; + Sampler& operator=(const Sampler& src) noexcept { + if (this != &src) + m_SamplerDesc = src.m_SamplerDesc; + return *this; + } + const auto& GetDesc() const { return m_SamplerDesc; } +private: + D3D12_SAMPLER_DESC m_SamplerDesc{}; +}; + + +/// Template specialization for Dx12 CreateSampler +template<> +Sampler CreateSampler( Dx12&, const CreateSamplerObjectInfo& ); diff --git a/framework/code/texture/dx12/texture.cpp b/framework/code/texture/dx12/texture.cpp new file mode 100644 index 0000000..0f610f1 --- /dev/null +++ b/framework/code/texture/dx12/texture.cpp @@ -0,0 +1,428 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "memory/dx12/memoryMapped.hpp" +#include "texture.hpp" +#include "dx12/dx12.hpp" +#include "system/os_common.h" +#include + + +Texture::Texture() noexcept : TextureBase() +{} + +Texture::Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, TextureFormat format, D3D12_TEXTURE_LAYOUT textureLayout, MemoryAllocatedBuffer resourceBuffer/*, const Sampler& sampler*/ ) noexcept + : TextureBase() + , Width(width) + , Height(height) + , Depth(depth) + , MipLevels(mipLevels) + , Format(format) + , TextureLayout(textureLayout) + , MemoryBuffer(std::move(resourceBuffer)) +// , Sampler(sampler) +{ + ResourceViewDesc = { + .Format = TextureFormatToDx(format), + .ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D, + .Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + .Texture2D { + .MostDetailedMip = 0, + .MipLevels = mipLevels + } }; +} + +// +// Constructors/move-operators for Texture. +// Ensures we are not leaking owned members. +// +Texture::Texture(Texture&& other) noexcept +{ + *this = std::move(other); +} +Texture& Texture::operator=(Texture&& other) noexcept +{ + if (this != &other) + { + // Some of the data can be copied and is ok to leave 'other' alone (move operator just has to ensure the 'other' is in a valid state and can be safely deleted) + Width = other.Width; + Height = other.Height; + Depth = other.Depth; + MipLevels = other.MipLevels; + FirstMip = other.FirstMip; + Format = other.Format; + TextureLayout = other.TextureLayout; + ResourceViewDesc = other.ResourceViewDesc; + // Actually transfer ownership from 'other' + MemoryBuffer = std::move(other.MemoryBuffer); +// Sampler = other.Sampler; +// other.Sampler = {}; + } + return *this; +} + +Texture::~Texture() noexcept +{ + // Asserts to ensure we called ReleaseTexture on this already. + assert(IsEmpty()); +} + +void Texture::Release(GraphicsApiBase* pGraphicsApi) +{ + auto* pDx12 = static_cast(pGraphicsApi); + + //Sampler = {}; + + if (MemoryBuffer) + pDx12->GetMemoryManager().Destroy(std::move(MemoryBuffer)); +} + +bool Texture::IsEmpty() const +{ + return !MemoryBuffer; +} + +constexpr UINT EnumToDx12( Msaa m ) { return UINT( m ); } + + +//----------------------------------------------------------------------------- +// Implementation of template function specialization +template<> +Texture CreateTextureObject( Dx12& dx12, const CreateTexObjectInfo& texInfo, MemoryPool* pPool ) +//----------------------------------------------------------------------------- +{ + assert( pPool == nullptr );//Needs implementing if used + + if (texInfo.pName == nullptr) + LOGI( "CreateTextureObject (%dx%d): ", texInfo.uiWidth, texInfo.uiHeight ); + else + LOGI( "CreateTextureObject (%dx%d): %s", texInfo.uiWidth, texInfo.uiHeight, texInfo.pName ); + + D3D12_TEXTURE_LAYOUT imageLayout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + D3D12_RESOURCE_FLAGS resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_RENDER_TARGET; + D3D12_CLEAR_VALUE clearValue{.Format = TextureFormatToDx( texInfo.Format ), .Color = {}}; + if (texInfo.TexType == TT_DEPTH_TARGET) + { + resourceFlags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + resourceState = D3D12_RESOURCE_STATE_DEPTH_WRITE; + clearValue.DepthStencil = {.Depth = 1.0f, .Stencil = 0}; + } + const D3D12_RESOURCE_DESC textureDesc = { .Dimension = ( texInfo.uiDepth == 1 ) ? D3D12_RESOURCE_DIMENSION_TEXTURE2D : D3D12_RESOURCE_DIMENSION_TEXTURE3D, + .Alignment = 0, + .Width = texInfo.uiWidth, + .Height = texInfo.uiHeight, + .DepthOrArraySize = (UINT16) texInfo.uiDepth, + .MipLevels = (UINT16) texInfo.uiMips, + .Format = clearValue.Format, + .SampleDesc = {.Count = EnumToDx12(texInfo.Msaa), .Quality = 0 }, + .Layout = imageLayout, + .Flags = resourceFlags + }; + + auto& memoryManager = dx12.GetMemoryManager(); + auto gpuBuffer = memoryManager.CreateImage( textureDesc, MemoryUsage::GpuExclusive, resourceState, clearValue ); + if (!gpuBuffer) + return {}; + + return{ texInfo.uiWidth, texInfo.uiHeight, texInfo.uiDepth, texInfo.uiMips, texInfo.Format, imageLayout, std::move(gpuBuffer) }; +} + + +//----------------------------------------------------------------------------- +// Implementation of template function specialization +template<> +Texture CreateTextureFromBuffer( Dx12& dx12, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ) +//----------------------------------------------------------------------------- +{ + assert( 0 && "unimplemented!" ); ///TODO: implement! + return {}; +} + +//----------------------------------------------------------------------------- +// Implementation of template function specialization +template<> +Texture CreateTextureObjectView( Dx12&, const Texture& original, TextureFormat viewFormat ) +//----------------------------------------------------------------------------- +{ + assert( 0 && "unimplemented!" ); ///TODO: implement! + return {}; +} + + +//----------------------------------------------------------------------------- +// Implementation of template function specialization +template<> +void ReleaseTexture( Dx12& dx12, Texture* pTexture ) +//----------------------------------------------------------------------------- +{ + pTexture->Release(&dx12); + *pTexture = Texture{}; // destroy and clear +} + + +static constexpr std::array sTextureFormatToDx { + DXGI_FORMAT_UNKNOWN, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R4G4_UNORM_PACK8, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R4G4B4A4_UNORM_PACK16, + DXGI_FORMAT_B4G4R4A4_UNORM,// VK_FORMAT_B4G4R4A4_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R5G6B5_UNORM_PACK16, + DXGI_FORMAT_B5G6R5_UNORM,// VK_FORMAT_B5G6R5_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R5G5B5A1_UNORM_PACK16, + DXGI_FORMAT_B5G5R5A1_UNORM,// VK_FORMAT_B5G5R5A1_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A1R5G5B5_UNORM_PACK16, + DXGI_FORMAT_R8_UNORM,// VK_FORMAT_R8_UNORM, + DXGI_FORMAT_R8_SNORM,// VK_FORMAT_R8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8_SSCALED, + DXGI_FORMAT_R8_UINT,// VK_FORMAT_R8_UINT, + DXGI_FORMAT_R8_SINT,// VK_FORMAT_R8_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8_SRGB, + DXGI_FORMAT_R8G8_UNORM,// VK_FORMAT_R8G8_UNORM, + DXGI_FORMAT_R8G8_SNORM,// VK_FORMAT_R8G8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8_SSCALED, + DXGI_FORMAT_R8G8_UINT,// VK_FORMAT_R8G8_UINT, + DXGI_FORMAT_R8G8_SINT,// VK_FORMAT_R8G8_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8_SRGB, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_SSCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8_SRGB, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_SSCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM,// VK_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_SNORM,// VK_FORMAT_R8G8B8A8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8A8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R8G8B8A8_SSCALED, + DXGI_FORMAT_R8G8B8A8_UINT,// VK_FORMAT_R8G8B8A8_UINT, + DXGI_FORMAT_R8G8B8A8_SINT,// VK_FORMAT_R8G8B8A8_SINT, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,// VK_FORMAT_R8G8B8A8_SRGB, + DXGI_FORMAT_B8G8R8A8_UNORM,// VK_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8A8_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8A8_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8A8_SSCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8A8_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8A8_SINT, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,// VK_FORMAT_B8G8R8A8_SRGB, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_UNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_SNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_USCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_SSCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_UINT_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_SINT_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A8B8G8R8_SRGB_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_UNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_SNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_USCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_SSCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_UINT_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2R10G10B10_SINT_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_UNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_SNORM_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_USCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_SSCALED_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_UINT_PACK32, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A2B10G10R10_SINT_PACK32, + DXGI_FORMAT_R16_UNORM,// VK_FORMAT_R16_UNORM, + DXGI_FORMAT_R16_SNORM,// VK_FORMAT_R16_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16_SSCALED, + DXGI_FORMAT_R16_UINT,// VK_FORMAT_R16_UINT, + DXGI_FORMAT_R16_SINT,// VK_FORMAT_R16_SINT, + DXGI_FORMAT_R16_FLOAT,// VK_FORMAT_R16_SFLOAT, + DXGI_FORMAT_R16G16_UNORM,// VK_FORMAT_R16G16_UNORM, + DXGI_FORMAT_R16G16_SNORM,// VK_FORMAT_R16G16_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16_SSCALED, + DXGI_FORMAT_R16G16_UINT,// VK_FORMAT_R16G16_UINT, + DXGI_FORMAT_R16G16_SINT,// VK_FORMAT_R16G16_SINT, + DXGI_FORMAT_R16G16_FLOAT,// VK_FORMAT_R16G16_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_SSCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16_SFLOAT, + DXGI_FORMAT_R16G16B16A16_UNORM,// VK_FORMAT_R16G16B16A16_UNORM, + DXGI_FORMAT_R16G16B16A16_SNORM,// VK_FORMAT_R16G16B16A16_SNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16A16_USCALED, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R16G16B16A16_SSCALED, + DXGI_FORMAT_R16G16B16A16_UINT,// VK_FORMAT_R16G16B16A16_UINT, + DXGI_FORMAT_R16G16B16A16_SINT,// VK_FORMAT_R16G16B16A16_SINT, + DXGI_FORMAT_R16G16B16A16_FLOAT,// VK_FORMAT_R16G16B16A16_SFLOAT, + DXGI_FORMAT_R32_UINT,// VK_FORMAT_R32_UINT, + DXGI_FORMAT_R32_SINT,// VK_FORMAT_R32_SINT, + DXGI_FORMAT_R32_FLOAT,// VK_FORMAT_R32_SFLOAT, + DXGI_FORMAT_R32G32_UINT,// VK_FORMAT_R32G32_UINT, + DXGI_FORMAT_R32G32_SINT,// VK_FORMAT_R32G32_SINT, + DXGI_FORMAT_R32G32_FLOAT,// VK_FORMAT_R32G32_SFLOAT, + DXGI_FORMAT_R32G32B32_UINT,// VK_FORMAT_R32G32B32_UINT, + DXGI_FORMAT_R32G32B32_SINT,// VK_FORMAT_R32G32B32_SINT, + DXGI_FORMAT_R32G32B32_FLOAT,// VK_FORMAT_R32G32B32_SFLOAT, + DXGI_FORMAT_R32G32B32A32_UINT,// VK_FORMAT_R32G32B32A32_UINT, + DXGI_FORMAT_R32G32B32A32_SINT,// VK_FORMAT_R32G32B32A32_SINT, + DXGI_FORMAT_R32G32B32A32_FLOAT,// VK_FORMAT_R32G32B32A32_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64A64_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64A64_SINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R64G64B64A64_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B10G11R11_UFLOAT_PACK32, //DXGI_FORMAT_R11G11B10_FLOAT + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, //DXGI_FORMAT_R9G9B9E5_SHAREDEXP + DXGI_FORMAT_D16_UNORM,// VK_FORMAT_D16_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_X8_D24_UNORM_PACK32, //DXGI_FORMAT_R24_UNORM_X8_TYPELESS + DXGI_FORMAT_D32_FLOAT,// VK_FORMAT_D32_SFLOAT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_S8_UINT, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_D16_UNORM_S8_UINT, + DXGI_FORMAT_D24_UNORM_S8_UINT,// VK_FORMAT_D24_UNORM_S8_UINT, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT,// VK_FORMAT_D32_SFLOAT_S8_UINT, + DXGI_FORMAT_BC1_UNORM,// VK_FORMAT_BC1_RGB_UNORM_BLOCK, + DXGI_FORMAT_BC1_UNORM_SRGB,// VK_FORMAT_BC1_RGB_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + DXGI_FORMAT_BC2_UNORM,// VK_FORMAT_BC2_UNORM_BLOCK, + DXGI_FORMAT_BC2_UNORM_SRGB,// VK_FORMAT_BC2_SRGB_BLOCK, + DXGI_FORMAT_BC3_UNORM,// VK_FORMAT_BC3_UNORM_BLOCK, + DXGI_FORMAT_BC3_UNORM_SRGB,// VK_FORMAT_BC3_SRGB_BLOCK, + DXGI_FORMAT_BC4_UNORM,// VK_FORMAT_BC4_UNORM_BLOCK, + DXGI_FORMAT_BC4_SNORM,// VK_FORMAT_BC4_SNORM_BLOCK, + DXGI_FORMAT_BC5_UNORM,// VK_FORMAT_BC5_UNORM_BLOCK, + DXGI_FORMAT_BC5_SNORM,// VK_FORMAT_BC5_SNORM_BLOCK, + DXGI_FORMAT_BC6H_UF16,// VK_FORMAT_BC6H_UFLOAT_BLOCK, + DXGI_FORMAT_BC6H_SF16,// VK_FORMAT_BC6H_SFLOAT_BLOCK, + DXGI_FORMAT_BC7_UNORM,// VK_FORMAT_BC7_UNORM_BLOCK, + DXGI_FORMAT_BC7_UNORM_SRGB,// VK_FORMAT_BC7_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_EAC_R11_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_EAC_R11_SNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x12_SRGB_BLOCK, //184 + + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, //1000054000 + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, + + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK, //1000066000 + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK, + + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8B8G8R8_422_UNORM, //1000156000 + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B8G8R8G8_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R10X6_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R12X4_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16B16G16R16_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_B16G16R16G16_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A4R4G4B4_UNORM_PACK16, + DXGI_FORMAT_UNKNOWN, // VK_FORMAT_A4B4G4R4_UNORM_PACK16, +}; + + +//----------------------------------------------------------------------------- +DXGI_FORMAT TextureFormatToDx(TextureFormat f) +//----------------------------------------------------------------------------- +{ + return sTextureFormatToDx[uint32_t(f)]; +} + diff --git a/framework/code/texture/dx12/texture.hpp b/framework/code/texture/dx12/texture.hpp new file mode 100644 index 0000000..6225188 --- /dev/null +++ b/framework/code/texture/dx12/texture.hpp @@ -0,0 +1,78 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "../texture.hpp" +#include "memory/dx12/memoryMapped.hpp" +#include "dx12/dx12.hpp" ///TODO: can this be removed? + +// Forward declarations +class Dx12; +enum DXGI_FORMAT; +enum D3D12_TEXTURE_LAYOUT; + +using TextureDx12 = Texture; +using SamplerDx12 = Sampler; +template class MemoryAllocatedBuffer; + + +/// @brief Template specialization of texture container for Dx12 graphics api. +template<> +class Texture final : public TextureBase +{ +public: + Texture() noexcept; + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + Texture(Texture&&) noexcept; + Texture& operator=(Texture&&) noexcept; + ~Texture() noexcept; + + /// @brief Construct Texture from a pre-existing image resource. + /// @param resourceBuffer - ownership passed to this Texture. + /// @param sampler - ownership passed to this Texture. + Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, TextureFormat format, D3D12_TEXTURE_LAYOUT textureLayout, MemoryAllocatedBuffer resourceBuffer /*, const Sampler& sampler*/) noexcept; + + void Release(GraphicsApiBase* pGfxApi) override; + + bool IsEmpty() const; + + auto* GetResource() const { return MemoryBuffer.GetResource(); } + const auto& GetResourceViewDesc() const { return ResourceViewDesc; } + +public: + uint32_t Width = 0; + uint32_t Height = 0; + uint32_t Depth = 0; + uint32_t MipLevels = 0; + uint32_t FirstMip = 0; + TextureFormat Format = TextureFormat::UNDEFINED; + D3D12_TEXTURE_LAYOUT TextureLayout{}; + D3D12_SHADER_RESOURCE_VIEW_DESC ResourceViewDesc{}; + + MemoryAllocatedBuffer MemoryBuffer; + //Sampler Sampler; +}; + +template<> +Texture CreateTextureObject(Dx12&, const CreateTexObjectInfo&, MemoryPool*); + +/// Template specialization for Dx12 CreateTextureFromBuffer +template<> +Texture CreateTextureFromBuffer(Dx12&, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName); + +/// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) +/// Template specialization for Dx12 CreateTextureFromBuffer +template<> +Texture CreateTextureObjectView(Dx12&, const Texture& original, TextureFormat viewFormat ); + +/// Template specialization for Dx12 CreateTextureFromBuffer +template<> +void ReleaseTexture(Dx12&, Texture* pTexture); + + +DXGI_FORMAT TextureFormatToDx(TextureFormat f); diff --git a/framework/code/texture/dx12/textureManager.cpp b/framework/code/texture/dx12/textureManager.cpp new file mode 100644 index 0000000..0953e33 --- /dev/null +++ b/framework/code/texture/dx12/textureManager.cpp @@ -0,0 +1,196 @@ +//============================================================================= +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#include "textureManager.hpp" +#include "sampler.hpp" +#include "texture.hpp" +#include "loaderKtx.hpp" +#include "../loaderPpm.hpp" +#include "dx12/dx12.hpp" + + +TextureManager::TextureManager(tGfxApi& rGfxApi, AssetManager& rAssetManager) noexcept : TextureManagerBase(rAssetManager), m_GfxApi(rGfxApi) +{ +} + +bool TextureManager::Initialize() +{ + m_Loader = std::make_unique>( static_cast(m_GfxApi) ); + return TextureManagerBase::Initialize(); +} + +void TextureManager::Release() +{ + for (auto& [key, texture] : m_LoadedTextures) + { + texture.Release(&m_GfxApi); + } + m_LoadedTextures.clear(); + m_Loader->Release(); + TextureManagerBase::Release(); +} + +const TextureBase* TextureManager::GetTexture(const std::string& textureSlotName) const +{ + auto iter = m_LoadedTextures.find(textureSlotName); + if (iter == m_LoadedTextures.end()) + return nullptr; + return &iter->second; +} + +const TextureBase* TextureManager::GetOrLoadTexture_(const std::string& textureSlotName, const std::string& filename, const SamplerBase& sampler) +{ + const TextureBase* pTexture = GetTexture(textureSlotName); + if (!pTexture) + { + const SamplerDx12& samplerDx12 = static_cast(sampler); + auto loadedTexture = GetLoader()->LoadKtx(m_GfxApi, m_AssetManager, filename.c_str(), samplerDx12); + if (!loadedTexture.IsEmpty()) + { + auto insertedIt = m_LoadedTextures.insert({ textureSlotName, std::move(loadedTexture) }); + pTexture = &(insertedIt.first->second); + } + } + + return pTexture; +} + +typedef std::queue> tLoadedFileQueue; +struct BatchLoadThreadParams { + AssetManager& assetManager; + std::mutex& loadedFileQueueMutex; + tLoadedFileQueue& loadedFileQueue; + Semaphore& dataReadySema; + const std::string& filename; + size_t slotIndex; +}; + +void TextureManager::BatchLoad(const std::span> slotAndFileNames, const SamplerBase& defaultSampler) +{ + std::mutex loadedFileQueueMutex; + tLoadedFileQueue loadedFileQueue; + Semaphore dataReadySema{ 0 }; + Semaphore finishedSema{ 0 }; + + // Setup the output textures + std::vector textures; + textures.resize(slotAndFileNames.size()); + + // We have one worker job just grabbing loaded textures and transfering them to vulkan (gpu memory). + struct TransferWorkerParams { + std::mutex& loadedFileQueueMutex; + tLoadedFileQueue& loadedFileQueue; + Semaphore& dataReadySema; + Semaphore& finishedSema; + const SamplerDx12& defaultSampler; + std::vector& textures; + } transferWorkerParams{ loadedFileQueueMutex, loadedFileQueue, dataReadySema, finishedSema, apiCast(defaultSampler), textures }; + + m_LoadingThreadWorker.DoWork2([](TextureManagerDx12* pThis, TransferWorkerParams params) + { + size_t texturesRemaining = params.textures.size(); + while (texturesRemaining > 0) + { + TextureKtxFileWrapper ktxData; + size_t slotIndex; + params.dataReadySema.Wait(); + { + std::lock_guard lock(params.loadedFileQueueMutex); + auto&& loadedData = params.loadedFileQueue.front(); + ktxData = std::move(loadedData.second); + slotIndex = loadedData.first; + params.loadedFileQueue.pop(); + } + if (ktxData) + params.textures[slotIndex] = pThis->GetLoader()->LoadKtx(pThis->m_GfxApi, ktxData, params.defaultSampler); + --texturesRemaining; + } + params.finishedSema.Post(); + + }, this, transferWorkerParams); + + size_t currentSlotIndex = 0; + for (const auto& [textureSlotName, filename] : slotAndFileNames) + { + auto iter = m_LoadedTextures.find(textureSlotName); + if (iter == m_LoadedTextures.end()) + { + BatchLoadThreadParams params{ m_AssetManager, loadedFileQueueMutex, loadedFileQueue, dataReadySema, filename, currentSlotIndex }; + + m_LoadingThreadWorker.DoWork2([](TextureManagerDx12* pThis, BatchLoadThreadParams params) + { + auto ktxData = pThis->m_Loader->LoadFile(params.assetManager, params.filename.c_str()); + auto* pKtxLoader = apiCast( pThis->GetLoader() ); + ktxData = pKtxLoader->Transcode(std::move(ktxData)); + { + std::lock_guard lock(params.loadedFileQueueMutex); + params.loadedFileQueue.emplace(std::pair{ params.slotIndex, std::move(ktxData) }); + } + params.dataReadySema.Post(); + }, this, params); + + ++currentSlotIndex; + } + else + { + // Signal the transfer thread, but dont give it any data (it finishes when it has recieved a signal for every texture being BatchLoaded) + dataReadySema.Post(); + } + } + + finishedSema.Wait(); + + // Transfer all the loaded textures to m_LoadedTextures + for (size_t i = 0; i < textures.size(); ++i) + { + if (!textures[i].IsEmpty()) + { + m_LoadedTextures.emplace(std::pair{ slotAndFileNames[i].first/*slot*/, std::move(textures[i])}); + } + } +} + +const TextureBase* TextureManager::CreateTextureObject( const CreateTexObjectInfo& texInfo ) /*override*/ +{ + auto texture = ::CreateTextureObject( m_GfxApi, texInfo ); + + assert( texInfo.pName != nullptr && texInfo.pName[0] != '\0' ); // must have a valid name + auto it = m_LoadedTextures.try_emplace( texInfo.pName, std::move( texture ) ); + if (!it.second) + { + assert( 0 && "CreateTextureObjectView duplicate texture name, must be unique (or use ::CreateTextureObject)" ); + return nullptr; + } + else + return &it.first->second; +} + +const TextureBase* TextureManager::CreateTextureFromBuffer( const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, std::string name ) /*override*/ +{ + auto texture = ::CreateTextureFromBuffer( m_GfxApi, pData, DataSize, Width, Height, Depth, Format, SamplerMode, Filter, name.c_str() ); + + assert( name.empty() ); // must have a valid name + auto it = m_LoadedTextures.try_emplace( name, std::move( texture ) ); + if (!it.second) + { + assert( 0 && "CreateTextureObjectView duplicate texture name, must be unique (or use ::CreateTextureObjectView)" ); + return nullptr; + } + else + return &it.first->second; +} + +const TextureBase* TextureManager::CreateTextureObjectView( const TextureBase& original, TextureFormat viewFormat, std::string name ) +{ + assert( 0 && "TextureManager::CreateTextureObjectView needs implementing!" ); + return {}; +} + +const SamplerBase* const TextureManager::GetSampler( SamplerAddressMode ) const +{ + assert( 0 && "needs to be implemented" ); + return nullptr; +} diff --git a/framework/code/texture/dx12/textureManager.hpp b/framework/code/texture/dx12/textureManager.hpp new file mode 100644 index 0000000..13ebcfb --- /dev/null +++ b/framework/code/texture/dx12/textureManager.hpp @@ -0,0 +1,60 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "../textureManager.hpp" +#include + + +// Forward declarations +class Dx12; +template class Texture; +template class TextureKtx; +template class TextureManager; +using TextureManagerDx12 = TextureManager; + + +/// @brief Templated implementation of TextureManagerBase that handles ktx file loading for Dx12 graphics api. +/// @tparam T_GFXAPI +template<> +class TextureManager final : public TextureManagerBase +{ +public: + using tGfxApi = Dx12; + using Texture = Texture; + using Sampler = Sampler; + + TextureManager(tGfxApi& rGfxApi, AssetManager& rAssetManager) noexcept; + + bool Initialize(); + void Release(); + + /// @brief Find a texture (by slot name) that may be already loaded + /// @param textureSlotName name to look for + /// @return pointer to already loaded texture, or null + const TextureBase* GetTexture( const std::string& textureSlotName ) const override; + + /// Create texture (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. + /// Implements the base class virtual function. + const TextureBase* CreateTextureObject( const CreateTexObjectInfo& texInfo ) override; + + /// Create texture from a block of texture data in memory (with correct format, span etc). + const TextureBase* CreateTextureFromBuffer( const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, std::string name ) override; + + /// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) + const TextureBase* CreateTextureObjectView( const TextureBase& original, TextureFormat viewFormat, std::string name ) override; + + const SamplerBase* const GetSampler( SamplerAddressMode ) const override; + +protected: + const TextureBase* GetOrLoadTexture_( const std::string& textureSlotName, const std::string& filename, const SamplerBase& sampler ) override; + void BatchLoad( const std::span>, const SamplerBase& defaultSampler ) override; + +private: + std::map m_LoadedTextures; + tGfxApi& m_GfxApi; +}; diff --git a/framework/code/texture/loaderKtx.cpp b/framework/code/texture/loaderKtx.cpp index c1bc362..62a08e2 100644 --- a/framework/code/texture/loaderKtx.cpp +++ b/framework/code/texture/loaderKtx.cpp @@ -1,7 +1,6 @@ //============================================================================================================ // -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,6 +8,7 @@ #include // KTX-Software #include #include +#include #include "system/assetManager.hpp" #include "loaderKtx.hpp" #include "../lib/gl_format.h" @@ -40,17 +40,17 @@ void TextureKtxFileWrapper::Release() m_ktxTexture = nullptr; } -TextureKtx::~TextureKtx() noexcept +TextureKtxBase::~TextureKtxBase() noexcept { Release(); } -bool TextureKtx::Initialize() +bool TextureKtxBase::Initialize() { return true; } -TextureKtxFileWrapper TextureKtx::LoadFile(AssetManager& assetManager, const char* const pFileName) const +TextureKtxFileWrapper TextureKtxBase::LoadFile(AssetManager& assetManager, const char* const pFileName) const { std::vector fileData; if (!assetManager.LoadFileIntoMemory(pFileName, fileData)) @@ -60,45 +60,54 @@ TextureKtxFileWrapper TextureKtx::LoadFile(AssetManager& assetManager, const cha } ///HACK: some of our ktx files have gl internal format and gl format set to be the same thing, which is the ktx library doesnt like. - struct KtxHeader { - uint8_t identifier[12]; - uint32_t endianness; - uint32_t glType; - uint32_t glTypeSize; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; - }; - KtxHeader* pHeader = (KtxHeader*)fileData.data(); - - ktx_uint8_t ktx_identifier[] = KTX_IDENTIFIER_REF; - if (fileData.size() >= sizeof(KtxHeader) && memcmp(pHeader->identifier, ktx_identifier, sizeof(ktx_identifier)) == 0) + if (1) { - const auto glFormat = pHeader->glFormat; - const auto glType = pHeader->glType; + struct KtxHeader { + uint8_t identifier[12]; + uint32_t endianness; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + }; + KtxHeader* pHeader = (KtxHeader*)fileData.data(); - // Internal format should be sized but some exporters write it as untyped, see if we can fix that! - if ((glFormat == pHeader->glInternalFormat) && glFormat != 0) + ktx_uint8_t ktx_identifier[] = KTX_IDENTIFIER_REF; + if (fileData.size() >= sizeof( KtxHeader ) && memcmp( pHeader->identifier, ktx_identifier, sizeof( ktx_identifier ) ) == 0) { - if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RGB) - { - pHeader->glInternalFormat = GL_RGB8; - } - else if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RED) + const auto glFormat = pHeader->glFormat; + const auto glType = pHeader->glType; + + // Pure hack! + if (glFormat == 6408 && pHeader->glInternalFormat == 36220 && glType == GL_UNSIGNED_BYTE) { - pHeader->glInternalFormat = GL_R8; + pHeader->glInternalFormat = GL_RGBA8; } - else if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RGBA) + + // Internal format should be sized but some exporters write it as untyped, see if we can fix that! + if ((glFormat == pHeader->glInternalFormat) && glFormat != 0) { - //pHeader->glInternalFormat = GL_SRGB8_ALPHA8_EXT; - pHeader->glInternalFormat = GL_RGBA8; + if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RGB) + { + pHeader->glInternalFormat = GL_RGB8; + } + else if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RED) + { + pHeader->glInternalFormat = GL_R8; + } + else if (glType == GL_UNSIGNED_BYTE && glFormat == GL_RGBA) + { + //pHeader->glInternalFormat = GL_SRGB8_ALPHA8_EXT; + pHeader->glInternalFormat = GL_RGBA8; + } } } } diff --git a/framework/code/texture/loaderKtx.hpp b/framework/code/texture/loaderKtx.hpp index 4e8c210..fe80401 100644 --- a/framework/code/texture/loaderKtx.hpp +++ b/framework/code/texture/loaderKtx.hpp @@ -1,6 +1,6 @@ //============================================================================================================ // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -15,9 +15,9 @@ // Forward declarations struct ktxTexture; class AssetManager; -template class TextureT; -template class TextureKtxT; -template class SamplerT; +template class Texture; +template class TextureKtx; +template class Sampler; /// @brief opaque class holding texture data (in some texture format internal format, not a vulkan texture) class TextureKtxFileWrapper final @@ -33,7 +33,7 @@ class TextureKtxFileWrapper final operator bool() const { return m_ktxTexture != nullptr; } void Release(); protected: - friend class TextureKtx; + friend class TextureKtxBase; auto GetKtxTexture() const { return m_ktxTexture; } private: ktxTexture* m_ktxTexture = nullptr; @@ -42,14 +42,14 @@ class TextureKtxFileWrapper final /// @brief Class to handle loading KTX textures /// Generally applications will want a singleton of this class (or a derived version eg @TextureKtxVulkan). -class TextureKtx +class TextureKtxBase { - TextureKtx(const TextureKtx&) = delete; - TextureKtx& operator=(const TextureKtx&) = delete; + TextureKtxBase(const TextureKtxBase&) = delete; + TextureKtxBase& operator=(const TextureKtxBase&) = delete; public: - template using tApiDerived = TextureKtxT; // make apiCast work! - TextureKtx() = default; - virtual ~TextureKtx() noexcept; + template using tApiDerived = TextureKtx; // make apiCast work! + TextureKtxBase() = default; + virtual ~TextureKtxBase() noexcept; /// @brief Load the given ktx(2) texture TextureKtxFileWrapper LoadFile(AssetManager& assetManager, const char* const pFileName) const; @@ -67,7 +67,10 @@ class TextureKtx /// @param sampler sampler that loaded texture will take OWNERSHIP of /// @returns a &TextureVulkan, will be empty on failure template - TextureT LoadKtx(T_GFXAPI& vulkan, const TextureKtxFileWrapper& textureFile, SamplerT sampler); + Texture LoadKtx(T_GFXAPI& gfxapi, const TextureKtxFileWrapper& textureFile, Sampler sampler) + { + return apiCast(this)->LoadKtx(gfxapi, textureFile, std::move(sampler)); + } /// @brief Load a ktx file and do the necessary upload etc to go from a cpu texture representation to Vulkan format /// WE EXPECT THERE TO BE A SPECIALIZED IMPLEMENTATION OF THIS TEMPLATE for each supported T_GFXAPI. @@ -75,7 +78,10 @@ class TextureKtx /// @param sampler sampler that loaded texture will take OWNERSHIP of /// @returns a &TextureVulkan, will be empty on failure template - TextureT LoadKtx(T_GFXAPI& vulkan, AssetManager& assetManager, const char* const pFileName, SamplerT sampler); + Texture LoadKtx(T_GFXAPI& gfxapi, AssetManager& assetManager, const char* const pFileName, Sampler sampler) + { + return apiCast(this)->LoadKtx(gfxapi, assetManager, pFileName, std::move(sampler)); + } protected: /// Helper to get the ktx texture pointer (for derived classes) @@ -85,13 +91,13 @@ class TextureKtx /// @brief Templated (by graphics api) ktx loader class, expected to be specialized. /// @tparam T_GFXAPI template -class TextureKtxT : public TextureKtx +class TextureKtx : public TextureKtxBase { - TextureKtxT(const TextureKtxT&) = delete; - TextureKtxT& operator=(const TextureKtxT&) = delete; + TextureKtx(const TextureKtx&) = delete; + TextureKtx& operator=(const TextureKtx&) = delete; public: - TextureKtxT(T_GFXAPI& rGfxApi) noexcept = delete; // class expected to be specialized - ~TextureKtxT() noexcept override = delete; // class expected to be specialized + TextureKtx(T_GFXAPI& rGfxApi) noexcept = delete; // class expected to be specialized + ~TextureKtx() noexcept override = delete; // class expected to be specialized - static_assert( sizeof( TextureKtxT ) != sizeof( TextureKtx ) ); // Ensure this class template is specialized (and not used as-is) + static_assert( sizeof( TextureKtx ) != sizeof( TextureKtxBase ) ); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/texture/loaderPpm.cpp b/framework/code/texture/loaderPpm.cpp index d6e816f..dd15267 100644 --- a/framework/code/texture/loaderPpm.cpp +++ b/framework/code/texture/loaderPpm.cpp @@ -1,7 +1,6 @@ //============================================================================================================ // -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -36,17 +35,17 @@ void TexturePpmFileWrapper::Release() //m_ppmTexture = nullptr; } -TexturePpm::~TexturePpm() noexcept +TexturePpmBase::~TexturePpmBase() noexcept { Release(); } -bool TexturePpm::Initialize() +bool TexturePpmBase::Initialize() { return true; } -TexturePpmFileWrapper TexturePpm::LoadFile( AssetManager& assetManager, const char* const pFileName ) const +TexturePpmFileWrapper TexturePpmBase::LoadFile( AssetManager& assetManager, const char* const pFileName ) const { std::vector fileData; if (!assetManager.LoadFileIntoMemory( pFileName, fileData )) diff --git a/framework/code/texture/loaderPpm.hpp b/framework/code/texture/loaderPpm.hpp index fd694e6..80f156d 100644 --- a/framework/code/texture/loaderPpm.hpp +++ b/framework/code/texture/loaderPpm.hpp @@ -1,7 +1,6 @@ //============================================================================================================ // -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -33,7 +32,7 @@ class TexturePpmFileWrapper final operator bool() const { return !m_Data.empty(); } void Release(); protected: - friend class TexturePpm; + friend class TexturePpmBase; private: uint32_t m_Width = 0; uint32_t m_Height = 0; @@ -44,13 +43,13 @@ class TexturePpmFileWrapper final /// @brief Class to handle loading PPM textures /// Generally applications will want a singleton of this class. -class TexturePpm +class TexturePpmBase { - TexturePpm(const TexturePpm&) = delete; - TexturePpm& operator=(const TexturePpm&) = delete; + TexturePpmBase(const TexturePpmBase&) = delete; + TexturePpmBase& operator=(const TexturePpmBase&) = delete; public: - TexturePpm() = default; - virtual ~TexturePpm() noexcept; + TexturePpmBase() = default; + virtual ~TexturePpmBase() noexcept; /// @brief Load the given ppm(2) texture TexturePpmFileWrapper LoadFile(AssetManager& assetManager, const char* const pFileName) const; @@ -63,14 +62,14 @@ class TexturePpm virtual void Release() {} template - TextureT LoadPpm( T_GFXAPI& gfxApi, AssetManager& assetManager, const char* const pFileName, const SamplerT& sampler ); + Texture LoadPpm( T_GFXAPI& gfxApi, AssetManager& assetManager, const char* const pFileName, const Sampler& sampler ); template - TextureT LoadPpm( T_GFXAPI& gfxApi, const TexturePpmFileWrapper& ppmData, const SamplerT& sampler ); + Texture LoadPpm( T_GFXAPI& gfxApi, const TexturePpmFileWrapper& ppmData, const Sampler& sampler ); }; template -TextureT TexturePpm::LoadPpm( T_GFXAPI& gfxApi, AssetManager& assetManager, const char* const pFileName, const SamplerT& sampler ) +Texture TexturePpmBase::LoadPpm( T_GFXAPI& gfxApi, AssetManager& assetManager, const char* const pFileName, const Sampler& sampler ) { auto ppmData = LoadFile( assetManager, pFileName ); if (!ppmData) @@ -79,7 +78,7 @@ TextureT TexturePpm::LoadPpm( T_GFXAPI& gfxApi, AssetManager& assetMan } template -TextureT TexturePpm::LoadPpm( T_GFXAPI& gfxApi, const TexturePpmFileWrapper& ppmData, const SamplerT& sampler ) +Texture TexturePpmBase::LoadPpm( T_GFXAPI& gfxApi, const TexturePpmFileWrapper& ppmData, const Sampler& sampler ) { return ::CreateTextureFromBuffer( gfxApi, ppmData.m_Data.data(), ppmData.m_Data.size(), ppmData.m_Width, ppmData.m_Height, ppmData.m_Depth, ppmData.m_Format, SamplerAddressMode::Repeat, SamplerFilter::Linear ); } @@ -88,13 +87,13 @@ TextureT TexturePpm::LoadPpm( T_GFXAPI& gfxApi, const TexturePpmFileWr /// @brief Templated (by graphics api) ppm loader class, expected to be specialized. /// @tparam T_GFXAPI template -class TexturePpmT : public TexturePpm +class TexturePpm : public TexturePpmBase { - TexturePpmT( const TexturePpmT& ) = delete; - TexturePpmT& operator=( const TexturePpmT& ) = delete; + TexturePpm( const TexturePpm& ) = delete; + TexturePpm& operator=( const TexturePpm& ) = delete; public: - TexturePpmT( T_GFXAPI& rGfxApi ) noexcept = delete; // class expected to be specialized - ~TexturePpmT() noexcept override = delete; // class expected to be specialized + TexturePpm( T_GFXAPI& rGfxApi ) noexcept = delete; // class expected to be specialized + ~TexturePpm() noexcept override = delete; // class expected to be specialized - static_assert( sizeof( TexturePpmT ) != sizeof( TexturePpmT ) ); // Ensure this class template is specialized (and not used as-is) + static_assert( sizeof( TexturePpm ) != sizeof( TexturePpm ) ); // Ensure this class template is specialized (and not used as-is) }; diff --git a/framework/code/texture/sampler.cpp b/framework/code/texture/sampler.cpp new file mode 100644 index 0000000..7d0a585 --- /dev/null +++ b/framework/code/texture/sampler.cpp @@ -0,0 +1,75 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include +#include +#include +#include +#include +#include "texture.hpp" +#include "nlohmann/json.hpp" + +using Json = nlohmann::json; + + +const static std::map cSamplerAddressModeByName{ + {"Undefined", SamplerAddressMode::Undefined}, + {"Repeat", SamplerAddressMode::Repeat}, + {"MirroredRepeat", SamplerAddressMode::MirroredRepeat}, + {"ClampEdge", SamplerAddressMode::ClampEdge}, + {"ClampBorder", SamplerAddressMode::ClampBorder}, + {"MirroredClampEdge", SamplerAddressMode::MirroredClampEdge} +}; + +const static std::map cSamplerFilterByName{ + {"Undefined", SamplerFilter::Undefined}, + {"Nearest", SamplerFilter::Nearest}, + {"Linear", SamplerFilter::Linear} +}; + +static void from_json( const Json& j, SamplerAddressMode& op ) { + const auto foundIt = cSamplerAddressModeByName.find( j ); + if (foundIt != cSamplerAddressModeByName.end()) { + op = foundIt->second; + } + else { + throw std::invalid_argument( "Unknown SamplerAddressMode" ); + } +} + +static void from_json( const Json& j, SamplerFilter& op ) { + const auto foundIt = cSamplerFilterByName.find( j ); + if (foundIt != cSamplerFilterByName.end()) { + op = foundIt->second; + } + else { + throw std::invalid_argument( "Unknown SamplerFilter" ); + } +} + +void from_json( const Json& j, CreateSamplerObjectInfo& info ) { + auto it = j.find( "Mode" ); + if (it != j.end()) it->get_to( info.Mode ); + + it = j.find( "Filter" ); + if (it != j.end()) it->get_to( info.Filter ); + + it = j.find( "MipFilter" ); + if (it != j.end()) it->get_to( info.MipFilter ); + + it = j.find( "MipBias" ); + if (it != j.end()) it->get_to( info.MipBias); + + it = j.find( "Anisotropy" ); + if (it != j.end()) it->get_to( info.Anisotropy ); + + it = j.find( "MinLod" ); + if (it != j.end()) it->get_to( info.MinLod ); + + it = j.find( "MaxLod" ); + if (it != j.end()) it->get_to( info.MaxLod ); +} diff --git a/framework/code/texture/sampler.hpp b/framework/code/texture/sampler.hpp new file mode 100644 index 0000000..e9d1f0b --- /dev/null +++ b/framework/code/texture/sampler.hpp @@ -0,0 +1,124 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include +#include +#include "json/include/nlohmann/json_fwd.hpp" + +// Forward declarations +class GraphicsApiBase; +template class Texture; +template class Image; +template class ImageView; +template class Sampler; +using Json = nlohmann::json; + +/// @brief Base class for a sampler object. +/// Is subclassed for each graphics API. +class SamplerBase +{ + SamplerBase(const SamplerBase&) = delete; + SamplerBase& operator=(const SamplerBase&) = delete; +public: + template using tApiDerived = Sampler; // make apiCast work! +protected: + SamplerBase() noexcept {} +}; + + +/// @brief Template for sampler to be specialized by Vulkan graphics api. +template +class Sampler final : public SamplerBase +{ + Sampler(const Sampler&) = delete; + Sampler& operator=(const Sampler&) = delete; +public: + Sampler() noexcept = delete; // template class expected to be specialized + Sampler(Sampler&&) noexcept = delete; // template class expected to be specialized + Sampler& operator=(Sampler&&) noexcept = delete;// template class expected to be specialized + + static_assert(sizeof(Sampler) != sizeof(SamplerBase)); // Ensure this class template is specialized (and not used as-is) +}; + + +enum class SamplerAddressMode { + Undefined = 0, + Repeat = 1, + MirroredRepeat = 2, + ClampEdge = 3, + ClampBorder = 4, + MirroredClampEdge = 5, + End +}; + + +enum class SamplerFilter { + Undefined = 0, + Nearest = 1, + Linear = 2, + Anisotropic = 3 //dx12 +}; + + +enum class SamplerBorderColor { + TransparentBlackFloat = 0, + TransparentBlackInt = 1, + OpaqueBlackFloat = 2, + OpaqueBlackInt = 3, + OpaqueWhiteFloat = 4, + OpaqueWhiteInt = 5 +}; + +/// Parameters for CreateTextureObject +struct CreateSamplerObjectInfo +{ + SamplerAddressMode Mode = SamplerAddressMode::Repeat; + SamplerFilter Filter = SamplerFilter::Linear; + SamplerFilter MipFilter = SamplerFilter::Linear; + SamplerBorderColor BorderColor = SamplerBorderColor::TransparentBlackFloat; + bool UnnormalizedCoordinates = false; + float MipBias = 0.0f; + float MinLod = 0.0f; + float MaxLod = FLT_MAX; + float Anisotropy = 4.0f; +}; + +/// Create a texture sampler (with some commonly used parameters) +template +Sampler CreateSampler( T_GFXAPI& gfxApi, SamplerAddressMode SamplerMode, SamplerFilter FilterMode, SamplerBorderColor BorderColor, float MipBias ) +{ + CreateSamplerObjectInfo createInfo{}; + createInfo.Mode = SamplerMode; + createInfo.Filter = FilterMode; + createInfo.MipFilter = FilterMode, + createInfo.BorderColor = BorderColor; + createInfo.MipBias = MipBias; + return CreateSampler( gfxApi, createInfo ); +} + +/// Create a texture sampler (must be specialized for the graphics api) +/// Must be specialized for the graphics api - this fallback will assert if called by application code. +template +Sampler CreateSampler( T_GFXAPI& gfxApi, const CreateSamplerObjectInfo&) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//sampler.hpp\""); + assert( 0 && "Expecting CreateSampler (per graphics api) to be used" ); + return {}; +} + +/// Release a texture sampler. +template +void ReleaseSampler( T_GFXAPI& gfxApi, Sampler* pSampler ) +{ + if (!pSampler) + return; + *pSampler = Sampler{}; // destroy and clear +} + +void from_json( const Json& j, CreateSamplerObjectInfo& info ); diff --git a/framework/code/texture/texture.hpp b/framework/code/texture/texture.hpp index 5e481a2..482cbcb 100644 --- a/framework/code/texture/texture.hpp +++ b/framework/code/texture/texture.hpp @@ -1,6 +1,6 @@ //============================================================================= // -// Copyright (c) 2023 QUALCOMM Technologies Inc. +// Copyright (c) 2022 QUALCOMM Technologies Inc. // All Rights Reserved. // //============================================================================== @@ -9,162 +9,108 @@ #include #include #include "textureFormat.hpp" +#include "sampler.hpp" // Forward declarations class GraphicsApiBase; -template class TextureT; -template class ImageT; -template class ImageViewT; -template class SamplerT; +template class Texture; +template class Image; +template class ImageView; +template class MemoryPool; +template class Sampler; /// @brief Base class for a texture object (image with a sampler). /// Is subclassed for each graphics API. -class Texture +class TextureBase { - Texture(const Texture&) = delete; - Texture& operator=(const Texture&) = delete; + TextureBase(const TextureBase&) = delete; + TextureBase& operator=(const TextureBase&) = delete; public: - template using tApiDerived = TextureT; // make apiCast work! + template using tApiDerived = Texture; // make apiCast work! - Texture() noexcept {} - virtual ~Texture() noexcept = 0; + TextureBase() noexcept {} + virtual ~TextureBase() noexcept = 0; virtual void Release(GraphicsApiBase* pApi) = 0; }; -inline Texture::~Texture() noexcept {} +inline TextureBase::~TextureBase() noexcept {} /// @brief Texture container for a (templated) graphics api. /// Owns memory and sampler etc associated with a single texture. /// Template is expected to be specialized for the graphics api (Vulkan, DirectX etc) template -class TextureT final : public Texture +class Texture final : public TextureBase { - TextureT(const TextureT&) = delete; - TextureT& operator=(const TextureT&) = delete; + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; public: - TextureT() noexcept = delete; // template class expected to be specialized - TextureT(TextureT&&) noexcept = delete; // template class expected to be specialized - TextureT& operator=(TextureT&&) noexcept = delete;// template class expected to be specialized + Texture() noexcept = delete; // template class expected to be specialized + Texture(Texture&&) noexcept = delete; // template class expected to be specialized + Texture& operator=(Texture&&) noexcept = delete;// template class expected to be specialized bool IsEmpty() const { return true; } protected: - static_assert(sizeof(TextureT) != sizeof(Texture)); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof(Texture) != sizeof(TextureBase)); // Ensure this class template is specialized (and not used as-is) }; /// @brief Base class for a image object. /// Is subclassed for each graphics API. -class Image +class ImageBase { - Image( const Image& ) = delete; - Image& operator=( const Image& ) = delete; + ImageBase( const ImageBase& ) = delete; + ImageBase& operator=( const ImageBase& ) = delete; public: - template using tApiDerived = ImageT; // make apiCast work! + template using tApiDerived = Image; // make apiCast work! protected: - Image() noexcept {} + ImageBase() noexcept {} }; /// @brief Template for image to be specialized by graphics api. template -class ImageT final : public Image +class Image final : public ImageBase { - ImageT( const ImageT& ) = delete; - ImageT& operator=( const ImageT& ) = delete; + Image( const Image& ) = delete; + Image& operator=( const Image& ) = delete; public: - ImageT() noexcept = delete; // template class expected to be specialized - ImageT( ImageT&& ) noexcept = delete; // template class expected to be specialized - ImageT& operator=( ImageT&& ) noexcept = delete; // template class expected to be specialized + Image() noexcept = delete; // template class expected to be specialized + Image( Image&& ) noexcept = delete; // template class expected to be specialized + Image& operator=( Image&& ) noexcept = delete; // template class expected to be specialized - static_assert(sizeof( ImageT ) != sizeof( Image )); // Ensure this class template is specialized (and not used as-is) + static_assert(sizeof( Image ) != sizeof( ImageBase )); // Ensure this class template is specialized (and not used as-is) }; /// @brief Base class for a image view object. /// Is subclassed for each graphics API. -class ImageView +class ImageViewBase { - ImageView(const ImageView&) = delete; - ImageView& operator=(const ImageView&) = delete; + ImageViewBase(const ImageViewBase&) = delete; + ImageViewBase& operator=(const ImageViewBase&) = delete; public: - template using tApiDerived = ImageViewT; // make apiCast work! + template using tApiDerived = ImageView; // make apiCast work! protected: - ImageView() noexcept {} + ImageViewBase() noexcept {} }; /// @brief Template for image view to be specialized by graphics api. template -class ImageViewT final : public ImageView +class ImageView final : public ImageViewBase { - ImageViewT(const ImageViewT&) = delete; - ImageViewT& operator=(const ImageViewT&) = delete; + ImageView(const ImageView&) = delete; + ImageView& operator=(const ImageView&) = delete; public: - ImageViewT() noexcept = delete; // template class expected to be specialized - ImageViewT( ImageViewT&&) noexcept = delete; // template class expected to be specialized - ImageViewT& operator=( ImageViewT&&) noexcept = delete; // template class expected to be specialized + ImageView() noexcept = delete; // template class expected to be specialized + ImageView( ImageView&&) noexcept = delete; // template class expected to be specialized + ImageView& operator=( ImageView&&) noexcept = delete; // template class expected to be specialized - static_assert(sizeof(ImageViewT) != sizeof(ImageView)); // Ensure this class template is specialized (and not used as-is) -}; - - -/// @brief Base class for a sampler object. -/// Is subclassed for each graphics API. -class Sampler -{ - Sampler(const Sampler&) = delete; - Sampler& operator=(const Sampler&) = delete; -public: - template using tApiDerived = SamplerT; // make apiCast work! -protected: - Sampler() noexcept {} -}; - - -/// @brief Template for sampler to be specialized by Vulkan graphics api. -template -class SamplerT final : public Sampler -{ - SamplerT(const SamplerT&) = delete; - SamplerT& operator=(const SamplerT&) = delete; -public: - SamplerT() noexcept = delete; // template class expected to be specialized - SamplerT(SamplerT&&) noexcept = delete; // template class expected to be specialized - SamplerT& operator=(SamplerT&&) noexcept = delete;// template class expected to be specialized - - static_assert(sizeof(SamplerT) != sizeof(Sampler)); // Ensure this class template is specialized (and not used as-is) -}; - - -enum class SamplerAddressMode { - Undefined = 0, - Repeat = 1, - MirroredRepeat = 2, - ClampEdge = 3, - ClampBorder = 4, - MirroredClampEdge = 5, - End -}; - - -enum class SamplerFilter { - Undefined = 0, - Nearest = 1, - Linear = 2 -}; - - -enum class SamplerBorderColor { - TransparentBlackFloat = 0, - TransparentBlackInt = 1, - OpaqueBlackFloat = 2, - OpaqueBlackInt = 3, - OpaqueWhiteFloat = 4, - OpaqueWhiteInt = 5 + static_assert(sizeof(ImageView) != sizeof(ImageViewBase)); // Ensure this class template is specialized (and not used as-is) }; @@ -176,7 +122,10 @@ enum TEXTURE_TYPE TT_RENDER_TARGET_WITH_STORAGE, TT_RENDER_TARGET_TRANSFERSRC, TT_RENDER_TARGET_SUBPASS, + TT_RENDER_TARGET_LOCAL_READ, + TT_RENDER_TARGET_LOCAL_READ_TRANSIENT, TT_DEPTH_TARGET, + TT_DEPTH_TARGET_LOCAL_READ, TT_COMPUTE_TARGET, TT_COMPUTE_STORAGE, TT_CPU_UPDATE, @@ -218,39 +167,25 @@ struct CreateTexObjectInfo TEXTURE_TYPE TexType = TEXTURE_TYPE::TT_NORMAL; TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None; const char* pName = nullptr; - uint32_t Msaa = 1; ///< number of msaa samples per pixel (ie 1, 2, 4, etc). Default to 1 (no msaa) + Msaa Msaa = Msaa::Samples1; ///< number of msaa samples per pixel (ie 1, 2, 4, etc). Default to 1 (no msaa) SamplerFilter FilterMode = SamplerFilter::Undefined; //default to picking from a default dependant on the texture format SamplerAddressMode SamplerMode = SamplerAddressMode::Undefined; //default to picking from a default dependant on the texture type bool UnNormalizedCoordinates = false; }; -/// Parameters for CreateTextureObject -struct CreateSamplerObjectInfo -{ - SamplerAddressMode Mode = SamplerAddressMode::Repeat; - SamplerFilter Filter = SamplerFilter::Linear; - SamplerFilter MipFilter = SamplerFilter::Linear; - SamplerBorderColor BorderColor = SamplerBorderColor::TransparentBlackFloat; - bool UnnormalizedCoordinates = false; - float MipBias = 0.0f; - float MinLod = 0.0f; - float MaxLod = FLT_MAX; - float Anisotropy = 4.0f; -}; - - /// Create texture (generally for render target usage) template -TextureT CreateTextureObject(T_GFXAPI& gfxApi, const CreateTexObjectInfo& texInfo) +Texture CreateTextureObject( T_GFXAPI&, const CreateTexObjectInfo&, MemoryPool* pPool = nullptr) { - assert(0 && "Expecting CreateTextureObject (per graphics api) to be used"); + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); + assert( 0 && "Expecting CreateTextureObject (per graphics api) to be used" ); return {}; } /// Create texture (generally for render target usage) template -TextureT CreateTextureObject(T_GFXAPI& gfxApi, uint32_t uiWidth, uint32_t uiHeight, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, uint32_t Msaa = 1, TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None) +Texture CreateTextureObject(T_GFXAPI& gfxApi, uint32_t uiWidth, uint32_t uiHeight, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, Msaa Msaa = Msaa::Samples1, TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None) { CreateTexObjectInfo createInfo{}; createInfo.uiWidth = uiWidth; @@ -265,47 +200,67 @@ TextureT CreateTextureObject(T_GFXAPI& gfxApi, uint32_t uiWidth, uint3 /// Create texture (unique_ptr) (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. template -std::unique_ptr CreateTextureObject(GraphicsApiBase& gfxApi, const CreateTexObjectInfo& texInfo) +std::unique_ptr CreateTextureObject(GraphicsApiBase& gfxApi, const CreateTexObjectInfo& texInfo) { - auto pTexture = std::make_unique>(); + auto pTexture = std::make_unique>(); *pTexture = std::move(CreateTextureObject(static_cast(gfxApi), texInfo)); return pTexture; } /// Create texture from a memory buffer. template -TextureT CreateTextureFromBuffer( T_GFXAPI& gfxApi, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName = nullptr ) +Texture CreateTextureFromBuffer( T_GFXAPI& gfxApi, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName = nullptr ) { + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); assert( 0 && "Expecting CreateTextureFromBuffer (per graphics api) to be used" ); return {}; } /// Create texture (unique_ptr) (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. template -std::unique_ptr CreateTextureFromBuffer( GraphicsApiBase& gfxApi, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName = nullptr ) +std::unique_ptr CreateTextureFromBuffer( GraphicsApiBase& gfxApi, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName = nullptr ) { - auto pTexture = std::make_unique>(); + auto pTexture = std::make_unique>(); *pTexture = std::move( CreateTextureFromBuffer( static_cast( gfxApi ), pData, DataSize, Width, Height, Depth, Format, SamplerMode, Filter, pName ) ); return pTexture; } +/// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) +template +Texture CreateTextureObjectView( T_GFXAPI&, const Texture& original, TextureFormat viewFormat ) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); + assert( 0 && "Expecting CreateTextureObjectView (per graphics api) to be used" ); + return {}; +} + + /// Release a texture. /// Must be specialized for the graphics api - will give linker error if called by application code. template -void ReleaseTexture(T_GFXAPI& gfxApi, TextureT*); +void ReleaseTexture(T_GFXAPI& gfxApi, Texture*) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); + assert( 0 && "Expecting ReleaseTexture (per graphics api) to be used" ); +} /// Release an image. /// Must be specialized for the graphics api - will give linker error if called by application code. template -void ReleaseImage( T_GFXAPI& gfxApi, ImageT* ); +void ReleaseImage( T_GFXAPI& gfxApi, Image* ) +{ + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); + assert( 0 && "Expecting ReleaseImage (per graphics api) to be used" ); +} /// Create a texture image view /// Must be specialized for the graphics api - will give linker error if called by application code. template -ImageViewT CreateImageView( T_GFXAPI& gfxApi, const ImageT< T_GFXAPI>& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t firstFace, ImageViewType viewType ) +ImageView CreateImageView( T_GFXAPI& gfxApi, const Image< T_GFXAPI>& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t firstFace, ImageViewType viewType ) { + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); assert( 0 && "Expecting CreateImageView (per graphics api) to be used" ); return {}; } @@ -313,32 +268,8 @@ ImageViewT CreateImageView( T_GFXAPI& gfxApi, const ImageT< T_GFXAPI>& /// Release a texture image view. /// Must be specialized for the graphics api - will give linker error if called by application code. template -void ReleaseImageView( T_GFXAPI& gfxApi, ImageViewT* ); - -/// Create a texture sampler (with some commonly used parameters) -template -SamplerT CreateSampler( T_GFXAPI& gfxApi, SamplerAddressMode SamplerMode, SamplerFilter FilterMode, SamplerBorderColor BorderColor, float MipBias ) -{ - CreateSamplerObjectInfo createInfo{}; - createInfo.Mode = SamplerMode; - createInfo.Filter = FilterMode; - createInfo.MipFilter = FilterMode, - createInfo.BorderColor = BorderColor; - createInfo.MipBias = MipBias; - return CreateSampler( gfxApi, createInfo ); -} - -/// Template specialization for Vulkan CreateSampler -/// Create a texture sampler (must be specialized for the graphics api) -/// Must be specialized for the graphics api - will give linker error if called by application code. -template -SamplerT CreateSampler( T_GFXAPI& gfxApi, const CreateSamplerObjectInfo&) +void ReleaseImageView( T_GFXAPI& gfxApi, ImageView* ) { - assert( 0 && "Expecting CreateSampler (per graphics api) to be used" ); - return {}; + static_assert(sizeof( T_GFXAPI ) != sizeof( T_GFXAPI ), "Must use the specialized version of this function. Your are likely missing #include \"texture//texture.hpp\""); + assert( 0 && "Expecting ReleaseImageView (per graphics api) to be used" ); } - -/// Release a texture sampler. -/// Must be specialized for the graphics api - will give linker error if called by application code. -template -void ReleaseSampler(T_GFXAPI& gfxApi, SamplerT*); diff --git a/framework/code/texture/textureFormat.cpp b/framework/code/texture/textureFormat.cpp index 8b29a79..ace39d9 100644 --- a/framework/code/texture/textureFormat.cpp +++ b/framework/code/texture/textureFormat.cpp @@ -1,10 +1,9 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2023 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #include "textureFormat.hpp" @@ -172,3 +171,156 @@ bool FormatIsSrgb( TextureFormat format ) return false; } } + +//----------------------------------------------------------------------------- +size_t FormatBytesPerPixel( TextureFormat format ) +//----------------------------------------------------------------------------- +{ + switch (format) + { + case TextureFormat::R4G4_UNORM_PACK8: + case TextureFormat::R8_UNORM: + case TextureFormat::R8_SNORM: + case TextureFormat::R8_USCALED: + case TextureFormat::R8_SSCALED: + case TextureFormat::R8_UINT: + case TextureFormat::R8_SINT: + case TextureFormat::R8_SRGB: + case TextureFormat::S8_UINT: + return 1; + case TextureFormat::R8G8_UNORM: + case TextureFormat::R8G8_SNORM: + case TextureFormat::R8G8_USCALED: + case TextureFormat::R8G8_SSCALED: + case TextureFormat::R8G8_UINT: + case TextureFormat::R8G8_SINT: + case TextureFormat::R8G8_SRGB: + case TextureFormat::R4G4B4A4_UNORM_PACK16: + case TextureFormat::B4G4R4A4_UNORM_PACK16: + case TextureFormat::R5G6B5_UNORM_PACK16: + case TextureFormat::B5G6R5_UNORM_PACK16: + case TextureFormat::R5G5B5A1_UNORM_PACK16: + case TextureFormat::B5G5R5A1_UNORM_PACK16: + case TextureFormat::A1R5G5B5_UNORM_PACK16: + case TextureFormat::R16_UNORM: + case TextureFormat::R16_SNORM: + case TextureFormat::R16_USCALED: + case TextureFormat::R16_SSCALED: + case TextureFormat::R16_UINT: + case TextureFormat::R16_SINT: + case TextureFormat::R16_SFLOAT: + case TextureFormat::D16_UNORM: + case TextureFormat::R12X4_UNORM_PACK16: + case TextureFormat::A4R4G4B4_UNORM_PACK16: + case TextureFormat::A4B4G4R4_UNORM_PACK16: + return 2; + case TextureFormat::R8G8B8_UNORM: + case TextureFormat::R8G8B8_SNORM: + case TextureFormat::R8G8B8_USCALED: + case TextureFormat::R8G8B8_SSCALED: + case TextureFormat::R8G8B8_UINT: + case TextureFormat::R8G8B8_SINT: + case TextureFormat::R8G8B8_SRGB: + case TextureFormat::B8G8R8_UNORM: + case TextureFormat::B8G8R8_SNORM: + case TextureFormat::B8G8R8_USCALED: + case TextureFormat::B8G8R8_SSCALED: + case TextureFormat::B8G8R8_UINT: + case TextureFormat::B8G8R8_SINT: + case TextureFormat::B8G8R8_SRGB: + return 3; + case TextureFormat::R8G8B8A8_UNORM: + case TextureFormat::R8G8B8A8_SNORM: + case TextureFormat::R8G8B8A8_USCALED: + case TextureFormat::R8G8B8A8_SSCALED: + case TextureFormat::R8G8B8A8_UINT: + case TextureFormat::R8G8B8A8_SINT: + case TextureFormat::R8G8B8A8_SRGB: + case TextureFormat::B8G8R8A8_UNORM: + case TextureFormat::B8G8R8A8_SNORM: + case TextureFormat::B8G8R8A8_USCALED: + case TextureFormat::B8G8R8A8_SSCALED: + case TextureFormat::B8G8R8A8_UINT: + case TextureFormat::B8G8R8A8_SINT: + case TextureFormat::B8G8R8A8_SRGB: + case TextureFormat::A8B8G8R8_UNORM_PACK32: + case TextureFormat::A8B8G8R8_SNORM_PACK32: + case TextureFormat::A8B8G8R8_USCALED_PACK32: + case TextureFormat::A8B8G8R8_SSCALED_PACK32: + case TextureFormat::A8B8G8R8_UINT_PACK32: + case TextureFormat::A8B8G8R8_SINT_PACK32: + case TextureFormat::A8B8G8R8_SRGB_PACK32: + case TextureFormat::A2R10G10B10_UNORM_PACK32: + case TextureFormat::A2R10G10B10_SNORM_PACK32: + case TextureFormat::A2R10G10B10_USCALED_PACK32: + case TextureFormat::A2R10G10B10_SSCALED_PACK32: + case TextureFormat::A2R10G10B10_UINT_PACK32: + case TextureFormat::A2R10G10B10_SINT_PACK32: + case TextureFormat::A2B10G10R10_UNORM_PACK32: + case TextureFormat::A2B10G10R10_SNORM_PACK32: + case TextureFormat::A2B10G10R10_USCALED_PACK32: + case TextureFormat::A2B10G10R10_SSCALED_PACK32: + case TextureFormat::A2B10G10R10_UINT_PACK32: + case TextureFormat::A2B10G10R10_SINT_PACK32: + case TextureFormat::R16G16_UNORM: + case TextureFormat::R16G16_SNORM: + case TextureFormat::R16G16_USCALED: + case TextureFormat::R16G16_SSCALED: + case TextureFormat::R16G16_UINT: + case TextureFormat::R16G16_SINT: + case TextureFormat::R16G16_SFLOAT: + case TextureFormat::R32_UINT: + case TextureFormat::R32_SINT: + case TextureFormat::R32_SFLOAT: + case TextureFormat::B10G11R11_UFLOAT_PACK32: + case TextureFormat::E5B9G9R9_UFLOAT_PACK32: + case TextureFormat::X8_D24_UNORM_PACK32: + case TextureFormat::D32_SFLOAT: + return 4; + case TextureFormat::R16G16B16_UNORM: + case TextureFormat::R16G16B16_SNORM: + case TextureFormat::R16G16B16_USCALED: + case TextureFormat::R16G16B16_SSCALED: + case TextureFormat::R16G16B16_UINT: + case TextureFormat::R16G16B16_SINT: + case TextureFormat::R16G16B16_SFLOAT: + return 6; + case TextureFormat::R32G32B32_UINT: + case TextureFormat::R32G32B32_SINT: + case TextureFormat::R32G32B32_SFLOAT: + return 12; + case TextureFormat::R32G32B32A32_UINT: + case TextureFormat::R32G32B32A32_SINT: + case TextureFormat::R32G32B32A32_SFLOAT: + return 16; + case TextureFormat::R16G16B16A16_UNORM: + case TextureFormat::R16G16B16A16_SNORM: + case TextureFormat::R16G16B16A16_USCALED: + case TextureFormat::R16G16B16A16_SSCALED: + case TextureFormat::R16G16B16A16_UINT: + case TextureFormat::R16G16B16A16_SINT: + case TextureFormat::R16G16B16A16_SFLOAT: + case TextureFormat::R64_UINT: + case TextureFormat::R64_SINT: + case TextureFormat::R64_SFLOAT: + case TextureFormat::R32G32_UINT: + case TextureFormat::R32G32_SINT: + case TextureFormat::R32G32_SFLOAT: + case TextureFormat::R64G64_UINT: + case TextureFormat::R64G64_SINT: + case TextureFormat::R64G64_SFLOAT: + return 8; + return 32; + case TextureFormat::R64G64B64_UINT: + case TextureFormat::R64G64B64_SINT: + case TextureFormat::R64G64B64_SFLOAT: + return 48; + case TextureFormat::R64G64B64A64_UINT: + case TextureFormat::R64G64B64A64_SINT: + case TextureFormat::R64G64B64A64_SFLOAT: + return 64; + case TextureFormat::UNDEFINED: + default: + return 0; + } +} diff --git a/framework/code/texture/textureFormat.hpp b/framework/code/texture/textureFormat.hpp index 3b9f4f8..bacdf85 100644 --- a/framework/code/texture/textureFormat.hpp +++ b/framework/code/texture/textureFormat.hpp @@ -1,11 +1,11 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #pragma once +#include /// @brief Texture formats that may be valid for creating texture surfaces/resources. /// Availability wil depend on the graphics api and hardware/driver. @@ -270,3 +270,17 @@ bool FormatHasDepth(TextureFormat); bool FormatIsCompressed(TextureFormat); /// @return true if the given format is sRGB, return false if linear bool FormatIsSrgb(TextureFormat); +/// @return number of bytes per pixel for this format (or 0 for block/unsupported formats) +size_t FormatBytesPerPixel(TextureFormat); + + +/// Multi sampling flags +enum class Msaa { + Samples1 = 1, + Samples2 = 2, + Samples4 = 4, + Samples8 = 8, + Samples16 = 16, + Samples32 = 32, + Samples64 = 64 +}; diff --git a/framework/code/texture/textureManager.cpp b/framework/code/texture/textureManager.cpp index ed8c1ea..c0d87f0 100644 --- a/framework/code/texture/textureManager.cpp +++ b/framework/code/texture/textureManager.cpp @@ -1,10 +1,9 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #include "system/assetManager.hpp" #include "textureManager.hpp" @@ -12,28 +11,29 @@ #include "loaderPpm.hpp" #include -TextureManager::TextureManager() noexcept +TextureManagerBase::TextureManagerBase( AssetManager& rAssetManager ) noexcept + : m_AssetManager( rAssetManager ) { } -TextureManager::~TextureManager() +TextureManagerBase::~TextureManagerBase() { Release(); } -bool TextureManager::Initialize() +bool TextureManagerBase::Initialize(uint32_t numWorkerThreads) { - if (m_LoadingThreadWorker.Initialize("TextureThreadWorker", 4) <= 0) + if (m_LoadingThreadWorker.Initialize("TextureThreadWorker", numWorkerThreads) <= 0) return false; return m_Loader->Initialize(); } -void TextureManager::Release() +void TextureManagerBase::Release() { m_LoadingThreadWorker.Terminate(); } -std::unique_ptr TextureManager::CreateTextureObject( GraphicsApiBase& gfxApi, uint32_t Width, uint32_t Height, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, uint32_t Msaa, TEXTURE_FLAGS Flags ) +const TextureBase* TextureManagerBase::CreateTextureObject( uint32_t Width, uint32_t Height, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, Msaa Msaa, TEXTURE_FLAGS Flags ) { CreateTexObjectInfo createInfo{}; createInfo.uiWidth = Width; @@ -43,5 +43,5 @@ std::unique_ptr TextureManager::CreateTextureObject( GraphicsApiBase& g createInfo.Flags = Flags; createInfo.pName = pName; createInfo.Msaa = Msaa; - return CreateTextureObject( gfxApi, createInfo ); + return CreateTextureObject( createInfo ); } diff --git a/framework/code/texture/textureManager.hpp b/framework/code/texture/textureManager.hpp index 86fb834..df7d8c1 100644 --- a/framework/code/texture/textureManager.hpp +++ b/framework/code/texture/textureManager.hpp @@ -1,14 +1,14 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #pragma once #include #include #include +#include /// /// Texture file loading and tracking @@ -19,52 +19,56 @@ // Forward declarations class AssetManager; -class TextureKtx; -class TexturePpm; -class Texture; -class Sampler; +class TextureKtxBase; +class TexturePpmBase; +class TextureBase; +class SamplerBase; class GraphicsApiBase; -template class TextureManagerT; +template class TextureManager; -class TextureManager +class TextureManagerBase { - TextureManager(const TextureManager&) = delete; - TextureManager& operator=(const TextureManager&) = delete; + TextureManagerBase(const TextureManagerBase&) = delete; + TextureManagerBase& operator=(const TextureManagerBase&) = delete; template static auto ExecutePathManipulators(std::string& filename, T&& currentManipulator, TT && ... subsequentManipulators); protected: - TextureManager() noexcept; + TextureManagerBase( AssetManager& ) noexcept; virtual void Release(); - bool Initialize(); + bool Initialize(uint32_t numWorkerThreads = 4); public: - virtual ~TextureManager(); - template using tApiDerived = TextureManagerT; // make apiCast work! + virtual ~TextureManagerBase(); + template using tApiDerived = TextureManager; // make apiCast work! /// @brief Load a texture if we haven't already loaded it (into the slot 'textureName'), otherwise return the previously loaded texture /// @param rAssetManager asset manager to use to load the file /// @param textureName name of texture (key in lookup) /// @param ...pathnameManipulators variadic functors to manipulate the textureName into a loadable image filename (eg change extension or path) /// @return pointer to loaded texture info - template - const Texture* GetOrLoadTexture(AssetManager& rAssetManager, const std::string& textureName, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators); + template || std::is_same_v, bool> = true, typename... T_PATHMANIPULATOR> + const TextureBase* GetOrLoadTexture(const std::string& textureName, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators); /// @brief Load a texture if we haven't already loaded it into a named 'slot', otherwise return the previously loaded texture /// @param rAssetManager asset manager to use to load the file /// @param textureName name of texture (key in lookup) /// @param ...pathnameManipulators variadic functors to manipulate the textureName into a loadable image filename (eg change extension or path) /// @return pointer to loaded texture info - template - const Texture* GetOrLoadTexture(const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators); + template + const TextureBase* GetOrLoadTexture(const std::string& textureSlotName, const std::string& filename, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators); template - void BatchLoad(AssetManager& rAssetManager, const std::span textureNames, const Sampler& defaultSampler, T_PATHMANIPULATOR && ... pathnameManipulators); + void BatchLoad(const std::span textureNames, const SamplerBase& defaultSampler, T_PATHMANIPULATOR && ... pathnameManipulators); /// @brief Find a texture (by slot name) that may be already loaded /// @param textureSlotName name to look for /// @return pointer to already loaded texture, or null - virtual const Texture* GetTexture(const std::string& textureSlotName) const = 0; + virtual const TextureBase* GetTexture(const std::string& textureSlotName) const = 0; + + /// @brief Helper to return the ktx file loader directly. If this throws a compile error then .cpp that includes this file is missing #include "texture/[gfxapi]/loaderKtx.hpp" + TextureKtxBase* GetLoader() const { return m_Loader.get(); } + TexturePpmBase* GetLoaderPpm() const { return m_LoaderPpm.get(); } /// @brief Set the pathname manipulators used by GetOrLoadTexture if it is not supplied a pathnameManipulators parameter. /// @param ...pathnameManipulators variadic functors to manipulate the textureName into a loadable image filename (eg change extension or path) @@ -72,34 +76,35 @@ class TextureManager void SetDefaultFilenameManipulators(T_PATHMANIPULATOR && ... pathnameManipulators); /// Create texture (generally for render target usage) - std::unique_ptr CreateTextureObject(GraphicsApiBase&, uint32_t uiWidth, uint32_t uiHeight, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, uint32_t Msaa = 1, TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None); + const TextureBase* CreateTextureObject( uint32_t uiWidth, uint32_t uiHeight, TextureFormat Format, TEXTURE_TYPE TexType, const char* pName, Msaa Msaa = Msaa::Samples1, TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None ); /// Create texture (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. Must be implemented per graphics api - virtual std::unique_ptr CreateTextureObject(GraphicsApiBase&, const CreateTexObjectInfo& texInfo) = 0; + virtual const TextureBase* CreateTextureObject( const CreateTexObjectInfo& texInfo ) = 0; /// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) - virtual std::unique_ptr CreateTextureObjectView( GraphicsApiBase& gfxApi, const Texture& original, TextureFormat viewFormat ) = 0; + virtual const TextureBase* CreateTextureObjectView( const TextureBase& original, TextureFormat viewFormat, std::string name ) = 0; /// Create texture from a block of texture data in memory (with correct format, span etc). - virtual std::unique_ptr CreateTextureFromBuffer( GraphicsApiBase&, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName) = 0; + virtual const TextureBase* CreateTextureFromBuffer( const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, std::string name) = 0; /// Get a 'default' sampler for the given address mode (all other sampler settings assumed to be 'normal' ie linearly sampled etc) - virtual const Sampler* const GetSampler( SamplerAddressMode ) const = 0; + virtual const SamplerBase* const GetSampler( SamplerAddressMode ) const = 0; protected: - const Texture* GetOrLoadTexture_( const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const SamplerAddressMode& sampler ) + const TextureBase* GetOrLoadTexture_( const std::string& textureSlotName, const std::string& filename, const SamplerAddressMode& sampler ) { - return GetOrLoadTexture_( textureSlotName, rAssetManager, filename, *GetSampler( sampler ) ); + return GetOrLoadTexture_( textureSlotName, filename, *GetSampler( sampler ) ); } - virtual const Texture* GetOrLoadTexture_( const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const Sampler& sampler ) = 0; - virtual void BatchLoad(AssetManager& rAssetManager, const std::span>, const Sampler& defaultSampler) = 0; + virtual const TextureBase* GetOrLoadTexture_( const std::string& textureSlotName, const std::string& filename, const SamplerBase& sampler ) = 0; + virtual void BatchLoad(const std::span>, const SamplerBase& defaultSampler) = 0; protected: - std::unique_ptr m_Loader; - std::unique_ptr m_LoaderPpm; - std::function m_DefaultFilenameManipulator = [](std::string&) {return; }; - CWorker m_LoadingThreadWorker; + AssetManager& m_AssetManager; + std::unique_ptr m_Loader; + std::unique_ptr m_LoaderPpm; + std::function m_DefaultFilenameManipulator = [](std::string&) {return; }; + ThreadWorker m_LoadingThreadWorker; }; @@ -107,25 +112,28 @@ class TextureManager /// This template is expected to be specialized. /// @tparam T_GFXAPI template -class TextureManagerT final : public TextureManager +class TextureManager final : public TextureManagerBase { - TextureManagerT() noexcept = delete; // Error if we use this non specialized version of the TextureManagerT. - ~TextureManagerT() override = delete; // Error if we use this non specialized version of the TextureManagerT. + TextureManager() noexcept = delete; // Error if we use this non specialized version of the TextureManager. + ~TextureManager() override = delete; // Error if we use this non specialized version of the TextureManager. + + static_assert(sizeof( TextureManager ) != sizeof( TextureManagerBase )); // Ensure this class template is specialized (and not used as-is) }; /// @brief Helper class/functor to prefix filename with a (given) directory string -/// Can be passed as a parameter to TextureManager::SetDefaultFilenameManipulators or TextureManager::GetOrLoadTexture +/// Can be passed as a parameter to TextureManagerBase::SetDefaultFilenameManipulators or TextureManagerBase::GetOrLoadTexture struct PathManipulator_PrefixDirectory { /// @param prefix to change to put before filename (eg "Textures/") - PathManipulator_PrefixDirectory(std::string prefix) noexcept : m_prefix(std::move(prefix)) {}; - void operator()(std::string& filename) const noexcept { filename.assign(m_prefix + filename); }; - std::string m_prefix; + PathManipulator_PrefixDirectory(std::string prefix) noexcept : m_prefix(prefix) {}; + void operator()(std::string& filename) const noexcept { filename.assign((m_prefix / filename).string()); }; + void operator()(std::filesystem::path& filename) const noexcept { filename.assign((m_prefix / filename).string()); }; + std::filesystem::path m_prefix; }; /// @brief Helper class/functor to change filename extension -/// Can be passed as a parameter to TextureManager::SetDefaultFilenameManipulators or TextureManager::GetOrLoadTexture +/// Can be passed as a parameter to TextureManagerBase::SetDefaultFilenameManipulators or TextureManagerBase::GetOrLoadTexture struct PathManipulator_ChangeExtension { /// @param extension to change to (eg ".ktx") @@ -142,7 +150,7 @@ struct PathManipulator_ChangeExtension // Template implementations // template -auto TextureManager::ExecutePathManipulators(std::string& filename, T&& currentManipulator, TT && ... subsequentManipulators) +auto TextureManagerBase::ExecutePathManipulators(std::string& filename, T&& currentManipulator, TT && ... subsequentManipulators) { currentManipulator(filename); if constexpr (sizeof...(subsequentManipulators) == 0) @@ -151,8 +159,8 @@ auto TextureManager::ExecutePathManipulators(std::string& filename, T&& currentM ExecutePathManipulators(filename, subsequentManipulators...);// Recursively call the subsequent manipulator(s). } -template -const Texture* TextureManager::GetOrLoadTexture(AssetManager& rAssetManager, const std::string& textureName, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators) +template || std::is_same_v, bool>, typename... T_PATHMANIPULATOR> +const TextureBase* TextureManagerBase::GetOrLoadTexture(const std::string& textureName, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators) { if (textureName.empty()) return nullptr; @@ -165,11 +173,11 @@ const Texture* TextureManager::GetOrLoadTexture(AssetManager& rAssetManager, con m_DefaultFilenameManipulator(filename); else ExecutePathManipulators(filename, pathnameManipulators...); - return GetOrLoadTexture_(textureName, rAssetManager, filename, sampler); + return GetOrLoadTexture_(textureName, filename, sampler); } template -const Texture* TextureManager::GetOrLoadTexture(const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators) +const TextureBase* TextureManagerBase::GetOrLoadTexture(const std::string& textureSlotName, const std::string& filename, const T_SAMPLER& sampler, T_PATHMANIPULATOR && ... pathnameManipulators) { if (textureSlotName.empty()) return nullptr; @@ -184,11 +192,11 @@ const Texture* TextureManager::GetOrLoadTexture(const std::string& textureSlotNa m_DefaultFilenameManipulator(filename_); else ExecutePathManipulators(filename_, pathnameManipulators...); - return GetOrLoadTexture_(textureSlotName, rAssetManager, filename_, sampler); + return GetOrLoadTexture_(textureSlotName, filename_, sampler); } template -void TextureManager::BatchLoad(AssetManager& rAssetManager, const std::span textureNames, const Sampler& defaultSampler, T_PATHMANIPULATOR && ... pathnameManipulators) +void TextureManagerBase::BatchLoad(const std::span textureNames, const SamplerBase& defaultSampler, T_PATHMANIPULATOR && ... pathnameManipulators) { std::vector< std::pair> filenamesAndSlotNames; filenamesAndSlotNames.reserve(textureNames.size()); @@ -201,11 +209,11 @@ void TextureManager::BatchLoad(AssetManager& rAssetManager, const std::span -void TextureManager::SetDefaultFilenameManipulators(T_PATHMANIPULATOR && ... pathnameManipulators) +void TextureManagerBase::SetDefaultFilenameManipulators(T_PATHMANIPULATOR && ... pathnameManipulators) { m_DefaultFilenameManipulator = [... p = std::forward(pathnameManipulators)](std::string& filename) -> void { ExecutePathManipulators(filename, p ...); diff --git a/framework/code/texture/vulkan/imageWrapper.cpp b/framework/code/texture/vulkan/imageWrapper.cpp deleted file mode 100644 index aacc903..0000000 --- a/framework/code/texture/vulkan/imageWrapper.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "imageWrapper.hpp" -#include "memory/memory.hpp" -#include "vulkan/vulkan.hpp" - - -//----------------------------------------------------------------------------- -Wrap_VkImage::Wrap_VkImage() - : m_Usage{ MemoryUsage::Unknown } - , m_ImageInfo{} -//----------------------------------------------------------------------------- -{ -} - -//----------------------------------------------------------------------------- -Wrap_VkImage::~Wrap_VkImage() -//----------------------------------------------------------------------------- -{ - Release(); -} - -//----------------------------------------------------------------------------- -bool Wrap_VkImage::Initialize(Vulkan* pVulkan, const VkImageCreateInfo& ImageInfo, MemoryUsage Usage, const char* pName) -//----------------------------------------------------------------------------- -{ - // If we have a name, save it - if (pName != NULL) - { - m_Name = pName; - } - - // Need Vulkan objects to release ourselves - m_pVulkan = pVulkan; - m_ImageInfo = ImageInfo; - m_Usage = Usage; - - auto& memoryManager = pVulkan->GetMemoryManager(); - m_VmaImage = memoryManager.CreateImage(ImageInfo, Usage); - - if (m_VmaImage) - { - pVulkan->SetDebugObjectName(m_VmaImage.GetVkBuffer(), pName); - } - - return !!m_VmaImage; -} - -//----------------------------------------------------------------------------- -void Wrap_VkImage::Release() -//----------------------------------------------------------------------------- -{ - if (m_pVulkan) - { - auto& memoryManager = m_pVulkan->GetMemoryManager(); - memoryManager.Destroy(std::move(m_VmaImage)); - } - m_pVulkan = nullptr; - m_ImageInfo = {}; - m_Name.clear(); -} diff --git a/framework/code/texture/vulkan/imageWrapper.hpp b/framework/code/texture/vulkan/imageWrapper.hpp deleted file mode 100644 index 8c10a2b..0000000 --- a/framework/code/texture/vulkan/imageWrapper.hpp +++ /dev/null @@ -1,45 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include "memory/vulkan/memoryMapped.hpp" - -// Forward declarations -class Vulkan; -template class MemoryAllocatedBuffer; -enum class MemoryUsage; - - -/// @brief Wrapper around VkImage -/// Owns the image's memory buffer -class Wrap_VkImage -{ - // Functions - Wrap_VkImage(const Wrap_VkImage&) = delete; - Wrap_VkImage& operator=(const Wrap_VkImage&) = delete; -public: - Wrap_VkImage(); - ~Wrap_VkImage(); - - bool Initialize(Vulkan* pVulkan, const VkImageCreateInfo& ImageInfo, MemoryUsage TypeFlag, const char* pName = nullptr); - void Release(); - - const auto& GetImageInfo() const { return m_ImageInfo; } - - // Attributes -public: - std::string m_Name; - MemoryAllocatedBuffer m_VmaImage; - -private: - Vulkan* m_pVulkan = nullptr; - MemoryUsage m_Usage; - VkImageCreateInfo m_ImageInfo; -}; diff --git a/framework/code/texture/vulkan/loaderKtx.cpp b/framework/code/texture/vulkan/loaderKtx.cpp index b979d68..7bf0bf1 100644 --- a/framework/code/texture/vulkan/loaderKtx.cpp +++ b/framework/code/texture/vulkan/loaderKtx.cpp @@ -1,6 +1,6 @@ //============================================================================================================ // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -14,19 +14,19 @@ // Static // Set to TextureKtxVulkan before calling upload (for the callbacks to use). Cleaner than hacking on the vulkan allocator, although still icky. -static thread_local TextureKtxT* sUploadingTextureKtxVulkan = nullptr; +static thread_local TextureKtx* sUploadingTextureKtxVulkan = nullptr; -TextureKtxT::TextureKtxT(Vulkan& vulkan) noexcept : TextureKtx(), m_Vulkan(vulkan) +TextureKtx::TextureKtx(Vulkan& vulkan) noexcept : TextureKtxBase(), m_Vulkan(vulkan) { } -TextureKtxT::~TextureKtxT() noexcept +TextureKtx::~TextureKtx() noexcept {} -bool TextureKtxT::Initialize() +bool TextureKtx::Initialize() { - if (!TextureKtx::Initialize()) + if (!TextureKtxBase::Initialize()) return false; // Setup the ktx library's device info (ready for subsequent ktx texture data transfers to the GPU) @@ -38,11 +38,11 @@ bool TextureKtxT::Initialize() return false; // Callbacks to override the allocation/bind functions used by ktxTexture_VkUploadEx - // We want to use our memorymanager (and the VMA allocator under it) to do vulkan allocations so the allocations play nicely with the TextureT container) - m_VulkanDeviceInfo->vkFuncs.vkAllocateMemory = TextureKtxT::VkAllocateMemory; - m_VulkanDeviceInfo->vkFuncs.vkFreeMemory = TextureKtxT::VkFreeMemory; - m_VulkanDeviceInfo->vkFuncs.vkDestroyImage = TextureKtxT::VkDestroyImage; - m_VulkanDeviceInfo->vkFuncs.vkBindImageMemory = TextureKtxT::VkBindImageMemory; + // We want to use our memorymanager (and the VMA allocator under it) to do vulkan allocations so the allocations play nicely with the Texture container) + m_VulkanDeviceInfo->vkFuncs.vkAllocateMemory = TextureKtx::VkAllocateMemory; + m_VulkanDeviceInfo->vkFuncs.vkFreeMemory = TextureKtx::VkFreeMemory; + m_VulkanDeviceInfo->vkFuncs.vkDestroyImage = TextureKtx::VkDestroyImage; + m_VulkanDeviceInfo->vkFuncs.vkBindImageMemory = TextureKtx::VkBindImageMemory; const auto& gpuFeatures = m_Vulkan.GetGpuFeatures().Base.features; if (gpuFeatures.textureCompressionBC) @@ -55,16 +55,16 @@ bool TextureKtxT::Initialize() return true; } -void TextureKtxT::Release() +void TextureKtx::Release() { if (m_VulkanDeviceInfo) ktxVulkanDeviceInfo_Destruct(m_VulkanDeviceInfo.get()); m_VulkanDeviceInfo.release(); // we didnt own the pointer! - TextureKtx::Release(); + TextureKtxBase::Release(); } -uint32_t TextureKtxT::DetermineTranscodeOutputFormat() const +uint32_t TextureKtx::DetermineTranscodeOutputFormat() const { if (m_SupportsBcCompression && (m_FavorBcCompression || (!m_SupportsAstcCompression && !m_SupportsEtc2Compression))) return KTX_TTF_BC7_RGBA; // should be better quality than KTX_TTF_BC1_OR_3 but may take longer to transcode (untested) @@ -77,7 +77,7 @@ uint32_t TextureKtxT::DetermineTranscodeOutputFormat() const return KTX_TTF_RGBA32; } -TextureKtxFileWrapper TextureKtxT::Transcode(TextureKtxFileWrapper&& fileData) +TextureKtxFileWrapper TextureKtx::Transcode(TextureKtxFileWrapper&& fileData) { auto* const pKtxData = GetKtxTexture(fileData); if (pKtxData!=nullptr && ktxTexture_NeedsTranscoding(pKtxData) && pKtxData->classId == class_id::ktxTexture2_c) @@ -91,7 +91,7 @@ TextureKtxFileWrapper TextureKtxT::Transcode(TextureKtxFileWrapper&& fil return std::move(fileData); } -TextureVulkan TextureKtxT::LoadKtx(Vulkan& vulkan, const TextureKtxFileWrapper& fileData, SamplerT sampler) +TextureVulkan TextureKtx::LoadKtx(Vulkan& vulkan, const TextureKtxFileWrapper& fileData, Sampler sampler) { auto* const pKtxData = GetKtxTexture(fileData); if (!pKtxData) @@ -118,7 +118,7 @@ TextureVulkan TextureKtxT::LoadKtx(Vulkan& vulkan, const TextureKtxFileW sUploadingTextureKtxVulkan = nullptr; // Take ownership of the image from the container holding images created during ktxTexture_VkUploadEx - ImageT allocatedImage{std::move( m_AllocatedImages.extract( m_AllocatedImages.find( uploadedTexture.image ) ).value() )}; + Image allocatedImage{std::move( m_AllocatedImages.extract( m_AllocatedImages.find( uploadedTexture.image ) ).value() )}; auto imageView = CreateImageView( vulkan, allocatedImage, VkToTextureFormat(uploadedTexture.imageFormat), uploadedTexture.levelCount, 0, uploadedTexture.layerCount, 0, (ImageViewType) uploadedTexture.viewType ); if (imageView.IsEmpty()) @@ -126,25 +126,15 @@ TextureVulkan TextureKtxT::LoadKtx(Vulkan& vulkan, const TextureKtxFileW ktxVulkanTexture_Destruct(&uploadedTexture, vulkan.m_VulkanDevice, nullptr); return {}; } - //SamplerT& samplerVulkan = static_cast&>(sampler); - //if (samplerVulkan.IsEmpty()) - //{ - // if (!CreateSampler(&vulkan, VK_SAMPLER_ADDRESS_MODE_REPEAT, SamplerFilter::Linear, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, false, 0.0f, &sampler)) - // { - // vkDestroyImageView(vulkan.m_VulkanDevice, imageView, nullptr); - // ktxVulkanTexture_Destruct(&uploadedTexture, vulkan.m_VulkanDevice, nullptr); - // return {}; - // } - //} TextureFormat textureFormat = VkToTextureFormat(uploadedTexture.imageFormat); // Return the fully formed texture object - TextureVulkan texture{ uploadedTexture.width, uploadedTexture.height, uploadedTexture.depth, uploadedTexture.levelCount, 0, uploadedTexture.layerCount, 0, textureFormat, uploadedTexture.imageLayout, std::move(allocatedImage), std::move(sampler), std::move(imageView) }; + TextureVulkan texture{uploadedTexture.width, uploadedTexture.height, uploadedTexture.depth, uploadedTexture.levelCount, 0, uploadedTexture.layerCount, 0, textureFormat, uploadedTexture.imageLayout, VkClearValue{}, std::move( allocatedImage ), std::move( sampler ), std::move( imageView )}; return texture; } -TextureVulkan TextureKtxT::LoadKtx( Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, SamplerT sampler ) +TextureVulkan TextureKtx::LoadKtx( Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, Sampler sampler ) { auto ktxData = LoadFile( assetManager, pFileName ); if (!ktxData) @@ -165,7 +155,7 @@ static bool operator<(const MemoryAllocatedBuffer& a, co static bool operator<(const VkDeviceMemory& a, const MemoryAllocatedBuffer& b) { return a < b.GetVkBuffer(); } static bool operator<(const MemoryAllocatedBuffer& a, const VkDeviceMemory& b) { return a.GetVkBuffer() < b; } -/*static*/ VkResult TextureKtxT::VkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) +/*static*/ VkResult TextureKtx::VkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) { if (sUploadingTextureKtxVulkan) { @@ -181,7 +171,7 @@ static bool operator<(const MemoryAllocatedBuffer& a, co return vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory); } -/*static*/ void TextureKtxT::VkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) +/*static*/ void TextureKtx::VkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) { if (sUploadingTextureKtxVulkan) { @@ -203,7 +193,7 @@ static bool operator<(const MemoryAllocatedBuffer& a, co return VkFreeMemory(device, memory, pAllocator); } -/*static*/ VkResult TextureKtxT::VkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) +/*static*/ VkResult TextureKtx::VkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) { if (sUploadingTextureKtxVulkan) { @@ -221,7 +211,7 @@ static bool operator<(const MemoryAllocatedBuffer& a, co return vkBindImageMemory(device, image, memory, memoryOffset); } -/*static*/ void TextureKtxT::VkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) +/*static*/ void TextureKtx::VkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) { if (sUploadingTextureKtxVulkan && image != VK_NULL_HANDLE) { @@ -238,15 +228,15 @@ static bool operator<(const MemoryAllocatedBuffer& a, co } /// @brief Function specialization -template<> -TextureT TextureKtx::LoadKtx( Vulkan& vulkan, const TextureKtxFileWrapper& textureFile, SamplerT sampler ) -{ - return apiCast( this )->LoadKtx( vulkan, textureFile, std::move(sampler) ); -} +//template<> +//Texture TextureKtxBase::LoadKtx( Vulkan& vulkan, const TextureKtxFileWrapper& textureFile, Sampler sampler ) +//{ +// return apiCast( this )->LoadKtx( vulkan, textureFile, std::move(sampler) ); +//} /// @brief Function specialization -template<> -TextureT TextureKtx::LoadKtx( Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, SamplerT sampler ) -{ - return apiCast( this )->LoadKtx( vulkan, assetManager, pFileName, std::move(sampler) ); -} +//template<> +//Texture TextureKtxBase::LoadKtx( Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, Sampler sampler ) +//{ +// return apiCast( this )->LoadKtx( vulkan, assetManager, pFileName, std::move(sampler) ); +//} diff --git a/framework/code/texture/vulkan/loaderKtx.hpp b/framework/code/texture/vulkan/loaderKtx.hpp index 21f41c1..1a5de8e 100644 --- a/framework/code/texture/vulkan/loaderKtx.hpp +++ b/framework/code/texture/vulkan/loaderKtx.hpp @@ -1,6 +1,6 @@ //============================================================================================================ // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -13,7 +13,7 @@ #include "../loaderKtx.hpp" #include "memory/vulkan/memoryMapped.hpp" #include "texture.hpp" -#include +#include #include #include @@ -21,20 +21,20 @@ struct ktxVulkanDeviceInfo; struct ktxTexture; class Vulkan; -template class TextureT; -template class SamplerT; -using TextureVulkan = TextureT; +template class Texture; +template class Sampler; +using TextureVulkan = Texture; /// @brief Class to handle loading KTX textures in to Vulkan memory template<> -class TextureKtxT : public TextureKtx +class TextureKtx : public TextureKtxBase { - TextureKtxT(const TextureKtxT&) = delete; - TextureKtxT& operator=(const TextureKtxT&) = delete; + TextureKtx(const TextureKtx&) = delete; + TextureKtx& operator=(const TextureKtx&) = delete; public: - TextureKtxT(Vulkan& vulkan) noexcept; - ~TextureKtxT() noexcept; + TextureKtx(Vulkan& vulkan) noexcept; + ~TextureKtx() noexcept; /// @brief Initialize this loader class /// @return true on success @@ -47,13 +47,13 @@ class TextureKtxT : public TextureKtx /// @param textureFile ktx file data we want to load as a vulkan texture /// @param sampler sampler that loaded texture will take OWNERSHIP of /// @returns a &TextureVulkan, will be empty on failure - TextureVulkan LoadKtx(Vulkan& vulkan, const TextureKtxFileWrapper& textureFile, SamplerT sampler); + TextureVulkan LoadKtx(Vulkan& vulkan, const TextureKtxFileWrapper& textureFile, Sampler sampler); /// @brief Load a ktx file and do the necessary upload etc to go from a cpu texture representation to Vulkan format /// @param filename of ktx (or ktx2) format file we want to load as a vulkan texture /// @param sampler sampler that loaded texture will take OWNERSHIP of /// @returns a &TextureVulkan, will be empty on failure - TextureVulkan LoadKtx(Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, SamplerT sampler); + TextureVulkan LoadKtx(Vulkan& vulkan, AssetManager& assetManager, const char* const pFileName, Sampler sampler); /// @brief Run the Ktx2 transcoding step (if needed) /// Will do nothing for textures that do not need transcoding. diff --git a/framework/code/texture/vulkan/sampler.cpp b/framework/code/texture/vulkan/sampler.cpp new file mode 100644 index 0000000..e77b221 --- /dev/null +++ b/framework/code/texture/vulkan/sampler.cpp @@ -0,0 +1,109 @@ +//============================================================================= +// +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "sampler.hpp" +#include "vulkan/vulkan.hpp" + +VkFilter EnumToVk(SamplerFilter s) { assert(s != SamplerFilter::Undefined && s <= SamplerFilter::Linear ); return VkFilter(int( s ) - 1); } +static_assert(VK_FILTER_NEAREST == int(SamplerFilter::Nearest) - 1); +static_assert(VK_FILTER_LINEAR == int(SamplerFilter::Linear) - 1); + +VkSamplerMipmapMode EnumToVkSamplerMipmapMode(SamplerFilter s) { assert(s != SamplerFilter::Undefined); return VkSamplerMipmapMode(int(s) - 1); } +static_assert(VK_SAMPLER_MIPMAP_MODE_NEAREST == int(SamplerFilter::Nearest) - 1); +static_assert(VK_SAMPLER_MIPMAP_MODE_LINEAR == int(SamplerFilter::Linear) - 1); + +constexpr VkSamplerAddressMode EnumToVk(SamplerAddressMode s) { assert(s != SamplerAddressMode::Undefined); return VkSamplerAddressMode(int(s) - 1); } +static_assert(VK_SAMPLER_ADDRESS_MODE_REPEAT == int(SamplerAddressMode::Repeat) -1); +static_assert(VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT == int(SamplerAddressMode::MirroredRepeat) - 1); +static_assert(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE == int(SamplerAddressMode::ClampEdge) - 1); +static_assert(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER == int(SamplerAddressMode::ClampBorder) - 1); +static_assert(VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE == int(SamplerAddressMode::MirroredClampEdge) - 1); + +constexpr VkBorderColor EnumToVk(SamplerBorderColor s) { return VkBorderColor(int(s)); } +static_assert(VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK == int(SamplerBorderColor::TransparentBlackFloat)); +static_assert(VK_BORDER_COLOR_INT_TRANSPARENT_BLACK == int(SamplerBorderColor::TransparentBlackInt)); +static_assert(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK == int(SamplerBorderColor::OpaqueBlackFloat)); +static_assert(VK_BORDER_COLOR_INT_OPAQUE_BLACK == int(SamplerBorderColor::OpaqueBlackInt)); +static_assert(VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE == int(SamplerBorderColor::OpaqueWhiteFloat)); +static_assert(VK_BORDER_COLOR_INT_OPAQUE_WHITE == int(SamplerBorderColor::OpaqueWhiteInt)); + + +static_assert(int(SamplerAddressMode::Repeat) == VK_SAMPLER_ADDRESS_MODE_REPEAT + 1); +static_assert(int(SamplerAddressMode::MirroredRepeat) == VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT + 1); +static_assert(int(SamplerAddressMode::ClampEdge) == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE + 1); +static_assert(int(SamplerAddressMode::ClampBorder) == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER + 1); +static_assert(int(SamplerAddressMode::MirroredClampEdge) == VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE + 1); + +static_assert(int(SamplerFilter::Nearest) == VK_FILTER_NEAREST + 1); +static_assert(int(SamplerFilter::Linear) == VK_FILTER_LINEAR + 1); + +static_assert(int(SamplerBorderColor::TransparentBlackFloat) == VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK); +static_assert(int(SamplerBorderColor::TransparentBlackInt) == VK_BORDER_COLOR_INT_TRANSPARENT_BLACK); +static_assert(int(SamplerBorderColor::OpaqueBlackFloat) == VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK); +static_assert(int(SamplerBorderColor::OpaqueBlackInt) == VK_BORDER_COLOR_INT_OPAQUE_BLACK); +static_assert(int(SamplerBorderColor::OpaqueWhiteFloat) == VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE); +static_assert(int(SamplerBorderColor::OpaqueWhiteInt) == VK_BORDER_COLOR_INT_OPAQUE_WHITE); + +// +// Constructors/move-operators for Sampler. +// +Sampler::Sampler() noexcept + : m_Sampler( VK_NULL_HANDLE, VK_NULL_HANDLE ) +{} +Sampler::~Sampler() noexcept +{ +} +Sampler::Sampler( VkDevice device, VkSampler sampler ) noexcept + : m_Sampler( device, sampler ) +{} +Sampler::Sampler(Sampler&& src) noexcept + : m_Sampler( std::move( src.m_Sampler ) ) +{ +} +Sampler& Sampler::operator=(Sampler&& src ) noexcept +{ + if (this != &src) + { + m_Sampler = src.m_Sampler; + src.m_Sampler = {}; + } + return *this; +} + + +//----------------------------------------------------------------------------- +template<> +Sampler CreateSampler(Vulkan& vulkan, const CreateSamplerObjectInfo& createInfo) +//----------------------------------------------------------------------------- +{ + const bool anisotropyEnable = createInfo.Anisotropy > 1.0f && createInfo.Filter == SamplerFilter::Linear; + const VkSamplerCreateInfo SamplerInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .flags = 0, + .magFilter = EnumToVk(createInfo.Filter), + .minFilter = EnumToVk(createInfo.Filter), + .mipmapMode = EnumToVkSamplerMipmapMode(createInfo.MipFilter), + .addressModeU = EnumToVk(createInfo.Mode), + .addressModeV = EnumToVk(createInfo.Mode), + .addressModeW = EnumToVk(createInfo.Mode), + .mipLodBias = createInfo.MipBias, + .anisotropyEnable = anisotropyEnable ? VK_TRUE : VK_FALSE, + .maxAnisotropy = anisotropyEnable == VK_TRUE ? createInfo.Anisotropy : 1.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = createInfo.MinLod, + .maxLod = createInfo.MaxLod, + .borderColor = EnumToVk(createInfo.BorderColor), + .unnormalizedCoordinates = createInfo.UnnormalizedCoordinates ? VK_TRUE : VK_FALSE }; + + VkSampler vkSampler; + VkResult retVal = vkCreateSampler(vulkan.m_VulkanDevice, &SamplerInfo, NULL, &vkSampler); + if (!CheckVkError("vkCreateSampler()", retVal)) + return {}; + return {vulkan.m_VulkanDevice, vkSampler}; +} + diff --git a/framework/code/texture/vulkan/sampler.hpp b/framework/code/texture/vulkan/sampler.hpp new file mode 100644 index 0000000..542207d --- /dev/null +++ b/framework/code/texture/vulkan/sampler.hpp @@ -0,0 +1,37 @@ +//============================================================================= +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "../sampler.hpp" +#include "vulkan/refHandle.hpp" +#include +#include + +class Vulkan; + +/// @brief Template specialization of sampler container for Vulkan graphics api. +template<> +class Sampler final : public SamplerBase +{ +public: + Sampler() noexcept; + ~Sampler() noexcept; + Sampler(VkDevice, VkSampler) noexcept; + Sampler(Sampler&& src) noexcept; + Sampler& operator=(Sampler&& src) noexcept; + Sampler Copy() const { return Sampler{*this}; } + + VkSampler GetVkSampler() const { return m_Sampler; } + bool IsEmpty() const { return m_Sampler == VK_NULL_HANDLE; } + +private: + Sampler( const Sampler& src ) noexcept { + m_Sampler = src.m_Sampler; + } + + RefHandle m_Sampler; +}; diff --git a/framework/code/texture/vulkan/texture.cpp b/framework/code/texture/vulkan/texture.cpp index 5a593f9..9f9ae88 100644 --- a/framework/code/texture/vulkan/texture.cpp +++ b/framework/code/texture/vulkan/texture.cpp @@ -1,44 +1,20 @@ //============================================================================= // -// Copyright (c) 2023 QUALCOMM Technologies Inc. +// Copyright (c) 2022 QUALCOMM Technologies Inc. // All Rights Reserved. // //============================================================================== #include "memory/vulkan/memoryMapped.hpp" #include "texture.hpp" -#include "imageWrapper.hpp" #include "vulkan/vulkan.hpp" +#include -VkFilter EnumToVk(SamplerFilter s) { assert(s != SamplerFilter::Undefined); return VkFilter(int( s ) - 1); } -static_assert(VK_FILTER_NEAREST == int(SamplerFilter::Nearest) - 1); -static_assert(VK_FILTER_LINEAR == int(SamplerFilter::Linear) - 1); - -VkSamplerMipmapMode EnumToVkSamplerMipmapMode(SamplerFilter s) { assert(s != SamplerFilter::Undefined); return VkSamplerMipmapMode(int(s) - 1); } -static_assert(VK_SAMPLER_MIPMAP_MODE_NEAREST == int(SamplerFilter::Nearest) - 1); -static_assert(VK_SAMPLER_MIPMAP_MODE_LINEAR == int(SamplerFilter::Linear) - 1); - -constexpr VkSamplerAddressMode EnumToVk(SamplerAddressMode s) { assert(s != SamplerAddressMode::Undefined); return VkSamplerAddressMode(int(s) - 1); } -static_assert(VK_SAMPLER_ADDRESS_MODE_REPEAT == int(SamplerAddressMode::Repeat) -1); -static_assert(VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT == int(SamplerAddressMode::MirroredRepeat) - 1); -static_assert(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE == int(SamplerAddressMode::ClampEdge) - 1); -static_assert(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER == int(SamplerAddressMode::ClampBorder) - 1); -static_assert(VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE == int(SamplerAddressMode::MirroredClampEdge) - 1); - -constexpr VkBorderColor EnumToVk(SamplerBorderColor s) { return VkBorderColor(int(s)); } -static_assert(VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK == int(SamplerBorderColor::TransparentBlackFloat)); -static_assert(VK_BORDER_COLOR_INT_TRANSPARENT_BLACK == int(SamplerBorderColor::TransparentBlackInt)); -static_assert(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK == int(SamplerBorderColor::OpaqueBlackFloat)); -static_assert(VK_BORDER_COLOR_INT_OPAQUE_BLACK == int(SamplerBorderColor::OpaqueBlackInt)); -static_assert(VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE == int(SamplerBorderColor::OpaqueWhiteFloat)); -static_assert(VK_BORDER_COLOR_INT_OPAQUE_WHITE == int(SamplerBorderColor::OpaqueWhiteInt)); - - -TextureT::TextureT() noexcept +Texture::Texture() noexcept {} -TextureT::TextureT(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, ImageT image, SamplerT sampler, ImageViewT imageView) noexcept - : Texture() +Texture::Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, VkClearValue clearValue, ::Image image, ::Sampler sampler, ::ImageView imageView) noexcept + : TextureBase() , Width(width) , Height(height) , Depth(depth) @@ -46,18 +22,19 @@ TextureT::TextureT(uint32_t width, uint32_t height, uint32_t depth, uint , Faces(faces) , Format(format) , ImageLayout(imageLayout) + , ClearValue(clearValue) , Image(std::move(image)) , Sampler(std::move(sampler)) , ImageView(std::move(imageView)) { } -/// @brief Construct TextureT from a pre-existing Vulkan image/memory handles. -/// @param image - ownership NOT passed in to this TextureT, beware of lifetime issues. -/// @param sampler - ownership passed to this TextureT. -/// @param imageView - ownership passed to this TextureT. -TextureT::TextureT(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, VkImage image, VkDeviceMemory memory, SamplerT sampler, ImageViewT imageView) noexcept - : Texture() +/// @brief Construct Texture from a pre-existing Vulkan image/memory handles. +/// @param image - ownership NOT passed in to this Texture, beware of lifetime issues. +/// @param sampler - ownership passed to this Texture. +/// @param imageView - ownership passed to this Texture. +Texture::Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, VkClearValue clearValue, VkImage image, VkDeviceMemory memory, ::Sampler sampler, ::ImageView imageView) noexcept + : TextureBase() , Width(width) , Height(height) , Depth(depth) @@ -67,6 +44,7 @@ TextureT::TextureT(uint32_t width, uint32_t height, uint32_t depth, uint , FirstFace( firstFace ) , Format(format) , ImageLayout(imageLayout) + , ClearValue(clearValue) , Sampler(std::move(sampler)) , ImageView(std::move(imageView)) , Image({image, memory}) @@ -74,14 +52,14 @@ TextureT::TextureT(uint32_t width, uint32_t height, uint32_t depth, uint } // -// Constructors/move-operators for TextureT. +// Constructors/move-operators for Texture. // Ensures we are not leaking owned members. // -TextureT::TextureT(TextureT&& other) noexcept +Texture::Texture(Texture&& other) noexcept { *this = std::move(other); } -TextureT& TextureT::operator=(TextureT&& other) noexcept +Texture& Texture::operator=(Texture&& other) noexcept { if (this != &other) { @@ -95,6 +73,7 @@ TextureT& TextureT::operator=(TextureT&& other) noexcept FirstFace = other.FirstFace; Format = other.Format; ImageLayout = other.ImageLayout; + ClearValue = other.ClearValue; // Actually transfer ownership from 'other' Image = std::move(other.Image); Sampler = std::move(other.Sampler); @@ -103,15 +82,15 @@ TextureT& TextureT::operator=(TextureT&& other) noexcept return *this; } -TextureT::~TextureT() noexcept +Texture::~Texture() noexcept { - // Asserts to ensure we called ReleaseTexture on this already. + // Asserts to ensure we called Release on this already. assert(Image.IsEmpty()); assert(ImageView.IsEmpty()); assert(Sampler.IsEmpty()); } -void TextureT::Release(GraphicsApiBase* pGraphicsApi) +void Texture::Release(GraphicsApiBase* pGraphicsApi) { auto* pVulkan = static_cast(pGraphicsApi); @@ -441,24 +420,31 @@ TextureFormat VkToTextureFormat(VkFormat f) return TextureFormat::UNDEFINED; } +//----------------------------------------------------------------------------- +VkSampleCountFlagBits EnumToVk(Msaa msaa) +//----------------------------------------------------------------------------- +{ + return (VkSampleCountFlagBits) msaa; +} //----------------------------------------------------------------------------- template<> -TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObjectInfo& texInfo) +Texture CreateTextureObject(Vulkan& vulkan, const CreateTexObjectInfo& texInfo, MemoryPool* pPool) //----------------------------------------------------------------------------- { - VkResult RetVal; + VkResult retVal; if (texInfo.pName == nullptr) LOGI("CreateTextureObject (%dx%d): ", texInfo.uiWidth, texInfo.uiHeight); else LOGI("CreateTextureObject (%dx%d): %s", texInfo.uiWidth, texInfo.uiHeight, texInfo.pName); - VkImageView RetImageView = VK_NULL_HANDLE; - VkImageLayout RetImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageView retImageView = VK_NULL_HANDLE; + VkImageLayout retImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkClearValue retClearValue{}; // How this texture object will be used. - MemoryUsage MemoryUsage = MemoryUsage::GpuExclusive; + MemoryUsage memoryUsage = MemoryUsage::GpuExclusive; VkFormat vkFormat = TextureFormatToVk(texInfo.Format); @@ -473,7 +459,7 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje ImageInfo.extent.depth = texInfo.uiDepth; ImageInfo.mipLevels = texInfo.uiMips; ImageInfo.arrayLayers = texInfo.uiFaces; - ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + ImageInfo.samples = EnumToVk(texInfo.Msaa); ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -498,13 +484,13 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje // If using VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ImageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // DO NOT enable Storage_Bit (potential performance impact) - ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; + ImageInfo.samples = EnumToVk(texInfo.Msaa); break; case TT_RENDER_TARGET_WITH_STORAGE: // If using VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL // Also enabling Storage may disable some hardware render buffer optimizations ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - assert(texInfo.Msaa == 1 && texInfo.Format != TextureFormat::R8G8B8A8_SRGB); //TODO: use Vulkan to determine if this texture buffer can have storeage set, for now assert on formats known to be problematic (msaa or srgb) + assert(texInfo.Msaa == Msaa::Samples1 && texInfo.Format != TextureFormat::R8G8B8A8_SRGB); //TODO: use Vulkan to determine if this texture buffer can have storeage set, for now assert on formats known to be problematic (msaa or srgb) ImageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; break; @@ -514,11 +500,14 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; break; case TT_RENDER_TARGET_SUBPASS: + case TT_RENDER_TARGET_LOCAL_READ_TRANSIENT: + // Subpass render targets dont need to be backed by memory! + memoryUsage = MemoryUsage::GpuLazilyAllocated; + [[fallthrough]]; + case TT_RENDER_TARGET_LOCAL_READ: ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ImageInfo.usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; - // Subpass render targets dont need to be backed by memory! - MemoryUsage = MemoryUsage::GpuLazilyAllocated; break; case TT_COMPUTE_TARGET: ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; @@ -535,10 +524,19 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; ImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT /*| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT*/; - if (texInfo.Msaa != VK_SAMPLE_COUNT_1_BIT) + if (texInfo.Msaa != Msaa::Samples1) + ImageInfo.flags |= VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT; + break; + case TT_DEPTH_TARGET_LOCAL_READ: + // If using VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL + ImageInfo.mipLevels = 1; + ImageInfo.arrayLayers = 1; + ImageInfo.samples = (VkSampleCountFlagBits)texInfo.Msaa; + ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + ImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT /* | VK_IMAGE_USAGE_STORAGE_BIT*/ | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + if (texInfo.Msaa != Msaa::Samples1) ImageInfo.flags |= VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT; break; - default: assert(0); break; @@ -551,16 +549,22 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje // Create the Image auto& memoryManager = vulkan.GetMemoryManager(); - auto VmaImage = memoryManager.CreateImage( ImageInfo, MemoryUsage ); + auto createImage = [&]() { + if (pPool && *pPool) + return memoryManager.CreateImage( *pPool, ImageInfo ); + else + return memoryManager.CreateImage( ImageInfo, memoryUsage ); + }; + auto vmaImage = createImage(); - if (!VmaImage && MemoryUsage == MemoryUsage::GpuLazilyAllocated) + if (!vmaImage && memoryUsage == MemoryUsage::GpuLazilyAllocated) { LOGI("Unable to initialize GpuLazilyAllocated image (probably not supported by GPU hardware). Falling back to GpuExclusive"); - MemoryUsage = MemoryUsage::GpuExclusive; + memoryUsage = MemoryUsage::GpuExclusive; ImageInfo.usage &= ~VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; - VmaImage = memoryManager.CreateImage( ImageInfo, MemoryUsage ); + vmaImage = memoryManager.CreateImage( ImageInfo, memoryUsage ); } - if (!VmaImage) + if (!vmaImage) { LOGE("Unable to initialize image (Not from file)"); return {}; @@ -570,50 +574,61 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje switch (texInfo.TexType) { case TT_SHADING_RATE_IMAGE: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_GENERAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); + retImageLayout = VK_IMAGE_LAYOUT_GENERAL; break; case TT_CPU_UPDATE: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); + retImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break; case TT_NORMAL: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); + retImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break; case TT_RENDER_TARGET: case TT_RENDER_TARGET_WITH_STORAGE: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + retImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break; case TT_RENDER_TARGET_TRANSFERSRC: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); - RetImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + retImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + break; + case TT_RENDER_TARGET_LOCAL_READ: + case TT_RENDER_TARGET_LOCAL_READ_TRANSIENT: + retImageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR; break; case TT_RENDER_TARGET_SUBPASS: - RetImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + retImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; break; case TT_COMPUTE_TARGET: case TT_COMPUTE_STORAGE: - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_GENERAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_GENERAL; + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_GENERAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); + retImageLayout = VK_IMAGE_LAYOUT_GENERAL; break; + case TT_DEPTH_TARGET_LOCAL_READ: + retImageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR; + memoryUsage = MemoryUsage::GpuLazilyAllocated; + [[fallthrough]]; case TT_DEPTH_TARGET: if (FormatHasStencil( texInfo.Format)) { // Can have depth and stencil flag - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + retClearValue.depthStencil.depth = 1.0f; } else if (FormatHasDepth(texInfo.Format)) { // Only has the depth flag set - vulkan.SetImageLayout(VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + vulkan.SetImageLayout(vmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); + retClearValue.depthStencil.depth = 1.0f; } else { LOGE("Unhandled depth format!!!"); } - RetImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + if(retImageLayout != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) + retImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; break; default: assert(0); @@ -623,7 +638,7 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje vulkan.FinishSetupCommandBuffer(SetupCmdBuffer); // Create the Image wrapper (containing the vmaimage) - ImageT Image{ std::move( VmaImage ) }; + Image Image{ std::move( vmaImage ) }; vulkan.SetDebugObjectName( Image.GetVkImage(), texInfo.pName ); // ... and an ImageView @@ -660,7 +675,12 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje case TT_COMPUTE_STORAGE: ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; break; + case TT_RENDER_TARGET_LOCAL_READ: + case TT_RENDER_TARGET_LOCAL_READ_TRANSIENT: + ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT; + break; case TT_DEPTH_TARGET: + case TT_DEPTH_TARGET_LOCAL_READ: ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; SamplerMode = (SamplerMode == SamplerAddressMode::Undefined) ? SamplerAddressMode::ClampBorder : SamplerMode; // default to ClampBorder BorderColor = SamplerBorderColor::OpaqueWhiteFloat; @@ -671,14 +691,14 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje } SamplerMode = (SamplerMode == SamplerAddressMode::Undefined) ? SamplerAddressMode::ClampEdge : SamplerMode; // default to ClampEdge - RetVal = vkCreateImageView(vulkan.m_VulkanDevice, &ImageViewInfo, NULL, &RetImageView); - if (!CheckVkError("vkCreateImageView()", RetVal)) + retVal = vkCreateImageView(vulkan.m_VulkanDevice, &ImageViewInfo, NULL, &retImageView); + if (!CheckVkError("vkCreateImageView()", retVal)) { return {}; } - ImageViewT ImageView{RetImageView, ImageViewInfo.viewType}; + ImageView ImageView{retImageView, ImageViewInfo.viewType}; - // LOGI("vkCreateImageView: %s -> %p", pName, RetImageView); + // LOGI("vkCreateImageView: %s -> %p", pName, retImageView); // Need a sampler... SamplerFilter FilterMode = texInfo.FilterMode; @@ -691,18 +711,19 @@ TextureT CreateTextureObject(Vulkan& vulkan, const CreateTexObje else FilterMode = SamplerFilter::Nearest; } - SamplerT Sampler = CreateSampler(vulkan, SamplerMode, FilterMode, BorderColor, 0.0f); + SamplerVulkan Sampler = CreateSampler(vulkan, SamplerMode, FilterMode, BorderColor, 0.0f); if (Sampler.IsEmpty()) { return {}; } - return{ texInfo.uiWidth, texInfo.uiHeight, texInfo.uiDepth, texInfo.uiMips, 0/*first mip*/, texInfo.uiFaces, 0/*first face*/, texInfo.Format, RetImageLayout, std::move(Image), std::move(Sampler), std::move(ImageView)}; + return{ texInfo.uiWidth, texInfo.uiHeight, texInfo.uiDepth, texInfo.uiMips, 0/*first mip*/, texInfo.uiFaces, 0/*first face*/, texInfo.Format, retImageLayout, retClearValue, std::move(Image), std::move(Sampler), std::move(ImageView)}; } //----------------------------------------------------------------------------- -TextureT CreateTextureObjectView( Vulkan& vulkan, const TextureT& original, TextureFormat viewFormat ) +template<> +Texture CreateTextureObjectView( Vulkan& vulkan, const Texture& original, TextureFormat viewFormat ) //----------------------------------------------------------------------------- { auto imageView = CreateImageView( vulkan, original.Image, viewFormat, original.MipLevels, original.FirstMip, original.Faces, original.FirstFace, original.ImageView.GetImageViewType() ); @@ -717,12 +738,12 @@ TextureT CreateTextureObjectView( Vulkan& vulkan, const TextureT return {}; } - return { original.Width, original.Height, original.Depth, original.MipLevels, original.FirstMip, original.Faces, original.FirstFace, original.Format, original.GetVkImageLayout(), original.GetVkImage(), VK_NULL_HANDLE, std::move(sampler), std::move(imageView) }; + return { original.Width, original.Height, original.Depth, original.MipLevels, original.FirstMip, original.Faces, original.FirstFace, original.Format, original.GetVkImageLayout(), original.GetVkClearValue(), original.GetVkImage(), VK_NULL_HANDLE, std::move(sampler), std::move(imageView) }; } //----------------------------------------------------------------------------- -TextureT CreateTextureObjectView( Vulkan& vulkan, const TextureT& original, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace ) +Texture CreateTextureObjectView( Vulkan& vulkan, const Texture& original, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace ) //----------------------------------------------------------------------------- { ImageViewVulkan imageView = CreateImageView( vulkan, original.Image, original.Format, mipLevels, firstMip, faces, firstFace, original.ImageView.GetImageViewType() ); @@ -735,13 +756,13 @@ TextureT CreateTextureObjectView( Vulkan& vulkan, const TextureT { return {}; } - return {original.Width, original.Height, original.Depth, original.MipLevels, original.FirstMip, original.Faces, original.FirstFace, original.Format, original.GetVkImageLayout(), original.GetVkImage(), VK_NULL_HANDLE, std::move( sampler ), std::move( imageView )}; + return {original.Width, original.Height, original.Depth, original.MipLevels, original.FirstMip, original.Faces, original.FirstFace, original.Format, original.GetVkImageLayout(), original.GetVkClearValue(), original.GetVkImage(), VK_NULL_HANDLE, std::move( sampler ), std::move( imageView )}; } //----------------------------------------------------------------------------- template<> -TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ) +Texture CreateTextureFromBuffer( Vulkan& vulkan, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ) //----------------------------------------------------------------------------- { if (pName == nullptr) @@ -749,7 +770,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD else LOGI( "CreateTextureFromBuffer (%dx%d): %s", Width, Height, pName ); - auto& memorymanager = vulkan.GetMemoryManager(); + auto& memoryManager = vulkan.GetMemoryManager(); uint32_t Faces = 1; uint32_t MipLevels = 1; @@ -777,10 +798,19 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD struct CubeFace { - CubeFace( uint32_t depthSlices, uint32_t mips ) : mipsPerDepthSlice( mips ), images( depthSlices* mips ) + CubeFace( MemoryManager& _memoryManager, uint32_t depthSlices, uint32_t mips ) : memoryManager(_memoryManager), mipsPerDepthSlice( mips ), images( depthSlices* mips ) {} + ~CubeFace() { for (auto& image : images) memoryManager.Destroy( std::move( image ) ); } + CubeFace& operator=( CubeFace&& ) noexcept { assert( 0 ); return *this; } //define but expect to never be called + CubeFace(CubeFace&&other) noexcept : memoryManager(other.memoryManager), mipsPerDepthSlice(other.mipsPerDepthSlice), images(std::move(other.images)){} const auto& GetImage( uint32_t depth, uint32_t mip ) const { return images[depth * mipsPerDepthSlice]; } auto& GetImage( uint32_t depth, uint32_t mip ) { return images[depth * mipsPerDepthSlice + mip]; } + auto& CreateImage( uint32_t depth, uint32_t mip, const VkImageCreateInfo& imageInfo, MemoryUsage usage ) { + auto& image = GetImage( depth, mip ); + image.operator=( std::move( memoryManager.CreateImage( imageInfo, usage ) ) ); + return image; + } + MemoryManager& memoryManager; const uint32_t mipsPerDepthSlice; std::vector> images; }; @@ -790,7 +820,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD cubeFaces.reserve( Faces ); for (uint32_t WhichFace = 0; WhichFace < Faces; ++WhichFace) { - cubeFaces.emplace_back( Depth, MipLevels ); + cubeFaces.emplace_back( memoryManager, Depth, MipLevels ); } // Create and copy mip levels @@ -822,8 +852,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD ImageInfo.extent.height = Height; ImageInfo.extent.depth = 1; - MemoryAllocatedBuffer& faceImage = cubeFaces[WhichFace].GetImage( WhichDepth, WhichMip ); - faceImage.operator=( std::move( memorymanager.CreateImage( ImageInfo, MemoryUsage::CpuToGpu ) ) ); + MemoryAllocatedBuffer& faceImage = cubeFaces[WhichFace].CreateImage( WhichDepth, WhichMip, ImageInfo, MemoryUsage::CpuToGpu ); if (!faceImage) { LOGE( "CreateTextureFromBuffer: Unable to initialize mip %d of face %d", WhichMip + 1, WhichFace + 1 ); @@ -840,7 +869,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD vkGetImageSubresourceLayout( vulkan.m_VulkanDevice, faceImage.GetVkBuffer(), &SubresInfo, &SubResLayout ); { - auto mappedMemory = memorymanager.Map( faceImage ); + auto mappedMemory = memoryManager.Map( faceImage ); if (SubResLayout.rowPitch == Width * formatBytesPerPixel) { memcpy( mappedMemory.data(), pData8, SubResLayout.size ); @@ -856,7 +885,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD pData8 += Width * formatBytesPerPixel; } } - memorymanager.Unmap( faceImage, std::move( mappedMemory ) ); + memoryManager.Unmap( faceImage, std::move( mappedMemory ) ); } // Image barrier for linear image (base) @@ -899,8 +928,11 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD ImageInfo.arrayLayers = Faces; + // Clear value (probably unused) + VkClearValue ClearValue{}; + // Create the 'final' image - auto FinalVmaImage = memorymanager.CreateImage( ImageInfo, MemoryUsage::GpuExclusive ); + auto FinalVmaImage = memoryManager.CreateImage( ImageInfo, MemoryUsage::GpuExclusive ); if (!FinalVmaImage) { LOGE( "CreateTextureFromBuffer: Unable to initialize texture image" ); @@ -993,7 +1025,7 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD vulkan.FinishSetupCommandBuffer( SetupCmdBuffer ); // Create the Image wrapper (takes ownership of FinalVmaImage) - ImageT Image{ std::move(FinalVmaImage) }; + Image Image{ std::move(FinalVmaImage) }; // Need a sampler... SamplerVulkan sampler = CreateSampler( vulkan, SamplerMode, Filter, SamplerBorderColor::TransparentBlackFloat, 0.0f ); @@ -1016,67 +1048,64 @@ TextureT CreateTextureFromBuffer( Vulkan& vulkan, const void* pD return {}; } - // Cleanup - cubeFaces.clear(); - // Set the return values - return { Width, Height, Depth, MipLevels, 0/*firstmip*/, Faces, 0/*firstface*/, Format, FinalLayout, std::move(Image), std::move(sampler), std::move(imageView)}; + return { Width, Height, Depth, MipLevels, 0/*firstmip*/, Faces, 0/*firstface*/, Format, FinalLayout, ClearValue, std::move(Image), std::move(sampler), std::move(imageView)}; } //----------------------------------------------------------------------------- template<> -void ReleaseTexture( Vulkan& vulkan, TextureT* pTexture ) +void ReleaseTexture( Vulkan& vulkan, Texture* pTexture ) //----------------------------------------------------------------------------- { if (!pTexture) return; pTexture->Release( &vulkan ); - *pTexture = TextureT{}; // destroy and clear + *pTexture = Texture{}; // destroy and clear } //----------------------------------------------------------------------------- -ImageT::ImageT( MemoryAllocatedBuffer vmaImage ) noexcept : VmaImage( std::move(vmaImage) ) +Image::Image( MemoryAllocatedBuffer vmaImage ) noexcept : m_VmaImage( std::move(vmaImage) ) //----------------------------------------------------------------------------- {} //----------------------------------------------------------------------------- -ImageT::ImageT( VkImage image, VkDeviceMemory memory ) noexcept : VmaImage(), Image(image)/*no change of ownership*/, Memory(memory)/*no change of ownership*/ +Image::Image( VkImage image, VkDeviceMemory memory ) noexcept : m_VmaImage(), m_Image(image)/*no change of ownership*/, m_Memory(memory)/*no change of ownership*/ //----------------------------------------------------------------------------- {} //----------------------------------------------------------------------------- -ImageT::~ImageT() +Image::~Image() //----------------------------------------------------------------------------- { - assert( !VmaImage ); // expecting ReleaseImage to have been called already (and so this is empty) - Image = VK_NULL_HANDLE; - Memory = VK_NULL_HANDLE; + assert( !m_VmaImage ); // expecting ReleaseImage to have been called already (and so this is empty) + m_Image = VK_NULL_HANDLE; + m_Memory = VK_NULL_HANDLE; } //----------------------------------------------------------------------------- template<> -void ReleaseImage( Vulkan& vulkan, ImageT* pImage ) +void ReleaseImage( Vulkan& vulkan, Image* pImage ) //----------------------------------------------------------------------------- { - if (pImage->VmaImage) - vulkan.GetMemoryManager().Destroy( std::move( pImage->VmaImage ) ); + if (pImage->m_VmaImage) + vulkan.GetMemoryManager().Destroy( std::move( pImage->m_VmaImage ) ); // We dont own Image or Memory, so just clear them! - pImage->Image = VK_NULL_HANDLE; - pImage->Memory = VK_NULL_HANDLE; + pImage->m_Image = VK_NULL_HANDLE; + pImage->m_Memory = VK_NULL_HANDLE; - *pImage = ImageT{}; // destroy and clear + *pImage = Image{}; // destroy and clear } //----------------------------------------------------------------------------- -ImageViewT::ImageViewT( VkImageView imageView, VkImageViewType viewType ) noexcept : m_ImageView( imageView ), m_ImageViewType( VkToEnum(viewType) ) +ImageView::ImageView( VkImageView imageView, VkImageViewType viewType ) noexcept : m_ImageView( imageView ), m_ImageViewType( VkToEnum(viewType) ) //----------------------------------------------------------------------------- {} //----------------------------------------------------------------------------- -ImageViewT::~ImageViewT() +ImageView::~ImageView() //----------------------------------------------------------------------------- { assert( m_ImageView == VK_NULL_HANDLE ); @@ -1085,9 +1114,11 @@ ImageViewT::~ImageViewT() //----------------------------------------------------------------------------- /// Create a vulkan image view object template<> -ImageViewT CreateImageView(Vulkan& vulkan, const ImageT& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t baseFace, ImageViewType viewType ) +ImageView CreateImageView(Vulkan& vulkan, const Image& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t baseFace, ImageViewType viewType ) //----------------------------------------------------------------------------- { + const bool hasDepth = FormatHasDepth( format ); + const bool hasStencil = FormatHasStencil( format ); VkImageViewCreateInfo ImageViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; ImageViewInfo.flags = 0; ImageViewInfo.image = image.GetVkImage(); @@ -1098,15 +1129,20 @@ ImageViewT CreateImageView(Vulkan& vulkan, const ImageT& ImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; ImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; ImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + if (hasDepth) + ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + else if (hasStencil)//only depth OR stencil OR color should be set! + ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + else + ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ImageViewInfo.subresourceRange.baseMipLevel = baseMipLevel; ImageViewInfo.subresourceRange.levelCount = numMipLevels; ImageViewInfo.subresourceRange.baseArrayLayer = baseFace; ImageViewInfo.subresourceRange.layerCount = numFaces; VkImageView imageView; - auto RetVal = vkCreateImageView(vulkan.m_VulkanDevice, &ImageViewInfo, NULL, &imageView); - if (!CheckVkError("vkCreateImageView()", RetVal)) + auto retVal = vkCreateImageView(vulkan.m_VulkanDevice, &ImageViewInfo, NULL, &imageView); + if (!CheckVkError("vkCreateImageView()", retVal)) { return {}; } @@ -1115,7 +1151,7 @@ ImageViewT CreateImageView(Vulkan& vulkan, const ImageT& //----------------------------------------------------------------------------- template<> -void ReleaseImageView( Vulkan& vulkan, ImageViewT* pImageView ) +void ReleaseImageView( Vulkan& vulkan, ImageView* pImageView ) //----------------------------------------------------------------------------- { if (!pImageView || pImageView->IsEmpty()) @@ -1125,77 +1161,8 @@ void ReleaseImageView( Vulkan& vulkan, ImageViewT* pImageView ) pImageView->m_ImageViewType = ImageViewType::View1D; } -// -// Constructors/move-operators for SamplerT. -// -SamplerT::SamplerT() noexcept : m_Sampler( VK_NULL_HANDLE ) -{} -SamplerT::~SamplerT() noexcept -{ -} -SamplerT::SamplerT(VkSampler sampler) noexcept : m_Sampler( sampler ) -{} -SamplerT::SamplerT(SamplerT&& src) noexcept -{ - *this = std::move( src ); -} -SamplerT& SamplerT::operator=(SamplerT&& src ) noexcept -{ - if (this != &src) - { - m_Sampler = src.m_Sampler; - src.m_Sampler = VK_NULL_HANDLE; - } - return *this; -} - - -//----------------------------------------------------------------------------- -template<> -SamplerT CreateSampler(Vulkan& vulkan, const CreateSamplerObjectInfo& createInfo) -//----------------------------------------------------------------------------- -{ - const bool anisotropyEnable = createInfo.Anisotropy > 1.0f && createInfo.Filter == SamplerFilter::Linear; - const VkSamplerCreateInfo SamplerInfo{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .flags = 0, - .magFilter = EnumToVk(createInfo.Filter), - .minFilter = EnumToVk(createInfo.Filter), - .mipmapMode = EnumToVkSamplerMipmapMode(createInfo.MipFilter), - .addressModeU = EnumToVk(createInfo.Mode), - .addressModeV = EnumToVk(createInfo.Mode), - .addressModeW = EnumToVk(createInfo.Mode), - .mipLodBias = createInfo.MipBias, - .anisotropyEnable = anisotropyEnable ? VK_TRUE : VK_FALSE, - .maxAnisotropy = anisotropyEnable == VK_TRUE ? createInfo.Anisotropy : 1.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = createInfo.MinLod, - .maxLod = createInfo.MaxLod, - .borderColor = EnumToVk(createInfo.BorderColor), - .unnormalizedCoordinates = createInfo.UnnormalizedCoordinates ? VK_TRUE : VK_FALSE }; - - VkSampler vkSampler; - VkResult RetVal = vkCreateSampler(vulkan.m_VulkanDevice, &SamplerInfo, NULL, &vkSampler); - if (!CheckVkError("vkCreateSampler()", RetVal)) - return {}; - return { vkSampler }; -} - -//----------------------------------------------------------------------------- -// Implementation of template function specialization -template<> -void ReleaseSampler( Vulkan& vulkan, SamplerT* pSampler) -//----------------------------------------------------------------------------- -{ - if (!pSampler) - return; - vkDestroySampler(vulkan.m_VulkanDevice, pSampler->m_Sampler, nullptr); - *pSampler = SamplerT{}; // destroy and clear -} - //----------------------------------------------------------------------------- -std::vector> MakeTextureMipViews( Vulkan& vulkan, const TextureT& source, uint32_t maxMips ) +std::vector> MakeTextureMipViews( Vulkan& vulkan, const Texture& source, uint32_t maxMips ) //----------------------------------------------------------------------------- { TextureVulkan m_ReducedZBuffer; ///< half sized 'depth' buffer (half in each dimension of original ZBuffer size) with mips. Populated by the reduce. @@ -1215,8 +1182,8 @@ std::vector> MakeTextureMipViews( Vulkan& vulkan, const Texture samplerCreateInfo.MaxLod = float( mipLevel ); auto imageView = CreateImageView( vulkan, source.Image, source.Format, 1, mipLevel, source.Faces, 0, source.ImageView.GetImageViewType() ); - SamplerT sampler = CreateSampler( vulkan, samplerCreateInfo ); - mips.emplace_back( source.Width, source.Height, source.Depth, 1, mipLevel, 1, 0, source.Format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, source.GetVkImage(), (VkDeviceMemory)VK_NULL_HANDLE, std::move( sampler ), std::move( imageView ) ); + Sampler sampler = CreateSampler( vulkan, samplerCreateInfo ); + mips.emplace_back( source.Width, source.Height, source.Depth, 1, mipLevel, 1, 0, source.Format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, source.GetVkClearValue(), source.GetVkImage(), (VkDeviceMemory)VK_NULL_HANDLE, std::move(sampler), std::move(imageView)); } return mips; } diff --git a/framework/code/texture/vulkan/texture.hpp b/framework/code/texture/vulkan/texture.hpp index e155380..6813b14 100644 --- a/framework/code/texture/vulkan/texture.hpp +++ b/framework/code/texture/vulkan/texture.hpp @@ -1,79 +1,59 @@ -//============================================================================================================ +//============================================================================= // +// Copyright (c) 2022 QUALCOMM Technologies Inc. +// All Rights Reserved. // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ +//============================================================================== #pragma once #include "../texture.hpp" +#include "sampler.hpp" #include "memory/vulkan/memoryMapped.hpp" -#include +#include "vulkan/refHandle.hpp" +#include #include // Forward declarations class Vulkan; template class MemoryAllocatedBuffer; -using TextureVulkan = TextureT; -using ImageVulkan = ImageT; -using ImageViewVulkan = ImageViewT; -using SamplerVulkan = SamplerT; +template class MemoryPool; +using TextureVulkan = Texture; +using ImageVulkan = Image; +using ImageViewVulkan = ImageView; +using SamplerVulkan = Sampler; /// @brief Convert from our TextureFormat to Vulkan's VkFormat extern VkFormat TextureFormatToVk(TextureFormat); -/// @brief Convert from our Vulkan's VkFormat to our TextureFormat. +/// @brief Convert from Vulkan's VkFormat to our TextureFormat. extern TextureFormat VkToTextureFormat(VkFormat); +/// @brief Convert from our MSaa enum to Vulkan's VkSampleCountFlagBits +extern VkSampleCountFlagBits EnumToVk(Msaa msaa); -/// @brief Template specialization of sampler container for Vulkan graphics api. -template<> -class SamplerT final : public Sampler -{ -public: - SamplerT() noexcept; - ~SamplerT() noexcept; - SamplerT(VkSampler sampler) noexcept; - SamplerT(SamplerT&& src) noexcept; - SamplerT& operator=(SamplerT&& src) noexcept; - SamplerT Copy() const noexcept - { - return {m_Sampler}; - } - - auto GetVkSampler() const { return m_Sampler; } - bool IsEmpty() const { return m_Sampler == VK_NULL_HANDLE; } - -private: - friend void ReleaseSampler( Vulkan& vulkan, SamplerT* pSampler ); - VkSampler m_Sampler; -}; - - -/// @brief Template specialization of sampler container for Vulkan graphics api. +/// @brief Template specialization of imageview container for Vulkan graphics api. template<> -class ImageViewT final : public ImageView +class ImageView final : public ImageViewBase { public: - ImageViewT() noexcept : m_ImageView( VK_NULL_HANDLE ), m_ImageViewType( ImageViewType::View1D ) {}; - ImageViewT( VkImageView imageView, VkImageViewType viewType ) noexcept; - ImageViewT( ImageViewT&& src ) noexcept { + ImageView() noexcept : m_ImageView( VK_NULL_HANDLE ), m_ImageViewType( ImageViewType::View1D ) {}; + ImageView( VkImageView imageView, VkImageViewType viewType ) noexcept; + ImageView( ImageView&& src ) noexcept { *this = std::move( src ); } - ImageViewT& operator=( ImageViewT&& src ) noexcept { + ImageView& operator=( ImageView&& src ) noexcept { m_ImageView = src.m_ImageView; src.m_ImageView = VK_NULL_HANDLE; m_ImageViewType = src.m_ImageViewType; src.m_ImageViewType = ImageViewType::View1D; return *this; } - ~ImageViewT(); + ~ImageView(); auto GetImageViewType() const { return m_ImageViewType; } auto GetVkImageView() const { return m_ImageView; } bool IsEmpty() const { return m_ImageView == VK_NULL_HANDLE; } private: - friend void ReleaseImageView( Vulkan& vulkan, ImageViewT* pImageView ); + friend void ReleaseImageView( Vulkan& vulkan, ImageView* pImageView ); VkImageView m_ImageView; ImageViewType m_ImageViewType; }; @@ -81,71 +61,76 @@ class ImageViewT final : public ImageView /// @brief Template specialization of image container for Vulkan graphics api. template<> -class ImageT final : public Image +class Image final : public ImageBase { public: - ImageT() noexcept {}; + Image() noexcept {}; - /// @brief Construct ImageT from a pre-existing vmaImage. - /// @param vmaImage - ownership passed to this ImageT. - ImageT( MemoryAllocatedBuffer vmaImage ) noexcept; + /// @brief Construct Image from a pre-existing vmaImage. + /// @param vmaImage - ownership passed to this Image. + Image( MemoryAllocatedBuffer vmaImage ) noexcept; - /// @brief Construct ImageT from a pre-existing Vulkan image/memory handles. - /// @param image - ownership NOT passed in to this ImageT, beware of lifetime issues. - /// @param memory - ownership NOT passed to this ImageT, beware of lifetime issues. - ImageT( VkImage image, VkDeviceMemory memory ) noexcept; + /// @brief Construct Image from a pre-existing Vulkan image/memory handles. + /// @param image - ownership NOT passed in to this Image, beware of lifetime issues. + /// @param memory - ownership NOT passed to this Image, beware of lifetime issues. + Image( VkImage image, VkDeviceMemory memory ) noexcept; - ImageT( ImageT&& src ) noexcept { + Image( Image&& src ) noexcept { *this = std::move( src ); } - ImageT& operator=( ImageT&& src ) noexcept + Image& operator=( Image&& src ) noexcept { - VmaImage = std::move(src.VmaImage); - Image = src.Image; - src.Image = VK_NULL_HANDLE; - Memory = src.Memory; - src.Memory = VK_NULL_HANDLE; + m_VmaImage = std::move(src.m_VmaImage ); + m_Image = src.m_Image; + src.m_Image = VK_NULL_HANDLE; + m_Memory = src.m_Memory; + src.m_Memory = VK_NULL_HANDLE; return *this; } - ~ImageT(); - VkImage GetVkImage() const { return VmaImage ? VmaImage.GetVkBuffer() : Image; } - bool IsEmpty() const { return !VmaImage && Image == VK_NULL_HANDLE; } + ~Image(); + VkImage GetVkImage() const { return m_VmaImage ? m_VmaImage.GetVkBuffer() : m_Image; } + const auto& GetMemoryAllocation() const { return m_VmaImage.GetMemoryAllocation(); } + bool IsEmpty() const { return !m_VmaImage && m_Image == VK_NULL_HANDLE; } + + //Image Copy(); private: - friend void ReleaseImage( Vulkan& vulkan, ImageT* pImage ); + friend void ReleaseImage( Vulkan& vulkan, Image* pImage ); // Managed memory buffer allocation and VkImage - MemoryAllocatedBuffer VmaImage; + MemoryAllocatedBuffer m_VmaImage; // Needed for functions handling own memory (i.e. AndroidHardwareBuffers) - VkImage Image = VK_NULL_HANDLE; - VkDeviceMemory Memory = VK_NULL_HANDLE; + VkImage m_Image = VK_NULL_HANDLE; + VkDeviceMemory m_Memory = VK_NULL_HANDLE; }; /// @brief Template specialization of texture container for Vulkan graphics api. template<> -class TextureT final : public Texture +class Texture final : public TextureBase { public: - TextureT() noexcept; - TextureT(const TextureT&) = delete; - TextureT& operator=(const TextureT&) = delete; - TextureT(TextureT&&) noexcept; - TextureT& operator=(TextureT&&) noexcept; - ~TextureT() noexcept; - - /// @brief Construct TextureT from a pre-existing vmaImage. - /// @param vmaImage - ownership passed to this TextureT. - /// @param sampler - ownership passed to this TextureT. - /// @param imageView - ownership passed to this TextureT. - TextureT(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat, VkImageLayout imageLayout, ImageT image, SamplerT sampler, ImageViewT imageView) noexcept; - - /// @brief Construct TextureT from a pre-existing Vulkan image/memory handles. - /// @param image - ownership NOT passed in to this TextureT, beware of lifetime issues. - /// @param sampler - ownership passed to this TextureT. - /// @param imageView - ownership passed to this TextureT. - TextureT(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, VkImage image, VkDeviceMemory memory, SamplerT sampler, ImageViewT imageView) noexcept; + Texture() noexcept; + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + Texture(Texture&&) noexcept; + Texture& operator=(Texture&&) noexcept; + ~Texture() noexcept; + + operator bool() const { return !IsEmpty(); } + + /// @brief Construct Texture from a pre-existing vmaImage. + /// @param vmaImage - ownership passed to this Texture. + /// @param sampler - ownership passed to this Texture. + /// @param imageView - ownership passed to this Texture. + Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat, VkImageLayout imageLayout, VkClearValue clearValue, ::Image image, ::Sampler sampler, ::ImageView imageView) noexcept; + + /// @brief Construct Texture from a pre-existing Vulkan image/memory handles. + /// @param image - ownership NOT passed in to this Texture, beware of lifetime issues. + /// @param sampler - ownership passed to this Texture. + /// @param imageView - ownership passed to this Texture. + Texture(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t firstMip, uint32_t faces, uint32_t firstFace, TextureFormat format, VkImageLayout imageLayout, VkClearValue clearValue, VkImage image, VkDeviceMemory memory, Sampler sampler, ::ImageView imageView) noexcept; void Release(GraphicsApiBase* pGraphicsApi) override; @@ -154,6 +139,7 @@ class TextureT final : public Texture VkImageLayout GetVkImageLayout() const { return ImageLayout; } VkSampler GetVkSampler() const { return Sampler.GetVkSampler(); } VkImageView GetVkImageView() const { return ImageView.GetVkImageView(); } + VkClearValue GetVkClearValue() const { return ClearValue; } bool IsEmpty() const { return Image.IsEmpty() || Sampler.IsEmpty(); } uint32_t Width = 0; @@ -165,49 +151,47 @@ class TextureT final : public Texture uint32_t FirstFace = 0; TextureFormat Format = TextureFormat::UNDEFINED; VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkClearValue ClearValue {}; public: - ImageT Image; - SamplerT Sampler; - ImageViewT ImageView; + Image Image; + Sampler Sampler; + ImageView ImageView; }; /// Template specialization for Vulkan CreateTextureObject template<> -TextureT CreateTextureObject( Vulkan&, const CreateTexObjectInfo& texInfo ); +Texture CreateTextureObject( Vulkan&, const CreateTexObjectInfo& texInfo, MemoryPool* pPool); /// Template specialization for Vulkan CreateTextureFromBuffer template<> -TextureT CreateTextureFromBuffer( Vulkan&, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ); +Texture CreateTextureFromBuffer( Vulkan&, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ); /// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) -TextureT CreateTextureObjectView( Vulkan&, const TextureT& original, TextureFormat viewFormat ); +template<> +Texture CreateTextureObjectView( Vulkan&, const Texture& original, TextureFormat viewFormat ); /// Template specialization for Vulkan ReleaseTexture template<> -void ReleaseTexture( Vulkan& vulkan, TextureT* ); +void ReleaseTexture( Vulkan& vulkan, Texture* ); /// Template specialization for Vulkan ReleaseImage template<> -void ReleaseImage( Vulkan& vulkan, ImageT* ); +void ReleaseImage( Vulkan& vulkan, Image* ); /// Template specialization for Vulkan CreateImageView template<> -ImageViewT CreateImageView( Vulkan&, const ImageT& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t firstFace, ImageViewType viewType ); +ImageView CreateImageView( Vulkan&, const Image& image, TextureFormat format, uint32_t numMipLevels, uint32_t baseMipLevel, uint32_t numFaces, uint32_t firstFace, ImageViewType viewType ); /// Template specialization for Vulkan ReleaseImageView template<> -void ReleaseImageView( Vulkan& vulkan, ImageViewT* ); +void ReleaseImageView( Vulkan& vulkan, ImageView* ); /// Template specialization for Vulkan CreateSampler template<> -SamplerT CreateSampler( Vulkan&, const CreateSamplerObjectInfo& ); - -/// Template specialization for Vulkan ReleaseSampler -template<> -void ReleaseSampler( Vulkan& vulkan, SamplerT* ); +Sampler CreateSampler( Vulkan&, const CreateSamplerObjectInfo& ); /// Helper to take a sorce texture and make an array of textures where each one points to a single mip in the source -std::vector> MakeTextureMipViews( Vulkan& vulkan, const TextureT& source, uint32_t maxMips ); +std::vector> MakeTextureMipViews( Vulkan& vulkan, const Texture& source, uint32_t maxMips ); diff --git a/framework/code/texture/vulkan/textureManager.cpp b/framework/code/texture/vulkan/textureManager.cpp index 0c98f3b..f8618c2 100644 --- a/framework/code/texture/vulkan/textureManager.cpp +++ b/framework/code/texture/vulkan/textureManager.cpp @@ -1,12 +1,11 @@ //============================================================================= // -// Copyright (c) 2023 QUALCOMM Technologies Inc. +// Copyright (c) 2022 QUALCOMM Technologies Inc. // All Rights Reserved. // //============================================================================== #include "textureManager.hpp" #include "texture.hpp" -#include "imageWrapper.hpp" #include "loaderKtx.hpp" #include "../loaderPpm.hpp" #include "vulkan/vulkan.hpp" @@ -14,23 +13,25 @@ //----------------------------------------------------------------------------- -TextureManagerT::TextureManagerT( tGfxApi& rGfxApi ) noexcept : TextureManager(), m_GfxApi( rGfxApi ) +TextureManager::TextureManager( Vulkan& rGfxApi, AssetManager& rAssetManager ) noexcept + : TextureManagerBase(rAssetManager) + , m_GfxApi( rGfxApi ) //----------------------------------------------------------------------------- { } //----------------------------------------------------------------------------- -TextureManagerT::~TextureManagerT() +TextureManager::~TextureManager() //----------------------------------------------------------------------------- { Release(); } //----------------------------------------------------------------------------- -bool TextureManagerT::Initialize() +bool TextureManager::Initialize() //----------------------------------------------------------------------------- { - m_Loader = std::make_unique>( static_cast(m_GfxApi) ); + m_Loader = std::make_unique>( static_cast(m_GfxApi) ); m_DefaultSamplers.reserve( size_t(SamplerAddressMode::End) ); for (size_t i = (size_t)SamplerAddressMode::Repeat; i < (size_t)SamplerAddressMode::End; ++i) @@ -42,11 +43,11 @@ bool TextureManagerT::Initialize() m_DefaultSamplers.push_back( {} ); } - return TextureManager::Initialize(); + return TextureManagerBase::Initialize(0/*as many threads as cores*/); } //----------------------------------------------------------------------------- -void TextureManagerT::Release() +void TextureManager::Release() //----------------------------------------------------------------------------- { for (auto& [key, texture] : m_LoadedTextures) @@ -55,11 +56,11 @@ void TextureManagerT::Release() } m_LoadedTextures.clear(); m_Loader->Release(); - TextureManager::Release(); + TextureManagerBase::Release(); } //----------------------------------------------------------------------------- -const Texture* TextureManagerT::GetTexture(const std::string& textureSlotName) const +const TextureBase* TextureManager::GetTexture(const std::string& textureSlotName) const //----------------------------------------------------------------------------- { auto iter = m_LoadedTextures.find(textureSlotName); @@ -69,21 +70,21 @@ const Texture* TextureManagerT::GetTexture(const std::string& textureSlo } //----------------------------------------------------------------------------- -const Sampler* const TextureManagerT::GetSampler( SamplerAddressMode sam ) const +const SamplerBase* const TextureManager::GetSampler( SamplerAddressMode sam ) const //----------------------------------------------------------------------------- { return &m_DefaultSamplers[size_t(sam)]; } //----------------------------------------------------------------------------- -const Texture* TextureManagerT::GetOrLoadTexture_(const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const Sampler& sampler) +const TextureBase* TextureManager::GetOrLoadTexture_(const std::string& textureSlotName, const std::string& filename, const SamplerBase& sampler) //----------------------------------------------------------------------------- { - const Texture* pTexture = GetTexture(textureSlotName); + const TextureBase* pTexture = GetTexture(textureSlotName); if (!pTexture) { const SamplerVulkan& samplerVulkan = static_cast(sampler); - auto loadedTexture = GetLoader()->LoadKtx(m_GfxApi, rAssetManager, filename.c_str(), std::move(samplerVulkan.Copy())); + auto loadedTexture = GetLoader()->LoadKtx(m_GfxApi, m_AssetManager, filename.c_str(), std::move(samplerVulkan.Copy())); if (!loadedTexture.IsEmpty()) { auto insertedIt = m_LoadedTextures.insert({ textureSlotName, std::move(loadedTexture) }); @@ -105,7 +106,7 @@ struct BatchLoadThreadParams { }; //----------------------------------------------------------------------------- -void TextureManagerT::BatchLoad(AssetManager& rAssetManager, const std::span> slotAndFileNames, const Sampler& defaultSampler) +void TextureManager::BatchLoad(const std::span> slotAndFileNames, const SamplerBase& defaultSampler) //----------------------------------------------------------------------------- { std::mutex loadedFileQueueMutex; @@ -114,7 +115,7 @@ void TextureManagerT::BatchLoad(AssetManager& rAssetManager, const std:: Semaphore finishedSema{ 0 }; // Setup the output textures - std::vector> vulkanTextures; + std::vector vulkanTextures; vulkanTextures.resize(slotAndFileNames.size()); // We have one worker job just grabbing loaded textures and transfering them to vulkan (gpu memory). @@ -124,7 +125,7 @@ void TextureManagerT::BatchLoad(AssetManager& rAssetManager, const std:: Semaphore& dataReadySema; Semaphore& finishedSema; const SamplerVulkan& defaultSampler; - std::vector>& vulkanTextures; + std::vector& vulkanTextures; } transferWorkerParams{ loadedFileQueueMutex, loadedFileQueue, dataReadySema, finishedSema, apiCast(defaultSampler), vulkanTextures }; m_LoadingThreadWorker.DoWork2([](TextureManagerVulkan* pThis, TransferWorkerParams params) @@ -156,12 +157,12 @@ void TextureManagerT::BatchLoad(AssetManager& rAssetManager, const std:: auto iter = m_LoadedTextures.find(textureSlotName); if (iter == m_LoadedTextures.end()) { - BatchLoadThreadParams params{ rAssetManager, loadedFileQueueMutex, loadedFileQueue, dataReadySema, filename, currentSlotIndex }; + BatchLoadThreadParams params{ m_AssetManager, loadedFileQueueMutex, loadedFileQueue, dataReadySema, filename, currentSlotIndex }; m_LoadingThreadWorker.DoWork2([](TextureManagerVulkan* pThis, BatchLoadThreadParams params) { auto ktxData = pThis->m_Loader->LoadFile(params.assetManager, params.filename.c_str()); - auto* pKtxLoader = static_cast*>(pThis->GetLoader()); + auto* pKtxLoader = static_cast*>(pThis->GetLoader()); ktxData = pKtxLoader->Transcode(std::move(ktxData)); { std::lock_guard lock(params.loadedFileQueueMutex); @@ -186,34 +187,58 @@ void TextureManagerT::BatchLoad(AssetManager& rAssetManager, const std:: { if (!vulkanTextures[i].IsEmpty()) { - m_LoadedTextures.emplace(std::pair>{ slotAndFileNames[i].first/*slot*/, std::move(vulkanTextures[i])}); + m_LoadedTextures.emplace(std::pair{ slotAndFileNames[i].first/*slot*/, std::move(vulkanTextures[i])}); } } } //----------------------------------------------------------------------------- -std::unique_ptr TextureManagerT::CreateTextureObject(GraphicsApiBase& gfxApi, const CreateTexObjectInfo& texInfo) /*override*/ +const TextureBase* TextureManager::CreateTextureObject( const CreateTexObjectInfo& texInfo) /*override*/ //----------------------------------------------------------------------------- { - auto pTexture = std::make_unique>(); - *pTexture = ::CreateTextureObject(static_cast(gfxApi), texInfo); - return pTexture; + auto texture = ::CreateTextureObject( m_GfxApi, texInfo); + + assert( texInfo.pName!=nullptr && texInfo.pName[0] != '\0' ); // must have a valid name + auto it = m_LoadedTextures.try_emplace( texInfo.pName, std::move( texture ) ); + if (!it.second) + { + assert( 0 && "CreateTextureObjectView duplicate texture name, must be unique (or use ::CreateTextureObject)" ); + return nullptr; + } + else + return &it.first->second; } //----------------------------------------------------------------------------- -std::unique_ptr TextureManagerT::CreateTextureFromBuffer( GraphicsApiBase& gfxApi, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ) +const TextureBase* TextureManager::CreateTextureFromBuffer( const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, std::string name ) //----------------------------------------------------------------------------- { - auto pTexture = std::make_unique>(); - *pTexture = ::CreateTextureFromBuffer( static_cast( gfxApi ), pData, DataSize, Width, Height, Depth, Format, SamplerMode, Filter, pName ); - return pTexture; + auto texture = ::CreateTextureFromBuffer( m_GfxApi, pData, DataSize, Width, Height, Depth, Format, SamplerMode, Filter, name.c_str() ); + + assert( name.empty() ); // must have a valid name + auto it = m_LoadedTextures.try_emplace( name, std::move(texture) ); + if (!it.second) + { + assert( 0 && "CreateTextureFromBuffer duplicate texture name, must be unique (or use ::CreateTextureFromBuffer)" ); + return nullptr; + } + else + return &it.first->second; } //----------------------------------------------------------------------------- -std::unique_ptr TextureManagerT::CreateTextureObjectView( GraphicsApiBase& gfxApi, const Texture& original, TextureFormat viewFormat ) +const TextureBase* TextureManager::CreateTextureObjectView( const TextureBase& original, TextureFormat viewFormat, std::string name ) //----------------------------------------------------------------------------- { - auto pTexture = std::make_unique>(); - *pTexture = ::CreateTextureObjectView( static_cast( gfxApi ), apiCast(original), viewFormat ); - return pTexture; + auto texture = ::CreateTextureObjectView( m_GfxApi, apiCast(original), viewFormat ); + + assert( name.empty() ); // must have a valid name + auto it = m_LoadedTextures.try_emplace( name, std::move( texture ) ); + if (!it.second) + { + assert( 0 && "CreateTextureObjectView duplicate texture name, must be unique (or use ::CreateTextureObjectView)" ); + return nullptr; + } + else + return &it.first->second; } diff --git a/framework/code/texture/vulkan/textureManager.hpp b/framework/code/texture/vulkan/textureManager.hpp index 9050894..62b2ba6 100644 --- a/framework/code/texture/vulkan/textureManager.hpp +++ b/framework/code/texture/vulkan/textureManager.hpp @@ -1,6 +1,6 @@ //============================================================================= // -// Copyright (c) 2023 QUALCOMM Technologies Inc. +// Copyright (c) 2022 QUALCOMM Technologies Inc. // All Rights Reserved. // //============================================================================== @@ -13,60 +13,59 @@ class Vulkan; template class LoaderKtxT; -template class TextureT; -class TextureKtx; -template class TextureKtxT; -template class TexturePpmT; -template class TextureManagerT; -template class SamplerT; -using TextureManagerVulkan = TextureManagerT; +template class Sampler; +template class Texture; +class TextureKtxBase; +template class TextureManager; +template class TexturePpm; +using TextureManagerVulkan = TextureManager; /// @brief Templated implementation of TextureManager that handles ktx file loading for Vulkan graphics api. /// @tparam T_GFXAPI template<> -class TextureManagerT final : public TextureManager +class TextureManager final : public TextureManagerBase { public: using tGfxApi = Vulkan; + using Texture = Texture; + using Sampler = Sampler; - TextureManagerT(tGfxApi& rGfxApi) noexcept; - virtual ~TextureManagerT() override; - TextureManagerT& operator=(const TextureManagerT&) = delete; - TextureManagerT(const TextureManagerT&) = delete; + TextureManager(tGfxApi& rGfxApi, AssetManager&) noexcept; + virtual ~TextureManager() override; + TextureManager& operator=(const TextureManager&) = delete; + TextureManager(const TextureManager&) = delete; bool Initialize(); void Release() override; - /// @brief Helper to return the ktx file loader directly. If this throws a compile error then .cpp that includes this file is missing #include "texture/[gfxapi]/loaderKtx.hpp" - TextureKtx* GetLoader() const { return m_Loader.get(); } - TexturePpm* GetLoaderPpm() const { return m_LoaderPpm.get(); } - - const Texture* GetTexture(const std::string& textureSlotName) const override; + /// @brief Find a texture (by slot name) that may be already loaded + /// @param textureSlotName name to look for + /// @return pointer to already loaded texture, or null + const TextureBase* GetTexture(const std::string& textureSlotName) const override; /// Create texture (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. /// Implements the base class virtual function. - std::unique_ptr CreateTextureObject(GraphicsApiBase& gfxApi, const CreateTexObjectInfo& texInfo) override; + const TextureBase* CreateTextureObject( const CreateTexObjectInfo& texInfo) override; /// Create texture from a block of texture data in memory (with correct format, span etc). /// Implements the base class virtual function. - std::unique_ptr CreateTextureFromBuffer( GraphicsApiBase&, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, const char* pName ) override; + const TextureBase* CreateTextureFromBuffer( const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, TextureFormat Format, SamplerAddressMode SamplerMode, SamplerFilter Filter, std::string name ) override; /// Create a texture that views (aliases) another texture but using a different texture format (must be 'related' formats, which formats are related is dependant on graphics api) /// Implements the base class virtual function. - std::unique_ptr CreateTextureObjectView( GraphicsApiBase& gfxApi, const Texture& original, TextureFormat viewFormat ) override; + const TextureBase* CreateTextureObjectView( const TextureBase& original, TextureFormat viewFormat, std::string name ) override; /// Get a 'default' sampler for the given address mode (all other sampler settings assumed to be 'normal' ie linearly sampled etc) - const Sampler* const GetSampler( SamplerAddressMode ) const override; + const SamplerBase* const GetSampler( SamplerAddressMode ) const override; protected: - const Texture* GetOrLoadTexture_(const std::string& textureSlotName, AssetManager& rAssetManager, const std::string& filename, const Sampler& sampler) override; - void BatchLoad(AssetManager& rAssetManager, const std::span>, const Sampler& defaultSampler) override; + const TextureBase* GetOrLoadTexture_(const std::string& textureSlotName, const std::string& filename, const SamplerBase& sampler) override; + void BatchLoad(const std::span>, const SamplerBase& defaultSampler) override; private: - std::vector< TextureT> arses; - std::map> m_LoadedTextures; - std::vector< SamplerT> m_DefaultSamplers; - const bool m_MirrorClampToEdgeSupported = false; // currently this is never set, if we add VK_KHR_sampler_mirror_clamp_to_edge extension support or samplerMirrorClampToEdge feature flag then we can change this to non const and set/reset - tGfxApi& m_GfxApi; + std::map m_LoadedTextures; + std::vector m_DefaultSamplers; + const bool m_MirrorClampToEdgeSupported = false; // currently this is never set, if we add VK_KHR_sampler_mirror_clamp_to_edge extension support or samplerMirrorClampToEdge feature flag then we can change this to non const and set/reset + tGfxApi& m_GfxApi; }; diff --git a/framework/code/vulkan/MeshObject.cpp b/framework/code/vulkan/MeshObject.cpp index a5c8269..09906be 100644 --- a/framework/code/vulkan/MeshObject.cpp +++ b/framework/code/vulkan/MeshObject.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/vulkan/MeshObject.h b/framework/code/vulkan/MeshObject.h index 3422803..38b3f2d 100644 --- a/framework/code/vulkan/MeshObject.h +++ b/framework/code/vulkan/MeshObject.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/vulkan/TextureFuncts.cpp b/framework/code/vulkan/TextureFuncts.cpp index 32da45d..b957c91 100644 --- a/framework/code/vulkan/TextureFuncts.cpp +++ b/framework/code/vulkan/TextureFuncts.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,812 +9,14 @@ // TextureFuncts.cpp // Vulkan texture handling support -#include "system/os_common.h" -#include "system/assetManager.hpp" -#include "memory/memoryManager.hpp" -#include "vulkan_support.hpp" -#include "TextureFuncts.h" +#include "texture/textureFormat.hpp" #include "texture/vulkan/texture.hpp" -#include -#include +#include "vulkan/vulkan.hpp" #include #define STB_IMAGE_IMPLEMENTATION #include "tinygltf/stb_image.h" #include "tinygltf/stb_image_write.h" -#if 0 - -// http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ -constexpr std::array KTX_IDENTIFIER_REF = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; -constexpr uint32_t KTX_ENDIAN_REF = 0x04030201; // Big Endian -constexpr uint32_t KTX_ENDIAN_REF_REV = 0x01020304; // Little Endian -constexpr uint32_t KTX_HEADER_SIZE = 64; - -// https://github.khronos.org/KTX-Specification/ -// https://github.com/KhronosGroup/KTX-Software/blob/master/lib/ktxint.h -constexpr std::array KTX2_IDENTIFIER_REF{ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; -#define KTX2_HEADER_SIZE (80) - -#if !defined(FLT_MAX) -#define FLT_MAX 3.402823466e+38f -#endif // !defined(FLT_MAX) - - -//----------------------------------------------------------------------------- -// Implementation data structures -//----------------------------------------------------------------------------- - -typedef struct _KTXHeader -{ - uint8_t identifier[12]; - uint32_t endianness; - uint32_t glType; - uint32_t glTypeSize; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; -} KTXHeader; - -// https://github.khronos.org/KTX-Specification/ -// https://github.com/KhronosGroup/KTX-Software/blob/master/lib/ktxint.h -typedef struct ktxIndexEntry32 { - uint32_t byteOffset; /*!< Offset of item from start of file. */ - uint32_t byteLength; /*!< Number of bytes of data in the item. */ -} ktxIndexEntry32; -/** - * @internal - * @~English - * @brief 64-bit KTX 2 index entry. - */ -typedef struct ktxIndexEntry64 { - uint64_t byteOffset; /*!< Offset of item from start of file. */ - uint64_t byteLength; /*!< Number of bytes of data in the item. */ -} ktxIndexEntry64; - -typedef struct KTX_header2 { - uint8_t identifier[12]; - uint32_t vkFormat; - uint32_t typeSize; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t layerCount; - uint32_t faceCount; - uint32_t levelCount; - uint32_t supercompressionScheme; - ktxIndexEntry32 dataFormatDescriptor; - ktxIndexEntry32 keyValueData; - ktxIndexEntry64 supercompressionGlobalData; -} KTX_header2; - -typedef struct ktxLevelIndexEntry { - uint64_t byteOffset; /*!< Offset of level from start of file. */ - uint64_t byteLength; - /*!< Number of bytes of compressed image data in the level. */ - uint64_t uncompressedByteLength; - /*!< Number of bytes of uncompressed image data in the level. */ -} ktxLevelIndexEntry; - - -// This is not a class. It is up to caller to release any objects in this structure -typedef struct _MipTexData -{ - uint32_t Width; - uint32_t Height; - uint32_t Size; - void* pData; -} MipTexData; - -typedef struct _LayerTexData -{ - uint32_t NumMipLevels; - MipTexData* pMipData; -} LayerTexData; - -typedef struct _FaceTexData -{ - uint32_t NumLayers; - LayerTexData* pLayerData; -} FaceTexData; - -typedef struct _VulkanTexData -{ - uint32_t glType; - uint32_t glFormat; - uint32_t glInternalFormat; - VkFormat VulkanFormat; - - uint32_t NumFaces; - FaceTexData* pFaceData; -} VulkanTexData; - - -//----------------------------------------------------------------------------- -static void L_FreeTexData(VulkanTexData* pTexData) -//----------------------------------------------------------------------------- -{ - if (pTexData == NULL) - return; - - if (pTexData->NumFaces == 0 && pTexData->pFaceData == NULL) - { - // The data structure is already reset - return; - } - - // If any of these next conditions are true the structure is in a horked state! - if (pTexData->NumFaces == 0 || pTexData->pFaceData == NULL) - { - LOGE("Texture data is horked: NumFaces is 0 or FaceData is NULL!"); - return; - } - - if (pTexData->pFaceData->NumLayers == 0 || pTexData->pFaceData->pLayerData == NULL) - { - LOGE("Texture data is horked: NumLayers is 0 or LayerData is NULL!"); - return; - } - - if (pTexData->pFaceData->pLayerData->NumMipLevels == 0 || pTexData->pFaceData->pLayerData->pMipData == NULL) - { - LOGE("Texture data is horked: NumMipLevels is 0 or MipData is NULL!"); - return; - } - - // Each texture has faces... - for (uint32_t WhichFace = 0; WhichFace < pTexData->NumFaces; WhichFace++) - { - // ...each face has layers... - for (uint32_t WhichLayer = 0; WhichLayer < pTexData->pFaceData[WhichFace].NumLayers; WhichLayer++) - { - // ...each layer has mip levels - for (uint32_t WhichMipLevel = 0; WhichMipLevel < pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels; WhichMipLevel++) - { - free(pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData); - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData = NULL; - } // Which MipLevel - - free(pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData); - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels = 0; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData = NULL; - } // Which Layer - - free(pTexData->pFaceData[WhichFace].pLayerData); - pTexData->pFaceData[WhichFace].NumLayers = 0;; - pTexData->pFaceData[WhichFace].pLayerData = NULL; - } // Which Face - - free(pTexData->pFaceData); - pTexData->NumFaces = 0; - pTexData->pFaceData = NULL; -} - -static const std::map CompressedGlFormatToVkFormat = { - {0x83F0/*GL_COMPRESSED_RGB_S3TC_DXT1_EXT*/, VK_FORMAT_BC1_RGB_UNORM_BLOCK}, - {0x83F1/*GL_COMPRESSED_RGBA_S3TC_DXT1_EXT*/, VK_FORMAT_BC1_RGBA_UNORM_BLOCK}, - {0x83F2/*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT*/, VK_FORMAT_BC2_UNORM_BLOCK}, - {0x83F3/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/, VK_FORMAT_BC3_UNORM_BLOCK}, - {0x8C4C/*GL_COMPRESSED_SRGB_S3TC_DXT1_EXT*/, VK_FORMAT_BC1_RGB_SRGB_BLOCK }, - {0x8C4D/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT*/, VK_FORMAT_BC1_RGBA_SRGB_BLOCK }, - {0x8C4E/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT*/, VK_FORMAT_BC2_SRGB_BLOCK}, - {0x8C4F/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT*/, VK_FORMAT_BC3_SRGB_BLOCK}, - - {0x9274/*GL_COMPRESSED_RGB8_ETC2*/, VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK}, - {0x9275/*GL_COMPRESSED_SRGB8_ETC2*/, VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK}, - {0x9276/*GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2*/, VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK }, - {0x9277/*GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2*/, VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK }, - {0x9278/*GL_COMPRESSED_RGBA8_ETC2_EAC*/, VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK}, - {0x9279/*GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC*/, VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK}, - - {0x93B0/*GL_COMPRESSED_RGBA_ASTC_4x4_KHR*/, VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, - {0x93B1/*GL_COMPRESSED_RGBA_ASTC_5x4_KHR*/, VK_FORMAT_ASTC_5x4_UNORM_BLOCK}, - {0x93B2/*GL_COMPRESSED_RGBA_ASTC_5x5_KHR*/, VK_FORMAT_ASTC_5x5_UNORM_BLOCK}, - {0x93B3/*GL_COMPRESSED_RGBA_ASTC_6x5_KHR*/, VK_FORMAT_ASTC_6x5_UNORM_BLOCK}, - {0x93B4/*GL_COMPRESSED_RGBA_ASTC_6x6_KHR*/, VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, - {0x93B5/*GL_COMPRESSED_RGBA_ASTC_8x5_KHR*/, VK_FORMAT_ASTC_8x5_UNORM_BLOCK}, - {0x93B6/*GL_COMPRESSED_RGBA_ASTC_8x6_KHR*/, VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, - {0x93B7/*GL_COMPRESSED_RGBA_ASTC_8x8_KHR*/, VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, - {0x93B8/*GL_COMPRESSED_RGBA_ASTC_10x5_KHR*/, VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, - {0x93B9/*GL_COMPRESSED_RGBA_ASTC_10x6_KHR*/, VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, - {0x93BA/*GL_COMPRESSED_RGBA_ASTC_10x8_KHR*/, VK_FORMAT_ASTC_10x8_UNORM_BLOCK}, - {0x93BB/*GL_COMPRESSED_RGBA_ASTC_10x10_KHR*/, VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, - {0x93BC/*GL_COMPRESSED_RGBA_ASTC_12x10_KHR*/, VK_FORMAT_ASTC_12x10_UNORM_BLOCK}, - {0x93BD/*GL_COMPRESSED_RGBA_ASTC_12x12_KHR*/, VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, - - {0x93D0/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR*/, VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, - {0x93D1/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR*/, VK_FORMAT_ASTC_5x4_SRGB_BLOCK}, - {0x93D2/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR*/, VK_FORMAT_ASTC_5x5_SRGB_BLOCK}, - {0x93D3/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR*/, VK_FORMAT_ASTC_6x5_SRGB_BLOCK}, - {0x93D4/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR*/, VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, - {0x93D5/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR*/, VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, - {0x93D6/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR*/, VK_FORMAT_ASTC_8x6_SRGB_BLOCK}, - {0x93D7/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR*/, VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, - {0x93D8/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR*/, VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, - {0x93D9/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR*/, VK_FORMAT_ASTC_10x6_SRGB_BLOCK}, - {0x93DA/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR*/, VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, - {0x93DB/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR*/, VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, - {0x93DC/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR*/, VK_FORMAT_ASTC_12x10_SRGB_BLOCK}, - {0x93DD/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR*/, VK_FORMAT_ASTC_12x12_SRGB_BLOCK} -}; - -//----------------------------------------------------------------------------- -static VkFormat L_ReturnVulkanFormat(uint32_t glType, uint32_t glFormat, uint32_t glInternalFormat) -//----------------------------------------------------------------------------- -{ - // Really need GL versions of these numbers - const uint32_t glType_GL_UNSIGNED_BYTE = 0x1401; - const uint32_t glType_GL_HALF_FLOAT = 0x140B; - const uint32_t glType_GL_FLOAT = 0x1406; - - const uint32_t glFormat_GL_RGB = 0x1907; - const uint32_t glFormat_GL_RGBA = 0x1908; - - const uint32_t glFormat_GL_RGB8 = 0x8051; - const uint32_t glFormat_GL_RGBA8 = 0x8058; - - const uint32_t glFormat_GL_SRGB8_EXT = 0x8C41; - const uint32_t glFormat_GL_SRGB8_ALPHA8_EXT = 0x8C43; - - const uint32_t glFormat_GL_RGBA16F = 0x881A; - const uint32_t glFormat_GL_RGBA32F = 0x8814; - - const uint32_t glFormat_GL_RED = 0x1903; - const uint32_t glFormat_GL_R8 = 0x8229; - - VkFormat RetVal = VK_FORMAT_UNDEFINED; - - // Unsigned Byte Formats (SimpleTextureConverted uses either GL_RGB or GL_RGB8) - if (glType == glType_GL_UNSIGNED_BYTE && (glFormat == glFormat_GL_RGB || glFormat == glFormat_GL_RGB8)) - { - LOGE("VK_FORMAT_R8G8B8_UNORM / VK_FORMAT_R8G8B8_SRGB Texture format NOT tested!"); - if (glInternalFormat == glFormat_GL_SRGB8_EXT) - { - RetVal = VK_FORMAT_R8G8B8_SRGB; - } - else - { - RetVal = VK_FORMAT_R8G8B8_UNORM; - } - } - - else if (glType == glType_GL_UNSIGNED_BYTE && (glFormat == glFormat_GL_RED || glFormat == glFormat_GL_R8)) - { - if (glInternalFormat == glFormat_GL_R8) - { - RetVal = VK_FORMAT_R8_UNORM; - } - } - - else if (glType == glType_GL_UNSIGNED_BYTE && (glFormat == glFormat_GL_RGBA || glFormat == glFormat_GL_RGBA8)) - { - if (glInternalFormat == glFormat_GL_SRGB8_ALPHA8_EXT) - { - RetVal = VK_FORMAT_R8G8B8A8_SRGB; - } - else - { - RetVal = VK_FORMAT_R8G8B8A8_UNORM; - } - } - - // Float Formats - else if (glType == glType_GL_HALF_FLOAT && glInternalFormat == glFormat_GL_RGBA16F) - { - RetVal = VK_FORMAT_R16G16B16A16_SFLOAT; - } - - else if (glType == glType_GL_FLOAT && glInternalFormat == glFormat_GL_RGBA32F) - { - LOGE("VK_FORMAT_R32G32B32A32_SFLOAT Texture format NOT tested!"); - RetVal = VK_FORMAT_R32G32B32A32_SFLOAT; - } - - // Compressed Formats - else - { - const auto it = CompressedGlFormatToVkFormat.find( glInternalFormat ); - if( it != CompressedGlFormatToVkFormat.end() ) - { - RetVal = it->second; - } - } - - // Make sure we found something - if (RetVal == VK_FORMAT_UNDEFINED) - { - LOGE("KTX texture formats could not be converted to Vulkan! Type = 0x%x; Format = 0x%x", glType, glFormat); - } - - return RetVal; -} - -//----------------------------------------------------------------------------- -static bool L_ParseKTXBuffer(const char* pFileName, void* pKTXBuffer, uint32_t BufferLength, VulkanTexData* pTexData) -//----------------------------------------------------------------------------- -{ - if (pTexData == NULL) - return false; - - // Make sure starting from a clean place - L_FreeTexData(pTexData); - - // Set up the walker - void* pWalker = pKTXBuffer; - uint32_t uiWalkerDist = 0; - - // Read and verify the KTX Header - KTXHeader* pHeader = (KTXHeader*)pWalker; - - if (memcmp(pHeader->identifier, KTX_IDENTIFIER_REF.data(), KTX_IDENTIFIER_REF.size()) != 0) - { - LOGE("KTX file has invalid header: %s", pFileName); - return false; - } - - if( pHeader->endianness != KTX_ENDIAN_REF ) - { - LOGE("KTX file has invalid endianness (loader does not currently support little endian): %s", pFileName); - return false; - } - - // Skip over the header - uiWalkerDist += sizeof(KTXHeader); - pWalker = (char*)pKTXBuffer + uiWalkerDist; - - // Skip over key value data - uiWalkerDist += pHeader->bytesOfKeyValueData; - pWalker = (char*)pKTXBuffer + uiWalkerDist; - - // LOGI("Texture Header Info (%s)", pFileName); - // LOGI(" glType: 0x%x", pHeader->glType); - // LOGI(" glTypeSize: %d", pHeader->glTypeSize); - // LOGI(" glFormat: 0x%x", pHeader->glFormat); - // LOGI(" glInternalFormat: 0x%x", pHeader->glInternalFormat); - // LOGI(" glBaseInternalFormat: 0x%x", pHeader->glBaseInternalFormat); - // LOGI(" pixelWidth: %d", pHeader->pixelWidth); - // LOGI(" pixelHeight: %d", pHeader->pixelHeight); - // LOGI(" pixelDepth: %d", pHeader->pixelDepth); - // LOGI(" numberOfArrayElements: %d", pHeader->numberOfArrayElements); - // LOGI(" numberOfFaces: %d", pHeader->numberOfFaces); - // LOGI(" numberOfMipmapLevels: %d", pHeader->numberOfMipmapLevels); - - // ******************************** - // Memory Allocation - // ******************************** - // Each texture has faces... - pTexData->pFaceData = (FaceTexData*)malloc(pHeader->numberOfFaces * sizeof(FaceTexData)); - if (pTexData->pFaceData == NULL) - { - LOGE("Unable to allocate memory for %d faces: %s", pHeader->numberOfFaces, pFileName); - return false; - } - - // Pull off the info we care about - pTexData->glType = pHeader->glType; - pTexData->glFormat = pHeader->glFormat; - pTexData->glInternalFormat = pHeader->glInternalFormat; - pTexData->VulkanFormat = L_ReturnVulkanFormat(pHeader->glType, pHeader->glFormat, pHeader->glInternalFormat); - pTexData->NumFaces = pHeader->numberOfFaces; - - for (uint32_t WhichFace = 0; WhichFace < pTexData->NumFaces; WhichFace++) - { - // ...each face has layers... - - // For loop, we need at least one - if (pHeader->numberOfArrayElements == 0) - pHeader->numberOfArrayElements = 1; - - pTexData->pFaceData[WhichFace].pLayerData = (LayerTexData*)malloc(pHeader->numberOfArrayElements * sizeof(LayerTexData)); - if (pTexData->pFaceData[WhichFace].pLayerData == NULL) - { - LOGE("Unable to allocate memory for %d layers: %s", pHeader->numberOfArrayElements, pFileName); - return false; - } - pTexData->pFaceData[WhichFace].NumLayers = pHeader->numberOfArrayElements; - - for (uint32_t WhichLayer = 0; WhichLayer < pTexData->pFaceData[WhichFace].NumLayers; WhichLayer++) - { - // ...each layer has mip levels - - // For loop, we need at least one - if (pHeader->numberOfMipmapLevels == 0) - pHeader->numberOfMipmapLevels = 1; - - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData = (MipTexData*)malloc(pHeader->numberOfMipmapLevels * sizeof(MipTexData)); - if (pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData == NULL) - { - LOGE("Unable to allocate memory for %d mip levels: %s", pHeader->numberOfMipmapLevels, pFileName); - return false; - } - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels = pHeader->numberOfMipmapLevels; - - for (uint32_t WhichMipLevel = 0; WhichMipLevel < pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels; WhichMipLevel++) - { - // These will be allocated and filled in later. Set to NULL for error checking - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData = NULL; - } // Which MipLevel - - } // Which Layer - - } // Which Face - - // ******************************** - // Allocate and fill mip levels - // ******************************** - uint32_t uiMipWidth = pHeader->pixelWidth; - uint32_t uiMipHeight = pHeader->pixelHeight; - uint32_t uiMipSize = 0; - for (uint32_t WhichMipLevel = 0; WhichMipLevel < pHeader->numberOfMipmapLevels; WhichMipLevel++) - { - // What is the data size of this mip level - uiMipSize = *((uint32_t*)pWalker); - uiWalkerDist += sizeof(uint32_t); - pWalker = (char*)pKTXBuffer + uiWalkerDist; - - // LOGI("Mip %d: %dx%d => %d bytes", WhichMipLevel, uiMipWidth, uiMipHeight, uiMipSize); - - for (uint32_t WhichLayer = 0; WhichLayer < pHeader->numberOfArrayElements; WhichLayer++) - { - for (uint32_t WhichFace = 0; WhichFace < pHeader->numberOfFaces; WhichFace++) - { - // TODO: for (uint32_t WhichSlice = 0; WhichSlice < pHeader->pixelDepth; WhichSlice++) - - // Allocate this mip level... - void* pTempData = malloc(uiMipSize); - if (pTempData == NULL) - { - LOGE("Unable to allocate %d bytes of memory for mip level: %s", uiMipSize, pFileName); - return false; - } - - // ...copy the data over... - memcpy(pTempData, pWalker, uiMipSize); - - // ... and set the data in the structure - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Width = uiMipWidth; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Height = uiMipHeight; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Size = uiMipSize; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData = pTempData; - - - // Step forward but make sure on the correct boundary - uiWalkerDist += uiMipSize; - pWalker = (char*)pKTXBuffer + uiWalkerDist; - - // Possible to have face padding here - // (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) - // cubePadding: "For non-array cubemap textures (any texture where numberOfFaces is 6 and - // numberOfArrayElements is 0) cubePadding contains between 0 and 3 bytes to ensure that the - // data in each face begins at a file offset that is a multiple of 4. In all other cases - // cubePadding is empty (0 bytes long)." - uint32_t CubePadding = (uiWalkerDist % 4); - if (CubePadding != 0) - { - uiWalkerDist += CubePadding; - pWalker = (char*)pKTXBuffer + uiWalkerDist; - } - - } // Which Face - } // Which Layer - - // Divide the mip size to go to next one - uiMipWidth /= 2; - uiMipHeight /= 2; - - // Make sure we don't have a size of zero - if (uiMipWidth == 0) - uiMipWidth = 1; - if (uiMipHeight == 0) - uiMipHeight = 1; - - // May need to adjust pointer due to padding (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) - uint32_t BytePadding = 3 - ((uiMipSize + 3) % 4); - if (BytePadding != 0) - { - uiWalkerDist += BytePadding; - pWalker = (char*)pKTXBuffer + uiWalkerDist; - } - } // Which MipLevel - - // Everything worked out - return true; -} -//----------------------------------------------------------------------------- -static bool L_ParseKTX2Buffer(const char* pFileName, void* pKTXBuffer, uint32_t BufferLength, VulkanTexData* pTexData) -//----------------------------------------------------------------------------- -{ - if (pTexData == NULL) - return false; - - // Make sure starting from a clean place - L_FreeTexData(pTexData); - - // Set up the walker - void* pWalker = pKTXBuffer; - uint32_t uiWalkerDist = 0; - - // Read and verify the KTX Header - KTX_header2* pHeader = (KTX_header2*)pWalker; - - if (memcmp(pHeader->identifier, KTX2_IDENTIFIER_REF.data(), KTX2_IDENTIFIER_REF.size()) != 0) - { - LOGE("KTX file has invalid header: %s", pFileName); - return false; - } - - LOGI("Texture Header Info (%s)", pFileName); - LOGI(" vkFormat: 0x%x", pHeader->vkFormat); - LOGI(" typeSize: %d", pHeader->typeSize); - LOGI(" pixelWidth: %d", pHeader->pixelWidth); - LOGI(" pixelHeight: %d", pHeader->pixelHeight); - LOGI(" pixelDepth: %d", pHeader->pixelDepth); - LOGI(" layerCount: %d", pHeader->layerCount); - LOGI(" faceCount: %d", pHeader->faceCount); - LOGI(" levelCount: %d", pHeader->levelCount); - - // Cubemaps => NOT SUPPORTED - if (pHeader->faceCount != 1) - { - LOGE(" CubeMaps in KTX2 NOT SUPPORTED!"); - return false; - } - - // Array textures => NOT SUPPORTED - if (pHeader->layerCount != 0) - { - LOGE(" Array Textures in KTX2 NOT SUPPORTED!"); - return false; - } - - // Single Mip => NOT SUPPORTED - if (pHeader->levelCount == 0) - { - LOGE(" Single mip in KTX2 NOT SUPPORTED!"); - return false; - } - - if (pHeader->vkFormat == VK_FORMAT_R16G16B16A16_SFLOAT) - { - LOGI(" Floating point format: VK_FORMAT_R16G16B16A16_SFLOAT"); - } - - // if (pHeader->vkFormat == VK_FORMAT_B8G8R8_SRGB) - // { - // LOGE(" SRGB KTX2 NOT SUPPORTED! Changing VK_FORMAT_B8G8R8_SRGB to VK_FORMAT_R8G8B8_UNORM"); - // pHeader->vkFormat = VK_FORMAT_R8G8B8_UNORM; - // } - // - // if (pHeader->vkFormat == VK_FORMAT_B8G8R8A8_SRGB) - // { - // LOGE(" SRGB KTX2 NOT SUPPORTED! Changing VK_FORMAT_B8G8R8A8_SRGB to VK_FORMAT_R8G8B8A8_UNORM"); - // pHeader->vkFormat = VK_FORMAT_R8G8B8A8_UNORM; - // } - - // Skip over the header - uiWalkerDist += sizeof(KTX_header2); - pWalker = (char*)pKTXBuffer + uiWalkerDist; - // LOGI("Walker is now %d bytes from start of file", uiWalkerDist); - - // DataFormatDescriptor - // LOGI(" DataFormatDescriptor: %d bytes starting at %d", pHeader->dataFormatDescriptor.byteLength, pHeader->dataFormatDescriptor.byteOffset); - // uiWalkerDist = pHeader->dataFormatDescriptor.byteOffset + pHeader->dataFormatDescriptor.byteLength; - // pWalker = (char*)pKTXBuffer + uiWalkerDist; - // LOGI("Walker is now %d bytes from start of file", uiWalkerDist); - - - // KeyValueData - // LOGI(" KeyValueData: %d bytes starting at %d", pHeader->keyValueData.byteLength, pHeader->keyValueData.byteOffset); - // uiWalkerDist = pHeader->keyValueData.byteOffset + pHeader->keyValueData.byteLength; - // pWalker = (char*)pKTXBuffer + uiWalkerDist; - // LOGI("Walker is now %d bytes from start of file", uiWalkerDist); - - // SupercompressionGlobalData => NOT SUPPORTED - if (pHeader->supercompressionGlobalData.byteOffset != 0 || pHeader->supercompressionGlobalData.byteLength != 0) - { - LOGE(" SupercompressionGlobalData is NOT SUPPORTED!"); - return false; - } - - // Now come the levels: One entry for each mip - uint32_t MipWidth = pHeader->pixelWidth; - uint32_t MipHeight = pHeader->pixelHeight; - ktxLevelIndexEntry* pLevelEntry = (ktxLevelIndexEntry*)pWalker; - for (uint32_t WhichLevel = 0; WhichLevel < pHeader->levelCount; WhichLevel++) - { - // LOGI(" Level %02d (%d x %d): %lld bytes starting at %lld", WhichLevel, MipWidth, MipHeight, pLevelEntry[WhichLevel].byteLength, pLevelEntry[WhichLevel].byteOffset); - - MipWidth /= 2; - MipHeight /= 2; - - uiWalkerDist += sizeof(ktxLevelIndexEntry); - pWalker = (char*)pKTXBuffer + uiWalkerDist; - } - // LOGI("Walker is now %d bytes from start of file", uiWalkerDist); - - // Sanity check that I am the start of DataFormatDescriptor - if (uiWalkerDist != pHeader->dataFormatDescriptor.byteOffset) - { - LOGE("KTX file has invalid header: %s", pFileName); - LOGE(" Accounted for %d bytes but should be at %d offset", uiWalkerDist, pHeader->dataFormatDescriptor.byteOffset); - return false; - } - - // ******************************** - // Memory Allocation - // ******************************** - // Each texture has faces... - pTexData->pFaceData = (FaceTexData*)malloc(pHeader->faceCount * sizeof(FaceTexData)); - if (pTexData->pFaceData == NULL) - { - LOGE("Unable to allocate memory for %d faces: %s", pHeader->faceCount, pFileName); - return false; - } - - // Pull off the info we care about - pTexData->glType = pHeader->vkFormat; - pTexData->glFormat = pHeader->vkFormat; - pTexData->glInternalFormat = pHeader->vkFormat; - pTexData->VulkanFormat = (VkFormat)pHeader->vkFormat; - pTexData->NumFaces = pHeader->faceCount; - - for (uint32_t WhichFace = 0; WhichFace < pTexData->NumFaces; WhichFace++) - { - pTexData->pFaceData[WhichFace].pLayerData = (LayerTexData*)malloc(sizeof(LayerTexData)); - if (pTexData->pFaceData[WhichFace].pLayerData == NULL) - { - LOGE("Unable to allocate memory for layer: %s", pFileName); - return false; - } - pTexData->pFaceData[WhichFace].NumLayers = 1; - - for (uint32_t WhichLayer = 0; WhichLayer < pTexData->pFaceData[WhichFace].NumLayers; WhichLayer++) - { - // ...each layer has mip levels - - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData = (MipTexData*)malloc(pHeader->levelCount * sizeof(MipTexData)); - if (pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData == NULL) - { - LOGE("Unable to allocate memory for %d mip levels: %s", pHeader->levelCount, pFileName); - return false; - } - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels = pHeader->levelCount; - - for (uint32_t WhichMipLevel = 0; WhichMipLevel < pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].NumMipLevels; WhichMipLevel++) - { - // These will be allocated and filled in later. Set to NULL for error checking - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData = nullptr; - } // Which MipLevel - - } // Which Layer - - } // Which Face - - // ******************************** - // Allocate and fill mip levels - // ******************************** - uint32_t uiMipWidth = pHeader->pixelWidth; - uint32_t uiMipHeight = pHeader->pixelHeight; - uint32_t uiMipSize = 0; - for (uint32_t WhichMipLevel = 0; WhichMipLevel < pHeader->levelCount; WhichMipLevel++) - { - // What is the data size of this mip level - uiMipSize = (uint32_t)pLevelEntry[WhichMipLevel].byteLength; - - // LOGI("Mip %d: %dx%d => %d bytes", WhichMipLevel, uiMipWidth, uiMipHeight, uiMipSize); - - for (uint32_t WhichLayer = 0; WhichLayer < 1; WhichLayer++) - { - for (uint32_t WhichFace = 0; WhichFace < pHeader->faceCount; WhichFace++) - { - // TODO: for (uint32_t WhichSlice = 0; WhichSlice < pHeader->pixelDepth; WhichSlice++) - - // Allocate this mip level... - void* pTempData = malloc(uiMipSize); - if (pTempData == NULL) - { - LOGE("Unable to allocate %d bytes of memory for mip level: %s", uiMipSize, pFileName); - return false; - } - - // ...copy the data over... - void* pMipSource = (char*)pKTXBuffer + (uint32_t)pLevelEntry[WhichMipLevel].byteOffset; - memcpy(pTempData, pMipSource, uiMipSize); - - // ... and set the data in the structure - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Width = uiMipWidth; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Height = uiMipHeight; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].Size = uiMipSize; - pTexData->pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMipLevel].pData = pTempData; - - } // Which Face - } // Which Layer - - // Divide the mip size to go to next one - uiMipWidth /= 2; - uiMipHeight /= 2; - - // Make sure we don't have a size of zero - if (uiMipWidth == 0) - uiMipWidth = 1; - if (uiMipHeight == 0) - uiMipHeight = 1; - } // Which MipLevel - - // Everything worked out - return true; -} - -//----------------------------------------------------------------------------- -static bool L_ParsePNGBuffer( const char* pFileName, void* pPNGBuffer, uint32_t BufferLength, VulkanTexData* pTexData ) -//----------------------------------------------------------------------------- -{ - if (pTexData == NULL) - return false; - - // Make sure starting from a clean place - L_FreeTexData( pTexData ); - int width = 0, height = 0, componentsPerPixel = 0; - unsigned char* data = stbi_load_from_memory( static_cast(pPNGBuffer), BufferLength, &width, &height, &componentsPerPixel, 0/*forced components per pixel*/); - uint32_t dataSize = width * height * componentsPerPixel; - if (dataSize <= 0) - return false; - if (componentsPerPixel == 3) - { - // RGB formats not supported, shuffle to RGBA. - uint8_t* dataRGBA = new uint8_t[width*height*4]; - uint8_t* pOutRGBA = dataRGBA; - const uint8_t* pInRGB = (const uint8_t*)data; - for (int i = 0; i < width * height; ++i) - { - *pOutRGBA++ = *pInRGB++; - *pOutRGBA++ = *pInRGB++; - *pOutRGBA++ = *pInRGB++; - *pOutRGBA++ = 255; - } - STBI_FREE( data ); - data = dataRGBA; - componentsPerPixel = 4; - } - - pTexData->NumFaces = 1; - pTexData->pFaceData = (FaceTexData*) malloc( pTexData->NumFaces * sizeof( FaceTexData ) ); - pTexData->pFaceData->NumLayers = 1; - pTexData->pFaceData->pLayerData = (LayerTexData*) malloc( pTexData->pFaceData->NumLayers * sizeof( LayerTexData ) ); - pTexData->pFaceData->pLayerData->NumMipLevels = 1; - pTexData->pFaceData->pLayerData->pMipData = (MipTexData*) malloc( pTexData->pFaceData->pLayerData->NumMipLevels * sizeof( MipTexData ) ); - pTexData->pFaceData->pLayerData->pMipData->Height = height; - pTexData->pFaceData->pLayerData->pMipData->Width = width; - pTexData->pFaceData->pLayerData->pMipData->Size = dataSize; - pTexData->pFaceData->pLayerData->pMipData->pData = data; - - switch (componentsPerPixel) { - default: - break; - case 1: - pTexData->VulkanFormat = VK_FORMAT_R8_UNORM; - break; - case 2: - pTexData->VulkanFormat = VK_FORMAT_R8G8_UNORM; - break; - case 3: - pTexData->VulkanFormat = VK_FORMAT_R8G8B8_UNORM; - break; - case 4: - pTexData->VulkanFormat = VK_FORMAT_R8G8B8A8_SRGB; - break; - } - return true; -} - -#endif // 0 - //----------------------------------------------------------------------------- bool SaveTextureData( const char* pFileName, TextureFormat format, int width, int height, const void* data ) //----------------------------------------------------------------------------- @@ -847,1245 +49,3 @@ bool SaveTextureData( const char* pFileName, TextureFormat format, int width, in return (error != 0); } } - -#if 0 - -//----------------------------------------------------------------------------- -bool CreateSampler(Vulkan* pVulkan, VkSamplerAddressMode SamplerMode, VkFilter FilterMode, VkBorderColor BorderColor, bool UnnormalizedCoordinates, float mipBias, VkSampler* pRetSampler) -//----------------------------------------------------------------------------- -{ - assert(pRetSampler); - VkSamplerCreateInfo SamplerInfo{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - SamplerInfo.flags = 0; - SamplerInfo.magFilter = FilterMode; - SamplerInfo.minFilter = FilterMode; - SamplerInfo.mipmapMode = FilterMode == SamplerFilter::Linear ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST; - SamplerInfo.addressModeU = SamplerMode; - SamplerInfo.addressModeV = SamplerMode; - SamplerInfo.addressModeW = SamplerMode; - SamplerInfo.mipLodBias = mipBias; - SamplerInfo.anisotropyEnable = FilterMode == SamplerFilter::Linear ? VK_TRUE : VK_FALSE; - SamplerInfo.maxAnisotropy = SamplerInfo.anisotropyEnable==VK_TRUE ? 4.0f : 1.0f; - SamplerInfo.compareEnable = VK_FALSE; - SamplerInfo.compareOp = VK_COMPARE_OP_NEVER; - SamplerInfo.minLod = 0.0f; - SamplerInfo.maxLod = FLT_MAX; - SamplerInfo.borderColor = BorderColor; // VkBorderColor - SamplerInfo.unnormalizedCoordinates = UnnormalizedCoordinates ? VK_TRUE : VK_FALSE; - - VkResult RetVal = vkCreateSampler(pVulkan->m_VulkanDevice, &SamplerInfo, NULL, pRetSampler); - if (!CheckVkError("vkCreateSampler()", RetVal)) - return false; - return true; -} - -//----------------------------------------------------------------------------- -bool CreateImageView(Vulkan* pVulkan, VkImage image, VkFormat format, uint32_t baseMipLevel, uint32_t numMipLevels, uint32_t numFaces, VkImageViewType viewType, VkImageView* pRetImageView) -//----------------------------------------------------------------------------- -{ - assert(pRetImageView); - VkImageViewCreateInfo ImageViewInfo { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ImageViewInfo.flags = 0; - ImageViewInfo.image = image; - ImageViewInfo.viewType = viewType; - - ImageViewInfo.format = format; - ImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - ImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; - ImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; - ImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - ImageViewInfo.subresourceRange.baseMipLevel = baseMipLevel; - ImageViewInfo.subresourceRange.levelCount = numMipLevels; - ImageViewInfo.subresourceRange.baseArrayLayer = 0; - ImageViewInfo.subresourceRange.layerCount = 1; - if (numFaces == 6) - ImageViewInfo.subresourceRange.layerCount = 6; - else - ImageViewInfo.subresourceRange.layerCount = 1; - - VkImageView RetImageView; - auto RetVal = vkCreateImageView(pVulkan->m_VulkanDevice, &ImageViewInfo, NULL, pRetImageView); - if (!CheckVkError("vkCreateImageView()", RetVal)) - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -TextureVulkan LoadKTXTexture(Vulkan* pVulkan, AssetManager& assetManager, const char* pFileName, VkSamplerAddressMode SamplerMode, int32_t NumMipsToLoad, float mipBias) -//----------------------------------------------------------------------------- -{ - // Texture Convert Command Line: simpletextureconverter hud.tga hud.ktx -format R8G8B8A8Unorm -flipY - - VkResult RetVal; - - uint32_t uiWidth = 0; - uint32_t uiHeight = 0; - uint32_t uiFaces = 0; - uint32_t uiMipLevels = 0; - uint32_t uiMipOffset = 0; - VkFormat VulkanFormat = VK_FORMAT_UNDEFINED; - bool forceLinearTiling = false; - - VulkanTexData TexData = {}; - - LOGI("Loading KTX texture: %s", pFileName); - - { - std::vector fileData; - if (!assetManager.LoadFileIntoMemory(pFileName, fileData)) - { - LOGE("Error reading texture file: %s", pFileName); - return {}; - } - - size_t filenameLength = strlen( pFileName ); - if (filenameLength > 4 && strcmp( pFileName + filenameLength - 4, ".ktx" ) == 0) - { - if (!L_ParseKTXBuffer(pFileName, fileData.data(), (uint32_t)fileData.size(), &TexData)) - { - LOGE("Error parsing texture file: %s", pFileName); - return {}; - } - } - else if (filenameLength > 5 && strcmp(pFileName + filenameLength - 5, ".ktx2") == 0) - { - if (!L_ParseKTX2Buffer(pFileName, fileData.data(), (uint32_t)fileData.size(), &TexData)) - { - LOGE("Error parsing texture file: %s", pFileName); - return {}; - } - } - else - { - if (!L_ParsePNGBuffer( pFileName, fileData.data(), (uint32_t) fileData.size(), &TexData )) - { - LOGE( "Error parsing texture file: %s", pFileName ); - return {}; - } - } - } - - - uiWidth = TexData.pFaceData[0].pLayerData[0].pMipData[0].Width; - uiHeight = TexData.pFaceData[0].pLayerData[0].pMipData[0].Height; - uiFaces = TexData.NumFaces; - uiMipLevels = TexData.pFaceData[0].pLayerData[0].NumMipLevels; - VulkanFormat = TexData.VulkanFormat; - - if (NumMipsToLoad < (int32_t)uiMipLevels) - { - // Reset the "starting" mip so allocated texture memory is correct. - uiMipOffset = uiMipLevels - (uint32_t)NumMipsToLoad; - - uiWidth = TexData.pFaceData[0].pLayerData[0].pMipData[uiMipOffset].Width; - uiHeight = TexData.pFaceData[0].pLayerData[0].pMipData[uiMipOffset].Height; - uiMipLevels = NumMipsToLoad; - } - - // Check that the device supports this format - if (!pVulkan->IsTextureFormatSupported(VulkanFormat)) - { - // Potentially fallback to loading a .win.ktx file - size_t filenameLength = strlen(pFileName); - if ((filenameLength < 8 || strcmp(pFileName + filenameLength - 8, ".win.ktx") != 0) && (filenameLength>4 && strcmp(pFileName + filenameLength - 4, ".ktx") == 0)) - { - std::string fallbackFilename(pFileName, filenameLength - 4); - fallbackFilename.append(".win.ktx"); - auto fallback = LoadKTXTexture(pVulkan, assetManager, fallbackFilename.c_str(), SamplerMode); - if (!fallback.IsEmpty()) - return fallback; - } - - LOGE("Error, texture format (%d) not supported by device: %s", int(VulkanFormat), pFileName); - return {}; - } - - // Get device properites for the requested texture format - const auto& formatProperties = pVulkan->GetFormatProperties(VulkanFormat); - bool useStaging = !(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); - - // Image creation info. Will change below based on need - VkImageCreateInfo ImageInfo {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - ImageInfo.flags = 0; - ImageInfo.imageType = VK_IMAGE_TYPE_2D; - ImageInfo.format = VulkanFormat; - ImageInfo.extent.width = uiWidth; - ImageInfo.extent.height = uiHeight; - ImageInfo.extent.depth = 1; // Spec says for VK_IMAGE_TYPE_2D depth must be 1 - ImageInfo.mipLevels = 1; // Set to 1 since making each level. Later will be set to MipLevels; - ImageInfo.arrayLayers = 1; // Set to 6 for cube maps. - ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - ImageInfo.tiling = VK_IMAGE_TILING_LINEAR; - ImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - ImageInfo.queueFamilyIndexCount = 0; - ImageInfo.pQueueFamilyIndices = NULL; - ImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; // VK_IMAGE_LAYOUT_UNDEFINED says data not guaranteed to be preserved when changing state - - struct CubeFace - { - CubeFace(uint32_t uiFaces) : mipImages(uiFaces) {} - std::vector mipImages; - }; - - // Allocate the cube face and mips for each face - std::vector cubeFaces; - cubeFaces.reserve(uiFaces); - - for (uint32_t WhichFace = 0; WhichFace < uiFaces; WhichFace++) - { - cubeFaces.emplace_back(uiMipLevels); - } - - - // Create and copy mip levels - - // Need the setup command buffer for loading images - VkCommandBuffer SetupCmdBuffer = pVulkan->StartSetupCommandBuffer(); - - // Load separate cube map faces into linear tiled textures - for (uint32_t WhichFace = 0; WhichFace < uiFaces; WhichFace++) - { - for (uint32_t WhichMip = 0; WhichMip < uiMipLevels; WhichMip++) - { - // TODO: Layers are not supported - uint32_t WhichLayer = 0; - - uint32_t TexDataMip = WhichMip + uiMipOffset; - - ImageInfo.extent.width = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Width; - ImageInfo.extent.height = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Height; - - char szName[256]; - memset(szName, 0, sizeof(szName)); - sprintf(szName, "%s: Face %d; Mip %d", pFileName, WhichFace, WhichMip); - if (!cubeFaces[WhichFace].mipImages[WhichMip].Initialize(pVulkan, ImageInfo, MemoryUsage::CpuToGpu)) - { - LOGE("Unable to initialize mip %d of face %d (%s)", WhichMip + 1, WhichFace + 1, pFileName); - return {}; - } - - // ... copy texture data into the image - VkImageSubresource SubresInfo {}; - SubresInfo.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - SubresInfo.mipLevel = 0; // This is always 0 since dealing with temporary images - SubresInfo.arrayLayer = 0; // TODO: Adjust this as we support 3D/array textures - - VkSubresourceLayout SubResLayout {}; - auto& faceImage = cubeFaces[WhichFace].mipImages[WhichMip].m_VmaImage; - vkGetImageSubresourceLayout(pVulkan->m_VulkanDevice, faceImage.GetVkBuffer(), &SubresInfo, &SubResLayout); - - { - // account for SubResLayout.offset too? - auto& memorymanager = pVulkan->GetMemoryManager(); - auto mappedMemory = memorymanager.Map(faceImage); - uint8_t* pDst = (uint8_t*)mappedMemory.data(); - uint8_t* pSrc = (uint8_t*)TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].pData; - if (FormatIsCompressed(VulkanFormat) || SubResLayout.rowPitch * ImageInfo.extent.height == TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Size) - { - // Block sizes match, copy entire mip level. - memcpy(pDst, pSrc, TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Size); - } - else - { - // Pitch is such that we need to copy line by line - can happen on non power-2 textures and when texture size gets very small (depending on GPU memory alignment requirements) - size_t srcRowPitch = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Size / ImageInfo.extent.height; - for (uint32_t y = 0; y < ImageInfo.extent.height; ++y) - { - memcpy(pDst, pSrc, srcRowPitch); - pDst += SubResLayout.rowPitch; - pSrc += srcRowPitch; - } - } - memorymanager.Unmap(faceImage, std::move(mappedMemory)); - } - - // Image barrier for linear image (base) - // Linear image will be used as a source for the copy - VkPipelineStageFlags srcMask = 0; - VkPipelineStageFlags dstMask = 1; - uint32_t baseMipLevel = 0; - uint32_t mipLevelCount = 1; - - pVulkan->SetImageLayout(cubeFaces[WhichFace].mipImages[WhichMip].m_VmaImage.GetVkBuffer(), - SetupCmdBuffer, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount); - } // Each mipmap in each face - } // Each Face - - // Transfer cube map faces to optimal tiling - - // Now that creating the whole thing we need the correct size - ImageInfo.extent.width = uiWidth; - ImageInfo.extent.height = uiHeight; - - // Now that we are done creating single images, need to create all mip levels - ImageInfo.mipLevels = uiMipLevels; - - // Setup texture as blit target with optimal tiling - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - - if (uiFaces == 6) - ImageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - - ImageInfo.arrayLayers = uiFaces; - - // Need the return image - Wrap_VkImage RetImage; - if(!RetImage.Initialize(pVulkan, ImageInfo, MemoryUsage::GpuExclusive, pFileName)) - { - LOGE("Unable to initialize texture image (%s)", pFileName); - return {}; - } - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - VkPipelineStageFlags srcMask = 0; - VkPipelineStageFlags dstMask = 1; - uint32_t baseMipLevel = 0; - uint32_t mipLevelCount = uiMipLevels; - uint32_t baseLayer = 0; - uint32_t layerCount = uiFaces; - - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), - SetupCmdBuffer, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount, - baseLayer, - layerCount); - - // Copy cube map faces one by one - // Vulkan spec says the order is +X, -X, +Y, -Y, +Z, -Z - for (uint32_t WhichFace = 0; WhichFace < uiFaces; WhichFace++) - { - for (uint32_t WhichMip = 0; WhichMip < uiMipLevels; WhichMip++) - { - // TODO: Layers are not supported - uint32_t WhichLayer = 0; - - uint32_t TexDataMip = WhichMip + uiMipOffset; - - // Copy region for image blit - VkImageCopy copyRegion = {}; - - // Source is always base level because we have an image for each face and each mip - copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyRegion.srcSubresource.baseArrayLayer = 0; - copyRegion.srcSubresource.mipLevel = 0; - copyRegion.srcSubresource.layerCount = 1; - copyRegion.srcOffset.x = 0; - copyRegion.srcOffset.y = 0; - copyRegion.srcOffset.z = 0; - - // Source is the section of the main image - copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyRegion.dstSubresource.baseArrayLayer = WhichFace; - copyRegion.dstSubresource.mipLevel = WhichMip; - copyRegion.dstSubresource.layerCount = 1; - copyRegion.dstOffset.x = 0; - copyRegion.dstOffset.y = 0; - copyRegion.dstOffset.z = 0; - - // Size is the size of this mip - copyRegion.extent.width = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Width; - copyRegion.extent.height = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[TexDataMip].Height; - - copyRegion.extent.depth = 1; - - // Put image copy into command buffer - vkCmdCopyImage( - SetupCmdBuffer, - cubeFaces[WhichFace].mipImages[WhichMip].m_VmaImage.GetVkBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - RetImage.m_VmaImage.GetVkBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ©Region); - } // Each mipmap in each face - } // Each Face - - // No longer need the texture data - L_FreeTexData(&TexData); - - // Change texture image layout to shader read after the copy - VkImageLayout RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - srcMask = 0; - dstMask = 1; - baseMipLevel = 0; - mipLevelCount = uiMipLevels; - baseLayer = 0; - layerCount = uiFaces; - - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, RetImageLayout, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount, - baseLayer, - layerCount); - - // Submit the command buffer we have been working on - pVulkan->FinishSetupCommandBuffer(SetupCmdBuffer); - - // Need a sampler... - VkSampler RetSampler; - if (!CreateSampler(pVulkan, SamplerMode, SamplerFilter::Linear, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, false, mipBias, &RetSampler)) - { - return {}; - } - - // ... and an ImageView - VkImageViewType viewType; - if (layerCount == 6) - viewType = VK_IMAGE_VIEW_TYPE_CUBE; - else - viewType = VK_IMAGE_VIEW_TYPE_2D; - assert(layerCount == 1 || layerCount == 6); - VkImageView RetImageView; - if (!CreateImageView(pVulkan, RetImage.m_VmaImage.GetVkBuffer(), ImageInfo.format, baseMipLevel, mipLevelCount, layerCount, viewType, &RetImageView)) - { - vkDestroySampler(pVulkan->m_VulkanDevice, RetSampler, nullptr); - return {}; - } - - // LOGI("vkCreateImageView: %s -> %p", pFileName, RetImageView); - - - // Cleanup - cubeFaces.clear(); - - - // **************************************************** - // **************************************************** - - // Set the return values - return TextureVulkan{ uiWidth, uiHeight, 1, uiMipLevels, ImageInfo.format, RetImageLayout, std::move(RetImage.m_VmaImage), RetSampler, RetImageView }; -} - -//----------------------------------------------------------------------------- -void DumpKTXMipFiles(AssetManager& assetManager, std::string SourceFile, std::string OutBaseFile) -//----------------------------------------------------------------------------- -{ - // This is basically this function: TextureFuncts.cpp ==> LoadKTXTexture() - - LOGI("Dumping KTX texture mipmaps: %s -> %s", SourceFile.c_str(), OutBaseFile.c_str()); -#if !defined(OS_WINDOWS) - if(true) - { - LOGI(" DumpKTXMipFiles() only supported on Windows! Sorry."); - return; - } -#endif // !defined(OS_WINDOWS) - - uint32_t uiWidth = 0; - uint32_t uiHeight = 0; - uint32_t uiFaces = 0; - uint32_t uiMipLevels = 0; - VkFormat VulkanFormat = VK_FORMAT_UNDEFINED; - - VulkanTexData TexData = {}; - { - std::vector fileData; - if (!assetManager.LoadFileIntoMemory(SourceFile.c_str(), fileData)) - { - LOGE("Error reading texture file: %s", SourceFile.c_str()); - return; - } - - if (!L_ParseKTXBuffer(SourceFile.c_str(), fileData.data(), (uint32_t)fileData.size(), &TexData)) - { - LOGE("Error parsing texture file: %s", SourceFile.c_str()); - return; - } - } - - uiWidth = TexData.pFaceData[0].pLayerData[0].pMipData[0].Width; - uiHeight = TexData.pFaceData[0].pLayerData[0].pMipData[0].Height; - uiFaces = TexData.NumFaces; - uiMipLevels = TexData.pFaceData[0].pLayerData[0].NumMipLevels; - VulkanFormat = TexData.VulkanFormat; - - if (uiFaces != 1) - { - LOGE("DumpKTXMipFiles Error: Source has %d faces and only 1 face is supported!", uiFaces); - return; - } - - // Copy each mip level to a file (Faces and Layers not supported) - uint32_t WhichFace = 0; - uint32_t WhichLayer = 0; - - for (uint32_t WhichMip = 0; WhichMip < uiMipLevels; WhichMip++) - { - uint32_t MipWidth = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMip].Width; - uint32_t MipHeight = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMip].Height; - - uint8_t* pSrc = (uint8_t*)TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMip].pData; - uint32_t MipSize = TexData.pFaceData[WhichFace].pLayerData[WhichLayer].pMipData[WhichMip].Size; - - char szName[512]; - memset(szName, 0, sizeof(szName)); - sprintf(szName, "%s_Mip%02d_%dx%d.data", OutBaseFile.c_str(), WhichMip, MipWidth, MipHeight); - LOGI(" %s (%d bytes)...", szName, MipSize); - - FILE* OutStream = fopen(szName, "wb"); - if (OutStream == nullptr) - { - // Not really sure what to do with this error. Try to keep going I guess - LOGE(" Unable to create %s", szName); - continue; - } - - size_t NumWritten = fwrite(pSrc, sizeof(uint8_t), MipSize, OutStream); - if ((uint32_t)NumWritten != MipSize) - { - int WriteError = ferror(OutStream); - LOGE(" Wrote %d bytes. Should have written %d (ferror = %d)", (uint32_t)NumWritten, MipSize, WriteError); - } - - fclose(OutStream); - } // Which mipmap - - // No longer need the texture data - L_FreeTexData(&TexData); - -} - -//----------------------------------------------------------------------------- -TextureVulkan LoadPPMTexture(Vulkan* pVulkan, AssetManager& assetManager, const char* pFileName, SamplerAddressMode SamplerMode) -//----------------------------------------------------------------------------- -{ - LOGI("Loading PPM texture: %s", pFileName); - - std::vector fileData; - if (!assetManager.LoadFileIntoMemory(pFileName, fileData)) - { - LOGE("Error reading texture file: %s", pFileName); - return {}; - } - - if (fileData.size() < 20) - return {}; - auto fileDataIt = fileData.begin(); - if (*fileDataIt++ != 'P' || *fileDataIt++ != '6' || !isspace(*fileDataIt++)) - return {}; - while (isspace(*fileDataIt)) - ++fileDataIt; - uint32_t width = 0, height = 0, maxColor = 0; - while (isdigit(*fileDataIt)) - width = width * 10 + uint32_t(*fileDataIt++ - '0'); - while (isspace(*fileDataIt)) - ++fileDataIt; - while (isdigit(*fileDataIt)) - height = height * 10 + uint32_t(*fileDataIt++ - '0'); - while (isspace(*fileDataIt)) - ++fileDataIt; - while (isdigit(*fileDataIt)) - maxColor = maxColor * 10 + uint32_t(*fileDataIt++ - '0'); - uint32_t bytesPerPPMPixel = maxColor < 256 ? 1 : 2; - if (!isspace(*fileDataIt++)) - return {}; - // Images follow. - size_t singleImageBytes = width * height * 3 * bytesPerPPMPixel; - // If there are multiple images treat this as a 3d texture. - size_t dataOffset = fileDataIt - fileData.begin(); - size_t dataBytes = fileData.size() - dataOffset; - uint32_t depth = (uint32_t)(dataBytes / singleImageBytes); - - // Copy image data in to correct format for copying in to vulkan texture. - - //VkFormat format = VK_FORMAT_R8_UNORM; - VkFormat format = VK_FORMAT_R8G8_UNORM; - size_t bytesPerPixel = 2; - std::vector targetData; - targetData.resize(width * height * depth * bytesPerPixel); - if (bytesPerPixel == 1) - { - for (auto& target : targetData) - { - target = *fileDataIt; - fileDataIt += 3; - } - } - else - { - auto targetIt = targetData.begin(); - for (size_t i = 0; i < width * height * depth; ++i) - { - *targetIt = *fileDataIt; - ++targetIt; - ++fileDataIt; - *targetIt = *fileDataIt; - ++targetIt; - ++fileDataIt; - if (bytesPerPixel > 2) - { - *targetIt = *fileDataIt; - ++targetIt; - } - if (bytesPerPixel > 3) - { - *targetIt = 255; // no alpha in ppm - ++targetIt; - } - ++fileDataIt; - } - } - return CreateTextureFromBuffer(pVulkan, targetData.data(), targetData.size(), width, height, depth, format, SamplerMode); -} - -//----------------------------------------------------------------------------- -TextureVulkan CreateTextureFromBuffer(Vulkan* pVulkan, const void* pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, VkFormat Format, VkSamplerAddressMode SamplerMode, VkFilter Filter, VkImageUsageFlags FinalUsage, VkImageLayout FinalLayout) -//----------------------------------------------------------------------------- -{ - VkResult RetVal; - - uint32_t Faces = 1; - uint32_t MipLevels = 1; - VkFormat VulkanFormat = Format; - - // Image creation info. Will change below based on need - VkImageCreateInfo ImageInfo {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - ImageInfo.flags = 0; - ImageInfo.imageType = VK_IMAGE_TYPE_2D; - ImageInfo.format = VulkanFormat; - ImageInfo.extent.width = Width; - ImageInfo.extent.height = Height; - ImageInfo.extent.depth = Depth; - ImageInfo.mipLevels = 1; // Set to 1 since making each level. Later will be set to MipLevels; - ImageInfo.arrayLayers = 1; // Set to 6 for cube maps - ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - ImageInfo.tiling = VK_IMAGE_TILING_LINEAR; - ImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - ImageInfo.queueFamilyIndexCount = 0; - ImageInfo.pQueueFamilyIndices = NULL; - ImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; // VK_IMAGE_LAYOUT_UNDEFINED says data not guaranteed to be preserved when changing state - - struct CubeFace - { - CubeFace(uint32_t depthSlices, uint32_t mips) : mipsPerDepthSlice(mips), images(depthSlices*mips) - {} - const auto& GetImage(uint32_t depth, uint32_t mip) const { return images[depth * mipsPerDepthSlice]; } - auto& GetImage(uint32_t depth, uint32_t mip) { return images[depth * mipsPerDepthSlice + mip]; } - const uint32_t mipsPerDepthSlice; - std::vector images; - }; - - // Allocate the depth slices (and contained mips) for each face - std::vector cubeFaces; - cubeFaces.reserve(Faces); - for (uint32_t WhichFace = 0; WhichFace < Faces; ++WhichFace) - { - cubeFaces.emplace_back(Depth, MipLevels); - } - - // Create and copy mip levels - - // Need the setup command buffer for loading images - VkCommandBuffer SetupCmdBuffer = pVulkan->StartSetupCommandBuffer(); - - uint32_t FormatBytesPerPixel = 4; - if (VulkanFormat == VK_FORMAT_R32G32B32A32_SFLOAT) - FormatBytesPerPixel = 16; - else if (VulkanFormat >= VK_FORMAT_R8_UNORM && VulkanFormat <= VK_FORMAT_R8_SRGB) - FormatBytesPerPixel = 1; - else if (VulkanFormat >= VK_FORMAT_R8G8_UNORM && VulkanFormat <= VK_FORMAT_R8G8_SRGB) - FormatBytesPerPixel = 2; - else if (VulkanFormat >= VK_FORMAT_R16_UNORM && VulkanFormat <= VK_FORMAT_R16_SFLOAT) - FormatBytesPerPixel = 2; - - const uint8_t* pData8 = static_cast(pData); - - // Load separate cube map faces into linear tiled textures (for copying only). - // Split into faces and 2d depth slices because linear textures have extremely limited format/size restrictions. - for (uint32_t WhichFace = 0; WhichFace < Faces; WhichFace++) - { - for (uint32_t WhichDepth = 0; WhichDepth < Depth; WhichDepth++) - { - for (uint32_t WhichMip = 0; WhichMip < MipLevels; WhichMip++) - { - ImageInfo.extent.width = Width; - ImageInfo.extent.height = Height; - ImageInfo.extent.depth = 1; - - Wrap_VkImage& faceImage = cubeFaces[WhichFace].GetImage(WhichDepth, WhichMip); - if (!faceImage.Initialize(pVulkan, ImageInfo, MemoryUsage::CpuToGpu)) - { - LOGE("CreateTextureFromBuffer: Unable to initialize mip %d of face %d", WhichMip + 1, WhichFace + 1); - return {}; - } - - // ... copy texture data into the image - VkImageSubresource SubresInfo = {}; - SubresInfo.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - SubresInfo.mipLevel = 0; - SubresInfo.arrayLayer = 0; - - VkSubresourceLayout SubResLayout = {}; - auto& faceImageMem = faceImage.m_VmaImage; - vkGetImageSubresourceLayout(pVulkan->m_VulkanDevice, faceImageMem.GetVkBuffer(), &SubresInfo, &SubResLayout); - - { - auto& memorymanager = pVulkan->GetMemoryManager(); - auto mappedMemory = memorymanager.Map(faceImageMem); - if (SubResLayout.rowPitch == Width * FormatBytesPerPixel) - { - memcpy(mappedMemory.data(), pData8, SubResLayout.size); - pData8 += Width * Height * FormatBytesPerPixel; - } - else - { - uint8_t* pDest = mappedMemory.data(); - for (uint32_t h = 0; h < Height; ++h) - { - memcpy(pDest, pData8, Width * FormatBytesPerPixel); - pDest += SubResLayout.rowPitch; - pData8 += Width * FormatBytesPerPixel; - } - } - memorymanager.Unmap(faceImageMem, std::move(mappedMemory)); - } - - // Image barrier for linear image (base) - // Linear image will be used as a source for the copy - VkPipelineStageFlags srcMask = 0; - VkPipelineStageFlags dstMask = 1; - uint32_t baseMipLevel = 0; - uint32_t mipLevelCount = 1; - - pVulkan->SetImageLayout(faceImageMem.GetVkBuffer(), - SetupCmdBuffer, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount); - } // Each mipmap in each depth slice - } // Each depth slice in each face - } // Each Face - - // Transfer cube map faces to optimal tiling - - // Now that creating the whole thing we need the correct size and type - ImageInfo.extent.width = Width; - ImageInfo.extent.height = Height; - ImageInfo.extent.depth = Depth; - ImageInfo.imageType = Depth == 1 ? VK_IMAGE_TYPE_2D : VK_IMAGE_TYPE_3D; - - // Now that we are done creating single images, need to create all mip levels - ImageInfo.mipLevels = MipLevels; - - // Setup texture as blit target with optimal tiling - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = FinalUsage; - - if (Faces == 6) - ImageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - - ImageInfo.arrayLayers = Faces; - - // Need the return image - Wrap_VkImage RetImage; - if (!RetImage.Initialize(pVulkan, ImageInfo, MemoryUsage::GpuExclusive)) - { - LOGE("CreateTextureFromBuffer: Unable to initialize texture image"); - return {}; - } - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - VkPipelineStageFlags srcMask = 0; - VkPipelineStageFlags dstMask = 1; - uint32_t baseMipLevel = 0; - uint32_t mipLevelCount = MipLevels; - uint32_t baseLayer = 0; - uint32_t layerCount = Faces; - - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), - SetupCmdBuffer, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount, - baseLayer, - layerCount); - - // Copy cube map faces one by one - // Vulkan spec says the order is +X, -X, +Y, -Y, +Z, -Z - for (uint32_t WhichFace = 0; WhichFace < Faces; WhichFace++) - { - // Copy depth slices one at a time - for (uint32_t WhichDepth = 0; WhichDepth < Depth; WhichDepth++) - { - for (uint32_t WhichMip = 0; WhichMip < MipLevels; WhichMip++) - { - // Copy region for image blit - VkImageCopy copyRegion = {}; - - // Source is always base level because we have an image for each face and each mip - copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyRegion.srcSubresource.baseArrayLayer = 0; - copyRegion.srcSubresource.mipLevel = 0; - copyRegion.srcSubresource.layerCount = 1; - copyRegion.srcOffset.x = 0; - copyRegion.srcOffset.y = 0; - copyRegion.srcOffset.z = 0; - - // Source is the section of the main image - copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyRegion.dstSubresource.baseArrayLayer = WhichFace; - copyRegion.dstSubresource.mipLevel = WhichMip; - copyRegion.dstSubresource.layerCount = 1; - copyRegion.dstOffset.x = 0; - copyRegion.dstOffset.y = 0; - copyRegion.dstOffset.z = WhichDepth; - - // Size is the size of this mip - copyRegion.extent.width = Width; - copyRegion.extent.height = Height; - copyRegion.extent.depth = 1; - - // Put image copy into command buffer - vkCmdCopyImage( - SetupCmdBuffer, - cubeFaces[WhichFace].GetImage(WhichDepth, WhichMip).m_VmaImage.GetVkBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - RetImage.m_VmaImage.GetVkBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ©Region); - } // Each mipmap in each depth slice - } // Each depth slice in each face - } // Each Face - - // Change texture image layout to the 'final' settings now we are done transferring - srcMask = 0; - dstMask = 1; - baseMipLevel = 0; - mipLevelCount = MipLevels; - baseLayer = 0; - layerCount = Faces; - - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, FinalLayout, - srcMask, - dstMask, - baseMipLevel, - mipLevelCount, - baseLayer, - layerCount); - - // Submit the command buffer we have been working on - pVulkan->FinishSetupCommandBuffer(SetupCmdBuffer); - - // Need a sampler... - VkSampler RetSampler; - if (!CreateSampler(pVulkan, SamplerMode, Filter, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, false, 0.0f, &RetSampler)) - { - return {}; - } - - VkImageViewType viewType; - if (Depth > 1) - viewType = VK_IMAGE_VIEW_TYPE_3D; - else if (Faces == 6) - viewType = VK_IMAGE_VIEW_TYPE_CUBE; - else - viewType = VK_IMAGE_VIEW_TYPE_2D; - VkImageView RetImageView; - if (!CreateImageView(pVulkan, RetImage.m_VmaImage.GetVkBuffer(), Format, baseMipLevel, MipLevels, Faces, viewType, &RetImageView)) - { - vkDestroySampler(pVulkan->m_VulkanDevice, RetSampler, nullptr); - return {}; - } - - // Cleanup - cubeFaces.clear(); - - // Set the return values - TextureVulkan RetTex{ Width, Height, Depth, MipLevels, ImageInfo.format, FinalLayout, std::move( RetImage.m_VmaImage ), RetSampler, RetImageView }; - return RetTex; -} - - -////----------------------------------------------------------------------------- -//void ReleaseTexture(Vulkan* pVulkan, TextureVulkan* pTexInfo) -////----------------------------------------------------------------------------- -//{ -// pTexInfo->Release(pVulkan); -// -// *pTexInfo = TextureVulkan{}; // destroy and clear -//} - -#if 0 -// -// Constructors/move-operators for TextureVulkan. -// Ensures we are not leaking Samplers, ImageViews or memory. -// -TextureVulkan::TextureVulkan(TextureVulkan&& other) noexcept -{ - *this = std::move(other); -} -TextureVulkan& TextureVulkan::operator=(TextureVulkan&& other) noexcept -{ - if (this != &other) - { - // Some of the data can be copied and is ok to leave 'other' alone (move operator just has to ensure the 'other' is in a valid state and can be safely deleted) - Width = other.Width; - Height = other.Height; - Depth = other.Depth; - MipLevels = other.MipLevels; - FirstMip = other.FirstMip; - Format = other.Format; - ImageLayout = other.ImageLayout; - // Actually transfer ownership from 'other' - VmaImage = std::move(other.VmaImage); - Sampler = other.Sampler; - other.Sampler = VK_NULL_HANDLE; - ImageView = other.ImageView; - other.ImageView = VK_NULL_HANDLE; - Image = other.Image; - other.Image = VK_NULL_HANDLE; - Memory = other.Memory; - other.Memory = VK_NULL_HANDLE; - } - return *this; -} -TextureVulkan::~TextureVulkan() -{ - // Asserts to ensure we called ReleaseTexture on this already. - assert(!VmaImage); - assert(Sampler == VK_NULL_HANDLE); - assert(ImageView == VK_NULL_HANDLE); -} - - -void TextureVulkan::Release( Vulkan* pVulkan ) -{ - if( ImageView != VK_NULL_HANDLE ) - vkDestroyImageView( pVulkan->m_VulkanDevice, ImageView, NULL ); - ImageView = VK_NULL_HANDLE; - - if( Sampler != VK_NULL_HANDLE ) - vkDestroySampler( pVulkan->m_VulkanDevice, Sampler, NULL ); - Sampler = VK_NULL_HANDLE; - - if( VmaImage ) - pVulkan->GetMemoryManager().Destroy( std::move( VmaImage ) ); - - Image = VK_NULL_HANDLE; - Memory = VK_NULL_HANDLE; -} -#endif - -//----------------------------------------------------------------------------- -TextureVulkan CreateTextureObject(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, VkFormat Format, TEXTURE_TYPE TexType, const char* pName, VkSampleCountFlagBits Msaa, TEXTURE_FLAGS Flags) -//----------------------------------------------------------------------------- -{ - CreateTexObjectInfo createInfo{}; - createInfo.uiWidth = uiWidth; - createInfo.uiHeight = uiHeight; - createInfo.Format = Format; - createInfo.TexType = TexType; - createInfo.Flags = Flags; - createInfo.pName = pName; - createInfo.Msaa = Msaa; - return CreateTextureObject(pVulkan, createInfo); -} - -//----------------------------------------------------------------------------- -TextureVulkan CreateTextureObject(Vulkan* pVulkan, const CreateTexObjectInfo& texInfo) -//----------------------------------------------------------------------------- -{ - VkResult RetVal; - - if(texInfo.pName == nullptr) - LOGI("CreateTextureObject (%dx%d): ", texInfo.uiWidth, texInfo.uiHeight); - else - LOGI("CreateTextureObject (%dx%d): %s", texInfo.uiWidth, texInfo.uiHeight, texInfo.pName); - - VkSampler RetSampler = VK_NULL_HANDLE; - VkImageView RetImageView = VK_NULL_HANDLE; - VkImageLayout RetImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - // How this texture object will be used. - MemoryUsage MemoryUsage = MemoryUsage::GpuExclusive; - - // Create the image... - VkImageCreateInfo ImageInfo{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - ImageInfo.flags = 0; - assert(texInfo.uiDepth != 0); - ImageInfo.imageType = (texInfo.uiDepth == 1) ? VK_IMAGE_TYPE_2D : VK_IMAGE_TYPE_3D; - ImageInfo.format = texInfo.Format; - ImageInfo.extent.width = texInfo.uiWidth; - ImageInfo.extent.height = texInfo.uiHeight; - ImageInfo.extent.depth = texInfo.uiDepth; - ImageInfo.mipLevels = texInfo.uiMips; - ImageInfo.arrayLayers = texInfo.uiFaces; - ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - ImageInfo.queueFamilyIndexCount = 0; - ImageInfo.pQueueFamilyIndices = NULL; - ImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; - - switch (texInfo.TexType) - { - case TT_SHADING_RATE_IMAGE: - ImageInfo.usage = VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - break; - case TT_CPU_UPDATE: - ImageInfo.tiling = VK_IMAGE_TILING_LINEAR; - ImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - break; - case TT_NORMAL: - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;// VK_IMAGE_TILING_LINEAR; // VK_IMAGE_TILING_OPTIMAL - ImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - break; - case TT_RENDER_TARGET: - // If using VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // DO NOT enable Storage_Bit (potential performance impact) - ImageInfo.samples = texInfo.Msaa; - break; - case TT_RENDER_TARGET_WITH_STORAGE: - // If using VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL - // Also enabling Storage may disable some hardware render buffer optimizations - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - assert(texInfo.Msaa == VK_SAMPLE_COUNT_1_BIT && texInfo.Format != VK_FORMAT_R8G8B8A8_SRGB); //TODO: use Vulkan to determine if this texture buffer can have storeage set, for now assert on formats known to be problematic (msaa or srgb) - ImageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - ImageInfo.samples = texInfo.Msaa; - break; - case TT_RENDER_TARGET_TRANSFERSRC: - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - ImageInfo.samples = texInfo.Msaa; - break; - case TT_RENDER_TARGET_SUBPASS: - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; - ImageInfo.samples = texInfo.Msaa; - // Subpass render targets dont need to be backed by memory! - MemoryUsage = MemoryUsage::GpuLazilyAllocated; - break; - case TT_COMPUTE_TARGET: - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;// | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - break; - case TT_DEPTH_TARGET: - // If using VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT then tiling MUST be VK_IMAGE_TILING_OPTIMAL - ImageInfo.mipLevels = 1; - ImageInfo.arrayLayers = 1; - ImageInfo.samples = texInfo.Msaa; - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT /*| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT*/; - if(texInfo.Msaa != VK_SAMPLE_COUNT_1_BIT ) - ImageInfo.flags |= VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT; - break; - - default: - assert(0); - break; - } - - if ((texInfo.Flags & TEXTURE_FLAGS::MutableFormat) != 0) - ImageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - if ((texInfo.Flags & TEXTURE_FLAGS::ForceLinearTiling) != 0) - ImageInfo.tiling = VK_IMAGE_TILING_LINEAR; - - // Need the return image - Wrap_VkImage RetImage; - bool ImageInitialized = RetImage.Initialize( pVulkan, ImageInfo, MemoryUsage, texInfo.pName ); - if( !ImageInitialized && MemoryUsage == MemoryUsage::GpuLazilyAllocated ) - { - LOGI( "Unable to initialize GpuLazilyAllocated image (probably not supported by GPU hardware). Falling back to GpuExclusive" ); - MemoryUsage = MemoryUsage::GpuExclusive; - ImageInfo.usage &= ~VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; - ImageInitialized = RetImage.Initialize( pVulkan, ImageInfo, MemoryUsage, texInfo.pName ); - } - if (!ImageInitialized) - { - LOGE("Unable to initialize image (Not from file)"); - return {}; - } - - VkCommandBuffer SetupCmdBuffer = pVulkan->StartSetupCommandBuffer(); - switch (texInfo.TexType) - { - case TT_SHADING_RATE_IMAGE: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_GENERAL; - break; - case TT_CPU_UPDATE: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - break; - case TT_NORMAL: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - break; - case TT_RENDER_TARGET: - case TT_RENDER_TARGET_WITH_STORAGE: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); - RetImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - break; - case TT_RENDER_TARGET_TRANSFERSRC: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, 1, 0, 1); - RetImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - break; - case TT_RENDER_TARGET_SUBPASS: - RetImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - break; - case TT_COMPUTE_TARGET: - pVulkan->SetImageLayout(RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_COLOR_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_GENERAL, (VkPipelineStageFlags)0/*unused param*/, (VkPipelineStageFlags)0/*unused param*/, 0, ImageInfo.mipLevels, 0, ImageInfo.arrayLayers); - RetImageLayout = VK_IMAGE_LAYOUT_GENERAL; - break; - case TT_DEPTH_TARGET: - if( Vulkan::FormatHasStencil(texInfo.Format ) ) - { - // Can have depth and stencil flag - pVulkan->SetImageLayout( RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags) 0/*unused param*/, (VkPipelineStageFlags) 0/*unused param*/, 0, 1, 0, 1 ); - } - else if ( Vulkan::FormatHasDepth(texInfo.Format ) ) - { - // Only has the depth flag set - pVulkan->SetImageLayout( RetImage.m_VmaImage.GetVkBuffer(), SetupCmdBuffer, VK_IMAGE_ASPECT_DEPTH_BIT, ImageInfo.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkPipelineStageFlags) 0/*unused param*/, (VkPipelineStageFlags) 0/*unused param*/, 0, 1, 0, 1 ); - } - else - { - LOGE("Unhandled depth format!!!"); - } - RetImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; - break; - default: - assert(0); - break; - } - - pVulkan->FinishSetupCommandBuffer(SetupCmdBuffer); - - // ... and an ImageView - VkImageViewCreateInfo ImageViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ImageViewInfo.flags = 0; - ImageViewInfo.image = RetImage.m_VmaImage.GetVkBuffer(); - ImageViewInfo.viewType = (texInfo.uiDepth == 1) ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_3D; // <== No support for VK_IMAGE_VIEW_TYPE_CUBE - ImageViewInfo.format = ImageInfo.format; - ImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; // VK_COMPONENT_SWIZZLE_R; - ImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; // VK_COMPONENT_SWIZZLE_G; - ImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; // VK_COMPONENT_SWIZZLE_B; - ImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; // VK_COMPONENT_SWIZZLE_A; - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - ImageViewInfo.subresourceRange.baseMipLevel = 0; - ImageViewInfo.subresourceRange.levelCount = ImageInfo.mipLevels; - ImageViewInfo.subresourceRange.baseArrayLayer = 0; - ImageViewInfo.subresourceRange.layerCount = 1; - - VkSamplerAddressMode SamplerMode = texInfo.SamplerMode; - - VkBorderColor BorderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; - - switch (texInfo.TexType) - { - case TT_SHADING_RATE_IMAGE: - case TT_CPU_UPDATE: - case TT_NORMAL: - case TT_RENDER_TARGET: - case TT_RENDER_TARGET_WITH_STORAGE: - case TT_RENDER_TARGET_TRANSFERSRC: - case TT_RENDER_TARGET_SUBPASS: - case TT_COMPUTE_TARGET: - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - break; - case TT_DEPTH_TARGET: - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - SamplerMode = (SamplerMode == VK_SAMPLER_ADDRESS_MODE_MAX_ENUM) ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER : SamplerMode; // default to VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - BorderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - break; - default: - assert(0); - break; - } - SamplerMode = (SamplerMode == VK_SAMPLER_ADDRESS_MODE_MAX_ENUM) ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : SamplerMode; // default to VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE - - RetVal = vkCreateImageView(pVulkan->m_VulkanDevice, &ImageViewInfo, NULL, &RetImageView); - if (!CheckVkError("vkCreateImageView()", RetVal)) - { - return {}; - } - - // LOGI("vkCreateImageView: %s -> %p", pName, RetImageView); - - // Need a sampler... - VkFilter FilterMode = texInfo.FilterMode; - if (FilterMode == VK_FILTER_MAX_ENUM) - { - const auto& FormatProperties = pVulkan->GetFormatProperties(texInfo.Format); - auto TilingFeatures = (ImageInfo.tiling == VK_IMAGE_TILING_LINEAR) ? FormatProperties.linearTilingFeatures : FormatProperties.optimalTilingFeatures; - if ((TilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0) - FilterMode = VK_FILTER_LINEAR; - else - FilterMode = VK_FILTER_NEAREST; - } - if (!CreateSampler(pVulkan, SamplerMode, FilterMode, BorderColor, texInfo.UnNormalizedCoordinates, 0.0f, &RetSampler)) - { - return {}; - } - - TextureVulkan RetTex{ texInfo.uiWidth, texInfo.uiHeight, texInfo.uiDepth, texInfo.uiMips, texInfo.Format, RetImageLayout, std::move( RetImage.m_VmaImage ), RetSampler, RetImageView }; - return RetTex; -} - -//----------------------------------------------------------------------------- -TextureVulkan CreateTextureObjectView( Vulkan* pVulkan, const TextureVulkan& original, VkFormat viewFormat ) -//----------------------------------------------------------------------------- -{ - VkSampler sampler = VK_NULL_HANDLE; - VkImageView imageView = VK_NULL_HANDLE; - - VkImageViewCreateInfo imageViewCreateInfo { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - imageViewCreateInfo.image = original.GetVkImage(); - imageViewCreateInfo.viewType = (original.Depth == 1) ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_3D; // <== No support for VK_IMAGE_VIEW_TYPE_CUBE - imageViewCreateInfo.format = viewFormat; - imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageViewCreateInfo.subresourceRange.baseMipLevel = 0; - imageViewCreateInfo.subresourceRange.levelCount = original.MipLevels; - imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; - imageViewCreateInfo.subresourceRange.layerCount = 1; - - auto RetVal = vkCreateImageView( pVulkan->m_VulkanDevice, &imageViewCreateInfo, NULL, &imageView ); - if (!CheckVkError( "vkCreateImageView()", RetVal )) - { - return {}; - } - - if (!CreateSampler( pVulkan, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, SamplerFilter::Linear, {}, false, 0.0f, &sampler )) - { - return {}; - } - - // TextureVulkan( uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageLayout imageLayout, VkImage image, VkDeviceMemory memory, VkSampler sampler, VkImageView imageView ) - TextureVulkan RetTex { original.Width, original.Height, original.Depth, original.MipLevels, original.Format, original.GetVkImageLayout(), original.GetVkImage(), VK_NULL_HANDLE, sampler, imageView }; - return RetTex; -} - -#endif diff --git a/framework/code/vulkan/TextureFuncts.h b/framework/code/vulkan/TextureFuncts.h index 4cb011c..a3323d8 100644 --- a/framework/code/vulkan/TextureFuncts.h +++ b/framework/code/vulkan/TextureFuncts.h @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,127 +10,8 @@ // TextureFuncts.h // Vulkan texture handling support -//#include "vulkan/vulkan.hpp" #include "texture/texture.hpp" -#if 0 -// Forward declarations -class AssetManager; -class Vulkan; -template class TextureT; -using TextureVulkan = TextureT; - -/// @brief A Vulkan texture -/// Owns memory and sampler etc associated with a single texture. -class TextureVulkan -{ -public: - TextureVulkan() : VmaImage() {} - TextureVulkan(const TextureVulkan&) = delete; - TextureVulkan& operator=(const TextureVulkan&) = delete; - TextureVulkan(TextureVulkan&&) noexcept; - TextureVulkan& operator=(TextureVulkan&&) noexcept; - ~TextureVulkan(); - - /// @brief Construct TextureVulkan from a pre-existing vmaImage. - /// @param vmaImage - ownership passed to this TextureVulkan. - /// @param sampler - ownership passed to this TextureVulkan. - /// @param imageView - ownership passed to this TextureVulkan. - TextureVulkan( uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, VkFormat format, VkImageLayout imageLayout, MemoryAllocatedBuffer vmaImage, VkSampler sampler, VkImageView imageView ) - : Width(width) - , Height(height) - , Depth(depth) - , MipLevels(mipLevels) - , Format(format) - , ImageLayout(imageLayout) - , VmaImage(std::move(vmaImage)) - , Sampler(sampler) - , ImageView(imageView) - { - } - - /// @brief Construct TextureVulkan from a pre-existing Vulkan image/memory handles. - /// @param image - ownership NOT passed in to this TextureVulkan, beware of lifetime issues. - /// @param sampler - ownership passed to this TextureVulkan. - /// @param imageView - ownership passed to this TextureVulkan. - TextureVulkan( uint32_t width, uint32_t height, uint32_t mipLevels, uint32_t firstMip, VkFormat format, VkImageLayout imageLayout, VkImage image, VkDeviceMemory memory, VkSampler sampler, VkImageView imageView ) - : Width(width) - , Height(height) - , Depth(1) - , MipLevels( mipLevels ) - , FirstMip( firstMip ) - , Format(format) - , ImageLayout(imageLayout) - , Image(image) - , Memory(memory) - , Sampler(sampler) - , ImageView(imageView) - { - } - - void Release(Vulkan* pVulkan); - - VkImage GetVkImage() const { return VmaImage ? VmaImage.GetVkBuffer() : Image; } - VkDescriptorImageInfo GetVkDescriptorImageInfo() const { return { Sampler, ImageView, ImageLayout }; } - VkImageLayout GetVkImageLayout() const { return ImageLayout; } - VkSampler GetVkSampler() const { return Sampler; } - VkImageView GetVkImageView() const { return ImageView; } - bool IsEmpty() const { return !VmaImage; } - - uint32_t Width = 0; - uint32_t Height = 0; - uint32_t Depth = 0; - uint32_t MipLevels = 0; - uint32_t FirstMip = 0; - VkFormat Format = VK_FORMAT_UNDEFINED; - VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - -public: - - MemoryAllocatedBuffer VmaImage; - - VkSampler Sampler = VK_NULL_HANDLE; - VkImageView ImageView = VK_NULL_HANDLE; - - // Needed for functions handling own memory (i.e. AndroidHardwareBuffers) - VkImage Image = VK_NULL_HANDLE; - VkDeviceMemory Memory = VK_NULL_HANDLE; -}; -#endif // 0 - -// Actual support functions - -#if 0 - -/// Load/create texture from .ktx file (Mips to load are lowest resolution, NOT 0,1,2...) -TextureVulkan LoadKTXTexture(Vulkan *pVulkan, AssetManager&, const char* pFileName, VkSamplerAddressMode SamplerMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, int32_t NumMipsToLoad = 0x7fffffff, float mipBias = 0.0f); -/// Parse a KTX texture and dump each mip to individual file (restrictions on faces, formats, output, etc.) -void DumpKTXMipFiles(AssetManager& assetManager, std::string SourceFile, std::string OutBaseFile); -/// Load/create texture from .ppm file -TextureVulkan LoadPPMTexture(Vulkan* pVulkan, AssetManager&, const char* pFileName, VkSamplerAddressMode SamplerMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); -/// Create texture from an existing memory buffer -TextureVulkan CreateTextureFromBuffer(Vulkan* pVulkan, const void *pData, size_t DataSize, uint32_t Width, uint32_t Height, uint32_t Depth, VkFormat Format, VkSamplerAddressMode SamplerMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, SamplerFilter Filter = SamplerFilter::Linear, VkImageUsageFlags FinalUsage = (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), VkImageLayout FinalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - -#endif // 0 - /// Save texture data to 'filename' /// @returns true on success bool SaveTextureData(const char* pFileName, TextureFormat format, int width, int height, const void* data); - -#if 0 - -/// Create texture (generally for render target usage) -TextureVulkan CreateTextureObject(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, VkFormat Format, TEXTURE_TYPE TexType, const char* pName, VkSampleCountFlagBits Msaa = VK_SAMPLE_COUNT_1_BIT, TEXTURE_FLAGS Flags = TEXTURE_FLAGS::None); -/// Create texture (generally for render target usage). Uses CreateTexObjectInfo structure to define texture creation parameters. -TextureVulkan CreateTextureObject(Vulkan* pVulkan, const CreateTexObjectInfo& texInfo); -/// Create texture that is an imageview referencing an existing TextureVulkan. -/// Required that the referenced originalTexInfo does not go out of scope (be destroyed) before the referencing texture. -TextureVulkan CreateTextureObjectView( Vulkan* pVulkan, const TextureVulkan& original, VkFormat viewFormat ); -///// Release memory associated with the given texture and reset to 'empty' state -//void ReleaseTexture(Vulkan* pVulkan, TextureVulkan *pTexInfo); -/// Create a vulkan image view object -bool CreateImageView(Vulkan* pVulkan, VkImage image, VkFormat format, uint32_t baseMipLevel, uint32_t numMipLevels, uint32_t numFaces, VkImageViewType viewType, VkImageView* pRetImageView); -/// Create a vulkan texture sampler -bool CreateSampler(Vulkan* pVulkan, VkSamplerAddressMode SamplerMode, VkFilter FilterMode, VkBorderColor BorderColor, bool UnnormalizedCoordinates, float mipBias, VkSampler* pRetSampler); - -#endif diff --git a/framework/code/vulkan/commandBuffer.cpp b/framework/code/vulkan/commandBuffer.cpp index 54a7669..f162436 100644 --- a/framework/code/vulkan/commandBuffer.cpp +++ b/framework/code/vulkan/commandBuffer.cpp @@ -1,47 +1,50 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "commandBuffer.hpp" +#include "framebuffer.hpp" +#include "renderContext.hpp" #include "renderTarget.hpp" #include "timerPool.hpp" +#include "vulkan/extensionLib.hpp" #include "vulkan_support.hpp" //----------------------------------------------------------------------------- -CommandListT::CommandListT() noexcept +CommandList::CommandList() noexcept //----------------------------------------------------------------------------- { } //----------------------------------------------------------------------------- -CommandListT::~CommandListT() +CommandList::~CommandList() //----------------------------------------------------------------------------- { Release(); } //----------------------------------------------------------------------------- -CommandListT::CommandListT(CommandListT&& other) noexcept +CommandList::CommandList(CommandList&& other) noexcept //--------------------------------------------------------------------------- { - assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandListT will not resize) + assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandList will not resize) } //----------------------------------------------------------------------------- -CommandListT& CommandListT::operator=(CommandListT && other) noexcept +CommandList& CommandList::operator=(CommandList && other) noexcept //----------------------------------------------------------------------------- { - assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandListT will not resize) + assert(0); // Currently move is not implemented for this class, but std::vector will not compile if a move (or copy) is not provided, we currently expect that the move will not get called (ie vector of CommandList will not resize) return *this; } //----------------------------------------------------------------------------- -bool CommandListT::Initialize(Vulkan* pVulkan, const std::string& Name, VkCommandBufferLevel CmdBuffLevel, uint32_t QueueIndex, TimerPoolBase* pGpuTimerPool) +bool CommandList::Initialize(Vulkan* pVulkan, const std::string& Name, CommandListBase::Type CmdBuffType, uint32_t QueueIndex, TimerPoolBase* pGpuTimerPool) //----------------------------------------------------------------------------- { m_Name = Name; @@ -58,17 +61,18 @@ bool CommandListT::Initialize(Vulkan* pVulkan, const std::string& Name, assert(m_GpuTimerQueries.empty()); // What type of command buffer are we dealing with - if (CmdBuffLevel == VK_COMMAND_BUFFER_LEVEL_PRIMARY) - { - m_IsPrimary = true; - } - else if (CmdBuffLevel == VK_COMMAND_BUFFER_LEVEL_SECONDARY) - { - m_IsPrimary = false; - } - else - { - LOGE("Command Buffer initialization with unknown level! (%d)", CmdBuffLevel); + VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + m_Type = Type::Primary; + switch (CmdBuffType) { + case Type::Primary: + break; + case Type::Secondary: + m_Type = Type::Secondary; + CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + break; + default: + LOGE( "Command Buffer initialization with unsupported type! (%d)", CmdBuffType ); + break; } // Store which queue this command list is using (index in to Vulkan::m_VulkanQueues) so we can submit to the associated device queue. @@ -80,7 +84,7 @@ bool CommandListT::Initialize(Vulkan* pVulkan, const std::string& Name, } //----------------------------------------------------------------------------- -bool CommandListT::Reset() +bool CommandList::Reset() //----------------------------------------------------------------------------- { VkResult RetVal; @@ -112,66 +116,80 @@ bool CommandListT::Reset() } //----------------------------------------------------------------------------- -bool CommandListT::Begin(VkCommandBufferUsageFlags CmdBuffUsage) +bool CommandList::Begin( VkCommandBufferUsageFlags CmdBuffUsage ) //----------------------------------------------------------------------------- { - assert(m_IsPrimary); - if (!m_IsPrimary) + assert(m_Type == Type::Primary); + if (m_Type != Type::Primary) { LOGE("Error! Trying to begin secondary command buffer without Framebuffer or RenderPass: %s", m_Name.c_str()); return false; } - return Begin(VK_NULL_HANDLE, VK_NULL_HANDLE, false, 0, CmdBuffUsage); + const VkCommandBufferBeginInfo CmdBeginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = CmdBuffUsage, + }; + return Begin( CmdBeginInfo ); } //----------------------------------------------------------------------------- -bool CommandListT::Begin( VkFramebuffer FrameBuffer, VkRenderPass RenderPass, bool IsSwapChainRenderPass, uint32_t SubPass, VkCommandBufferUsageFlags CmdBuffUsage) +bool CommandList::Begin( VkFramebuffer FrameBuffer, VkRenderPass RenderPass, bool IsSwapChainRenderPass, uint32_t SubPass, VkCommandBufferUsageFlags CmdBuffUsage ) //----------------------------------------------------------------------------- { - VkResult RetVal; - if (m_VkCommandBuffer == VK_NULL_HANDLE) { - LOGE("Error! Trying to begin command buffer before it has been initialized: %s", m_Name.c_str()); + LOGE( "Error! Trying to begin command buffer before it has been initialized: %s", m_Name.c_str() ); + return false; + } + + assert( m_Type == Type::Secondary ); + if (m_Type != Type::Secondary) + { + LOGE( "Error! Trying to begin primary command buffer with inheritance info (%s)", m_Name.c_str() ); return false; } - VkCommandBufferBeginInfo CmdBeginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; - CmdBeginInfo.flags = CmdBuffUsage; + VkCommandBufferInheritanceInfo InheritanceInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + .framebuffer = FrameBuffer, + .occlusionQueryEnable = VK_FALSE, + .queryFlags = 0, + .pipelineStatistics = 0, + }; + VkCommandBufferBeginInfo CmdBeginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = CmdBuffUsage, + .pInheritanceInfo = &InheritanceInfo, + }; - VkCommandBufferInheritanceInfo InheritanceInfo {VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO}; VkCommandBufferInheritanceRenderPassTransformInfoQCOM InheritanceInfoRenderPassTransform = {}; - // If this is a secondary command buffer it has inheritance information. - // If primary, the inheritance stuff is ignored. - if (!m_IsPrimary) + // Secondary command buffer MAY BE inside another render pass (compute can be in a secondary buffer and not have/inherit a renderder pass) + if (RenderPass != VK_NULL_HANDLE) { - InheritanceInfo.framebuffer = FrameBuffer; - InheritanceInfo.occlusionQueryEnable = VK_FALSE; - InheritanceInfo.queryFlags = 0; - InheritanceInfo.pipelineStatistics = 0; - - // If this is a secondary command buffer it MAY BE inside another render pass (compute can be in a secondary buffer and not have/inherit a renderder pass) - if (RenderPass != VK_NULL_HANDLE) - { - CmdBeginInfo.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + CmdBeginInfo.flags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - InheritanceInfo.renderPass = RenderPass; - InheritanceInfo.subpass = SubPass; + InheritanceInfo.renderPass = RenderPass; + InheritanceInfo.subpass = SubPass; - // We may also have to pass the inherited render pass transform down to the secondary buffer - if (IsSwapChainRenderPass && m_pVulkan->FillCommandBufferInheritanceRenderPassTransformInfoQCOM(InheritanceInfoRenderPassTransform)) - { - LOGI("VkCommandBufferInheritanceRenderPassTransformInfoQCOM (%s): Extents = (%d x %d)", m_Name.c_str(), InheritanceInfoRenderPassTransform.renderArea.extent.width, InheritanceInfoRenderPassTransform.renderArea.extent.height); - InheritanceInfo.pNext = &InheritanceInfoRenderPassTransform; - } + // We may also have to pass the inherited render pass transform down to the secondary buffer + if (IsSwapChainRenderPass && m_pVulkan->FillCommandBufferInheritanceRenderPassTransformInfoQCOM( InheritanceInfoRenderPassTransform )) + { + LOGI( "VkCommandBufferInheritanceRenderPassTransformInfoQCOM (%s): Extents = (%d x %d)", m_Name.c_str(), InheritanceInfoRenderPassTransform.renderArea.extent.width, InheritanceInfoRenderPassTransform.renderArea.extent.height ); + InheritanceInfo.pNext = &InheritanceInfoRenderPassTransform; } - CmdBeginInfo.pInheritanceInfo = &InheritanceInfo; } + return Begin( CmdBeginInfo ); +} + +//----------------------------------------------------------------------------- +bool CommandList::Begin( const VkCommandBufferBeginInfo& CmdBeginInfo ) +//----------------------------------------------------------------------------- +{ // By calling vkBeginCommandBuffer, cmdBuffer is put into the recording state. - RetVal = vkBeginCommandBuffer(m_VkCommandBuffer, &CmdBeginInfo); + VkResult RetVal = vkBeginCommandBuffer(m_VkCommandBuffer, &CmdBeginInfo ); if (!CheckVkError("vkBeginCommandBuffer()", RetVal)) { LOGE("Unable to begin command buffer: %s", m_Name.c_str()); @@ -190,27 +208,101 @@ bool CommandListT::Begin( VkFramebuffer FrameBuffer, VkRenderPass Render } //----------------------------------------------------------------------------- -bool CommandListT::BeginRenderPass(VkRect2D RenderExtent, float MinDepth, float MaxDepth, const std::span ClearColors, uint32_t NumColorBuffers, bool HasDepth, VkRenderPass RenderPass, bool IsSwapChainRenderPass, VkFramebuffer FrameBuffer, VkSubpassContents SubContents) +bool CommandList::Begin( VkFramebuffer FrameBuffer, const RenderPass& RenderPass, bool IsSwapChainRenderPass, uint32_t SubPass, VkCommandBufferUsageFlags CmdBuffUsage ) +//----------------------------------------------------------------------------- +{ + return Begin( FrameBuffer, RenderPass.mRenderPass, IsSwapChainRenderPass, SubPass, CmdBuffUsage ); +} + +//----------------------------------------------------------------------------- +bool CommandList::Begin( const Framebuffer& FrameBuffer, const RenderPass& RenderPass, bool IsSwapChainRenderPass, uint32_t SubPass, VkCommandBufferUsageFlags CmdBuffUsage ) +//----------------------------------------------------------------------------- +{ + return Begin( FrameBuffer.m_FrameBuffer, RenderPass.mRenderPass, IsSwapChainRenderPass, SubPass, CmdBuffUsage ); +} + +//----------------------------------------------------------------------------- +bool CommandList::Begin(const VkCommandBufferInheritanceRenderingInfo& DynamicRenderingInheritanceInfo, VkCommandBufferUsageFlags CmdBuffUsage) +//----------------------------------------------------------------------------- +{ + assert(m_Type == Type::Secondary); + if (m_Type != Type::Secondary) + { + LOGE("Error! Trying to begin primary command buffer with inheritance info (%s)", m_Name.c_str()); + return false; + } + + VkCommandBufferInheritanceInfo InheritanceInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + .pNext = &DynamicRenderingInheritanceInfo + }; + VkCommandBufferInheritanceRenderPassTransformInfoQCOM InheritanceInfoRenderPassTransform = {}; + + VkCommandBufferBeginInfo CmdBeginInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = CmdBuffUsage, + .pInheritanceInfo = &InheritanceInfo, + }; + + return Begin(CmdBeginInfo); +} + + +//----------------------------------------------------------------------------- +bool CommandList::Begin(const RenderContext& renderContext, VkCommandBufferUsageFlags cmdBuffUsage) +//----------------------------------------------------------------------------- +{ + assert( m_Type == Type::Secondary ); + if (m_Type != Type::Secondary) + { + LOGE( "Error! Trying to begin primary command buffer with inheritance info (%s)", m_Name.c_str() ); + return false; + } + + if (renderContext.IsDynamic()) + { + // dynamic rendering + const auto& dynamicRenderContext = std::get::DynamicRenderContextData>( renderContext.v ); + VkCommandBufferInheritanceRenderingInfo dynamicRenderingInheritanceInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + .flags = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT, + .colorAttachmentCount = (uint32_t)dynamicRenderContext.colorAttachmentFormats.size(), + .pColorAttachmentFormats = dynamicRenderContext.colorAttachmentFormats.data(), + .depthAttachmentFormat = dynamicRenderContext.depthAttachmentFormat, + .stencilAttachmentFormat = dynamicRenderContext.stencilAttachmentFormat, + .rasterizationSamples = EnumToVk( renderContext.msaa ) + }; + return Begin( dynamicRenderingInheritanceInfo, cmdBuffUsage ); + } + else + { + const auto& renderPassContext = std::get::RenderPassContextData>(renderContext.v); + + return Begin( renderPassContext.framebuffer, renderPassContext.renderPass, false, renderContext.subPass, cmdBuffUsage ); + } +} + +//----------------------------------------------------------------------------- +bool CommandList::BeginRenderPass(VkRect2D RenderExtent, float MinDepth, float MaxDepth, const std::span ClearColors, uint32_t NumColorBuffers, bool HasDepth, VkRenderPass RenderPass, bool IsSwapChainRenderPass, VkFramebuffer FrameBuffer, VkSubpassContents SubContents) //----------------------------------------------------------------------------- { VkResult RetVal = VK_SUCCESS; // ... set Viewport and Scissor for this pass ... - VkViewport Viewport = {}; - Viewport.x = (float)RenderExtent.offset.x; - Viewport.y = (float)RenderExtent.offset.y; - Viewport.width = (float)RenderExtent.extent.width; - Viewport.height = (float)RenderExtent.extent.height; - Viewport.minDepth = MinDepth; - Viewport.maxDepth = MaxDepth; - + const VkViewport Viewport{ + .width = (float)RenderExtent.extent.width, + .height = (float)RenderExtent.extent.height, + .minDepth = MinDepth, + .maxDepth = MaxDepth + }; vkCmdSetViewport(m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_VkCommandBuffer, 0, 1, &RenderExtent); + vkCmdSetScissor (m_VkCommandBuffer, 0, 1, &RenderExtent); // ... set clear values ... std::array ClearValues; uint32_t ClearValuesCount = 0; + assert(NumColorBuffers <= (ClearValues.size() - (HasDepth?1:0))); for (uint32_t i=0; i< NumColorBuffers; ++i) { ClearValues[ClearValuesCount].color = ClearColors[ClearColors.size() > NumColorBuffers ? ClearValuesCount : 0]; @@ -224,42 +316,170 @@ bool CommandListT::BeginRenderPass(VkRect2D RenderExtent, float MinDepth } // ... begin render pass ... - VkRenderPassBeginInfo RPBeginInfo = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; - RPBeginInfo.renderPass = RenderPass; - RPBeginInfo.renderArea = RenderExtent; - RPBeginInfo.clearValueCount = ClearValuesCount; - RPBeginInfo.pClearValues = ClearValues.data(); - VkRenderPassTransformBeginInfoQCOM RPTransformBeginInfoQCOM = {}; - - RPBeginInfo.framebuffer = FrameBuffer; - - if (IsSwapChainRenderPass && m_pVulkan->FillRenderPassTransformBeginInfoQCOM(RPTransformBeginInfoQCOM)) + fvk::VkRenderPassBeginInfo RPBeginInfo{{ + .renderPass = RenderPass, + .framebuffer = FrameBuffer, + .renderArea = RenderExtent, + .clearValueCount = ClearValuesCount, + .pClearValues = ClearValues.data() + }}; + + fvk::VkRenderPassTransformBeginInfoQCOM RPTransformBeginInfoQCOM = {}; + if (IsSwapChainRenderPass && m_pVulkan->FillRenderPassTransformBeginInfoQCOM(*RPTransformBeginInfoQCOM)) { //LOGI("RPTransformBeginInfoQCOM(%s): Pre swap Extents = (%d x %d) transform = %d", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, RPTransformBeginInfoQCOM.transform); - RPBeginInfo.pNext = &RPTransformBeginInfoQCOM; + RPBeginInfo.Add( RPTransformBeginInfoQCOM ); } //LOGI("BeginRenderPass(%s): Extents = (%d x %d) %s", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, (RPBeginInfo.pNext == &RPTransformBeginInfoQCOM) ? " (transformed)" : ""); - vkCmdBeginRenderPass(m_VkCommandBuffer, &RPBeginInfo, SubContents); - return true; + return BeginRenderPass(*RPBeginInfo, SubContents); +} + + +//----------------------------------------------------------------------------- +bool CommandList::BeginRenderPass(VkRect2D RenderExtent, float MinDepth, float MaxDepth, const std::span ClearColors, uint32_t NumColorBuffers, bool HasDepth, const RenderPass& RenderPass, bool IsSwapChainRenderPass, VkFramebuffer FrameBuffer, VkSubpassContents SubContents) +//----------------------------------------------------------------------------- +{ + return BeginRenderPass( RenderExtent, MinDepth, MaxDepth, ClearColors, NumColorBuffers, HasDepth, RenderPass.mRenderPass, IsSwapChainRenderPass, FrameBuffer, SubContents ); } //----------------------------------------------------------------------------- -bool CommandListT::BeginRenderPass(const CRenderTarget& renderTarget, VkRenderPass RenderPass, VkSubpassContents SubContents) +bool CommandList::BeginRenderPass(const RenderTarget& RenderTarget, VkRenderPass RenderPass, VkSubpassContents SubContents) //----------------------------------------------------------------------------- { VkRect2D Scissor = {}; Scissor.offset.x = 0; Scissor.offset.y = 0; - Scissor.extent.width = renderTarget.m_Width; - Scissor.extent.height = renderTarget.m_Height; + Scissor.extent.width = RenderTarget.m_Width; + Scissor.extent.height = RenderTarget.m_Height; - return BeginRenderPass(Scissor, 0.0f, 1.0f, { renderTarget.m_ClearColorValues.data(), renderTarget.m_ClearColorValues.size() }, renderTarget.GetNumColorLayers(), renderTarget.m_DepthFormat != TextureFormat::UNDEFINED, RenderPass, false, renderTarget.m_FrameBuffer, SubContents); + return BeginRenderPass(Scissor, 0.0f, 1.0f, { RenderTarget.m_ClearColorValues.data(), RenderTarget.m_ClearColorValues.size() }, RenderTarget.GetNumColorLayers(), RenderTarget.m_DepthFormat != TextureFormat::UNDEFINED, RenderPass, false, RenderTarget.m_FrameBuffer, SubContents); } //----------------------------------------------------------------------------- -bool CommandListT::NextSubpass( VkSubpassContents SubContents ) +bool CommandList::BeginRenderPass( const RenderTarget& RenderTarget, const RenderPass& RenderPass, VkSubpassContents SubContents ) +//----------------------------------------------------------------------------- +{ + return BeginRenderPass(RenderTarget, RenderPass.mRenderPass, SubContents); +} + +//----------------------------------------------------------------------------- +bool CommandList::BeginRenderPass( const Framebuffer& Framebuffer, const RenderPass& RenderPass, const RenderPassClearData& RenderPassClearData, VkSubpassContents SubContents ) +//----------------------------------------------------------------------------- +{ + VkResult RetVal = VK_SUCCESS; + + vkCmdSetViewport( m_VkCommandBuffer, 0, 1, &RenderPassClearData.viewport ); + vkCmdSetScissor( m_VkCommandBuffer, 0, 1, &RenderPassClearData.scissor); + + // ... begin render pass ... + fvk::VkRenderPassBeginInfo RPBeginInfo{{ + .renderPass = RenderPass.mRenderPass, + .framebuffer = Framebuffer.m_FrameBuffer, + .renderArea = RenderPassClearData.scissor, + .clearValueCount = (uint32_t)RenderPassClearData.clearValues.size(), + .pClearValues = RenderPassClearData.clearValues.data() + }}; + + //fvk::VkRenderPassTransformBeginInfoQCOM RPTransformBeginInfoQCOM = {}; + //if (IsSwapChainRenderPass && m_pVulkan->FillRenderPassTransformBeginInfoQCOM( *RPTransformBeginInfoQCOM )) + //{ + // //LOGI("RPTransformBeginInfoQCOM(%s): Pre swap Extents = (%d x %d) transform = %d", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, RPTransformBeginInfoQCOM.transform); + // RPBeginInfo.Add( RPTransformBeginInfoQCOM ); + //} + + //LOGI("BeginRenderPass(%s): Extents = (%d x %d) %s", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, (RPBeginInfo.pNext == &RPTransformBeginInfoQCOM) ? " (transformed)" : ""); + + return BeginRenderPass( *RPBeginInfo, SubContents ); +} + +//----------------------------------------------------------------------------- +bool CommandList::BeginSwapchainRenderPass( uint32_t SwapchainImageIndex, const RenderPass& RenderPass, const RenderPassClearData& RenderPassClearData, VkSubpassContents SubContents ) +//----------------------------------------------------------------------------- +{ + VkResult RetVal = VK_SUCCESS; + + vkCmdSetViewport( m_VkCommandBuffer, 0, 1, &RenderPassClearData.viewport ); + vkCmdSetScissor( m_VkCommandBuffer, 0, 1, &RenderPassClearData.scissor ); + + // ... begin render pass ... + fvk::VkRenderPassBeginInfo RPBeginInfo{{ + .renderPass = RenderPass.mRenderPass, + .framebuffer = m_pVulkan->m_SwapchainBuffers[SwapchainImageIndex].framebuffer.m_FrameBuffer, + .renderArea = RenderPassClearData.scissor, + .clearValueCount = (uint32_t)RenderPassClearData.clearValues.size(), + .pClearValues = RenderPassClearData.clearValues.data() + }}; + + fvk::VkRenderPassTransformBeginInfoQCOM RPTransformBeginInfoQCOM = {}; + if (m_pVulkan->FillRenderPassTransformBeginInfoQCOM( *RPTransformBeginInfoQCOM )) + { + //LOGI("RPTransformBeginInfoQCOM(%s): Pre swap Extents = (%d x %d) transform = %d", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, RPTransformBeginInfoQCOM.transform); + RPBeginInfo.Add( RPTransformBeginInfoQCOM ); + } + + //LOGI("BeginSwapchainRenderPass(%s): Extents = (%d x %d) %s", m_Name.c_str(), RPBeginInfo.renderArea.extent.width, RPBeginInfo.renderArea.extent.height, (RPBeginInfo.pNext == &RPTransformBeginInfoQCOM) ? " (transformed)" : ""); + + return BeginRenderPass( *RPBeginInfo, SubContents ); +} + +//----------------------------------------------------------------------------- +bool CommandList::BeginRenderPass( const RenderContext& renderContext, VkSubpassContents SubContents ) +//----------------------------------------------------------------------------- +{ + // ... begin render pass ... + fvk::VkRenderPassBeginInfo RPBeginInfo{ renderContext.GetRenderPassBeginInfo() }; + return BeginRenderPass( *RPBeginInfo, SubContents ); +} + +//----------------------------------------------------------------------------- +bool CommandList::BeginRenderPass( const VkRenderPassBeginInfo& RPBeginInfo, VkSubpassContents SubContents ) +//----------------------------------------------------------------------------- +{ + vkCmdBeginRenderPass(m_VkCommandBuffer, &RPBeginInfo, SubContents); + return true; +} + +//----------------------------------------------------------------------------- +void CommandList::BeginRenderPass( const VkRenderingInfo& renderingInfo ) +//----------------------------------------------------------------------------- +{ + /* + const RenderTarget& renderTarget; + std::array colorAttachments; + VkRenderingAttachmentInfo depthAttachment; + + for (auto i = 0; i < renderTarget.GetNumColorLayers(); ++i) + { + auto& image = renderTarget.m_ColorAttachments[i]; + colorAttachments[i] = { + .imageView = image.GetVkImageView(), + .imageLayout = image.GetVkImageLayout(), + .resolveMode = VK_RESOLVE_MODE_NONE, + .loadOp = + .clearValue = renderTarget.m_ClearColorValues[i], + } + } + + VkRenderingInfo renderingInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, + .renderArea = {0, 0, renderTarget.m_Width, renderTarget.m_Height}, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = colorAttachments.data(), + .pDepthAttachment = renderTarget.m_DepthAttachment ? &depthAttachment : nullptr, + .pStencilAttachment = VK_NULL_HANDLE, + }; + */ + + + auto* dynamicRenderingExt = m_pVulkan->GetExtension(); + dynamicRenderingExt->m_vkCmdBeginRenderingKHR( m_VkCommandBuffer, &renderingInfo ); +} + +//----------------------------------------------------------------------------- +bool CommandList::NextSubpass( VkSubpassContents SubContents ) //----------------------------------------------------------------------------- { vkCmdNextSubpass(m_VkCommandBuffer, SubContents); @@ -267,7 +487,7 @@ bool CommandListT::NextSubpass( VkSubpassContents SubContents ) } //----------------------------------------------------------------------------- -bool CommandListT::EndRenderPass() +bool CommandList::EndRenderPass() //----------------------------------------------------------------------------- { vkCmdEndRenderPass(m_VkCommandBuffer); @@ -275,7 +495,7 @@ bool CommandListT::EndRenderPass() } //----------------------------------------------------------------------------- -int CommandListT::StartGpuTimer(const std::string_view& timerName) +int CommandList::StartGpuTimer(const std::string_view& timerName) //----------------------------------------------------------------------------- { if (!m_GpuTimerPool) @@ -289,7 +509,7 @@ int CommandListT::StartGpuTimer(const std::string_view& timerName) } //----------------------------------------------------------------------------- -void CommandListT::StopGpuTimer(int timerId) +void CommandList::StopGpuTimer(int timerId) //----------------------------------------------------------------------------- { if (timerId < 0) @@ -300,7 +520,21 @@ void CommandListT::StopGpuTimer(int timerId) } //----------------------------------------------------------------------------- -bool CommandListT::End() +void CommandList::ExecuteCommands( const CommandList& secondaryCommands ) +//----------------------------------------------------------------------------- +{ + vkCmdExecuteCommands( m_VkCommandBuffer, 1, &secondaryCommands.m_VkCommandBuffer ); +} + +//----------------------------------------------------------------------------- +void CommandList::ExecuteCommands( VkCommandBuffer vkCommandBuffer ) +//----------------------------------------------------------------------------- +{ + vkCmdExecuteCommands( m_VkCommandBuffer, 1, &vkCommandBuffer ); +} + +//----------------------------------------------------------------------------- +bool CommandList::End() //----------------------------------------------------------------------------- { VkResult RetVal; @@ -323,7 +557,7 @@ bool CommandListT::End() } //----------------------------------------------------------------------------- -void CommandListT::QueueSubmit( const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkFence CompletionFence ) const +void CommandList::QueueSubmit( const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkFence CompletionFence ) const //----------------------------------------------------------------------------- { assert( m_VkCommandBuffer != VK_NULL_HANDLE ); @@ -331,7 +565,7 @@ void CommandListT::QueueSubmit( const std::span WaitS } //----------------------------------------------------------------------------- -void CommandListT::QueueSubmit(const std::span WaitSemaphores, const std::span SignalSemaphores, VkFence CompletionFence) const +void CommandList::QueueSubmit(const std::span WaitSemaphores, const std::span SignalSemaphores, VkFence CompletionFence) const //----------------------------------------------------------------------------- { constexpr uint32_t cMaxSemaphores = 8; @@ -373,7 +607,7 @@ void CommandListT::QueueSubmit(const std::span Wait } //----------------------------------------------------------------------------- -void CommandListT::QueueSubmit( const Vulkan::BufferIndexAndFence& CurrentVulkanBuffer, VkSemaphore renderCompleteSemaphore ) const +void CommandList::QueueSubmit( const Vulkan::BufferIndexAndFence& CurrentVulkanBuffer, VkSemaphore renderCompleteSemaphore ) const //----------------------------------------------------------------------------- { assert( m_VkCommandBuffer != VK_NULL_HANDLE ); @@ -382,9 +616,11 @@ void CommandListT::QueueSubmit( const Vulkan::BufferIndexAndFence& Curre //----------------------------------------------------------------------------- -void CommandListT::Release() +void CommandList::Release() //----------------------------------------------------------------------------- { + CommandListBase::Release(); + if (m_VkCommandBuffer != VK_NULL_HANDLE) { // Do not need to worry about the device or pool being NULL since we could @@ -393,10 +629,6 @@ void CommandListT::Release() m_VkCommandBuffer = nullptr; } - m_Name.clear(); - m_NumDrawCalls = 0; - m_NumTriangles = 0; - m_IsPrimary = true; m_QueueIndex = 0; m_GpuTimerPool = nullptr; m_GpuTimerQueries.clear(); diff --git a/framework/code/vulkan/commandBuffer.hpp b/framework/code/vulkan/commandBuffer.hpp index 731e1c8..75dcdbb 100644 --- a/framework/code/vulkan/commandBuffer.hpp +++ b/framework/code/vulkan/commandBuffer.hpp @@ -1,81 +1,94 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include -//#include -//#include -//#include -//#include -//#include -//#include "vulkan/vulkan.hpp" -//#include "vulkan/TextureFuncts.h" -//#include "texture/vulkan/texture.hpp" -//#include "material/vulkan/shaderModule.hpp" -// -//#include "system/os_common.h" -// // Need the vulkan wrapper #include "vulkan.hpp" #include "graphicsApi/commandList.hpp" -//class TimerPoolBase; -//typedef uint64_t VkFlags64; -//typedef VkFlags64 VkPipelineStageFlags2; -//typedef VkPipelineStageFlags2 VkPipelineStageFlags2KHR; - // Forward declarations -class CRenderTarget; class Vulkan; +class RenderPassClearData; class TimerPoolBase; class SemaphoreWait; class SemaphoreSignal; -template class CommandListT; -using CommandListVulkan = CommandListT; -using Wrap_VkCommandBuffer = CommandListT; // legacy name +template class CommandList; +using CommandListVulkan = CommandList; +template class Framebuffer; +template class RenderTarget; +template class RenderContext; + /// /// Wrapper around VkCommandBuffer. /// Simplifies creation, use and destruction of VkCommandBuffer. /// template<> -class CommandListT : public CommandList +class CommandList final : public CommandListBase { - CommandListT(const CommandListT&) = delete; - CommandListT& operator=(const CommandListT&) = delete; + CommandList(const CommandList&) = delete; + CommandList& operator=(const CommandList&) = delete; // Functions public: - CommandListT() noexcept; - ~CommandListT(); - CommandListT(CommandListT&&) noexcept; - CommandListT& operator=(CommandListT&&) noexcept; - - bool Initialize(Vulkan* pVulkan, const std::string& Name = {}, VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_PRIMARY, uint32_t QueueIndex = Vulkan::eGraphicsQueue, TimerPoolBase* pTimerPool = nullptr); + CommandList() noexcept; + ~CommandList(); + CommandList(CommandList&&) noexcept; + CommandList& operator=(CommandList&&) noexcept; + + // Curtesty + operator VkCommandBuffer() const { return m_VkCommandBuffer; } + + bool Initialize(Vulkan* pVulkan, const std::string& Name = {}, CommandListBase::Type CmdBuffType = Type::Primary, uint32_t QueueIndex = Vulkan::eGraphicsQueue, TimerPoolBase* pTimerPool = nullptr); //bool Initialize(Vulkan* pVulkan, const std::string& Name, VkCommandBufferLevel CmdBuffLevel, std::same_as auto QueueIndex, TimerPoolBase* pTimerPool = nullptr) = delete; - bool Reset(); // Begin primary command buffer bool Begin(VkCommandBufferUsageFlags CmdBuffUsage = 0); - // Begin secondary command buffer (with inheritance) + // Begin command buffer + bool Begin(const VkCommandBufferBeginInfo& CmdBeginInfo); + + // Begin secondary command buffer with inheritance bool Begin(VkFramebuffer FrameBuffer, VkRenderPass RenderPass, bool IsSwapChainRenderPass = false, uint32_t SubPass = 0, VkCommandBufferUsageFlags CmdBuffUsage = 0); + // Begin secondary command buffer with inheritance + bool Begin(VkFramebuffer FrameBuffer, const RenderPass& RenderPass, bool IsSwapChainRenderPass = false, uint32_t SubPass = 0, VkCommandBufferUsageFlags CmdBuffUsage = 0); + // Begin secondary command buffer with inheritance + bool Begin(const Framebuffer& FrameBuffer, const RenderPass& RenderPass, bool IsSwapChainRenderPass = false, uint32_t SubPass = 0, VkCommandBufferUsageFlags CmdBuffUsage = 0); + // Begin secondary command buffer (dynamic rendering) + bool Begin(const VkCommandBufferInheritanceRenderingInfo& DynamicRenderingInheritanceInfo, VkCommandBufferUsageFlags CmdBuffUsage); + // Begin secondary command buffer with inheritance + bool Begin(const RenderContext& RenderContext, VkCommandBufferUsageFlags CmdBuffUsage = 0 ); bool BeginRenderPass(VkRect2D RenderExtent, float MinDepth, float MaxDepth, const std::span ClearColors, uint32_t NumColorBuffers, bool HasDepth, VkRenderPass RenderPass, bool IsSwapChainRenderPass, VkFramebuffer FrameBuffer, VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - bool BeginRenderPass(const CRenderTarget& renderTarget, VkRenderPass RenderPass, VkSubpassContents SubContents); - bool NextSubpass( VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS ); + bool BeginRenderPass(VkRect2D RenderExtent, float MinDepth, float MaxDepth, const std::span ClearColors, uint32_t NumColorBuffers, bool HasDepth, const RenderPass& RenderPass, bool IsSwapChainRenderPass, VkFramebuffer FrameBuffer, VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + bool BeginRenderPass(const RenderTarget& renderTarget, VkRenderPass RenderPass, VkSubpassContents SubContents); + bool BeginRenderPass(const RenderTarget& renderTarget, const RenderPass& RenderPass, VkSubpassContents SubContents); + bool BeginRenderPass(const Framebuffer& Framebuffer, const RenderPass& RenderPass, const RenderPassClearData& RenderPassClearData, VkSubpassContents SubContents); + bool BeginRenderPass(const VkRenderPassBeginInfo&, VkSubpassContents SubContents); + bool BeginSwapchainRenderPass(uint32_t SwapchainImageIndex, const RenderPass& RenderPass, const RenderPassClearData& RenderPassClearData, VkSubpassContents SubContents); + bool BeginRenderPass(const RenderContext& renderContext, VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + void BeginRenderPass(const VkRenderingInfo& renderingInfo); // dynamic rendering + bool NextSubpass(VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); bool EndRenderPass(); int StartGpuTimer(const std::string_view& timerName); void StopGpuTimer(int timerId); + /// Execute contents of the supplied command buffer (must be a secondary command buffer) + void ExecuteCommands( const CommandList& secondaryCommands ); + void ExecuteCommands( VkCommandBuffer ); + bool End(); + /// @brief Get command list ready for re-recording + bool Reset(); + /// @brief Call vkQueueSubmit for this command buffer. /// Will submit to the compute or graphics queue (depending on m_QueueIndex). /// @param WaitSemaphores semaphores to wait upon before the gpu can execute this command buffer @@ -110,18 +123,12 @@ class CommandListT : public CommandList void QueueSubmit( const Vulkan::BufferIndexAndFence& CurrentVulkanBuffer, VkSemaphore renderCompleteSemaphore ) const; /// @brief Release the Vulkan resources used by this wrapper and cleanup. - void Release(); + void Release() override; private: // Attributes public: - std::string m_Name; - - uint32_t m_NumDrawCalls = 0; - uint32_t m_NumTriangles = 0; - - bool m_IsPrimary = true; uint32_t m_QueueIndex = 0; VkCommandBuffer m_VkCommandBuffer = VK_NULL_HANDLE; TimerPoolBase* m_GpuTimerPool = nullptr; diff --git a/framework/code/vulkan/extension.cpp b/framework/code/vulkan/extension.cpp index 6d47d0f..6a09033 100644 --- a/framework/code/vulkan/extension.cpp +++ b/framework/code/vulkan/extension.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,4 +9,3 @@ #include "extension.hpp" #include "system/os_common.h" #include - diff --git a/framework/code/vulkan/extension.hpp b/framework/code/vulkan/extension.hpp index 3893058..fcc2a98 100644 --- a/framework/code/vulkan/extension.hpp +++ b/framework/code/vulkan/extension.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -138,11 +138,17 @@ class ExtensionChain /// @brief Enumeration of the Vulkan extension type (eg Device extension or Instance extansion). -enum class VulkanExtensionType { eDevice, eInstance }; +enum class VulkanExtensionType { eDevice, eInstance, eLayer }; -/// @brief Enumeration of the Vulkan extension status (eg loading state of an extenstion). +/// @brief Enumeration of the Vulkan extension status (eg loading state of an extension). enum class VulkanExtensionStatus { eUninitialized, eOptional, eRequired, eLoaded }; +/// @brief Enumeration for how extension is to be loaded. +/// eDefault - request extension from vulkan normally (to spec). THIS IS THE OPTION TO USE ALMOST ALWAYS! +/// eForceLoad - ignore list of extensions reported by vulkan driver and request this extension anyways. This is for developement/private driver extensions use at your own risk. +/// eSkipRequest - if not in the list of extensions reported by vulkan dont request this extension, but act as if it is enabled (do initialization and mark extension as loaded). This is for developement/private driver extensions use at your own risk. +enum class VulkanExtensionLoadMode { eDefault, eForceLoad, eSkipRequest }; + /// @brief Class describing a Vulkan extension base for defining extension behaviour and hooks into Vulkan calls class VulkanExtensionBase { @@ -156,6 +162,7 @@ class VulkanExtensionBase const std::string Name; VulkanExtensionStatus Status = VulkanExtensionStatus::eUninitialized; + VulkanExtensionLoadMode LoadMode = VulkanExtensionLoadMode::eDefault; uint32_t Version = 0; ///< Extension version (from Driver) /// Register this extension with Vulkan. Typically will call Vulkan::AddExtensionHooks if the extension needs to hook in to any functionality. @@ -175,4 +182,6 @@ class VulkanExtension : public VulkanExtensionBase VulkanExtension( std::string name, VulkanExtensionStatus status = VulkanExtensionStatus::eUninitialized, uint32_t version = 0 ) noexcept : VulkanExtensionBase(name, status, version ) {} virtual ~VulkanExtension() = default; + + virtual void PostLoad() {} //< Function called after extension is loaded (eg after VkCreateDevice). Can be overridden. }; diff --git a/framework/code/vulkan/extensionHelpers.cpp b/framework/code/vulkan/extensionHelpers.cpp index 8693f1c..e30b9d9 100644 --- a/framework/code/vulkan/extensionHelpers.cpp +++ b/framework/code/vulkan/extensionHelpers.cpp @@ -1,12 +1,12 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "vulkan.hpp" -#include "extensionHelpers.hpp" +#include "extensionLib.hpp" #include "system/os_common.h" #include #include @@ -14,8 +14,8 @@ template<> void VulkanFunctionPointerExtensionHelper::Register(Vulkan& vulkan) { - vulkan.AddExtensionHooks( &m_InstanceFunctionPointerHook ); - vulkan.AddExtensionHooks( &m_DeviceFunctionPointerHook ); + vulkan.AddExtensionHooks(&m_InstanceFunctionPointerHook); + vulkan.AddExtensionHooks(&m_DeviceFunctionPointerHook); } template<> @@ -23,467 +23,3 @@ void VulkanFunctionPointerExtensionHelper::Regis { vulkan.AddExtensionHooks(&m_InstanceFunctionPointerHook); } - - -namespace ExtensionHelper { - -#if VK_EXT_mesh_shader - void Ext_VK_KHR_mesh_shader::LookupFunctionPointers(VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr) - { - m_vkCmdDrawMeshTasksEXT = (PFN_vkCmdDrawMeshTasksEXT)deviceProcAddr(vkDevice, "vkCmdDrawMeshTasksEXT"); - m_vkCmdDrawMeshTasksIndirectEXT = (PFN_vkCmdDrawMeshTasksIndirectEXT)deviceProcAddr(vkDevice, "vkCmdDrawMeshTasksIndirectEXT"); - m_vkCmdDrawMeshTasksIndirectCountEXT = (PFN_vkCmdDrawMeshTasksIndirectCountEXT)deviceProcAddr(vkDevice, "vkCmdDrawMeshTasksIndirectCountEXT"); - } - void Ext_VK_KHR_mesh_shader::PrintFeatures() const - { - LOGI("FeaturesMeshShader: "); - LOGI(" taskShader: %s", this->AvailableFeatures.taskShader ? "True" : "False"); - LOGI(" meshShader: %s", this->AvailableFeatures.meshShader ? "True" : "False"); - LOGI(" multiviewMeshShader: %s", this->AvailableFeatures.multiviewMeshShader ? "True" : "False"); - LOGI(" primitiveFragmentShadingRateMeshShader: %s", this->AvailableFeatures.primitiveFragmentShadingRateMeshShader ? "True" : "False"); - LOGI(" meshShaderQueries: %s", this->AvailableFeatures.meshShaderQueries ? "True" : "False"); - } - void Ext_VK_KHR_mesh_shader::PrintProperties() const - { - LOGI("VK_EXT_descriptor_indexing (VkPhysicalDeviceDescriptorIndexingProperties): "); - LOGI(" maxTaskWorkGroupTotalCount: %u", Properties.maxTaskWorkGroupTotalCount); - LOGI(" maxTaskWorkGroupCount: %u %u %u", Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0]); - LOGI(" maxTaskWorkGroupTotalCount: %u", Properties.maxTaskWorkGroupTotalCount); - LOGI(" maxTaskWorkGroupCount: %u %u %u", Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0]); - LOGI(" maxTaskWorkGroupInvocations: %u", Properties.maxTaskWorkGroupInvocations); - LOGI(" maxTaskWorkGroupSize: %u %u %u", Properties.maxTaskWorkGroupSize[0], Properties.maxTaskWorkGroupSize[0], Properties.maxTaskWorkGroupSize[0]); - LOGI(" maxTaskPayloadSize: %u", Properties.maxTaskPayloadSize); - LOGI(" maxTaskSharedMemorySize: %u", Properties.maxTaskSharedMemorySize); - LOGI(" maxTaskPayloadAndSharedMemorySize: %u", Properties.maxTaskPayloadAndSharedMemorySize); - LOGI(" maxMeshWorkGroupTotalCount: %u", Properties.maxMeshWorkGroupTotalCount); - LOGI(" maxMeshWorkGroupCount: %u %u %u", Properties.maxMeshWorkGroupCount[0], Properties.maxMeshWorkGroupCount[0], Properties.maxMeshWorkGroupCount[0]); - LOGI(" maxMeshWorkGroupInvocations: %u", Properties.maxMeshWorkGroupInvocations); - LOGI(" maxMeshWorkGroupSize: %u %u %u", Properties.maxMeshWorkGroupSize[0], Properties.maxMeshWorkGroupSize[0], Properties.maxMeshWorkGroupSize[0]); - LOGI(" maxMeshSharedMemorySize: %u", Properties.maxMeshSharedMemorySize); - LOGI(" maxMeshPayloadAndSharedMemorySize: %u", Properties.maxMeshPayloadAndSharedMemorySize); - LOGI(" maxMeshOutputMemorySize: %u", Properties.maxMeshOutputMemorySize); - LOGI(" maxMeshPayloadAndOutputMemorySize: %u", Properties.maxMeshPayloadAndOutputMemorySize); - LOGI(" maxMeshOutputComponents: %u", Properties.maxMeshOutputComponents); - LOGI(" maxMeshOutputVertices: %u", Properties.maxMeshOutputVertices); - LOGI(" maxMeshOutputPrimitives: %u", Properties.maxMeshOutputPrimitives); - LOGI(" maxMeshOutputLayers: %u", Properties.maxMeshOutputLayers); - LOGI(" maxMeshMultiviewViewCount: %u", Properties.maxMeshMultiviewViewCount); - LOGI(" meshOutputPerVertexGranularity: %u", Properties.meshOutputPerVertexGranularity); - LOGI(" meshOutputPerPrimitiveGranularity: %u", Properties.meshOutputPerPrimitiveGranularity); - LOGI(" maxPreferredTaskWorkGroupInvocations: %u", Properties.maxPreferredTaskWorkGroupInvocations); - LOGI(" maxPreferredMeshWorkGroupInvocations: %u", Properties.maxPreferredMeshWorkGroupInvocations); - LOGI(" prefersLocalInvocationVertexOutput: %s", Properties.prefersLocalInvocationVertexOutput ? "True" : "False"); - LOGI(" prefersLocalInvocationVertexOutput: %s", Properties.prefersLocalInvocationPrimitiveOutput ? "True" : "False"); - LOGI(" prefersLocalInvocationVertexOutput: %s", Properties.prefersCompactVertexOutput ? "True" : "False"); - LOGI(" prefersLocalInvocationVertexOutput: %s", Properties.prefersCompactPrimitiveOutput ? "True" : "False"); - } -#endif - - -#if VK_KHR_shader_float16_int8 - void Ext_VK_KHR_shader_float16_int8::PrintFeatures() const - { - LOGI( "FeaturesShaderFloat16Int8: " ); - LOGI( " shaderFloat16: %s", this->AvailableFeatures.shaderFloat16 ? "True" : "False" ); - LOGI( " shaderInt8: %s", this->AvailableFeatures.shaderInt8 ? "True" : "False" ); - } -#endif // VK_KHR_shader_float16_int8 - -#if VK_EXT_shader_image_atomic_int64 - void Ext_VK_EXT_shader_image_atomic_int64::PrintFeatures() const - { - LOGI( "FeaturesShaderImageAtomicInt64: " ); - LOGI( " shaderImageInt64Atomics: %s", this->AvailableFeatures.shaderImageInt64Atomics ? "True" : "False" ); - LOGI( " sparseImageInt64Atomics: %s", this->AvailableFeatures.sparseImageInt64Atomics ? "True" : "False" ); - } -#endif // VK_EXT_shader_image_atomic_int64 - -#if VK_EXT_index_type_uint8 - void Ext_VK_EXT_index_type_uint8::PrintFeatures() const - { - LOGI( "VK_EXT_index_type_uint8 (VkPhysicalDeviceIndexTypeUint8FeaturesEXT): " ); - LOGI( " indexTypeUint8: %s", this->AvailableFeatures.indexTypeUint8 ? "True" : "False" ); - } -#endif // VK_EXT_index_type_uint8 - -#if VK_KHR_shader_subgroup_extended_types - void Ext_VK_KHR_shader_subgroup_extended_types::PrintFeatures() const - { - LOGI( "FeaturesShaderSubgroupExtendedTypes: " ); - LOGI( " shaderSubgroupExtendedTypes: %s", this->AvailableFeatures.shaderSubgroupExtendedTypes ? "True" : "False" ); - } -#endif // VK_KHR_shader_subgroup_extended_types - -#if VK_EXT_descriptor_indexing - void Ext_VK_EXT_descriptor_indexing::PrintFeatures() const - { - LOGI( "VK_EXT_descriptor_indexing (VkPhysicalDeviceDescriptorIndexingFeatures): " ); - LOGI( " shaderInputAttachmentArrayDynamicIndexing: %s", this->AvailableFeatures.shaderInputAttachmentArrayDynamicIndexing ? "True" : "False" ); - LOGI( " shaderUniformTexelBufferArrayDynamicIndexing: %s", this->AvailableFeatures.shaderUniformTexelBufferArrayDynamicIndexing ? "True" : "False" ); - LOGI( " shaderStorageTexelBufferArrayDynamicIndexing: %s", this->AvailableFeatures.shaderStorageTexelBufferArrayDynamicIndexing ? "True" : "False" ); - LOGI( " shaderUniformBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderUniformBufferArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderSampledImageArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderSampledImageArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderStorageBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageBufferArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderStorageImageArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageImageArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderInputAttachmentArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderInputAttachmentArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderUniformTexelBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderUniformTexelBufferArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " shaderStorageTexelBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageTexelBufferArrayNonUniformIndexing ? "True" : "False" ); - LOGI( " descriptorBindingUniformBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingUniformBufferUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingSampledImageUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingSampledImageUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingStorageImageUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageImageUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingStorageBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageBufferUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingUniformTexelBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingUniformTexelBufferUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingStorageTexelBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageTexelBufferUpdateAfterBind ? "True" : "False" ); - LOGI( " descriptorBindingUpdateUnusedWhilePending: %s", this->AvailableFeatures.descriptorBindingUpdateUnusedWhilePending ? "True" : "False" ); - LOGI( " descriptorBindingPartiallyBound: %s", this->AvailableFeatures.descriptorBindingPartiallyBound ? "True" : "False" ); - LOGI( " descriptorBindingVariableDescriptorCount: %s", this->AvailableFeatures.descriptorBindingVariableDescriptorCount ? "True" : "False" ); - LOGI( " runtimeDescriptorArray: %s", this->AvailableFeatures.runtimeDescriptorArray ? "True" : "False" ); - } - - void Ext_VK_EXT_descriptor_indexing::PrintProperties() const - { - LOGI( "VK_EXT_descriptor_indexing (VkPhysicalDeviceDescriptorIndexingProperties): " ); - LOGI( " maxUpdateAfterBindDescriptorsInAllPools: %u", Properties.maxUpdateAfterBindDescriptorsInAllPools ); - LOGI( " shaderUniformBufferArrayNonUniformIndexingNative: %s", Properties.shaderUniformBufferArrayNonUniformIndexingNative ? "True" : "False" ); - LOGI( " shaderSampledImageArrayNonUniformIndexingNative: %s", Properties.shaderSampledImageArrayNonUniformIndexingNative ? "True" : "False" ); - LOGI( " shaderStorageBufferArrayNonUniformIndexingNative: %s", Properties.shaderStorageBufferArrayNonUniformIndexingNative ? "True" : "False" ); - LOGI( " shaderStorageImageArrayNonUniformIndexingNative: %s", Properties.shaderStorageImageArrayNonUniformIndexingNative ? "True" : "False" ); - LOGI( " shaderInputAttachmentArrayNonUniformIndexingNative: %s", Properties.shaderInputAttachmentArrayNonUniformIndexingNative ? "True" : "False" ); - LOGI( " robustBufferAccessUpdateAfterBind: %s", Properties.robustBufferAccessUpdateAfterBind ? "True" : "False" ); - LOGI( " quadDivergentImplicitLod: %s", Properties.quadDivergentImplicitLod ? "True" : "False" ); - LOGI( " maxPerStageDescriptorUpdateAfterBindSamplers: %u", Properties.maxPerStageDescriptorUpdateAfterBindSamplers ); - LOGI( " maxPerStageDescriptorUpdateAfterBindUniformBuffers: %u", Properties.maxPerStageDescriptorUpdateAfterBindUniformBuffers ); - LOGI( " maxPerStageDescriptorUpdateAfterBindStorageBuffers: %u", Properties.maxPerStageDescriptorUpdateAfterBindStorageBuffers ); - LOGI( " maxPerStageDescriptorUpdateAfterBindSampledImages: %u", Properties.maxPerStageDescriptorUpdateAfterBindSampledImages ); - LOGI( " maxPerStageDescriptorUpdateAfterBindStorageImages: %u", Properties.maxPerStageDescriptorUpdateAfterBindStorageImages ); - LOGI( " maxPerStageDescriptorUpdateAfterBindInputAttachments: %u", Properties.maxPerStageDescriptorUpdateAfterBindInputAttachments ); - LOGI( " maxPerStageUpdateAfterBindResources: %u", Properties.maxPerStageUpdateAfterBindResources ); - LOGI( " maxDescriptorSetUpdateAfterBindSamplers: %u", Properties.maxDescriptorSetUpdateAfterBindSamplers ); - LOGI( " maxDescriptorSetUpdateAfterBindUniformBuffers: %u", Properties.maxDescriptorSetUpdateAfterBindUniformBuffers ); - LOGI( " maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: %u", Properties.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic ); - LOGI( " maxDescriptorSetUpdateAfterBindStorageBuffers: %u", Properties.maxDescriptorSetUpdateAfterBindStorageBuffers ); - LOGI( " maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: %u", Properties.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic ); - LOGI( " maxDescriptorSetUpdateAfterBindSampledImages: %u", Properties.maxDescriptorSetUpdateAfterBindSampledImages ); - LOGI( " maxDescriptorSetUpdateAfterBindStorageImages: %u", Properties.maxDescriptorSetUpdateAfterBindStorageImages ); - LOGI( " maxDescriptorSetUpdateAfterBindInputAttachments: %u", Properties.maxDescriptorSetUpdateAfterBindInputAttachments ); - } -#endif // VK_EXT_descriptor_indexing - -#if VK_KHR_8bit_storage - void Ext_VK_KHR_8bit_storage::PrintFeatures() const - { - LOGI( "VK_KHR_8bit_storage (VkPhysicalDevice8BitStorageFeaturesKHR): " ); - LOGI( " storageBuffer8BitAccess: %s", this->AvailableFeatures.storageBuffer8BitAccess ? "True" : "False" ); - LOGI( " uniformAndStorageBuffer8BitAccess: %s", this->AvailableFeatures.uniformAndStorageBuffer8BitAccess ? "True" : "False" ); - LOGI( " storagePushConstant8: %s", this->AvailableFeatures.storagePushConstant8 ? "True" : "False" ); - } -#endif // VK_KHR_8bit_storage - -#if VK_KHR_portability_subset - void Ext_VK_KHR_portability_subset::PrintFeatures() const - { - LOGI( "VK_KHR_portability_subset (VkPhysicalDevicePortabilitySubsetFeaturesKHR): " ); - LOGI( " constantAlphaColorBlendFactors: %s", this->AvailableFeatures.constantAlphaColorBlendFactors ? "True" : "False" ); - LOGI( " events: %s", this->AvailableFeatures.events ? "True" : "False" ); - LOGI( " imageViewFormatReinterpretation: %s", this->AvailableFeatures.imageViewFormatReinterpretation ? "True" : "False" ); - LOGI( " imageViewFormatSwizzle: %s", this->AvailableFeatures.imageViewFormatSwizzle ? "True" : "False" ); - LOGI( " imageView2DOn3DImage: %s", this->AvailableFeatures.imageView2DOn3DImage ? "True" : "False" ); - LOGI( " multisampleArrayImage: %s", this->AvailableFeatures.multisampleArrayImage ? "True" : "False" ); - LOGI( " mutableComparisonSamplers: %s", this->AvailableFeatures.mutableComparisonSamplers ? "True" : "False" ); - LOGI( " pointPolygons: %s", this->AvailableFeatures.pointPolygons ? "True" : "False" ); - LOGI( " samplerMipLodBias: %s", this->AvailableFeatures.samplerMipLodBias ? "True" : "False" ); - LOGI( " separateStencilMaskRef: %s", this->AvailableFeatures.separateStencilMaskRef ? "True" : "False" ); - LOGI( " shaderSampleRateInterpolationFunctions: %s", this->AvailableFeatures.shaderSampleRateInterpolationFunctions ? "True" : "False" ); - LOGI( " tessellationIsolines: %s", this->AvailableFeatures.tessellationIsolines ? "True" : "False" ); - LOGI( " tessellationPointMode: %s", this->AvailableFeatures.tessellationPointMode ? "True" : "False" ); - LOGI( " triangleFans: %s", this->AvailableFeatures.triangleFans ? "True" : "False" ); - LOGI( " vertexAttributeAccessBeyondStride: %s", this->AvailableFeatures.vertexAttributeAccessBeyondStride ? "True" : "False" ); - } - void Ext_VK_KHR_portability_subset::PrintProperties() const - { - LOGI( "VK_KHR_portability_subset (VkPhysicalDevicePortabilitySubsetPropertiesKHR): " ); - LOGI( " minVertexInputBindingStrideAlignment: %u", this->Properties.minVertexInputBindingStrideAlignment ); - } -#endif // VK_KHR_portability_subset - -#if VK_KHR_fragment_shading_rate - void Ext_VK_KHR_fragment_shading_rate::PrintFeatures() const - { - LOGI( "VK_KHR_fragment_shading_rate (VkPhysicalDeviceFragmentShadingRateFeaturesKHR): " ); - LOGI( " pipelineFragmentShadingRate: %s", this->AvailableFeatures.pipelineFragmentShadingRate ? "True" : "False" ); - LOGI( " primitiveFragmentShadingRate: %s", this->AvailableFeatures.primitiveFragmentShadingRate ? "True" : "False" ); - LOGI( " attachmentFragmentShadingRate: %s", this->AvailableFeatures.attachmentFragmentShadingRate ? "True" : "False" ); - } - void Ext_VK_KHR_fragment_shading_rate::PrintProperties() const - { - LOGI( "VK_KHR_fragment_shading_rate (VkPhysicalDeviceFragmentShadingRatePropertiesKHR): " ); - LOGI( " minFragmentDensityTexelSize: %ux%u", this->Properties.minFragmentShadingRateAttachmentTexelSize.width, this->Properties.minFragmentShadingRateAttachmentTexelSize.height ); - LOGI( " maxFragmentDensityTexelSize: %ux%u", this->Properties.maxFragmentShadingRateAttachmentTexelSize.width, this->Properties.maxFragmentShadingRateAttachmentTexelSize.height ); - LOGI( " maxFragmentShadingRateAttachmentTexelSizeAspectRatio: %u", this->Properties.maxFragmentShadingRateAttachmentTexelSizeAspectRatio ); - LOGI( " primitiveFragmentShadingRateWithMultipleViewports: %s", this->Properties.primitiveFragmentShadingRateWithMultipleViewports ? "True" : "False" ); - LOGI( " layeredShadingRateAttachments: %s", this->Properties.layeredShadingRateAttachments ? "True" : "False" ); - LOGI( " fragmentShadingRateNonTrivialCombinerOps: %s", this->Properties.fragmentShadingRateNonTrivialCombinerOps ? "True" : "False" ); - LOGI( " maxFragmentSize: %ux%u", this->Properties.maxFragmentSize.width, this->Properties.maxFragmentSize.height ); - LOGI( " maxFragmentSizeAspectRatio: %u", this->Properties.maxFragmentSizeAspectRatio ); - LOGI( " maxFragmentShadingRateCoverageSamples: %u", this->Properties.maxFragmentShadingRateCoverageSamples ); - LOGI( " maxFragmentShadingRateRasterizationSamples: 0x%02x", this->Properties.maxFragmentShadingRateRasterizationSamples ); - LOGI( " fragmentShadingRateWithShaderDepthStencilWrites: %s", this->Properties.fragmentShadingRateWithShaderDepthStencilWrites ? "True" : "False" ); - LOGI( " fragmentShadingRateWithSampleMask: %s", this->Properties.fragmentShadingRateWithSampleMask ? "True" : "False" ); - LOGI( " fragmentShadingRateWithShaderSampleMask: %s", this->Properties.fragmentShadingRateWithShaderSampleMask ? "True" : "False" ); - LOGI( " fragmentShadingRateWithConservativeRasterization: %s", this->Properties.fragmentShadingRateWithConservativeRasterization ? "True" : "False" ); - LOGI( " fragmentShadingRateWithFragmentShaderInterlock: %s", this->Properties.fragmentShadingRateWithFragmentShaderInterlock ? "True" : "False" ); - LOGI( " fragmentShadingRateWithCustomSampleLocations: %s", this->Properties.fragmentShadingRateWithCustomSampleLocations ? "True" : "False" ); - LOGI( " fragmentShadingRateStrictMultiplyCombiner: %s", this->Properties.fragmentShadingRateStrictMultiplyCombiner ? "True" : "False" ); - } -#endif // VK_KHR_fragment_shading_rate - -#if VK_KHR_create_renderpass2 - void Ext_VK_KHR_create_renderpass2::LookupFunctionPointers( VkInstance vkInstance ) - { - m_vkCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCreateRenderPass2KHR" ); - m_vkCmdBeginRenderPass2KHR = (PFN_vkCmdBeginRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdBeginRenderPass2KHR" ); - m_vkCmdNextSubpass2KHR = (PFN_vkCmdNextSubpass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdNextSubpass2KHR" ); - m_vkCmdEndRenderPass2KHR = (PFN_vkCmdEndRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdEndRenderPass2KHR" ); - } -#endif // VK_KHR_create_renderpass2 - -#if VK_KHR_draw_indirect_count - void Ext_VK_KHR_draw_indirect_count::LookupFunctionPointers( VkInstance vkInstance ) - { - m_vkCmdDrawIndirectCountKHR = (PFN_vkCmdDrawIndirectCountKHR)vkGetInstanceProcAddr( vkInstance, "vkCmdDrawIndirectCountKHR" ); - m_vkCmdDrawIndexedIndirectCountKHR = (PFN_vkCmdDrawIndexedIndirectCountKHR)vkGetInstanceProcAddr( vkInstance, "vkCmdDrawIndexedIndirectCountKHR" ); - } -#endif // VK_KHR_draw_indirect_count - -#if VK_EXT_debug_utils - bool Ext_VK_EXT_debug_utils::SetDebugUtilsObjectName( VkDevice vkDevice, uint64_t object, VkObjectType objectType, const char* name ) const - { - if (m_vkSetDebugUtilsObjectNameEXT == nullptr) - return false; - VkDebugUtilsObjectNameInfoEXT nameInfo { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT }; - nameInfo.objectType = objectType; - nameInfo.objectHandle = object; - nameInfo.pObjectName = name; - return m_vkSetDebugUtilsObjectNameEXT( vkDevice, &nameInfo ) == VK_SUCCESS; - } -#endif // VK_EXT_debug_utils - -#if VK_EXT_debug_marker - bool Ext_VK_EXT_debug_marker::DebugMarkerSetObjectName( VkDevice vkDevice, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char* name ) const - { - if (m_vkDebugMarkerSetObjectNameEXT == nullptr) - return false; - VkDebugMarkerObjectNameInfoEXT markerInfo { VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT }; - markerInfo.objectType = objectType; - markerInfo.object = object; - markerInfo.pObjectName = name; - return m_vkDebugMarkerSetObjectNameEXT( vkDevice, &markerInfo ) == VK_SUCCESS; - } -#endif // VK_EXT_debug_marker - - static std::string GetSubgroupStagesString( VkShaderStageFlags Stages ) - { - std::string StagesString; - static const char* SubgroupStages[] = { "Vertex", "Tessellation Control", "Tessellation Evaluation", "Geometry", "Fragment", "Compute" }; - int i = 0; - for (; i < sizeof( SubgroupStages ) / sizeof( *SubgroupStages ); ++i) - { - if ((Stages & (1 << i)) != 0) - { - StagesString.append( SubgroupStages[i] ); - StagesString.append( ", " ); - } - } - // remainder - if ((Stages & ~((1 << i) - 1)) != 0) - { - StagesString.append( std::to_string( Stages & ~((1 << i) - 1) ) ); - } - return StagesString; - } - - -#if VK_EXT_subgroup_size_control - void Ext_VK_EXT_subgroup_size_control::PrintFeatures() const - { - LOGI( "VK_EXT_subgroup_size_control (VkPhysicalDeviceSubgroupSizeControlFeaturesEXT): " ); - LOGI( " subgroupSizeControl: %s", this->AvailableFeatures.subgroupSizeControl ? "True" : "False" ); - LOGI( " computeFullSubgroups: %s", this->AvailableFeatures.computeFullSubgroups ? "True" : "False" ); - } - - void Ext_VK_EXT_subgroup_size_control::PrintProperties() const - { - LOGI( "VK_EXT_subgroup_size_control (VkPhysicalDeviceSubgroupSizeControlPropertiesEXT): " ); - LOGI( " minSubgroupSize: %u", this->Properties.minSubgroupSize ); - LOGI( " maxSubgroupSize: %u", this->Properties.maxSubgroupSize ); - LOGI( " maxComputeWorkgroupSubgroups: %u", this->Properties.maxComputeWorkgroupSubgroups ); - auto SupportedStages = GetSubgroupStagesString( this->Properties.requiredSubgroupSizeStages ); - LOGI( " requiredSubgroupSizeStages: %s", SupportedStages.c_str() ); - } -#endif // VK_EXT_subgroup_size_control - -#if VK_EXT_host_query_reset - void Ext_VK_EXT_host_query_reset::PrintFeatures() const - { - LOGI( "VK_EXT_host_query_reset (VkPhysicalDeviceHostQueryResetFeaturesEXT): " ); - LOGI( " hostQueryReset: %s", this->AvailableFeatures.hostQueryReset ? "True" : "False" ); - } -#endif // VK_EXT_host_query_reset - -#if VK_ARM_tensors - void Ext_VK_ARM_tensors::PrintFeatures() const - { - LOGI("VK_ARM_tensors (VkPhysicalDeviceTensorFeaturesARM):"); - LOGI(" tensorNonPacked: %s", this->AvailableFeatures.tensorNonPacked ? "True" : "False"); - LOGI(" shaderTensorAccess: %s", this->AvailableFeatures.shaderTensorAccess ? "True" : "False"); - LOGI(" shaderStorageTensorArrayDynamicIndexing: %s", this->AvailableFeatures.shaderStorageTensorArrayDynamicIndexing ? "True" : "False"); - LOGI(" shaderStorageTensorArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageTensorArrayNonUniformIndexing ? "True" : "False"); - LOGI(" descriptorBindingStorageTensorUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageTensorUpdateAfterBind ? "True" : "False"); - LOGI(" tensors: %s", this->AvailableFeatures.tensors ? "True" : "False"); - } - - void Ext_VK_ARM_tensors::PrintProperties() const - { - LOGI("VK_ARM_tensors (VkPhysicalDeviceTensorPropertiesARM):"); - LOGI(" maxTensorDimensionCount: %u", this->Properties.maxTensorDimensionCount); - LOGI(" maxTensorElements: %" PRIu64, this->Properties.maxTensorElements); - LOGI(" maxPerDimensionTensorElements: %" PRIu64, this->Properties.maxPerDimensionTensorElements); - LOGI(" maxTensorStride: %" PRId64, this->Properties.maxTensorStride); - LOGI(" maxTensorSize: %" PRIu64, this->Properties.maxTensorSize); - LOGI(" maxTensorShaderAccessArrayLength: %u", this->Properties.maxTensorShaderAccessArrayLength); - LOGI(" maxTensorShaderAccessSize: %u", this->Properties.maxTensorShaderAccessSize); - LOGI(" maxDescriptorSetStorageTensors: %u", this->Properties.maxDescriptorSetStorageTensors); - LOGI(" maxPerStageDescriptorSetStorageTensors: %u", this->Properties.maxPerStageDescriptorSetStorageTensors); - LOGI(" maxDescriptorSetUpdateAfterBindStorageTensors: %u", this->Properties.maxDescriptorSetUpdateAfterBindStorageTensors); - LOGI(" maxPerStageDescriptorUpdateAfterBindStorageTensors: %u", this->Properties.maxPerStageDescriptorUpdateAfterBindStorageTensors); - LOGI(" shaderStorageTensorArrayNonUniformIndexingNative: %s", this->Properties.shaderStorageTensorArrayNonUniformIndexingNative ? "True" : "False"); - LOGI(" shaderTensorSupportedStages: 0x%08X", this->Properties.shaderTensorSupportedStages); - } -#endif // VK_ARM_tensors - -#if VK_ARM_data_graph - void Ext_VK_ARM_data_graph::PrintFeatures() const - { - LOGI("VK_ARM_data_graph (VkPhysicalDeviceDataGraphFeaturesARM):"); - LOGI(" dataGraph: %s", this->AvailableFeatures.dataGraph ? "True" : "False"); - LOGI(" dataGraphUpdateAfterBind: %s", this->AvailableFeatures.dataGraphUpdateAfterBind ? "True" : "False"); - LOGI(" dataGraphSpecializationConstants: %s", this->AvailableFeatures.dataGraphSpecializationConstants ? "True" : "False"); - LOGI(" dataGraphDescriptorBuffer: %s", this->AvailableFeatures.dataGraphDescriptorBuffer ? "True" : "False"); - LOGI(" dataGraphShaderModule: %s", this->AvailableFeatures.dataGraphShaderModule ? "True" : "False"); - } -#endif // VK_ARM_data_graph -#if VK_KHR_timeline_semaphore - void Ext_VK_KHR_timeline_semaphore::PrintFeatures() const - { - LOGI("VK_KHR_timeline_semaphore (VkPhysicalDeviceTimelineSemaphoreFeaturesKHR): "); - LOGI(" timelineSemaphore: %s", this->AvailableFeatures.timelineSemaphore ? "True" : "False"); - } - void Ext_VK_KHR_timeline_semaphore::PrintProperties() const - { - LOGI("VK_KHR_timeline_semaphore (VkPhysicalDeviceTimelineSemaphorePropertiesKHR): "); - LOGI(" maxTimelineSemaphoreValueDifference: %" PRIu64, this->Properties.maxTimelineSemaphoreValueDifference); - } -#endif // VK_KHR_timeline_semaphore - -#if VK_KHR_synchronization2 - void Ext_VK_KHR_synchronization2::PrintFeatures() const - { - LOGI("Ext_VK_KHR_synchronization2 (VkPhysicalDeviceSynchronization2FeaturesKHR): "); - LOGI(" synchronization2: %s", this->AvailableFeatures.synchronization2 ? "True" : "False"); - } - void Ext_VK_KHR_synchronization2::LookupFunctionPointers(VkInstance vkInstance) - { - m_vkQueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)vkGetInstanceProcAddr(vkInstance, "vkQueueSubmit2KHR"); - m_vkCmdSetEvent2KHR = (PFN_vkCmdSetEvent2KHR)vkGetInstanceProcAddr(vkInstance, "vkCmdSetEvent2KHR"); - m_vkCmdResetEvent2KHR = (PFN_vkCmdResetEvent2KHR)vkGetInstanceProcAddr(vkInstance, "vkCmdResetEvent2KHR"); - m_vkCmdWaitEvents2KHR = (PFN_vkCmdWaitEvents2KHR)vkGetInstanceProcAddr(vkInstance, "vkCmdWaitEvents2KHR"); - m_vkCmdPipelineBarrier2KHR = (PFN_vkCmdPipelineBarrier2KHR)vkGetInstanceProcAddr(vkInstance, "vkCmdPipelineBarrier2KHR"); - m_vkCmdWriteTimestamp2KHR = (PFN_vkCmdWriteTimestamp2KHR)vkGetInstanceProcAddr(vkInstance, "vkCmdWriteTimestamp2KHR"); - } -#endif // VK_KHR_synchronization2 - -#if VK_KHR_get_physical_device_properties2 - - void Ext_VK_KHR_get_physical_device_properties2::LookupFunctionPointers(VkInstance vkInstance) - { - m_vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceFeatures2KHR"); - m_vkGetPhysicalDeviceFormatProperties2KHR = (PFN_vkGetPhysicalDeviceFormatProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceFormatProperties2KHR" ); - m_vkGetPhysicalDeviceImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceImageFormatProperties2KHR" ); - m_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceMemoryProperties2KHR" ); - m_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceProperties2KHR" ); - } - -#endif // VK_KHR_get_physical_device_properties2 - -#if VK_KHR_surface - - void Ext_VK_KHR_surface::LookupFunctionPointers( VkInstance vkInstance ) - { - m_vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr( vkInstance, "vkDestroySurfaceKHR" ); - m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" ); - m_vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceFormatsKHR" ); - m_vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfacePresentModesKHR" ); - m_vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceSupportKHR" ); - } - -#endif // VK_KHR_surface - -#if VK_KHR_get_surface_capabilities2 - - void Ext_VK_KHR_get_surface_capabilities2::LookupFunctionPointers( VkInstance vkInstance ) - { - m_vkGetPhysicalDeviceSurfaceCapabilities2KHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); - m_vkGetPhysicalDeviceSurfaceFormats2KHR = (PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceSurfaceFormats2KHR"); - } - -#endif // VK_KHR_get_surface_capabilities2 - -#if VK_QCOM_tile_properties - - void Ext_VK_QCOM_tile_properties::LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr ) - { - m_vkGetDynamicRenderingTilePropertiesQCOM = (PFN_vkGetDynamicRenderingTilePropertiesQCOM)deviceProcAddr( vkDevice, "vkGetDynamicRenderingTilePropertiesQCOM" ); - m_vkGetFramebufferTilePropertiesQCOM = (PFN_vkGetFramebufferTilePropertiesQCOM)deviceProcAddr( vkDevice, "vkGetFramebufferTilePropertiesQCOM" ); - } - - void Ext_VK_QCOM_tile_properties::PrintFeatures() const - { - LOGI( "TileProperties: " ); - } - -#endif // VK_QCOM_tile_properties - - void Vulkan_SubgroupPropertiesHook::PrintProperties() const - { - LOGI( "VkPhysicalDeviceSubgroupProperties: " ); - LOGI( " subgroupSize: %d", Properties.subgroupSize ); - auto SupportedStages = GetSubgroupStagesString( Properties.supportedStages ); - LOGI( " supportedStages: %s", SupportedStages.c_str() ); - - std::string SupportedOperations; - static const char* SubgroupOperations[] = { "Basic", "Vote", "Arithmetic", "Ballot", "Shuffle", "Shuffle Relative", "Clustered", "Quad" }; - for (int i = 0; i < sizeof( SubgroupOperations ) / sizeof( *SubgroupOperations ); ++i) - { - if ((Properties.supportedOperations & (1 << i)) != 0) - { - SupportedOperations.append( SubgroupOperations[i] ); - SupportedOperations.append( ", " ); - } - } - LOGI( " supportedOperations: %s", SupportedOperations.c_str() ); - - LOGI( " quadOperationsInAllStages: %s", Properties.quadOperationsInAllStages ? "True" : "False" ); - } - - - void Vulkan_StorageFeaturesHook::PrintFeatures() const - { - LOGI( "FeaturesStorage16Bit: " ); - LOGI( " storageBuffer16BitAccess: %s", this->AvailableFeatures.storageBuffer16BitAccess ? "True" : "False" ); - LOGI( " uniformAndStorageBuffer16BitAccess: %s", this->AvailableFeatures.uniformAndStorageBuffer16BitAccess ? "True" : "False" ); - LOGI( " storagePushConstant16: %s", this->AvailableFeatures.storagePushConstant16 ? "True" : "False" ); - LOGI( " storageInputOutput16: %s", this->AvailableFeatures.storageInputOutput16 ? "True" : "False" ); - } - - -}; //namespace diff --git a/framework/code/vulkan/extensionHelpers.hpp b/framework/code/vulkan/extensionHelpers.hpp index c424e33..697928c 100644 --- a/framework/code/vulkan/extensionHelpers.hpp +++ b/framework/code/vulkan/extensionHelpers.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -30,6 +30,7 @@ struct VulkanDeviceFunctionPointerLookup const PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr; }; + template class DeviceCreateInfoHook final : public ExtensionHook { @@ -37,13 +38,19 @@ class DeviceCreateInfoHook final : public ExtensionHook explicit DeviceCreateInfoHook( T* _Parent ) : ExtensionHook(), Parent( _Parent ) {} VkStructureType StructureType() const override { return T_FEATURESSTRUCTURE_TYPE; }; VkBaseOutStructure* Obtain( tBase* ) override { - Parent->PopulateRequestedFeatures(); - Parent->RequestedFeatures.pNext = nullptr; - return (VkBaseOutStructure*) &Parent->RequestedFeatures; + if (Parent->Status == VulkanExtensionStatus::eLoaded) + { + Parent->PopulateRequestedFeatures(); + Parent->RequestedFeatures.pNext = nullptr; + return (VkBaseOutStructure*)&Parent->RequestedFeatures; + } + else + return nullptr; }; T* Parent; }; + template class GetPhysicalDeviceFeaturesHook final : public ExtensionHook { @@ -54,6 +61,7 @@ class GetPhysicalDeviceFeaturesHook final : public ExtensionHook class VulkanDeviceFeaturePrintHook final : public ExtensionHook { @@ -64,6 +72,7 @@ class VulkanDeviceFeaturePrintHook final : public ExtensionHook class GetPhysicalDevicePropertiesHook final : public ExtensionHook { @@ -74,6 +83,7 @@ class GetPhysicalDevicePropertiesHook final : public ExtensionHook class VulkanDevicePropertiesPrintHook final : public ExtensionHook { @@ -84,6 +94,7 @@ class VulkanDevicePropertiesPrintHook final : public ExtensionHook m_VulkanDeviceFeaturePrintHook; }; + +/// @brief Helper template for extensions that are initialized through the vkCreateDevice and queried through vkGetPhysicalDeviceProperties2 +/// Default behaviour assumes that the application will do a query before the vkCreateDevice and the properties populated in that call are then required/desired by the vkCreateDevice. +/// Overriding 'PopulateRequestedProperties' allows for changes to this behaviour (called when the properties 'chain' for vkCreateDevice is being constructed). +template +class VulkanDevicePropertiesExtensionHelper : public VulkanExtension +{ +public: + using tBase = VulkanExtension; + VulkanDevicePropertiesExtensionHelper( std::string extensionName, VulkanExtensionStatus status ) : tBase( extensionName, status ), m_GetPhysicalDevicePropertiesHook( this ), m_VulkanDevicePropertiesPrintHook( this ) + {} + + void Register( Vulkan& vulkan ) override + { + vulkan.AddExtensionHooks( &m_GetPhysicalDevicePropertiesHook, &m_VulkanDevicePropertiesPrintHook ); + } + virtual void PrintProperties() const = 0; + + T_PROPERTIESSTRUCTURE Properties {T_PROPERTIESSTRUSTURE_TYPE}; +private: + GetPhysicalDevicePropertiesHook m_GetPhysicalDevicePropertiesHook; + VulkanDevicePropertiesPrintHook m_VulkanDevicePropertiesPrintHook; +}; + + /// @brief Helper template for extensions that are initialized through the vkCreateDevice and queried through vkGetPhysicalDeviceFeatures2 /// AND have properties queried through VkPhysicalDeviceProperties2. /// Default behaviour assumes that the application will do a query before the vkCreateDevice and the features populated in that call are then required/desired by the vkCreateDevice. @@ -157,6 +193,7 @@ class InstanceFunctionPointerHook final : public ExtensionHook class DeviceFunctionPointerHook final : public ExtensionHook { @@ -222,6 +259,36 @@ class VulkanFeaturesAndFunctionPointerExtensionHelper : public VulkanDeviceFeatu }; +/// @brief Helper template for device extensions that have properties queried through +/// VkPhysicalDeviceProperties2 AND want function pointer lookup. +/// These extensions do not pass a structure in to VkCreateDevice. +template +class VulkanPropertiesAndFunctionPointerExtensionHelper : public VulkanDevicePropertiesExtensionHelper +{ + using tBase = VulkanDevicePropertiesExtensionHelper; +public: + explicit VulkanPropertiesAndFunctionPointerExtensionHelper( std::string extensionName, VulkanExtensionStatus status ) : tBase( extensionName, status ), m_InstanceFunctionPointerHook( this ), m_DeviceFunctionPointerHook( this ) + {} + + void Register( Vulkan& vulkan ) override + { + tBase::Register( vulkan ); + vulkan.AddExtensionHooks( &m_InstanceFunctionPointerHook ); + vulkan.AddExtensionHooks( &m_DeviceFunctionPointerHook ); + } + +protected: + friend class InstanceFunctionPointerHook< VulkanPropertiesAndFunctionPointerExtensionHelper>; + friend class DeviceFunctionPointerHook< VulkanPropertiesAndFunctionPointerExtensionHelper>; + virtual void LookupFunctionPointers( VkInstance ) = 0; + virtual void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) = 0; + +private: + InstanceFunctionPointerHook m_InstanceFunctionPointerHook; + DeviceFunctionPointerHook m_DeviceFunctionPointerHook; +}; + + /// @brief Helper template for extensions that are initialized through the vkCreateDevice and queried through vkGetPhysicalDeviceFeatures2 /// AND have properties queried through VkPhysicalDeviceProperties2. /// AND want function pointer lookup @@ -250,652 +317,3 @@ class VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper : public VulkanD InstanceFunctionPointerHook m_InstanceFunctionPointerHook; DeviceFunctionPointerHook m_DeviceFunctionPointerHook; }; - - -/// -/// Library of Vulkan extension helpers -/// - -namespace ExtensionHelper -{ - -#if VK_EXT_mesh_shader - - struct Ext_VK_KHR_mesh_shader : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceMeshShaderFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, - VkPhysicalDeviceMeshShaderPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT> - { - static constexpr auto Name = VK_EXT_MESH_SHADER_EXTENSION_NAME; - explicit Ext_VK_KHR_mesh_shader(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper(Name, status) - {} - - void PopulateRequestedFeatures() override - { - RequestedFeatures = AvailableFeatures; - RequestedFeatures.multiviewMeshShader = VK_FALSE; //if we need multiview then device needs setting up for multiview also - RequestedFeatures.primitiveFragmentShadingRateMeshShader = VK_FALSE;//if we need fragment shading rate then device needs setting up for it (and we need to revisit this flag!) - } - void LookupFunctionPointers(VkInstance) override {} - void LookupFunctionPointers(VkDevice, PFN_vkGetDeviceProcAddr) override; - void PrintFeatures() const override; - void PrintProperties() const override; - PFN_vkCmdDrawMeshTasksEXT m_vkCmdDrawMeshTasksEXT = nullptr; - PFN_vkCmdDrawMeshTasksIndirectEXT m_vkCmdDrawMeshTasksIndirectEXT = nullptr; - PFN_vkCmdDrawMeshTasksIndirectCountEXT m_vkCmdDrawMeshTasksIndirectCountEXT = nullptr; - }; - -#endif // VK_EXT_mesh_shader - -#if VK_KHR_shader_float16_int8 - - struct Ext_VK_KHR_shader_float16_int8 : public VulkanDeviceFeaturesExtensionHelper - { - static constexpr auto Name = VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME; - explicit Ext_VK_KHR_shader_float16_int8( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - }; - -#endif // VK_KHR_shader_float16_int8 - -#if VK_EXT_shader_image_atomic_int64 - - struct Ext_VK_EXT_shader_image_atomic_int64 : public VulkanDeviceFeaturesExtensionHelper - { - static constexpr auto Name = VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME; - explicit Ext_VK_EXT_shader_image_atomic_int64( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper( Name, status ) - {} - void PrintFeatures() const override; - }; - -#endif // VK_EXT_shader_image_atomic_int64 - -#if VK_EXT_index_type_uint8 - - struct Ext_VK_EXT_index_type_uint8 : public VulkanDeviceFeaturesExtensionHelper - { - static constexpr auto Name = VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME; - explicit Ext_VK_EXT_index_type_uint8( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - }; - -#endif // VK_EXT_index_type_uint8 - -#if VK_KHR_shader_subgroup_extended_types - - struct Ext_VK_KHR_shader_subgroup_extended_types : public VulkanDeviceFeaturesExtensionHelper - { - static constexpr auto Name = VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME; - explicit Ext_VK_KHR_shader_subgroup_extended_types( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - }; - -#endif // VK_KHR_shader_subgroup_extended_types - -#if VK_EXT_descriptor_indexing - - struct Ext_VK_EXT_descriptor_indexing : public VulkanDeviceFeaturePropertiesExtensionHelper< - VkPhysicalDeviceDescriptorIndexingFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT, - VkPhysicalDeviceDescriptorIndexingPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT> - { - static constexpr auto Name = VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME; - explicit Ext_VK_EXT_descriptor_indexing( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - void PrintProperties() const override; - }; - -#endif // VK_EXT_descriptor_indexing - -#if VK_KHR_8bit_storage - - struct Ext_VK_KHR_8bit_storage : public VulkanDeviceFeaturesExtensionHelper< - VkPhysicalDevice8BitStorageFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR> - { - static constexpr auto Name = VK_KHR_8BIT_STORAGE_EXTENSION_NAME; - explicit Ext_VK_KHR_8bit_storage( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - }; - -#endif // VK_KHR_8bit_storage - -#if VK_KHR_portability_subset - - struct Ext_VK_KHR_portability_subset : public VulkanDeviceFeaturePropertiesExtensionHelper< - VkPhysicalDevicePortabilitySubsetFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR, - VkPhysicalDevicePortabilitySubsetPropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR> - { - static constexpr auto Name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME; - explicit Ext_VK_KHR_portability_subset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) - {} - void PrintFeatures() const override; - void PrintProperties() const override; - }; - -#else - - // 'dummy' implementation of Ext_VK_KHR_portability_subset for when vulkan headers do not contain 'VK_KHR_portability_subset' - struct Ext_VK_KHR_portability_subset : public VulkanExtension - { - static constexpr auto Name = "VK_KHR_portability_subset"; - Ext_VK_KHR_portability_subset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanExtension( Name, status ) {} - }; - -#endif // VK_KHR_portability_subset - -#if VK_KHR_fragment_shading_rate - - struct Ext_VK_KHR_fragment_shading_rate : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceFragmentShadingRateFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, - VkPhysicalDeviceFragmentShadingRatePropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR> - { - static constexpr auto Name = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; - explicit Ext_VK_KHR_fragment_shading_rate( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper( Name, status ) - {} - - void PopulateRequestedFeatures() override - { - RequestedFeatures.sType = AvailableFeatures.sType; - RequestedFeatures.attachmentFragmentShadingRate = AvailableFeatures.attachmentFragmentShadingRate; - } - void LookupFunctionPointers( VkInstance vkInstance ) override - { - m_vkCmdSetFragmentShadingRateKHR = (PFN_vkCmdSetFragmentShadingRateKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdSetFragmentShadingRateKHR" ); - } - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {} - void PrintFeatures() const override; - void PrintProperties() const override; - PFN_vkCmdSetFragmentShadingRateKHR m_vkCmdSetFragmentShadingRateKHR = nullptr; - }; - -#endif // VK_KHR_fragment_shading_rate - -#if VK_KHR_create_renderpass2 - - struct Ext_VK_KHR_create_renderpass2 : public VulkanFunctionPointerExtensionHelper - { - static constexpr auto Name = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; - explicit Ext_VK_KHR_create_renderpass2( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} - void LookupFunctionPointers(VkInstance vkInstance) override; - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } - PFN_vkCreateRenderPass2KHR m_vkCreateRenderPass2KHR = nullptr; - PFN_vkCmdBeginRenderPass2KHR m_vkCmdBeginRenderPass2KHR = nullptr; - PFN_vkCmdNextSubpass2KHR m_vkCmdNextSubpass2KHR = nullptr; - PFN_vkCmdEndRenderPass2KHR m_vkCmdEndRenderPass2KHR = nullptr; - }; - -#endif // VK_KHR_create_renderpass2 - -#if VK_KHR_draw_indirect_count - - struct Ext_VK_KHR_draw_indirect_count : public VulkanFunctionPointerExtensionHelper - { - static constexpr auto Name = VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME; - explicit Ext_VK_KHR_draw_indirect_count( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} - void LookupFunctionPointers(VkInstance vkInstance) override; - void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/} - PFN_vkCmdDrawIndirectCountKHR m_vkCmdDrawIndirectCountKHR = nullptr; - PFN_vkCmdDrawIndexedIndirectCountKHR m_vkCmdDrawIndexedIndirectCountKHR = nullptr; - }; - -#endif // VK_KHR_draw_indirect_count - -#if VK_EXT_hdr_metadata - - struct Ext_VK_EXT_hdr_metadata : public VulkanFunctionPointerExtensionHelper - { - static constexpr auto Name = VK_EXT_HDR_METADATA_EXTENSION_NAME; - explicit Ext_VK_EXT_hdr_metadata(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanFunctionPointerExtensionHelper(Name, status) {} - void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/} - void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override - { - m_vkSetHdrMetadataEXT = (PFN_vkSetHdrMetadataEXT) fpGetDeviceProcAddr( vkDevice, "vkSetHdrMetadataEXT" ); - } - PFN_vkSetHdrMetadataEXT m_vkSetHdrMetadataEXT = nullptr; - }; - -#endif // VK_EXT_hdr_metadata - -#if VK_EXT_debug_utils - - struct Ext_VK_EXT_debug_utils : public VulkanFunctionPointerExtensionHelper - { - static constexpr auto Name = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - explicit Ext_VK_EXT_debug_utils( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} - void LookupFunctionPointers( VkInstance vkInstance ) override - { - m_vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT) vkGetInstanceProcAddr( vkInstance, "vkSetDebugUtilsObjectNameEXT" ); - } - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } - bool SetDebugUtilsObjectName( VkDevice vkDevice, uint64_t object, VkObjectType objectType, const char* name ) const; - - PFN_vkSetDebugUtilsObjectNameEXT m_vkSetDebugUtilsObjectNameEXT = nullptr; - }; - -#endif // VK_EXT_debug_utils - -#if VK_EXT_debug_marker - - struct Ext_VK_EXT_debug_marker : public VulkanFunctionPointerExtensionHelper - { - static constexpr auto Name = VK_EXT_DEBUG_MARKER_EXTENSION_NAME; - explicit Ext_VK_EXT_debug_marker( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} - void LookupFunctionPointers( VkInstance ) override {/*no instance functions*/ } - void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override - { - m_vkDebugMarkerSetObjectNameEXT = (PFN_vkDebugMarkerSetObjectNameEXT) fpGetDeviceProcAddr( vkDevice, "vkDebugMarkerSetObjectNameEXT" ); - } - bool DebugMarkerSetObjectName( VkDevice vkDevice, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char* name ) const; - - PFN_vkDebugMarkerSetObjectNameEXT m_vkDebugMarkerSetObjectNameEXT = nullptr; - }; - -#endif // Ext_VK_EXT_debug_marker - -#if VK_EXT_subgroup_size_control - - struct Ext_VK_EXT_subgroup_size_control : public VulkanDeviceFeaturePropertiesExtensionHelper< - VkPhysicalDeviceSubgroupSizeControlFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT, - VkPhysicalDeviceSubgroupSizeControlPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT > - { - using tBase = VulkanDeviceFeaturePropertiesExtensionHelper; - static constexpr auto Name = VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME; - explicit Ext_VK_EXT_subgroup_size_control( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper( Name, status ), m_ShaderCreateHook(this) - {} - void PrintFeatures() const override; - void PrintProperties() const override; - - VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT SubGroupSizeControl { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT }; - - private: - class ShaderCreateStageHook final : public ExtensionHook - { - friend struct Ext_VK_EXT_subgroup_size_control; - explicit ShaderCreateStageHook( Ext_VK_EXT_subgroup_size_control* _Parent ) : ExtensionHook(), Parent( _Parent ) {} - VkStructureType StructureType() const override { return (VkStructureType) 0; }; - VkBaseOutStructure* Obtain( tBase* pBase ) override { - if ((pBase->stage & Parent->Properties.requiredSubgroupSizeStages)!=0 - && Parent->RequestedFeatures.subgroupSizeControl - && Parent->SubGroupSizeControl.requiredSubgroupSize != 0) - { - Parent->SubGroupSizeControl.pNext = nullptr; - pBase->flags |= VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT; - return (VkBaseOutStructure*) &Parent->SubGroupSizeControl; - } - return nullptr; - } - void Release( VkBaseOutStructure* pBase ) override { - assert( pBase == (VkBaseOutStructure *) &Parent->SubGroupSizeControl ); - } - Ext_VK_EXT_subgroup_size_control* Parent; - } m_ShaderCreateHook; - - void Register( Vulkan& vulkan ) override - { - tBase::Register( vulkan ); - vulkan.AddExtensionHooks( &m_ShaderCreateHook ); - } - }; - -#endif // VK_EXT_subgroup_size_control - -#if VK_EXT_host_query_reset - - struct Ext_VK_EXT_host_query_reset : public VulkanFeaturesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceHostQueryResetFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT > - { - using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; - static constexpr auto Name = VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME; - explicit Ext_VK_EXT_host_query_reset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) - {} - void PrintFeatures() const override; - void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/ } - void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override - { - if (RequestedFeatures.hostQueryReset == VK_TRUE) - m_vkResetQueryPoolEXT = (PFN_vkResetQueryPoolEXT) fpGetDeviceProcAddr( vkDevice, "vkResetQueryPoolEXT" ); - } - PFN_vkResetQueryPoolEXT m_vkResetQueryPoolEXT = nullptr; - }; - -#endif // VK_EXT_host_query_reset - -#if VK_ARM_tensors - - struct Ext_VK_ARM_tensors : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceTensorFeaturesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TENSOR_FEATURES_ARM, - VkPhysicalDeviceTensorPropertiesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TENSOR_PROPERTIES_ARM> - { - static constexpr auto Name = VK_ARM_TENSORS_EXTENSION_NAME; - - explicit Ext_VK_ARM_tensors(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) - : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper(Name, status) - { - } - - void PopulateRequestedFeatures() override - { - RequestedFeatures.sType = AvailableFeatures.sType; - RequestedFeatures.tensorNonPacked = AvailableFeatures.tensorNonPacked; - RequestedFeatures.shaderTensorAccess = AvailableFeatures.shaderTensorAccess; - RequestedFeatures.shaderStorageTensorArrayDynamicIndexing = AvailableFeatures.shaderStorageTensorArrayDynamicIndexing; - RequestedFeatures.shaderStorageTensorArrayNonUniformIndexing = AvailableFeatures.shaderStorageTensorArrayNonUniformIndexing; - RequestedFeatures.descriptorBindingStorageTensorUpdateAfterBind = AvailableFeatures.descriptorBindingStorageTensorUpdateAfterBind; - RequestedFeatures.tensors = AvailableFeatures.tensors; - - // Forcing a few features while the extension is private: - RequestedFeatures.shaderTensorAccess = true; - RequestedFeatures.tensors = true; - } - - void LookupFunctionPointers(VkInstance) override {} - - void LookupFunctionPointers(VkDevice device, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr) override - { - m_vkCreateTensorARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkCreateTensorARM")); - m_vkDestroyTensorARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkDestroyTensorARM")); - m_vkCreateTensorViewARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkCreateTensorViewARM")); - m_vkDestroyTensorViewARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkDestroyTensorViewARM")); - m_vkGetTensorMemoryRequirementsARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkGetTensorMemoryRequirementsARM")); - m_vkBindTensorMemoryARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkBindTensorMemoryARM")); - m_vkGetDeviceTensorMemoryRequirementsARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkGetDeviceTensorMemoryRequirementsARM")); - m_vkCmdCopyTensorARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkCmdCopyTensorARM")); - m_vkGetTensorOpaqueCaptureDescriptorDataARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkGetTensorOpaqueCaptureDescriptorDataARM")); - m_vkGetTensorViewOpaqueCaptureDescriptorDataARM = reinterpret_cast(fpGetDeviceProcAddr(device, "vkGetTensorViewOpaqueCaptureDescriptorDataARM")); - } - - void PrintFeatures() const override; - void PrintProperties() const override; - - PFN_vkCreateTensorARM m_vkCreateTensorARM = nullptr; - PFN_vkDestroyTensorARM m_vkDestroyTensorARM = nullptr; - PFN_vkCreateTensorViewARM m_vkCreateTensorViewARM = nullptr; - PFN_vkDestroyTensorViewARM m_vkDestroyTensorViewARM = nullptr; - PFN_vkGetTensorMemoryRequirementsARM m_vkGetTensorMemoryRequirementsARM = nullptr; - PFN_vkBindTensorMemoryARM m_vkBindTensorMemoryARM = nullptr; - PFN_vkGetDeviceTensorMemoryRequirementsARM m_vkGetDeviceTensorMemoryRequirementsARM = nullptr; - PFN_vkCmdCopyTensorARM m_vkCmdCopyTensorARM = nullptr; - PFN_vkGetTensorOpaqueCaptureDescriptorDataARM m_vkGetTensorOpaqueCaptureDescriptorDataARM = nullptr; - PFN_vkGetTensorViewOpaqueCaptureDescriptorDataARM m_vkGetTensorViewOpaqueCaptureDescriptorDataARM = nullptr; - }; - -#endif // VK_ARM_tensors - -#if VK_ARM_data_graph - - struct Ext_VK_ARM_data_graph : public VulkanFeaturesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceDataGraphFeaturesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DATA_GRAPH_FEATURES_ARM> - { - using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; - static constexpr auto Name = VK_ARM_DATA_GRAPH_EXTENSION_NAME; - - explicit Ext_VK_ARM_data_graph(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) - : tBase(Name, status) - { - } - - void PopulateRequestedFeatures() override - { - RequestedFeatures.sType = AvailableFeatures.sType; - RequestedFeatures.dataGraph = AvailableFeatures.dataGraph; - RequestedFeatures.dataGraphUpdateAfterBind = AvailableFeatures.dataGraphUpdateAfterBind; - RequestedFeatures.dataGraphSpecializationConstants = AvailableFeatures.dataGraphSpecializationConstants; - RequestedFeatures.dataGraphDescriptorBuffer = AvailableFeatures.dataGraphDescriptorBuffer; - RequestedFeatures.dataGraphShaderModule = AvailableFeatures.dataGraphShaderModule; - - // Forcing a few features while the extension is private: - RequestedFeatures.dataGraph = true; - } - - void PrintFeatures() const override; - void LookupFunctionPointers(VkInstance vkInstance) override - { - m_vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM = reinterpret_cast( - vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM")); - m_vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM = reinterpret_cast( - vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM")); - } - - void LookupFunctionPointers(VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr) override - { - m_vkCreateDataGraphPipelinesARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkCreateDataGraphPipelinesARM")); - m_vkCreateDataGraphPipelineSessionARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkCreateDataGraphPipelineSessionARM")); - m_vkGetDataGraphPipelineSessionBindPointRequirementsARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkGetDataGraphPipelineSessionBindPointRequirementsARM")); - m_vkGetDataGraphPipelineSessionMemoryRequirementsARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkGetDataGraphPipelineSessionMemoryRequirementsARM")); - m_vkBindDataGraphPipelineSessionMemoryARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkBindDataGraphPipelineSessionMemoryARM")); - m_vkDestroyDataGraphPipelineSessionARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkDestroyDataGraphPipelineSessionARM")); - m_vkCmdDispatchDataGraphARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkCmdDispatchDataGraphARM")); - m_vkGetDataGraphPipelineAvailablePropertiesARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkGetDataGraphPipelineAvailablePropertiesARM")); - m_vkGetDataGraphPipelinePropertiesARM = reinterpret_cast( - fpGetDeviceProcAddr(vkDevice, "vkGetDataGraphPipelinePropertiesARM")); - } - - // Function pointers - PFN_vkCreateDataGraphPipelinesARM m_vkCreateDataGraphPipelinesARM = nullptr; - PFN_vkCreateDataGraphPipelineSessionARM m_vkCreateDataGraphPipelineSessionARM = nullptr; - PFN_vkGetDataGraphPipelineSessionBindPointRequirementsARM m_vkGetDataGraphPipelineSessionBindPointRequirementsARM = nullptr; - PFN_vkGetDataGraphPipelineSessionMemoryRequirementsARM m_vkGetDataGraphPipelineSessionMemoryRequirementsARM = nullptr; - PFN_vkBindDataGraphPipelineSessionMemoryARM m_vkBindDataGraphPipelineSessionMemoryARM = nullptr; - PFN_vkDestroyDataGraphPipelineSessionARM m_vkDestroyDataGraphPipelineSessionARM = nullptr; - PFN_vkCmdDispatchDataGraphARM m_vkCmdDispatchDataGraphARM = nullptr; - PFN_vkGetDataGraphPipelineAvailablePropertiesARM m_vkGetDataGraphPipelineAvailablePropertiesARM = nullptr; - PFN_vkGetDataGraphPipelinePropertiesARM m_vkGetDataGraphPipelinePropertiesARM = nullptr; - PFN_vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM m_vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM = nullptr; - PFN_vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM m_vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM = nullptr; - }; - -#endif // VK_ARM_data_graph -#if VK_KHR_timeline_semaphore - - struct Ext_VK_KHR_timeline_semaphore : public VulkanDeviceFeaturePropertiesExtensionHelper< - VkPhysicalDeviceTimelineSemaphoreFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, - VkPhysicalDeviceTimelineSemaphorePropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR > - { - using tBase = VulkanDeviceFeaturePropertiesExtensionHelper; - static constexpr auto Name = VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME; - explicit Ext_VK_KHR_timeline_semaphore( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) - {} - void PrintFeatures() const override; - void PrintProperties() const override; - }; - -#endif // VK_KHR_timeline_semaphore - -#if VK_KHR_synchronization2 - - struct Ext_VK_KHR_synchronization2 : public VulkanFeaturesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceSynchronization2FeaturesKHR, (VkStructureType) VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR > - { - using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; - static constexpr auto Name = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; - explicit Ext_VK_KHR_synchronization2(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : tBase(Name, status) - {} - void PrintFeatures() const override; - void LookupFunctionPointers(VkInstance vkInstance) override; - void LookupFunctionPointers(VkDevice, PFN_vkGetDeviceProcAddr) override {/*no device functions*/ } - - PFN_vkCmdSetEvent2KHR m_vkCmdSetEvent2KHR = nullptr; - PFN_vkCmdResetEvent2KHR m_vkCmdResetEvent2KHR = nullptr; - PFN_vkCmdWaitEvents2KHR m_vkCmdWaitEvents2KHR = nullptr; - PFN_vkCmdPipelineBarrier2KHR m_vkCmdPipelineBarrier2KHR = nullptr; - PFN_vkQueueSubmit2KHR m_vkQueueSubmit2KHR = nullptr; - PFN_vkCmdWriteTimestamp2KHR m_vkCmdWriteTimestamp2KHR = nullptr; - }; - -#endif // VK_KHR_synchronization2 - -#if VK_QCOM_tile_properties - - struct Ext_VK_QCOM_tile_properties : public VulkanFeaturesAndFunctionPointerExtensionHelper< - VkPhysicalDeviceTilePropertiesFeaturesQCOM, (VkStructureType)VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM> - { - using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; - static constexpr auto Name = VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME; - explicit Ext_VK_QCOM_tile_properties( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) - {} - void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/ }; - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override; - void PrintFeatures() const override; - PFN_vkGetDynamicRenderingTilePropertiesQCOM m_vkGetDynamicRenderingTilePropertiesQCOM = nullptr; - PFN_vkGetFramebufferTilePropertiesQCOM m_vkGetFramebufferTilePropertiesQCOM = nullptr; - }; - -#endif // VK_QCOM_tile_properties - -#if VK_KHR_ray_tracing_position_fetch - - struct Ext_VK_KHR_ray_tracing_position_fetch : public VulkanDeviceFeaturesExtensionHelper< - VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR> - { - static constexpr auto Name = VK_KHR_RAY_TRACING_POSITION_FETCH_EXTENSION_NAME; - explicit Ext_VK_KHR_ray_tracing_position_fetch( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper( Name, status ) - {} - void PrintFeatures() const override; - }; - -#endif // VK_KHR_ray_tracing_position_fetch - -#if VK_EXT_scalar_block_layout - - struct Ext_VK_EXT_scalar_block_layout : public VulkanDeviceFeaturesExtensionHelper< - VkPhysicalDeviceScalarBlockLayoutFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT> - { - static constexpr auto Name = VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME; - explicit Ext_VK_EXT_scalar_block_layout( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper( Name, status ) - {} - void PrintFeatures() const override; - }; - -#endif // VK_EXT_scalar_block_layout - -#if VK_KHR_get_physical_device_properties2 - - // Instance extension - struct Ext_VK_KHR_get_physical_device_properties2 : public VulkanFunctionPointerExtensionHelper - { - using tBase = VulkanFunctionPointerExtensionHelper; - static constexpr auto Name = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; - explicit Ext_VK_KHR_get_physical_device_properties2(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : tBase(Name, status) - {} - void LookupFunctionPointers(VkInstance vkInstance) override; - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } - PFN_vkGetPhysicalDeviceFeatures2KHR m_vkGetPhysicalDeviceFeatures2KHR = nullptr; - PFN_vkGetPhysicalDeviceFormatProperties2KHR m_vkGetPhysicalDeviceFormatProperties2KHR = nullptr; - PFN_vkGetPhysicalDeviceImageFormatProperties2KHR m_vkGetPhysicalDeviceImageFormatProperties2KHR = nullptr; - PFN_vkGetPhysicalDeviceMemoryProperties2KHR m_vkGetPhysicalDeviceMemoryProperties2KHR = nullptr; - PFN_vkGetPhysicalDeviceProperties2KHR m_vkGetPhysicalDeviceProperties2KHR = nullptr; - }; - -#endif // VK_KHR_get_physical_device_properties2 - -#if VK_KHR_surface - - // Instance extension - struct Ext_VK_KHR_surface : public VulkanFunctionPointerExtensionHelper - { - using tBase = VulkanFunctionPointerExtensionHelper; - static constexpr auto Name = VK_KHR_SURFACE_EXTENSION_NAME; - explicit Ext_VK_KHR_surface( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) - {} - void LookupFunctionPointers( VkInstance vkInstance ) override; - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } - PFN_vkDestroySurfaceKHR m_vkDestroySurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR m_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR m_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; - }; - -#endif // VK_KHR_surface - -#if VK_KHR_get_surface_capabilities2 - - // Instance extension - struct Ext_VK_KHR_get_surface_capabilities2 : public VulkanFunctionPointerExtensionHelper - { - using tBase = VulkanFunctionPointerExtensionHelper; - static constexpr auto Name = VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME; - explicit Ext_VK_KHR_get_surface_capabilities2( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) - {} - void LookupFunctionPointers( VkInstance vkInstance ) override; - void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } - PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR m_vkGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceFormats2KHR m_vkGetPhysicalDeviceSurfaceFormats2KHR = nullptr; - }; - -#endif // VK_KHR_get_surface_capabilities2 - - // - // Vulkan 1.1 (VK_VERSION_1_1) provided features/properties. - // Same interface as other extensions but do not need to be added to the list of extension names on vkCreateDevice. - // - - struct Vulkan_SubgroupPropertiesHook : public VulkanExtension - { - static constexpr auto Name = "SubgroupProperties"; - using tBase = VulkanExtension; - Vulkan_SubgroupPropertiesHook& operator=( const Vulkan_SubgroupPropertiesHook& ) = delete; - Vulkan_SubgroupPropertiesHook( const Vulkan_SubgroupPropertiesHook& ) = delete; - explicit Vulkan_SubgroupPropertiesHook( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ), m_GetPhysicalDevicePropertiesHook( this ), m_VulkanDevicePropertiesPrintHook( this ) - {} - VkPhysicalDeviceSubgroupProperties Properties { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES }; - - private: - void Register( Vulkan& vulkan ) override - { - vulkan.AddExtensionHooks( &m_GetPhysicalDevicePropertiesHook, &m_VulkanDevicePropertiesPrintHook ); - } - - friend class GetPhysicalDevicePropertiesHook; - friend class VulkanDevicePropertiesPrintHook; - GetPhysicalDevicePropertiesHook m_GetPhysicalDevicePropertiesHook; - VulkanDevicePropertiesPrintHook m_VulkanDevicePropertiesPrintHook; - - void PrintProperties() const; - }; - - struct Vulkan_StorageFeaturesHook : public VulkanExtension - { - static constexpr auto Name = "StorageFeatures"; - using tBase = VulkanExtension; - Vulkan_StorageFeaturesHook& operator=( const Vulkan_StorageFeaturesHook& ) = delete; - Vulkan_StorageFeaturesHook( const Vulkan_StorageFeaturesHook& ) = delete; - explicit Vulkan_StorageFeaturesHook( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( std::string(), status ), m_DeviceCreateInfoHook(this), m_GetPhysicalDeviceFeaturesHook(this), m_VulkanDeviceFeaturePrintHook(this) - {} - VkPhysicalDevice16BitStorageFeatures AvailableFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR }; - - private: - friend class DeviceCreateInfoHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; - friend class GetPhysicalDeviceFeaturesHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; - friend class VulkanDeviceFeaturePrintHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; - DeviceCreateInfoHook m_DeviceCreateInfoHook; - GetPhysicalDeviceFeaturesHook m_GetPhysicalDeviceFeaturesHook; - VulkanDeviceFeaturePrintHook m_VulkanDeviceFeaturePrintHook; - - void Register( Vulkan& vulkan ) override - { - vulkan.AddExtensionHooks( &m_DeviceCreateInfoHook, &m_GetPhysicalDeviceFeaturesHook, &m_VulkanDeviceFeaturePrintHook ); - } - virtual void PrintFeatures() const; - virtual void PopulateRequestedFeatures() { RequestedFeatures = AvailableFeatures; } - VkPhysicalDevice16BitStorageFeatures RequestedFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR }; - }; - -}; // namespace diff --git a/framework/code/vulkan/extensionLib.cpp b/framework/code/vulkan/extensionLib.cpp new file mode 100644 index 0000000..eeb93aa --- /dev/null +++ b/framework/code/vulkan/extensionLib.cpp @@ -0,0 +1,609 @@ +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "vulkan.hpp" +#include "extensionLib.hpp" +#include + +namespace ExtensionLib +{ + +#if VK_EXT_mesh_shader + void Ext_VK_KHR_mesh_shader::LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr ) + { + m_vkCmdDrawMeshTasksEXT = (PFN_vkCmdDrawMeshTasksEXT)deviceProcAddr( vkDevice, "vkCmdDrawMeshTasksEXT" ); + m_vkCmdDrawMeshTasksIndirectEXT = (PFN_vkCmdDrawMeshTasksIndirectEXT)deviceProcAddr( vkDevice, "vkCmdDrawMeshTasksIndirectEXT" ); + m_vkCmdDrawMeshTasksIndirectCountEXT = (PFN_vkCmdDrawMeshTasksIndirectCountEXT)deviceProcAddr( vkDevice, "vkCmdDrawMeshTasksIndirectCountEXT" ); + } + void Ext_VK_KHR_mesh_shader::PrintFeatures() const + { + LOGI( "FeaturesMeshShader: " ); + LOGI( " taskShader: %s", this->AvailableFeatures.taskShader ? "True" : "False" ); + LOGI( " meshShader: %s", this->AvailableFeatures.meshShader ? "True" : "False" ); + LOGI( " multiviewMeshShader: %s", this->AvailableFeatures.multiviewMeshShader ? "True" : "False" ); + LOGI( " primitiveFragmentShadingRateMeshShader: %s", this->AvailableFeatures.primitiveFragmentShadingRateMeshShader ? "True" : "False" ); + LOGI( " meshShaderQueries: %s", this->AvailableFeatures.meshShaderQueries ? "True" : "False" ); + } + void Ext_VK_KHR_mesh_shader::PrintProperties() const + { + LOGI( "VK_KHR_mesh_shader (VkPhysicalDeviceMeshShaderPropertiesEXT): " ); + LOGI( " maxTaskWorkGroupTotalCount: %u", Properties.maxTaskWorkGroupTotalCount ); + LOGI( " maxTaskWorkGroupCount: %u %u %u", Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0] ); + LOGI( " maxTaskWorkGroupTotalCount: %u", Properties.maxTaskWorkGroupTotalCount ); + LOGI( " maxTaskWorkGroupCount: %u %u %u", Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0], Properties.maxTaskWorkGroupCount[0] ); + LOGI( " maxTaskWorkGroupInvocations: %u", Properties.maxTaskWorkGroupInvocations ); + LOGI( " maxTaskWorkGroupSize: %u %u %u", Properties.maxTaskWorkGroupSize[0], Properties.maxTaskWorkGroupSize[0], Properties.maxTaskWorkGroupSize[0] ); + LOGI( " maxTaskPayloadSize: %u", Properties.maxTaskPayloadSize ); + LOGI( " maxTaskSharedMemorySize: %u", Properties.maxTaskSharedMemorySize ); + LOGI( " maxTaskPayloadAndSharedMemorySize: %u", Properties.maxTaskPayloadAndSharedMemorySize ); + LOGI( " maxMeshWorkGroupTotalCount: %u", Properties.maxMeshWorkGroupTotalCount ); + LOGI( " maxMeshWorkGroupCount: %u %u %u", Properties.maxMeshWorkGroupCount[0], Properties.maxMeshWorkGroupCount[0], Properties.maxMeshWorkGroupCount[0] ); + LOGI( " maxMeshWorkGroupInvocations: %u", Properties.maxMeshWorkGroupInvocations ); + LOGI( " maxMeshWorkGroupSize: %u %u %u", Properties.maxMeshWorkGroupSize[0], Properties.maxMeshWorkGroupSize[0], Properties.maxMeshWorkGroupSize[0] ); + LOGI( " maxMeshSharedMemorySize: %u", Properties.maxMeshSharedMemorySize ); + LOGI( " maxMeshPayloadAndSharedMemorySize: %u", Properties.maxMeshPayloadAndSharedMemorySize ); + LOGI( " maxMeshOutputMemorySize: %u", Properties.maxMeshOutputMemorySize ); + LOGI( " maxMeshPayloadAndOutputMemorySize: %u", Properties.maxMeshPayloadAndOutputMemorySize ); + LOGI( " maxMeshOutputComponents: %u", Properties.maxMeshOutputComponents ); + LOGI( " maxMeshOutputVertices: %u", Properties.maxMeshOutputVertices ); + LOGI( " maxMeshOutputPrimitives: %u", Properties.maxMeshOutputPrimitives ); + LOGI( " maxMeshOutputLayers: %u", Properties.maxMeshOutputLayers ); + LOGI( " maxMeshMultiviewViewCount: %u", Properties.maxMeshMultiviewViewCount ); + LOGI( " meshOutputPerVertexGranularity: %u", Properties.meshOutputPerVertexGranularity ); + LOGI( " meshOutputPerPrimitiveGranularity: %u", Properties.meshOutputPerPrimitiveGranularity ); + LOGI( " maxPreferredTaskWorkGroupInvocations: %u", Properties.maxPreferredTaskWorkGroupInvocations ); + LOGI( " maxPreferredMeshWorkGroupInvocations: %u", Properties.maxPreferredMeshWorkGroupInvocations ); + LOGI( " prefersLocalInvocationVertexOutput: %s", Properties.prefersLocalInvocationVertexOutput ? "True" : "False" ); + LOGI( " prefersLocalInvocationVertexOutput: %s", Properties.prefersLocalInvocationPrimitiveOutput ? "True" : "False" ); + LOGI( " prefersLocalInvocationVertexOutput: %s", Properties.prefersCompactVertexOutput ? "True" : "False" ); + LOGI( " prefersLocalInvocationVertexOutput: %s", Properties.prefersCompactPrimitiveOutput ? "True" : "False" ); + } +#endif + +#if VK_KHR_swapchain + + void Ext_VK_KHR_swapchain::LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr ) + { + m_vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)deviceProcAddr( vkDevice, "vkCreateSwapchainKHR" ); + m_vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)deviceProcAddr( vkDevice, "vkDestroySwapchainKHR" ); + m_vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)deviceProcAddr( vkDevice, "vkGetSwapchainImagesKHR" ); + m_vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR)deviceProcAddr( vkDevice, "vkAcquireNextImageKHR" ); + m_vkQueuePresentKHR = (PFN_vkQueuePresentKHR)deviceProcAddr( vkDevice, "vkQueuePresentKHR" ); + m_vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR)deviceProcAddr( vkDevice, "vkGetDeviceGroupPresentCapabilitiesKHR" ); + m_vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR)deviceProcAddr( vkDevice, "vkGetDeviceGroupSurfacePresentModesKHR" ); + m_vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR)deviceProcAddr( vkDevice, "vkAcquireNextImage2KHR" ); + } + void Ext_VK_KHR_swapchain::LookupFunctionPointers( VkInstance vkInstance ) + { + m_vkGetPhysicalDevicePresentRectanglesKHR = (PFN_vkGetPhysicalDevicePresentRectanglesKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDevicePresentRectanglesKHR" ); + } + +#endif // VK_KHR_swapchain + + +#if VK_KHR_shader_float16_int8 + void Ext_VK_KHR_shader_float16_int8::PrintFeatures() const + { + LOGI( "FeaturesShaderFloat16Int8: " ); + LOGI( " shaderFloat16: %s", this->AvailableFeatures.shaderFloat16 ? "True" : "False" ); + LOGI( " shaderInt8: %s", this->AvailableFeatures.shaderInt8 ? "True" : "False" ); + } +#endif // VK_KHR_shader_float16_int8 + +#if VK_EXT_shader_image_atomic_int64 + void Ext_VK_EXT_shader_image_atomic_int64::PrintFeatures() const + { + LOGI( "FeaturesShaderImageAtomicInt64: " ); + LOGI( " shaderImageInt64Atomics: %s", this->AvailableFeatures.shaderImageInt64Atomics ? "True" : "False" ); + LOGI( " sparseImageInt64Atomics: %s", this->AvailableFeatures.sparseImageInt64Atomics ? "True" : "False" ); + } +#endif // VK_EXT_shader_image_atomic_int64 + +#if VK_KHR_ray_tracing_position_fetch + void Ext_VK_KHR_ray_tracing_position_fetch::PrintFeatures() const + { + LOGI( "FeaturesRayTracingPositionFetch: " ); + LOGI( " rayTracingPositionFetch: %s", this->AvailableFeatures.rayTracingPositionFetch ? "True" : "False" ); + } +#endif // VK_EXT_shader_image_atomic_int64 + +#if VK_EXT_scalar_block_layout + + void Ext_VK_EXT_scalar_block_layout::PrintFeatures() const + { + LOGI( "FeaturesScalarBlockLayout: " ); + LOGI( " scalarBlockLayout: %s", this->AvailableFeatures.scalarBlockLayout ? "True" : "False" ); + } + +#endif // VK_EXT_scalar_block_layout + + +#if VK_EXT_index_type_uint8 + void Ext_VK_EXT_index_type_uint8::PrintFeatures() const + { + LOGI( "VK_EXT_index_type_uint8 (VkPhysicalDeviceIndexTypeUint8FeaturesEXT): " ); + LOGI( " indexTypeUint8: %s", this->AvailableFeatures.indexTypeUint8 ? "True" : "False" ); + } +#endif // VK_EXT_index_type_uint8 + +#if VK_KHR_shader_subgroup_extended_types + void Ext_VK_KHR_shader_subgroup_extended_types::PrintFeatures() const + { + LOGI( "FeaturesShaderSubgroupExtendedTypes: " ); + LOGI( " shaderSubgroupExtendedTypes: %s", this->AvailableFeatures.shaderSubgroupExtendedTypes ? "True" : "False" ); + } +#endif // VK_KHR_shader_subgroup_extended_types + +#if VK_EXT_descriptor_indexing + void Ext_VK_EXT_descriptor_indexing::PrintFeatures() const + { + LOGI( "VK_EXT_descriptor_indexing (VkPhysicalDeviceDescriptorIndexingFeatures): " ); + LOGI( " shaderInputAttachmentArrayDynamicIndexing: %s", this->AvailableFeatures.shaderInputAttachmentArrayDynamicIndexing ? "True" : "False" ); + LOGI( " shaderUniformTexelBufferArrayDynamicIndexing: %s", this->AvailableFeatures.shaderUniformTexelBufferArrayDynamicIndexing ? "True" : "False" ); + LOGI( " shaderStorageTexelBufferArrayDynamicIndexing: %s", this->AvailableFeatures.shaderStorageTexelBufferArrayDynamicIndexing ? "True" : "False" ); + LOGI( " shaderUniformBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderUniformBufferArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderSampledImageArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderSampledImageArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderStorageBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageBufferArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderStorageImageArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageImageArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderInputAttachmentArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderInputAttachmentArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderUniformTexelBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderUniformTexelBufferArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " shaderStorageTexelBufferArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageTexelBufferArrayNonUniformIndexing ? "True" : "False" ); + LOGI( " descriptorBindingUniformBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingUniformBufferUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingSampledImageUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingSampledImageUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingStorageImageUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageImageUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingStorageBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageBufferUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingUniformTexelBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingUniformTexelBufferUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingStorageTexelBufferUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageTexelBufferUpdateAfterBind ? "True" : "False" ); + LOGI( " descriptorBindingUpdateUnusedWhilePending: %s", this->AvailableFeatures.descriptorBindingUpdateUnusedWhilePending ? "True" : "False" ); + LOGI( " descriptorBindingPartiallyBound: %s", this->AvailableFeatures.descriptorBindingPartiallyBound ? "True" : "False" ); + LOGI( " descriptorBindingVariableDescriptorCount: %s", this->AvailableFeatures.descriptorBindingVariableDescriptorCount ? "True" : "False" ); + LOGI( " runtimeDescriptorArray: %s", this->AvailableFeatures.runtimeDescriptorArray ? "True" : "False" ); + } + + void Ext_VK_EXT_descriptor_indexing::PrintProperties() const + { + LOGI( "VK_EXT_descriptor_indexing (VkPhysicalDeviceDescriptorIndexingProperties): " ); + LOGI( " maxUpdateAfterBindDescriptorsInAllPools: %u", Properties.maxUpdateAfterBindDescriptorsInAllPools ); + LOGI( " shaderUniformBufferArrayNonUniformIndexingNative: %s", Properties.shaderUniformBufferArrayNonUniformIndexingNative ? "True" : "False" ); + LOGI( " shaderSampledImageArrayNonUniformIndexingNative: %s", Properties.shaderSampledImageArrayNonUniformIndexingNative ? "True" : "False" ); + LOGI( " shaderStorageBufferArrayNonUniformIndexingNative: %s", Properties.shaderStorageBufferArrayNonUniformIndexingNative ? "True" : "False" ); + LOGI( " shaderStorageImageArrayNonUniformIndexingNative: %s", Properties.shaderStorageImageArrayNonUniformIndexingNative ? "True" : "False" ); + LOGI( " shaderInputAttachmentArrayNonUniformIndexingNative: %s", Properties.shaderInputAttachmentArrayNonUniformIndexingNative ? "True" : "False" ); + LOGI( " robustBufferAccessUpdateAfterBind: %s", Properties.robustBufferAccessUpdateAfterBind ? "True" : "False" ); + LOGI( " quadDivergentImplicitLod: %s", Properties.quadDivergentImplicitLod ? "True" : "False" ); + LOGI( " maxPerStageDescriptorUpdateAfterBindSamplers: %u", Properties.maxPerStageDescriptorUpdateAfterBindSamplers ); + LOGI( " maxPerStageDescriptorUpdateAfterBindUniformBuffers: %u", Properties.maxPerStageDescriptorUpdateAfterBindUniformBuffers ); + LOGI( " maxPerStageDescriptorUpdateAfterBindStorageBuffers: %u", Properties.maxPerStageDescriptorUpdateAfterBindStorageBuffers ); + LOGI( " maxPerStageDescriptorUpdateAfterBindSampledImages: %u", Properties.maxPerStageDescriptorUpdateAfterBindSampledImages ); + LOGI( " maxPerStageDescriptorUpdateAfterBindStorageImages: %u", Properties.maxPerStageDescriptorUpdateAfterBindStorageImages ); + LOGI( " maxPerStageDescriptorUpdateAfterBindInputAttachments: %u", Properties.maxPerStageDescriptorUpdateAfterBindInputAttachments ); + LOGI( " maxPerStageUpdateAfterBindResources: %u", Properties.maxPerStageUpdateAfterBindResources ); + LOGI( " maxDescriptorSetUpdateAfterBindSamplers: %u", Properties.maxDescriptorSetUpdateAfterBindSamplers ); + LOGI( " maxDescriptorSetUpdateAfterBindUniformBuffers: %u", Properties.maxDescriptorSetUpdateAfterBindUniformBuffers ); + LOGI( " maxDescriptorSetUpdateAfterBindUniformBuffersDynamic: %u", Properties.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic ); + LOGI( " maxDescriptorSetUpdateAfterBindStorageBuffers: %u", Properties.maxDescriptorSetUpdateAfterBindStorageBuffers ); + LOGI( " maxDescriptorSetUpdateAfterBindStorageBuffersDynamic: %u", Properties.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic ); + LOGI( " maxDescriptorSetUpdateAfterBindSampledImages: %u", Properties.maxDescriptorSetUpdateAfterBindSampledImages ); + LOGI( " maxDescriptorSetUpdateAfterBindStorageImages: %u", Properties.maxDescriptorSetUpdateAfterBindStorageImages ); + LOGI( " maxDescriptorSetUpdateAfterBindInputAttachments: %u", Properties.maxDescriptorSetUpdateAfterBindInputAttachments ); + } +#endif // VK_EXT_descriptor_indexing + +#if VK_KHR_8bit_storage + void Ext_VK_KHR_8bit_storage::PrintFeatures() const + { + LOGI( "VK_KHR_8bit_storage (VkPhysicalDevice8BitStorageFeaturesKHR): " ); + LOGI( " storageBuffer8BitAccess: %s", this->AvailableFeatures.storageBuffer8BitAccess ? "True" : "False" ); + LOGI( " uniformAndStorageBuffer8BitAccess: %s", this->AvailableFeatures.uniformAndStorageBuffer8BitAccess ? "True" : "False" ); + LOGI( " storagePushConstant8: %s", this->AvailableFeatures.storagePushConstant8 ? "True" : "False" ); + } +#endif // VK_KHR_8bit_storage + +#if VK_KHR_portability_subset + void Ext_VK_KHR_portability_subset::PrintFeatures() const + { + LOGI( "VK_KHR_portability_subset (VkPhysicalDevicePortabilitySubsetFeaturesKHR): " ); + LOGI( " constantAlphaColorBlendFactors: %s", this->AvailableFeatures.constantAlphaColorBlendFactors ? "True" : "False" ); + LOGI( " events: %s", this->AvailableFeatures.events ? "True" : "False" ); + LOGI( " imageViewFormatReinterpretation: %s", this->AvailableFeatures.imageViewFormatReinterpretation ? "True" : "False" ); + LOGI( " imageViewFormatSwizzle: %s", this->AvailableFeatures.imageViewFormatSwizzle ? "True" : "False" ); + LOGI( " imageView2DOn3DImage: %s", this->AvailableFeatures.imageView2DOn3DImage ? "True" : "False" ); + LOGI( " multisampleArrayImage: %s", this->AvailableFeatures.multisampleArrayImage ? "True" : "False" ); + LOGI( " mutableComparisonSamplers: %s", this->AvailableFeatures.mutableComparisonSamplers ? "True" : "False" ); + LOGI( " pointPolygons: %s", this->AvailableFeatures.pointPolygons ? "True" : "False" ); + LOGI( " samplerMipLodBias: %s", this->AvailableFeatures.samplerMipLodBias ? "True" : "False" ); + LOGI( " separateStencilMaskRef: %s", this->AvailableFeatures.separateStencilMaskRef ? "True" : "False" ); + LOGI( " shaderSampleRateInterpolationFunctions: %s", this->AvailableFeatures.shaderSampleRateInterpolationFunctions ? "True" : "False" ); + LOGI( " tessellationIsolines: %s", this->AvailableFeatures.tessellationIsolines ? "True" : "False" ); + LOGI( " tessellationPointMode: %s", this->AvailableFeatures.tessellationPointMode ? "True" : "False" ); + LOGI( " triangleFans: %s", this->AvailableFeatures.triangleFans ? "True" : "False" ); + LOGI( " vertexAttributeAccessBeyondStride: %s", this->AvailableFeatures.vertexAttributeAccessBeyondStride ? "True" : "False" ); + } + void Ext_VK_KHR_portability_subset::PrintProperties() const + { + LOGI( "VK_KHR_portability_subset (VkPhysicalDevicePortabilitySubsetPropertiesKHR): " ); + LOGI( " minVertexInputBindingStrideAlignment: %u", this->Properties.minVertexInputBindingStrideAlignment ); + } +#endif // VK_KHR_portability_subset + +#if VK_KHR_fragment_shading_rate + void Ext_VK_KHR_fragment_shading_rate::PrintFeatures() const + { + LOGI( "VK_KHR_fragment_shading_rate (VkPhysicalDeviceFragmentShadingRateFeaturesKHR): " ); + LOGI( " pipelineFragmentShadingRate: %s", this->AvailableFeatures.pipelineFragmentShadingRate ? "True" : "False" ); + LOGI( " primitiveFragmentShadingRate: %s", this->AvailableFeatures.primitiveFragmentShadingRate ? "True" : "False" ); + LOGI( " attachmentFragmentShadingRate: %s", this->AvailableFeatures.attachmentFragmentShadingRate ? "True" : "False" ); + } + void Ext_VK_KHR_fragment_shading_rate::PrintProperties() const + { + LOGI( "VK_KHR_fragment_shading_rate (VkPhysicalDeviceFragmentShadingRatePropertiesKHR): " ); + LOGI( " minFragmentDensityTexelSize: %ux%u", this->Properties.minFragmentShadingRateAttachmentTexelSize.width, this->Properties.minFragmentShadingRateAttachmentTexelSize.height ); + LOGI( " maxFragmentDensityTexelSize: %ux%u", this->Properties.maxFragmentShadingRateAttachmentTexelSize.width, this->Properties.maxFragmentShadingRateAttachmentTexelSize.height ); + LOGI( " maxFragmentShadingRateAttachmentTexelSizeAspectRatio: %u", this->Properties.maxFragmentShadingRateAttachmentTexelSizeAspectRatio ); + LOGI( " primitiveFragmentShadingRateWithMultipleViewports: %s", this->Properties.primitiveFragmentShadingRateWithMultipleViewports ? "True" : "False" ); + LOGI( " layeredShadingRateAttachments: %s", this->Properties.layeredShadingRateAttachments ? "True" : "False" ); + LOGI( " fragmentShadingRateNonTrivialCombinerOps: %s", this->Properties.fragmentShadingRateNonTrivialCombinerOps ? "True" : "False" ); + LOGI( " maxFragmentSize: %ux%u", this->Properties.maxFragmentSize.width, this->Properties.maxFragmentSize.height ); + LOGI( " maxFragmentSizeAspectRatio: %u", this->Properties.maxFragmentSizeAspectRatio ); + LOGI( " maxFragmentShadingRateCoverageSamples: %u", this->Properties.maxFragmentShadingRateCoverageSamples ); + LOGI( " maxFragmentShadingRateRasterizationSamples: 0x%02x", this->Properties.maxFragmentShadingRateRasterizationSamples ); + LOGI( " fragmentShadingRateWithShaderDepthStencilWrites: %s", this->Properties.fragmentShadingRateWithShaderDepthStencilWrites ? "True" : "False" ); + LOGI( " fragmentShadingRateWithSampleMask: %s", this->Properties.fragmentShadingRateWithSampleMask ? "True" : "False" ); + LOGI( " fragmentShadingRateWithShaderSampleMask: %s", this->Properties.fragmentShadingRateWithShaderSampleMask ? "True" : "False" ); + LOGI( " fragmentShadingRateWithConservativeRasterization: %s", this->Properties.fragmentShadingRateWithConservativeRasterization ? "True" : "False" ); + LOGI( " fragmentShadingRateWithFragmentShaderInterlock: %s", this->Properties.fragmentShadingRateWithFragmentShaderInterlock ? "True" : "False" ); + LOGI( " fragmentShadingRateWithCustomSampleLocations: %s", this->Properties.fragmentShadingRateWithCustomSampleLocations ? "True" : "False" ); + LOGI( " fragmentShadingRateStrictMultiplyCombiner: %s", this->Properties.fragmentShadingRateStrictMultiplyCombiner ? "True" : "False" ); + } +#endif // VK_KHR_fragment_shading_rate + +#if VK_KHR_create_renderpass2 + void Ext_VK_KHR_create_renderpass2::LookupFunctionPointers( VkInstance vkInstance ) + { + m_vkCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCreateRenderPass2KHR" ); + m_vkCmdBeginRenderPass2KHR = (PFN_vkCmdBeginRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdBeginRenderPass2KHR" ); + m_vkCmdNextSubpass2KHR = (PFN_vkCmdNextSubpass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdNextSubpass2KHR" ); + m_vkCmdEndRenderPass2KHR = (PFN_vkCmdEndRenderPass2KHR)vkGetInstanceProcAddr( vkInstance, "vkCmdEndRenderPass2KHR" ); + } +#endif // VK_KHR_create_renderpass2 + +#if VK_KHR_draw_indirect_count + void Ext_VK_KHR_draw_indirect_count::LookupFunctionPointers( VkInstance vkInstance ) + { + m_vkCmdDrawIndirectCountKHR = (PFN_vkCmdDrawIndirectCountKHR)vkGetInstanceProcAddr( vkInstance, "vkCmdDrawIndirectCountKHR" ); + m_vkCmdDrawIndexedIndirectCountKHR = (PFN_vkCmdDrawIndexedIndirectCountKHR)vkGetInstanceProcAddr( vkInstance, "vkCmdDrawIndexedIndirectCountKHR" ); + } +#endif // VK_KHR_draw_indirect_count + +#if VK_KHR_dynamic_rendering + void Ext_VK_KHR_dynamic_rendering::PrintFeatures() const + { + LOGI( "VK_KHR_dynamic_rendering (VkPhysicalDeviceDynamicRenderingFeaturesKHR): " ); + LOGI( " dynamicRendering: %s", this->AvailableFeatures.dynamicRendering ? "True" : "False" ); + } +#endif // VK_KHR_dynamic_rendering + +#if VK_KHR_dynamic_rendering_local_read + void Ext_VK_KHR_dynamic_rendering_local_read::PrintFeatures() const + { + LOGI( "VK_KHR_dynamic_rendering_local_read (VkPhysicalDeviceDynamicRenderingLocalReadFeaturesKHR): " ); + LOGI( " dynamicRenderingLocalRead: %s", this->AvailableFeatures.dynamicRenderingLocalRead ? "True" : "False" ); + } +#endif // VK_KHR_dynamic_rendering_local_read + +#if VK_EXT_debug_utils + bool Ext_VK_EXT_debug_utils::SetDebugUtilsObjectName( VkDevice vkDevice, uint64_t object, VkObjectType objectType, const char* name ) const + { + if (m_vkSetDebugUtilsObjectNameEXT == nullptr) + return false; + VkDebugUtilsObjectNameInfoEXT nameInfo { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT }; + nameInfo.objectType = objectType; + nameInfo.objectHandle = object; + nameInfo.pObjectName = name; + return m_vkSetDebugUtilsObjectNameEXT( vkDevice, &nameInfo ) == VK_SUCCESS; + } +#endif // VK_EXT_debug_utils + +#if VK_EXT_debug_marker + bool Ext_VK_EXT_debug_marker::DebugMarkerSetObjectName( VkDevice vkDevice, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char* name ) const + { + if (m_vkDebugMarkerSetObjectNameEXT == nullptr) + return false; + VkDebugMarkerObjectNameInfoEXT markerInfo { VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT }; + markerInfo.objectType = objectType; + markerInfo.object = object; + markerInfo.pObjectName = name; + return m_vkDebugMarkerSetObjectNameEXT( vkDevice, &markerInfo ) == VK_SUCCESS; + } +#endif // VK_EXT_debug_marker + + static std::string GetSubgroupStagesString( VkShaderStageFlags Stages ) + { + std::string StagesString; + static const char* SubgroupStages[] = { "Vertex", "Tessellation Control", "Tessellation Evaluation", "Geometry", "Fragment", "Compute" }; + int i = 0; + for (; i < sizeof( SubgroupStages ) / sizeof( *SubgroupStages ); ++i) + { + if ((Stages & (1 << i)) != 0) + { + StagesString.append( SubgroupStages[i] ); + StagesString.append( ", " ); + } + } + // remainder + if ((Stages & ~((1 << i) - 1)) != 0) + { + StagesString.append( std::to_string( Stages & ~((1 << i) - 1) ) ); + } + return StagesString; + } + + +#if VK_EXT_subgroup_size_control + void Ext_VK_EXT_subgroup_size_control::PrintFeatures() const + { + LOGI( "VK_EXT_subgroup_size_control (VkPhysicalDeviceSubgroupSizeControlFeaturesEXT): " ); + LOGI( " subgroupSizeControl: %s", this->AvailableFeatures.subgroupSizeControl ? "True" : "False" ); + LOGI( " computeFullSubgroups: %s", this->AvailableFeatures.computeFullSubgroups ? "True" : "False" ); + } + + void Ext_VK_EXT_subgroup_size_control::PrintProperties() const + { + LOGI( "VK_EXT_subgroup_size_control (VkPhysicalDeviceSubgroupSizeControlPropertiesEXT): " ); + LOGI( " minSubgroupSize: %u", this->Properties.minSubgroupSize ); + LOGI( " maxSubgroupSize: %u", this->Properties.maxSubgroupSize ); + LOGI( " maxComputeWorkgroupSubgroups: %u", this->Properties.maxComputeWorkgroupSubgroups ); + auto SupportedStages = GetSubgroupStagesString( this->Properties.requiredSubgroupSizeStages ); + LOGI( " requiredSubgroupSizeStages: %s", SupportedStages.c_str() ); + } +#endif // VK_EXT_subgroup_size_control + +#if VK_EXT_host_query_reset + void Ext_VK_EXT_host_query_reset::PrintFeatures() const + { + LOGI( "VK_EXT_host_query_reset (VkPhysicalDeviceHostQueryResetFeaturesEXT): " ); + LOGI( " hostQueryReset: %s", this->AvailableFeatures.hostQueryReset ? "True" : "False" ); + } +#endif // VK_EXT_host_query_reset + +#if VK_KHR_timeline_semaphore + void Ext_VK_KHR_timeline_semaphore::PrintFeatures() const + { + LOGI("VK_KHR_timeline_semaphore (VkPhysicalDeviceTimelineSemaphoreFeaturesKHR): "); + LOGI(" timelineSemaphore: %s", this->AvailableFeatures.timelineSemaphore ? "True" : "False"); + } + void Ext_VK_KHR_timeline_semaphore::PrintProperties() const + { + LOGI("VK_KHR_timeline_semaphore (VkPhysicalDeviceTimelineSemaphorePropertiesKHR): "); + LOGI(" maxTimelineSemaphoreValueDifference: %" PRIu64, this->Properties.maxTimelineSemaphoreValueDifference); + } +#endif // VK_KHR_timeline_semaphore + +#if VK_KHR_synchronization2 + void Ext_VK_KHR_synchronization2::PrintFeatures() const + { + LOGI("VK_KHR_synchronization2 (VkPhysicalDeviceSynchronization2FeaturesKHR): "); + LOGI(" synchronization2: %s", this->AvailableFeatures.synchronization2 ? "True" : "False"); + } + void Ext_VK_KHR_synchronization2::LookupFunctionPointers(VkInstance vkInstance) + { + m_vkQueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)vkGetInstanceProcAddr(vkInstance, "vkQueueSubmit2KHR"); + } +#endif // VK_KHR_synchronization2 + +#if VK_ARM_tensors + void Ext_VK_ARM_tensors::PrintFeatures() const + { + LOGI("VK_ARM_tensors (VkPhysicalDeviceTensorFeaturesARM):"); + LOGI(" tensorNonPacked: %s", this->AvailableFeatures.tensorNonPacked ? "True" : "False"); + LOGI(" shaderTensorAccess: %s", this->AvailableFeatures.shaderTensorAccess ? "True" : "False"); + LOGI(" shaderStorageTensorArrayDynamicIndexing: %s", this->AvailableFeatures.shaderStorageTensorArrayDynamicIndexing ? "True" : "False"); + LOGI(" shaderStorageTensorArrayNonUniformIndexing: %s", this->AvailableFeatures.shaderStorageTensorArrayNonUniformIndexing ? "True" : "False"); + LOGI(" descriptorBindingStorageTensorUpdateAfterBind: %s", this->AvailableFeatures.descriptorBindingStorageTensorUpdateAfterBind ? "True" : "False"); + LOGI(" tensors: %s", this->AvailableFeatures.tensors ? "True" : "False"); + } + + void Ext_VK_ARM_tensors::PrintProperties() const + { + LOGI("VK_ARM_tensors (VkPhysicalDeviceTensorPropertiesARM):"); + LOGI(" maxTensorDimensionCount: %u", this->Properties.maxTensorDimensionCount); + LOGI(" maxTensorElements: %" PRIu64, this->Properties.maxTensorElements); + LOGI(" maxPerDimensionTensorElements: %" PRIu64, this->Properties.maxPerDimensionTensorElements); + LOGI(" maxTensorStride: %" PRId64, this->Properties.maxTensorStride); + LOGI(" maxTensorSize: %" PRIu64, this->Properties.maxTensorSize); + LOGI(" maxTensorShaderAccessArrayLength: %u", this->Properties.maxTensorShaderAccessArrayLength); + LOGI(" maxTensorShaderAccessSize: %u", this->Properties.maxTensorShaderAccessSize); + LOGI(" maxDescriptorSetStorageTensors: %u", this->Properties.maxDescriptorSetStorageTensors); + LOGI(" maxPerStageDescriptorSetStorageTensors: %u", this->Properties.maxPerStageDescriptorSetStorageTensors); + LOGI(" maxDescriptorSetUpdateAfterBindStorageTensors: %u", this->Properties.maxDescriptorSetUpdateAfterBindStorageTensors); + LOGI(" maxPerStageDescriptorUpdateAfterBindStorageTensors: %u", this->Properties.maxPerStageDescriptorUpdateAfterBindStorageTensors); + LOGI(" shaderStorageTensorArrayNonUniformIndexingNative: %s", this->Properties.shaderStorageTensorArrayNonUniformIndexingNative ? "True" : "False"); + LOGI(" shaderTensorSupportedStages: 0x%08X", this->Properties.shaderTensorSupportedStages); + } +#endif // VK_ARM_tensors + +#if VK_ARM_data_graph + void Ext_VK_ARM_data_graph::PrintFeatures() const + { + LOGI("VK_ARM_data_graph (VkPhysicalDeviceDataGraphFeaturesARM):"); + LOGI(" dataGraph: %s", this->AvailableFeatures.dataGraph ? "True" : "False"); + LOGI(" dataGraphUpdateAfterBind: %s", this->AvailableFeatures.dataGraphUpdateAfterBind ? "True" : "False"); + LOGI(" dataGraphSpecializationConstants: %s", this->AvailableFeatures.dataGraphSpecializationConstants ? "True" : "False"); + LOGI(" dataGraphDescriptorBuffer: %s", this->AvailableFeatures.dataGraphDescriptorBuffer ? "True" : "False"); + LOGI(" dataGraphShaderModule: %s", this->AvailableFeatures.dataGraphShaderModule ? "True" : "False"); + } +#endif // VK_ARM_data_graph + +#if VK_KHR_get_physical_device_properties2 + + void Ext_VK_KHR_get_physical_device_properties2::LookupFunctionPointers(VkInstance vkInstance) + { + m_vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceFeatures2KHR"); + m_vkGetPhysicalDeviceFormatProperties2KHR = (PFN_vkGetPhysicalDeviceFormatProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceFormatProperties2KHR" ); + m_vkGetPhysicalDeviceImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceImageFormatProperties2KHR" ); + m_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceMemoryProperties2KHR" ); + m_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceProperties2KHR" ); + } + +#endif // VK_KHR_get_physical_device_properties2 + +#if VK_KHR_surface + + void Ext_VK_KHR_surface::LookupFunctionPointers( VkInstance vkInstance ) + { + m_vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr( vkInstance, "vkDestroySurfaceKHR" ); + m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" ); + m_vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceFormatsKHR" ); + m_vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfacePresentModesKHR" ); + m_vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR)vkGetInstanceProcAddr( vkInstance, "vkGetPhysicalDeviceSurfaceSupportKHR" ); + } + +#endif // VK_KHR_surface + +#if VK_KHR_get_surface_capabilities2 + + void Ext_VK_KHR_get_surface_capabilities2::LookupFunctionPointers( VkInstance vkInstance ) + { + m_vkGetPhysicalDeviceSurfaceCapabilities2KHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); + m_vkGetPhysicalDeviceSurfaceFormats2KHR = (PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)vkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceSurfaceFormats2KHR"); + } + +#endif // VK_KHR_get_surface_capabilities2 + +#if VK_QCOM_tile_properties + + void Ext_VK_QCOM_tile_properties::LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr ) + { + m_vkGetDynamicRenderingTilePropertiesQCOM = (PFN_vkGetDynamicRenderingTilePropertiesQCOM)deviceProcAddr( vkDevice, "vkGetDynamicRenderingTilePropertiesQCOM" ); + m_vkGetFramebufferTilePropertiesQCOM = (PFN_vkGetFramebufferTilePropertiesQCOM)deviceProcAddr( vkDevice, "vkGetFramebufferTilePropertiesQCOM" ); + } + + void Ext_VK_QCOM_tile_properties::PrintFeatures() const + { + LOGI( "VK_QCOM_tile_properties (VkPhysicalDeviceTilePropertiesFeaturesQCOM): " ); + LOGI( " tileProperties: %s", this->AvailableFeatures.tileProperties ? "True" : "False" ); + } + +#endif // VK_QCOM_tile_properties + +#if VK_QCOM_tile_memory_heap + void Ext_VK_QCOM_tile_memory_heap::PrintFeatures() const + { + LOGI("VK_QCOM_tile_memory_heap (VkPhysicalDeviceTileMemoryHeapFeaturesQCOM): "); + LOGI(" tileMemoryHeap: %s", this->AvailableFeatures.tileMemoryHeap ? "True" : "False"); + } + + void Ext_VK_QCOM_tile_memory_heap::PrintProperties() const + { + LOGI("VK_QCOM_tile_memory_heap (VkPhysicalDeviceTileMemoryHeapPropertiesQCOM): "); + LOGI(" queueSubmitBoundary: %s", this->Properties.queueSubmitBoundary ? "True" : "False"); + LOGI(" tileBufferTransfers: %s", this->Properties.tileBufferTransfers ? "True" : "False"); + } +#endif // VK_KHR_fragment_shading_rate + +#if VK_QCOM_tile_shading + + void Ext_VK_QCOM_tile_shading::PopulateRequestedFeatures() + { + tBase::PopulateRequestedFeatures(); + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.tileShading = AvailableFeatures.tileShading; + RequestedFeatures.tileShadingPerTileDispatch = AvailableFeatures.tileShadingPerTileDispatch; + RequestedFeatures.tileShadingColorAttachments = AvailableFeatures.tileShadingColorAttachments; + RequestedFeatures.tileShadingAtomicOps = AvailableFeatures.tileShadingAtomicOps; + RequestedFeatures.tileShadingFragmentStage = AvailableFeatures.tileShadingFragmentStage; + RequestedFeatures.tileShadingPerTileDraw = AvailableFeatures.tileShadingPerTileDraw; + RequestedFeatures.tileShadingApron = AvailableFeatures.tileShadingApron; + RequestedFeatures.tileShadingDepthAttachments = AvailableFeatures.tileShadingDepthAttachments; + RequestedFeatures.tileShadingStencilAttachments = AvailableFeatures.tileShadingStencilAttachments; + RequestedFeatures.tileShadingInputAttachments = AvailableFeatures.tileShadingInputAttachments; + RequestedFeatures.tileShadingSampledAttachments = AvailableFeatures.tileShadingSampledAttachments; + RequestedFeatures.tileShadingAnisotropicApron = AvailableFeatures.tileShadingAnisotropicApron; + RequestedFeatures.tileShadingDispatchTile = AvailableFeatures.tileShadingDispatchTile; + RequestedFeatures.tileShadingImageProcessing = AvailableFeatures.tileShadingImageProcessing; + } + + void Ext_VK_QCOM_tile_shading::LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr deviceProcAddr ) + { + m_vkCmdDispatchTileQCOM = (PFN_vkCmdDispatchTileQCOM)deviceProcAddr( vkDevice, "vkCmdDispatchTileQCOM" ); + if (m_vkCmdDispatchTileQCOM == nullptr) + { + LOGE("Driver does not expose \"vkCmdDispatchTileQCOM\" function, trying \"vkCmdTileDispatchQCOM\""); + m_vkCmdDispatchTileQCOM = (PFN_vkCmdDispatchTileQCOM)deviceProcAddr( vkDevice, "vkCmdTileDispatchQCOM" ); + } + m_vkCmdBeginPerTileExecutionQCOM = (PFN_vkCmdBeginPerTileExecutionQCOM)deviceProcAddr( vkDevice, "vkCmdBeginPerTileExecutionQCOM" ); + m_vkCmdEndPerTileExecutionQCOM = (PFN_vkCmdEndPerTileExecutionQCOM)deviceProcAddr( vkDevice, "vkCmdEndPerTileExecutionQCOM" ); + + LOGI( "Ext_VK_QCOM_tile_shading function pointers: %p %p %p", m_vkCmdDispatchTileQCOM, m_vkCmdBeginPerTileExecutionQCOM, m_vkCmdEndPerTileExecutionQCOM ); + } + + void Ext_VK_QCOM_tile_shading::PrintFeatures() const + { + LOGI( "VK_QCOM_tile_shading (VkPhysicalDeviceTileShadingFeaturesQCOM): " ); + + + LOGI( " tileShading: %s", this->AvailableFeatures.tileShading ? "True" : "False" ); + LOGI( " tileShadingFragmentStage: %s", this->AvailableFeatures.tileShadingFragmentStage ? "True" : "False" ); + LOGI( " tileShadingColorAttachments: %s", this->AvailableFeatures.tileShadingColorAttachments ? "True" : "False" ); + LOGI( " tileShadingDepthAttachments: %s", this->AvailableFeatures.tileShadingDepthAttachments ? "True" : "False" ); + LOGI( " tileShadingStencilAttachments: %s", this->AvailableFeatures.tileShadingStencilAttachments ? "True" : "False" ); + LOGI( " tileShadingInputAttachments: %s", this->AvailableFeatures.tileShadingInputAttachments ? "True" : "False" ); + LOGI( " tileShadingSampledAttachments: %s", this->AvailableFeatures.tileShadingSampledAttachments ? "True" : "False" ); + LOGI( " tileShadingPerTileDraw: %s", this->AvailableFeatures.tileShadingPerTileDraw ? "True" : "False" ); + LOGI( " tileShadingPerTileDispatch: %s", this->AvailableFeatures.tileShadingPerTileDispatch ? "True" : "False" ); + LOGI( " tileShadingApron: %s", this->AvailableFeatures.tileShadingApron ? "True" : "False" ); + LOGI( " tileShadingAnisotropicApron: %s", this->AvailableFeatures.tileShadingAnisotropicApron ? "True" : "False" ); + LOGI( " tileShadingAtomicOps: %s", this->AvailableFeatures.tileShadingAtomicOps ? "True" : "False" ); + LOGI( " tileShadingDispatchTile: %s", this->AvailableFeatures.tileShadingDispatchTile ? "True" : "False" ); + LOGI( " tileShadingImageProcessing: %s", this->AvailableFeatures.tileShadingImageProcessing ? "True" : "False" ); + } + + void Ext_VK_QCOM_tile_shading::PrintProperties() const + { + LOGI( "VK_QCOM_tile_shading (VkPhysicalDeviceTileShadingPropertiesQCOM): " ); + LOGI( " maxApronSize: %d", this->Properties.maxApronSize ); + LOGI( " preferNonCoherent: %s", this->Properties.preferNonCoherent ? "True" : "False" ); + LOGI( " tileGranularity: [%d, %d]", this->Properties.tileGranularity.width, this->Properties.tileGranularity.height ); + LOGI( " maxTileShadingRate: [%d, %d]", this->Properties.maxTileShadingRate.width, this->Properties.maxTileShadingRate.height ); + } + +#endif // VK_QCOM_tile_shading + + void Vulkan_SubgroupPropertiesHook::PrintProperties() const + { + LOGI( "VkPhysicalDeviceSubgroupProperties: " ); + LOGI( " subgroupSize: %d", Properties.subgroupSize ); + auto SupportedStages = GetSubgroupStagesString( Properties.supportedStages ); + LOGI( " supportedStages: %s", SupportedStages.c_str() ); + + std::string SupportedOperations; + static const char* SubgroupOperations[] = { "Basic", "Vote", "Arithmetic", "Ballot", "Shuffle", "Shuffle Relative", "Clustered", "Quad" }; + for (int i = 0; i < sizeof( SubgroupOperations ) / sizeof( *SubgroupOperations ); ++i) + { + if ((Properties.supportedOperations & (1 << i)) != 0) + { + SupportedOperations.append( SubgroupOperations[i] ); + SupportedOperations.append( ", " ); + } + } + LOGI( " supportedOperations: %s", SupportedOperations.c_str() ); + + LOGI( " quadOperationsInAllStages: %s", Properties.quadOperationsInAllStages ? "True" : "False" ); + } + + + void Vulkan_StorageFeaturesHook::PrintFeatures() const + { + LOGI( "FeaturesStorage16Bit: " ); + LOGI( " storageBuffer16BitAccess: %s", this->AvailableFeatures.storageBuffer16BitAccess ? "True" : "False" ); + LOGI( " uniformAndStorageBuffer16BitAccess: %s", this->AvailableFeatures.uniformAndStorageBuffer16BitAccess ? "True" : "False" ); + LOGI( " storagePushConstant16: %s", this->AvailableFeatures.storagePushConstant16 ? "True" : "False" ); + LOGI( " storageInputOutput16: %s", this->AvailableFeatures.storageInputOutput16 ? "True" : "False" ); + } + + +}; //namespace diff --git a/framework/code/vulkan/extensionLib.hpp b/framework/code/vulkan/extensionLib.hpp new file mode 100644 index 0000000..beda17d --- /dev/null +++ b/framework/code/vulkan/extensionLib.hpp @@ -0,0 +1,770 @@ +//============================================================================================================ +// +// +// Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once +#include "extensionHelpers.hpp" +#include + +class Vulkan; + +/// +/// Library of Vulkan extensions +/// + +namespace ExtensionLib +{ +#if VK_KHR_buffer_device_address + + struct Ext_VK_KHR_buffer_device_address : public VulkanDeviceFeaturesExtensionHelper + { + static constexpr auto Name = VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME; + Ext_VK_KHR_buffer_device_address(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) + : VulkanDeviceFeaturesExtensionHelper(Name, status) + {} + void PrintFeatures() const override + { + LOGI("FeaturesBufferDeviceAddress: "); + LOGI(" bufferDeviceAddress: %s", this->AvailableFeatures.bufferDeviceAddress ? "True" : "False"); + LOGI(" bufferDeviceAddressCaptureReplay: %s", this->AvailableFeatures.bufferDeviceAddressCaptureReplay ? "True" : "False"); + LOGI(" bufferDeviceAddressMultiDevice: %s", this->AvailableFeatures.bufferDeviceAddressMultiDevice ? "True" : "False"); + } + void PopulateRequestedFeatures() override + { + // Enable just the 'bufferDeviceAddress' feature + RequestedFeatures.bufferDeviceAddress = AvailableFeatures.bufferDeviceAddress; + } + }; + +#endif // VK_KHR_buffer_device_address + + +#if VK_EXT_mesh_shader + + struct Ext_VK_KHR_mesh_shader : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceMeshShaderFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, + VkPhysicalDeviceMeshShaderPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT> + { + static constexpr auto Name = VK_EXT_MESH_SHADER_EXTENSION_NAME; + explicit Ext_VK_KHR_mesh_shader( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper( Name, status ) + {} + + void PopulateRequestedFeatures() override + { + RequestedFeatures = AvailableFeatures; + RequestedFeatures.multiviewMeshShader = VK_FALSE; //if we need multiview then device needs setting up for multiview also + RequestedFeatures.primitiveFragmentShadingRateMeshShader = VK_FALSE;//if we need fragment shading rate then device needs setting up for it (and we need to revisit this flag!) + } + void LookupFunctionPointers( VkInstance ) override {} + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override; + void PrintFeatures() const override; + void PrintProperties() const override; + PFN_vkCmdDrawMeshTasksEXT m_vkCmdDrawMeshTasksEXT = nullptr; + PFN_vkCmdDrawMeshTasksIndirectEXT m_vkCmdDrawMeshTasksIndirectEXT = nullptr; + PFN_vkCmdDrawMeshTasksIndirectCountEXT m_vkCmdDrawMeshTasksIndirectCountEXT = nullptr; + }; + +#endif // VK_EXT_mesh_shader + +#if VK_KHR_swapchain + + struct Ext_VK_KHR_swapchain : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + explicit Ext_VK_KHR_swapchain( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers( VkInstance ) override; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override; + + PFN_vkCreateSwapchainKHR m_vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR m_vkDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR m_vkGetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR m_vkAcquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR m_vkQueuePresentKHR = nullptr; + PFN_vkGetDeviceGroupPresentCapabilitiesKHR m_vkGetDeviceGroupPresentCapabilitiesKHR = nullptr; + PFN_vkGetDeviceGroupSurfacePresentModesKHR m_vkGetDeviceGroupSurfacePresentModesKHR = nullptr; + PFN_vkGetPhysicalDevicePresentRectanglesKHR m_vkGetPhysicalDevicePresentRectanglesKHR = nullptr; + PFN_vkAcquireNextImage2KHR m_vkAcquireNextImage2KHR = nullptr; + }; + +#endif // VK_KHR_swapchain + + +#if VK_KHR_shader_float16_int8 + + struct Ext_VK_KHR_shader_float16_int8 : public VulkanDeviceFeaturesExtensionHelper + { + static constexpr auto Name = VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME; + explicit Ext_VK_KHR_shader_float16_int8( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + }; + +#endif // VK_KHR_shader_float16_int8 + +#if VK_EXT_shader_image_atomic_int64 + + struct Ext_VK_EXT_shader_image_atomic_int64 : public VulkanDeviceFeaturesExtensionHelper + { + static constexpr auto Name = VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME; + explicit Ext_VK_EXT_shader_image_atomic_int64( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper( Name, status ) + {} + void PrintFeatures() const override; + }; + +#endif // VK_EXT_shader_image_atomic_int64 + +#if VK_EXT_index_type_uint8 + + struct Ext_VK_EXT_index_type_uint8 : public VulkanDeviceFeaturesExtensionHelper + { + static constexpr auto Name = VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME; + explicit Ext_VK_EXT_index_type_uint8( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + }; + +#endif // VK_EXT_index_type_uint8 + +#if VK_KHR_shader_subgroup_extended_types + + struct Ext_VK_KHR_shader_subgroup_extended_types : public VulkanDeviceFeaturesExtensionHelper + { + static constexpr auto Name = VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME; + explicit Ext_VK_KHR_shader_subgroup_extended_types( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + }; + +#endif // VK_KHR_shader_subgroup_extended_types + +#if VK_EXT_descriptor_indexing + + struct Ext_VK_EXT_descriptor_indexing : public VulkanDeviceFeaturePropertiesExtensionHelper< + VkPhysicalDeviceDescriptorIndexingFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT, + VkPhysicalDeviceDescriptorIndexingPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT> + { + static constexpr auto Name = VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME; + explicit Ext_VK_EXT_descriptor_indexing( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + void PrintProperties() const override; + }; + +#endif // VK_EXT_descriptor_indexing + +#if VK_KHR_8bit_storage + + struct Ext_VK_KHR_8bit_storage : public VulkanDeviceFeaturesExtensionHelper< + VkPhysicalDevice8BitStorageFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR> + { + static constexpr auto Name = VK_KHR_8BIT_STORAGE_EXTENSION_NAME; + explicit Ext_VK_KHR_8bit_storage( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + }; + +#endif // VK_KHR_8bit_storage + +#if VK_KHR_portability_subset + + struct Ext_VK_KHR_portability_subset : public VulkanDeviceFeaturePropertiesExtensionHelper< + VkPhysicalDevicePortabilitySubsetFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR, + VkPhysicalDevicePortabilitySubsetPropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR> + { + static constexpr auto Name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME; + explicit Ext_VK_KHR_portability_subset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) + {} + void PrintFeatures() const override; + void PrintProperties() const override; + }; + +#else + + // 'dummy' implementation of Ext_VK_KHR_portability_subset for when vulkan headers do not contain 'VK_KHR_portability_subset' + struct Ext_VK_KHR_portability_subset : public VulkanExtension + { + static constexpr auto Name = "VK_KHR_portability_subset"; + Ext_VK_KHR_portability_subset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanExtension( Name, status ) {} + }; + +#endif // VK_KHR_portability_subset + +#if VK_KHR_fragment_shading_rate + + struct Ext_VK_KHR_fragment_shading_rate : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceFragmentShadingRateFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, + VkPhysicalDeviceFragmentShadingRatePropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR> + { + static constexpr auto Name = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; + explicit Ext_VK_KHR_fragment_shading_rate( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper( Name, status ) + {} + + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.attachmentFragmentShadingRate = AvailableFeatures.attachmentFragmentShadingRate; + } + void LookupFunctionPointers( VkInstance vkInstance ) override + { + m_vkCmdSetFragmentShadingRateKHR = (PFN_vkCmdSetFragmentShadingRateKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdSetFragmentShadingRateKHR" ); + } + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {} + void PrintFeatures() const override; + void PrintProperties() const override; + PFN_vkCmdSetFragmentShadingRateKHR m_vkCmdSetFragmentShadingRateKHR = nullptr; + }; + +#endif // VK_KHR_fragment_shading_rate + +#if VK_KHR_create_renderpass2 + + struct Ext_VK_KHR_create_renderpass2 : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; + explicit Ext_VK_KHR_create_renderpass2( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers(VkInstance vkInstance) override; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } + PFN_vkCreateRenderPass2KHR m_vkCreateRenderPass2KHR = nullptr; + PFN_vkCmdBeginRenderPass2KHR m_vkCmdBeginRenderPass2KHR = nullptr; + PFN_vkCmdNextSubpass2KHR m_vkCmdNextSubpass2KHR = nullptr; + PFN_vkCmdEndRenderPass2KHR m_vkCmdEndRenderPass2KHR = nullptr; + }; + +#endif // VK_KHR_create_renderpass2 + +#if VK_KHR_draw_indirect_count + + struct Ext_VK_KHR_draw_indirect_count : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME; + explicit Ext_VK_KHR_draw_indirect_count( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers( VkInstance vkInstance ) override; + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/} + PFN_vkCmdDrawIndirectCountKHR m_vkCmdDrawIndirectCountKHR = nullptr; + PFN_vkCmdDrawIndexedIndirectCountKHR m_vkCmdDrawIndexedIndirectCountKHR = nullptr; + }; + +#endif // VK_KHR_draw_indirect_count + +#if VK_KHR_depth_stencil_resolve + + struct Ext_VK_KHR_depth_stencil_resolve : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME; + explicit Ext_VK_KHR_depth_stencil_resolve( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers( VkInstance vkInstance ) override + {/*no instance functions*/} + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/} + }; + +#endif // VK_KHR_depth_stencil_resolve + +#if VK_KHR_dynamic_rendering + + struct Ext_VK_KHR_dynamic_rendering : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceDynamicRenderingFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES > + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME; + explicit Ext_VK_KHR_dynamic_rendering( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) {} + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.dynamicRendering = AvailableFeatures.dynamicRendering; + } + void LookupFunctionPointers( VkInstance vkInstance ) override + { + m_vkCmdBeginRenderingKHR = (PFN_vkCmdBeginRenderingKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdBeginRenderingKHR" ); + m_vkCmdEndRenderingKHR = (PFN_vkCmdEndRenderingKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdEndRenderingKHR" ); + } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/} + void PrintFeatures() const override; + PFN_vkCmdBeginRenderingKHR m_vkCmdBeginRenderingKHR = nullptr; + PFN_vkCmdEndRenderingKHR m_vkCmdEndRenderingKHR = nullptr; + }; + +#endif // VK_KHR_dynamic_rendering + +#if VK_KHR_dynamic_rendering_local_read + + struct Ext_VK_KHR_dynamic_rendering_local_read : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceDynamicRenderingLocalReadFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES_KHR > + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME; + explicit Ext_VK_KHR_dynamic_rendering_local_read( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) {} + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.dynamicRenderingLocalRead = AvailableFeatures.dynamicRenderingLocalRead; + } + void LookupFunctionPointers( VkInstance vkInstance ) override + { + m_vkCmdSetRenderingAttachmentLocationsKHR = (PFN_vkCmdSetRenderingAttachmentLocationsKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdSetRenderingAttachmentLocationsKHR" ); + m_vkCmdSetRenderingInputAttachmentIndicesKHR = (PFN_vkCmdSetRenderingInputAttachmentIndicesKHR) vkGetInstanceProcAddr( vkInstance, "vkCmdSetRenderingInputAttachmentIndicesKHR" ); + } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/} + void PrintFeatures() const override; + PFN_vkCmdSetRenderingAttachmentLocationsKHR m_vkCmdSetRenderingAttachmentLocationsKHR = nullptr; + PFN_vkCmdSetRenderingInputAttachmentIndicesKHR m_vkCmdSetRenderingInputAttachmentIndicesKHR = nullptr; + }; + +#endif // VK_KHR_dynamic_rendering_local_read + +#if VK_EXT_hdr_metadata + + struct Ext_VK_EXT_hdr_metadata : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_EXT_HDR_METADATA_EXTENSION_NAME; + explicit Ext_VK_EXT_hdr_metadata(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanFunctionPointerExtensionHelper(Name, status) {} + void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/} + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override + { + m_vkSetHdrMetadataEXT = (PFN_vkSetHdrMetadataEXT) fpGetDeviceProcAddr( vkDevice, "vkSetHdrMetadataEXT" ); + } + PFN_vkSetHdrMetadataEXT m_vkSetHdrMetadataEXT = nullptr; + }; + +#endif // VK_EXT_hdr_metadata + +#if VK_EXT_debug_utils + + struct Ext_VK_EXT_debug_utils : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + explicit Ext_VK_EXT_debug_utils( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers( VkInstance vkInstance ) override + { + m_vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT) vkGetInstanceProcAddr( vkInstance, "vkSetDebugUtilsObjectNameEXT" ); + } + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } + bool SetDebugUtilsObjectName( VkDevice vkDevice, uint64_t object, VkObjectType objectType, const char* name ) const; + + PFN_vkSetDebugUtilsObjectNameEXT m_vkSetDebugUtilsObjectNameEXT = nullptr; + }; + +#endif // VK_EXT_debug_utils + +#if VK_EXT_debug_marker + + struct Ext_VK_EXT_debug_marker : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_EXT_DEBUG_MARKER_EXTENSION_NAME; + explicit Ext_VK_EXT_debug_marker( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanFunctionPointerExtensionHelper( Name, status ) {} + void LookupFunctionPointers( VkInstance ) override {/*no instance functions*/ } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override + { + m_vkDebugMarkerSetObjectNameEXT = (PFN_vkDebugMarkerSetObjectNameEXT) fpGetDeviceProcAddr( vkDevice, "vkDebugMarkerSetObjectNameEXT" ); + } + bool DebugMarkerSetObjectName( VkDevice vkDevice, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char* name ) const; + + PFN_vkDebugMarkerSetObjectNameEXT m_vkDebugMarkerSetObjectNameEXT = nullptr; + }; + +#endif // Ext_VK_EXT_debug_marker + +#if VK_EXT_subgroup_size_control + + struct Ext_VK_EXT_subgroup_size_control : public VulkanDeviceFeaturePropertiesExtensionHelper< + VkPhysicalDeviceSubgroupSizeControlFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT, + VkPhysicalDeviceSubgroupSizeControlPropertiesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT > + { + using tBase = VulkanDeviceFeaturePropertiesExtensionHelper; + static constexpr auto Name = VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME; + explicit Ext_VK_EXT_subgroup_size_control( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : VulkanDeviceFeaturePropertiesExtensionHelper( Name, status ), m_ShaderCreateHook(this) + {} + void PrintFeatures() const override; + void PrintProperties() const override; + + VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT SubGroupSizeControl { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT }; + + private: + class ShaderCreateStageHook final : public ExtensionHook + { + friend struct Ext_VK_EXT_subgroup_size_control; + explicit ShaderCreateStageHook( Ext_VK_EXT_subgroup_size_control* _Parent ) : ExtensionHook(), Parent( _Parent ) {} + VkStructureType StructureType() const override { return (VkStructureType) 0; }; + VkBaseOutStructure* Obtain( tBase* pBase ) override { + if ((pBase->stage & Parent->Properties.requiredSubgroupSizeStages)!=0 + && Parent->RequestedFeatures.subgroupSizeControl + && Parent->SubGroupSizeControl.requiredSubgroupSize != 0) + { + Parent->SubGroupSizeControl.pNext = nullptr; + pBase->flags |= VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT; + return (VkBaseOutStructure*) &Parent->SubGroupSizeControl; + } + return nullptr; + } + void Release( VkBaseOutStructure* pBase ) override { + assert( pBase == (VkBaseOutStructure *) &Parent->SubGroupSizeControl ); + } + Ext_VK_EXT_subgroup_size_control* Parent; + } m_ShaderCreateHook; + + void Register( Vulkan& vulkan ) override + { + tBase::Register( vulkan ); + vulkan.AddExtensionHooks( &m_ShaderCreateHook ); + } + }; + +#endif // VK_EXT_subgroup_size_control + +#if VK_EXT_host_query_reset + + struct Ext_VK_EXT_host_query_reset : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceHostQueryResetFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT > + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME; + explicit Ext_VK_EXT_host_query_reset( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void PrintFeatures() const override; + void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/ } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr ) override + { + if (RequestedFeatures.hostQueryReset == VK_TRUE) + m_vkResetQueryPoolEXT = (PFN_vkResetQueryPoolEXT) fpGetDeviceProcAddr( vkDevice, "vkResetQueryPoolEXT" ); + } + PFN_vkResetQueryPoolEXT m_vkResetQueryPoolEXT = nullptr; + }; + +#endif // VK_EXT_host_query_reset + +#if VK_KHR_timeline_semaphore + + struct Ext_VK_KHR_timeline_semaphore : public VulkanDeviceFeaturePropertiesExtensionHelper< + VkPhysicalDeviceTimelineSemaphoreFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, + VkPhysicalDeviceTimelineSemaphorePropertiesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR > + { + using tBase = VulkanDeviceFeaturePropertiesExtensionHelper; + static constexpr auto Name = VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME; + explicit Ext_VK_KHR_timeline_semaphore( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void PrintFeatures() const override; + void PrintProperties() const override; + }; + +#endif // VK_KHR_timeline_semaphore + +#if VK_KHR_synchronization2 + + struct Ext_VK_KHR_synchronization2 : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceSynchronization2FeaturesKHR, (VkStructureType) VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR > + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; + explicit Ext_VK_KHR_synchronization2(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : tBase(Name, status) + {} + void PrintFeatures() const override; + void LookupFunctionPointers(VkInstance vkInstance) override; + void LookupFunctionPointers(VkDevice, PFN_vkGetDeviceProcAddr) override {/*no device functions*/ } + PFN_vkQueueSubmit2KHR m_vkQueueSubmit2KHR = nullptr; + }; + +#endif // VK_KHR_synchronization2 + +#if VK_ARM_tensors + + struct Ext_VK_ARM_tensors : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceTensorFeaturesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TENSOR_FEATURES_ARM, + VkPhysicalDeviceTensorPropertiesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TENSOR_PROPERTIES_ARM> + { + static constexpr auto Name = VK_ARM_TENSORS_EXTENSION_NAME; + + explicit Ext_VK_ARM_tensors(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) + : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper(Name, status) + { + } + + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.tensorNonPacked = AvailableFeatures.tensorNonPacked; + RequestedFeatures.shaderTensorAccess = AvailableFeatures.shaderTensorAccess; + RequestedFeatures.shaderStorageTensorArrayDynamicIndexing = AvailableFeatures.shaderStorageTensorArrayDynamicIndexing; + RequestedFeatures.shaderStorageTensorArrayNonUniformIndexing = AvailableFeatures.shaderStorageTensorArrayNonUniformIndexing; + RequestedFeatures.descriptorBindingStorageTensorUpdateAfterBind = AvailableFeatures.descriptorBindingStorageTensorUpdateAfterBind; + RequestedFeatures.tensors = AvailableFeatures.tensors; + } + + void PrintFeatures() const override; + void PrintProperties() const override; + void LookupFunctionPointers(VkInstance vkInstance) override {} + void LookupFunctionPointers(VkDevice, PFN_vkGetDeviceProcAddr) override {} + }; + +#endif // VK_ARM_tensors + +#if VK_ARM_data_graph + + struct Ext_VK_ARM_data_graph : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceDataGraphFeaturesARM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DATA_GRAPH_FEATURES_ARM> + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_ARM_DATA_GRAPH_EXTENSION_NAME; + + explicit Ext_VK_ARM_data_graph(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) + : tBase(Name, status) + { + } + + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.dataGraph = AvailableFeatures.dataGraph; + RequestedFeatures.dataGraphUpdateAfterBind = AvailableFeatures.dataGraphUpdateAfterBind; + RequestedFeatures.dataGraphSpecializationConstants = AvailableFeatures.dataGraphSpecializationConstants; + RequestedFeatures.dataGraphDescriptorBuffer = AvailableFeatures.dataGraphDescriptorBuffer; + RequestedFeatures.dataGraphShaderModule = AvailableFeatures.dataGraphShaderModule; + } + + void PrintFeatures() const override; + void LookupFunctionPointers(VkInstance vkInstance) override {} + void LookupFunctionPointers(VkDevice, PFN_vkGetDeviceProcAddr) override {} + }; + +#endif // VK_ARM_data_graph + +#if VK_QCOM_tile_properties + + struct Ext_VK_QCOM_tile_properties : public VulkanFeaturesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceTilePropertiesFeaturesQCOM, (VkStructureType)VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_PROPERTIES_FEATURES_QCOM> + { + using tBase = VulkanFeaturesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME; + explicit Ext_VK_QCOM_tile_properties( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/ }; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override; + void PrintFeatures() const override; + PFN_vkGetDynamicRenderingTilePropertiesQCOM m_vkGetDynamicRenderingTilePropertiesQCOM = nullptr; + PFN_vkGetFramebufferTilePropertiesQCOM m_vkGetFramebufferTilePropertiesQCOM = nullptr; + }; + +#endif // VK_QCOM_tile_properties + +#if VK_QCOM_tile_shading + + struct Ext_VK_QCOM_tile_shading : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceTileShadingFeaturesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_SHADING_FEATURES_QCOM, + VkPhysicalDeviceTileShadingPropertiesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_SHADING_PROPERTIES_QCOM> + { + using tBase = VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper; + static constexpr auto Name = VK_QCOM_TILE_SHADING_EXTENSION_NAME; + explicit Ext_VK_QCOM_tile_shading( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void PopulateRequestedFeatures() override; + void LookupFunctionPointers( VkInstance vkInstance ) override {/*no instance functions*/ } + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override; + void PrintFeatures() const override; + void PrintProperties() const override; + + PFN_vkCmdDispatchTileQCOM m_vkCmdDispatchTileQCOM = nullptr; + PFN_vkCmdBeginPerTileExecutionQCOM m_vkCmdBeginPerTileExecutionQCOM = nullptr; + PFN_vkCmdEndPerTileExecutionQCOM m_vkCmdEndPerTileExecutionQCOM = nullptr; + }; + inline namespace fvk { + using VkPerTileBeginInfoQCOM = ::fvk::VkStructWrapper; + using VkPerTileEndInfoQCOM = ::fvk::VkStructWrapper; + using VkDispatchTileInfoQCOM = ::fvk::VkStructWrapper; + }; + +#endif // VK_QCOM_tile_shading + +#if VK_QCOM_tile_memory_heap + + struct Ext_VK_QCOM_tile_memory_heap : public VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper< + VkPhysicalDeviceTileMemoryHeapFeaturesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_MEMORY_HEAP_FEATURES_QCOM, + VkPhysicalDeviceTileMemoryHeapPropertiesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TILE_MEMORY_HEAP_PROPERTIES_QCOM> + { + static constexpr auto Name = VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME; + explicit Ext_VK_QCOM_tile_memory_heap(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanFeaturesPropertiesAndFunctionPointerExtensionHelper(Name, status) + {} + + void PopulateRequestedFeatures() override + { + RequestedFeatures.sType = AvailableFeatures.sType; + RequestedFeatures.tileMemoryHeap = true; /*AvailableFeatures.tileMemoryHeap;*/ + } + void LookupFunctionPointers( VkInstance ) override {/*no instance functions*/ } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override + { + m_vkCmdBindTileMemoryQCOM = (PFN_vkCmdBindTileMemoryQCOM)vkGetDeviceProcAddr(vkDevice, "vkCmdBindTileMemoryQCOM"); + } + void PrintFeatures() const override; + void PrintProperties() const override; + PFN_vkCmdBindTileMemoryQCOM m_vkCmdBindTileMemoryQCOM = nullptr; + }; + +#endif // VK_QCOM_tile_memory_heap + +#if VK_KHR_get_memory_requirements2 + + struct Ext_VK_KHR_get_memory_requirements2 : public VulkanFunctionPointerExtensionHelper + { + static constexpr auto Name = VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME; + explicit Ext_VK_KHR_get_memory_requirements2(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanFunctionPointerExtensionHelper(Name, status) {} + void LookupFunctionPointers( VkInstance ) override {/*no instance functions*/ } + void LookupFunctionPointers( VkDevice vkDevice, PFN_vkGetDeviceProcAddr ) override + { + m_vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(vkDevice, "vkGetBufferMemoryRequirements2KHR"); + m_vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(vkDevice, "vkGetImageMemoryRequirements2KHR"); + m_vkGetImageSparseMemoryRequirements2KHR = (PFN_vkGetImageSparseMemoryRequirements2KHR)vkGetDeviceProcAddr(vkDevice, "vkGetImageSparseMemoryRequirements2KHR"); + } + PFN_vkGetBufferMemoryRequirements2KHR m_vkGetBufferMemoryRequirements2KHR = nullptr; + PFN_vkGetImageMemoryRequirements2KHR m_vkGetImageMemoryRequirements2KHR = nullptr; + PFN_vkGetImageSparseMemoryRequirements2KHR m_vkGetImageSparseMemoryRequirements2KHR = nullptr; + }; + +#endif // VK_KHR_get_memory_requirements2 + +#if VK_KHR_ray_tracing_position_fetch + + struct Ext_VK_KHR_ray_tracing_position_fetch : public VulkanDeviceFeaturesExtensionHelper< + VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR> + { + static constexpr auto Name = VK_KHR_RAY_TRACING_POSITION_FETCH_EXTENSION_NAME; + explicit Ext_VK_KHR_ray_tracing_position_fetch( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper( Name, status ) + {} + void PrintFeatures() const override; + }; + +#endif // VK_KHR_ray_tracing_position_fetch + +#if VK_EXT_scalar_block_layout + + struct Ext_VK_EXT_scalar_block_layout : public VulkanDeviceFeaturesExtensionHelper< + VkPhysicalDeviceScalarBlockLayoutFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT> + { + static constexpr auto Name = VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME; + explicit Ext_VK_EXT_scalar_block_layout( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) + : VulkanDeviceFeaturesExtensionHelper( Name, status ) + {} + void PrintFeatures() const override; + }; + +#endif // VK_EXT_scalar_block_layout + +#if VK_KHR_get_physical_device_properties2 + + // Instance extension + struct Ext_VK_KHR_get_physical_device_properties2 : public VulkanFunctionPointerExtensionHelper + { + using tBase = VulkanFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + explicit Ext_VK_KHR_get_physical_device_properties2(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : tBase(Name, status) + {} + void LookupFunctionPointers(VkInstance vkInstance) override; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } + PFN_vkGetPhysicalDeviceFeatures2KHR m_vkGetPhysicalDeviceFeatures2KHR = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties2KHR m_vkGetPhysicalDeviceFormatProperties2KHR = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties2KHR m_vkGetPhysicalDeviceImageFormatProperties2KHR = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties2KHR m_vkGetPhysicalDeviceMemoryProperties2KHR = nullptr; + PFN_vkGetPhysicalDeviceProperties2KHR m_vkGetPhysicalDeviceProperties2KHR = nullptr; + }; + +#endif // VK_KHR_get_physical_device_properties2 + +#if VK_KHR_surface + + // Instance extension + struct Ext_VK_KHR_surface : public VulkanFunctionPointerExtensionHelper + { + using tBase = VulkanFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_SURFACE_EXTENSION_NAME; + explicit Ext_VK_KHR_surface( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void LookupFunctionPointers( VkInstance vkInstance ) override; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } + PFN_vkDestroySurfaceKHR m_vkDestroySurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR m_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR m_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; + }; + +#endif // VK_KHR_surface + +#if VK_KHR_get_surface_capabilities2 + + // Instance extension + struct Ext_VK_KHR_get_surface_capabilities2 : public VulkanFunctionPointerExtensionHelper + { + using tBase = VulkanFunctionPointerExtensionHelper; + static constexpr auto Name = VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME; + explicit Ext_VK_KHR_get_surface_capabilities2( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ) + {} + void LookupFunctionPointers( VkInstance vkInstance ) override; + void LookupFunctionPointers( VkDevice, PFN_vkGetDeviceProcAddr ) override {/*no device functions*/ } + PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR m_vkGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormats2KHR m_vkGetPhysicalDeviceSurfaceFormats2KHR = nullptr; + }; + +#endif // VK_KHR_get_surface_capabilities2 + + // + // Vulkan 1.1 (VK_VERSION_1_1) provided features/properties. + // Same interface as other extensions but do not need to be added to the list of extension names on vkCreateDevice. + // + + struct Vulkan_SubgroupPropertiesHook : public VulkanExtension + { + static constexpr auto Name = "SubgroupProperties"; + using tBase = VulkanExtension; + Vulkan_SubgroupPropertiesHook& operator=( const Vulkan_SubgroupPropertiesHook& ) = delete; + Vulkan_SubgroupPropertiesHook( const Vulkan_SubgroupPropertiesHook& ) = delete; + explicit Vulkan_SubgroupPropertiesHook( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( Name, status ), m_GetPhysicalDevicePropertiesHook( this ), m_VulkanDevicePropertiesPrintHook( this ) + {} + VkPhysicalDeviceSubgroupProperties Properties { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES }; + + private: + void Register( Vulkan& vulkan ) override + { + vulkan.AddExtensionHooks( &m_GetPhysicalDevicePropertiesHook, &m_VulkanDevicePropertiesPrintHook ); + } + + friend class GetPhysicalDevicePropertiesHook; + friend class VulkanDevicePropertiesPrintHook; + GetPhysicalDevicePropertiesHook m_GetPhysicalDevicePropertiesHook; + VulkanDevicePropertiesPrintHook m_VulkanDevicePropertiesPrintHook; + + void PrintProperties() const; + }; + + struct Vulkan_StorageFeaturesHook : public VulkanExtension + { + static constexpr auto Name = "StorageFeatures"; + using tBase = VulkanExtension; + Vulkan_StorageFeaturesHook& operator=( const Vulkan_StorageFeaturesHook& ) = delete; + Vulkan_StorageFeaturesHook( const Vulkan_StorageFeaturesHook& ) = delete; + explicit Vulkan_StorageFeaturesHook( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) : tBase( std::string(), status ), m_DeviceCreateInfoHook(this), m_GetPhysicalDeviceFeaturesHook(this), m_VulkanDeviceFeaturePrintHook(this) + {} + VkPhysicalDevice16BitStorageFeatures AvailableFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR }; + + private: + friend class DeviceCreateInfoHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; + friend class GetPhysicalDeviceFeaturesHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; + friend class VulkanDeviceFeaturePrintHook< Vulkan_StorageFeaturesHook, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR>; + DeviceCreateInfoHook m_DeviceCreateInfoHook; + GetPhysicalDeviceFeaturesHook m_GetPhysicalDeviceFeaturesHook; + VulkanDeviceFeaturePrintHook m_VulkanDeviceFeaturePrintHook; + + void Register( Vulkan& vulkan ) override + { + vulkan.AddExtensionHooks( &m_DeviceCreateInfoHook, &m_GetPhysicalDeviceFeaturesHook, &m_VulkanDeviceFeaturePrintHook ); + } + virtual void PrintFeatures() const; + virtual void PopulateRequestedFeatures() { RequestedFeatures = AvailableFeatures; } + VkPhysicalDevice16BitStorageFeatures RequestedFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR }; + }; + +}; // namespace diff --git a/framework/code/vulkan/framebuffer.cpp b/framework/code/vulkan/framebuffer.cpp new file mode 100644 index 0000000..da539db --- /dev/null +++ b/framework/code/vulkan/framebuffer.cpp @@ -0,0 +1,185 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#include "framebuffer.hpp" +#include "vulkan.hpp" +#include "system/os_common.h" +#include "texture/vulkan/texture.hpp" + + +//----------------------------------------------------------------------------- +Framebuffer::Framebuffer() +//----------------------------------------------------------------------------- +{ + // class is fully initialized by member constructors and member value initilization in the class definition +} + +//----------------------------------------------------------------------------- +Framebuffer& Framebuffer::operator=( const Framebuffer& other ) +//----------------------------------------------------------------------------- +{ + if (&other != this) + { + this->m_FrameBuffer = other.m_FrameBuffer; + this->m_Name = other.m_Name; + this->m_RenderPassClearData = other.m_RenderPassClearData.Copy(); + } + return *this; +} + +//----------------------------------------------------------------------------- +Framebuffer::Framebuffer( const Framebuffer& other) +//----------------------------------------------------------------------------- +{ + *this = other; +} + +//----------------------------------------------------------------------------- +bool Framebuffer::Initialize( Vulkan& vulkan, const RenderPass& renderPass, const std::span ColorAttachments, const Texture* pDepthAttachment, std::string name, const std::span ResolveAttachments, const Texture* pVRSAttachment ) +//----------------------------------------------------------------------------- +{ + VkResult RetVal; + + if (ColorAttachments.empty() && !pDepthAttachment) + { + assert( 0 && "Expect to have at least one color and/or a depth in a framebuffer" ); + return false; + } + + // check/get dimensions from color and/or depth (we assume/require they all match; change code if we need this to not be the case) + uint32_t width = ColorAttachments.empty() ? pDepthAttachment->Width : ColorAttachments.front().Width; + uint32_t height = ColorAttachments.empty() ? pDepthAttachment->Height : ColorAttachments.front().Height; + for (const auto& a: ColorAttachments) + { + if (a.Width != width || a.Height != height) + { + assert( 0 && "all color and depth buffers in a framebuffer should be same dimensions" ); + return false; + } + } + if (!m_RenderPassClearData.Initialize( ColorAttachments, pDepthAttachment )) + { + return false; + } + + std::vector attachments; + attachments.reserve( ColorAttachments.size() + 1/*depth*/ ); + + for (const auto& ColorAttachement: ColorAttachments) + { + attachments.push_back(ColorAttachement.GetVkImageView()); + } + if (pDepthAttachment && pDepthAttachment->Format != TextureFormat::UNDEFINED) + { + if (pDepthAttachment->Width != width || pDepthAttachment->Height != height) + { + assert( 0 && "all color and depth buffers in a framebuffer should be same dimensions" ); + return false; + } + attachments.push_back(pDepthAttachment->GetVkImageView()); + } + if (attachments.empty()) + { + assert( 0 && "Framebuffer must have color and/or depth buffer(s)" ); + return false; + } + for (const auto& ResolveAttachment : ResolveAttachments) + { + attachments.push_back( ResolveAttachment.GetVkImageView() ); + } + if (pVRSAttachment) + { + attachments.push_back( pVRSAttachment->GetVkImageView() ); + } + + fvk::VkFramebufferCreateInfo BufferInfo{{ + .flags = 0, + .renderPass = renderPass.mRenderPass, + .attachmentCount = (uint32_t)attachments.size(), + .pAttachments = attachments.data(), + .width = width, + .height = height, + .layers = 1 + }}; + + VkFramebuffer vkFramebuffer = VK_NULL_HANDLE; + RetVal = vkCreateFramebuffer(vulkan.m_VulkanDevice, &BufferInfo, NULL, &vkFramebuffer); + if (!CheckVkError("vkCreateFramebuffer()", RetVal)) + { + return false; + } + m_Name = std::move(name); + vulkan.SetDebugObjectName( vkFramebuffer, m_Name.c_str() ); + m_FrameBuffer = {vulkan.m_VulkanDevice, vkFramebuffer}; + + return true; +} + +//----------------------------------------------------------------------------- +void Framebuffer::Release() +//----------------------------------------------------------------------------- +{ + m_FrameBuffer = {}; +} + +//----------------------------------------------------------------------------- +bool RenderPassClearData::Initialize( const std::span ColorAttachments, + const Texture* pDepthAttachment ) +//----------------------------------------------------------------------------- +{ + if (ColorAttachments.empty() && !pDepthAttachment) + { + assert( 0 && "Expect to have at least one color and/or a depth in a framebuffer" ); + return false; + } + // check/get dimensions from color and/or depth (we assume/require they all match; change code if we need this to not be the case) + uint32_t width = ColorAttachments.empty() ? pDepthAttachment->Width : ColorAttachments.front().Width; + uint32_t height = ColorAttachments.empty() ? pDepthAttachment->Height : ColorAttachments.front().Height; + for (const auto& a : ColorAttachments) + { + if (a.Width != width || a.Height != height) + { + assert( 0 && "all color and depth buffers in a renderpass should be same dimensions" ); + return false; + } + } + + std::vector clearValues; + clearValues.reserve( ColorAttachments.size() + 1/*potentially depth*/ ); + + for (const auto& ColorAttachement : ColorAttachments) + { + clearValues.push_back( {ColorAttachement.GetVkClearValue()}); + } + if (pDepthAttachment && pDepthAttachment->Format != TextureFormat::UNDEFINED) + { + if (pDepthAttachment->Width != width || pDepthAttachment->Height != height) + { + assert( 0 && "all color and depth buffers in a renderpass should be same dimensions" ); + return false; + } + clearValues.push_back( pDepthAttachment->GetVkClearValue() ); + } + if (clearValues.empty()) + { + assert( 0 && "RenderpassClearData must have color and/or depth buffer(s)" ); + return false; + } + this->clearValues = std::move( clearValues ); + + // Viewport/scissor is set to defaults (up to end user to change if needed) + this->scissor = {.extent {.width = width, .height = height}}; + this->viewport = { + .width = (float)width, + .height = (float)height, + .minDepth = 0.0f, // sensible defaults but application may wish to override + .maxDepth = 1.0f // sensible defaults but application may wish to override + }; + + return true; +} diff --git a/framework/code/vulkan/framebuffer.hpp b/framework/code/vulkan/framebuffer.hpp new file mode 100644 index 0000000..ea4461d --- /dev/null +++ b/framework/code/vulkan/framebuffer.hpp @@ -0,0 +1,122 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include +#include "refHandle.hpp" + +// Forward declarations +template class RenderPass; +template class Texture; +class Vulkan; + + +/// Simple wrapper around VkFramebuffer (no DirectX equivalent). +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// This template class expected to be specialized (if this template throws compiler errors then the code is not using the specialization classes which is an issue!) +/// @ingroup Material +template +class Framebuffer +{ + Framebuffer& operator=( const Framebuffer& ) = delete; + Framebuffer( const Framebuffer& ) = delete; +public: + Framebuffer() noexcept = delete; + Framebuffer( Framebuffer&& ) noexcept = delete; + ~Framebuffer() = delete; + + static_assert(sizeof( Framebuffer ) >= 1); // Ensure this class template is specialized (and not used as-is) +}; + + +//============================================================================= +// RenderpassClearData +//============================================================================= +class RenderPassClearData +{ + RenderPassClearData( const RenderPassClearData& ) = default; // private but usable from Copy() + RenderPassClearData& operator=( const RenderPassClearData& ) = default; +public: + using Texture = Texture; + RenderPassClearData() noexcept = default; + RenderPassClearData( VkRect2D _scissor, VkViewport _viewport, std::vector _clearValues ) + : scissor{_scissor} + , viewport{_viewport} + , clearValues{std::move( _clearValues )} + { + } + + RenderPassClearData( RenderPassClearData&& ) noexcept = default; + RenderPassClearData& operator=( RenderPassClearData&& ) = default; + + bool Initialize( const std::span ColorAttachments, + const Texture* pDepthAttachment ); + + RenderPassClearData Copy() const { + return *this; // use the 'private' copy operator + } + + VkRect2D scissor {}; + VkViewport viewport {}; + std::vector clearValues; +}; + + +//============================================================================= +// Framebuffer +//============================================================================= + +/// Container for a framebuffer. +template<> +class Framebuffer final +{ +public: + using Texture = Texture; + using RenderPass = RenderPass; + using RefVkFramebuffer = RefHandle; + Framebuffer(); + ~Framebuffer() = default; + Framebuffer( const Framebuffer& ); + Framebuffer& operator=( const Framebuffer& ); + Framebuffer( Framebuffer&& ) noexcept = default; + Framebuffer& operator=( Framebuffer&& ) noexcept = default; + Framebuffer(RefVkFramebuffer framebuffer, VkRect2D renderArea, VkViewport viewport, std::vector clearValues, std::string name) noexcept + : m_FrameBuffer( std::move(framebuffer)) + , m_Name(std::move(name)) + , m_RenderPassClearData{renderArea, viewport, std::move(clearValues)} + {} + Framebuffer( RefVkFramebuffer framebuffer, RenderPassClearData clearData, std::string name ) noexcept + : m_FrameBuffer( std::move( framebuffer ) ) + , m_Name( std::move( name ) ) + , m_RenderPassClearData{std::move( clearData )} + {} + operator bool() const { return !!m_FrameBuffer; } + + /// @brief Initialize the framebuffer with (potentially) multiple color buffers (including depth and resolve buffers where applicable +// bool Initialize( Vulkan* pVulkan, const RenderTargetInitializeInfo& info, const char* pName ); + bool Initialize( Vulkan& vulkan, + const RenderPass& renderPass, + const std::span ColorAttachments, + const Texture* pDepthAttachment, + std::string name, + const std::span ResolveAttachments = {}, + const Texture* pVRSAttachment = nullptr); + void Release(); + operator VkFramebuffer() const { return m_FrameBuffer; } + const auto& GetRenderPassClearData() const { return m_RenderPassClearData; } ///< default clear data and framebuffer extents + + // Attributes +public: + RefVkFramebuffer m_FrameBuffer{}; +protected: + std::string m_Name; + RenderPassClearData m_RenderPassClearData; +}; diff --git a/framework/code/vulkan/refHandle.hpp b/framework/code/vulkan/refHandle.hpp new file mode 100644 index 0000000..ac22392 --- /dev/null +++ b/framework/code/vulkan/refHandle.hpp @@ -0,0 +1,125 @@ +//============================================================================================================ +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================================================ +#pragma once + +#include +#include +#include +#include "refHandleDestroyFuncs.hpp" + +/// +/// Reference counted handle to Vulkan object. +/// Use for sharing VkHandle across objects (and allowing this container to do destruction). +/// There is no DX12 equivalent because their objects are already ref counted with ComPtr. +/// +/// CANNOT be used by multiple threads +/// + +template +class RefHandle +{ +public: + RefHandle() noexcept + {} + + RefHandle(VkDevice _device, T _handle) noexcept + : handle(_handle) + , device(_device) + { + if (handle) + m_shared_ref_count = new std::atomic(1); + } + + RefHandle(const RefHandle& other) noexcept + : handle(other.handle) + , device(other.device) + , m_shared_ref_count(other.m_shared_ref_count) + { + if (m_shared_ref_count) + { + auto old_ref_count = m_shared_ref_count->fetch_add(1); + assert(old_ref_count > 0); + } + } + + RefHandle& operator=(const RefHandle& other) noexcept + { + if (this != &other) + { + free(); + if (other.m_shared_ref_count) + { + auto old_ref_count = other.m_shared_ref_count->fetch_add(1); + if (old_ref_count > 0) + { + handle = other.handle; + device = other.device; + m_shared_ref_count = other.m_shared_ref_count; + } + } + } + return *this; + } + + ~RefHandle() noexcept + { + free(); + } + + RefHandle(RefHandle&& other) noexcept + : handle(other.handle) + , device(other.device) + , m_shared_ref_count(other.m_shared_ref_count) + { + other.handle = VK_NULL_HANDLE; + other.device = VK_NULL_HANDLE; + other.m_shared_ref_count = nullptr; + } + + RefHandle& operator=(RefHandle&& other) noexcept + { + if (&other != this) + { + free(); + std::swap(handle, other.handle); + std::swap(device, other.device); + std::swap(m_shared_ref_count, other.m_shared_ref_count); + } + return *this; + } + + operator T() const { return get(); } + T get() const { return handle; } + VkDevice getDevice() const { return device; } + + T handle = {}; + +private: + VkDevice device = VK_NULL_HANDLE; + + void free() + { + if (m_shared_ref_count) + { + auto old_count = m_shared_ref_count->fetch_add(-1); + m_shared_ref_count = nullptr; + if (old_count == 1) + { + VulkanTraits::DestroyFn::Call(device, handle, nullptr); + } + handle = VK_NULL_HANDLE; + device = VK_NULL_HANDLE; + } + else + { + assert(handle == VK_NULL_HANDLE); + } + } + + std::atomic* m_shared_ref_count = nullptr; + +}; diff --git a/framework/code/vulkan/refHandleDestroyFuncs.hpp b/framework/code/vulkan/refHandleDestroyFuncs.hpp new file mode 100644 index 0000000..0e97721 --- /dev/null +++ b/framework/code/vulkan/refHandleDestroyFuncs.hpp @@ -0,0 +1,164 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + + +#include +#include +#include + +namespace VulkanTraits +{ + template + struct DestroyFn; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator) + { + vkDestroyFramebuffer(device, framebuffer, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator) + { + vkDestroyImageView(device, imageView, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) + { + vkDestroyImage(device, image, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator) + { + vkDestroyBuffer(device, buffer, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator) + { + vkDestroyShaderModule(device, shaderModule, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator) + { + vkDestroyPipeline(device, pipeline, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkPipelineLayout layout, const VkAllocationCallbacks* pAllocator) + { + vkDestroyPipelineLayout(device, layout, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator) + { + vkDestroyRenderPass(device, renderPass, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkDescriptorSetLayout layout, const VkAllocationCallbacks* pAllocator) + { + vkDestroyDescriptorSetLayout(device, layout, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkDescriptorPool pool, const VkAllocationCallbacks* pAllocator) + { + vkDestroyDescriptorPool(device, pool, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator) + { + vkDestroySampler(device, sampler, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) + { + vkFreeMemory(device, memory, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator) + { + vkDestroyFence(device, fence, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator) + { + vkDestroySemaphore(device, semaphore, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator) + { + vkDestroyCommandPool(device, commandPool, pAllocator); + } + }; + + template<> + struct DestroyFn + { + static void Call(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) + { + vkDestroySwapchainKHR(device, swapchain, pAllocator); + } + }; + +} // VulkanTraits \ No newline at end of file diff --git a/framework/code/vulkan/renderContext.cpp b/framework/code/vulkan/renderContext.cpp new file mode 100644 index 0000000..f3c0668 --- /dev/null +++ b/framework/code/vulkan/renderContext.cpp @@ -0,0 +1,355 @@ +#include "renderContext.hpp" +#include "allocator/threadBufferResource.hpp" + +namespace +{ + // TODO: Make the same function from vulkan.cpp public accessible and replace this with it + static constexpr VkAttachmentLoadOp InputUsageToVkAttachmentLoadOp(const RenderPassInputUsage t) + { + constexpr VkAttachmentLoadOp cRenderPassInputUsageToVk[]{ + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_DONT_CARE + }; + return cRenderPassInputUsageToVk[(int)t]; + } +} + +//----------------------------------------------------------------------------- +VkRenderingAttachmentInfo RenderingAttachmentInfo::ToVkAttachmentInfo() const +//----------------------------------------------------------------------------- +{ + VkRenderingAttachmentInfo attachmentInfo = renderingAttachmentInfo; + attachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + // Color attachment + if (std::holds_alternative(v)) + { + const auto& info = std::get(v); + attachmentInfo.loadOp = InputUsageToVkAttachmentLoadOp(info.colorInputUsage); + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + switch (info.colorOutputUsage) { + case RenderPassOutputUsage::Discard: + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + break; + case RenderPassOutputUsage::Store: + case RenderPassOutputUsage::StoreReadOnly: + case RenderPassOutputUsage::StoreTransferSrc: + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + break; + case RenderPassOutputUsage::Clear: + assert(0); // currently unsupported + break; + case RenderPassOutputUsage::Present: + break; + } + } + // Depth attachment + else if (std::holds_alternative(v)) + { + const auto& info = std::get(v); + attachmentInfo.loadOp = InputUsageToVkAttachmentLoadOp(info.clearDepth ? RenderPassInputUsage::Clear : RenderPassInputUsage::Load); + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + switch (info.depthOutputUsage) { + case RenderPassOutputUsage::Discard: + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + break; + case RenderPassOutputUsage::Store: + case RenderPassOutputUsage::StoreReadOnly: + case RenderPassOutputUsage::StoreTransferSrc: + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + break; + case RenderPassOutputUsage::Clear: + assert(0); // currently unsupported + break; + case RenderPassOutputUsage::Present: + break; + } + } + // Not-specified, go with default values (might be expensive and not work well with local load/read) + else + { + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + if (renderingAttachmentInfo.imageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + { + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + else if (renderingAttachmentInfo.imageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + || renderingAttachmentInfo.imageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) + { + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + else + { + // Cannot decide if color or depth + assert(false); + } + } + + // Rendering local read override (check for original layout) + // Note: From the spec, VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR can be used as color/depth + // attachment, storage and input operations (barriers are need to ensure memory visibility) + if (renderingAttachmentInfo.imageLayout == VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) + { + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR; + } + + attachmentInfo.clearValue = clearValue.value_or(attachmentInfo.clearValue); + + return attachmentInfo; +} + +//----------------------------------------------------------------------------- +std::tuple, std::optional> RenderingAttachmentInfoGroup::ToVkAttachmentInfos() const +//----------------------------------------------------------------------------- +{ + core::ThreadAutomaticMonotonicMemoryResource threadMemoryResource; + std::pmr::vector colorInfos(threadMemoryResource); + colorInfos.reserve(colorCount); + + for (uint32_t i = 0; i < colorCount; ++i) + { + colorInfos.push_back(colorAttachments[i].ToVkAttachmentInfo()); + } + + std::optional depth_info; + if (depthAttachment.has_value()) + { + depth_info = depthAttachment->ToVkAttachmentInfo(); + } + + return { std::move(colorInfos), depth_info }; +} + +//----------------------------------------------------------------------------- +VkRect2D RenderingAttachmentInfoGroup::GetRenderArea() const +//----------------------------------------------------------------------------- +{ + VkRect2D renderArea = VkRect2D{ {0, 0}, {0, 0} }; + + for (int i = 0; i < colorCount; i++) + { + renderArea.extent.width = std::max(renderArea.extent.width, colorAttachments[i].renderArea.width); + renderArea.extent.height = std::max(renderArea.extent.height, colorAttachments[i].renderArea.height); + } + + if (depthAttachment) + { + renderArea.extent.width = std::max(renderArea.extent.width, depthAttachment->renderArea.width); + renderArea.extent.height = std::max(renderArea.extent.height, depthAttachment->renderArea.height); + } + + return renderArea; +} + +//----------------------------------------------------------------------------- +RenderContext::RenderContext( RenderContext&& other ) noexcept = default; +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +RenderContext& RenderContext::operator=( RenderContext&& other ) noexcept +//----------------------------------------------------------------------------- +{ + if (this != &other) + { + v = std::move( other.v ); + + viewMask = other.viewMask; + other.viewMask = {}; + subPass = other.subPass; + other.subPass = 0; + msaa = other.msaa; + other.msaa = Msaa::Samples1; + name = std::move( other.name ); + } + + return *this; +} + +//----------------------------------------------------------------------------- +RenderContext::RenderPassContextData& RenderContext::RenderPassContextData::operator=( RenderContext::RenderPassContextData&& other ) noexcept +//----------------------------------------------------------------------------- +{ + if (this != &other) + { + renderPass = std::move( other.renderPass ); + overridePipeline = std::move( other.overridePipeline); + framebuffer = std::move( other.framebuffer ); + renderPassClearData = std::move( other.renderPassClearData); + } + return *this; +} + +//----------------------------------------------------------------------------- +RenderContext::RenderContext( RenderPass _renderPass, Pipeline _pipeline, Framebuffer _framebuffer, std::string _name ) noexcept +//----------------------------------------------------------------------------- + : v{std::move( RenderPassContextData { + std::move( _renderPass ), std::move( _pipeline ), std::move( _framebuffer ), _framebuffer.GetRenderPassClearData().Copy() + } )} + , name{std::move( _name )} +{ +} + +//----------------------------------------------------------------------------- +RenderContext::RenderContext( RenderPass _renderPass, Framebuffer _framebuffer, std::string _name ) noexcept +//----------------------------------------------------------------------------- + : v{std::move( RenderPassContextData { + std::move( _renderPass ), Pipeline(), std::move(_framebuffer), _framebuffer.GetRenderPassClearData().Copy() + } )} + , name{std::move( _name )} +{ +} + +//----------------------------------------------------------------------------- +RenderContext::RenderContext(std::span _colorAttachmentFormats, TextureFormat _depthAttachmentFormat, TextureFormat _stencilAttachmentFormat, std::string _name ) noexcept +//----------------------------------------------------------------------------- + : v{std::move(DynamicRenderContextData { + [] (std::span formats) -> std::vector { + std::vector vkFormats; + vkFormats.reserve(formats.size()); + for (const auto& format : formats) + { + vkFormats.push_back(TextureFormatToVk(format)); + } + return vkFormats; + }(_colorAttachmentFormats), + TextureFormatToVk(_depthAttachmentFormat), + TextureFormatToVk(_stencilAttachmentFormat) + } )} + , name{std::move(_name)} +{ +} + + +//----------------------------------------------------------------------------- +RenderContext::RenderPassContextData::RenderPassContextData( RenderPass _renderPass, Pipeline _pipeline, Framebuffer _framebuffer, RenderPassClearData _renderPassClearData) noexcept + : renderPass{std::move( _renderPass )} + , overridePipeline{std::move( _pipeline )} + , framebuffer{std::move( _framebuffer )} + , renderPassClearData{std::move(_renderPassClearData)} +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +fvk::VkRenderPassBeginInfo RenderContext::GetRenderPassBeginInfo() const +//----------------------------------------------------------------------------- +{ + const RenderPassContextData& context = std::get( v ); + fvk::VkRenderPassBeginInfo renderPassBeginInfo{VkRenderPassBeginInfo{ + .renderPass = context.renderPass.mRenderPass, + .framebuffer = context.framebuffer, + .renderArea = context.renderPassClearData.scissor, + }}; + auto infoClearValues = renderPassBeginInfo.AddMemberArray(context.renderPassClearData.clearValues.size()); + std::copy( context.renderPassClearData.clearValues.begin(), context.renderPassClearData.clearValues.end(), infoClearValues.begin() ); + + return renderPassBeginInfo; +} + +//----------------------------------------------------------------------------- +fvk::VkRenderingInfo RenderContext::GetRenderingInfo(const RenderingAttachmentInfoGroup& renderingAttachmentInfoGroup, std::optional< VkRect2D> renderArea) const +//----------------------------------------------------------------------------- +{ + auto [colorAttachmentInfo, depthAttachmentInfo] = renderingAttachmentInfoGroup.ToVkAttachmentInfos(); + + const DynamicRenderContextData& context = std::get( v ); + fvk::VkRenderingInfo renderingInfo{VkRenderingInfo{ + .renderArea = renderArea ? renderArea.value() : renderingAttachmentInfoGroup.GetRenderArea(), + .layerCount = 1, + .pStencilAttachment = VK_NULL_HANDLE, + }}; + + auto colorAttachmentValues = renderingInfo.AddMemberArray(colorAttachmentInfo.size()); + std::copy(colorAttachmentInfo.begin(), colorAttachmentInfo.end(), colorAttachmentValues.begin()); + + if (depthAttachmentInfo) + { + auto depthAttachmentValue = renderingInfo.AddMemberArray(1); + depthAttachmentValue[0] = depthAttachmentInfo.value(); + } + + return renderingInfo; +} + +//----------------------------------------------------------------------------- +Pipeline RenderContext::GetOverridePipeline() const +//----------------------------------------------------------------------------- +{ + if (!IsDynamic()) + { + return std::get( v ).overridePipeline.Copy(); + } + else + return {}; +} + +//----------------------------------------------------------------------------- +RenderPassClearData RenderContext::GetRenderPassClearData() const +//----------------------------------------------------------------------------- +{ + if (!IsDynamic()) + { + return std::get( v ).renderPassClearData.Copy(); + } + else + return {}; +} + +//----------------------------------------------------------------------------- +RenderPass RenderContext::GetRenderPass() const +//----------------------------------------------------------------------------- +{ + if (!IsDynamic()) + { + return std::get( v ).renderPass.Copy(); + } + else + return {}; +} + +//----------------------------------------------------------------------------- +const Framebuffer* RenderContext::GetFramebuffer() const +//----------------------------------------------------------------------------- +{ + if (!IsDynamic()) + { + return &std::get( v ).framebuffer; + } + else + return nullptr; +} + +//----------------------------------------------------------------------------- +size_t RenderContext::GetNumColorAttachmentFormats() const +//----------------------------------------------------------------------------- +{ + if (IsDynamic()) + { + return std::get(v).colorAttachmentFormats.size(); + } + else + return 0; +} + +//----------------------------------------------------------------------------- +fvk::VkPipelineRenderingCreateInfo RenderContext::GetPipelineRenderingCreateInfo() const +//----------------------------------------------------------------------------- +{ + const DynamicRenderContextData& context = std::get( v ); + fvk::VkPipelineRenderingCreateInfo info{VkPipelineRenderingCreateInfo{ + .viewMask = viewMask, + .depthAttachmentFormat = context.depthAttachmentFormat, + .stencilAttachmentFormat = context.stencilAttachmentFormat + }}; + auto infoColorAttachments = info.AddMemberArray( context.colorAttachmentFormats.size() ); + std::copy( context.colorAttachmentFormats.begin(), context.colorAttachmentFormats.end(), infoColorAttachments.begin() ); + + return info; +} + diff --git a/framework/code/vulkan/renderContext.hpp b/framework/code/vulkan/renderContext.hpp new file mode 100644 index 0000000..2c29b79 --- /dev/null +++ b/framework/code/vulkan/renderContext.hpp @@ -0,0 +1,348 @@ +//============================================================================================================ +// +// +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include +#include +#include "graphicsApi/renderContext.hpp" +#include "material/vulkan/pipeline.hpp" //TODO: move or refactor +#include "vulkan.hpp" +#include "framebuffer.hpp" +#include "renderPass.hpp" +#include "vulkan/renderTarget.hpp" +#include "texture/texture.hpp" +#include "texture/textureFormat.hpp" // for msaa +#include "texture/vulkan/texture.hpp" + +// Forward declarations +class Vulkan; + +struct RenderingAttachmentInfo +{ + VkRenderingAttachmentInfo renderingAttachmentInfo{ VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO }; + std::optional clearValue; + VkExtent2D renderArea = {0, 0}; + + // this + struct ColorInfo + { + RenderPassInputUsage colorInputUsage = RenderPassInputUsage::Load; + RenderPassOutputUsage colorOutputUsage = RenderPassOutputUsage::Store; + }; + + // or this + struct DepthInfo + { + bool clearDepth = false; + RenderPassOutputUsage depthOutputUsage = RenderPassOutputUsage::Store; + }; + + std::variant v; + + RenderingAttachmentInfo() = default; + inline RenderingAttachmentInfo( + const Texture& _texture, + std::optional _clear = std::nullopt) + : clearValue(_clear) + , renderArea({ _texture.Width , _texture.Height }) + { + renderingAttachmentInfo.imageView = _texture.GetVkImageView(); + renderingAttachmentInfo.imageLayout = _texture.GetVkImageLayout(); + renderingAttachmentInfo.clearValue = clearValue.value_or(_texture.GetVkClearValue()); + } + + static RenderingAttachmentInfo Color( + const Texture& _texture, + RenderPassInputUsage _inputUsage, + RenderPassOutputUsage _outputUsage, + std::optional _clear = std::nullopt) + { + RenderingAttachmentInfo renderingAttachmentInfo(_texture, _clear); + renderingAttachmentInfo.v = ColorInfo{ _inputUsage , _outputUsage }; + return renderingAttachmentInfo; + } + + static RenderingAttachmentInfo Depth( + const Texture& _texture, + bool _clearDepth, + RenderPassOutputUsage _outputUsage, + std::optional _clear = std::nullopt) + { + RenderingAttachmentInfo renderingAttachmentInfo(_texture, _clear); + renderingAttachmentInfo.v = DepthInfo{ _clearDepth , _outputUsage }; + return renderingAttachmentInfo; + } + + /* + * Converts this struct into a Vulkan VkRenderingAttachmentInfo. + */ + VkRenderingAttachmentInfo ToVkAttachmentInfo() const; +}; + +struct RenderingAttachmentInfoGroup +{ + std::array colorAttachments; + uint32_t colorCount = 0; + std::optional depthAttachment; + + /* + * Constructs a RenderingAttachmentInfoGroup from a list of color attachments, an optional depth attachment, and an optional clear value override. + */ + inline RenderingAttachmentInfoGroup( + std::initializer_list colors, + std::optional depth = std::nullopt, + std::optional colorInputUsage = std::nullopt, + std::optional colorOutputUsage = std::nullopt, + std::optional clearDepth = std::nullopt, + std::optional depthOutputUsage = std::nullopt, + std::optional clearOverride = std::nullopt) + : colorCount(static_cast(colors.size())) + , depthAttachment(depth) + { + assert(colorCount <= colorAttachments.size()); + + std::copy(colors.begin(), colors.end(), colorAttachments.begin()); + + if (clearOverride.has_value()) ApplyClearValueToAll(clearOverride.value()); + + if (colorInputUsage || colorOutputUsage) + { + for (int i = 0; i < colorCount; i++) + { + colorAttachments[i].v = RenderingAttachmentInfo::ColorInfo(); + + if(colorInputUsage) std::get(colorAttachments[i].v).colorInputUsage = colorInputUsage.value(); + if(colorOutputUsage) std::get(colorAttachments[i].v).colorOutputUsage = colorOutputUsage.value(); + } + } + + if (depth && (clearDepth || depthOutputUsage)) + { + depthAttachment = RenderingAttachmentInfo(*depth); + depthAttachment->v = RenderingAttachmentInfo::DepthInfo(); + if (clearDepth && depthAttachment) std::get(depthAttachment->v).clearDepth = clearDepth.value(); + if (depthOutputUsage && depthAttachment) std::get(depthAttachment->v).depthOutputUsage = depthOutputUsage.value(); + } + } + + /* + * Constructs a RenderingAttachmentInfoGroup directly from Texture objects. + */ + inline RenderingAttachmentInfoGroup( + std::initializer_list> textures, + Texture* depth = nullptr, + std::optional colorInputUsage = std::nullopt, + std::optional colorOutputUsage = std::nullopt, + std::optional clearDepth = std::nullopt, + std::optional depthOutputUsage = std::nullopt, + std::optional clearOverride = std::nullopt) + { + assert(textures.size() <= colorAttachments.size()); + + for (const auto& tex : textures) colorAttachments[colorCount++] = RenderingAttachmentInfo(tex); + + if (clearOverride.has_value()) ApplyClearValueToAll(clearOverride.value()); + + if (colorInputUsage || colorOutputUsage) + { + for (int i = 0; i < colorCount; i++) + { + colorAttachments[i].v = RenderingAttachmentInfo::ColorInfo(); + + if(colorInputUsage) std::get(colorAttachments[i].v).colorInputUsage = colorInputUsage.value(); + if(colorOutputUsage) std::get(colorAttachments[i].v).colorOutputUsage = colorOutputUsage.value(); + } + } + + if (depth && (clearDepth || depthOutputUsage)) + { + depthAttachment = RenderingAttachmentInfo(*depth); + depthAttachment->v = RenderingAttachmentInfo::DepthInfo(); + if (clearDepth && depthAttachment) std::get(depthAttachment->v).clearDepth = clearDepth.value(); + if (depthOutputUsage && depthAttachment) std::get(depthAttachment->v).depthOutputUsage = depthOutputUsage.value(); + } + } + + /* + * Constructs a RenderingAttachmentInfoGroup directly from a span of Texture objects. + */ + inline RenderingAttachmentInfoGroup( + std::span> textures, + Texture* depth = nullptr, + std::optional colorInputUsage = std::nullopt, + std::optional colorOutputUsage = std::nullopt, + std::optional clearDepth = std::nullopt, + std::optional depthOutputUsage = std::nullopt, + std::optional clearOverride = std::nullopt) + { + assert(textures.size() <= colorAttachments.size()); + + for (const auto& tex : textures) {RenderingAttachmentInfo info(tex); colorAttachments[colorCount++] = info;} + + if (clearOverride.has_value()) ApplyClearValueToAll(clearOverride.value()); + + if (colorInputUsage || colorOutputUsage) + { + for (int i = 0; i < colorCount; i++) + { + colorAttachments[i].v = RenderingAttachmentInfo::ColorInfo(); + + if(colorInputUsage) std::get(colorAttachments[i].v).colorInputUsage = colorInputUsage.value(); + if(colorOutputUsage) std::get(colorAttachments[i].v).colorOutputUsage = colorOutputUsage.value(); + } + } + + if (depth && (clearDepth || depthOutputUsage)) + { + depthAttachment = RenderingAttachmentInfo(*depth); + depthAttachment->v = RenderingAttachmentInfo::DepthInfo(); + if (clearDepth && depthAttachment) std::get(depthAttachment->v).clearDepth = clearDepth.value(); + if (depthOutputUsage && depthAttachment) std::get(depthAttachment->v).depthOutputUsage = depthOutputUsage.value(); + } + } + + /* + * Constructs a RenderingAttachmentInfoGroup directly from a vulkan render target. + */ + inline RenderingAttachmentInfoGroup( + const RenderTarget& renderTarget, + std::optional colorInputUsage = std::nullopt, + std::optional colorOutputUsage = std::nullopt, + std::optional clearDepth = std::nullopt, + std::optional depthOutputUsage = std::nullopt, + std::optional clearOverride = std::nullopt) + { + assert(renderTarget.GetColorAttachments().size() <= colorAttachments.size()); + + for (const auto& attachment : renderTarget.GetColorAttachments()) {colorAttachments[colorCount++] = RenderingAttachmentInfo(attachment);} + + if (clearOverride.has_value()) ApplyClearValueToAll(clearOverride.value()); + + if (colorInputUsage || colorOutputUsage) + { + for (int i = 0; i < colorCount; i++) + { + colorAttachments[i].v = RenderingAttachmentInfo::ColorInfo(); + + if(colorInputUsage) std::get(colorAttachments[i].v).colorInputUsage = colorInputUsage.value(); + if(colorOutputUsage) std::get(colorAttachments[i].v).colorOutputUsage = colorOutputUsage.value(); + } + } + + if (!renderTarget.GetDepthAttachment().IsEmpty() && (clearDepth || depthOutputUsage)) + { + depthAttachment = RenderingAttachmentInfo(renderTarget.GetDepthAttachment()); + depthAttachment->v = RenderingAttachmentInfo::DepthInfo(); + if (clearDepth && depthAttachment) std::get(depthAttachment->v).clearDepth = clearDepth.value(); + if (depthOutputUsage && depthAttachment) std::get(depthAttachment->v).depthOutputUsage = depthOutputUsage.value(); + } + } + + inline bool AddColorAttachment(const RenderingAttachmentInfo& attachment) + { + if (colorCount >= colorAttachments.size()) + { + return false; + } + colorAttachments[colorCount++] = attachment; + return true; + } + + inline void SetDepthAttachment(const RenderingAttachmentInfo& attachment) + { + depthAttachment = attachment; + } + + inline void ApplyClearValueToAll(const VkClearValue& clear_value) + { + for (uint32_t i = 0; i < colorCount; ++i) + { + colorAttachments[i].clearValue = clear_value; + } + + if (depthAttachment.has_value()) + { + depthAttachment->clearValue = clear_value; + } + } + + std::tuple, std::optional> ToVkAttachmentInfos() const; + + VkRect2D GetRenderArea() const; +}; + +/// Context for a single dynamic rendering context +template<> +class RenderContext final +{ + using Texture = Texture; + + RenderContext( const RenderContext& ) noexcept = delete; + RenderContext& operator=( const RenderContext& ) noexcept = delete; +public: + RenderContext(RenderContext&&) noexcept; + RenderContext& operator=(RenderContext&&) noexcept; + + RenderContext() noexcept = default; + RenderContext(RenderPass, Pipeline, Framebuffer, std::string name) noexcept; + RenderContext(RenderPass, Framebuffer, std::string name) noexcept; + RenderContext(std::span, TextureFormat, TextureFormat, std::string name) noexcept; + + // this + struct RenderPassContextData { + RenderPassContextData( RenderPass, Pipeline, Framebuffer, RenderPassClearData) noexcept; + RenderPassContextData& operator=( RenderPassContextData&& other ) noexcept; + RenderPassContextData( RenderPassContextData&& other ) noexcept = default; + + RenderPass renderPass{}; + Pipeline overridePipeline{}; + Framebuffer framebuffer{}; + RenderPassClearData renderPassClearData{}; + }; + + uint32_t viewMask = 0; + uint32_t subPass = 0; + Msaa msaa = Msaa::Samples1; + + // or this + struct DynamicRenderContextData { + std::vector colorAttachmentFormats; + VkFormat depthAttachmentFormat = VK_FORMAT_UNDEFINED; + VkFormat stencilAttachmentFormat = VK_FORMAT_UNDEFINED; + }; + + std::variant v; + + bool IsDynamic() const { return std::holds_alternative(v); } + fvk::VkRenderPassBeginInfo GetRenderPassBeginInfo() const; // render pass rendering + fvk::VkRenderingInfo GetRenderingInfo(const RenderingAttachmentInfoGroup&, std::optional< VkRect2D> = std::nullopt) const; // dynamic rendering + + Pipeline GetOverridePipeline() const; + RenderPass GetRenderPass() const; + const Framebuffer* GetFramebuffer() const; + size_t GetNumColorAttachmentFormats() const; + fvk::VkPipelineRenderingCreateInfo GetPipelineRenderingCreateInfo() const; + RenderPassClearData GetRenderPassClearData() const; + + std::string name {}; +}; + + +// /// Dynamic Rendering Context +// struct RenderingContext +// { +// RenderingContext() = default; +// RenderingContext(const RenderingPassContext& passContext) +// : passContexts({ passContext }) +// { +// } + +// std::vector< VkSampleCountFlagBits> passMultisample; +// std::vector< RenderingPassContext> passContexts; +// }; + diff --git a/framework/code/vulkan/renderPass.cpp b/framework/code/vulkan/renderPass.cpp new file mode 100644 index 0000000..620d1fb --- /dev/null +++ b/framework/code/vulkan/renderPass.cpp @@ -0,0 +1,38 @@ +//============================================================================= +// +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "renderPass.hpp" +#include "texture/textureFormat.hpp" +#include "vulkan/vulkan.hpp" + +// Forward declarations +class Vulkan; + + +RenderPass::RenderPass() noexcept +{ +} + +RenderPass::~RenderPass() +{ +} + +RenderPass::RenderPass(VkDevice device, VkRenderPass renderPass) noexcept + : mRenderPass(device, renderPass) +{ +} + +RenderPass CreateRenderPass( Vulkan& vulkan, std::span ColorFormats, TextureFormat DepthFormat, Msaa Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, RenderPassOutputUsage DepthOutputUsage, std::span < const TextureFormat > ResolveFormats ) +{ + RenderPass renderPass{}; + if (!vulkan.CreateRenderPass(ColorFormats, DepthFormat, Msaa, ColorInputUsage, ColorOutputUsage, ShouldClearDepth, DepthOutputUsage, renderPass, ResolveFormats)) + { + return RenderPass{}; + } + return renderPass; +} diff --git a/framework/code/vulkan/renderPass.hpp b/framework/code/vulkan/renderPass.hpp new file mode 100644 index 0000000..f112183 --- /dev/null +++ b/framework/code/vulkan/renderPass.hpp @@ -0,0 +1,67 @@ +//============================================================================= +// +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include +#include +#include "graphicsApi/graphicsApiBase.hpp" +#include "vulkan/refHandle.hpp" +#include "graphicsApi/renderPass.hpp" +#include + +// Forward declarations +class Vulkan; +enum class Msaa; +enum class TextureFormat; + + +/// Simple wrapper around VkRenderPass. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// Specialization of RenderPass +/// @ingroup Vulkan +template<> +class RenderPass +{ + RenderPass& operator=(const RenderPass&) = delete; +public: + RenderPass() noexcept; + RenderPass( RenderPass&& other ) noexcept = default; + RenderPass& operator=( RenderPass&& other ) noexcept = default; + + RenderPass(VkDevice device, VkRenderPass renderPass) noexcept; + //RenderPass& operator=( VkRenderPass renderPass ) noexcept { + // assert( mRenderPass == VK_NULL_HANDLE ); + // mRenderPass = renderPass; + // return *this; + //} + ~RenderPass(); + operator bool() const { return mRenderPass != VK_NULL_HANDLE; } + + RenderPass Copy() const { return RenderPass{*this}; } + + RefHandle mRenderPass; + +private: + RenderPass( const RenderPass& src ) noexcept { + mRenderPass = src.mRenderPass; + } +}; + + +RenderPass CreateRenderPass( Vulkan& vulkan, + std::span < const TextureFormat > ColorFormats, + TextureFormat DepthFormat, + Msaa Msaa, + RenderPassInputUsage ColorInputUsage, + RenderPassOutputUsage ColorOutputUsage, + bool ShouldClearDepth, + RenderPassOutputUsage DepthOutputUsage, + std::span < const TextureFormat > ResolveFormats); + +//RenderPass CreateRenderPass( Vulkan& vulkan, +// const VkRenderPassCreateInfo& ); diff --git a/framework/code/vulkan/renderTarget.cpp b/framework/code/vulkan/renderTarget.cpp index 1760afe..89a7c75 100644 --- a/framework/code/vulkan/renderTarget.cpp +++ b/framework/code/vulkan/renderTarget.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,37 +9,36 @@ #include "renderTarget.hpp" #include "system/os_common.h" -//============================================================================= -// CRenderTarget -//============================================================================= //----------------------------------------------------------------------------- -CRenderTarget::CRenderTarget() +RenderTarget::RenderTarget() //----------------------------------------------------------------------------- { // class is fully initialized by member constructors and member value initilization in the class definition } //----------------------------------------------------------------------------- -CRenderTarget::~CRenderTarget() +RenderTarget::~RenderTarget() //----------------------------------------------------------------------------- { - Release(true/*assume we own the framebuffers*/); + Release(); } //----------------------------------------------------------------------------- -CRenderTarget::CRenderTarget( CRenderTarget&& src ) noexcept +RenderTarget::RenderTarget( RenderTarget&& src ) noexcept +//----------------------------------------------------------------------------- { *this = std::move( src ); } -//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -CRenderTarget& CRenderTarget::operator=( CRenderTarget&& src) noexcept +RenderTarget& RenderTarget::operator=( RenderTarget&& src) noexcept //----------------------------------------------------------------------------- { if (this != &src) { + Release(); // Release first so textures get freed! + m_Name = std::move(src.m_Name); m_Width = src.m_Width; src.m_Width = 0; @@ -54,10 +53,8 @@ CRenderTarget& CRenderTarget::operator=( CRenderTarget&& src) noexcept m_ClearColorValues = std::move( src.m_ClearColorValues ); m_ResolveAttachments = std::move( src.m_ResolveAttachments ); m_DepthAttachment = std::move( src.m_DepthAttachment ); - m_FrameBuffer = src.m_FrameBuffer; - src.m_FrameBuffer = VK_NULL_HANDLE; - m_FrameBufferDepthOnly = src.m_FrameBufferDepthOnly; - src.m_FrameBufferDepthOnly = VK_NULL_HANDLE; + m_FrameBuffer = std::move( src.m_FrameBuffer ); + m_FrameBufferDepthOnly = std::move( src.m_FrameBufferDepthOnly ); m_pVulkan = src.m_pVulkan; src.m_pVulkan = nullptr; @@ -66,18 +63,37 @@ CRenderTarget& CRenderTarget::operator=( CRenderTarget&& src) noexcept return *this; } + //----------------------------------------------------------------------------- -bool CRenderTarget::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, std::span Msaa, const char* pName) +bool RenderTarget::Initialize( Vulkan* pVulkan, const RenderTargetInitializeInfo& info, const char* pName, const RenderPass* renderPass, const RenderPass* renderPassDepthOnly ) //----------------------------------------------------------------------------- { + const size_t numColorAttachments = info.LayerFormats.size(); + m_pVulkan = pVulkan; - m_DepthFormat = DepthFormat; - m_Msaa.assign(Msaa.begin(), Msaa.end()); - m_Msaa.resize( pLayerFormats.size(), VK_SAMPLE_COUNT_1_BIT ); - m_FilterMode.resize(pLayerFormats.size(), SamplerFilter::Linear); + m_DepthFormat = info.DepthFormat; + m_Msaa.assign( info.Msaa.begin(), info.Msaa.end() ); + if (info.Msaa.empty()) + m_Msaa.resize( numColorAttachments, Msaa::Samples1 ); + else + m_Msaa.resize( numColorAttachments, m_Msaa.back() ); + + m_FilterMode.assign( info.FilterModes.begin(), info.FilterModes.end() ); + if (info.FilterModes.empty()) + m_FilterMode.resize( numColorAttachments, SamplerFilter::Linear ); + else + m_FilterMode.resize( numColorAttachments, info.FilterModes.back() ); + + std::vector colorTextureTypes {info.TextureTypes.begin(), info.TextureTypes.end()}; + if (info.TextureTypes.empty()) + colorTextureTypes.resize( numColorAttachments, TT_RENDER_TARGET ); + else + colorTextureTypes.resize( numColorAttachments, info.TextureTypes.back() ); + + std::optional depthTextureType = info.DepthTextureType; - m_Width = uiWidth; - m_Height = uiHeight; + m_Width = info.Width; + m_Height = info.Height; // If we have a name, save it if (pName != NULL) @@ -86,23 +102,79 @@ bool CRenderTarget::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHei } m_ColorAttachments.clear(); - m_ClearColorValues.resize(pLayerFormats.size(), {{ 0.0f, 0.0f, 0.0f, 0.0f }}); + m_ClearColorValues.resize( numColorAttachments, {{ 0.0f, 0.0f, 0.0f, 0.0f }} ); m_ResolveAttachments.clear(); - m_pLayerFormats.assign( pLayerFormats.begin(), pLayerFormats.end() ); + m_pLayerFormats.assign( info.LayerFormats.begin(), info.LayerFormats.end() ); + + if (!InitializeDepth(depthTextureType)) + return false; + if (!InitializeColor(colorTextureTypes)) + return false; + if (!InitializeResolve( info.ResolveTextureFormats )) + return false; + if (renderPass && *renderPass && !CreateFrameBuffer( *renderPass, m_ColorAttachments, &m_DepthAttachment, m_ResolveAttachments, nullptr/*pVRSAttachment*/, &m_FrameBuffer )) + return false; + if (renderPassDepthOnly && *renderPassDepthOnly && m_DepthAttachment && !CreateFrameBuffer( *renderPassDepthOnly, {}, &m_DepthAttachment, m_ResolveAttachments, nullptr/*pVRSAttachment*/, &m_FrameBufferDepthOnly )) + return false; return true; } + //----------------------------------------------------------------------------- -bool CRenderTarget::InitializeDepth() +bool RenderTarget::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, const char* pName, const std::span TextureTypes, std::span Multisample, const std::span ResolveTextureFormats, const std::span FilterModes) +//----------------------------------------------------------------------------- +{ + const RenderTargetInitializeInfo info{ + .Width = uiWidth, + .Height = uiHeight, + .LayerFormats = pLayerFormats, + .DepthFormat = DepthFormat, + .TextureTypes = TextureTypes, + .Msaa = Multisample, + .ResolveTextureFormats = ResolveTextureFormats, + .FilterModes = FilterModes + }; + return Initialize( pVulkan, info, pName, nullptr, nullptr ); +} + +////----------------------------------------------------------------------------- +//bool RenderTarget::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureVulkan* inheritDepth, Msaa msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) +////----------------------------------------------------------------------------- +//{ +// if (!Initialize( pVulkan, uiWidth, uiHeight, pLayerFormats, TextureFormat::UNDEFINED/*depth*/, msaa, pName )) +// return false; +// if (inheritDepth) +// m_DepthAttachment = std::move( TextureVulkan( inheritDepth->Width, inheritDepth->Height, inheritDepth->Depth, inheritDepth->MipLevels, inheritDepth->FirstMip, inheritDepth->Faces, inheritDepth->FirstFace, inheritDepth->Format, inheritDepth->ImageLayout, inheritDepth->ClearValue, inheritDepth->Image, inheritDepth->Sampler, inheritDepth->ImageView ) ); +//} + + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeFrameBuffer( Vulkan* pVulkan, const RenderPass& renderPass ) +//----------------------------------------------------------------------------- +{ + bool success = CreateFrameBuffer( renderPass, m_ColorAttachments, &m_DepthAttachment, m_ResolveAttachments, nullptr, &m_FrameBuffer ); + return success; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeFrameBufferDepthOnly( Vulkan* pVulkan, const RenderPass& renderPassDepthOnly ) +//----------------------------------------------------------------------------- +{ + bool success = CreateFrameBuffer( renderPassDepthOnly, {}, &m_DepthAttachment, {}, nullptr, &m_FrameBufferDepthOnly ); + return success; +} + +//----------------------------------------------------------------------------- +bool RenderTarget::InitializeDepth(std::optional textureType) //----------------------------------------------------------------------------- { if (m_DepthFormat != TextureFormat::UNDEFINED) { char szName[256]; sprintf(szName, "%s: Depth", m_Name.c_str()); - m_DepthAttachment = CreateTextureObject(*m_pVulkan, m_Width, m_Height, m_DepthFormat, TT_DEPTH_TARGET, m_Name.c_str(), m_Msaa.empty() ? VK_SAMPLE_COUNT_1_BIT : m_Msaa[0]); + m_DepthAttachment = CreateTextureObject(*m_pVulkan, m_Width, m_Height, m_DepthFormat, TT_DEPTH_TARGET, m_Name.c_str(), m_Msaa.empty() ? Msaa::Samples1 : m_Msaa[0]); } else { @@ -112,7 +184,7 @@ bool CRenderTarget::InitializeDepth() } //----------------------------------------------------------------------------- -bool CRenderTarget::InitializeColor(const std::span TextureTypes) +bool RenderTarget::InitializeColor(const std::span TextureTypes) //----------------------------------------------------------------------------- { const auto NumColorLayers = GetNumColorLayers(); @@ -147,7 +219,7 @@ bool CRenderTarget::InitializeColor(const std::span TextureT } //----------------------------------------------------------------------------- -bool CRenderTarget::InitializeResolve(const std::span ResolveTextureFormats) +bool RenderTarget::InitializeResolve(const std::span ResolveTextureFormats) //----------------------------------------------------------------------------- { m_ResolveAttachments.clear(); @@ -163,7 +235,7 @@ bool CRenderTarget::InitializeResolve(const std::span Resol char szName[256]; for (size_t WhichLayer = 0; WhichLayer < NumColorLayers; WhichLayer++) { - if (m_Msaa[WhichLayer] != VK_SAMPLE_COUNT_1_BIT && WhichLayer < ResolveTextureFormats.size() && ResolveTextureFormats[WhichLayer] != TextureFormat::UNDEFINED) + if (m_Msaa[WhichLayer] != Msaa::Samples1 && WhichLayer < ResolveTextureFormats.size() && ResolveTextureFormats[WhichLayer] != TextureFormat::UNDEFINED) { sprintf(szName, "%s: Color Resolve", m_Name.c_str()); @@ -176,10 +248,11 @@ bool CRenderTarget::InitializeResolve(const std::span Resol } //----------------------------------------------------------------------------- -bool CRenderTarget::InitializeFrameBuffer(VkRenderPass renderPass, const std::span ColorAttachments, const TextureVulkan* pDepthAttachment, const std::span ResolveAttachments, const TextureVulkan* pVRSAttachment, VkFramebuffer* pFramebuffer ) +bool RenderTarget::CreateFrameBuffer(const RenderPass& renderPass, const std::span ColorAttachments, const TextureVulkan* pDepthAttachment, const std::span ResolveAttachments, const TextureVulkan* pVRSAttachment, Framebuffer* pFramebuffer ) //----------------------------------------------------------------------------- { VkResult RetVal; + assert( pFramebuffer ); // ... then attach them to the render target std::vector attachments; @@ -202,26 +275,21 @@ bool CRenderTarget::InitializeFrameBuffer(VkRenderPass renderPass, const std::sp attachments.push_back( pVRSAttachment->GetVkImageView() ); } - VkFramebufferCreateInfo BufferInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; - BufferInfo.flags = 0; - BufferInfo.renderPass = renderPass; - BufferInfo.attachmentCount = (uint32_t)attachments.size(); - BufferInfo.pAttachments = attachments.data(); - BufferInfo.width = m_Width; - BufferInfo.height = m_Height; - BufferInfo.layers = 1; - - RetVal = vkCreateFramebuffer(m_pVulkan->m_VulkanDevice, &BufferInfo, NULL, pFramebuffer); - if (!CheckVkError("vkCreateFramebuffer()", RetVal)) - { - return false; - } - - return true; + fvk::VkFramebufferCreateInfo BufferInfo{{ + .flags = 0, + .renderPass = renderPass.mRenderPass, + .attachmentCount = (uint32_t)attachments.size(), + .pAttachments = attachments.data(), + .width = m_Width, + .height = m_Height, + .layers = 1 + }}; + + return pFramebuffer->Initialize( *m_pVulkan, renderPass, ColorAttachments, pDepthAttachment, m_Name, ResolveAttachments, pVRSAttachment ); } //----------------------------------------------------------------------------- -void CRenderTarget::SetClearColors(const std::span clearColors) +void RenderTarget::SetClearColors(const std::span clearColors) //----------------------------------------------------------------------------- { assert(clearColors.size() == m_ColorAttachments.size()); @@ -229,7 +297,7 @@ void CRenderTarget::SetClearColors(const std::span clea } //----------------------------------------------------------------------------- -void CRenderTarget::Release(bool bReleaseFramebuffers) +void RenderTarget::Release() //----------------------------------------------------------------------------- { if (m_pVulkan == nullptr) @@ -255,13 +323,8 @@ void CRenderTarget::Release(bool bReleaseFramebuffers) m_DepthAttachment.Release(m_pVulkan); m_DepthFormat = TextureFormat::UNDEFINED; - if (m_FrameBufferDepthOnly != VK_NULL_HANDLE && bReleaseFramebuffers) - vkDestroyFramebuffer(m_pVulkan->m_VulkanDevice, m_FrameBufferDepthOnly, NULL); - m_FrameBufferDepthOnly = VK_NULL_HANDLE; - - if (m_FrameBuffer != VK_NULL_HANDLE && bReleaseFramebuffers) - vkDestroyFramebuffer(m_pVulkan->m_VulkanDevice, m_FrameBuffer, NULL); - m_FrameBuffer = VK_NULL_HANDLE; + m_FrameBufferDepthOnly = {}; + m_FrameBuffer = {}; m_Height = 0; m_Width = 0; diff --git a/framework/code/vulkan/renderTarget.hpp b/framework/code/vulkan/renderTarget.hpp index fc825a5..7ea397f 100644 --- a/framework/code/vulkan/renderTarget.hpp +++ b/framework/code/vulkan/renderTarget.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,108 +11,255 @@ #include #include #include +#include "graphicsApi/renderTarget.hpp" +#include "vulkan/renderPass.hpp" #include "texture/vulkan/texture.hpp" #include "vulkan.hpp" //#include "TextureFuncts.h" #include "system/os_common.h" //============================================================================= -// CRenderTarget +// RenderTarget //============================================================================= /// Container for a single frame render target. /// Contains multiple color buffers, multiple resolve buffers, and an optional depth buffer. -class CRenderTarget +/// A Framebuffer object is created given a valid render pass is passed on initialization. +template<> +class RenderTarget final : public RenderTargetBase { // Functions - CRenderTarget(const CRenderTarget&) = delete; - CRenderTarget& operator=(const CRenderTarget&) = delete; + RenderTarget(const RenderTarget&) = delete; + RenderTarget& operator=(const RenderTarget&) = delete; public: - CRenderTarget(); - ~CRenderTarget(); - CRenderTarget( CRenderTarget&& ) noexcept; - CRenderTarget& operator=( CRenderTarget&& ) noexcept; + RenderTarget(); + ~RenderTarget(); + RenderTarget( RenderTarget&& ) noexcept; + RenderTarget& operator=( RenderTarget&& ) noexcept; + + /// @brief Initialize the render target with (potentially) multiple color buffers (including depth and resolve buffers where applicable + bool Initialize( Vulkan* pVulkan, const RenderTargetInitializeInfo& info, const char* pName, const RenderPass* renderPass = nullptr, const RenderPass* renderPassDepthOnly = nullptr ); + + /// @brief Initialize the render target with (potentially) multiple color buffers (including depth and resolve buffers where applicable + /// @param uiWidth + /// @param uiHeight + /// @param pLayerFormats color buffer formats + /// @param DepthFormat depth buffer format (optional) + /// @param pName + /// @param TextureTypes initial usage/type of render target (defualts to TT_RENDER_TARGET if empty span) + /// @param msaa multisampling setting of color buffer (can be one setting, which is copied to all buffers). Default Sample1 if empty span + /// @param ResolveTextureFormats resolve buffer formats (if msaa used) + /// @return true on success + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, const char* pName = NULL, const std::span TextureTypes = {}, std::span msaa = {}, const std::span ResolveTextureFormats = {}, const std::span FilterModes = {}); + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, Msaa msaa = Msaa::Samples1, const char* pName = NULL) + { + return Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, DepthFormat, pName, {/* texture type*/}, {&msaa,1}); + } + + /* + * Helper functions to manually initialize the framebuffers. + * These functions are intended to be used when the main Initialize() function is called without a render pass. + * In such cases, the user must explicitly call these functions with a valid render pass to complete framebuffer setup. + * @param pVulkan : Pointer to the Vulkan context + * @param renderPass / renderPassDepthOnly : Valid render pass used to create the framebuffer(s) + * @return true on successful framebuffer creation + * @note These functions provide manual control over framebuffer creation, useful for advanced or staged initialization flows. + */ + bool InitializeFrameBuffer(Vulkan* pVulkan, const RenderPass& renderPass); + bool InitializeFrameBufferDepthOnly(Vulkan* pVulkan, const RenderPass& renderPassDepthOnly); + + void Release(); + + inline uint32_t GetNumColorLayers() const + { + return static_cast(m_pLayerFormats.size()); + } + + /* + * Get the name of the render target + * @return std::string reference to the name + */ + inline const std::string& GetName() const + { + return m_Name; + } - uint32_t GetNumColorLayers() const { return (uint32_t)m_pLayerFormats.size(); } + /* + * Get the width of the render target + * @return uint32_t width + */ + inline uint32_t GetWidth() const + { + return m_Width; + } + + /* + * Get the height of the render target + * @return uint32_t height + */ + inline uint32_t GetHeight() const + { + return m_Height; + } + + /* + * Get the color layer formats + * @return const reference to vector of TextureFormat + */ + inline const std::vector& GetLayerFormats() const + { + return m_pLayerFormats; + } - bool Initialize( Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, std::span Msaa = {}, const char* pName = NULL); - bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, VkSampleCountFlagBits Msaa = VK_SAMPLE_COUNT_1_BIT, const char* pName = NULL) + /* + * Get the MSAA settings + * @return const reference to vector of Msaa + */ + inline const std::vector& GetMsaa() const { - return Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, DepthFormat, { &Msaa,1 }, pName); + return m_Msaa; } + + /* + * Get the filter modes + * @return const reference to vector of SamplerFilter + */ + inline const std::vector& GetFilterModes() const + { + return m_FilterMode; + } + + /* + * Get the depth format + * @return TextureFormat depth format + */ + inline TextureFormat GetDepthFormat() const + { + return m_DepthFormat; + } + + /* + * Get the color attachments + * @return const reference to vector of TextureVulkan + */ + inline const std::vector& GetColorAttachments() const + { + return m_ColorAttachments; + } + + /* + * Get the clear color values + * @return const reference to vector of VkClearColorValue + */ + inline const std::vector& GetClearColorValues() const + { + return m_ClearColorValues; + } + + /* + * Get the resolve attachments + * @return const reference to vector of TextureVulkan + */ + inline const std::vector& GetResolveAttachments() const + { + return m_ResolveAttachments; + } + + /* + * Get the depth attachment + * @return const reference to TextureVulkan + */ + inline const TextureVulkan& GetDepthAttachment() const + { + return m_DepthAttachment; + } + + /* + * Get the framebuffer + * @return const reference to Framebuffer + */ + inline const Framebuffer& GetFrameBuffer() const + { + return m_FrameBuffer; + } + + /* + * Get the depth-only framebuffer + * @return const reference to Framebuffer + */ + inline const Framebuffer& GetFrameBufferDepthOnly() const + { + return m_FrameBufferDepthOnly; + } + private: - template friend class CRenderTargetArray; - bool InitializeDepth(); + template friend class RenderTargetArray; + bool InitializeDepth(std::optional textureType); bool InitializeColor(const std::span TextureTypes); bool InitializeResolve(const std::span ResolveTextureFormats); - // Allow buffers to be initialized from the swapchain (for render target that writes to the swapchain) - bool InitializeColor(const SwapchainBuffers& SwapchainBuffer); - bool InitializeResolve(const SwapchainBuffers& SwapchainBuffer); - - bool InitializeFrameBuffer(VkRenderPass renderPass, const std::span ColorAttachments, const TextureVulkan* pDepthAttachment, const std::span ResolveAttachments, const TextureVulkan* pVRSAttachment, VkFramebuffer* pFramebuffer); + bool CreateFrameBuffer(const RenderPass& renderPass, const std::span ColorAttachments, const TextureVulkan* pDepthAttachment, const std::span ResolveAttachments, const TextureVulkan* pVRSAttachment, Framebuffer* pFrameBuffer); void SetClearColors(const std::span clearColors); - void Release(bool bReleaseFramebuffers /*set true if we are the owner of the framebuffers (and so want to clean them up)*/); - // Attributes public: - std::string m_Name; + std::string m_Name; - uint32_t m_Width = 0; - uint32_t m_Height = 0; + uint32_t m_Width = 0; + uint32_t m_Height = 0; - std::vector m_pLayerFormats; - std::vector m_Msaa; - std::vector m_FilterMode; - TextureFormat m_DepthFormat = TextureFormat::UNDEFINED; + std::vector m_pLayerFormats; + std::vector m_Msaa; + std::vector m_FilterMode; + TextureFormat m_DepthFormat = TextureFormat::UNDEFINED; // The Color Attachments - std::vector m_ColorAttachments; + std::vector m_ColorAttachments; std::vector m_ClearColorValues; // The Resolve Attachments - std::vector m_ResolveAttachments; + std::vector m_ResolveAttachments; // The Depth Attachment - TextureVulkan m_DepthAttachment; + TextureVulkan m_DepthAttachment; // The Frame Buffer - VkFramebuffer m_FrameBuffer = VK_NULL_HANDLE; + Framebuffer m_FrameBuffer; // The Frame Buffer (depth only) - VkFramebuffer m_FrameBufferDepthOnly = VK_NULL_HANDLE; + Framebuffer m_FrameBufferDepthOnly; private: - Vulkan* m_pVulkan = nullptr; + Vulkan* m_pVulkan = nullptr; }; +#if 0 /// Fixed size array of CRenderTargets (eg one per 'frame') that share RenderPass objects template -class CRenderTargetArray +class RenderTargetArray { - CRenderTargetArray(const CRenderTargetArray&) = delete; - CRenderTargetArray& operator=(const CRenderTargetArray&) = delete; + RenderTargetArray(const RenderTargetArray&) = delete; + RenderTargetArray& operator=(const RenderTargetArray&) = delete; public: - CRenderTargetArray() = default; - ~CRenderTargetArray(); - CRenderTargetArray( CRenderTargetArray&& src ) noexcept; - CRenderTargetArray& operator=(CRenderTargetArray &&) noexcept; + RenderTargetArray() = default; + ~RenderTargetArray(); + RenderTargetArray( RenderTargetArray&& src ) noexcept; + RenderTargetArray& operator=(RenderTargetArray &&) noexcept; /// @brief initialize the render target array (including depth) with the given dimensions and buffer formats. /// Creates VkRenderPasses for color and depth only passes (for the purpose of creating the pipeline). Creates the pipeline referencing these buffers (and one for depth only rendering). /// @return true if successful - bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, VkSampleCountFlagBits Msaa = VK_SAMPLE_COUNT_1_BIT, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE, std::span ResolveFormats = {}/*default no resolve*/); + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat = TextureFormat::D24_UNORM_S8_UINT, Msaa msaa = Msaa::Samples1, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE, std::span ResolveFormats = {}/*default no resolve*/); /// @brief initialize the render target array with the given dimensions and buffer formats. Creates render passes for the purpose of creating the pipeline. Pipeline is initialized with the depth buffer passed in inheritDepth parameter. If Present is true will write output to the backbuffer (with resolve as appropriate) /// Creates VkRenderPasses for color and depth only passes. /// @return true if successful - bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const CRenderTargetArray& inheritDepth, VkSampleCountFlagBits Msaa = VK_SAMPLE_COUNT_1_BIT, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); - bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const std::array& inheritDepth, VkSampleCountFlagBits Msaa = VK_SAMPLE_COUNT_1_BIT, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const RenderTargetArray& inheritDepth, Msaa msaa = Msaa::Samples1, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const std::array& inheritDepth, Msaa msaa = Msaa::Samples1, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); /// @brief initialize the render target array with the given dimensions and buffer formats. DOES take ownership of the passed in render passes. Because render passes could have a mix of msaa settings take a span for each color buffer. - bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, VkRenderPass RenderPass, VkRenderPass RenderPassDepthOnly, std::span Msaa = {}, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); + bool Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, RenderPass renderPass, RenderPass renderPassDepthOnly, std::span msaa = {}, const char* pName = NULL, const std::span ColorTypes = {}, const std::span FilterModes = {}, const TextureVulkan* pVRSMap = VK_NULL_HANDLE); /// @brief initialize the render target array using the vulkan swapchain pipeline and swapchain resolution/format. Use as a helper to render to the swapchain bool InitializeFromSwapchain( Vulkan* pVulkan ); @@ -120,38 +267,32 @@ class CRenderTargetArray void SetClearColors(const std::span clearColors); void Release(); - const CRenderTarget& operator[](size_t idx) const { return m_RenderTargets[idx]; } - CRenderTarget& operator[](size_t idx) { return m_RenderTargets[idx]; } + const RenderTarget& operator[](size_t idx) const { return m_RenderTargets[idx]; } + RenderTarget& operator[](size_t idx) { return m_RenderTargets[idx]; } /// The Render Pass (shared between buffers). - VkRenderPass m_RenderPass = VK_NULL_HANDLE; + RenderPass m_RenderPass; /// Depth only Render Pass (shared between buffers) - VkRenderPass m_RenderPassDepthOnly = VK_NULL_HANDLE; + RenderPass m_RenderPassDepthOnly; /// The render target buffers - std::array m_RenderTargets; - /// Set if the framebuffers in m_RenderTargets are owned (created) by CRenderTargetArray or are owned externally (eg by @Vulkan in the case of @InitializeFromSwapchain) - bool m_FramebufferOwner = false; + std::array, T_NUM_BUFFERS> m_RenderTargets; private: Vulkan* m_pVulkan = nullptr; }; //============================================================================= -// CRenderTargetArray template implementation +// RenderTargetArray template implementation //============================================================================= template -CRenderTargetArray& CRenderTargetArray::operator=( CRenderTargetArray&& src ) noexcept +RenderTargetArray& RenderTargetArray::operator=( RenderTargetArray&& src ) noexcept { if (this != &src) { - m_RenderPass = src.m_RenderPass; - src.m_RenderPass = VK_NULL_HANDLE; - m_RenderPassDepthOnly = src.m_RenderPassDepthOnly; - src.m_RenderPassDepthOnly = VK_NULL_HANDLE; + m_RenderPass = std::move(src.m_RenderPass); + m_RenderPassDepthOnly = std::move(src.m_RenderPassDepthOnly); m_RenderTargets = std::move( src.m_RenderTargets ); - m_FramebufferOwner = src.m_FramebufferOwner; - src.m_FramebufferOwner = false; m_pVulkan = src.m_pVulkan; src.m_pVulkan = nullptr; } @@ -159,46 +300,46 @@ CRenderTargetArray& CRenderTargetArray::operator=( } template -CRenderTargetArray::CRenderTargetArray( CRenderTargetArray&& src ) noexcept +RenderTargetArray::RenderTargetArray( RenderTargetArray&& src ) noexcept { *this = std::move(src); } template -CRenderTargetArray::~CRenderTargetArray() +RenderTargetArray::~RenderTargetArray() { Release(); } template -void CRenderTargetArray::Release() +void RenderTargetArray::Release() { for (int WhichBuffer = T_NUM_BUFFERS - 1; WhichBuffer >= 0; --WhichBuffer) - m_RenderTargets[WhichBuffer].Release(m_FramebufferOwner); - if (m_RenderPassDepthOnly != VK_NULL_HANDLE) - vkDestroyRenderPass(m_pVulkan->m_VulkanDevice, m_RenderPassDepthOnly, NULL); - m_RenderPassDepthOnly = VK_NULL_HANDLE; - if (m_RenderPass != VK_NULL_HANDLE) - vkDestroyRenderPass(m_pVulkan->m_VulkanDevice, m_RenderPass, NULL); - m_RenderPass = VK_NULL_HANDLE; + m_RenderTargets[WhichBuffer].Release(); + m_RenderPassDepthOnly = {}; + m_RenderPass = {}; } template -bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, VkSampleCountFlagBits Msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap, std::span ResolveFormats) +bool RenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, Msaa msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap, std::span ResolveFormats) { m_pVulkan = pVulkan; + // Allow 0 Msaa (same as 1 bit) + if (msaa < Msaa::Samples1) + msaa = Msaa::Samples1; + if (pVRSMap) { // ... create the render pass... - if (!pVulkan->CreateRenderPassVRS( pLayerFormats, DepthFormat, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPass, ResolveFormats )) + if (!pVulkan->CreateRenderPassVRS( pLayerFormats, DepthFormat, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPass, ResolveFormats )) { LOGE( "Unable to create render pass: %s", pName ); return false; } // ... create the depth only render pass... - if (!pVulkan->CreateRenderPassVRS( {}, DepthFormat, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPassDepthOnly )) + if (!pVulkan->CreateRenderPassVRS( {}, DepthFormat, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPassDepthOnly )) { LOGE( "Unable to create render pass: %s", pName ); return false; @@ -208,13 +349,13 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW else { // ... create the render pass... - if (!pVulkan->CreateRenderPass(pLayerFormats, DepthFormat, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPass, ResolveFormats)) + if (!pVulkan->CreateRenderPass(pLayerFormats, DepthFormat, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPass, ResolveFormats)) { LOGE("Unable to create render pass: %s", pName); return false; } // ... create the depth only render pass... - if (!pVulkan->CreateRenderPass({}, DepthFormat, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPassDepthOnly)) + if (!pVulkan->CreateRenderPass({}, DepthFormat, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPassDepthOnly)) { LOGE("Unable to create render pass: %s", pName); return false; @@ -222,26 +363,28 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW } // ... create the render targets and framebuffers - m_FramebufferOwner = true; char szName[128]; uint32_t WhichBuffer = 0; + + RenderTargetInitializeInfo renderTargetInitInfo{ + .Width = uiWidth, + .Height = uiHeight, + .LayerFormats = pLayerFormats, + .DepthFormat = DepthFormat, + .TextureTypes = ColorTypes, + .Msaa = {&msaa, 1}, + .ResolveTextureFormats = ResolveFormats, + .FilterModes = FilterModes + }; for (auto& RenderTarget : m_RenderTargets) { - snprintf(szName, sizeof(szName), "%s (Buffer %d of %d)", pName, WhichBuffer + 1, T_NUM_BUFFERS); szName[sizeof(szName) - 1] = 0; - if (!RenderTarget.Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, DepthFormat, Msaa, szName)) + snprintf(szName, sizeof(szName), "%s (Buffer %d of %d)", pName, WhichBuffer + 1, (int)m_RenderTargets.size() ); szName[sizeof(szName) - 1] = 0; + if (!RenderTarget.Initialize(pVulkan, renderTargetInitInfo, szName, &m_RenderPass, &m_RenderPassDepthOnly)) { return false; } - if (FilterModes.empty()) - std::fill(RenderTarget.m_FilterMode.begin(), RenderTarget.m_FilterMode.end(), SamplerFilter::Linear); - else - RenderTarget.m_FilterMode.assign(std::begin(FilterModes), std::end(FilterModes)); - RenderTarget.InitializeDepth(); - RenderTarget.InitializeColor(ColorTypes); - if (!ResolveFormats.empty()) - RenderTarget.InitializeResolve(ResolveFormats); - RenderTarget.InitializeFrameBuffer(m_RenderPass, RenderTarget.m_ColorAttachments, &RenderTarget.m_DepthAttachment, RenderTarget.m_ResolveAttachments, pVRSMap, &RenderTarget.m_FrameBuffer); - RenderTarget.InitializeFrameBuffer(m_RenderPassDepthOnly, {}, &RenderTarget.m_DepthAttachment, {}, pVRSMap, &RenderTarget.m_FrameBufferDepthOnly); + //RenderTarget.CreateFrameBuffer(m_RenderPass, RenderTarget.m_ColorAttachments, &RenderTarget.m_DepthAttachment, RenderTarget.m_ResolveAttachments, pVRSMap, &RenderTarget.m_FrameBuffer); + //RenderTarget.CreateFrameBuffer(m_RenderPassDepthOnly, {}, &RenderTarget.m_DepthAttachment, {}, pVRSMap, &RenderTarget.m_FrameBufferDepthOnly); pVulkan->SetDebugObjectName(RenderTarget.m_FrameBuffer, szName); ++WhichBuffer; } @@ -249,59 +392,54 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW } template -bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, VkRenderPass RenderPass, VkRenderPass RenderPassDepthOnly, std::span Msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) +bool RenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, TextureFormat DepthFormat, RenderPass renderPass, RenderPass RenderPassDepthOnly, std::span msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) { m_pVulkan = pVulkan; - m_RenderPass = RenderPass; - m_RenderPassDepthOnly = RenderPassDepthOnly; + m_RenderPass = std::move(renderPass); + m_RenderPassDepthOnly = std::move(RenderPassDepthOnly); // ... create the render targets and framebuffers - m_FramebufferOwner = true; char szName[128]; uint32_t WhichBuffer = 0; + + const RenderTargetInitializeInfo renderTargetInitInfo { + .Width = uiWidth, + .Height = uiHeight, + .LayerFormats = pLayerFormats, + .DepthFormat = DepthFormat, + .Msaa = msaa, + .FilterModes = FilterModes + }; + for (auto& RenderTarget : m_RenderTargets) { snprintf(szName, sizeof(szName), "%s (Buffer %d of %d)", pName, WhichBuffer + 1, T_NUM_BUFFERS); szName[sizeof(szName) - 1] = 0; - if (!RenderTarget.Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, DepthFormat, Msaa, szName)) + if (!RenderTarget.Initialize(pVulkan, renderTargetInitInfo, szName, &m_RenderPass, &m_RenderPassDepthOnly)) { return false; } - if (FilterModes.empty()) - std::fill(RenderTarget.m_FilterMode.begin(), RenderTarget.m_FilterMode.end(), SamplerFilter::Linear); - else - RenderTarget.m_FilterMode.assign(std::begin(FilterModes), std::end(FilterModes)); - RenderTarget.InitializeDepth(); - RenderTarget.InitializeColor(ColorTypes); - if( RenderPass != VK_NULL_HANDLE ) - { - RenderTarget.InitializeFrameBuffer( RenderPass, RenderTarget.m_ColorAttachments, &RenderTarget.m_DepthAttachment, RenderTarget.m_ResolveAttachments, pVRSMap, &RenderTarget.m_FrameBuffer ); - } - if( RenderPassDepthOnly != VK_NULL_HANDLE ) - { - RenderTarget.InitializeFrameBuffer(RenderPassDepthOnly, {}, &RenderTarget.m_DepthAttachment, {}, pVRSMap, &RenderTarget.m_FrameBufferDepthOnly); - } ++WhichBuffer; } return true; } template -bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const CRenderTargetArray& inheritDepth, VkSampleCountFlagBits Msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) +bool RenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const RenderTargetArray& inheritDepth, Msaa msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) { std::array pReferencedDepthTextures; - std::transform(std::begin(inheritDepth.m_RenderTargets), std::end(inheritDepth.m_RenderTargets), std::begin(pReferencedDepthTextures), [](const CRenderTarget& rt) { return &rt.m_DepthAttachment; }); - return Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, pReferencedDepthTextures, Msaa, pName, ColorTypes, FilterModes, pVRSMap); + std::transform(std::begin(inheritDepth.m_RenderTargets), std::end(inheritDepth.m_RenderTargets), std::begin(pReferencedDepthTextures), [](const RenderTarget& rt) { return &rt.m_DepthAttachment; }); + return Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, pReferencedDepthTextures, msaa, pName, ColorTypes, FilterModes, pVRSMap); } template -bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const std::array& inheritDepth, VkSampleCountFlagBits Msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) +bool RenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiWidth, uint32_t uiHeight, const std::span pLayerFormats, const std::array& inheritDepth, Msaa msaa, const char* pName, const std::span ColorTypes, const std::span FilterModes, const TextureVulkan* pVRSMap) { m_pVulkan = pVulkan; if (pVRSMap) { // ... create the render pass (color only)... - if (!pVulkan->CreateRenderPassVRS( pLayerFormats, inheritDepth[0]->Format, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPass )) + if (!pVulkan->CreateRenderPassVRS( pLayerFormats, inheritDepth[0]->Format, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPass )) { LOGE( "Unable to create render pass: %s", pName ); return false; @@ -310,7 +448,7 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW else { // ... create the render pass (color only)... - if (!pVulkan->CreateRenderPass(pLayerFormats, inheritDepth[0]->Format, Msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, &m_RenderPass)) + if (!pVulkan->CreateRenderPass(pLayerFormats, inheritDepth[0]->Format, msaa, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, m_RenderPass)) { LOGE("Unable to create render pass: %s", pName); return false; @@ -318,13 +456,12 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW } // Create the render target and framebuffers - m_FramebufferOwner = true; char szName[128]; uint32_t WhichBuffer = 0; for (auto& RenderTarget : m_RenderTargets) { snprintf(szName, sizeof(szName), "%s (Buffer %d of %d)", pName, WhichBuffer + 1, T_NUM_BUFFERS); szName[sizeof(szName) - 1] = 0; - if (!RenderTarget.Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, inheritDepth[WhichBuffer]->Format, Msaa, szName)) + if (!RenderTarget.Initialize(pVulkan, uiWidth, uiHeight, pLayerFormats, inheritDepth[WhichBuffer]->Format, msaa, szName)) { return false; } @@ -332,11 +469,7 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW std::fill(RenderTarget.m_FilterMode.begin(), RenderTarget.m_FilterMode.end(), SamplerFilter::Linear); else RenderTarget.m_FilterMode.assign(std::begin(FilterModes), std::end(FilterModes)); - if (!RenderTarget.InitializeColor(ColorTypes)) - { - return false; - } - if (!RenderTarget.InitializeFrameBuffer(m_RenderPass, RenderTarget.m_ColorAttachments, inheritDepth[WhichBuffer], RenderTarget.m_ResolveAttachments, pVRSMap, &RenderTarget.m_FrameBuffer)) + if (!RenderTarget.CreateFrameBuffer(m_RenderPass, RenderTarget.m_ColorAttachments, inheritDepth[WhichBuffer], RenderTarget.m_ResolveAttachments, pVRSMap, &RenderTarget.m_FrameBuffer)) { return false; } @@ -346,31 +479,27 @@ bool CRenderTargetArray::Initialize(Vulkan* pVulkan, uint32_t uiW } template -bool CRenderTargetArray::InitializeFromSwapchain(Vulkan* pVulkan) +bool RenderTargetArray::InitializeFromSwapchain(Vulkan* pVulkan) { m_pVulkan = pVulkan; - m_FramebufferOwner = false; const TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; const TextureFormat depthFormat = pVulkan->m_SwapchainDepth.format; - size_t WhichFrame = 0; - for (auto& RenderTarget : m_RenderTargets) + for(size_t whichFrame = 0; whichFrame< pVulkan->m_SwapchainBuffers.size() && whichFrame < T_NUM_BUFFERS; ++whichFrame) { - if (!RenderTarget.Initialize(pVulkan, pVulkan->m_SurfaceWidth, pVulkan->m_SurfaceHeight, { &surfaceFormat, 1 }, depthFormat, VK_SAMPLE_COUNT_1_BIT, "Swapchain")) + RenderTarget& renderTarget = m_RenderTargets[whichFrame]; + if (!renderTarget.Initialize( pVulkan, pVulkan->m_SurfaceWidth, pVulkan->m_SurfaceHeight, {&surfaceFormat, 1}, depthFormat, Msaa::Samples1, "Swapchain" )) return false; - if (WhichFrame < pVulkan->m_SwapchainBuffers.size()) - RenderTarget.m_FrameBuffer = pVulkan->m_SwapchainBuffers[WhichFrame].framebuffer; - else - RenderTarget.m_FrameBuffer = VK_NULL_HANDLE; - ++WhichFrame; + renderTarget.m_FrameBuffer = pVulkan->m_SwapchainBuffers[whichFrame].framebuffer; } return true; } template -void CRenderTargetArray::SetClearColors(const std::span clearColors) +void RenderTargetArray::SetClearColors(const std::span clearColors) { - for (auto& RenderTarget : m_RenderTargets) - RenderTarget.SetClearColors(clearColors); + for (auto& renderTarget : m_RenderTargets) + renderTarget.SetClearColors(clearColors); } +#endif //0 diff --git a/framework/code/vulkan/semaphore.cpp b/framework/code/vulkan/semaphore.cpp new file mode 100644 index 0000000..c21716f --- /dev/null +++ b/framework/code/vulkan/semaphore.cpp @@ -0,0 +1,33 @@ +//============================================================================= +// +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "semaphore.hpp" + +// +// Constructors/move-operators for SemaphoreVulkan (wrapper around vkSemaphore to handle destroy and ref counting). +// +SemaphoreVulkan::SemaphoreVulkan() noexcept + : m_Semaphore( VK_NULL_HANDLE, VK_NULL_HANDLE ) +{} +SemaphoreVulkan::~SemaphoreVulkan() noexcept +{} +SemaphoreVulkan::SemaphoreVulkan( VkDevice device, VkSemaphore semaphore ) noexcept + : m_Semaphore( device, semaphore ) +{} +SemaphoreVulkan::SemaphoreVulkan( SemaphoreVulkan&& src ) noexcept + : m_Semaphore( std::move( src.m_Semaphore ) ) +{} +SemaphoreVulkan& SemaphoreVulkan::operator=( SemaphoreVulkan&& src ) noexcept +{ + if (this != &src) + { + m_Semaphore = src.m_Semaphore; + src.m_Semaphore = {}; + } + return *this; +} diff --git a/framework/code/vulkan/semaphore.hpp b/framework/code/vulkan/semaphore.hpp new file mode 100644 index 0000000..99fb7e8 --- /dev/null +++ b/framework/code/vulkan/semaphore.hpp @@ -0,0 +1,38 @@ +//============================================================================= +// +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +#include "graphicsApi/graphicsApiBase.hpp" +#include "vulkan/refHandle.hpp" + +// Forward declarations + + +/// Simple wrapper around VkSemaphore. +/// Simplifies creation (and checks for leaks on destruction - is up to the owner to call Destroy) +/// @ingroup Vulkan +class SemaphoreVulkan final +{ +public: + SemaphoreVulkan() noexcept; + ~SemaphoreVulkan() noexcept; + SemaphoreVulkan( VkDevice, VkSemaphore ) noexcept; + SemaphoreVulkan( SemaphoreVulkan&& src ) noexcept; + SemaphoreVulkan& operator=( SemaphoreVulkan&& src ) noexcept; + SemaphoreVulkan Copy() const { return SemaphoreVulkan{*this}; } + + VkSemaphore GetVkSemaphore() const { return m_Semaphore; } + bool IsEmpty() const { return m_Semaphore == VK_NULL_HANDLE; } + +private: + SemaphoreVulkan( const SemaphoreVulkan& src ) noexcept { + m_Semaphore = src.m_Semaphore; + } + + RefHandle m_Semaphore; +}; diff --git a/framework/code/vulkan/timerPool.cpp b/framework/code/vulkan/timerPool.cpp index 82a70ad..fa88671 100644 --- a/framework/code/vulkan/timerPool.cpp +++ b/framework/code/vulkan/timerPool.cpp @@ -1,13 +1,13 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "timerPool.hpp" -#include "extensionHelpers.hpp" +#include "extensionLib.hpp" #include #include @@ -19,7 +19,7 @@ TimerPoolBase::TimerPoolBase( Vulkan& vulkan ) noexcept : m_Vulkan( vulkan ) TimerPoolBase::~TimerPoolBase() { - assert(m_VulkanQueryPool == VK_NULL_HANDLE); + Destroy(); } @@ -39,10 +39,10 @@ bool TimerPoolBase::Initialize( uint32_t maxTimers ) QueryInfo.queryCount = timerCount * 2/*one for start time, one for stop time*/; // size based on number of frames, may fall-down if we want/support timers that run across frame boundaries. QueryInfo.pipelineStatistics = 0; - const auto* hostQueryResetExt = m_Vulkan.GetExtension(); + const auto* hostQueryResetExt = m_Vulkan.GetExtension(); if (!hostQueryResetExt || hostQueryResetExt->Status != VulkanExtensionStatus::eLoaded) { - LOGE("TimerPoolBase functionality requires VK_EXT_host_query_reset extension"); // Likely missing appConfig.RequiredExtension() (or hardware does not support VK_EXT_host_query_reset) + LOGE("TimerPoolBase functionality requires VK_EXT_host_query_reset extension"); // Likely missing appConfig.RequiredExtension() (or hardware does not support VK_EXT_host_query_reset) // If we move to requiring Vulkan 1.2 then we can remove this check and use vkResetQueryPool (no extension needed in 1.2) // Alternately we could do the resets on the GPU (which is supported in 1.1) and modify tracking of valid timers accordingly return false; @@ -70,7 +70,7 @@ bool TimerPoolBase::Initialize( uint32_t maxTimers ) ResetQueryPool( 0, QueryInfo.queryCount ); // Queues can have different timer 'valid bits', grab the values for each queue. - std::transform( m_Vulkan.m_pVulkanQueueProps.begin(), m_Vulkan.m_pVulkanQueueProps.end(), std::back_insert_iterator(m_DeviceQueueValidTimerBitMask), []( const auto& a ) -> uint64_t { + std::transform( m_Vulkan.m_pVulkanQueueProps.begin(), m_Vulkan.m_pVulkanQueueProps.end(), std::back_inserter(m_DeviceQueueValidTimerBitMask), []( const auto& a ) -> uint64_t { uint64_t v = a.timestampValidBits < 64 ? (uint64_t(1) << uint64_t(a.timestampValidBits)) : 0; return v - uint64_t(1); }); @@ -149,7 +149,7 @@ void TimerPoolBase::ReadResults(VkCommandBuffer commandBuffer, uint32_t whichFra vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT/*src stage*/, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT/* dest stage bit*/, 0, 0, nullptr, 0, nullptr, 0, nullptr); // Copy the timing queries that may have been written this frame to a buffer (ready for mapping back to the cpu). - vkCmdCopyQueryPoolResults(commandBuffer, m_VulkanQueryPool, 0, maxUsedQueries, m_VulkanQueryResults.vkBuffers[whichFrame], 0, sizeof(VulkanQueryResult), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); + vkCmdCopyQueryPoolResults(commandBuffer, m_VulkanQueryPool, 0, maxUsedQueries, m_VulkanQueryResults.bufferHandles[whichFrame], 0, sizeof(VulkanQueryResult), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); // Reset the timing queries that may have been written this frame. vkCmdResetQueryPool(commandBuffer, m_VulkanQueryPool, 0, maxUsedQueries); } diff --git a/framework/code/vulkan/timerPool.hpp b/framework/code/vulkan/timerPool.hpp index d67c135..72a7ce0 100644 --- a/framework/code/vulkan/timerPool.hpp +++ b/framework/code/vulkan/timerPool.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/vulkan/timerSimple.cpp b/framework/code/vulkan/timerSimple.cpp index 646b4d3..f14f706 100644 --- a/framework/code/vulkan/timerSimple.cpp +++ b/framework/code/vulkan/timerSimple.cpp @@ -1,13 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "timerSimple.hpp" #include +#include void TimerSimple::Update(uint32_t whichFrame, uint64_t startTick, uint64_t stopTick) @@ -62,7 +63,7 @@ void TimerPoolSimple::Log( const TimerPoolSimple::tTimers& timers ) const for (const auto& timer : timers) { if (timer.TotalCompletedTicks > 0) - LOGI( "Timer %s :\t%.3fms\t(avg %.3fms)", timer.Name.c_str(), GetTimeInMs(timer), GetAverageTimeInMs(timer) ); + LOGI( "Timer %s : %.3fms (avg)", timer.Name.c_str(), GetAverageTimeInMs(timer) ); } } @@ -85,7 +86,7 @@ void TimerPoolSimple::Log2( const TimerPoolSimple::tTimers& timers ) const std::sort( std::begin( timersInEventOrder ), std::end( timersInEventOrder ), []( auto a, auto b ) -> bool { return (a.isStop ? a.pTimer->LastStopTick : a.pTimer->LastStartTick) < (b.isStop ? b.pTimer->LastStopTick : b.pTimer->LastStartTick); } ); constexpr uint32_t cMaxTimerOverlaps = 4; - constexpr uint32_t cMaxNameLength = 20; + constexpr uint32_t cMaxNameLength = 40; constexpr uint32_t cQueueFamilyWidth = cMaxTimerOverlaps * 2 + cMaxNameLength; constexpr uint32_t cMaxQueueFamilies = 3; struct @@ -150,3 +151,10 @@ void TimerPoolSimple::Log2( const TimerPoolSimple::tTimers& timers ) const } } } + +TimerPoolSimple::tTimers TimerPoolSimple::SnapshotAndReset() +{ + auto snapshotData = m_Timers; + ResetTimers( -1 ); + return snapshotData; +} diff --git a/framework/code/vulkan/timerSimple.hpp b/framework/code/vulkan/timerSimple.hpp index 935d8b0..c56d45c 100644 --- a/framework/code/vulkan/timerSimple.hpp +++ b/framework/code/vulkan/timerSimple.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -66,4 +66,6 @@ class TimerPoolSimple : public TTimerPool /// Log the results from a collection of timers (logs using LOGI) void Log(const tTimers& timers) const; void Log2( const TimerPoolSimple::tTimers& timers ) const; + /// Take a snapshot of the current timer pool and reset the cumulative counters + TimerPoolSimple::tTimers SnapshotAndReset(); }; diff --git a/framework/code/vulkan/vulkan.cpp b/framework/code/vulkan/vulkan.cpp index e88682f..260b1ef 100644 --- a/framework/code/vulkan/vulkan.cpp +++ b/framework/code/vulkan/vulkan.cpp @@ -1,38 +1,31 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "vulkanDebugCallback.hpp" #include "vulkan.hpp" -#include "extension.hpp" -#include "extensionHelpers.hpp" +#include "extensionLib.hpp" #include "system/os_common.h" #include "system/config.h" #include "texture/vulkan/texture.hpp" +#include "vulkan/renderContext.hpp" +#include "vulkan/renderPass.hpp" + #include #include +#include #include #include +#if defined(OS_LINUX) +#include +#endif // OS_LINUX -// Functions whose pointers are in the instance -PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR = nullptr; -PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; -PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR fpGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr; -PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR = nullptr; -PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR = nullptr; -PFN_vkGetPhysicalDeviceProperties2 fpGetPhysicalDeviceProperties2 = nullptr; -PFN_vkGetPhysicalDeviceFeatures2 fpGetPhysicalDeviceFeatures2 = nullptr; -PFN_vkGetPhysicalDeviceMemoryProperties2 fpGetPhysicalDeviceMemoryProperties2 = nullptr; - -// Functions whose pointers are in the device -PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR = nullptr; -PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR = nullptr; -PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR = nullptr; -//PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR = nullptr; +#define VOLK_IMPLEMENTATION +#include #if defined(OS_ANDROID) #if __ANDROID_API__ < 29 @@ -124,16 +117,12 @@ Vulkan::Vulkan() m_LayerKhronosValidationAvailable = false; m_ExtGlobalPriorityAvailable = false; - m_ExtSwapchainColorspaceAvailable = false; - m_ExtSurfaceCapabilities2Available = false; m_ExtRenderPassTransformAvailable = false; m_ExtRenderPassShaderResolveAvailable = false; m_ExtRenderPassTransformLegacy = false; m_ExtRenderPassTransformEnabled = false; - m_ExtValidationFeaturesVersion = 0; m_ExtPortability = false; #if defined (OS_ANDROID) - m_ExtExternMemoryCapsAvailable = false; m_ExtAndroidExternalMemoryAvailable = false; #endif // defined (OS_ANDROID) @@ -190,9 +179,7 @@ Vulkan::~Vulkan() m_VulkanGpuCount = 0; m_VulkanGpuIdx = 0; - // Debug/Validation Layers - m_InstanceLayerProps.clear(); - m_InstanceLayerNames.clear(); + volkFinalize(); } @@ -200,11 +187,18 @@ Vulkan::~Vulkan() bool Vulkan::Init(uintptr_t windowHandle, uintptr_t hInst, const Vulkan::tSelectSurfaceFormatFn& SelectSurfaceFormatFn, const Vulkan::tConfigurationFn& CustomConfigurationFn) //----------------------------------------------------------------------------- { + if (volkInitialize() != VK_SUCCESS) + { + return false; + } + #if defined (OS_WINDOWS) m_hWnd = (HWND)windowHandle; m_hInstance = (HINSTANCE)hInst; #elif defined (OS_ANDROID) m_pAndroidWindow = (ANativeWindow*)windowHandle; +#elif defined (OS_LINUX) + m_pGlfwWindow = (GLFWwindow*)windowHandle; #endif // defined (OS_WINDOWS|OS_ANDROID) if (CustomConfigurationFn) @@ -303,10 +297,19 @@ bool Vulkan::ReInit(uintptr_t windowHandle) #elif defined (OS_ANDROID) if (m_pAndroidWindow != (ANativeWindow*)windowHandle) { - return false; + m_pAndroidWindow = (ANativeWindow*)windowHandle; + + vkDestroySurfaceKHR( m_VulkanInstance, m_VulkanSurface, nullptr ); + m_VulkanSurface = nullptr; + + InitSurface(); + + return true; } return true; // Success -#endif // defined (OS_WINDOWS|OS_ANDROID) +#elif defined (OS_LINUX) + return false; ///TODO: implement ReInit on Linux +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) } //----------------------------------------------------------------------------- @@ -529,9 +532,8 @@ void Vulkan::SetImageLayout(VkImage image, break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: - LOGE("Don't know how to set image layout if starting with depth/stencil read only!"); - assert(0); - return; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + srcStageFlags = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: @@ -681,16 +683,37 @@ bool Vulkan::RegisterKnownExtensions() assert(insertIt.second); // check we didnt add a duplicate! } - for ( auto& appExtension : m_ConfigOverride.AdditionalVulkanDeviceExtensions ) + for (auto& appExtension : m_ConfigOverride.AdditionalVulkanDeviceExtensions) { auto insertIt = m_DeviceExtensions.m_Extensions.insert( std::move( appExtension ) ); assert( insertIt.second ); // check we didnt add a duplicate! } + for (auto& appExtension : m_ConfigOverride.AdditionalVulkanInstanceLayers) + { + auto insertIt = m_InstanceLayers.m_Extensions.insert( std::move( appExtension ) ); + assert( insertIt.second ); // check we didnt add a duplicate! + } + +#if defined(USES_VULKAN_DEBUG_LAYERS) + // + // Add validation layer and layer extensions + // + // If this is NOT set, we cannot use the debug extensions! (Find vkCreateDebugReportCallback) + if (gEnableValidation) + { + m_InstanceLayers.AddExtension( "VK_LAYER_KHRONOS_validation", VulkanExtensionStatus::eOptional ); + m_InstanceExtensions.AddExtension( VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); + m_InstanceExtensions.AddExtension( VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); + // This extension allows us to use VkValidationFeaturesEXT (>0 if available) + m_ExtValidationFeatures = m_InstanceExtensions.AddExtension( VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); + } +#endif // USES_VULKAN_DEBUG_LAYERS + // // Add the Instance extensions we need // - m_ExtKhrSurface = m_InstanceExtensions.AddExtension(); + m_ExtKhrSurface = m_InstanceExtensions.AddExtension(); #if defined(VK_USE_PLATFORM_WIN32_KHR) m_InstanceExtensions.AddExtension( VK_KHR_WIN32_SURFACE_EXTENSION_NAME, VulkanExtensionStatus::eRequired ); @@ -700,78 +723,79 @@ bool Vulkan::RegisterKnownExtensions() m_InstanceExtensions.AddExtension( VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VulkanExtensionStatus::eRequired ); #endif // VK_USE_PLATFORM_ANDROID_KHR -#if defined(USES_VULKAN_DEBUG_LAYERS) - // If this is NOT set, we cannot use the debug extensions! (Find vkCreateDebugReportCallback) - if (gEnableValidation) - { - m_InstanceExtensions.AddExtension( VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); - m_InstanceExtensions.AddExtension( VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); - } -#endif // USES_VULKAN_DEBUG_LAYERS +#if defined(OS_LINUX) + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + for(int i=0;i(); + m_ExtKhrGetPhysicalDeviceProperties2 = m_InstanceExtensions.GetExtension(); if (!m_ExtKhrGetPhysicalDeviceProperties2) - m_ExtKhrGetPhysicalDeviceProperties2 = m_InstanceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_ExtKhrGetPhysicalDeviceProperties2 = m_InstanceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); // This extension allows us to call VkPhysicalDeviceSurfaceInfo2KHR (enable if available) - m_ExtSurfaceCapabilities2 = m_InstanceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); - - // This extension allows us to use VkValidationFeaturesEXT (>0 if available) -#if defined(USES_VULKAN_DEBUG_LAYERS) - if (gEnableValidation) - { - m_ExtValidationFeatures = m_InstanceExtensions.AddExtension( VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); - } -#endif // defined(USES_VULKAN_DEBUG_LAYERS) + m_ExtSurfaceCapabilities2 = m_InstanceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); #if defined (OS_ANDROID) // This extension allow us to use Android Hardware Buffers m_InstanceExtensions.AddExtension( "VK_KHR_external_memory_capabilities", VulkanExtensionStatus::eOptional ); #endif // defined (OS_ANDROID) +#if defined (OS_LINUX) + // May want portability + m_InstanceExtensions.AddExtension( "VK_KHR_portability_enumeration", VulkanExtensionStatus::eOptional ); +#endif // defined (OS_LINUX) + // This extension says the device must be able to present images directly to the screen. - m_DeviceExtensions.AddExtension( VK_KHR_SWAPCHAIN_EXTENSION_NAME, VulkanExtensionStatus::eRequired ); + m_ExtSwapchain = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eRequired ); // // Add extensions we would always LIKE to initialize (if they exist). // - m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); #if defined(USES_VULKAN_DEBUG_LAYERS) - m_ExtDebugUtils = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); - m_ExtDebugMarker = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_ExtDebugUtils = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_ExtDebugMarker = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); #else - m_ExtDebugUtils = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eUninitialized ); - m_ExtDebugMarker = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eUninitialized ); + m_ExtDebugUtils = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eUninitialized ); + m_ExtDebugMarker = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eUninitialized ); #endif m_DeviceExtensions.AddExtension( VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); - m_ExtHdrMetadata = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_ExtHdrMetadata = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); m_DeviceExtensions.AddExtension( VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); m_DeviceExtensions.AddExtension( "VK_QCOM_render_pass_transform", VulkanExtensionStatus::eOptional); // This extension allows us to set VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM (enable if available) m_DeviceExtensions.AddExtension( "VK_QCOM_render_pass_shader_resolve", VulkanExtensionStatus::eOptional); - m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); - m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + m_DeviceExtensions.AddExtension( VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); - m_SubgroupProperties = m_Vulkan11ProvidedExtensions.AddExtension( VulkanExtensionStatus::eRequired ); - m_StorageFeatures = m_Vulkan11ProvidedExtensions.AddExtension( VulkanExtensionStatus::eRequired ); + m_ExtQcomTileProperties = m_DeviceExtensions.AddExtension( VulkanExtensionStatus::eOptional ); + + m_SubgroupProperties = m_Vulkan11ProvidedExtensions.AddExtension(); + m_StorageFeatures = m_Vulkan11ProvidedExtensions.AddExtension(); #if defined (OS_ANDROID) m_DeviceExtensions.AddExtension( VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, VulkanExtensionStatus::eOptional ); #endif - m_ExtKhrSynchronization2 = m_DeviceExtensions.GetExtension(); - m_ExtKhrDrawIndirectCount = m_DeviceExtensions.GetExtension(); - m_ExtRenderPass2 = m_DeviceExtensions.GetExtension(); - m_ExtArmTensors = m_DeviceExtensions.GetExtension(); - m_ExtArmDataGraph = m_DeviceExtensions.GetExtension(); - m_ExtFragmentShadingRate = m_DeviceExtensions.GetExtension(); - m_ExtMeshShader = m_DeviceExtensions.GetExtension(); + m_ExtKhrSynchronization2 = m_DeviceExtensions.GetExtension(); + m_ExtKhrDrawIndirectCount = m_DeviceExtensions.GetExtension(); + m_ExtRenderPass2 = m_DeviceExtensions.GetExtension(); + m_ExtFragmentShadingRate = m_DeviceExtensions.GetExtension(); + m_ExtMeshShader = m_DeviceExtensions.GetExtension(); + m_ExtDynamicRendering = m_DeviceExtensions.GetExtension(); + m_ExtQcomTileShading = m_DeviceExtensions.GetExtension(); + m_ExtQcomTileMemoryHeap = m_DeviceExtensions.GetExtension(); + m_ExtKhrGetMemoryRequirements = m_DeviceExtensions.GetExtension(); // Now we have a list of all the extensions we know about we can ask them to Register themselves with whatever 'hooks' they require. + m_InstanceLayers.RegisterAll(*this); m_InstanceExtensions.RegisterAll(*this); m_DeviceExtensions.RegisterAll( *this ); m_Vulkan11ProvidedExtensions.RegisterAll( *this ); @@ -783,7 +807,7 @@ bool Vulkan::RegisterKnownExtensions() bool Vulkan::CreateInstance() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Debug/Validation/Whatever Layers @@ -806,31 +830,40 @@ bool Vulkan::CreateInstance() AppInfoStruct.applicationVersion = 0; AppInfoStruct.pEngineName = "VkFrameworkEngine"; AppInfoStruct.engineVersion = 0; - AppInfoStruct.apiVersion = m_ConfigOverride.ApiVerson.value_or( VK_MAKE_VERSION( 1, 1, 0 ) ); + AppInfoStruct.apiVersion = m_ConfigOverride.ApiVerson.value_or( VK_MAKE_VERSION( 1, 2, 0 ) ); - // Creation information for the instance points to details about - // the application, and also the list of extensions to enable. + // Create the list of layers to enable. + std::vector InstanceLayerNames; + InstanceLayerNames.reserve( m_InstanceLayers.m_Extensions.size() ); + for (const auto& e : m_InstanceLayers.m_Extensions) + if (e.second->Status == VulkanExtensionStatus::eLoaded && e.second->LoadMode != VulkanExtensionLoadMode::eSkipRequest) + InstanceLayerNames.push_back( e.first.c_str() ); + + // Create the list of instance extensions to enable std::vector InstanceExtensionNames; InstanceExtensionNames.reserve(m_InstanceExtensions.m_Extensions.size()); for (const auto& e : m_InstanceExtensions.m_Extensions) - if (e.second->Status == VulkanExtensionStatus::eLoaded) + if (e.second->Status == VulkanExtensionStatus::eLoaded && e.second->LoadMode != VulkanExtensionLoadMode::eSkipRequest) InstanceExtensionNames.push_back(e.first.c_str()); - VkInstanceCreateInfo InstanceInfoStruct {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; - InstanceInfoStruct.flags = 0; - InstanceInfoStruct.pApplicationInfo = &AppInfoStruct; - InstanceInfoStruct.enabledLayerCount = (uint32_t) m_InstanceLayerNames.size(); - InstanceInfoStruct.ppEnabledLayerNames = m_InstanceLayerNames.data(); - InstanceInfoStruct.enabledExtensionCount = (uint32_t) InstanceExtensionNames.size(); - InstanceInfoStruct.ppEnabledExtensionNames = InstanceExtensionNames.data(); + fvk::VkStructWrapper InstanceInfoStruct{{ + .flags = 0, + .pApplicationInfo = &AppInfoStruct, + .enabledLayerCount = (uint32_t)InstanceLayerNames.size(), + .ppEnabledLayerNames = InstanceLayerNames.data(), + .enabledExtensionCount = (uint32_t)InstanceExtensionNames.size(), + .ppEnabledExtensionNames = InstanceExtensionNames.data() + }}; + InstanceInfoStruct->flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; - // + // ******************************** // Potentially add Validation layer feature settings. - // - VkValidationFeaturesEXT ValidationFeaturesStruct { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT }; + // ******************************** std::vector ValidationFeaturesEnables; if (m_ExtValidationFeatures && m_ExtValidationFeatures->Version >= 2) // spec version 1 does not support 'best practices' { + auto& ValidationFeaturesStruct = InstanceInfoStruct.Add(); + #if (VK_EXT_VALIDATION_FEATURES_SPEC_VERSION < 4) // if our header does not define the spec version 4 then add the 'missing' enable #define VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT (4) #endif @@ -866,10 +899,9 @@ bool Vulkan::CreateInstance() if (!ValidationFeaturesEnables.empty()) { - ValidationFeaturesStruct.enabledValidationFeatureCount = (uint32_t) ValidationFeaturesEnables.size(); - ValidationFeaturesStruct.pEnabledValidationFeatures = ValidationFeaturesEnables.data(); + ValidationFeaturesStruct->enabledValidationFeatureCount = (uint32_t) ValidationFeaturesEnables.size(); + ValidationFeaturesStruct->pEnabledValidationFeatures = ValidationFeaturesEnables.data(); } - InstanceInfoStruct.pNext = &ValidationFeaturesStruct; } uint32_t MajorVersion = VK_VERSION_MAJOR(AppInfoStruct.apiVersion); @@ -878,7 +910,7 @@ bool Vulkan::CreateInstance() LOGI("Requesting Vulkan version %d.%d.%d", MajorVersion, MinorVersion, PatchVersion); LOGI("Requesting Layers:"); - for (const auto& LayerName : m_InstanceLayerNames) + for (const auto& LayerName : InstanceLayerNames) { LOGI(" %s", LayerName); } @@ -891,42 +923,56 @@ bool Vulkan::CreateInstance() LOGI("Creating Vulkan Instance..."); - LOGI(" InstanceInfo.enabledLayerCount: %d", InstanceInfoStruct.enabledLayerCount); - for (uint32_t Which = 0; Which < InstanceInfoStruct.enabledLayerCount; Which++) + LOGI(" InstanceInfo.enabledLayerCount: %d", InstanceInfoStruct->enabledLayerCount); + for (uint32_t Which = 0; Which < InstanceInfoStruct->enabledLayerCount; Which++) { - LOGI(" %d: %s", Which, InstanceInfoStruct.ppEnabledLayerNames[Which]); + LOGI(" %d: %s", Which, InstanceInfoStruct->ppEnabledLayerNames[Which]); } - LOGI(" InstanceInfo.enabledExtensionCount: %d", InstanceInfoStruct.enabledExtensionCount); - for (uint32_t Which = 0; Which < InstanceInfoStruct.enabledExtensionCount; Which++) + LOGI(" InstanceInfo.enabledExtensionCount: %d", InstanceInfoStruct->enabledExtensionCount); + for (uint32_t Which = 0; Which < InstanceInfoStruct->enabledExtensionCount; Which++) { - LOGI(" %d: %s", Which, InstanceInfoStruct.ppEnabledExtensionNames[Which]); + LOGI(" %d: %s", Which, InstanceInfoStruct->ppEnabledExtensionNames[Which]); } // The main Vulkan instance is created with the creation infos above. // We do not specify a custom memory allocator for instance creation. - RetVal = vkCreateInstance(&InstanceInfoStruct, nullptr, &m_VulkanInstance); + retVal = vkCreateInstance(&InstanceInfoStruct, nullptr, &m_VulkanInstance); + // ******************************** + // Handle any errors from create instance + // ******************************** // Vulkan API return values can expose further information on a failure. // For instance, INCOMPATIBLE_DRIVER may be returned if the API level // an application is built with, exposed through VkApplicationInfo, is // newer than the driver present on a device. - if (RetVal == VK_ERROR_INCOMPATIBLE_DRIVER) + if (retVal == VK_ERROR_INCOMPATIBLE_DRIVER) { LOGE("Cannot find a compatible Vulkan installable client driver: vkCreateInstance Failure"); return false; } - else if (RetVal == VK_ERROR_EXTENSION_NOT_PRESENT) + else if (retVal == VK_ERROR_EXTENSION_NOT_PRESENT) { LOGE("Cannot find a specified extension library: vkCreateInstance Failure"); return false; } - else if (!CheckVkError("vkCreateInstance()", RetVal)) + else if (!CheckVkError("vkCreateInstance()", retVal)) { return false; } + volkLoadInstance(m_VulkanInstance); + m_VulkanApiVersion = AppInfoStruct.apiVersion; + // ******************************** + // Call the PostLoad for all instance extensions + // ******************************** + for (auto& extension : m_InstanceExtensions.m_Extensions) + { + if (extension.second->Status == VulkanExtensionStatus::eLoaded) + extension.second->PostLoad(); + } + return true; } @@ -941,7 +987,7 @@ static bool ParseExtensionProperties( const std::vector& bool success = true; for (uint32_t uiIndx = 0; uiIndx < (uint32_t)ExtensionProps.size(); uiIndx++) { - const VkExtensionProperties* const pOneItem = &ExtensionProps[uiIndx]; + const auto* const pOneItem = &ExtensionProps[uiIndx]; LOGI( " %d: %s", uiIndx, pOneItem->extensionName ); // Need to check if we found specific device extensions @@ -976,8 +1022,16 @@ static bool ParseExtensionProperties( const std::vector& { if (extension.second->Status == VulkanExtensionStatus::eRequired) { - LOGE( "Required Vulkan extension \"%s\" was not found", extension.first.c_str() ); - success = false; + if (extension.second->LoadMode == VulkanExtensionLoadMode::eDefault) + { + LOGE( "Required Vulkan extension \"%s\" was not found.", extension.first.c_str() ); + success = false; + } + else + { + LOGW( "Required Vulkan extension \"%s\" not found but VulkanExtensionLoadMode requesting we continue.", extension.first.c_str() ); + extension.second->Status = VulkanExtensionStatus::eLoaded; + } } else if (extension.second->Status == VulkanExtensionStatus::eOptional) { @@ -988,19 +1042,85 @@ static bool ParseExtensionProperties( const std::vector& return success; } +//----------------------------------------------------------------------------- +// Parse LayerProps for layers in RegisteredLayers. +// If found set the registered layer's version and change Status to eLoaded if requested to load (status eOptional or eRequired). +// If not found add to the list of RegisteredLayers (with a eUninitialized status) +static bool ParseLayerProperties( const std::vector& LayerProps, Vulkan::RegisteredExtensions& RegisteredLayers ) +//----------------------------------------------------------------------------- +{ + bool success = true; + for (uint32_t uiIndx = 0; uiIndx < (uint32_t)LayerProps.size(); uiIndx++) + { + const auto* const pOneItem = &LayerProps[uiIndx]; + LOGI( " %d: %s", uiIndx, pOneItem->layerName ); + + // Need to check if we found specific device extensions + { + std::string name = pOneItem->layerName; + auto* pLayer = RegisteredLayers.GetExtension( name ); + if (pLayer) + { + // Found an already known extension, see if we want to do anything with it. + switch (pLayer->Status) + { + case VulkanExtensionStatus::eOptional: + case VulkanExtensionStatus::eRequired: + pLayer->Status = VulkanExtensionStatus::eLoaded; // strictly not 'yet' loaded but will be shortly + break; + case VulkanExtensionStatus::eLoaded: + case VulkanExtensionStatus::eUninitialized: + break; + } + pLayer->Version = pOneItem->specVersion; + } + else + { + // Add to the list of known extensions (assume we dont want to load it) + RegisteredLayers.AddExtension( name, VulkanExtensionStatus::eUninitialized, pOneItem->specVersion ); + } + } + } + + // Do a final check of extensions we requested (as required or optional) that are not present in the list of available extensions. + for (auto& extension : RegisteredLayers.m_Extensions) + { + if (extension.second->Status == VulkanExtensionStatus::eRequired) + { + if (extension.second->LoadMode == VulkanExtensionLoadMode::eDefault) + { + LOGE( "Required Vulkan extension \"%s\" was not found.", extension.first.c_str() ); + success = false; + } + else + { + LOGW( "Required Vulkan extension \"%s\" not found but VulkanExtensionLoadMode requesting we continue.", extension.first.c_str() ); + extension.second->Status = VulkanExtensionStatus::eLoaded; + } + } + else if (extension.second->Status == VulkanExtensionStatus::eOptional) + { + // If requested as optional but not available then set to be uninitialized. + extension.second->Status = VulkanExtensionStatus::eUninitialized; + } + } + return success; +} + + //----------------------------------------------------------------------------- bool Vulkan::InitInstanceExtensions() //----------------------------------------------------------------------------- { - VkResult RetVal; + VkResult retVal; // ******************************** - // Look for Instance Validation Layers + // Look for Instance Layers (eg Validation) // ******************************** { uint32_t NumInstanceLayerProps = 0; - RetVal = vkEnumerateInstanceLayerProperties(&NumInstanceLayerProps, nullptr); - if (!CheckVkError("vkEnumerateInstanceLayerProperties(nullptr)", RetVal)) + retVal = vkEnumerateInstanceLayerProperties(&NumInstanceLayerProps, nullptr); + if (!CheckVkError("vkEnumerateInstanceLayerProperties(nullptr)", retVal)) { return false; } @@ -1009,25 +1129,26 @@ bool Vulkan::InitInstanceExtensions() if (NumInstanceLayerProps > 0) { // Allocate memory for the structures... - m_InstanceLayerProps.resize(NumInstanceLayerProps); + std::vector InstanceLayerProps; + InstanceLayerProps.resize(NumInstanceLayerProps); // ... then read the structures from the driver - RetVal = vkEnumerateInstanceLayerProperties(&NumInstanceLayerProps, m_InstanceLayerProps.data()); - if (!CheckVkError("vkEnumerateInstanceLayerProperties()", RetVal)) + retVal = vkEnumerateInstanceLayerProperties(&NumInstanceLayerProps, InstanceLayerProps.data()); + if (!CheckVkError("vkEnumerateInstanceLayerProperties()", retVal)) { return false; } - for (uint32_t uiIndx = 0; uiIndx < NumInstanceLayerProps; uiIndx++) + if (!ParseLayerProperties( InstanceLayerProps, m_InstanceLayers )) { - const VkLayerProperties& rOneItem = m_InstanceLayerProps[uiIndx]; - LOGI(" %d: %s => %s", uiIndx, rOneItem.layerName, rOneItem.description); + LOGE( "Required Vulkan Instance Layer(s) missing." ); + return false; + } - // Need to check that we found specific extensions - if(!strcmp("VK_LAYER_KHRONOS_validation", rOneItem.layerName)) - { - m_LayerKhronosValidationAvailable = true; - } + auto* validationLayer = m_InstanceLayers.GetExtension( "VK_LAYER_KHRONOS_validation" ); + if (validationLayer && validationLayer->Status == VulkanExtensionStatus::eLoaded) + { + m_LayerKhronosValidationAvailable = true; } } } @@ -1037,8 +1158,8 @@ bool Vulkan::InitInstanceExtensions() // ******************************** { uint32_t NumInstanceExtensionProps = 0; - RetVal = vkEnumerateInstanceExtensionProperties(nullptr, &NumInstanceExtensionProps, nullptr); - if (!CheckVkError("vkEnumerateInstanceExtensionProperties(nullptr)", RetVal)) + retVal = vkEnumerateInstanceExtensionProperties(nullptr, &NumInstanceExtensionProps, nullptr); + if (!CheckVkError("vkEnumerateInstanceExtensionProperties(nullptr)", retVal)) { return false; } @@ -1052,38 +1173,42 @@ bool Vulkan::InitInstanceExtensions() InstanceExtensionProps.resize(NumInstanceExtensionProps); // ... then read the structures from the driver - RetVal = vkEnumerateInstanceExtensionProperties(nullptr, &NumInstanceExtensionProps, InstanceExtensionProps.data()); - if (!CheckVkError("vkEnumerateInstanceExtensionProperties()", RetVal)) + retVal = vkEnumerateInstanceExtensionProperties(nullptr, &NumInstanceExtensionProps, InstanceExtensionProps.data()); + if (!CheckVkError("vkEnumerateInstanceExtensionProperties()", retVal)) { return false; } } // Now add in any instance extensions exposed by layers (that we care about) - if (m_LayerKhronosValidationAvailable) + for (auto& layer : m_InstanceLayers.m_Extensions) { - uint32_t NumValidationLayerInstanceExtensionProps = 0; - - RetVal = vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &NumValidationLayerInstanceExtensionProps, nullptr); - if (!CheckVkError("vkEnumerateInstanceExtensionProperties(VK_LAYER_KHRONOS_validation)", RetVal)) - { - return false; - } - - LOGI("Found %d Vulkan VK_LAYER_KHRONOS_validation Instance Extension Properties", NumValidationLayerInstanceExtensionProps); - if (NumValidationLayerInstanceExtensionProps > 0) + if (layer.second->Status == VulkanExtensionStatus::eLoaded) { - // Allocate memory for the structures... - InstanceExtensionProps.resize(NumInstanceExtensionProps + NumValidationLayerInstanceExtensionProps); + uint32_t NumLayerInstanceExtensionProps = 0; + const char* const pLayerName = layer.first.c_str(); - // ... then read the structures from the driver - RetVal = vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &NumValidationLayerInstanceExtensionProps, &InstanceExtensionProps[NumInstanceExtensionProps]); - if (!CheckVkError("vkEnumerateInstanceExtensionProperties(VK_LAYER_KHRONOS_validation)", RetVal)) + retVal = vkEnumerateInstanceExtensionProperties( pLayerName, &NumLayerInstanceExtensionProps, nullptr ); + if (!CheckVkError( "vkEnumerateInstanceExtensionProperties(VK_LAYER_KHRONOS_validation)", retVal )) { return false; } + + LOGI( "Found %d Vulkan %s Instance Extension Properties", NumLayerInstanceExtensionProps, pLayerName ); + if (NumLayerInstanceExtensionProps > 0) + { + // Allocate memory for the structures... + InstanceExtensionProps.resize( NumInstanceExtensionProps + NumLayerInstanceExtensionProps ); + + // ... then read the structures from the driver + retVal = vkEnumerateInstanceExtensionProperties( pLayerName, &NumLayerInstanceExtensionProps, &InstanceExtensionProps[NumInstanceExtensionProps] ); + if (!CheckVkError( "vkEnumerateInstanceExtensionProperties(VK_LAYER_KHRONOS_validation)", retVal )) + { + return false; + } + } + NumInstanceExtensionProps += NumLayerInstanceExtensionProps; } - NumInstanceExtensionProps += NumValidationLayerInstanceExtensionProps; } if (!ParseExtensionProperties( InstanceExtensionProps, m_InstanceExtensions )) @@ -1093,126 +1218,6 @@ bool Vulkan::InitInstanceExtensions() } } - // Add layers we want for debugging -#if defined(USES_VULKAN_DEBUG_LAYERS) - if (gEnableValidation && m_LayerKhronosValidationAvailable) - { - m_InstanceLayerNames.push_back( "VK_LAYER_KHRONOS_validation" ); - } -#endif // USES_VULKAN_DEBUG_LAYERS - - { - // Sort alphabetically (can search using std::lower_bound) - std::sort(std::begin(m_InstanceLayerNames), std::end(m_InstanceLayerNames), [](auto a, auto b) { return strcmp(a, b) < 0; }); - size_t outIdx = 0; - for (size_t inIdx = 0; inIdx < m_InstanceLayerNames.size(); ++inIdx) - if (inIdx == 0 || strcmp(m_InstanceLayerNames[outIdx-1], m_InstanceLayerNames[inIdx]) != 0) - ++outIdx; - - // Remove duplicates - m_InstanceLayerNames.resize(outIdx); - } - return true; -} - -//----------------------------------------------------------------------------- -bool Vulkan::GetPhysicalDevices() -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - - // Query number of physical devices available - RetVal = vkEnumeratePhysicalDevices(m_VulkanInstance, &m_VulkanGpuCount, nullptr); - if (!CheckVkError("vkEnumeratePhysicalDevices()", RetVal)) - { - return false; - } - - if (m_VulkanGpuCount == 0) - { - LOGE("Could not find a Vulkan GPU!!!!"); - return false; - } - - LOGI("Found %d Vulkan GPU[s]", m_VulkanGpuCount); - - // Allocate space the the correct number of devices, before requesting their data - std::vector pDevices; - pDevices.resize(m_VulkanGpuCount, VK_NULL_HANDLE); - - RetVal = vkEnumeratePhysicalDevices(m_VulkanInstance, &m_VulkanGpuCount, pDevices.data()); - if (!CheckVkError("vkEnumeratePhysicalDevices()", RetVal)) - { - return false; - } - - // Query and display the available GPU devices and determine the 'best' GPU to use. - { - std::vector DeviceFeatures; - std::vector DeviceProperties; - DeviceFeatures.reserve( pDevices.size() ); - DeviceProperties.reserve( pDevices.size() ); - - for (VkPhysicalDevice device : pDevices) - { - QueryPhysicalDeviceFeatures( device, DeviceFeatures.emplace_back() ); - QueryPhysicalDeviceProperties( device, DeviceFeatures.back(), DeviceProperties.emplace_back() ); - } - - DumpDeviceInfo( DeviceFeatures, DeviceProperties ); - - if (gPhysicalDevice >= 0) - { - LOGI( "Forcing physical device: (config gPhysicalDevice=%d).\n", gPhysicalDevice ); - - if (gPhysicalDevice >= DeviceProperties.size()) - { - LOGE( "Forced physical device out of range. Reverting to automatic selection.\n" ); - m_VulkanGpuIdx = (uint32_t)GetBestVulkanPhysicalDeviceId( DeviceProperties ); - } - else - { - m_VulkanGpuIdx = gPhysicalDevice; - } - } - - m_VulkanGpuIdx = (uint32_t) GetBestVulkanPhysicalDeviceId( DeviceProperties ); - - LOGI("Using Vulkan Device (GPU): %d \"%s\"", m_VulkanGpuIdx, DeviceProperties[m_VulkanGpuIdx].Base.properties.deviceName); - } - - m_VulkanGpu = pDevices[m_VulkanGpuIdx]; - - // ******************************** - // Debug/Validation/Whatever Layers and extensions - // ******************************** - if (!InitDeviceExtensions()) - { - return false; - } - - // Query the available features and properties for this device. - PhysicalDeviceFeatures AvailableFeatures{}; - QueryPhysicalDeviceFeatures( m_VulkanGpu, AvailableFeatures ); - QueryPhysicalDeviceProperties( m_VulkanGpu, AvailableFeatures, m_VulkanGpuProperties ); - - // Get Memory information and properties - this is required later, when we begin - // allocating buffers to store data. - if (m_ExtKhrGetPhysicalDeviceProperties2->Status == VulkanExtensionStatus::eLoaded) - m_ExtKhrGetPhysicalDeviceProperties2->m_vkGetPhysicalDeviceMemoryProperties2KHR(m_VulkanGpu, &m_PhysicalDeviceMemoryProperties); - else - vkGetPhysicalDeviceMemoryProperties(m_VulkanGpu, &m_PhysicalDeviceMemoryProperties.memoryProperties); - - // VK_QCOM_render_pass_transform had 2 versions, switch to 'legacy' if on an older driver verson. - m_ExtRenderPassTransformLegacy = (m_ExtRenderPassTransformAvailable && (m_VulkanGpuProperties.Base.properties.driverVersion < VK_MAKE_VERSION(512, 467, 0))); - // Internal builds have version 366 hardcoded, use the supported Vulkan version instead (the switch to legacy before 1.1.126.0 may be incorrect, 1.1.124.0 cartainly was legacy though and 1.1.128 is not!) - m_ExtRenderPassTransformLegacy = m_ExtRenderPassTransformLegacy && ((m_VulkanGpuProperties.Base.properties.driverVersion != VK_MAKE_VERSION(512, 366, 0) || m_VulkanGpuProperties.Base.properties.apiVersion < VK_MAKE_VERSION(1, 1, 126))); - - if( m_ExtRenderPassTransformLegacy ) - { - LOGI( "VK_QCOM_render_pass_transform legacy values" ); - } - return true; } @@ -1225,11 +1230,12 @@ bool Vulkan::GetDataGraphProcessingEngine() return true; } - // Force-enable the extension until it's turned into public (we check "Ext_VK_ARM_data_graph->AvailableFeatures.dataGraph" for HW support). + // If Ext_VK_ARM_data_graph->AvailableFeatures.dataGraph is supported, force graph pipeline support here while that + // isn't fully supported publicly by the driver #if defined(OS_ANDROID) { - auto* Ext_VK_ARM_tensors = static_cast(m_DeviceExtensions.GetExtension(VK_ARM_TENSORS_EXTENSION_NAME)); - auto* Ext_VK_ARM_data_graph = static_cast(m_DeviceExtensions.GetExtension(VK_ARM_DATA_GRAPH_EXTENSION_NAME)); + auto* Ext_VK_ARM_tensors = static_cast(m_DeviceExtensions.GetExtension(VK_ARM_TENSORS_EXTENSION_NAME)); + auto* Ext_VK_ARM_data_graph = static_cast(m_DeviceExtensions.GetExtension(VK_ARM_DATA_GRAPH_EXTENSION_NAME)); auto fpGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(GetVulkanInstance(), "vkGetDeviceProcAddr"); if (Ext_VK_ARM_tensors && Ext_VK_ARM_data_graph @@ -1253,20 +1259,14 @@ bool Vulkan::GetDataGraphProcessingEngine() LOGI("*** DATA GRAPH PROCESSING ENGINE ***"); LOGI("************************************"); - const auto& data_graph_extension = GetExtension(); - if (!data_graph_extension) - { - return false; - } - uint32_t propCount = 0; - data_graph_extension->m_vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM( + vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM( m_VulkanGpu, m_VulkanQueues[Vulkan::eDataGraphQueue].QueueFamilyIndex, &propCount, nullptr); std::vector dataGraphProps = std::vector(propCount); - data_graph_extension->m_vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM( + vkGetPhysicalDeviceQueueFamilyDataGraphPropertiesARM( m_VulkanGpu, m_VulkanQueues[Vulkan::eDataGraphQueue].QueueFamilyIndex, &propCount, @@ -1301,8 +1301,7 @@ bool Vulkan::GetDataGraphProcessingEngine() LOGI("Checking for Tensor Storage Format Support"); { - if(const auto& physical_device_properties2 = GetExtension(); - physical_device_properties2 && physical_device_properties2->m_vkGetPhysicalDeviceFormatProperties2KHR) + if(HasLoadedVulkanDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { VkTensorFormatPropertiesARM tensorFmtProps = {}; VkFormatProperties2 f32Props = {}; @@ -1311,7 +1310,7 @@ bool Vulkan::GetDataGraphProcessingEngine() f32Props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; f32Props.pNext = &tensorFmtProps; - physical_device_properties2->m_vkGetPhysicalDeviceFormatProperties2KHR(m_VulkanGpu, VK_FORMAT_R32_SFLOAT, &f32Props); + vkGetPhysicalDeviceFormatProperties2KHR(m_VulkanGpu, VK_FORMAT_R32_SFLOAT, &f32Props); LOGI("*** \t\t\ttensorFmtProps.linearTilingTensorFeatures: %d", static_cast(tensorFmtProps.linearTilingTensorFeatures)); LOGI("*** \t\t\ttensorFmtProps.optimalTilingTensorFeatures: %d", static_cast(tensorFmtProps.optimalTilingTensorFeatures)); @@ -1321,7 +1320,7 @@ bool Vulkan::GetDataGraphProcessingEngine() } } } - + LOGI("Ensuring Engine Synchronization Support"); { VkPhysicalDeviceQueueFamilyDataGraphProcessingEngineInfoARM info = {}; @@ -1331,7 +1330,7 @@ bool Vulkan::GetDataGraphProcessingEngine() VkQueueFamilyDataGraphProcessingEnginePropertiesARM engineProps = {}; engineProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_QUEUE_FAMILY_DATA_GRAPH_PROCESSING_ENGINE_INFO_ARM; - data_graph_extension->m_vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM(m_VulkanGpu, &info, &engineProps); + vkGetPhysicalDeviceQueueFamilyDataGraphProcessingEnginePropertiesARM(m_VulkanGpu, &info, &engineProps); // NOTE: These are only needed if you are using external objects (memory, synchronization, etc.). For this sample we only // care about Vulkan primitives, but if you are using e.g. Android buffers, you should ensure they are supported first. @@ -1354,19 +1353,133 @@ bool Vulkan::GetDataGraphProcessingEngine() return true; } +//----------------------------------------------------------------------------- +bool Vulkan::GetPhysicalDevices() +//----------------------------------------------------------------------------- +{ + VkResult retVal = VK_SUCCESS; + + // Query number of physical devices available + retVal = vkEnumeratePhysicalDevices(m_VulkanInstance, &m_VulkanGpuCount, nullptr); + if (!CheckVkError("vkEnumeratePhysicalDevices()", retVal)) + { + return false; + } + + if (m_VulkanGpuCount == 0) + { + LOGE("Could not find a Vulkan GPU!!!!"); + return false; + } + + LOGI("Found %d Vulkan GPU[s]", m_VulkanGpuCount); + + // Allocate space the the correct number of devices, before requesting their data + std::vector pDevices; + pDevices.resize(m_VulkanGpuCount, VK_NULL_HANDLE); + + retVal = vkEnumeratePhysicalDevices(m_VulkanInstance, &m_VulkanGpuCount, pDevices.data()); + if (!CheckVkError("vkEnumeratePhysicalDevices()", retVal)) + { + return false; + } + + // Query and display the available GPU devices and determine the 'best' GPU to use. + { + std::vector DeviceFeatures; + std::vector DeviceProperties; + DeviceFeatures.reserve( pDevices.size() ); + DeviceProperties.reserve( pDevices.size() ); + + for (VkPhysicalDevice device : pDevices) + { + QueryPhysicalDeviceFeatures( device, DeviceFeatures.emplace_back() ); + QueryPhysicalDeviceProperties( device, DeviceFeatures.back(), DeviceProperties.emplace_back() ); + } + + DumpDeviceInfo( DeviceFeatures, DeviceProperties ); + + if (gPhysicalDevice >= 0) + { + LOGI( "Forcing physical device: (config gPhysicalDevice=%d).\n", gPhysicalDevice ); + + if (gPhysicalDevice >= DeviceProperties.size()) + { + LOGE( "Forced physical device out of range. Reverting to automatic selection.\n" ); + m_VulkanGpuIdx = (uint32_t)GetBestVulkanPhysicalDeviceId( DeviceProperties ); + } + else + { + m_VulkanGpuIdx = gPhysicalDevice; + } + } + + m_VulkanGpuIdx = (uint32_t) GetBestVulkanPhysicalDeviceId( DeviceProperties ); + + LOGI("Using Vulkan Device (GPU): %d \"%s\"", m_VulkanGpuIdx, DeviceProperties[m_VulkanGpuIdx].Base.properties.deviceName); + } + + m_VulkanGpu = pDevices[m_VulkanGpuIdx]; + + // ******************************** + // Debug/Validation/Whatever Layers and extensions + // ******************************** + if (!InitDeviceExtensions()) + { + return false; + } + + // Query the available features and properties for this device. + PhysicalDeviceFeatures AvailableFeatures{}; + QueryPhysicalDeviceFeatures( m_VulkanGpu, AvailableFeatures ); + QueryPhysicalDeviceProperties( m_VulkanGpu, AvailableFeatures, m_VulkanGpuProperties ); + + // Get Memory information and properties - this is required later, when we begin + // allocating buffers to store data. + if (m_ExtKhrGetPhysicalDeviceProperties2->Status == VulkanExtensionStatus::eLoaded) + m_ExtKhrGetPhysicalDeviceProperties2->m_vkGetPhysicalDeviceMemoryProperties2KHR(m_VulkanGpu, &m_PhysicalDeviceMemoryProperties); + else + vkGetPhysicalDeviceMemoryProperties(m_VulkanGpu, &m_PhysicalDeviceMemoryProperties.memoryProperties); + + // Memory manager not available yet so can't use MemoryManager::GetHeapWithHeapFlags + // Go directly to Vulkan api for memry heap data + { + const auto& memoryProperties = m_PhysicalDeviceMemoryProperties.memoryProperties; + LOGI( "VkPhysicalDeviceMemoryProperties:" ); + LOGI( " VkMemoryType" ); + for (auto i = 0; i < memoryProperties.memoryTypeCount; ++i) + LOGI( " %d: propertyFlags 0x%x heapIndex %d", i, memoryProperties.memoryTypes[i].propertyFlags, memoryProperties.memoryTypes[i].heapIndex ); + LOGI( " VkMemoryHeap" ); + for (auto i = 0; i < memoryProperties.memoryHeapCount; ++i) + LOGI( " %d: size %zu VkMemoryHeapFlags 0x%x", i, (size_t)memoryProperties.memoryHeaps[i].size, memoryProperties.memoryHeaps[i].flags ); + } + + // VK_QCOM_render_pass_transform had 2 versions, switch to 'legacy' if on an older driver verson. + m_ExtRenderPassTransformLegacy = (m_ExtRenderPassTransformAvailable && (m_VulkanGpuProperties.Base.properties.driverVersion < VK_MAKE_VERSION(512, 467, 0))); + // Internal builds have version 366 hardcoded, use the supported Vulkan version instead (the switch to legacy before 1.1.126.0 may be incorrect, 1.1.124.0 cartainly was legacy though and 1.1.128 is not!) + m_ExtRenderPassTransformLegacy = m_ExtRenderPassTransformLegacy && ((m_VulkanGpuProperties.Base.properties.driverVersion != VK_MAKE_VERSION(512, 366, 0) || m_VulkanGpuProperties.Base.properties.apiVersion < VK_MAKE_VERSION(1, 1, 126))); + + if( m_ExtRenderPassTransformLegacy ) + { + LOGI( "VK_QCOM_render_pass_transform legacy values" ); + } + + return true; +} + //----------------------------------------------------------------------------- bool Vulkan::InitDeviceExtensions() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Look for Device Validation Layers // ******************************** { uint32_t NumDeviceLayerProps = 0; - RetVal = vkEnumerateDeviceLayerProperties(m_VulkanGpu, &NumDeviceLayerProps, nullptr); - if (!CheckVkError("vkEnumerateDeviceLayerProperties(nullptr)", RetVal)) + retVal = vkEnumerateDeviceLayerProperties(m_VulkanGpu, &NumDeviceLayerProps, nullptr); + if (!CheckVkError("vkEnumerateDeviceLayerProperties(nullptr)", retVal)) { return false; } @@ -1379,8 +1492,8 @@ bool Vulkan::InitDeviceExtensions() DeviceLayerProps.resize(NumDeviceLayerProps, {}); // ... then read the structures from the driver - RetVal = vkEnumerateDeviceLayerProperties(m_VulkanGpu, &NumDeviceLayerProps, DeviceLayerProps.data()); - if (!CheckVkError("vkEnumerateDeviceLayerProperties()", RetVal)) + retVal = vkEnumerateDeviceLayerProperties(m_VulkanGpu, &NumDeviceLayerProps, DeviceLayerProps.data()); + if (!CheckVkError("vkEnumerateDeviceLayerProperties()", retVal)) { return false; } @@ -1398,8 +1511,8 @@ bool Vulkan::InitDeviceExtensions() // ******************************** { uint32_t NumDeviceExtensionProps = 0; - RetVal = vkEnumerateDeviceExtensionProperties(m_VulkanGpu, nullptr, &NumDeviceExtensionProps, nullptr); - if (!CheckVkError("vkEnumerateDeviceExtensionProperties(nullptr)", RetVal)) + retVal = vkEnumerateDeviceExtensionProperties(m_VulkanGpu, nullptr, &NumDeviceExtensionProps, nullptr); + if (!CheckVkError("vkEnumerateDeviceExtensionProperties(nullptr)", retVal)) { return false; } @@ -1413,8 +1526,8 @@ bool Vulkan::InitDeviceExtensions() DeviceExtensionProps.resize( NumDeviceExtensionProps ); // ... then read the structures from the driver - RetVal = vkEnumerateDeviceExtensionProperties(m_VulkanGpu, nullptr, &NumDeviceExtensionProps, DeviceExtensionProps.data()); - if (!CheckVkError("vkEnumerateDeviceExtensionProperties()", RetVal)) + retVal = vkEnumerateDeviceExtensionProperties(m_VulkanGpu, nullptr, &NumDeviceExtensionProps, DeviceExtensionProps.data()); + if (!CheckVkError("vkEnumerateDeviceExtensionProperties()", retVal)) { return false; } @@ -1468,26 +1581,6 @@ bool Vulkan::InitDeviceExtensions() #endif // defined (ANDROID_HARDWARE_BUFFER_SUPPORT) #endif // defined (OS_ANDROID) - - // Create a vector of all the extensions (names) we are going to load as part of InitDevice() - std::vector DeviceExtensionNames; - for (const auto& e : m_DeviceExtensions.m_Extensions) - { - if (e.second->Status == VulkanExtensionStatus::eLoaded) - DeviceExtensionNames.push_back(e.first.c_str()); - } - - // Sort alphabetically (so we can search using std::lower_bound). Also remove duplicates. - { - std::sort(std::begin(DeviceExtensionNames), std::end(DeviceExtensionNames), [](auto a, auto b) { return strcmp(a, b) < 0; }); - // Remove duplicates - auto endIt = std::unique(std::begin(DeviceExtensionNames), std::end(DeviceExtensionNames), [](auto a, auto b) { return strcmp(a, b) == 0; }); - DeviceExtensionNames.resize(endIt - std::begin(DeviceExtensionNames)); - } - - for(const auto& n: DeviceExtensionNames) - m_DeviceExtensions.m_Extensions[n]->Status = VulkanExtensionStatus::eLoaded; - return true; } @@ -1495,7 +1588,7 @@ bool Vulkan::InitDeviceExtensions() bool Vulkan::InitQueue() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // Before we create our main Vulkan device, we must ensure our physical device // has queue families which can perform the actions we require. For this, we request @@ -1546,7 +1639,7 @@ bool Vulkan::InitQueue() { LOGI(" %d: Does NOT Support TRANSFER", uiIndx); } - + if (pOneItem->queueFlags & VK_QUEUE_DATA_GRAPH_BIT_ARM) { LOGI(" %d: Supports DATA GRAPH", uiIndx); @@ -1566,6 +1659,7 @@ bool Vulkan::InitQueue() } } + return true; } @@ -1573,56 +1667,7 @@ bool Vulkan::InitQueue() bool Vulkan::InitInstanceFunctions() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; - -#if defined (OS_WINDOWS) - // TODO: How do we define functions we want? - - // These functions are part of the instance so we must query for them using - // "vkGetInstanceProcAddr" once the instance has been created - fpGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfaceSupportKHR"); - if (fpGetPhysicalDeviceSurfaceSupportKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfaceSupportKHR"); - } - - fpGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - if (fpGetPhysicalDeviceSurfaceCapabilitiesKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - } - - fpGetPhysicalDeviceSurfaceCapabilities2KHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); - if (fpGetPhysicalDeviceSurfaceCapabilities2KHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfaceCapabilities2KHR"); - } - - fpGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfaceFormatsKHR"); - if (fpGetPhysicalDeviceSurfaceFormatsKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfaceFormatsKHR"); - } - - fpGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfacePresentModesKHR"); - if (fpGetPhysicalDeviceSurfacePresentModesKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfacePresentModesKHR"); - } - - fpGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetSwapchainImagesKHR"); - if (fpGetSwapchainImagesKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetSwapchainImagesKHR"); - } -#endif // defined (OS_WINDOWS) - - fpGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) vkGetInstanceProcAddr( m_VulkanInstance, "vkGetPhysicalDeviceMemoryProperties2" ); - if (fpGetPhysicalDeviceMemoryProperties2 == nullptr) - { - LOGE( "Unable to get function pointer from instance: vkGetPhysicalDeviceMemoryProperties2" ); - } - LOGI( "Initialized function pointer from instance: vkGetPhysicalDeviceMemoryProperties2" ); + VkResult retVal = VK_SUCCESS; #if defined (OS_ANDROID) #if __ANDROID_API__ < 29 @@ -1643,24 +1688,6 @@ bool Vulkan::InitInstanceFunctions() #endif // defined (OS_ANDROID) - fpGetPhysicalDeviceSurfaceCapabilities2KHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); - if (fpGetPhysicalDeviceSurfaceCapabilities2KHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceSurfaceCapabilities2KHR"); - } - - fpGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceProperties2"); - if (fpGetPhysicalDeviceProperties2 == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceProperties2"); - } - - fpGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(m_VulkanInstance, "vkGetPhysicalDeviceFeatures2"); - if (fpGetPhysicalDeviceFeatures2 == nullptr) - { - LOGE("Unable to get function pointer from instance: vkGetPhysicalDeviceProperties2"); - } - // Call any registered extensions for instance funtion pointer lookups. { VulkanInstanceFunctionPointerLookup fn { m_VulkanInstance }; @@ -1694,7 +1721,7 @@ void Vulkan::DestroyDebugCallback() bool Vulkan::InitSurface() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Create surface @@ -1707,18 +1734,22 @@ bool Vulkan::InitSurface() SurfaceInfoStruct.flags = 0; SurfaceInfoStruct.hinstance = m_hInstance; SurfaceInfoStruct.hwnd = m_hWnd; - RetVal = vkCreateWin32SurfaceKHR(m_VulkanInstance, &SurfaceInfoStruct, nullptr, &m_VulkanSurface); + retVal = vkCreateWin32SurfaceKHR(m_VulkanInstance, &SurfaceInfoStruct, nullptr, &m_VulkanSurface); #elif defined (OS_ANDROID) VkAndroidSurfaceCreateInfoKHR SurfaceInfoStruct {VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR}; SurfaceInfoStruct.flags = 0; SurfaceInfoStruct.window = m_pAndroidWindow; - RetVal = vkCreateAndroidSurfaceKHR(m_VulkanInstance, &SurfaceInfoStruct, nullptr, &m_VulkanSurface); + retVal = vkCreateAndroidSurfaceKHR(m_VulkanInstance, &SurfaceInfoStruct, nullptr, &m_VulkanSurface); -#endif // defined (OS_WINDOWS|OS_ANDROID) +#elif defined (OS_LINUX) + + retVal = glfwCreateWindowSurface(m_VulkanInstance, m_pGlfwWindow, nullptr, &m_VulkanSurface); - if (!CheckVkError("vkCreateXXXXSurfaceKHR()", RetVal)) +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) + + if (!CheckVkError("vkCreateXXXXSurfaceKHR()", retVal)) { return false; } @@ -1796,23 +1827,18 @@ bool Vulkan::InitSurface() } //----------------------------------------------------------------------------- -bool Vulkan::InitCompute() +bool Vulkan::InitDataGraph() //----------------------------------------------------------------------------- { VkResult RetVal = VK_SUCCESS; - // See if the present queue supports compute. - if (m_pVulkanQueueProps[m_VulkanQueues[eGraphicsQueue].QueueFamilyIndex].queueFlags & VK_QUEUE_COMPUTE_BIT) - { - m_VulkanGraphicsQueueSupportsCompute = true; - } - - // Look for a queue that supports Compute (but not graphics) + // Look for a queue that supports data graph (note that this queue, if present, can ONLY support data graph operations) for (int i = 0; i < m_pVulkanQueueProps.size(); ++i) { - if ((m_pVulkanQueueProps[i].queueFlags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT)) == VK_QUEUE_COMPUTE_BIT) + if ((m_pVulkanQueueProps[i].queueFlags & VK_QUEUE_DATA_GRAPH_BIT_ARM) == VK_QUEUE_DATA_GRAPH_BIT_ARM) { - m_VulkanQueues[eComputeQueue].QueueFamilyIndex = i; + m_VulkanQueues[eDataGraphQueue].QueueFamilyIndex = i; + m_VulkanGraphicsQueueSupportsDataGraph = true; break; } } @@ -1821,18 +1847,23 @@ bool Vulkan::InitCompute() } //----------------------------------------------------------------------------- -bool Vulkan::InitDataGraph() +bool Vulkan::InitCompute() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; - // Look for a queue that supports data graph (note that this queue, if present, can ONLY support data graph operations) + // See if the present queue supports compute. + if (m_pVulkanQueueProps[m_VulkanQueues[eGraphicsQueue].QueueFamilyIndex].queueFlags & VK_QUEUE_COMPUTE_BIT) + { + m_VulkanGraphicsQueueSupportsCompute = true; + } + + // Look for a queue that supports Compute (but not graphics) for (int i = 0; i < m_pVulkanQueueProps.size(); ++i) { - if ((m_pVulkanQueueProps[i].queueFlags & VK_QUEUE_DATA_GRAPH_BIT_ARM) == VK_QUEUE_DATA_GRAPH_BIT_ARM) + if ((m_pVulkanQueueProps[i].queueFlags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT)) == VK_QUEUE_COMPUTE_BIT) { - m_VulkanQueues[eDataGraphQueue].QueueFamilyIndex = i; - m_VulkanGraphicsQueueSupportsDataGraph = true; + m_VulkanQueues[eComputeQueue].QueueFamilyIndex = i; break; } } @@ -1844,7 +1875,7 @@ bool Vulkan::InitDataGraph() bool Vulkan::InitDevice() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Query the available device features @@ -1929,7 +1960,7 @@ bool Vulkan::InitDevice() std::vector DeviceExtensionNames; DeviceExtensionNames.reserve(m_DeviceExtensions.m_Extensions.size()); for (const auto& e : m_DeviceExtensions.m_Extensions) - if (e.second->Status == VulkanExtensionStatus::eLoaded) + if (e.second->Status == VulkanExtensionStatus::eLoaded && e.second->LoadMode != VulkanExtensionLoadMode::eSkipRequest) DeviceExtensionNames.push_back(e.first.c_str()); if (m_VulkanApiVersion >= VK_MAKE_VERSION( 1, 1, 0 )) @@ -1977,13 +2008,13 @@ bool Vulkan::InitDevice() LOGI(" %d: %s", Which, DeviceInfoStruct.ppEnabledExtensionNames[Which]); } - RetVal = vkCreateDevice(m_VulkanGpu, &DeviceInfoStruct, nullptr, &m_VulkanDevice); - if (!CheckVkError("vkCreateDevice()", RetVal)) + retVal = vkCreateDevice(m_VulkanGpu, &DeviceInfoStruct, nullptr, &m_VulkanDevice); + if (!CheckVkError("vkCreateDevice()", retVal)) { return false; } - LOGI("Vulkan Device Created!"); + volkLoadDevice(m_VulkanDevice); // Pop the extensions back off the VkPhysicalDeviceFeatures2 chain (most registered extensions will do nothing here) m_DeviceCreateInfoExtensions.PopExtensions(&DeviceInfoStruct); @@ -2009,27 +2040,15 @@ bool Vulkan::InitDevice() m_VulkanDeviceFunctionPointerLookupExtensions.PushExtensions( &fn ); } - - // VK_KHR_swapchain functions -#if defined (OS_WINDOWS) - fpCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)fpGetDeviceProcAddr(m_VulkanDevice, "vkCreateSwapchainKHR"); - if (fpCreateSwapchainKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkCreateSwapchainKHR"); - } - - fpDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)fpGetDeviceProcAddr(m_VulkanDevice, "vkDestroySwapchainKHR"); - if (fpDestroySwapchainKHR == nullptr) - { - LOGE("Unable to get function pointer from instance: vkDestroySwapchainKHR"); - } - - fpGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)fpGetDeviceProcAddr(m_VulkanDevice, "vkGetSwapchainImagesKHR"); - if (fpGetSwapchainImagesKHR == nullptr) + // ******************************** + // Call the PostLoad for all device extensions. + // This is done after the LookupFunctionPointers calls (where applicable) but maybe should go before? + // ******************************** + for (auto& extension : m_DeviceExtensions.m_Extensions) { - LOGE("Unable to get function pointer from instance: vkGetSwapchainImagesKHR"); + if (extension.second->Status == VulkanExtensionStatus::eLoaded) + extension.second->PostLoad(); } -#endif // defined (OS_WINDOWS) // ******************************** // Create the Device Queue @@ -2043,7 +2062,7 @@ bool Vulkan::InitDevice() { vkGetDeviceQueue(m_VulkanDevice, m_VulkanQueues[eComputeQueue].QueueFamilyIndex, 0, &m_VulkanQueues[eComputeQueue].Queue); } - + // ******************************** // Create the Data Graph Device Queue // ******************************** @@ -2056,8 +2075,8 @@ bool Vulkan::InitDevice() // Get Supported Formats // ******************************** uint32_t NumFormats; - RetVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_VulkanGpu, m_VulkanSurface, &NumFormats, nullptr); - if (!CheckVkError("vkGetPhysicalDeviceSurfaceFormatsKHR()", RetVal)) + retVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_VulkanGpu, m_VulkanSurface, &NumFormats, nullptr); + if (!CheckVkError("vkGetPhysicalDeviceSurfaceFormatsKHR()", retVal)) { return false; } @@ -2073,8 +2092,8 @@ bool Vulkan::InitDevice() vkSurfaceFormats.resize( NumFormats ); // ... then read the structures from the driver - RetVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_VulkanGpu, m_VulkanSurface, &NumFormats, vkSurfaceFormats.data()); - if (!CheckVkError("vkGetPhysicalDeviceSurfaceFormatsKHR()", RetVal)) + retVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceFormatsKHR(m_VulkanGpu, m_VulkanSurface, &NumFormats, vkSurfaceFormats.data()); + if (!CheckVkError("vkGetPhysicalDeviceSurfaceFormatsKHR()", retVal)) { return false; } @@ -2104,6 +2123,16 @@ bool Vulkan::InitDevice() { // Just take the first one by default m_SurfaceFormat = m_SurfaceFormats[0].format; + + // See if we have a srgb format (and use it if we do) + for(const auto& f: m_SurfaceFormats) + { + if (FormatIsSrgb(f.format)) + { + m_SurfaceFormat = f.format; + break; + } + } } // Taking the first colorspace @@ -2117,15 +2146,15 @@ bool Vulkan::InitDevice() bool Vulkan::InitSyncElements() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // We have semaphores for rendering and backbuffer signalling. VkSemaphoreCreateInfo semCreateInfo {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; semCreateInfo.flags = 0; m_RenderCompleteSemaphore = VK_NULL_HANDLE; - RetVal = vkCreateSemaphore(m_VulkanDevice, &semCreateInfo, nullptr, &m_RenderCompleteSemaphore); - if (!CheckVkError("vkCreateSemaphore()", RetVal)) + retVal = vkCreateSemaphore(m_VulkanDevice, &semCreateInfo, nullptr, &m_RenderCompleteSemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) { return false; } @@ -2137,19 +2166,21 @@ bool Vulkan::InitSyncElements() bool Vulkan::InitCommandPools() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Create the command pool // ******************************** LOGI("Creating Vulkan Command Pool..."); - VkCommandPoolCreateInfo CmdPoolInfoStruct {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; - CmdPoolInfoStruct.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - CmdPoolInfoStruct.queueFamilyIndex = m_VulkanQueues[eGraphicsQueue].QueueFamilyIndex; + const VkCommandPoolCreateInfo CmdPoolInfoStruct{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = (uint32_t)m_VulkanQueues[eGraphicsQueue].QueueFamilyIndex + }; - RetVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eGraphicsQueue].CommandPool); - if (!CheckVkError("vkCreateCommandPool()", RetVal)) + retVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eGraphicsQueue].CommandPool); + if (!CheckVkError("vkCreateCommandPool()", retVal)) { return false; } @@ -2157,12 +2188,14 @@ bool Vulkan::InitCommandPools() // Allocate a command pool for (async) Compute. if (m_VulkanQueues[eComputeQueue].Queue) { - VkCommandPoolCreateInfo CmdPoolInfoStruct {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; - CmdPoolInfoStruct.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - CmdPoolInfoStruct.queueFamilyIndex = m_VulkanQueues[eComputeQueue].QueueFamilyIndex; + const VkCommandPoolCreateInfo CmdPoolInfoStruct{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = (uint32_t)m_VulkanQueues[eComputeQueue].QueueFamilyIndex + }; - RetVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eComputeQueue].CommandPool); - if (!CheckVkError("vkCreateCommandPool()", RetVal)) + retVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eComputeQueue].CommandPool); + if (!CheckVkError("vkCreateCommandPool()", retVal)) { return false; } @@ -2177,13 +2210,13 @@ bool Vulkan::InitCommandPools() engineInfo.processingEngineCount = 1; engineInfo.pProcessingEngines = &m_VulkanDataGraphProcessingEngine; - VkCommandPoolCreateInfo CmdPoolInfoStruct {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; + VkCommandPoolCreateInfo CmdPoolInfoStruct{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; CmdPoolInfoStruct.pNext = &engineInfo; CmdPoolInfoStruct.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; CmdPoolInfoStruct.queueFamilyIndex = m_VulkanQueues[eDataGraphQueue].QueueFamilyIndex; - RetVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eDataGraphQueue].CommandPool); - if (!CheckVkError("vkCreateCommandPool()", RetVal)) + retVal = vkCreateCommandPool(m_VulkanDevice, &CmdPoolInfoStruct, nullptr, &m_VulkanQueues[eDataGraphQueue].CommandPool); + if (!CheckVkError("vkCreateCommandPool()", retVal)) { return false; } @@ -2205,8 +2238,8 @@ bool Vulkan::InitPipelineCache() { assert(m_PipelineCache == VK_NULL_HANDLE); VkPipelineCacheCreateInfo CreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; - auto RetVal = vkCreatePipelineCache( m_VulkanDevice, &CreateInfo, nullptr, &m_PipelineCache ); - if (!CheckVkError( "vkCreatePipelineCache()", RetVal )) + auto retVal = vkCreatePipelineCache( m_VulkanDevice, &CreateInfo, nullptr, &m_PipelineCache ); + if (!CheckVkError( "vkCreatePipelineCache()", retVal )) { return false; } @@ -2218,19 +2251,21 @@ bool Vulkan::InitPipelineCache() bool Vulkan::QuerySurfaceCapabilities(VkSurfaceCapabilitiesKHR& outVulkanSurfaceCaps) //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; if (m_ExtSurfaceCapabilities2 && m_ExtSurfaceCapabilities2->Status == VulkanExtensionStatus::eLoaded) { // Have the extension for vkGetPhysicalDeviceSurfaceCapabilities2KHR. // Ideally we could query the VkHdrMetadataEXT for this surface and use that as part of colormapping/output. // Doesnt seem to be supported for Qualcomm or Nvidia drivers ('undocumented' feature on AMD) - VkPhysicalDeviceSurfaceInfo2KHR SurfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR }; - SurfaceInfo.surface = m_VulkanSurface; - VkSurfaceCapabilities2KHR SurfaceCapabilities = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR }; + const VkPhysicalDeviceSurfaceInfo2KHR SurfaceInfo { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, + .surface = m_VulkanSurface + }; + VkSurfaceCapabilities2KHR SurfaceCapabilities { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR }; - RetVal = m_ExtSurfaceCapabilities2->m_vkGetPhysicalDeviceSurfaceCapabilities2KHR(m_VulkanGpu, &SurfaceInfo, &SurfaceCapabilities); - if (!CheckVkError("vkGetPhysicalDeviceSurfaceCapabilities2KHR()", RetVal)) + retVal = m_ExtSurfaceCapabilities2->m_vkGetPhysicalDeviceSurfaceCapabilities2KHR(m_VulkanGpu, &SurfaceInfo, &SurfaceCapabilities); + if (!CheckVkError("vkGetPhysicalDeviceSurfaceCapabilities2KHR()", retVal)) { return false; } @@ -2239,8 +2274,8 @@ bool Vulkan::QuerySurfaceCapabilities(VkSurfaceCapabilitiesKHR& outVulkanSurface } else { - RetVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_VulkanGpu, m_VulkanSurface, &outVulkanSurfaceCaps); - if (!CheckVkError("vkGetPhysicalDeviceSurfaceCapabilitiesKHR()", RetVal)) + retVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_VulkanGpu, m_VulkanSurface, &outVulkanSurfaceCaps); + if (!CheckVkError("vkGetPhysicalDeviceSurfaceCapabilitiesKHR()", retVal)) { return false; } @@ -2293,47 +2328,53 @@ bool Vulkan::QuerySurfaceCapabilities() bool Vulkan::InitSwapChain() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // ******************************** // Presentation Modes // ******************************** - uint32_t NumPresentModes; + uint32_t numPresentModes; - RetVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfacePresentModesKHR(m_VulkanGpu, m_VulkanSurface, &NumPresentModes, nullptr); - if (!CheckVkError("vkGetPhysicalDeviceSurfacePresentModesKHR()", RetVal)) + retVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfacePresentModesKHR( m_VulkanGpu, m_VulkanSurface, &numPresentModes, nullptr ); + if (!CheckVkError( "vkGetPhysicalDeviceSurfacePresentModesKHR()", retVal )) { return false; } - std::vector PresentModes; - PresentModes.resize(NumPresentModes); + std::vector presentModes; + presentModes.resize( numPresentModes ); - RetVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfacePresentModesKHR(m_VulkanGpu, m_VulkanSurface, &NumPresentModes, PresentModes.data()); - if (!CheckVkError("vkGetPhysicalDeviceSurfacePresentModesKHR()", RetVal)) + retVal = m_ExtKhrSurface->m_vkGetPhysicalDeviceSurfacePresentModesKHR( m_VulkanGpu, m_VulkanSurface, &numPresentModes, presentModes.data() ); + if (!CheckVkError( "vkGetPhysicalDeviceSurfacePresentModesKHR()", retVal )) { return false; } - LOGI("Supported Present Modes:"); - for (uint32_t WhichMode = 0; WhichMode < NumPresentModes; WhichMode++) + LOGI( "Supported Present Modes:" ); + for (uint32_t whichMode = 0; whichMode < numPresentModes; whichMode++) { - switch (PresentModes[WhichMode]) + switch (presentModes[whichMode]) { case VK_PRESENT_MODE_IMMEDIATE_KHR: - LOGI(" VK_PRESENT_MODE_IMMEDIATE_KHR"); + LOGI( " VK_PRESENT_MODE_IMMEDIATE_KHR" ); break; case VK_PRESENT_MODE_MAILBOX_KHR: - LOGI(" VK_PRESENT_MODE_MAILBOX_KHR"); + LOGI( " VK_PRESENT_MODE_MAILBOX_KHR" ); break; case VK_PRESENT_MODE_FIFO_KHR: - LOGI(" VK_PRESENT_MODE_FIFO_KHR"); + LOGI( " VK_PRESENT_MODE_FIFO_KHR" ); break; case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - LOGI(" VK_PRESENT_MODE_FIFO_RELAXED_KHR"); + LOGI( " VK_PRESENT_MODE_FIFO_RELAXED_KHR" ); + break; + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + LOGI( " VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR" ); + break; + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + LOGI( " VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR" ); break; default: - LOGI(" Unknown! (%d)", PresentModes[WhichMode]); + LOGI( " Unknown! (%d)", presentModes[whichMode] ); break; } } @@ -2345,41 +2386,41 @@ bool Vulkan::InitSwapChain() // sequential if there are more than two images // Immediate: Requests are applied immediately and tearing may occur. // Unless there is a vsync, the GPU is rendering frames that are not displayed. - VkPresentModeKHR SwapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // FIFO should always be available as a fall-back + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // FIFO should always be available as a fall-back - for (uint32_t uiIndx = 0; uiIndx < NumPresentModes; uiIndx++) + for (uint32_t uiIndx = 0; uiIndx < numPresentModes; uiIndx++) { - if (m_ConfigOverride.PresentMode.has_value() && m_ConfigOverride.PresentMode.value() == PresentModes[uiIndx]) + if (m_ConfigOverride.PresentMode.has_value() && m_ConfigOverride.PresentMode.value() == presentModes[uiIndx]) { // Found the app selected presentation mode! - SwapchainPresentMode = m_ConfigOverride.PresentMode.value(); + swapchainPresentMode = m_ConfigOverride.PresentMode.value(); break; } - else if (PresentModes[uiIndx] == VK_PRESENT_MODE_MAILBOX_KHR) + else if (presentModes[uiIndx] == VK_PRESENT_MODE_MAILBOX_KHR) { // Mailbox is our 1st choice if we dont have (or can't find) an app selected override. - SwapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; + swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; } - else if ((SwapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (PresentModes[uiIndx] == VK_PRESENT_MODE_IMMEDIATE_KHR)) + else if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[uiIndx] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { // Immediate is our 2nd choice if we dont have (or can't find) an app selected override. - SwapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } - switch (SwapchainPresentMode) + switch (swapchainPresentMode) { case VK_PRESENT_MODE_IMMEDIATE_KHR: - LOGI("Setting SwapChain Present Mode: VK_PRESENT_MODE_IMMEDIATE_KHR"); + LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_IMMEDIATE_KHR" ); break; case VK_PRESENT_MODE_MAILBOX_KHR: - LOGI("Setting SwapChain Present Mode: VK_PRESENT_MODE_MAILBOX_KHR"); + LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_MAILBOX_KHR" ); break; case VK_PRESENT_MODE_FIFO_KHR: - LOGI("Setting SwapChain Present Mode: VK_PRESENT_MODE_FIFO_KHR"); + LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_FIFO_KHR" ); break; case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - LOGI("Setting SwapChain Present Mode: VK_PRESENT_MODE_FIFO_RELAXED_KHR"); + LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_FIFO_RELAXED_KHR" ); break; case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR" ); @@ -2388,117 +2429,125 @@ bool Vulkan::InitSwapChain() LOGI( "Setting SwapChain Present Mode: VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR" ); break; default: - LOGI("Setting SwapChain Present Mode: Unknown! (%d)", SwapchainPresentMode); + LOGI( "Setting SwapChain Present Mode: Unknown! (%d)", swapchainPresentMode ); break; } // // Swapchain Extent (grab this again as the swapchain rotate may have swapped the dimensions) // - VkExtent2D SwapchainExtent; + VkExtent2D swapchainExtent; // If the surface capabilities comes back with current extent set to -1 it means // the size is undefined and we can set to what we want. // If it comes back as something it had better match what we set it to. if (m_VulkanSurfaceCaps.currentExtent.width == (uint32_t)-1 || m_VulkanSurfaceCaps.currentExtent.height == (uint32_t)-1 || - m_VulkanSurfaceCaps.currentExtent.width == 0 || m_VulkanSurfaceCaps.currentExtent.height == 0) + m_VulkanSurfaceCaps.currentExtent.width == 0 || m_VulkanSurfaceCaps.currentExtent.height == 0) { - SwapchainExtent.width = m_SurfaceWidth; - SwapchainExtent.height = m_SurfaceHeight; - LOGI("Surface Caps returned an extent with at least one dimension invalid! Setting to %dx%d", SwapchainExtent.width, SwapchainExtent.height); + swapchainExtent.width = m_SurfaceWidth; + swapchainExtent.height = m_SurfaceHeight; + LOGI( "Surface Caps returned an extent with at least one dimension invalid! Setting to %dx%d", swapchainExtent.width, swapchainExtent.height ); } else { - SwapchainExtent = m_VulkanSurfaceCaps.currentExtent; + swapchainExtent = m_VulkanSurfaceCaps.currentExtent; } // Take the surface width from the Vulkan surface - m_SurfaceWidth = SwapchainExtent.width; - m_SurfaceHeight = SwapchainExtent.height; + m_SurfaceWidth = swapchainExtent.width; + m_SurfaceHeight = swapchainExtent.height; - LOGI("Vulkan surface size: %dx%d", m_SurfaceWidth, m_SurfaceHeight); + LOGI( "Vulkan surface size: %dx%d", m_SurfaceWidth, m_SurfaceHeight ); - uint32_t DesiredSwapchainImages = NUM_VULKAN_BUFFERS; - if (SwapchainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + uint32_t desiredSwapchainImages = NUM_VULKAN_BUFFERS; + if (swapchainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { // In mailbox mode we request the number of buffers the application needs and Vulkan will want one or two more! https://github.com/KhronosGroup/Vulkan-Docs/issues/909 - DesiredSwapchainImages = std::max((uint32_t)3, m_VulkanSurfaceCaps.minImageCount + 1/*when running multithreaded and mailbox present we need even more swapchains on Android! However many Android needs (which includes one we can be preparing on the CPU) plus one if we have a thread simultaneously submitting the last frames commands*/); + desiredSwapchainImages = std::max( (uint32_t)3, m_VulkanSurfaceCaps.minImageCount + 1/*when running multithreaded and mailbox present we need even more swapchains on Android! However many Android needs (which includes one we can be preparing on the CPU) plus one if we have a thread simultaneously submitting the last frames commands*/ ); } - if (m_VulkanSurfaceCaps.minImageCount > DesiredSwapchainImages) + if (m_VulkanSurfaceCaps.minImageCount > desiredSwapchainImages) { - LOGE("****************************************"); - LOGE("Minimum image count (%d) is greater than NUM_VULKAN_BUFFERS (%d)!", m_VulkanSurfaceCaps.minImageCount, NUM_VULKAN_BUFFERS); - LOGE("You will have all sorts of problems!"); - LOGE("****************************************"); - assert(0); + LOGE( "****************************************" ); + LOGE( "Minimum image count (%d) is greater than NUM_VULKAN_BUFFERS (%d)!", m_VulkanSurfaceCaps.minImageCount, NUM_VULKAN_BUFFERS ); + LOGE( "You will have all sorts of problems!" ); + LOGE( "****************************************" ); + assert( 0 ); } - if ((m_VulkanSurfaceCaps.maxImageCount > 0) && (DesiredSwapchainImages > m_VulkanSurfaceCaps.maxImageCount)) + if ((m_VulkanSurfaceCaps.maxImageCount > 0) && (desiredSwapchainImages > m_VulkanSurfaceCaps.maxImageCount)) { - LOGE("****************************************"); - LOGE("We desired %d swapchain images but surface limits us to %d!", DesiredSwapchainImages, m_VulkanSurfaceCaps.maxImageCount); - LOGE("You will have all sorts of problems!"); - LOGE("****************************************"); - DesiredSwapchainImages = m_VulkanSurfaceCaps.maxImageCount; + LOGE( "****************************************" ); + LOGE( "We desired %d swapchain images but surface limits us to %d!", desiredSwapchainImages, m_VulkanSurfaceCaps.maxImageCount ); + LOGE( "You will have all sorts of problems!" ); + LOGE( "****************************************" ); + desiredSwapchainImages = m_VulkanSurfaceCaps.maxImageCount; } // If we have the render pass transform extension (and want to use it), then make the pre-transform match the current transform and enable flag to setup renderpasses/commandbuffers appropriately. // Otherwise we want to leave the pretransform alone and let the hardware do the rotation, if supported. { - if( m_ExtRenderPassTransformAvailable && m_LayerKhronosValidationAvailable ) + if (m_ExtRenderPassTransformAvailable && m_LayerKhronosValidationAvailable) { LOGE( "Disabling QCOM_Render_Pass_Transform as it is not supported while Validation layers are enabled" ); m_ExtRenderPassTransformAvailable = false; } - VkSurfaceTransformFlagsKHR DesiredPreTransform; + VkSurfaceTransformFlagsKHR desiredPreTransform; LOGI( "QCOM_Render_Pass_Transform - ExtRenderPassTransformAvailable=%s UseRenderPassTransform=%s", m_ExtRenderPassTransformAvailable ? "True" : "False", m_UseRenderPassTransform ? "True" : "False" ); if (m_UseRenderPassTransform && m_ExtRenderPassTransformAvailable) { - DesiredPreTransform = m_VulkanSurfaceCaps.currentTransform; + desiredPreTransform = m_VulkanSurfaceCaps.currentTransform; m_ExtRenderPassTransformEnabled = (m_VulkanSurfaceCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) || (m_VulkanSurfaceCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR); } else if (m_UsePreTransform) { - DesiredPreTransform = m_VulkanSurfaceCaps.currentTransform; + desiredPreTransform = m_VulkanSurfaceCaps.currentTransform; } else { m_ExtRenderPassTransformEnabled = false; if (m_VulkanSurfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { - DesiredPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + desiredPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { - DesiredPreTransform = m_VulkanSurfaceCaps.currentTransform; + desiredPreTransform = m_VulkanSurfaceCaps.currentTransform; } } - if ((DesiredPreTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) - || (DesiredPreTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) + if ((desiredPreTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) + || (desiredPreTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) { - LOGI("Vulkan surface extents swapped (width <-> height)"); - std::swap(SwapchainExtent.width, SwapchainExtent.height); + LOGI( "Vulkan surface extents swapped (width <-> height)" ); + std::swap( swapchainExtent.width, swapchainExtent.height ); } - m_SwapchainPreTransform = (VkSurfaceTransformFlagBitsKHR)DesiredPreTransform; + m_SwapchainPreTransform = (VkSurfaceTransformFlagBitsKHR)desiredPreTransform; } // ******************************** // Swapchain // ******************************** - VkSwapchainCreateInfoKHR SwapchainInfo {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; - SwapchainInfo.flags = 0; - SwapchainInfo.surface = m_VulkanSurface; - SwapchainInfo.minImageCount = DesiredSwapchainImages; - SwapchainInfo.imageFormat = TextureFormatToVk( m_SurfaceFormat ); - SwapchainInfo.imageColorSpace = m_SurfaceColorSpace; - SwapchainInfo.imageExtent = SwapchainExtent; - SwapchainInfo.imageArrayLayers = 1; - - // Image usage changes if we can blit it - SwapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + VkSwapchainCreateInfoKHR SwapchainInfo{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .flags = 0, + .surface = m_VulkanSurface, + .minImageCount = desiredSwapchainImages, + .imageFormat = TextureFormatToVk( m_SurfaceFormat ), + .imageColorSpace = m_SurfaceColorSpace, + .imageExtent = swapchainExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // Image usage changes if we can blit it + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = m_SwapchainPreTransform, + .compositeAlpha = GetBestVulkanCompositeAlpha(), // Get the supported composite alpha flag + .presentMode = swapchainPresentMode, + .clipped = true, + .oldSwapchain = m_VulkanSwapchain, + }; const VkFormatProperties& FormatProps = GetFormatProperties(TextureFormatToVk(m_SurfaceFormat)); if ((FormatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { @@ -2509,38 +2558,26 @@ bool Vulkan::InitSwapChain() SwapchainInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; // screenshottable } - SwapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - SwapchainInfo.queueFamilyIndexCount = 0; - SwapchainInfo.pQueueFamilyIndices = nullptr; - SwapchainInfo.preTransform = m_SwapchainPreTransform; - - // Get the supported composite alpha flag - SwapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; - SwapchainInfo.compositeAlpha = GetBestVulkanCompositeAlpha(); - - SwapchainInfo.presentMode = SwapchainPresentMode; - SwapchainInfo.clipped = true; - SwapchainInfo.oldSwapchain = m_VulkanSwapchain; - - LOGI("Trying to get %d swapchain images...", DesiredSwapchainImages); -#if defined (OS_WINDOWS) - RetVal = fpCreateSwapchainKHR(m_VulkanDevice, &SwapchainInfo, nullptr, &m_VulkanSwapchain); + LOGI("Trying to get %d swapchain images...", desiredSwapchainImages); +#if defined (OS_WINDOWS) || defined (OS_LINUX) + retVal = m_ExtSwapchain->m_vkCreateSwapchainKHR(m_VulkanDevice, &SwapchainInfo, nullptr, &m_VulkanSwapchain); #elif defined (OS_ANDROID) - RetVal = vkCreateSwapchainKHR(m_VulkanDevice, &SwapchainInfo, nullptr, &m_VulkanSwapchain); -#endif // defined (OS_WINDOWS|OS_ANDROID) - if (!CheckVkError("vkCreateSwapchainKHR()", RetVal)) + retVal = vkCreateSwapchainKHR(m_VulkanDevice, &SwapchainInfo, nullptr, &m_VulkanSwapchain); +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) + if (!CheckVkError("vkCreateSwapchainKHR()", retVal)) { return false; } -#if defined (OS_WINDOWS) - RetVal = fpGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, nullptr); +#if defined (OS_WINDOWS) || defined (OS_LINUX) + retVal = m_ExtSwapchain->m_vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, nullptr); #elif defined (OS_ANDROID) - RetVal = vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, nullptr); -#endif // defined (OS_WINDOWS|OS_ANDROID) - if (!CheckVkError("vkGetSwapchainImagesKHR(nullptr)", RetVal)) - { - return false; + retVal = vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, nullptr); +#else +#error "Unsupported platform" +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) + if (!CheckVkError("vkGetSwapchainImagesKHR(nullptr)", retVal)) + { return false; } LOGI("SwapChain has %d images", m_SwapchainImageCount); @@ -2554,7 +2591,7 @@ bool Vulkan::InitSwapChain() if ((m_VulkanSurfaceCaps.maxImageCount > 0) && (m_SwapchainImageCount > m_VulkanSurfaceCaps.maxImageCount)) { LOGE("****************************************"); - LOGE("We asked for %d swapchain images but got back %d!", DesiredSwapchainImages, m_SwapchainImageCount); + LOGE("We asked for %d swapchain images but got back %d!", desiredSwapchainImages, m_SwapchainImageCount); LOGE("****************************************"); } @@ -2572,127 +2609,138 @@ bool Vulkan::InitSwapChain() std::vector swapchainImages; swapchainImages.resize(m_SwapchainImageCount); -#if defined (OS_WINDOWS) - RetVal = fpGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, swapchainImages.data()); +#if defined (OS_WINDOWS) || defined (OS_LINUX) + retVal = m_ExtSwapchain->m_vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, swapchainImages.data()); #elif defined (OS_ANDROID) - RetVal = vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, swapchainImages.data()); -#endif // defined (OS_WINDOWS|OS_ANDROID) - if (!CheckVkError("vkGetSwapchainImagesKHR()", RetVal)) + retVal = vkGetSwapchainImagesKHR(m_VulkanDevice, m_VulkanSwapchain, &m_SwapchainImageCount, swapchainImages.data()); +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) + if (!CheckVkError("vkGetSwapchainImagesKHR()", retVal)) + { + return false; + } + + // ******************************** + // Setup Depth + // ******************************** + m_SwapchainDepth.format = m_ConfigOverride.SwapchainDepthFormat.value_or( GetBestSurfaceDepthFormat() ); + + if (m_SwapchainDepth.format != TextureFormat::UNDEFINED) { - return false; + const VkFormat vkSwapchainDepthFormat = TextureFormatToVk( m_SwapchainDepth.format ); + const VkImageCreateInfo imageInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = vkSwapchainDepthFormat, + .extent { + .width = swapchainExtent.width, + .height = swapchainExtent.height, + .depth = 1 }, // Spec says for VK_IMAGE_TYPE_2D depth must be 1 + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED + }; + + m_SwapchainDepth.image = m_MemoryManager.CreateImage( imageInfo, MemoryUsage::GpuExclusive ); + if (!m_SwapchainDepth.image) + { + LOGE( "Error creating swapchain back buffer depth image" ); + return false; + } + + // Create the view for this image + const VkImageViewCreateInfo ImageViewInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .flags = 0, + .image = m_SwapchainDepth.image.GetVkBuffer(), + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = vkSwapchainDepthFormat, + .components { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_G, + .b = VK_COMPONENT_SWIZZLE_B, + .a = VK_COMPONENT_SWIZZLE_A + }, + .subresourceRange { + .aspectMask = (VkImageAspectFlags)(FormatHasStencil( m_SwapchainDepth.format ) ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) : VK_IMAGE_ASPECT_DEPTH_BIT), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + }; + retVal = vkCreateImageView( m_VulkanDevice, &ImageViewInfo, nullptr, &m_SwapchainDepth.view ); + if (!CheckVkError( "vkCreateImageView()", retVal )) + { + return false; + } } assert(m_SwapchainBuffers.empty()); - m_SwapchainBuffers.resize(m_SwapchainImageCount, {}); + m_SwapchainBuffers.reserve(m_SwapchainImageCount); for (uint32_t uiIndx = 0; uiIndx < m_SwapchainImageCount; uiIndx++) { - m_SwapchainBuffers[uiIndx].image = swapchainImages[uiIndx]; + auto& swapchainBuffer = m_SwapchainBuffers.emplace_back( SwapchainBuffers{}); + swapchainBuffer.image = swapchainImages[uiIndx]; // Render loop will expect image to have been used before and in // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image // to that state - VkImageViewCreateInfo ImageViewInfo {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; - ImageViewInfo.flags = 0; - ImageViewInfo.image = m_SwapchainBuffers[uiIndx].image; - ImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - ImageViewInfo.format = TextureFormatToVk( m_SurfaceFormat ); - ImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - ImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; - ImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; - ImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - ImageViewInfo.subresourceRange.baseMipLevel = 0; - ImageViewInfo.subresourceRange.levelCount = 1; - ImageViewInfo.subresourceRange.baseArrayLayer = 0; - ImageViewInfo.subresourceRange.layerCount = 1; - - RetVal = vkCreateImageView(m_VulkanDevice, &ImageViewInfo, nullptr, &m_SwapchainBuffers[uiIndx].view); - if (!CheckVkError("vkCreateImageView()", RetVal)) + const VkImageViewCreateInfo ImageViewInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .flags = 0, + .image = swapchainBuffer.image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = TextureFormatToVk( m_SurfaceFormat ), + .components { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_G, + .b = VK_COMPONENT_SWIZZLE_B, + .a = VK_COMPONENT_SWIZZLE_A }, + .subresourceRange { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + retVal = vkCreateImageView( m_VulkanDevice, &ImageViewInfo, nullptr, &swapchainBuffer.view ); + if (!CheckVkError( "vkCreateImageView()", retVal )) { return false; } // Create the wait fences, one per swapchain image - VkFenceCreateInfo FenceInfo {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; - FenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - RetVal = vkCreateFence(m_VulkanDevice, &FenceInfo, nullptr, &m_SwapchainBuffers[uiIndx].fence); - if (!CheckVkError("vkCreateFence()", RetVal)) + const VkFenceCreateInfo fenceInfo{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT + }; + retVal = vkCreateFence( m_VulkanDevice, &fenceInfo, nullptr, &swapchainBuffer.fence ); + if (!CheckVkError( "vkCreateFence()", retVal )) { return false; } // Create semaphores for backbuffer signalling, one per swapchain. - VkSemaphoreCreateInfo semCreateInfo {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - semCreateInfo.flags = 0; - - RetVal = vkCreateSemaphore(m_VulkanDevice, &semCreateInfo, nullptr, &m_SwapchainBuffers[uiIndx].semaphore); - if (!CheckVkError("vkCreateSemaphore()", RetVal)) + const VkSemaphoreCreateInfo semCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .flags = 0 + }; + retVal = vkCreateSemaphore(m_VulkanDevice, &semCreateInfo, nullptr, &swapchainBuffer.semaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) { return false; } } // Each swapchain image - - // ******************************** - // Setup Depth - // ******************************** - m_SwapchainDepth.format = m_ConfigOverride.SwapchainDepthFormat.value_or( GetBestSurfaceDepthFormat() ); - - if (m_SwapchainDepth.format != TextureFormat::UNDEFINED) - { - const VkFormat vkSwapchainDepthFormat = TextureFormatToVk(m_SwapchainDepth.format); - VkImageCreateInfo ImageInfo {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - ImageInfo.flags = 0; - ImageInfo.imageType = VK_IMAGE_TYPE_2D; - ImageInfo.format = vkSwapchainDepthFormat; - ImageInfo.extent.width = SwapchainExtent.width; - ImageInfo.extent.height = SwapchainExtent.height; - ImageInfo.extent.depth = 1; // Spec says for VK_IMAGE_TYPE_2D depth must be 1 - ImageInfo.mipLevels = 1; - ImageInfo.arrayLayers = 1; - ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - ImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - ImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - ImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - m_SwapchainDepth.image = m_MemoryManager.CreateImage( ImageInfo, MemoryUsage::GpuExclusive ); - if( !m_SwapchainDepth.image ) - { - LOGE( "Error creating swapchain back buffer depth image" ); - return false; - } - - // Create the view for this image - VkImageViewCreateInfo ImageViewInfo {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; - ImageViewInfo.flags = 0; - ImageViewInfo.format = vkSwapchainDepthFormat; - ImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; - ImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; - ImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; - ImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; - if (FormatHasStencil( m_SwapchainDepth.format )) - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - else - ImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - ImageViewInfo.subresourceRange.baseMipLevel = 0; - ImageViewInfo.subresourceRange.levelCount = 1; - ImageViewInfo.subresourceRange.baseArrayLayer = 0; - ImageViewInfo.subresourceRange.layerCount = 1; - ImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - ImageViewInfo.image = m_SwapchainDepth.image.GetVkBuffer(); - - RetVal = vkCreateImageView(m_VulkanDevice, &ImageViewInfo, nullptr, &m_SwapchainDepth.view); - if (!CheckVkError("vkCreateImageView()", RetVal)) - { - return false; - } - } - return true; } @@ -2700,7 +2748,7 @@ bool Vulkan::InitSwapChain() bool Vulkan::InitSwapchainRenderPass() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // The renderpass defines the attachments to the framebuffer object that gets // used in the pipelines. We have two attachments, the colour buffer, and the @@ -2724,40 +2772,42 @@ bool Vulkan::InitSwapchainRenderPass() if( HasDepth ) { - attachmentDescriptions[1].flags = 0; - attachmentDescriptions[1].format = TextureFormatToVk( m_SwapchainDepth.format ); - attachmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachmentDescriptions[1].flags = 0; + attachmentDescriptions[1].format = TextureFormatToVk( m_SwapchainDepth.format ); + attachmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; ++numAttachmentDescriptions; } // We have references to the attachment offsets, stating the layout type. - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depthReference = {}; - depthReference.attachment = 1; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + const VkAttachmentReference colorReference { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; + const VkAttachmentReference depthReference{ + .attachment = 1, + .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + }; // There can be multiple subpasses in a renderpass, but this example has only one. // We set the color and depth references at the grahics bind point in the pipeline. - VkSubpassDescription subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.flags = 0; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pResolveAttachments = nullptr; - subpassDescription.pDepthStencilAttachment = HasDepth ? &depthReference : nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; + const VkSubpassDescription subpassDescription { + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = 1, + .pColorAttachments = &colorReference, + .pResolveAttachments = nullptr, + .pDepthStencilAttachment = HasDepth ? &depthReference : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr + }; // Dependencies m_SwapchainRenderPassDependencies.fill( {} ); @@ -2789,52 +2839,47 @@ bool Vulkan::InitSwapchainRenderPass() // The renderpass itself is created with the number of subpasses, and the // list of attachments which those subpasses can reference. - VkRenderPassCreateInfo renderPassCreateInfo {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; - renderPassCreateInfo.attachmentCount = numAttachmentDescriptions; - renderPassCreateInfo.pAttachments = attachmentDescriptions.data(); - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpassDescription; - renderPassCreateInfo.dependencyCount = (uint32_t) m_SwapchainRenderPassDependencies.size(); - renderPassCreateInfo.pDependencies = m_SwapchainRenderPassDependencies.data(); - renderPassCreateInfo.flags = (m_ExtRenderPassTransformEnabled) ? VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM : 0; - - RetVal = vkCreateRenderPass(m_VulkanDevice, &renderPassCreateInfo, nullptr, &m_SwapchainRenderPass); - if (!CheckVkError("vkCreateRenderPass()", RetVal)) - { - return false; - } - - - return true; + fvk::VkRenderPassCreateInfo renderPassCreateInfo{{ + .flags = (m_ExtRenderPassTransformEnabled) ? VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM : (VkRenderPassCreateFlags)0, + .attachmentCount = numAttachmentDescriptions, + .pAttachments = attachmentDescriptions.data(), + .subpassCount = 1, + .pSubpasses = &subpassDescription, + .dependencyCount = (uint32_t)m_SwapchainRenderPassDependencies.size(), + .pDependencies = m_SwapchainRenderPassDependencies.data(), + }}; + + // Create renderpass + return CreateRenderPass( *renderPassCreateInfo, m_SwapchainRenderPass ); } //----------------------------------------------------------------------------- -bool Vulkan::Create2SubpassRenderPass(const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, const std::span InternalMsaa/*two passes*/, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/) +bool Vulkan::Create2SubpassRenderPass(const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, const std::span InternalMsaa/*two passes*/, Msaa OutputMsaa, RenderPass& rRenderPass/*out*/) //----------------------------------------------------------------------------- { - assert(pRenderPass && *pRenderPass == VK_NULL_HANDLE); // check not already allocated and that we have a location to place the renderpass handle - assert(!InternalColorFormats.empty()); // not supporting a depth only pass + assert(!rRenderPass); // check not already allocated + assert(!InternalColorFormats.empty()); // not supporting a depth only pass assert(!OutputColorFormats.empty()); assert( InternalMsaa.size() == 2 ); //const bool NeedsResolve = InternalMsaa != OutputMsaa; // Color attachments and Depth attachment - std::vector PassAttachDescs; + std::vector PassAttachDescs; PassAttachDescs.reserve(InternalColorFormats.size() + OutputColorFormats.size() + 2); // Each subpass needs a reference to its attachments - std::array SubpassDesc{ - VkSubpassDescription {0/*flags*/, VK_PIPELINE_BIND_POINT_GRAPHICS}, - VkSubpassDescription {0/*flags*/, VK_PIPELINE_BIND_POINT_GRAPHICS} }; - std::vector ColorReferencesPass0; - std::vector ResolveReferencesPass0; + std::array SubpassDesc{ + VkSubpassDescription2 {.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS}, + VkSubpassDescription2 {.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS} }; + std::vector ColorReferencesPass0; + std::vector ResolveReferencesPass0; ColorReferencesPass0.reserve(InternalColorFormats.size()); ResolveReferencesPass0.reserve(InternalColorFormats.size()); - std::vector InputReferencesPass1; - std::vector ColorReferencesPass1; - std::vector ResolveReferencesPass1; + std::vector InputReferencesPass1; + std::vector ColorReferencesPass1; + std::vector ResolveReferencesPass1; InputReferencesPass1.reserve(InternalColorFormats.size()); ColorReferencesPass1.reserve(OutputColorFormats.size()); ResolveReferencesPass1.reserve(OutputColorFormats.size()); @@ -2850,9 +2895,12 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter for (const auto& ColorFormat : InternalColorFormats) { // Pass0 color and depth buffers setup to clear on load, discard on end (of entire pass). - VkAttachmentDescription AttachmentDescPass0 = { 0/*flags*/, + const VkAttachmentDescription2 AttachmentDescPass0 = { + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + nullptr, /*pNext*/ + 0/*flags*/, TextureFormatToVk(ColorFormat)/*format*/, - InternalMsaa[0]/*samples*/, + EnumToVk(InternalMsaa[0])/*samples*/, VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -2861,8 +2909,8 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL/*finalLayout*/ }; PassAttachDescs.push_back(AttachmentDescPass0); - ColorReferencesPass0.push_back({ (uint32_t)PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL/*output of first pass*/ }); - InputReferencesPass1.push_back({ (uint32_t)PassAttachDescs.size() - 1, Pass0NeedsResolve ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*input of second pass*/ }); + ColorReferencesPass0.push_back({ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t)PassAttachDescs.size() - 1, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL/*output of first pass*/ }); + InputReferencesPass1.push_back({ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t)PassAttachDescs.size() - 1, .layout = Pass0NeedsResolve ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*input of second pass*/ }); } if (Pass0NeedsResolve) @@ -2872,17 +2920,19 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter for (const auto& ColorFormat : InternalColorFormats) { // Pass1 color buffers to resolve to at end of pass. - VkAttachmentDescription AttachmentDescResolvePass0 = { 0, TextureFormatToVk(ColorFormat)/*format*/, - OutputMsaa/*samples*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*initialLayout*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/ }; + VkAttachmentDescription2 AttachmentDescResolvePass0 = { + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .format = TextureFormatToVk(ColorFormat), + .samples = EnumToVk(OutputMsaa), + .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; PassAttachDescs.push_back( AttachmentDescResolvePass0 ); - ResolveReferencesPass0.push_back( { (uint32_t) PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL} ); + ResolveReferencesPass0.push_back( {.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t) PassAttachDescs.size() - 1, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL} ); } // // Setup the resolve (first subpass) @@ -2894,17 +2944,19 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter for (const auto& ColorFormat : OutputColorFormats) { // Pass1 color buffers setup to, store on end (of entire pass). - VkAttachmentDescription AttachmentDescPass1 = { 0, TextureFormatToVk(ColorFormat)/*format*/, - (!Pass1NeedsResolve) ? OutputMsaa : InternalMsaa[1]/*samples*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, - (!Pass1NeedsResolve) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_UNDEFINED/*initialLayout*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/ }; + VkAttachmentDescription2 AttachmentDescPass1 = { + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .format = TextureFormatToVk(ColorFormat), + .samples = EnumToVk((!Pass1NeedsResolve) ? OutputMsaa : InternalMsaa[1]), + .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .storeOp = (!Pass1NeedsResolve) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; PassAttachDescs.push_back(AttachmentDescPass1); - ColorReferencesPass1.push_back({ (uint32_t)PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); + ColorReferencesPass1.push_back({.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t)PassAttachDescs.size() - 1, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); } if (Pass1NeedsResolve) @@ -2914,8 +2966,11 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter for (const auto& ColorFormat : OutputColorFormats) { // Pass1 color buffers to resolve to at end of pass. - VkAttachmentDescription AttachmentDescResolvePass1 = { 0, TextureFormatToVk(ColorFormat)/*format*/, - OutputMsaa/*samples*/, + VkAttachmentDescription2 AttachmentDescResolvePass1 = { + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + nullptr, // pNext + 0, TextureFormatToVk(ColorFormat)/*format*/, + EnumToVk(OutputMsaa)/*samples*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, VK_ATTACHMENT_STORE_OP_STORE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -2924,23 +2979,24 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/ }; PassAttachDescs.push_back( AttachmentDescResolvePass1 ); - ResolveReferencesPass1.push_back( { (uint32_t) PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL} ); + ResolveReferencesPass1.push_back( {.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t) PassAttachDescs.size() - 1, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL} ); } // // Setup the resolve (second subpass) SubpassDesc[1].pResolveAttachments = ResolveReferencesPass1.data(); } - // // Depth Attachment (cleared at start of pass, written by first subpass, discarded after pass) - VkAttachmentReference DepthReference = {}; - VkAttachmentReference DepthReferencePass1 = {}; + VkAttachmentReference2 DepthReference = {VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2}; if (HasDepth) { - VkAttachmentDescription AttachmentDescDepthPass0 = { 0/*flags*/, + VkAttachmentDescription2 AttachmentDescDepthPass0 = { + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + nullptr,/*pNext*/ + 0/*flags*/, TextureFormatToVk(InternalDepthFormat)/*format*/, - InternalMsaa[0]/*samples*/, + EnumToVk(InternalMsaa[0])/*samples*/, VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -2949,12 +3005,12 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL/*finalLayout*/ }; PassAttachDescs.push_back(AttachmentDescDepthPass0); - DepthReference = { (uint32_t)PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + DepthReference = {.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = (uint32_t)PassAttachDescs.size() - 1, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; } // // Subpass dependencies - std::array PassDependencies = {}; + std::array PassDependencies {{{.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2}}}; if (ColorReferencesPass0.size() > 0) { SubpassDesc[0].colorAttachmentCount = (uint32_t)ColorReferencesPass0.size(); @@ -2984,21 +3040,54 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter PassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Now ready to actually create the render pass - VkRenderPassCreateInfo RenderPassInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - RenderPassInfo.flags = 0; - RenderPassInfo.attachmentCount = (uint32_t)PassAttachDescs.size(); - RenderPassInfo.pAttachments = PassAttachDescs.data(); - RenderPassInfo.subpassCount = (uint32_t)SubpassDesc.size(); - RenderPassInfo.pSubpasses = SubpassDesc.data(); - RenderPassInfo.dependencyCount = (uint32_t)PassDependencies.size(); - RenderPassInfo.pDependencies = PassDependencies.data(); + fvk::VkRenderPassCreateInfo2 RenderPassInfo{{ + .flags = 0, + .attachmentCount = (uint32_t)PassAttachDescs.size(), + .pAttachments = PassAttachDescs.data(), + .subpassCount = (uint32_t)SubpassDesc.size(), + .pSubpasses = SubpassDesc.data(), + .dependencyCount = (uint32_t)PassDependencies.size(), + .pDependencies = PassDependencies.data() + }}; + + return CreateRenderPass( *RenderPassInfo, rRenderPass ); +} + +//----------------------------------------------------------------------------- +bool Vulkan::CreateRenderPass( const VkRenderPassCreateInfo& createInfo, RenderPass& rRenderPass/*out*/ ) +//----------------------------------------------------------------------------- +{ + VkRenderPass handle = VK_NULL_HANDLE; + VkResult retVal = vkCreateRenderPass( m_VulkanDevice, &createInfo, NULL, &handle ); + if (!CheckVkError( "vkCreateRenderPass()", retVal )) + { + rRenderPass = {}; + return false; + } + rRenderPass = {m_VulkanDevice, handle}; + return true; +} + +//----------------------------------------------------------------------------- +bool Vulkan::CreateRenderPass( const VkRenderPassCreateInfo2KHR& createInfo, RenderPass& rRenderPass/*out*/ ) +//----------------------------------------------------------------------------- +{ + assert( m_ExtRenderPass2 ); + if (!m_ExtRenderPass2) + { + rRenderPass = {}; + return false; + } - VkResult RetVal = vkCreateRenderPass(m_VulkanDevice, &RenderPassInfo, NULL, pRenderPass); - if (!CheckVkError("vkCreateRenderPass()", RetVal)) + VkRenderPass handle = VK_NULL_HANDLE; + VkResult retVal = m_ExtRenderPass2->m_vkCreateRenderPass2KHR( m_VulkanDevice, &createInfo, nullptr, &rRenderPass.mRenderPass.handle ); + if (!CheckVkError( "vkCreateRenderPass2KHR()", retVal )) { + rRenderPass = {}; return false; } + rRenderPass = {m_VulkanDevice, handle}; return true; } @@ -3006,21 +3095,26 @@ bool Vulkan::Create2SubpassRenderPass(const std::span Inter bool Vulkan::Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, - VkSampleCountFlagBits InternalPassMsaa, /* same for both passes*/ - VkSampleCountFlagBits OutputMsaa, - VkRenderPass* pRenderPass/*out*/ ) + Msaa InternalPassMsaa, /* same for both passes*/ + Msaa OutputMsaa, + RenderPass& rRenderPass/*out*/ ) //----------------------------------------------------------------------------- { - const VkSampleCountFlagBits InternalPassMsaa2[2] = { InternalPassMsaa, InternalPassMsaa }; - return Create2SubpassRenderPass( InternalColorFormats , OutputColorFormats , InternalDepthFormat, InternalPassMsaa2, OutputMsaa, pRenderPass ); + const Msaa InternalPassMsaa2[2] = { InternalPassMsaa, InternalPassMsaa }; + return Create2SubpassRenderPass( InternalColorFormats , OutputColorFormats , InternalDepthFormat, InternalPassMsaa2, OutputMsaa, rRenderPass ); } //----------------------------------------------------------------------------- -bool Vulkan::CreateSubpassShaderResolveRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, VkSampleCountFlagBits InternalMsaa, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/ ) +bool Vulkan::CreateSubpassShaderResolveRenderPass( const std::span InternalColorFormats, + const std::span OutputColorFormats, + TextureFormat InternalDepthFormat, + Msaa InternalMsaa, + Msaa OutputMsaa, + RenderPass& rRenderPass/*out*/ ) //----------------------------------------------------------------------------- { - assert( pRenderPass && *pRenderPass == VK_NULL_HANDLE ); // check not already allocated and that we have a location to place the renderpass handle - assert( !InternalColorFormats.empty() ); // not supporting a depth only pass + assert( !rRenderPass); // check not already allocated and that we have a location to place the renderpass handle + assert( !InternalColorFormats.empty() ); // not supporting a depth only pass assert( !OutputColorFormats.empty() ); if (InternalMsaa == OutputMsaa) @@ -3057,7 +3151,7 @@ bool Vulkan::CreateSubpassShaderResolveRenderPass( const std::spanrenderPass = m_SwapchainRenderPass.mRenderPass; + framebufferCreateInfo->attachmentCount = numAttachments; + framebufferCreateInfo->pAttachments = attachments.data(); + framebufferCreateInfo->width = m_SurfaceWidth; + framebufferCreateInfo->height = m_SurfaceHeight; if ((m_SwapchainPreTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) || (m_SwapchainPreTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) { - std::swap(framebufferCreateInfo.width, framebufferCreateInfo.height); + std::swap(framebufferCreateInfo->width, framebufferCreateInfo->height); } - framebufferCreateInfo.layers = 1; + framebufferCreateInfo->layers = 1; - LOGI("Creating %d frame buffers... (%d x %d)", m_SwapchainImageCount, framebufferCreateInfo.width, framebufferCreateInfo.height); + LOGI("Creating %d frame buffers... (%d x %d)", m_SwapchainImageCount, framebufferCreateInfo->width, framebufferCreateInfo->height); assert(!m_SwapchainBuffers.empty()); @@ -3203,16 +3290,34 @@ bool Vulkan::InitFrameBuffers() // only the attachment to the relevent image view changes each time. for (auto& buffer: m_SwapchainBuffers) { + assert( !buffer.framebuffer ); attachments[0] = buffer.view; - assert(buffer.framebuffer == VK_NULL_HANDLE); - - RetVal = vkCreateFramebuffer(m_VulkanDevice, &framebufferCreateInfo, nullptr, &buffer.framebuffer); - if (!CheckVkError("vkCreateFramebuffer()", RetVal)) + VkRect2D renderArea{ + .extent { + .width = framebufferCreateInfo->width, + .height = framebufferCreateInfo->height + }}; + VkViewport renderViewport{ + .width = float( renderArea.extent.width ), + .height = float( renderArea.extent.height ), + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + std::vector clearValues; + clearValues.resize( numAttachments ); // use 0 filled struct as default. + RenderPassClearData clearData {renderArea, renderViewport, std::move(clearValues)}; + + VkFramebuffer framebuffer{}; + retVal = vkCreateFramebuffer(m_VulkanDevice, &framebufferCreateInfo, nullptr, &framebuffer); + buffer.framebuffer = {{m_VulkanDevice, framebuffer}, std::move(clearData), "Swapchain Framebuffer"}; + if (!CheckVkError("vkCreateFramebuffer()", retVal)) { return false; } } + ReportFramebufferProperties( m_SwapchainBuffers.front().framebuffer.m_FrameBuffer ); + return true; } @@ -3233,16 +3338,16 @@ void Vulkan::DestroySwapChain() vkDestroySemaphore(m_VulkanDevice, swapchainBuffer.semaphore, nullptr); vkDestroyFence(m_VulkanDevice, swapchainBuffer.fence, nullptr); vkDestroyImageView(m_VulkanDevice, swapchainBuffer.view, nullptr); - assert(swapchainBuffer.framebuffer == VK_NULL_HANDLE); // framebuffers destroyed by DestroyFramebuffers + assert(!swapchainBuffer.framebuffer); // framebuffers destroyed by DestroyFramebuffers swapchainBuffer.image = VK_NULL_HANDLE; // images are owned by the m_VulkanSwapchain } m_SwapchainBuffers.clear(); -#if defined (OS_WINDOWS) - fpDestroySwapchainKHR(m_VulkanDevice, m_VulkanSwapchain, nullptr); +#if defined (OS_WINDOWS) || defined (OS_LINUX) + m_ExtSwapchain->m_vkDestroySwapchainKHR(m_VulkanDevice, m_VulkanSwapchain, nullptr); #elif defined (OS_ANDROID) vkDestroySwapchainKHR(m_VulkanDevice, m_VulkanSwapchain, nullptr); -#endif // defined (OS_WINDOWS|OS_ANDROID) +#endif // defined (OS_WINDOWS|OS_ANDROID|OS_LINUX) m_VulkanSwapchain = VK_NULL_HANDLE; } @@ -3250,22 +3355,14 @@ void Vulkan::DestroySwapChain() void Vulkan::DestroySwapchainRenderPass() //----------------------------------------------------------------------------- { - if (m_SwapchainRenderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(m_VulkanDevice, m_SwapchainRenderPass, nullptr); - m_SwapchainRenderPass = VK_NULL_HANDLE; - } + m_SwapchainRenderPass = {}; } //----------------------------------------------------------------------------- void Vulkan::DestroyFrameBuffers() //----------------------------------------------------------------------------- { - for (auto& buffer : m_SwapchainBuffers) - { - vkDestroyFramebuffer(m_VulkanDevice, buffer.framebuffer, nullptr); - buffer.framebuffer = VK_NULL_HANDLE; - } + m_SwapchainBuffers.clear(); } //----------------------------------------------------------------------------- @@ -3280,8 +3377,8 @@ bool Vulkan::AllocateCommandBuffer(VkCommandBufferLevel CmdBuffLevel, uint32_t Q AllocInfo.level = CmdBuffLevel; AllocInfo.commandBufferCount = 1; - VkResult RetVal = vkAllocateCommandBuffers(m_VulkanDevice, &AllocInfo, pCmdBuffer); - return CheckVkError("vkAllocateCommandBuffers()", RetVal); + VkResult retVal = vkAllocateCommandBuffers(m_VulkanDevice, &AllocInfo, pCmdBuffer); + return CheckVkError("vkAllocateCommandBuffers()", retVal); } //----------------------------------------------------------------------------- @@ -3293,30 +3390,65 @@ void Vulkan::FreeCommandBuffer(uint32_t QueueIndex, VkCommandBuffer CmdBuffer) c } //----------------------------------------------------------------------------- -bool Vulkan::CreateRenderPass(std::span ColorFormats, TextureFormat DepthFormat, VkSampleCountFlagBits Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, RenderPassOutputUsage DepthOutputUsage, VkRenderPass* pRenderPass/*out*/, std::span ResolveFormats) +bool Vulkan::CreateRenderPass(std::span ColorFormats, TextureFormat DepthFormat, Msaa Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, RenderPassOutputUsage DepthOutputUsage, RenderPass& rRenderPass/*out*/, std::span ResolveFormats) //----------------------------------------------------------------------------- { - assert(pRenderPass && *pRenderPass == VK_NULL_HANDLE); // check not already allocated and that we have a location to place the renderpass handle - VkResult RetVal = VK_SUCCESS; + std::vector colorInputUsagePerColor; + std::fill_n( std::back_inserter( colorInputUsagePerColor ), ColorFormats.size(), ColorInputUsage ); + std::vector colorOutputUsagePerColor; + std::fill_n( std::back_inserter( colorOutputUsagePerColor ), ColorFormats.size(), ColorOutputUsage ); + + const RenderPassCreateData createData{ + .ColorFormats = ColorFormats, + .DepthFormat = DepthFormat, + .Msaa = Msaa, + .ColorInputUsage = colorInputUsagePerColor, + .ColorOutputUsage = colorOutputUsagePerColor, + .DepthInputUsage = ShouldClearDepth ? RenderPassInputUsage::Clear : RenderPassInputUsage::Load, + .DepthOutputUsage = DepthOutputUsage, + .ResolveFormats = ResolveFormats, + }; + + auto renderPass = CreateRenderPass( createData ); + rRenderPass = std::move(renderPass); + return !!rRenderPass; +} + +static constexpr VkAttachmentLoadOp InputUsageToVkAttachmentLoadOp( const RenderPassInputUsage t ) { + constexpr VkAttachmentLoadOp cRenderPassInputUsageToVk[]{ + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_DONT_CARE + }; + return cRenderPassInputUsageToVk[(int)t]; +} + +//----------------------------------------------------------------------------- +RenderPass Vulkan::CreateRenderPass( const RenderPassCreateData& createData ) +//----------------------------------------------------------------------------- +{ + VkResult retVal = VK_SUCCESS; // Color attachments and Depth attachment std::vector PassAttachDescs; - PassAttachDescs.reserve(ColorFormats.size()+1); + PassAttachDescs.reserve(createData.ColorFormats.size()+1); bool bPresentPass = false; - bool bHasDepth = DepthFormat != TextureFormat::UNDEFINED; + bool bHasDepth = createData.DepthFormat != TextureFormat::UNDEFINED; + auto vkMsaa = EnumToVk(createData.Msaa); // Color Attachment - for(const auto ColorFormat: ColorFormats) + for(auto i = 0;i ColorFormats, Textu }); auto& PassAttachDesc = PassAttachDescs.back(); - switch( ColorInputUsage ) { + switch(createData.ColorInputUsage[i]) { case RenderPassInputUsage::Clear: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; break; case RenderPassInputUsage::Load: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; break; case RenderPassInputUsage::DontCare: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; break; }; - switch (ColorOutputUsage) { + switch (createData.ColorOutputUsage[i]) { case RenderPassOutputUsage::Discard: PassAttachDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; PassAttachDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // not sure why Vulkan spec says this can't be VK_IMAGE_LAYOUT_UNDEFINED - we aren't storing it afterall! @@ -3361,7 +3490,7 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu assert(0); // currently unsupported break; case RenderPassOutputUsage::Present: - if (ResolveFormats.empty()) + if (createData.ResolveFormats.empty()) { // Nothing to resolve - this is the output to present PassAttachDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; @@ -3382,17 +3511,17 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu { PassAttachDescs.push_back({ /*flags*/ 0, - /*format*/ TextureFormatToVk(DepthFormat), - /*samples*/ Msaa, - /*loadOp*/ ShouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, + /*format*/ TextureFormatToVk( createData.DepthFormat), + /*samples*/ vkMsaa, + /*loadOp*/ InputUsageToVkAttachmentLoadOp(createData.DepthInputUsage), /*storeOp*/ VK_ATTACHMENT_STORE_OP_STORE, /*stencilLoadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, /*stencilStoreOp*/ VK_ATTACHMENT_STORE_OP_DONT_CARE, - /*initialLayout*/ ShouldClearDepth ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /*initialLayout*/ createData.DepthInputUsage == RenderPassInputUsage::Clear ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, /*finalLayout*/ VK_IMAGE_LAYOUT_UNDEFINED }); auto& PassAttachDesc = PassAttachDescs.back(); - switch (DepthOutputUsage) { + switch (createData.DepthOutputUsage) { case RenderPassOutputUsage::Discard: PassAttachDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; PassAttachDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // not sure why Vulkan spec says this can't be VK_IMAGE_LAYOUT_UNDEFINED - we aren't storing it afterall! @@ -3417,9 +3546,9 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu // We need a reference to the attachments std::vector ColorReferences; - ColorReferences.reserve(ColorFormats.size()); + ColorReferences.reserve(createData.ColorFormats.size()); uint32_t AttachmentIndex = 0; - for(; AttachmentIndex < ColorFormats.size(); ++AttachmentIndex) + for(; AttachmentIndex < createData.ColorFormats.size(); ++AttachmentIndex) { ColorReferences.push_back({ AttachmentIndex, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); } @@ -3440,11 +3569,11 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu // When we have ResolveFormats we resolve each member of ColorFormats out to a matching ResolveFormats buffer std::vector ResolveReferences; - if( Msaa != VK_SAMPLE_COUNT_1_BIT && !ResolveFormats.empty() ) + if( createData.Msaa != Msaa::Samples1 && !createData.ResolveFormats.empty() ) { - ResolveReferences.resize( ColorFormats.size(), {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED} ); // must match the number of Color buffers (even if they are not resolving) + ResolveReferences.resize( createData.ColorFormats.size(), {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED} ); // must match the number of Color buffers (even if they are not resolving) uint32_t ColorIdx = 0; - for( const TextureFormat ResolveFormat : ResolveFormats ) + for( const TextureFormat ResolveFormat : createData.ResolveFormats ) { VkFormat vkResolveFormat = TextureFormatToVk(ResolveFormat); if( vkResolveFormat != VK_FORMAT_UNDEFINED) @@ -3481,7 +3610,7 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu // Use the same dependencies as the swapchain was created with PassDependencies = m_SwapchainRenderPassDependencies; } - else if (ColorFormats.empty()) + else if (createData.ColorFormats.empty()) { // Depth only pass PassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; @@ -3520,66 +3649,52 @@ bool Vulkan::CreateRenderPass(std::span ColorFormats, Textu PassDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } - - // We use subpass dependencies to define the color image layout transitions rather than -// explicitly do them in the command buffer, as it is more efficient to do it this way. -#if 0 -// Before we can use the back buffer from the swapchain, we must change the -// image layout from the PRESENT mode to the COLOR_ATTACHMENT mode. - PassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - PassDependencies[0].dstSubpass = 0; - PassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - PassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - PassDependencies[0].srcAccessMask = 0; - PassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - PassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - // After writing to the back buffer of the swapchain, we need to change the - // image layout from the COLOR_ATTACHMENT mode to the PRESENT mode which - // is optimal for sending to the screen for users to see the completed rendering. - PassDependencies[1].srcSubpass = 0; - PassDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - PassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - PassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - PassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - PassDependencies[1].dstAccessMask = 0; - PassDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; -#endif - // Now ready to actually create the render pass - VkRenderPassCreateInfo RenderPassInfo {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; - RenderPassInfo.flags = (bPresentPass && m_ExtRenderPassTransformEnabled) ? VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM : 0; - RenderPassInfo.attachmentCount = (uint32_t) PassAttachDescs.size(); - RenderPassInfo.pAttachments = PassAttachDescs.data(); - RenderPassInfo.subpassCount = 1; - RenderPassInfo.pSubpasses = &SubpassDesc; - RenderPassInfo.dependencyCount = (uint32_t) PassDependencies.size(); - RenderPassInfo.pDependencies = PassDependencies.data(); - - RetVal = vkCreateRenderPass(m_VulkanDevice, &RenderPassInfo, nullptr, pRenderPass); - if (!CheckVkError("vkCreateRenderPass()", RetVal)) - { - return false; - } - - return true; + fvk::VkRenderPassCreateInfo RenderPassCreateInfo {{ + .flags = (VkRenderPassCreateFlags) ((bPresentPass && m_ExtRenderPassTransformEnabled) ? VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM : 0), + .attachmentCount = (uint32_t)PassAttachDescs.size(), + .pAttachments = PassAttachDescs.data(), + .subpassCount = 1, + .pSubpasses = &SubpassDesc, + .dependencyCount = (uint32_t)PassDependencies.size(), + .pDependencies = PassDependencies.data() + }}; + + if (createData.TileShading != RenderPassTileShadingMode::Disabled) + { + VkTileShadingRenderPassFlagsQCOM tileShadingFlags = VK_TILE_SHADING_RENDER_PASS_ENABLE_BIT_QCOM; + if (createData.TileShading == RenderPassTileShadingMode::PerTileShading) + tileShadingFlags |= VK_TILE_SHADING_RENDER_PASS_PER_TILE_EXECUTION_BIT_QCOM; + + RenderPassCreateInfo.Add( { + .flags = tileShadingFlags,// + .tileApronSize = {createData.TileApron[0], createData.TileApron[1]} + } ); + if (createData.TileApron[0]>0 || createData.TileApron[1] > 0) + SubpassDesc.flags |= VK_SUBPASS_DESCRIPTION_TILE_SHADING_APRON_BIT_QCOM; + } + + RenderPass renderPass; + if (CreateRenderPass( *RenderPassCreateInfo, renderPass )) + return renderPass; + return {}; } //----------------------------------------------------------------------------- -bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, TextureFormat DepthFormat, VkSampleCountFlagBits Msaa, +bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, TextureFormat DepthFormat, Msaa Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, - RenderPassOutputUsage DepthOutputUsage, VkRenderPass* pRenderPass/*out*/, std::span ResolveFormats, bool HasDensityMap) + RenderPassOutputUsage DepthOutputUsage, RenderPass& rRenderPass/*out*/, std::span ResolveFormats, bool HasDensityMap) //----------------------------------------------------------------------------- { - assert(pRenderPass && *pRenderPass == VK_NULL_HANDLE); // check not already allocated and that we have a location to place the renderpass handle - VkResult RetVal = VK_SUCCESS; + assert( !rRenderPass ); // check not already allocated - std::vector PassAttachDescs; + std::vector PassAttachDescs; PassAttachDescs.reserve(ColorFormats.size()); bool bPresentPass = false; bool bHasDepth = DepthFormat != TextureFormat::UNDEFINED; + auto vkMsaa = EnumToVk( Msaa ); // Color Attachment for (const auto ColorFormat : ColorFormats) @@ -3592,8 +3707,8 @@ bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, Te /*pNext*/ nullptr, /*flags*/ 0, /*format*/ vkColorFormat, - /*samples*/ Msaa, - /*loadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, + /*samples*/ vkMsaa, + /*loadOp*/ InputUsageToVkAttachmentLoadOp(ColorInputUsage), /*storeOp*/ VK_ATTACHMENT_STORE_OP_STORE, /*stencilLoadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, /*stencilStoreOp*/ VK_ATTACHMENT_STORE_OP_DONT_CARE, @@ -3604,15 +3719,12 @@ bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, Te switch (ColorInputUsage) { case RenderPassInputUsage::Clear: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; break; case RenderPassInputUsage::Load: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; break; case RenderPassInputUsage::DontCare: - PassAttachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; PassAttachDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; break; }; @@ -3662,7 +3774,7 @@ bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, Te /*pNext*/ nullptr, /*flags*/ 0, /*format*/ TextureFormatToVk(DepthFormat), - /*samples*/ Msaa, + /*samples*/ vkMsaa, /*loadOp*/ ShouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, /*storeOp*/ VK_ATTACHMENT_STORE_OP_STORE, /*stencilLoadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, @@ -3751,7 +3863,7 @@ bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, Te ResolveReferences[ColorIdx] = { VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, nullptr, (uint32_t)PassAttachDescs.size(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; // Pass1 color buffers to resolve to at end of pass. - VkAttachmentDescription2KHR AttachmentDescResolvePass1 = { + VkAttachmentDescription2 AttachmentDescResolvePass1 = { VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, /*sType*/ nullptr, /*pNext*/ 0, vkResolveFormat/*format*/, @@ -3875,12 +3987,7 @@ bool Vulkan::CreateRenderPassVRS(std::span ColorFormats, Te RenderPassInfo.dependencyCount = (uint32_t)PassDependencies.size(); RenderPassInfo.pDependencies = PassDependencies.data(); - RetVal = m_ExtRenderPass2->m_vkCreateRenderPass2KHR(m_VulkanDevice, &RenderPassInfo, nullptr, pRenderPass); - if (!CheckVkError("vkCreateRenderPass2KHR()", RetVal)) - { - return false; - } - return true; + return CreateRenderPass( RenderPassInfo, rRenderPass ); } //----------------------------------------------------------------------------- @@ -3888,15 +3995,17 @@ bool Vulkan::CreatePipeline( VkPipelineCache pipelineCache, const VkPipelineVertexInputStateCreateInfo* visci, VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, + const RenderContext& renderingPassContext, const VkPipelineRasterizationStateCreateInfo* providedRS, const VkPipelineDepthStencilStateCreateInfo* providedDSS, const VkPipelineColorBlendStateCreateInfo* providedCBS, const VkPipelineMultisampleStateCreateInfo* providedMS, + const VkPipelineInputAssemblyStateCreateInfo* providedIA, std::span dynamicStates, const VkViewport* viewport, const VkRect2D* scissor, + VkShaderModule taskShaderModule, + VkShaderModule meshShaderModule, VkShaderModule vertShaderModule, VkShaderModule fragShaderModule, const VkSpecializationInfo* specializationInfo, @@ -3905,133 +4014,77 @@ bool Vulkan::CreatePipeline( VkPipeline* pipeline) //----------------------------------------------------------------------------- { - // Original version that only supports triangle lists - - // Create a basic pipeline structure with one or two shader stages, using the supplied cache. - - // Our vertex buffer describes a triangle list. - VkPipelineInputAssemblyStateCreateInfo ia_custom = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - ia_custom.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - // State for rasterization, such as polygon fill mode is defined. - // VkPipelineRasterizationStateCreateInfo rs_custom = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - // rs_custom.polygonMode = VK_POLYGON_MODE_FILL; - // rs_custom.cullMode = VK_CULL_MODE_NONE; - // rs_custom.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - // rs_custom.depthClampEnable = VK_FALSE; - // rs_custom.rasterizerDiscardEnable = VK_FALSE; - // rs_custom.depthBiasEnable = VK_FALSE; - // rs_custom.lineWidth = 1.0f; - - // Call the custom topology version - return Vulkan::CreatePipeline( - pipelineCache, - visci, - pipelineLayout, - renderPass, - subpass, - providedRS, - providedDSS, - providedCBS, - providedMS, - dynamicStates, - viewport, - scissor, - VK_NULL_HANDLE, - VK_NULL_HANDLE, - vertShaderModule, - fragShaderModule, - specializationInfo, - bAllowDerivation, - deriveFromPipeline, - pipeline, - ia_custom); -} - -//----------------------------------------------------------------------------- -bool Vulkan::CreatePipeline( - VkPipelineCache pipelineCache, - const VkPipelineVertexInputStateCreateInfo* visci, - VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, - const VkPipelineRasterizationStateCreateInfo* providedRS, - const VkPipelineDepthStencilStateCreateInfo* providedDSS, - const VkPipelineColorBlendStateCreateInfo* providedCBS, - const VkPipelineMultisampleStateCreateInfo* providedMS, - std::span dynamicStates, - const VkViewport* viewport, - const VkRect2D* scissor, - VkShaderModule taskShaderModule, - VkShaderModule meshShaderModule, - VkShaderModule vertShaderModule, - VkShaderModule fragShaderModule, - const VkSpecializationInfo* specializationInfo, - bool bAllowDerivation, - VkPipeline deriveFromPipeline, - VkPipeline* pipeline, - VkPipelineInputAssemblyStateCreateInfo ia_custom) - //----------------------------------------------------------------------------- -{ - // Create a basic pipeline structure with one or two shader stages, using the supplied cache. + // We support both dynamic rendering (no render pass) and old-style vkrenderpass pipelines + const bool dynamicRendering = renderingPassContext.IsDynamic(); // Our vertex buffer describes a triangle list. - VkPipelineInputAssemblyStateCreateInfo ia = ia_custom; + VkPipelineInputAssemblyStateCreateInfo ia{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST + }; // State for rasterization, such as polygon fill mode is defined. - VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - rs.polygonMode = VK_POLYGON_MODE_FILL; - rs.cullMode = VK_CULL_MODE_NONE; - rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rs.depthClampEnable = VK_FALSE; - rs.rasterizerDiscardEnable = VK_FALSE; - rs.depthBiasEnable = VK_FALSE; - rs.lineWidth = 1.0f; + VkPipelineRasterizationStateCreateInfo rs { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + .lineWidth = 1.0f + }; // Setup default blending state (disabled). Will be ignored if providedCBS is set. - VkPipelineColorBlendAttachmentState att_state[1] = {}; - att_state[0].colorWriteMask = 0xf; - att_state[0].blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo cb = {VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; - cb.attachmentCount = 1; - cb.pAttachments = &att_state[0]; + std::array< VkPipelineColorBlendAttachmentState, 10> att_state; + att_state.fill({ .blendEnable = VK_FALSE, .colorWriteMask = 0xf }); + assert(att_state.size() >= renderingPassContext.GetNumColorAttachmentFormats()); + + VkPipelineColorBlendStateCreateInfo cb { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .attachmentCount = std::max(uint32_t(1), uint32_t(renderingPassContext.GetNumColorAttachmentFormats())), + .pAttachments = att_state.data() + }; // Standard depth and stencil state is defined - VkPipelineDepthStencilStateCreateInfo ds = {VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; - ds.depthTestEnable = VK_TRUE; - ds.depthWriteEnable = VK_TRUE; - ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - ds.depthBoundsTestEnable = VK_FALSE; - ds.back.failOp = VK_STENCIL_OP_KEEP; - ds.back.passOp = VK_STENCIL_OP_KEEP; - ds.back.compareOp = VK_COMPARE_OP_ALWAYS; - ds.stencilTestEnable = VK_FALSE; + VkPipelineDepthStencilStateCreateInfo ds { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, + .depthBoundsTestEnable = VK_FALSE, + .stencilTestEnable = VK_FALSE, + .back = {.failOp = VK_STENCIL_OP_KEEP, + .passOp = VK_STENCIL_OP_KEEP, + .compareOp = VK_COMPARE_OP_ALWAYS}, + .minDepthBounds = 0.0f, + .maxDepthBounds = 1.0f, + }; ds.front = ds.back; - ds.minDepthBounds = 0.0f; - ds.maxDepthBounds = 1.0f; // Default to no msaa - VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - ms.pSampleMask = nullptr; - ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + VkPipelineMultisampleStateCreateInfo ms { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .pSampleMask = nullptr, + }; - // We define two shader stages: our vertex and fragment shader. + // We define four shader stages: vertex, fragment, task and mesh shaders. uint32_t stageCount = 1; - VkPipelineShaderStageCreateInfo shaderStages[3] = {}; + std::array shaderStages {}; uint32_t EVertBit = 0; uint32_t EFragBit = 1; uint32_t EMeshBit = 2; uint32_t ETaskBit = 3; - uint32_t EVertMask = 1 << EVertBit; - uint32_t EFragMask = 1 << EFragBit; - uint32_t EMeshMask = 1 << EMeshBit; - uint32_t ETaskMask = 1 << ETaskBit; - uint32_t EVertFragMask = EFragMask | EVertMask; - uint32_t EMeshFragMask = EFragMask | EMeshMask; - uint32_t ETaskMeshFragMask = ETaskMask | EMeshFragMask; + const uint32_t EVertMask = 1 << EVertBit; + const uint32_t EFragMask = 1 << EFragBit; + const uint32_t EMeshMask = 1 << EMeshBit; + const uint32_t ETaskMask = 1 << ETaskBit; + const uint32_t EVertFragMask = EFragMask | EVertMask; + const uint32_t EMeshFragMask = EFragMask | EMeshMask; + const uint32_t ETaskMeshFragMask = ETaskMask | EMeshFragMask; uint32_t drawbleType = 0; drawbleType |= (fragShaderModule != VK_NULL_HANDLE) << EFragBit; @@ -4099,6 +4152,10 @@ bool Vulkan::CreatePipeline( shaderStages[0].pName = "main"; shaderStages[0].pSpecializationInfo = specializationInfo; } + else + { + assert( 0 && "Unsupported combination of shader modules" ); + } // Set up the flags VkPipelineCreateFlags flags = 0; @@ -4181,6 +4238,9 @@ bool Vulkan::CreatePipeline( VkGraphicsPipelineCreateInfo pipelineCreateInfo{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipelineCreateInfo.flags = flags; pipelineCreateInfo.layout = pipelineLayout; + pipelineCreateInfo.pVertexInputState = visci; + pipelineCreateInfo.pInputAssemblyState = (providedIA != nullptr) ? providedIA : &ia; + if (drawbleType == EMeshFragMask || drawbleType == ETaskMeshFragMask) { pipelineCreateInfo.pVertexInputState = nullptr; pipelineCreateInfo.pInputAssemblyState = nullptr; @@ -4189,94 +4249,38 @@ bool Vulkan::CreatePipeline( pipelineCreateInfo.pVertexInputState = visci; pipelineCreateInfo.pInputAssemblyState = &ia; } + pipelineCreateInfo.pRasterizationState = (providedRS != nullptr) ? providedRS : &rs; pipelineCreateInfo.pColorBlendState = (providedCBS != nullptr) ? providedCBS : &cb; pipelineCreateInfo.pMultisampleState = (providedMS != nullptr) ? providedMS : &ms; pipelineCreateInfo.pViewportState = &ViewportInfo; pipelineCreateInfo.pDepthStencilState = (providedDSS != nullptr) ? providedDSS : &ds; pipelineCreateInfo.pStages = &shaderStages[0]; - pipelineCreateInfo.renderPass = renderPass; pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;// dynamicStateCreateInfo.dynamicStateCount > 0 ? : nullptr; pipelineCreateInfo.stageCount = stageCount; pipelineCreateInfo.basePipelineHandle = (deriveFromPipeline != VK_NULL_HANDLE) ? deriveFromPipeline : VK_NULL_HANDLE; pipelineCreateInfo.basePipelineIndex = -1; // indicates this field isn't used - pipelineCreateInfo.subpass = subpass; + pipelineCreateInfo.subpass = renderingPassContext.subPass; - VkResult RetVal = VK_SUCCESS; - RetVal = vkCreateGraphicsPipelines(m_VulkanDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, pipeline); - if (!CheckVkError("vkCreateGraphicsPipelines()", RetVal)) + fvk::VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo; + if (dynamicRendering) // don't move the pipelineRenderingCreateInfo local variable inside this if block; want to keep it in scope for vkCreateGraphicsPipelines + { + pipelineRenderingCreateInfo = std::move( renderingPassContext.GetPipelineRenderingCreateInfo() ); + pipelineCreateInfo.pNext = &pipelineRenderingCreateInfo; + } + else + { + pipelineCreateInfo.renderPass = renderingPassContext.GetRenderPass().mRenderPass; + } + + VkResult retVal = vkCreateGraphicsPipelines(m_VulkanDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, pipeline); + if (!CheckVkError("vkCreateGraphicsPipelines()", retVal)) { return false; } return true; -} - -//----------------------------------------------------------------------------- -bool Vulkan::CreatePipeline( - VkPipelineCache pipelineCache, - const VkPipelineVertexInputStateCreateInfo* visci, - VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, - const VkPipelineRasterizationStateCreateInfo* providedRS, - const VkPipelineDepthStencilStateCreateInfo* providedDSS, - const VkPipelineColorBlendStateCreateInfo* providedCBS, - const VkPipelineMultisampleStateCreateInfo* providedMS, - std::span dynamicStates, - const VkViewport* viewport, - const VkRect2D* scissor, - VkShaderModule taskShaderModule, - VkShaderModule meshShaderModule, - VkShaderModule vertShaderModule, - VkShaderModule fragShaderModule, - const VkSpecializationInfo* specializationInfo, - bool bAllowDerivation, - VkPipeline deriveFromPipeline, - VkPipeline* pipeline) - //----------------------------------------------------------------------------- -{ - // Original version that only supports triangle lists - - // Create a basic pipeline structure with one or two shader stages, using the supplied cache. - - // Our vertex buffer describes a triangle list. - VkPipelineInputAssemblyStateCreateInfo ia_custom = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - ia_custom.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - // State for rasterization, such as polygon fill mode is defined. - // VkPipelineRasterizationStateCreateInfo rs_custom = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - // rs_custom.polygonMode = VK_POLYGON_MODE_FILL; - // rs_custom.cullMode = VK_CULL_MODE_NONE; - // rs_custom.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - // rs_custom.depthClampEnable = VK_FALSE; - // rs_custom.rasterizerDiscardEnable = VK_FALSE; - // rs_custom.depthBiasEnable = VK_FALSE; - // rs_custom.lineWidth = 1.0f; - - // Call the custom topology version - return Vulkan::CreatePipeline( - pipelineCache, - visci, - pipelineLayout, - renderPass, - subpass, - providedRS, - providedDSS, - providedCBS, - providedMS, - dynamicStates, - viewport, - scissor, - taskShaderModule, - meshShaderModule, - vertShaderModule, - fragShaderModule, - specializationInfo, - bAllowDerivation, - deriveFromPipeline, - pipeline, - ia_custom); } //----------------------------------------------------------------------------- @@ -4298,9 +4302,9 @@ bool Vulkan::CreateComputePipeline( info.stage.pSpecializationInfo = specializationInfo; m_VulkanPipelineShaderStageCreateInfoExtensions.PushExtensions( &info.stage ); - VkResult RetVal = vkCreateComputePipelines(m_VulkanDevice, pipelineCache, 1, &info, nullptr, pipeline); + VkResult retVal = vkCreateComputePipelines(m_VulkanDevice, pipelineCache, 1, &info, nullptr, pipeline); m_VulkanPipelineShaderStageCreateInfoExtensions.PopExtensions( &info.stage ); - if (!CheckVkError("vkCreateComputePipelines()", RetVal)) + if (!CheckVkError("vkCreateComputePipelines()", retVal)) { return false; } @@ -4372,30 +4376,30 @@ bool Vulkan::RecreateSwapChain() Vulkan::BufferIndexAndFence Vulkan::SetNextBackBuffer() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; VkFence Fence = m_SwapchainBuffers[m_SwapchainCurrentIndx].fence; VkSemaphore BackBufferSemaphore = m_SwapchainBuffers[m_SwapchainCurrentIndx].semaphore; // Ensure the command buffer we will use to render the 'next image' is done rendering. - RetVal = vkWaitForFences(m_VulkanDevice, 1, &Fence, VK_TRUE, UINT64_MAX); - CheckVkError( "vkWaitForFences()", RetVal ); + retVal = vkWaitForFences(m_VulkanDevice, 1, &Fence, VK_TRUE, UINT64_MAX); + CheckVkError( "vkWaitForFences()", retVal ); // Reset Fence, ready to be set by the GPU when the command buffer has been submitted and completed. vkResetFences(m_VulkanDevice, 1, &Fence); // Get the next image to render to, then queue a wait until the image is ready uint32_t SwapchainPresentIndx = 0; - RetVal = vkAcquireNextImageKHR(m_VulkanDevice, m_VulkanSwapchain, UINT64_MAX, BackBufferSemaphore, VK_NULL_HANDLE, &SwapchainPresentIndx); - if (RetVal == VK_ERROR_OUT_OF_DATE_KHR) + retVal = vkAcquireNextImageKHR(m_VulkanDevice, m_VulkanSwapchain, UINT64_MAX, BackBufferSemaphore, VK_NULL_HANDLE, &SwapchainPresentIndx); + if (retVal == VK_ERROR_OUT_OF_DATE_KHR) { LOGI("VK_ERROR_OUT_OF_DATE_KHR not handled in sample"); } - else if (RetVal == VK_SUBOPTIMAL_KHR) + else if (retVal == VK_SUBOPTIMAL_KHR) { LOGI("VK_SUBOPTIMAL_KHR not handled in sample"); } - else if (!CheckVkError("vkAcquireNextImageKHR()", RetVal)) + else if (!CheckVkError("vkAcquireNextImageKHR()", retVal)) { } @@ -4441,8 +4445,8 @@ bool Vulkan::QueueSubmit(const std::span SubmitInfo, uint32_ { VkQueue Queue = m_VulkanQueues[QueueIndex].Queue; assert(Queue != VK_NULL_HANDLE); - VkResult RetVal = vkQueueSubmit(Queue, (uint32_t)SubmitInfo.size(), SubmitInfo.data(), CompletedFence); - if (!CheckVkError("vkQueueSubmit()", RetVal)) + VkResult retVal = vkQueueSubmit(Queue, (uint32_t)SubmitInfo.size(), SubmitInfo.data(), CompletedFence); + if (!CheckVkError("vkQueueSubmit()", retVal)) { return false; } @@ -4456,8 +4460,8 @@ bool Vulkan::QueueSubmit(const std::span SubmitInfo, uin VkQueue Queue = m_VulkanQueues[QueueIndex].Queue; assert(Queue != VK_NULL_HANDLE); assert( m_ExtKhrSynchronization2 && m_ExtKhrSynchronization2->Status == VulkanExtensionStatus::eLoaded ); - VkResult RetVal = m_ExtKhrSynchronization2->m_vkQueueSubmit2KHR(Queue, (uint32_t)SubmitInfo.size(), SubmitInfo.data(), CompletedFence); - if (!CheckVkError("vkQueueSubmit2KHR()", RetVal)) + VkResult retVal = m_ExtKhrSynchronization2->m_vkQueueSubmit2KHR(Queue, (uint32_t)SubmitInfo.size(), SubmitInfo.data(), CompletedFence); + if (!CheckVkError("vkQueueSubmit2KHR()", retVal)) { return false; } @@ -4468,7 +4472,7 @@ bool Vulkan::QueueSubmit(const std::span SubmitInfo, uin bool Vulkan::PresentQueue(const std::span pWaitSemaphores, uint32_t SwapchainPresentIndx) //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // Build up the present info and present the queue VkPresentInfoKHR PresentInfo {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; @@ -4486,15 +4490,15 @@ bool Vulkan::PresentQueue(const std::span pWaitSemaphores, ui PresentInfo.pWaitSemaphores = pWaitSemaphores.data(); } - RetVal = vkQueuePresentKHR(m_VulkanQueues[eGraphicsQueue].Queue, &PresentInfo); - if (RetVal == VK_ERROR_OUT_OF_DATE_KHR) + retVal = vkQueuePresentKHR(m_VulkanQueues[eGraphicsQueue].Queue, &PresentInfo); + if (retVal == VK_ERROR_OUT_OF_DATE_KHR) { // Swapchain is out of data. This can happen if the window was // resized after it was created (which is NOT supported here) LOGE("Swapchain is out of date! Unable to get next image!"); return false; } - else if (RetVal == VK_SUBOPTIMAL_KHR) + else if (retVal == VK_SUBOPTIMAL_KHR) { // This should not be spammed, print it 5 times and then be done! static int spamCount = 0; @@ -4510,7 +4514,7 @@ bool Vulkan::PresentQueue(const std::span pWaitSemaphores, ui } else { - if (!CheckVkError("vkQueuePresentKHR()", RetVal)) + if (!CheckVkError("vkQueuePresentKHR()", retVal)) { return false; } @@ -4531,23 +4535,23 @@ bool Vulkan::QueueWaitIdle(uint32_t QueueIndex) const //----------------------------------------------------------------------------- { assert(m_VulkanQueues[QueueIndex].Queue != VK_NULL_HANDLE); - VkResult RetVal = vkQueueWaitIdle(m_VulkanQueues[QueueIndex].Queue); - return CheckVkError("vkQueueWaitIdle()", RetVal); + VkResult retVal = vkQueueWaitIdle(m_VulkanQueues[QueueIndex].Queue); + return CheckVkError("vkQueueWaitIdle()", retVal); } //----------------------------------------------------------------------------- bool Vulkan::WaitUntilIdle() const //----------------------------------------------------------------------------- { - VkResult RetVal = vkDeviceWaitIdle(m_VulkanDevice); - return CheckVkError("vkDeviceWaitIdle()", RetVal); + VkResult retVal = vkDeviceWaitIdle(m_VulkanDevice); + return CheckVkError("vkDeviceWaitIdle()", retVal); } //----------------------------------------------------------------------------- VkCommandBuffer Vulkan::StartSetupCommandBuffer() //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // Make sure we are not leaking by starting without releasing! if (m_SetupCmdBuffer != VK_NULL_HANDLE) @@ -4569,8 +4573,8 @@ VkCommandBuffer Vulkan::StartSetupCommandBuffer() BeginInfo.flags = 0; BeginInfo.pInheritanceInfo = nullptr; - RetVal = vkBeginCommandBuffer(m_SetupCmdBuffer, &BeginInfo); - if (!CheckVkError("vkBeginCommandBuffer()", RetVal)) + retVal = vkBeginCommandBuffer(m_SetupCmdBuffer, &BeginInfo); + if (!CheckVkError("vkBeginCommandBuffer()", retVal)) { return VK_NULL_HANDLE; } @@ -4581,7 +4585,7 @@ VkCommandBuffer Vulkan::StartSetupCommandBuffer() void Vulkan::FinishSetupCommandBuffer(VkCommandBuffer setupCmdBuffer) //----------------------------------------------------------------------------- { - VkResult RetVal = VK_SUCCESS; + VkResult retVal = VK_SUCCESS; // Make sure we are not out of state! if (m_SetupCmdBuffer == VK_NULL_HANDLE) @@ -4595,8 +4599,8 @@ void Vulkan::FinishSetupCommandBuffer(VkCommandBuffer setupCmdBuffer) } // Stop recording the command buffer... - RetVal = vkEndCommandBuffer(m_SetupCmdBuffer); - if (!CheckVkError("vkEndCommandBuffer()", RetVal)) + retVal = vkEndCommandBuffer(m_SetupCmdBuffer); + if (!CheckVkError("vkEndCommandBuffer()", retVal)) { return; } @@ -5311,5 +5315,26 @@ void Vulkan::DumpDeviceInfo( const std::span DeviceFeatu m_VulkanDevicePropertiesPrintExtensions.PushExtensions( nullptr ); } } +} +//----------------------------------------------------------------------------- +void Vulkan::ReportFramebufferProperties( VkFramebuffer vkFrameBuffer ) const +//----------------------------------------------------------------------------- +{ + if (m_ExtQcomTileProperties && m_ExtQcomTileProperties->Status == VulkanExtensionStatus::eLoaded) + { + uint32_t propertiesCount = 0; + m_ExtQcomTileProperties->m_vkGetFramebufferTilePropertiesQCOM( m_VulkanDevice, vkFrameBuffer, &propertiesCount, nullptr ); + if (propertiesCount > 0) + { + std::vector< VkTilePropertiesQCOM> properties; + properties.resize(propertiesCount, {VK_STRUCTURE_TYPE_TILE_PROPERTIES_QCOM}); + m_ExtQcomTileProperties->m_vkGetFramebufferTilePropertiesQCOM( m_VulkanDevice, vkFrameBuffer, &propertiesCount, properties.data() ); + for (const auto& prop : properties) + { + LOGI("Framebuffer properties: %ux%ux%u (origin %ux%u; apron %ux%u)", prop.tileSize.width, prop.tileSize.height, prop.tileSize.depth, prop.origin.x, prop.origin.y, prop.apronSize.width, prop.apronSize.height); + } + } + } } + diff --git a/framework/code/vulkan/vulkan.hpp b/framework/code/vulkan/vulkan.hpp index 22682c6..b463a87 100644 --- a/framework/code/vulkan/vulkan.hpp +++ b/framework/code/vulkan/vulkan.hpp @@ -12,44 +12,29 @@ #ifdef OS_WINDOWS #define NOMINMAX #include -#define VK_USE_PLATFORM_WIN32_KHR #elif OS_ANDROID -#define VK_USE_PLATFORM_ANDROID_KHR #endif // OS_WINDOWS | OS_WINDOWS -// This definition allows prototypes of Vulkan API functions, -// rather than dynamically loading entrypoints to the API manually. -#define VK_PROTOTYPES - -#if defined(OS_WINDOWS) && !defined(VK_ENABLE_BETA_EXTENSIONS) -#define VK_ENABLE_BETA_EXTENSIONS -#endif -#include -#ifdef OS_ANDROID -//#include "VK_QCOM_render_pass_transform.h" -//#include "VK_KHR_fragment_shading_rate.h" -#endif // OS_ANDROID - +#include #include #include #include #include #include +#include #include #include #include -#include +#include #include "extension.hpp" #include "memory/vulkan/memoryManager.hpp" +#include "texture/textureFormat.hpp" +#include "framebuffer.hpp" +#include "../material/pipeline.hpp"///TODO: move pipeline.[ch]pp +#include "renderPass.hpp" // This should actually be defined in the makefile! #define USES_VULKAN_DEBUG_LAYERS -// Enable the Vulkan validation layer to also flag 'best practices' (if the debug/validation layers are in use) -//#define VULKAN_VALIDATION_ENABLE_BEST_PRACTICES -// Enable the Vulkan validation layer to also flag 'syncronization' issues (if the debug/validation layers are in use) -//#define VULKAN_VALIDATION_ENABLE_SYNCHRONIZATION -// Enable the Vulkan validation layer to output debugPrintf (if the debug/validation layers are in use) -//#define VULKAN_VALIDATION_ENABLE_PRINTF #define NUM_VULKAN_BUFFERS 8 // Kept track of with mSwapchainCurrentIdx @@ -59,33 +44,45 @@ // Forward declarations #if OS_ANDROID struct ANativeWindow; +#elif defined(OS_LINUX) +struct GLFWwindow; #endif // OS_ANDROID template class IndexBuffer; template class VertexBuffer; +template class RenderContext; +template class RenderPass; +class RenderPassClearData; struct VulkanDeviceFeaturePrint; struct VulkanDevicePropertiesPrint; struct VulkanInstanceFunctionPointerLookup; struct VulkanDeviceFunctionPointerLookup; -namespace ExtensionHelper { +namespace ExtensionLib { struct Ext_VK_KHR_surface; struct Ext_VK_KHR_get_physical_device_properties2; struct Ext_VK_KHR_get_surface_capabilities2; struct Ext_VK_KHR_draw_indirect_count; + struct Ext_VK_KHR_swapchain; struct Ext_VK_EXT_debug_utils; struct Ext_VK_EXT_debug_marker; struct Ext_VK_EXT_hdr_metadata; struct Ext_VK_KHR_fragment_shading_rate; struct Ext_VK_KHR_create_renderpass2; - struct Ext_VK_ARM_tensors; - struct Ext_VK_ARM_data_graph; struct Ext_VK_KHR_synchronization2; struct Ext_VK_QCOM_tile_properties; + struct Ext_VK_QCOM_tile_shading; + struct Ext_VK_QCOM_tile_memory_heap; + struct Ext_VK_KHR_get_memory_requirements2; + struct Ext_VK_ARM_tensors; + struct Ext_VK_ARM_data_graph; struct Vulkan_SubgroupPropertiesHook; struct Vulkan_StorageFeaturesHook; struct Ext_VK_KHR_mesh_shader; + struct Ext_VK_KHR_dynamic_rendering; }; +namespace vk {}; class VulkanDebugCallback; enum class TextureFormat; +enum class Msaa; bool CheckVkError(const char* pPrefix, VkResult CheckVal); @@ -94,32 +91,274 @@ bool CheckVkError(const char* pPrefix, VkResult CheckVal); //============================================================================= /// Single Swapchain image -typedef struct _SwapchainBuffers +struct SwapchainBuffers { - VkFramebuffer framebuffer; - VkImage image; - VkImageView view; - VkFence fence; - VkSemaphore semaphore; -} SwapchainBuffers; + SwapchainBuffers() noexcept = default; + SwapchainBuffers( SwapchainBuffers&& ) noexcept = default; + Framebuffer framebuffer; + VkImage image = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + VkFence fence = VK_NULL_HANDLE; + VkSemaphore semaphore = VK_NULL_HANDLE; +}; /// DepthBuffer memory, image and view typedef struct _DepthInfo { - TextureFormat format; - VkImageView view; - MemoryAllocatedBuffer image; + TextureFormat format; + VkImageView view; + MemoryAllocatedBuffer image; } DepthInfo; typedef struct _SurfaceFormat { - TextureFormat format; - VkColorSpaceKHR colorSpace; + TextureFormat format; + VkColorSpaceKHR colorSpace; } SurfaceFormat; + +// +// Wrapper template helpers for VkStructs +// in namespace fvk (framework vk). +// +namespace fvk +{ + template struct VkStructWrapperNext; // forward declaration + template struct VkStructWrapperMemberPtr; // forward declaration + template struct VkStructWrapperMemberArrayPtr;// forward declaration + //#define offsetof(s,m) ((::size_t)&reinterpret_cast((((s*)0)->m))) + //#define OwnedPointer(s,m) const_cast((((s*)0)->m)) + + /// @brief Helper for creating Vulkan api structures (eg for create parameters) with chaining via pNext. + /// @tparam VK_STRUCTURE Type of structure to create + /// @tparam T_STRUCTURE_TYPE Enum matching the structure (from VkStructureType) + + template + struct VkStructWrapperBase { + VkStructWrapperBase( const VkStructWrapperBase& ) = delete; + VkStructWrapperBase& operator=( const VkStructWrapperBase& ) noexcept = delete; + VkStructWrapperBase( VkStructWrapperBase&& other ) noexcept + { + *this = std::move( other ); + } + VkStructWrapperBase& operator=( VkStructWrapperBase&& other ) noexcept + { + if (this != std::addressof(other)) + { + // ugh! We can rely on these being C (vulkan) structures so no constructors, destructors + memcpy( this, &other, sizeof( *this ) ); + memset( &other, 0, sizeof( *this ) ); + other.s.sType = T_STRUCTURE_TYPE; + } + return *this; + } + + VkStructWrapperBase( VK_STRUCTURE&& contents = {} ) noexcept : s( std::forward( contents ) ) + { + static_assert(sizeof( *this ) == sizeof( VK_STRUCTURE )); + assert( contents.pNext == nullptr ); + //allow us to pass contents where sType is not set, compiler will have checked it is correct structure type so assume the data is good. + //this is nice because we can then use designated initializers to make things look nice + //assert( contents.sType == T_STRUCTURE_TYPE ); + s.sType = T_STRUCTURE_TYPE; + } + VkStructWrapperBase & operator=( VK_STRUCTURE&& other ) noexcept { + if (&other != this) { + assert( other.sType == T_STRUCTURE_TYPE ); + s = std::forward( other ); + // ugh! We can rely on these being (vulkan) structures so no constructors, destructors + memset( &other, 0, sizeof( *this ) ); + } + return *this; + }; + VK_STRUCTURE* operator&() { return &(this->s); } // This is 'unconventional'; if we start needing to use std::addressof then consider removing this and replacing with .get(). Saving grace may be that this is a non virtual class and only contains 's' + const VK_STRUCTURE* operator&() const { return &(this->s); } // This is 'unconventional'; if we start needing to use std::addressof then consider removing this and replacing with .get(). Saving grace may be that this is a non virtual class and only contains 's' + + using tStruct = VK_STRUCTURE; + static constexpr VkStructureType tStructType = T_STRUCTURE_TYPE; + VK_STRUCTURE s{.sType = T_STRUCTURE_TYPE}; + }; + + template... T_MEMBER_PTRS> + struct VkStructWrapperWithMembers : public VkStructWrapperBase + { + using tBase = VkStructWrapperBase; + VkStructWrapperWithMembers( const VkStructWrapperWithMembers& ) = delete; + VkStructWrapperWithMembers& operator=( const VkStructWrapperWithMembers& ) = delete; + VkStructWrapperWithMembers( VK_STRUCTURE&& contents ) noexcept : tBase( std::move( contents ) ) {} + VkStructWrapperWithMembers( VkStructWrapperWithMembers&& other ) noexcept + { + *this = std::move(other); + } + VkStructWrapperWithMembers& operator=( VkStructWrapperWithMembers&& other ) noexcept + { + if (this != std::addressof(other)) + { + tBase::operator=( std::move( static_cast(other) ) ); + mMemberPointers = std::move( other.mMemberPointers ); + std::fill( std::begin( other.mMemberPointers ), std::end( other.mMemberPointers ), nullptr ); + } + return *this; + } + ~VkStructWrapperWithMembers() + { + freemembers(); + } + template + auto AddMember() + { + return AddMemberArray(1).data(); + } + template + auto AddMemberArray( uint32_t arrayCount ) + { + auto member = findmember(); + void** pMember = reinterpret_cast(reinterpret_cast(&(this->s)) + member.offset); + T_MEMBER* p = (T_MEMBER*)calloc(sizeof(T_MEMBER), arrayCount); //yes, calloc not new[] because we dont have T_MEMEBER when freeing (freemembers()) so cant do delete[]. + static_assert(std::is_trivially_default_constructible_v< T_MEMBER>); // check that T_MEMBER is constructable with calloc (the Vulkan structs should all pass this check). If you hit this at compile time then you are trying to add a member that is not compatible with AddMemberArray/AddMember) + *pMember = p; + mMemberPointers[member.pointerIndex] = p; + if (member.countOffset > 0/*0 would be the offset of sType, so ok to use to indicate 'no count' */) + { + uint32_t* pCountMember = reinterpret_cast(reinterpret_cast(&(this->s)) + member.countOffset); + *pCountMember = arrayCount; + } + else + { + assert(arrayCount == 1); // if there is no count member of the structure then we expect the pointer to just be to a single value. + } + return std::span( p, arrayCount ); + } + protected: + std::array mMemberPointers{}; // pointers to data created by AddMember (duplicate of that pointer). Will be nullptr in the case where the member pointer was set but wasnt set via AddMember (and so we dont have ownership of the memory) + + struct Member { + size_t offset; //byte offset of pointer in VK_STRUCTURE + size_t countOffset; //byte offset of associated count(er) in VK_STRUCTURE + size_t pointerIndex; //index into mMemberPointers + }; + template TT_MEMBER_PTR, std::pair... TT_MEMBER_PTRS> + static consteval auto findmember( size_t memberIdx = 0 ) + { + if constexpr (TT_POINTEROFFSET == TT_MEMBER_PTR.first) + return Member{TT_MEMBER_PTR.first, TT_MEMBER_PTR.second, memberIdx}; + else if constexpr (sizeof...(TT_MEMBER_PTRS) != 0) + return findmember( memberIdx + 1 ); + else + { + static_assert((sizeof...(TT_MEMBER_PTRS) != 0) && "Cannot find pointer member in structure"); + } + return Member{}; + } + + template TT_MEMBER_PTR, std::pair... TT_MEMBER_PTRS> + void freemembers( size_t memberIdx = 0 ) + { + if constexpr (sizeof...(TT_MEMBER_PTRS) != 0) + { + freemembers( memberIdx + 1 ); + } + else + { + void** p = reinterpret_cast(reinterpret_cast(&(this->s)) + TT_MEMBER_PTR.first); + if (*p && this->mMemberPointers[memberIdx]) + { + assert( *p == mMemberPointers[memberIdx] ); + free(*p); // was allocated using calloc! (and checked to be trivially constructable + } + } + }; + }; + + template... T_MEMBER_PTRS> + struct VkStructWrapper : public std::conditional_t, VkStructWrapperBase> + { + using tStruct = VK_STRUCTURE; + using tBase = std::conditional_t, VkStructWrapperBase>; + VkStructWrapper( VkStructWrapper&& other ) noexcept : tBase( std::move( other ) ) + {} + VkStructWrapper& operator=( VkStructWrapper&& other ) noexcept = default; + + VkStructWrapper( tStruct&& contents = {} ) noexcept : tBase( std::move( contents ) ) + { + } + VkStructWrapper& operator=( tStruct&& other ) noexcept { + tBase::operator=( std::move( other ) ); + return *this; + } + tStruct& operator*() { return get(); } + tStruct* operator->() { return &get(); } + const tStruct& get() const { return this->s; } + tStruct& get() { return this->s; } + const tStruct& operator*() const { return get(); } + const tStruct* operator->() const { return &get(); } + tStruct* operator&() { return tBase::operator&(); } // This is 'unconventional'; if we start needing to use std::addressof then consider removing this and replacing with .get(). Saving grace may be that this is a non virtual class and only contains 's' + const tStruct* operator&() const { return tBase::operator&(); } // This is 'unconventional'; if we start needing to use std::addressof then consider removing this and replacing with .get(). Saving grace may be that this is a non virtual class and only contains 's' + template + auto& Add() { + auto* p = new VkStructWrapperNext(); + p->s.pNext = this->s.pNext; + this->s.pNext = p; + return *p; + } + template + auto& Add( VK_STRUCTURE_NEXT&& contents ) { + auto* p = new VkStructWrapperNext( std::forward< VK_STRUCTURE_NEXT>( contents ) ); + p->s.pNext = this->s.pNext; + this->s.pNext = p; + return *p; + } + template + auto& Add( VK_STRUCTURE_WRAPPER_NEXT&& contents ) { + auto* p = new std::remove_reference_t( std::move( contents.s ) ); + p->s.pNext = (void*)this->s.pNext; + this->s.pNext = p; + return *p; + } + + ~VkStructWrapper() { + VkBaseOutStructure* pNext = (VkBaseOutStructure*)this->s.pNext; + while (pNext) + { + auto* pNextPrev = pNext; + pNext = pNext->pNext; + free( pNextPrev ); + } + this->s.pNext = nullptr; + } + template friend struct VkStructWrapperNext; + }; + // Template for the structs chained to VkStructWrapper by calling Add + + /// @brief Helper class for vulkan structures chained (via pNext) onto a base VkStructWrapper + template + struct VkStructWrapperNext final : public VkStructWrapper + { + VkStructWrapperNext() noexcept : VkStructWrapper() {} + VkStructWrapperNext( VK_STRUCTURE&& contents ) noexcept : VkStructWrapper( std::forward< VK_STRUCTURE>( contents ) ) + {} + private: + // Destructor is private because we shouldnt construct/destruct these classes outside of VkStructWrapper::Add (and then the ~VkStructWrapper will cleanup the VkStructWrapperChild classes) + ~VkStructWrapperNext() noexcept { this->s.pNext = nullptr;/*detach from list so the base class destructor doesnt delete the pNext*/ } + }; + + // + // Set of aliases for common VkStructs wrapped in our helper + // + using VkRenderPassCreateInfo = VkStructWrapper; + using VkRenderPassCreateInfo2 = VkStructWrapper; + using VkRenderPassBeginInfo = VkStructWrapper < VkRenderPassBeginInfo, VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, std::pair{offsetof(VkRenderPassBeginInfo, pClearValues), offsetof(VkRenderPassBeginInfo, clearValueCount)} > ; + using VkFramebufferCreateInfo = VkStructWrapper; + using VkRenderPassTransformBeginInfoQCOM = VkStructWrapper; + using VkRenderingInfo = VkStructWrapper < VkRenderingInfo, VK_STRUCTURE_TYPE_RENDERING_INFO, std::pair{offsetof( VkRenderingInfo, pColorAttachments ), offsetof( VkRenderingInfo, colorAttachmentCount )}, std::pair{offsetof(VkRenderingInfo, pDepthAttachment), 0}, std::pair{offsetof(VkRenderingInfo, pStencilAttachment),0} >; + using VkPipelineRenderingCreateInfo = VkStructWrapper < VkPipelineRenderingCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, std::pair{offsetof(VkPipelineRenderingCreateInfo, pColorAttachmentFormats), offsetof(VkPipelineRenderingCreateInfo, colorAttachmentCount)} > ; + +} // namespace fvk; + + /// Vulkan API implementation /// Contains Vulkan top level (driver etc) objects and provides a simple initialization interface. -class Vulkan : public GraphicsApiBase +class Vulkan : public ::GraphicsApiBase { Vulkan(const Vulkan&) = delete; Vulkan& operator=(const Vulkan&) = delete; @@ -128,6 +367,9 @@ class Vulkan : public GraphicsApiBase using ViewportClass = VkViewport; using Rect2DClass = VkRect2D; using MemoryManager = MemoryManager; + using RenderContext = RenderContext; + using RenderPass = RenderPass; + using BufferHandleType = VkBuffer; public: Vulkan(); @@ -147,52 +389,47 @@ class Vulkan : public GraphicsApiBase /// @brief Register the vulkan extension (templated) as required by this app /// @tparam T template class for the extension /// @return pointer to the registered extension (guaranteed to not go out of scope or move until Vulkan deleted) - template - const T* RequiredExtension() { return AddExtension( std::make_unique( VulkanExtensionStatus::eRequired ) ); } + template + const T* RequiredExtension(ARGS&& ...args) { return AddExtension( std::make_unique( VulkanExtensionStatus::eRequired, std::forward(args)... ) ); } const VulkanExtension* RequiredExtension( const std::string& extensionName ) { return AddExtension( std::make_unique>( extensionName, VulkanExtensionStatus::eRequired ) ); } /// @brief Register the vulkan extension (templated) as desired by this app (but optional) /// @tparam T template class for the extension /// @return pointer to the registered extension (guaranteed to not go out of scope or move until Vulkan deleted) - template - const T* OptionalExtension() { return AddExtension( std::make_unique( VulkanExtensionStatus::eOptional ) ); } + template + const T* OptionalExtension(ARGS&& ...args) { return AddExtension( std::make_unique( VulkanExtensionStatus::eOptional, std::forward(args)... ) ); } const VulkanExtension* OptionalExtension( const std::string& extensionName ) { return AddExtension( std::make_unique>( extensionName, VulkanExtensionStatus::eOptional ) ); } template const T* AddExtension(std::unique_ptr extension) { - if constexpr (T::Type == VulkanExtensionType::eInstance) - { - auto it = AdditionalVulkanInstanceExtensions.try_emplace(extension->Name, std::move(extension)); - if (!it.second) + auto it = [&]() { + if constexpr (T::Type == VulkanExtensionType::eInstance) + return AdditionalVulkanInstanceExtensions.try_emplace( extension->Name, std::move( extension ) ); + else if constexpr (T::Type == VulkanExtensionType::eDevice) + return AdditionalVulkanDeviceExtensions.try_emplace( extension->Name, std::move( extension ) ); + else if constexpr (T::Type == VulkanExtensionType::eLayer) + return AdditionalVulkanInstanceLayers.try_emplace( extension->Name, std::move( extension ) ); + else { - assert(0); // cannot add another extension with the same name but (potentially) a different implementation class - return nullptr; + // constexpr static_assert in a template is 'problematic' in C++17 (and seemingly clang C++20). At some point this can turn back into static_assert(0, "..."); + [] () { static_assert(flag, "Unsupported VulkanExtensionType"); }(); } - return static_cast(&(*it.first->second.get())); - } - else if constexpr (T::Type == VulkanExtensionType::eDevice) - { - auto it = AdditionalVulkanDeviceExtensions.try_emplace(extension->Name, std::move(extension)); - if (!it.second) - { - assert(0); // cannot add another extension with the same name but (potentially) a different implementation class - return nullptr; - } - return static_cast(&(*it.first->second.get())); - } - else + }(); + if (!it.second) { - // constexpr static_assert in a template is 'problematic' in C++17 (and seemingly clang C++20). At some point this can turn back into static_assert(0, "..."); - [] () { static_assert(flag, "Unsupported VulkanExtensionType"); }(); + assert(0 && "Double 'add' of vulkan extension not allowed"); // cannot add another extension with the same name but (potentially) a different implementation class + return nullptr; } + return static_cast(&(*it.first->second.get())); } - protected: friend class Vulkan; /// (optional) list of 'additional' instance extensions that this app requires or would like (optional). std::map>> AdditionalVulkanInstanceExtensions; /// (optional) list of 'additional' device extensions that this app requires or would like (optional). std::map>> AdditionalVulkanDeviceExtensions; + /// (optional) list of 'additional' device layers that this app requires or would like (optional). + std::map>> AdditionalVulkanInstanceLayers; }; typedef std::function)> tSelectSurfaceFormatFn; @@ -219,10 +456,14 @@ class Vulkan : public GraphicsApiBase uint32_t GetSurfaceWidth() const { return m_SurfaceWidth; } ///< Swapchain width uint32_t GetSurfaceHeight() const { return m_SurfaceHeight; } ///< Swapchain height - VkFramebuffer GetSwapchainFramebuffer(uint32_t index) const { return m_SwapchainBuffers[index].framebuffer; } + const Framebuffer& GetSwapchainFramebuffer(uint32_t index) const { return m_SwapchainBuffers[index].framebuffer; } VkImage GetSwapchainImage(uint32_t index) const { return m_SwapchainBuffers[index].image; } TextureFormat GetSurfaceFormat() const { return m_SurfaceFormat; } + TextureFormat GetSwapchainFormat() const { return m_SurfaceFormat; } + size_t GetSwapchainBufferCount() const { return m_SwapchainBuffers.size(); } + TextureFormat GetSwapchainDepthFormat() const { return m_SwapchainDepth.format; } + /// Current buffer index (that can be filled) and the fence that should be signalled when the GPU completes this buffer and the semaphore to wait on before starting rendering. struct BufferIndexAndFence { @@ -317,7 +558,33 @@ class Vulkan : public GraphicsApiBase /// @param QueueIndex queue that this this command buffer was assigned to when allocated void FreeCommandBuffer(uint32_t QueueIndex, VkCommandBuffer CmdBuffer) const; - /// @brief Create a VkRenderPass (single subpass) with the given parameters. + + enum class RenderPassTileShadingMode { + Disabled = 0, + Enabled, + _unused, + PerTileShading + }; + + /// @brief Parameters for creation of a render pass. + struct RenderPassCreateData + { + std::span ColorFormats = {}; + TextureFormat DepthFormat = TextureFormat::UNDEFINED; + Msaa Msaa = Msaa::Samples1; + std::span ColorInputUsage = {}; + std::span ColorOutputUsage = {}; + RenderPassInputUsage DepthInputUsage = RenderPassInputUsage::Clear; + RenderPassOutputUsage DepthOutputUsage = RenderPassOutputUsage::Discard; + std::span ResolveFormats = {}; + RenderPassTileShadingMode TileShading = RenderPassTileShadingMode::Disabled; + std::array TileApron; + }; + + /// @brief Create a RenderPass (wrapper around VkRenderPass) for a single subpass with the given parameters. + RenderPass CreateRenderPass( const RenderPassCreateData& createData ); + + /// @brief Create a RenderPass (wrapper around VkRenderPass) for a single subpass with the given parameters. /// When we have ResolveFormats we resolve each member of ColorFormats out to a matching ResolveFormats buffer (unless ResolveFormat is VK_FORMAT_UNDEFINED, render pass expects ColorPass + number of defined Resolve buffers - resolve buffers must be VK_SAMPLE_COUNT_1_BIT) /// @param ColorFormats Format and usage of all expected color buffers. /// @param DepthFormat Format and usage of the depth buffer. @@ -329,24 +596,23 @@ class Vulkan : public GraphicsApiBase bool CreateRenderPass( std::span ColorFormats, TextureFormat DepthFormat, - VkSampleCountFlagBits Msaa, + Msaa Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, RenderPassOutputUsage DepthOutputUsage, - VkRenderPass* pRenderPass/*out*/, + RenderPass& rRenderPass/*out*/, std::span ResolveFormats = {} ); - bool CreateRenderPassVRS( std::span ColorFormats, TextureFormat DepthFormat, - VkSampleCountFlagBits Msaa, + Msaa Msaa, RenderPassInputUsage ColorInputUsage, RenderPassOutputUsage ColorOutputUsage, bool ShouldClearDepth, RenderPassOutputUsage DepthOutputUsage, - VkRenderPass* pRenderPass/*out*/, + RenderPass& rRenderPass/*out*/, std::span ResolveFormats = {}, bool hasDensityMap = false); @@ -363,15 +629,15 @@ class Vulkan : public GraphicsApiBase bool Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, - const std::span InternalPassMsaa, - VkSampleCountFlagBits OutputMsaa, - VkRenderPass* pRenderPass/*out*/ ); + const std::span InternalPassMsaa, + Msaa OutputMsaa, + RenderPass& rRenderPass/*out*/ ); bool Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, - VkSampleCountFlagBits InternalPassMsaa, /* same for both passes*/ - VkSampleCountFlagBits OutputMsaa, - VkRenderPass* pRenderPass/*out*/ ); + Msaa InternalPassMsaa, /* same for both passes*/ + Msaa OutputMsaa, + RenderPass& rRenderPass/*out*/ ); /// @brief Create a VkRenderPass (two subpasses) with MSAA shader resolves (uses and requires shader resolve extension). /// First subpass writes to the buffers described by 'InternalColorFormats'. Those buffers are cleared before use and discarded at the end of the (entire) pass. @@ -386,15 +652,20 @@ class Vulkan : public GraphicsApiBase bool CreateSubpassShaderResolveRenderPass(const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, - VkSampleCountFlagBits InternalMsaa, - VkSampleCountFlagBits OutputMsaa, - VkRenderPass* pRenderPass/*out*/ ); + Msaa InternalMsaa, + Msaa OutputMsaa, + RenderPass& rRenderPass/*out*/ ); + + /// @brief Create a render pass. + bool CreateRenderPass( const VkRenderPassCreateInfo& createInfo, RenderPass& rRenderPass/*out*/ ); + bool CreateRenderPass( const VkRenderPassCreateInfo2KHR& createInfo, RenderPass& rRenderPass/*out*/ ); - /// @brief Create a render pass pipeline. + /// @brief Create a render pass pipeline using a RenderContext that could contain renderpass/subpass + /// @brief or dynamic render pass data. /// @param pipelineCache (optional) vulkan pipeline cache /// @param visci (required) vertex input state /// @param pipelineLayout (required) Vulkan pipeline layout - /// @param renderPass (required) render pass to make this pipeline for + /// @param renderContext (required) context (with render pass) to make this pipeline for /// @param subpass (required) subpass number (0 if first subpass or not using subpasses) /// @param providedRS (optional) rasterization state /// @param providedDSS (optional) depth stencil state @@ -404,12 +675,12 @@ class Vulkan : public GraphicsApiBase VkPipelineCache pipelineCache, const VkPipelineVertexInputStateCreateInfo* visci, VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, + const RenderContext& renderContext, const VkPipelineRasterizationStateCreateInfo* providedRS, const VkPipelineDepthStencilStateCreateInfo* providedDSS, const VkPipelineColorBlendStateCreateInfo* providedCBS, const VkPipelineMultisampleStateCreateInfo* providedMS, + const VkPipelineInputAssemblyStateCreateInfo* providedIA, std::span dynamicStates, const VkViewport* viewport, const VkRect2D* scissor, @@ -420,71 +691,8 @@ class Vulkan : public GraphicsApiBase const VkSpecializationInfo* specializationInfo, bool bAllowDerivation, VkPipeline deriveFromPipeline, - VkPipeline* pipeline, - VkPipelineInputAssemblyStateCreateInfo ia_custom); - - /// @brief Create a render pass pipeline. - /// @param pipelineCache (optional) vulkan pipeline cache - /// @param visci (required) vertex input state - /// @param pipelineLayout (required) Vulkan pipeline layout - /// @param renderPass (required) render pass to make this pipeline for - /// @param subpass (required) subpass number (0 if first subpass or not using subpasses) - /// @param providedRS (optional) rasterization state - /// @param providedDSS (optional) depth stencil state - /// @param specializationInfo (optional) specialization constants (shared between vert and frag shader) - /// @return true on success - bool CreatePipeline( - VkPipelineCache pipelineCache, - const VkPipelineVertexInputStateCreateInfo* visci, - VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, - const VkPipelineRasterizationStateCreateInfo* providedRS, - const VkPipelineDepthStencilStateCreateInfo* providedDSS, - const VkPipelineColorBlendStateCreateInfo* providedCBS, - const VkPipelineMultisampleStateCreateInfo* providedMS, - std::span dynamicStates, - const VkViewport* viewport, - const VkRect2D* scissor, - VkShaderModule vertShaderModule, - VkShaderModule fragShaderModule, - const VkSpecializationInfo* specializationInfo, - bool bAllowDerivation, - VkPipeline deriveFromPipeline, VkPipeline* pipeline); - /// @brief Create a render pass pipeline. - /// @param pipelineCache (optional) vulkan pipeline cache - /// @param visci (required) vertex input state - /// @param pipelineLayout (required) Vulkan pipeline layout - /// @param renderPass (required) render pass to make this pipeline for - /// @param subpass (required) subpass number (0 if first subpass or not using subpasses) - /// @param providedRS (optional) rasterization state - /// @param providedDSS (optional) depth stencil state - /// @param specializationInfo (optional) specialization constants (shared between vert and frag shader) - /// @return true on success - bool CreatePipeline( - VkPipelineCache pipelineCache, - const VkPipelineVertexInputStateCreateInfo* visci, - VkPipelineLayout pipelineLayout, - VkRenderPass renderPass, - uint32_t subpass, - const VkPipelineRasterizationStateCreateInfo* providedRS, - const VkPipelineDepthStencilStateCreateInfo* providedDSS, - const VkPipelineColorBlendStateCreateInfo* providedCBS, - const VkPipelineMultisampleStateCreateInfo* providedMS, - std::span dynamicStates, - const VkViewport* viewport, - const VkRect2D* scissor, - VkShaderModule taskShaderModule, - VkShaderModule meshShaderModule, - VkShaderModule vertShaderModule, - VkShaderModule fragShaderModule, - const VkSpecializationInfo* specializationInfo, - bool bAllowDerivation, - VkPipeline deriveFromPipeline, - VkPipeline* pipeline); - /// @brief Create a compute shader pipeline /// @param pipelineCache (optional) vulkan pipeline cache /// @param pipelineLayout (required) vulkan pipeline layout @@ -552,6 +760,10 @@ class Vulkan : public GraphicsApiBase { return SetDebugObjectName( (uint64_t) shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, name ); } + bool SetDebugObjectName( const RenderPass& renderPass, const char* name ) + { + return SetDebugObjectName( renderPass.mRenderPass, name ); + } // Static helpers static const char* VulkanFormatString(VkFormat WhichFormat); @@ -562,6 +774,8 @@ class Vulkan : public GraphicsApiBase return m_VulkanGpuProperties.Base.properties.limits.timestampPeriod; } + void ReportFramebufferProperties( VkFramebuffer vkFrameBuffer ) const; + inline bool IsComputeQueueSupported() const { return m_VulkanGraphicsQueueSupportsCompute; @@ -646,7 +860,7 @@ class Vulkan : public GraphicsApiBase std::vector m_SwapchainBuffers; DepthInfo m_SwapchainDepth; // ... but they all use the same depth std::array m_SwapchainRenderPassDependencies{}; // dependencies used when creating m_SwapchainRenderPass - VkRenderPass m_SwapchainRenderPass; + RenderPass m_SwapchainRenderPass; TextureFormat m_SurfaceFormat; // Current surface format VkColorSpaceKHR m_SurfaceColorSpace; // Current surface colorspace @@ -719,6 +933,7 @@ class Vulkan : public GraphicsApiBase RegisteredExtensions m_InstanceExtensions; ///< Instance extensions RegisteredExtensions m_DeviceExtensions; ///< Device extensions RegisteredExtensions m_Vulkan11ProvidedExtensions; ///< 'extensions' included in Vulkan 1.1 (implicitly loaded). + RegisteredExtensions m_InstanceLayers; ///< Instance layers // Generic extension query (will fail to compile if type T does not define static Name). template @@ -736,31 +951,35 @@ class Vulkan : public GraphicsApiBase } // Template specializations for stored extension pointers (compile time lookup). template<> - const ExtensionHelper::Ext_VK_KHR_surface* GetExtension() const { return m_ExtKhrSurface; }; + const ExtensionLib::Ext_VK_KHR_surface* GetExtension() const { return m_ExtKhrSurface; }; template<> - const ExtensionHelper::Ext_VK_KHR_get_physical_device_properties2* GetExtension() const { return m_ExtKhrGetPhysicalDeviceProperties2; }; + const ExtensionLib::Ext_VK_KHR_get_physical_device_properties2* GetExtension() const { return m_ExtKhrGetPhysicalDeviceProperties2; }; template<> - const ExtensionHelper::Ext_VK_KHR_get_surface_capabilities2* GetExtension() const { return m_ExtSurfaceCapabilities2; }; + const ExtensionLib::Ext_VK_KHR_get_surface_capabilities2* GetExtension() const { return m_ExtSurfaceCapabilities2; }; template<> - const ExtensionHelper::Ext_VK_KHR_draw_indirect_count* GetExtension() const { return m_ExtKhrDrawIndirectCount; }; + const ExtensionLib::Ext_VK_KHR_draw_indirect_count* GetExtension() const { return m_ExtKhrDrawIndirectCount; }; template<> - const ExtensionHelper::Ext_VK_EXT_debug_utils* GetExtension() const { return m_ExtDebugUtils; }; + const ExtensionLib::Ext_VK_EXT_debug_utils* GetExtension() const { return m_ExtDebugUtils; }; template<> - const ExtensionHelper::Ext_VK_EXT_debug_marker* GetExtension() const { return m_ExtDebugMarker; }; + const ExtensionLib::Ext_VK_EXT_debug_marker* GetExtension() const { return m_ExtDebugMarker; }; template<> - const ExtensionHelper::Ext_VK_EXT_hdr_metadata* GetExtension() const { return m_ExtHdrMetadata; }; + const ExtensionLib::Ext_VK_EXT_hdr_metadata* GetExtension() const { return m_ExtHdrMetadata; }; template<> - const ExtensionHelper::Ext_VK_ARM_tensors* GetExtension() const { return m_ExtArmTensors; }; + const ExtensionLib::Ext_VK_KHR_synchronization2* GetExtension() const { return m_ExtKhrSynchronization2; }; template<> - const ExtensionHelper::Ext_VK_ARM_data_graph* GetExtension() const { return m_ExtArmDataGraph; }; + const ExtensionLib::Ext_VK_QCOM_tile_properties* GetExtension() const { return m_ExtQcomTileProperties; }; template<> - const ExtensionHelper::Ext_VK_KHR_synchronization2* GetExtension() const { return m_ExtKhrSynchronization2; }; + const ExtensionLib::Ext_VK_QCOM_tile_shading* GetExtension() const { return m_ExtQcomTileShading; }; template<> - const ExtensionHelper::Ext_VK_QCOM_tile_properties* GetExtension() const { return m_ExtQcomTileProperties; }; + const ExtensionLib::Ext_VK_QCOM_tile_memory_heap* GetExtension() const { return m_ExtQcomTileMemoryHeap; }; template<> - const ExtensionHelper::Vulkan_SubgroupPropertiesHook* GetExtension() const { return m_SubgroupProperties; }; + const ExtensionLib::Ext_VK_KHR_get_memory_requirements2* GetExtension() const { return m_ExtKhrGetMemoryRequirements; }; template<> - const ExtensionHelper::Ext_VK_KHR_mesh_shader* GetExtension() const { return m_ExtMeshShader; }; + const ExtensionLib::Vulkan_SubgroupPropertiesHook* GetExtension() const { return m_SubgroupProperties; }; + template<> + const ExtensionLib::Ext_VK_KHR_mesh_shader* GetExtension() const { return m_ExtMeshShader; }; + template<> + const ExtensionLib::Ext_VK_KHR_dynamic_rendering* GetExtension() const { return m_ExtDynamicRendering; }; template void AddExtensionHooks( ExtensionHook* t, TT... tt ) { @@ -785,7 +1004,9 @@ class Vulkan : public GraphicsApiBase HINSTANCE m_hInstance; HWND m_hWnd; #elif defined(OS_ANDROID) - ANativeWindow* m_pAndroidWindow; + ANativeWindow* m_pAndroidWindow = nullptr; +#elif defined(OS_LINUX) + GLFWwindow* m_pGlfwWindow = nullptr; #endif // defined(OS_WINDOWS) /// App driven configuration overrides (setup by app before Vulkan is initialized in order to potentially override default Vulkan configuration settings) @@ -794,15 +1015,6 @@ class Vulkan : public GraphicsApiBase /// Current frame index (internal - always in order) uint32_t m_SwapchainCurrentIndx; - // Debug/Validation Layers - std::vector m_InstanceLayerProps; - //std::vector m_InstanceExtensionProps; - - /// Layers we want to use (sorted alphabetically) - std::vector m_InstanceLayerNames; - /// Extensions we want to use (sorted alphabetically) - //std::vector m_InstanceExtensionNames; - // Vulkan Objects VkInstance m_VulkanInstance; uint32_t m_VulkanApiVersion; @@ -847,34 +1059,33 @@ class Vulkan : public GraphicsApiBase bool m_LayerKhronosValidationAvailable; bool m_ExtGlobalPriorityAvailable; - bool m_ExtSwapchainColorspaceAvailable; - bool m_ExtSurfaceCapabilities2Available; bool m_ExtRenderPassTransformAvailable; bool m_ExtRenderPassTransformEnabled; ///< Set when the device is using the renderpasstransform extension (ie display pre-rotation is happening and m_ExtRenderPassTransformAvailable) bool m_ExtRenderPassTransformLegacy; ///< Use the 'legacy' interface to VK_QCOM_render_pass_transform (older drivers) bool m_ExtRenderPassShaderResolveAvailable; - bool m_ExtDebugUtilsAvailable; bool m_ExtPortability; ///< Vulkan Portability extension present (and so must be enabled). Limited subset of Vulkan functionality (for backwards compatibility with other graphics api) - uint32_t m_ExtValidationFeaturesVersion; ///< Version of the VK_EXT_validation_features extension (0 validation is disabled and/or extension not loaded) // Extensions loaded by this class const VulkanExtension* m_ExtValidationFeatures = nullptr; - const ExtensionHelper::Ext_VK_KHR_surface* m_ExtKhrSurface = nullptr; - const ExtensionHelper::Ext_VK_KHR_get_physical_device_properties2*m_ExtKhrGetPhysicalDeviceProperties2 = nullptr; - const ExtensionHelper::Ext_VK_KHR_get_surface_capabilities2*m_ExtSurfaceCapabilities2 = nullptr; - const ExtensionHelper::Ext_VK_KHR_draw_indirect_count* m_ExtKhrDrawIndirectCount = nullptr; - const ExtensionHelper::Ext_VK_EXT_debug_utils* m_ExtDebugUtils = nullptr; - const ExtensionHelper::Ext_VK_EXT_debug_marker* m_ExtDebugMarker = nullptr; - const ExtensionHelper::Ext_VK_EXT_hdr_metadata* m_ExtHdrMetadata = nullptr; - const ExtensionHelper::Ext_VK_KHR_fragment_shading_rate* m_ExtFragmentShadingRate = nullptr; - const ExtensionHelper::Ext_VK_KHR_create_renderpass2* m_ExtRenderPass2 = nullptr; - const ExtensionHelper::Ext_VK_ARM_tensors* m_ExtArmTensors = nullptr; - const ExtensionHelper::Ext_VK_ARM_data_graph* m_ExtArmDataGraph = nullptr; - const ExtensionHelper::Ext_VK_KHR_synchronization2* m_ExtKhrSynchronization2 = nullptr; - const ExtensionHelper::Ext_VK_QCOM_tile_properties* m_ExtQcomTileProperties = nullptr; - const ExtensionHelper::Ext_VK_KHR_mesh_shader* m_ExtMeshShader = nullptr; - const ExtensionHelper::Vulkan_SubgroupPropertiesHook* m_SubgroupProperties = nullptr; - const ExtensionHelper::Vulkan_StorageFeaturesHook* m_StorageFeatures = nullptr; + const ExtensionLib::Ext_VK_KHR_surface* m_ExtKhrSurface = nullptr; + const ExtensionLib::Ext_VK_KHR_get_physical_device_properties2*m_ExtKhrGetPhysicalDeviceProperties2 = nullptr; + const ExtensionLib::Ext_VK_KHR_get_surface_capabilities2*m_ExtSurfaceCapabilities2 = nullptr; + const ExtensionLib::Ext_VK_KHR_draw_indirect_count* m_ExtKhrDrawIndirectCount = nullptr; + const ExtensionLib::Ext_VK_KHR_swapchain* m_ExtSwapchain = nullptr; + const ExtensionLib::Ext_VK_EXT_debug_utils* m_ExtDebugUtils = nullptr; + const ExtensionLib::Ext_VK_EXT_debug_marker* m_ExtDebugMarker = nullptr; + const ExtensionLib::Ext_VK_EXT_hdr_metadata* m_ExtHdrMetadata = nullptr; + const ExtensionLib::Ext_VK_KHR_fragment_shading_rate* m_ExtFragmentShadingRate = nullptr; + const ExtensionLib::Ext_VK_KHR_create_renderpass2* m_ExtRenderPass2 = nullptr; + const ExtensionLib::Ext_VK_KHR_synchronization2* m_ExtKhrSynchronization2 = nullptr; + const ExtensionLib::Ext_VK_QCOM_tile_properties* m_ExtQcomTileProperties = nullptr; + const ExtensionLib::Ext_VK_QCOM_tile_shading* m_ExtQcomTileShading = nullptr; + const ExtensionLib::Ext_VK_QCOM_tile_memory_heap* m_ExtQcomTileMemoryHeap = nullptr; + const ExtensionLib::Ext_VK_KHR_get_memory_requirements2* m_ExtKhrGetMemoryRequirements = nullptr; + const ExtensionLib::Ext_VK_KHR_mesh_shader* m_ExtMeshShader = nullptr; + const ExtensionLib::Ext_VK_KHR_dynamic_rendering* m_ExtDynamicRendering = nullptr; + const ExtensionLib::Vulkan_SubgroupPropertiesHook* m_SubgroupProperties = nullptr; + const ExtensionLib::Vulkan_StorageFeaturesHook* m_StorageFeatures = nullptr; #if defined (OS_ANDROID) bool m_ExtExternMemoryCapsAvailable; diff --git a/framework/code/vulkan/vulkanDebugCallback.cpp b/framework/code/vulkan/vulkanDebugCallback.cpp index c6d3e36..152363c 100644 --- a/framework/code/vulkan/vulkanDebugCallback.cpp +++ b/framework/code/vulkan/vulkanDebugCallback.cpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #include "vulkanDebugCallback.hpp" #include "system/os_common.h" @@ -183,7 +183,7 @@ VkBool32 VulkanDebugCallback::DebugCallback(VkDebugReportFlagsEXT Flags, VkDebug { LOGI("%s", szBuffer); } - LOGE("********** Validation Message - End **********"); + LOGE("********** Validation Message - End **********"); // Return "True" to cause layer to bail out and command is NOT sent to Vulkan // Return "False" to send it to the Vulkan layer (behave like final product) diff --git a/framework/code/vulkan/vulkanDebugCallback.hpp b/framework/code/vulkan/vulkanDebugCallback.hpp index 9f99fa0..8f4c221 100644 --- a/framework/code/vulkan/vulkanDebugCallback.hpp +++ b/framework/code/vulkan/vulkanDebugCallback.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include "vulkan.hpp" diff --git a/framework/code/vulkan/vulkan_support.cpp b/framework/code/vulkan/vulkan_support.cpp index f619d72..a2662b6 100644 --- a/framework/code/vulkan/vulkan_support.cpp +++ b/framework/code/vulkan/vulkan_support.cpp @@ -435,65 +435,6 @@ bool DumpImagePixelData( #endif // OS_WINDOWS } -#if 0 -//============================================================================= -// Wrap_VkImage -//============================================================================= - -//----------------------------------------------------------------------------- -Wrap_VkImage::Wrap_VkImage() -//----------------------------------------------------------------------------- -{ -} - -//----------------------------------------------------------------------------- -Wrap_VkImage::~Wrap_VkImage() -//----------------------------------------------------------------------------- -{ - Release(); -} - -//----------------------------------------------------------------------------- -bool Wrap_VkImage::Initialize(Vulkan* pVulkan, const VkImageCreateInfo& ImageInfo, MemoryUsage Usage, const char* pName) -//----------------------------------------------------------------------------- -{ - // If we have a name, save it - if (pName != NULL) - { - m_Name = pName; - } - - // Need Vulkan objects to release ourselves - m_pVulkan = pVulkan; - m_ImageInfo = ImageInfo; - m_Usage = Usage; - - auto& memoryManager = pVulkan->GetMemoryManager(); - m_VmaImage = memoryManager.CreateImage(ImageInfo, Usage); - - if( m_VmaImage ) - { - pVulkan->SetDebugObjectName( m_VmaImage.GetVkBuffer(), pName ); - } - - return !!m_VmaImage; -} - -//----------------------------------------------------------------------------- -void Wrap_VkImage::Release() -//----------------------------------------------------------------------------- -{ - if (m_pVulkan) - { - auto& memoryManager = m_pVulkan->GetMemoryManager(); - memoryManager.Destroy(std::move(m_VmaImage)); - } - m_pVulkan = nullptr; - m_ImageInfo = {}; - m_Name.clear(); -} -#endif - //============================================================================= // Wrap_VkSemaphore diff --git a/framework/code/vulkan/vulkan_support.hpp b/framework/code/vulkan/vulkan_support.hpp index 93ff224..9fa46a8 100644 --- a/framework/code/vulkan/vulkan_support.hpp +++ b/framework/code/vulkan/vulkan_support.hpp @@ -24,7 +24,6 @@ // Forward declarations class TimerPoolBase; -class CRenderTarget; typedef uint64_t VkFlags64; typedef VkFlags64 VkPipelineStageFlags2; typedef VkPipelineStageFlags2 VkPipelineStageFlags2KHR; @@ -35,8 +34,10 @@ typedef VkPipelineStageFlags2 VkPipelineStageFlags2KHR; struct ShaderInfo { - ShaderModuleT VertShaderModule; - ShaderModuleT FragShaderModule; + ShaderModule VertShaderModule; + ShaderModule FragShaderModule; + ShaderModule TaskShaderModule; + ShaderModule MeshShaderModule; }; /// Helper enum for setting up a VkPipelineColorBlendAttachmentState @@ -73,36 +74,6 @@ bool DumpImagePixelData( uint32_t arrayLayer, const tDumpImageOutputFn& outputFunction); -#if 0 -//============================================================================= -// Wrap_VkImage -//============================================================================= -class Wrap_VkImage -{ - // Functions - Wrap_VkImage(const Wrap_VkImage&) = delete; - Wrap_VkImage& operator=(const Wrap_VkImage&) = delete; -public: - Wrap_VkImage(); - ~Wrap_VkImage(); - - bool Initialize(Vulkan* pVulkan, const VkImageCreateInfo& ImageInfo, MemoryUsage TypeFlag, const char* pName = nullptr); - void Release(); - - const auto& GetImageInfo() const { return m_ImageInfo; } - - // Attributes -public: - std::string m_Name; - MemoryAllocatedBuffer m_VmaImage; - -private: - Vulkan * m_pVulkan = nullptr; - MemoryUsage m_Usage { MemoryUsage::Unknown }; - VkImageCreateInfo m_ImageInfo{}; -}; -#endif - /// /// Wrapper around VkSemaphore. /// Simplifies creation, use and destruction of VkSemaphore. diff --git a/framework/code/vulkanRT/accelerationInstanceBufferObject.cpp b/framework/code/vulkanRT/accelerationInstanceBufferObject.cpp index 88bf02c..54891ce 100644 --- a/framework/code/vulkanRT/accelerationInstanceBufferObject.cpp +++ b/framework/code/vulkanRT/accelerationInstanceBufferObject.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -20,7 +20,7 @@ AccelerationInstanceBufferObject& AccelerationInstanceBufferObject::operator=(Ac { if (this != &other) { - BufferT::operator=(std::move(other)); + Buffer::operator=(std::move(other)); mNumInstances = other.mNumInstances; other.mNumInstances = 0; } @@ -33,7 +33,7 @@ bool AccelerationInstanceBufferObject::Initialize(MemoryManager* pManager, size_ { mNumInstances = numInstances; - return BufferT::Initialize(pManager, (VkDeviceSize)(sizeof(VkAccelerationStructureInstanceKHR) * numInstances), BufferUsageFlags::AccelerationStructureBuild | BufferUsageFlags::ShaderDeviceAddress, MemoryUsage::CpuToGpu); + return Buffer::Initialize(pManager, (VkDeviceSize)(sizeof(VkAccelerationStructureInstanceKHR) * numInstances), BufferUsageFlags::AccelerationStructureBuild | BufferUsageFlags::ShaderDeviceAddress, MemoryUsage::CpuToGpu); } /////////////////////////////////////////////////////////////////////////////// @@ -41,7 +41,7 @@ bool AccelerationInstanceBufferObject::Initialize(MemoryManager* pManager, size_ void AccelerationInstanceBufferObject::Destroy() { mNumInstances = 0; - BufferT::Destroy(); + Buffer::Destroy(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/framework/code/vulkanRT/accelerationInstanceBufferObject.hpp b/framework/code/vulkanRT/accelerationInstanceBufferObject.hpp index 5e6d363..fe09743 100644 --- a/framework/code/vulkanRT/accelerationInstanceBufferObject.hpp +++ b/framework/code/vulkanRT/accelerationInstanceBufferObject.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -14,7 +14,7 @@ // forward declarations // Class -class AccelerationInstanceBufferObject : public BufferT +class AccelerationInstanceBufferObject : public Buffer { AccelerationInstanceBufferObject& operator=(const AccelerationInstanceBufferObject&) = delete; AccelerationInstanceBufferObject(const AccelerationInstanceBufferObject&) = delete; diff --git a/framework/code/vulkanRT/accelerationStructure.cpp b/framework/code/vulkanRT/accelerationStructure.cpp index c13a415..61344b0 100644 --- a/framework/code/vulkanRT/accelerationStructure.cpp +++ b/framework/code/vulkanRT/accelerationStructure.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -9,7 +9,7 @@ #include "vulkanRT.hpp" -bool AccelerationStructure::Create(Vulkan& vulkan, VulkanRT& vulkanRT, size_t accelerationStructureSize, VkAccelerationStructureTypeKHR type) +bool AccelerationStructure::Create(Vulkan& vulkan, VulkanRT& vulkanRT, size_t accelerationStructureSize, VkAccelerationStructureTypeKHR type) { auto& memoryManager = vulkan.GetMemoryManager(); @@ -37,7 +37,7 @@ bool AccelerationStructure::Create(Vulkan& vulkan, VulkanRT& vulkanRT, size_t ac return true; } -void AccelerationStructure::CloneCmd(VulkanRT& vulkanRT, VkCommandBuffer cmdBuffer, const AccelerationStructure& src) const +void AccelerationStructure::CloneCmd(VulkanRT& vulkanRT, VkCommandBuffer cmdBuffer, const AccelerationStructure& src) const { assert(src.m_accelerationStructure != VK_NULL_HANDLE); assert(m_accelerationStructure != VK_NULL_HANDLE); @@ -49,7 +49,7 @@ void AccelerationStructure::CloneCmd(VulkanRT& vulkanRT, VkCommandBuffer cmdBuff vulkanRT.vkCmdCopyAccelerationStructureKHR(cmdBuffer, ©Info); } -void AccelerationStructure::Destroy(Vulkan& vulkan, VulkanRT& vulkanRT) +void AccelerationStructure::Destroy(Vulkan& vulkan, VulkanRT& vulkanRT) { vulkanRT.vkDestroyAccelerationStructureKHR(m_accelerationStructure); m_accelerationStructure = VK_NULL_HANDLE; diff --git a/framework/code/vulkanRT/accelerationStructure.hpp b/framework/code/vulkanRT/accelerationStructure.hpp index 87a9c83..7965e96 100644 --- a/framework/code/vulkanRT/accelerationStructure.hpp +++ b/framework/code/vulkanRT/accelerationStructure.hpp @@ -1,26 +1,28 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #pragma once #include "vulkan/vulkan.hpp" #include "memory/memoryMapped.hpp" +#include "rayTracing/accelerationStructure.hpp" class VulkanRT; /// @brief Base class for anything using a Ray Tracing Acceleration structure. /// Contains the Acceleration structure and it's memory buffer -class AccelerationStructure +template<> +class AccelerationStructure final : public AccelerationStructureBase { - AccelerationStructure& operator=(const AccelerationStructure&) = delete; - AccelerationStructure(const AccelerationStructure&) = delete; + AccelerationStructure& operator=(const AccelerationStructure&) = delete; + AccelerationStructure(const AccelerationStructure&) = delete; public: AccelerationStructure() : m_accelerationStructure(VK_NULL_HANDLE) {} - AccelerationStructure(AccelerationStructure&& other) noexcept + AccelerationStructure( AccelerationStructure&& other) noexcept : m_accelerationStructureBuffer(std::move(other.m_accelerationStructureBuffer)) { m_accelerationStructure = other.m_accelerationStructure; @@ -38,15 +40,15 @@ class AccelerationStructure /// Add the GPU commands to clone the @src accleleration structure into this one. /// Requires/expects that the acceleration structure is Created and is sized appropriately. - void CloneCmd(VulkanRT& vulkanRT, VkCommandBuffer cmdBuffer, const AccelerationStructure& src) const; + void CloneCmd(VulkanRT& vulkanRT, VkCommandBuffer cmdBuffer, const AccelerationStructure& src) const; /// @return Vulkan Acceletation Structure owned by this class - VkAccelerationStructureKHR GetVkAccelerationStructure()const { return m_accelerationStructure; } + const VkAccelerationStructureKHR& GetVkAccelerationStructure()const { return m_accelerationStructure; } private: - VkAccelerationStructureKHR m_accelerationStructure; - MemoryAllocatedBuffer m_accelerationStructureBuffer; -}; + VkAccelerationStructureKHR m_accelerationStructure; + MemoryAllocatedBuffer m_accelerationStructureBuffer; +}; class AccelerationStructureScratch @@ -69,8 +71,8 @@ class AccelerationStructureScratch uint64_t GetDeviceAddress() const { return m_scratchBufferDeviceAddress; } private: - MemoryAllocatedBuffer m_scratchBuffer; - uint64_t m_scratchBufferDeviceAddress; + MemoryAllocatedBuffer m_scratchBuffer; + uint64_t m_scratchBufferDeviceAddress; }; @@ -117,7 +119,7 @@ class AccelerationStructureUpdateable virtual void Destroy(Vulkan& vulkan, VulkanRT& vulkanRT); /// @return Vulkan Acceleration Structure for Ray Querying/Tracing - VkAccelerationStructureKHR GetVkAccelerationStructure()const { return m_target.GetVkAccelerationStructure(); } + const AccelerationStructure& GetAccelerationStructure() const { return m_target; } VkDeviceSize GetBuildScratchSize() const { return m_buildScratchSize; } VkDeviceSize GetUpdateScratchSize() const { return m_updateScratchSize; } @@ -137,6 +139,6 @@ class AccelerationStructureUpdateable UpdateMode m_updateMode = UpdateMode::NotUpdatable; size_t m_buildScratchSize = 0; size_t m_updateScratchSize = 0; - AccelerationStructure m_source; - AccelerationStructure m_target; + AccelerationStructure m_source; + AccelerationStructure m_target; }; diff --git a/framework/code/vulkanRT/extensionHelpersRT.hpp b/framework/code/vulkanRT/extensionLibRT.hpp similarity index 82% rename from framework/code/vulkanRT/extensionHelpersRT.hpp rename to framework/code/vulkanRT/extensionLibRT.hpp index 0cdda35..d09a981 100644 --- a/framework/code/vulkanRT/extensionHelpersRT.hpp +++ b/framework/code/vulkanRT/extensionLibRT.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,11 +12,11 @@ class Vulkan; /// -/// Library of Vulkan extension helpers for Ray Tracing +/// Library of Vulkan extensions for Ray Tracing /// -namespace ExtensionHelperRT +namespace ExtensionLibRT { #if VK_KHR_acceleration_structure @@ -60,32 +60,6 @@ namespace ExtensionHelperRT #endif // VK_KHR_acceleration_structure -#if VK_KHR_buffer_device_address - - struct Ext_VK_KHR_buffer_device_address : public VulkanDeviceFeaturesExtensionHelper - { - static constexpr auto Name = VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME; - Ext_VK_KHR_buffer_device_address( VulkanExtensionStatus status = VulkanExtensionStatus::eRequired ) - : VulkanDeviceFeaturesExtensionHelper(Name, status) - {} - void PrintFeatures() const override - { - LOGI("FeaturesBufferDeviceAddress: "); - LOGI(" bufferDeviceAddress: %s", this->AvailableFeatures.bufferDeviceAddress ? "True" : "False"); - LOGI(" bufferDeviceAddressCaptureReplay: %s", this->AvailableFeatures.bufferDeviceAddressCaptureReplay ? "True" : "False"); - LOGI(" bufferDeviceAddressMultiDevice: %s", this->AvailableFeatures.bufferDeviceAddressMultiDevice ? "True" : "False"); - } - void PopulateRequestedFeatures() override - { - // Enable just the 'bufferDeviceAddress' feature - RequestedFeatures.bufferDeviceAddress = AvailableFeatures.bufferDeviceAddress; - } - }; - -#endif // VK_KHR_buffer_device_address - - - #if VK_KHR_ray_tracing_pipeline struct Ext_VK_KHR_ray_tracing_pipeline : public VulkanDeviceFeaturePropertiesExtensionHelper diff --git a/framework/code/vulkanRT/meshObjectRT.cpp b/framework/code/vulkanRT/meshObjectRT.cpp index 12cb9be..82e2338 100644 --- a/framework/code/vulkanRT/meshObjectRT.cpp +++ b/framework/code/vulkanRT/meshObjectRT.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -39,12 +39,12 @@ MeshObjectRT::~MeshObjectRT() {} -bool MeshObjectRT::Create( Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectIntermediate& meshObject, MeshObjectRT::UpdateMode updateMode ) +bool MeshObjectRT::Create( Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectIntermediate& meshObject, bool allowDataAccess, MeshObjectRT::UpdateMode updateMode) { auto& memoryManager = vulkan.GetMemoryManager(); // Convert the 'intermediate' mesh vertex data in to 'vertexFormat' and then copy in to a VertexBuffer (device memory) - BufferT deviceVertexBuffer = CreateRtVertexBuffer(memoryManager, meshObject); + auto deviceVertexBuffer = CreateRtVertexBuffer(memoryManager, meshObject); if (deviceVertexBuffer.GetVkBuffer() == VK_NULL_HANDLE) { return false; @@ -70,8 +70,8 @@ bool MeshObjectRT::Create( Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectI // // If meshObject has an index buffer then copy the data in to a Vulkan buffer // - std::optional deviceIndexBuffer; - if (!MeshHelper::CreateIndexBuffer(memoryManager, meshObject, deviceIndexBuffer, BufferUsageFlags::AccelerationStructureBuild|BufferUsageFlags::ShaderDeviceAddress)) + std::optional> deviceIndexBuffer; + if (!MeshHelper::CreateIndexBuffer(memoryManager, meshObject, deviceIndexBuffer, BufferUsageFlags::AccelerationStructureBuild|BufferUsageFlags::ShaderDeviceAddress)) { return false; } @@ -103,6 +103,9 @@ bool MeshObjectRT::Create( Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectI if (updateMode != UpdateMode::NotUpdatable) asFlags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; + if (allowDataAccess) + asFlags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_DATA_ACCESS_KHR ; + std::array asMaxPrimitiveCounts = { primitiveCount }; // Create the Acceleration structue and its backing buffer. Potentially creates a second AS too if the VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR flag is set and updateMode PingPong (two buffers needed for updates). @@ -139,7 +142,7 @@ bool MeshObjectRT::Create( Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectI // Acceleration structure is ready. Grab its device address (for the top level structure to reference) assert(!m_DeviceAddress); - m_DeviceAddress = vulkanRT.vkGetAccelerationStructureDeviceAddressKHR( GetVkAccelerationStructure() ); + m_DeviceAddress = vulkanRT.vkGetAccelerationStructureDeviceAddressKHR( GetAccelerationStructure().GetVkAccelerationStructure() ); // Cleanup asBuildScratch.Destroy(vulkan); @@ -155,11 +158,11 @@ void MeshObjectRT::Destroy(Vulkan& vulkan, VulkanRT& vulkanRT) } -BufferT MeshObjectRT::CreateRtVertexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, BufferUsageFlags usageFlags) +Buffer MeshObjectRT::CreateRtVertexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, BufferUsageFlags usageFlags) { - BufferT deviceVertexBuffer; + Buffer deviceVertexBuffer; { - const std::vector vertexData = MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(meshObject.m_VertexBuffer, accelerationStructureVertexFormat); + const std::vector vertexData = MeshObjectIntermediate::CopyFatVertexToFormattedBuffer(meshObject.m_VertexBuffer, {}, accelerationStructureVertexFormat); if (!deviceVertexBuffer.Initialize(&memoryManager, vertexData.size() * sizeof(uint32_t), usageFlags, vertexData.data())) { return {}; diff --git a/framework/code/vulkanRT/meshObjectRT.hpp b/framework/code/vulkanRT/meshObjectRT.hpp index 25c791a..3a89f8b 100644 --- a/framework/code/vulkanRT/meshObjectRT.hpp +++ b/framework/code/vulkanRT/meshObjectRT.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -27,13 +27,13 @@ class MeshObjectRT final : public AccelerationStructureUpdateable MeshObjectRT(MeshObjectRT&& other); ~MeshObjectRT(); - bool Create(Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectIntermediate& meshObject, UpdateMode updateMode = UpdateMode::NotUpdatable); + bool Create(Vulkan& vulkan, VulkanRT& vulkanRT, const MeshObjectIntermediate& meshObject, bool allowDataAccess = false, UpdateMode updateMode = UpdateMode::NotUpdatable); void Destroy(Vulkan& vulkan, VulkanRT& vulkanRT) override; uint64_t GetVkDeviceAddress() const { return m_DeviceAddress; } std::pair GetAABB() const { return { m_AABBMin, m_AABBMax }; }; - static BufferT CreateRtVertexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, BufferUsageFlags usageFlags = BufferUsageFlags::AccelerationStructureBuild|BufferUsageFlags::ShaderDeviceAddress); + static Buffer CreateRtVertexBuffer(MemoryManager& memoryManager, const MeshObjectIntermediate& meshObject, BufferUsageFlags usageFlags = BufferUsageFlags::AccelerationStructureBuild|BufferUsageFlags::ShaderDeviceAddress); static const VertexFormat accelerationStructureVertexFormat; static const VkFormat accelerationStructureVkVertexFormat; diff --git a/framework/code/vulkanRT/meshUpdateRT.cpp b/framework/code/vulkanRT/meshUpdateRT.cpp index 2aeecc9..6875884 100644 --- a/framework/code/vulkanRT/meshUpdateRT.cpp +++ b/framework/code/vulkanRT/meshUpdateRT.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/framework/code/vulkanRT/meshUpdateRT.hpp b/framework/code/vulkanRT/meshUpdateRT.hpp index 1508f4c..ee633e9 100644 --- a/framework/code/vulkanRT/meshUpdateRT.hpp +++ b/framework/code/vulkanRT/meshUpdateRT.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -47,11 +47,11 @@ class MeshUpdateRT protected: // Variables needed for Updates - BufferT m_VertexBuffer; - std::optional m_IndexBuffer; // optional, depending on meshObject having indices - AccelerationStructureScratch m_UpdateScratch; - VkDeviceAddress m_VertexBufferDeviceAddress = {}; - VkDeviceAddress m_IndexBufferDeviceAddress = {}; - uint32_t m_PrimitiveCount = 0; - uint32_t m_NumVertices = 0; + Buffer m_VertexBuffer; + std::optional> m_IndexBuffer; // optional, depending on meshObject having indices + AccelerationStructureScratch m_UpdateScratch; + VkDeviceAddress m_VertexBufferDeviceAddress = {}; + VkDeviceAddress m_IndexBufferDeviceAddress = {}; + uint32_t m_PrimitiveCount = 0; + uint32_t m_NumVertices = 0; }; diff --git a/framework/code/vulkanRT/sceneRT.cpp b/framework/code/vulkanRT/sceneRT.cpp index fc07b04..99f68f5 100644 --- a/framework/code/vulkanRT/sceneRT.cpp +++ b/framework/code/vulkanRT/sceneRT.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -11,6 +11,7 @@ #include #include #include +#include #include "system/os_common.h" SceneRTBase::SceneRTBase() @@ -73,7 +74,7 @@ void SceneRT::RemoveAllInstances() m_pendingInstancesUpdate = !m_instances.empty(); } -bool SceneRT::CreateAccelerationStructure( UpdateMode updateMode, size_t minSize ) +bool SceneRT::CreateAccelerationStructure( UpdateMode updateMode, size_t minSize) { auto& memoryManager = m_vulkan.GetMemoryManager(); @@ -92,8 +93,7 @@ bool SceneRT::CreateAccelerationStructure( UpdateMode updateMode, size_t minSize m_accelerationBuildFlags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; if (updateMode != UpdateMode::NotUpdatable) - m_accelerationBuildFlags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; - + m_accelerationBuildFlags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; { // Populate the instance buffer data diff --git a/framework/code/vulkanRT/sceneRT.hpp b/framework/code/vulkanRT/sceneRT.hpp index e519899..d5e0eee 100644 --- a/framework/code/vulkanRT/sceneRT.hpp +++ b/framework/code/vulkanRT/sceneRT.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -76,7 +76,7 @@ class SceneRT : public SceneRTBase, public AccelerationStructureUpdateable typedef std::unordered_map tMeshObjectUnorderedMap; // Build the initial acceleration (and scratch) buffer. - bool CreateAccelerationStructure( UpdateMode updateMode, size_t minSize ); + bool CreateAccelerationStructure( UpdateMode updateMode, size_t minSize); protected: Vulkan& m_vulkan; @@ -146,7 +146,7 @@ class SceneRTCulled : public SceneRT void Update(const SceneRTCullable& scene/*scene we will generate a RT Acceleration Structure from*/, const T_TEST& cullTest); // Build the initial acceleration (and scratch) buffer. (makes base class function public) - bool CreateAccelerationStructure(UpdateMode updateMode, size_t minSize) { return SceneRT::CreateAccelerationStructure( updateMode, minSize ); } + bool CreateAccelerationStructure(UpdateMode updateMode, size_t minSize) { return SceneRT::CreateAccelerationStructure( updateMode, minSize); } protected: void PostQueryUpdate(const SceneRTCullable& scene, bool regenerateInstances); diff --git a/framework/code/vulkanRT/traceable.cpp b/framework/code/vulkanRT/traceable.cpp index a73ecc9..cec55d7 100644 --- a/framework/code/vulkanRT/traceable.cpp +++ b/framework/code/vulkanRT/traceable.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -42,7 +42,7 @@ TraceablePass::~TraceablePass() assert(mPipeline == VK_NULL_HANDLE); } -Traceable::Traceable(VulkanRT& vulkanRt, Material&& material) +Traceable::Traceable(VulkanRT& vulkanRt, Material&& material) : mVulkanRt(vulkanRt) , mMaterial(std::move(material)) { @@ -137,7 +137,7 @@ bool Traceable::Init() for (uint32_t materialPassIdx = 0; materialPassIdx < (uint32_t) materialPasses.size(); ++materialPassIdx) { const auto& materialPass = materialPasses[materialPassIdx]; - const auto& shaderPass = materialPass.mShaderPass; + const auto& shaderPass = materialPass.GetShaderPass(); assert(std::holds_alternative>(shaderPass.m_shaders.m_modules)); // Usually pipeline layout will be stored with the shader but if the descriptor set layout is 'dynamic' (and stored in the materialPass) the pipeline layout will also be in the materialPass. @@ -246,8 +246,8 @@ bool Traceable::Init() VK_ACCESS_SHADER_READ_BIT, //dstAccessMask VK_QUEUE_FAMILY_IGNORED, //srcQueueFamilyIndex; VK_QUEUE_FAMILY_IGNORED, //dstQueueFamilyIndex; - buffer.buffer, //buffer; - buffer.offset, //offset + buffer.buffer(), //buffer; + buffer.offset(), //offset VK_WHOLE_SIZE //size }); }); @@ -382,7 +382,7 @@ void Traceable::SetRayThreadCount(uint32_t passIdx, const std::arraym_VkCommandBuffer, traceablePass, (whichBuffer + startDescriptorSetIdx) % (uint32_t)traceablePass.GetVkDescriptorSets().size()); + DispatchPass(*cmdBuffers, traceablePass, (whichBuffer + startDescriptorSetIdx) % (uint32_t)traceablePass.GetVkDescriptorSets().size()); } ++cmdBuffers; } diff --git a/framework/code/vulkanRT/traceable.hpp b/framework/code/vulkanRT/traceable.hpp index 4080fe3..77fa8c7 100644 --- a/framework/code/vulkanRT/traceable.hpp +++ b/framework/code/vulkanRT/traceable.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,15 +12,15 @@ #include #include #include -#include "vulkan/vulkan.h" +#include #include "memory/vulkan/bufferObject.hpp" #include "memory/vulkan/uniform.hpp" -#include "material/material.hpp" +#include "material/vulkan/material.hpp" // Forward Declarations class VulkanRT; -template class CommandListT; -using Wrap_VkCommandBuffer = CommandListT; +template class CommandList; +using CommandListVulkan = CommandList; /// Encapsulates a 'traceable' pass, contains the materialpass, pipeline, etc). @@ -31,7 +31,7 @@ class TraceablePass TraceablePass(const TraceablePass&) = delete; TraceablePass& operator=(const TraceablePass&) = delete; public: - TraceablePass(const MaterialPass& materialPass, VkPipeline pipeline, VkPipelineLayout pipelineLayout, Uniform shaderBindingTable, std::array shaderBindingTableAddresses + TraceablePass(const MaterialPass& materialPass, VkPipeline pipeline, VkPipelineLayout pipelineLayout, Uniform shaderBindingTable, std::array shaderBindingTableAddresses //, std::vector imageMemoryBarriers, std::vector bufferMemoryBarriers, bool needsExecutionBarrier ) : mMaterialPass(materialPass) @@ -55,12 +55,12 @@ class TraceablePass void SetRayThreadCount(std::array count) { mRayThreadCount = count; } const auto& GetRayThreadCount() const { return mRayThreadCount; } - const MaterialPass& mMaterialPass; + const MaterialPass& mMaterialPass; VkPipeline mPipeline = VK_NULL_HANDLE; // Owned by us - VkPipelineLayout mPipelineLayout; // Owned by ShaderPass or MaterialPass + VkPipelineLayout mPipelineLayout; // Owned by ShaderPass or MaterialPassBase - Uniform mShaderBindingTable; + UniformVulkan mShaderBindingTable; std::array< VkStridedDeviceAddressRegionKHR, 4> mShaderBindingTableAddresses{}; @@ -72,14 +72,14 @@ class TraceablePass }; -/// Encapsulates a 'traceable' object, contains the Material, traceable passes. +/// Encapsulates a 'traceable' object, contains the MaterialBase, traceable passes. /// Similar to a Drawable but for ray tracing class Traceable { Traceable(const Traceable&) = delete; Traceable& operator=(const Traceable&) = delete; public: - Traceable(VulkanRT& vulkanRt, Material&&); + Traceable(VulkanRT& vulkanRt, Material&&); ~Traceable(); bool Init(); @@ -99,8 +99,8 @@ class Traceable /// @param cmdBuffers pointer to array of commandbuffers we want to fill, assumed to be sized [numRenderPasses] /// @param numRenderPasses number of cmdBuffers to fill /// @param startDescriptorSetIdx index of the first descriptor set to add - void Dispatch(Wrap_VkCommandBuffer* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx) const; - void Dispatch(Wrap_VkCommandBuffer& cmdBuffer) + void Dispatch(CommandListVulkan* cmdBuffers, uint32_t numCmdBuffers, uint32_t startDescriptorSetIdx) const; + void Dispatch(CommandListVulkan& cmdBuffer) { Dispatch(&cmdBuffer, 1, 0); } @@ -110,7 +110,7 @@ class Traceable void DispatchPass(VkCommandBuffer cmdBuffer, const TraceablePass& TraceablePass, uint32_t bufferIdx) const; protected: - Material mMaterial; + Material mMaterial; VulkanRT& mVulkanRt; std::vector mPasses; //std::vector mImageInputMemoryBarriers; // barriers for ENTRY to this computable. Could be stored in material if non compute shaders wanted this barrier information, non compute should use the pass depenancies tho diff --git a/framework/code/vulkanRT/vulkanRT.cpp b/framework/code/vulkanRT/vulkanRT.cpp index b2c9d9e..6d9165e 100644 --- a/framework/code/vulkanRT/vulkanRT.cpp +++ b/framework/code/vulkanRT/vulkanRT.cpp @@ -1,15 +1,16 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "vulkanRT.hpp" -#include "extensionHelpersRT.hpp" +#include "extensionLibRT.hpp" #include #include -#include "vulkan/extensionHelpers.hpp" +#include +#include "vulkan/extensionLib.hpp" #include "memory/vulkan/uniform.hpp" #include "vulkan/vulkan_support.hpp" @@ -65,16 +66,21 @@ void VulkanRT::RegisterRequiredVulkanLayerExtensions( Vulkan::AppConfiguration& { assert( m_pExtKhrBufferDeviceAddress == nullptr ); assert( m_pExtKhrRayTracingPipeline == nullptr ); - m_pExtKhrBufferDeviceAddress = appConfig.RequiredExtension(); - appConfig.RequiredExtension(); - appConfig.RequiredExtension(); + m_pExtKhrBufferDeviceAddress = appConfig.RequiredExtension(); + appConfig.RequiredExtension(); + appConfig.RequiredExtension(); + appConfig.RequiredExtension(); appConfig.RequiredExtension( "VK_KHR_deferred_host_operations" ); appConfig.RequiredExtension( "VK_KHR_shader_float_controls" ); appConfig.RequiredExtension( "VK_KHR_spirv_1_4" ); - appConfig.RequiredExtension(); + appConfig.RequiredExtension(); if (!rayQueryOnly) { - m_pExtKhrRayTracingPipeline = appConfig.OptionalExtension(); +#if ANDROID + m_pExtKhrRayTracingPipeline = appConfig.OptionalExtension(); +#else // ANDROID + m_pExtKhrRayTracingPipeline = appConfig.RequiredExtension(); +#endif // ANDROID } } @@ -233,7 +239,7 @@ bool VulkanRT::CreateRTPipeline(VkPipelineCache pipelineCache, const size_t shaderGroupBaseAlignment = rayTracingPipelineProperties.shaderGroupBaseAlignment; { - // Initialize the shader binding table strides and sizes (without the device address) to help us calulate the GPU Shader Binding Table buffer size. + // Initialize the shader binding table strides and sizes (without the device address) to help us calulate the GPU ShaderBase Binding Table buffer size. // Ray generation shader binding assert(rayGenerationCount == 1); // must have one and only one raygen @@ -324,8 +330,8 @@ bool VulkanRTStub::Init() void VulkanRTStub::RegisterRequiredVulkanLayerExtensions( Vulkan::AppConfiguration& appConfig, bool rayQueryOnly ) { // Register a subset of what actual Ray Tracing or Ray Query needs. - appConfig.RequiredExtension(); - appConfig.RequiredExtension(); + appConfig.RequiredExtension(); + appConfig.RequiredExtension(); appConfig.RequiredExtension( "VK_KHR_shader_float_controls" ); appConfig.RequiredExtension( "VK_KHR_spirv_1_4" ); } diff --git a/framework/code/vulkanRT/vulkanRT.hpp b/framework/code/vulkanRT/vulkanRT.hpp index 4204839..b8f67a4 100644 --- a/framework/code/vulkanRT/vulkanRT.hpp +++ b/framework/code/vulkanRT/vulkanRT.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -12,9 +12,12 @@ // forward declarations class Vulkan; template struct Uniform; -namespace ExtensionHelperRT +namespace ExtensionLib { struct Ext_VK_KHR_buffer_device_address; +}; +namespace ExtensionLibRT +{ struct Ext_VK_KHR_ray_tracing_pipeline; }; @@ -76,8 +79,8 @@ class VulkanRT private: Vulkan& m_vulkan; - const ExtensionHelperRT::Ext_VK_KHR_buffer_device_address* m_pExtKhrBufferDeviceAddress = nullptr; ///< owned by Vulkan - const ExtensionHelperRT::Ext_VK_KHR_ray_tracing_pipeline* m_pExtKhrRayTracingPipeline = nullptr; ///< owned by Vulkan + const ExtensionLib::Ext_VK_KHR_buffer_device_address* m_pExtKhrBufferDeviceAddress = nullptr; ///< owned by Vulkan + const ExtensionLibRT::Ext_VK_KHR_ray_tracing_pipeline* m_pExtKhrRayTracingPipeline = nullptr; ///< owned by Vulkan PFN_vkGetAccelerationStructureBuildSizesKHR m_fpGetAccelerationStructureBuildSizesKHR = nullptr; PFN_vkCreateAccelerationStructureKHR m_fpCreateAccelerationStructureKHR = nullptr; diff --git a/framework/external/.gitignore b/framework/external/.gitignore new file mode 100644 index 0000000..1b65954 --- /dev/null +++ b/framework/external/.gitignore @@ -0,0 +1,10 @@ +#ignore exerything in external (expected to be populated by cmake fetchcontent) +* + +# Except build/housekeeping +!CMakeLists.txt +!.gitignore + +# and select externals checked into our repository +!blueNoisei/* + diff --git a/framework/external/KTX-Software b/framework/external/KTX-Software deleted file mode 160000 index 38f4858..0000000 --- a/framework/external/KTX-Software +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 38f485866a0bb6381d725dd69c8836e269917f84 diff --git a/framework/external/Vulkan-Headers b/framework/external/Vulkan-Headers deleted file mode 160000 index a4f8ada..0000000 --- a/framework/external/Vulkan-Headers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a4f8ada9f4f97c45b8c89c57997be9cebaae65d2 diff --git a/framework/external/VulkanMemoryAllocator b/framework/external/VulkanMemoryAllocator deleted file mode 160000 index db4c163..0000000 --- a/framework/external/VulkanMemoryAllocator +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db4c1639bf30c51bbddcd813c6521b3473afa1a1 diff --git a/framework/external/eigen b/framework/external/eigen deleted file mode 160000 index 46f8a18..0000000 --- a/framework/external/eigen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 46f8a18567731925e06a7389a6c611e1dc420ea8 diff --git a/framework/external/glm b/framework/external/glm deleted file mode 160000 index bf71a83..0000000 --- a/framework/external/glm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf71a834948186f4097caa076cd2663c69a10e1e diff --git a/framework/external/imgui b/framework/external/imgui deleted file mode 160000 index 4f9ba19..0000000 --- a/framework/external/imgui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4f9ba19e520bea478f5cb654d37ef45e6404bd52 diff --git a/framework/external/implot b/framework/external/implot deleted file mode 160000 index f156599..0000000 --- a/framework/external/implot +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f156599faefe316f7dd20fe6c783bf87c8bb6fd9 diff --git a/framework/external/json b/framework/external/json deleted file mode 160000 index db78ac1..0000000 --- a/framework/external/json +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db78ac1d7716f56fc9f1b030b715f872f93964e4 diff --git a/framework/external/portable-file-dialogs b/framework/external/portable-file-dialogs deleted file mode 160000 index 7f852d8..0000000 --- a/framework/external/portable-file-dialogs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f852d88a480020d7f91957cbcefe514fc95000c diff --git a/framework/external/tinygltf b/framework/external/tinygltf deleted file mode 160000 index 925b836..0000000 --- a/framework/external/tinygltf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 925b83627a136d24411067031893dc8ea661444d diff --git a/framework/external/tinyobjloader b/framework/external/tinyobjloader deleted file mode 160000 index e39c173..0000000 --- a/framework/external/tinyobjloader +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e39c1737bc61c8dce28be7932cfe839d408e7838 diff --git a/framework/schema/shaderAliasSchema.json b/framework/schema/shaderAliasSchema.json index bbedb7d..055922c 100644 --- a/framework/schema/shaderAliasSchema.json +++ b/framework/schema/shaderAliasSchema.json @@ -25,6 +25,12 @@ "examples": [ "vulkan1.1", "spirv1.4" ], "minLength": 1 }, + "Stage": { + "type": "string", + "description": "uses specified stage rather than parsing the file extension", + "examples": [ "frag", "vert", "tesc", "tese", "geom", "or", "comp" ], + "minLength": 1 + }, "$schema": { "type": "string" } diff --git a/framework/schema/shaderSchema.json b/framework/schema/shaderSchema.json index 04a71db..b91b400 100644 --- a/framework/schema/shaderSchema.json +++ b/framework/schema/shaderSchema.json @@ -45,6 +45,14 @@ "RayMiss": { "type": "string", "description": "filename of the spir-v ray miss shader" + }, + "Mesh": { + "type": "string", + "description": "filename of the spir-v mesh shader" + }, + "Task": { + "type": "string", + "description": "filename of the spir-v task shader" } }, "minProperties": 1, @@ -57,6 +65,9 @@ "type": "object", "description": "descriptor set", "properties": { + "Name": { + "type": "string" + }, "Buffers": { "type": "array", "items": { @@ -66,7 +77,7 @@ "Type": { "type": "string", "description": "Descriptor set(s) type", - "enum": [ "ImageSampler", "UniformBuffer", "Texture", "Sampler", "StorageBuffer", "ImageStorage", "InputAttachment", "AccelerationStructure" ] + "enum": [ "UniformBuffer", "ImageSampler", "ImageSampled", "Sampler", "StorageBuffer", "ImageStorage", "InputAttachment", "AccelerationStructure", "DescriptorTable", "Unused" ] }, "Stages": { "type": "array", @@ -74,7 +85,7 @@ "items": { "type": "string", "description": "Stage name", - "enum": [ "Vertex", "Fragment", "Compute", "RayGeneration", "RayClosestHit", "RayAnyHit", "RayMiss" ] + "enum": [ "Vertex", "Fragment", "Compute", "RayGeneration", "RayClosestHit", "RayAnyHit", "RayMiss", "Mesh", "Task" ] }, "uniqueItems": true, "minItems": 1, @@ -108,6 +119,43 @@ "minItems": 1 } }, + "RootSamplers": { + "type": "array", + "description": "Immutable Root signature Texture Samplers (DX12 only)", + "items": { + "type": "object", + "description": "root sampler", + "properties": { + "Mode": { + "type": "string", + "enum": [ "Undefined", "Repeat", "MirroredRepeat", "ClampEdge", "ClampBorder", "MirroredClampEdge" ] + }, + "Filter": { + "type": "string", + "enum": [ "Undefined", "Nearest", "Linear" ] + }, + "MipFilter": { + "type": "string", + "enum": [ "Undefined", "Nearest", "Linear" ] + }, + "MipBias": { + "type": "number" + }, + "Anisotropy": { + "type": "integer" + }, + "MinLod": { + "type": "number" + }, + "MaxLod": { + "type": "number" + } + }, + "additionalProperties": false + }, + "additionalProperties": false, + "minItems": 0 + }, "VertexBindings": { "type": "array", "description": "Vertex set binding(s)", @@ -190,38 +238,41 @@ "additionalProperties": false }, "Outputs": { - "type": "array", - "description": "Output(s) description (frag shader)", - "items": { - "type": "object", - "description": "fixedFunction settings", - "properties": { - "BlendEnable": { - "type": "boolean" - }, - "SrcColorBlendFactor": { - "type": "string", - "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] - }, - "DstColorBlendFactor": { - "type": "string", - "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] - }, - "SrcAlphaBlendFactor": { - "type": "string", - "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] - }, - "DstAlphaBlendFactor": { - "type": "string", - "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] - }, - "ColorWriteMask": { - "type": "integer", - "minimum": 0 - } - }, - "additionalProperties": false - } + "type": "array", + "description": "Output(s) description (frag shader)", + "items": { + "type": "object", + "description": "fixedFunction settings", + "properties": { + "BlendEnable": { + "type": "boolean" + }, + "SrcColorBlendFactor": { + "type": "string", + "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] + }, + "DstColorBlendFactor": { + "type": "string", + "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] + }, + "SrcAlphaBlendFactor": { + "type": "string", + "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] + }, + "DstAlphaBlendFactor": { + "type": "string", + "enum": [ "Zero", "One", "SrcAlpha", "OneMinusSrcAlpha", "DstAlpha", "OneMinusDstAlpha" ] + }, + "ColorWriteMask": { + "type": "integer", + "minimum": 0 + }, + "Name": { + "type": "string" + } + }, + "additionalProperties": false + } }, "WorkGroup": { "type": "object", @@ -237,9 +288,16 @@ "default": 1 }, "description": "local workgroup size (x,y,z)" + }, + "PerTileDispatch": { + "type": "boolean", + "description": "Enable per tile dispatch mode (threads dispatched = Number of pixels in tile x rate setting in shader')" } }, - "required": [ "LocalSize" ], + "oneOf": [ + { "required": [ "LocalSize" ] }, + { "required": [ "PerTileDispatch" ] } + ], "additionalProperties": false }, "RayTracing": { @@ -298,7 +356,7 @@ }, "Type": { "type": "string", - "enum": [ "Int32", "Float", "Vec2", "Vec3", "Vec4", "Int16", "Float16", "F16Vec2", "F16Vec3", "F16Vec4" ], + "enum": [ "Int32", "Float", "Vec2", "Vec3", "Vec4", "Int16", "UInt16", "Float16", "F16Vec2", "F16Vec3", "F16Vec4", "I16Vec2", "I16Vec3", "I16Vec4", "U16Vec2", "U16Vec3", "U16Vec4", "IVec2", "IVec3", "IVec4", "UVec2", "UVec3", "UVec4" ], "description": "Element data type" } }, @@ -332,6 +390,9 @@ }, "minItems": 0, "additionalProperties": false + }, + "TileShader": { + }, "$schema": { "type": "string" diff --git a/project/android/build.bat b/project/android/build.bat index 7744b14..bdcba8a 100644 --- a/project/android/build.bat +++ b/project/android/build.bat @@ -1,4 +1,6 @@ @echo off +cd /D "%~dp0" + if "%1"=="" ( echo Building all projects [build.bat can be invoved with project name argument to build just one project, if desired] call gradlew build -Dorg.gradle.warning.mode=none --no-daemon -Dorg.gradle.jvmargs=-Xmx4096m --continue -x test diff --git a/project/android/build.gradle b/project/android/build.gradle index 88387e3..9e2c699 100644 --- a/project/android/build.gradle +++ b/project/android/build.gradle @@ -2,14 +2,14 @@ // Put configuration in here for all projects. buildscript { - repositories { mavenCentral() google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:8.8.1' + //classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.2.0' // No application dependencies here; they belong in the individual module build.gradle files } } @@ -22,13 +22,34 @@ allprojects { } // Default ndk version (should be picked up in app specific build.gradle files) -// project.ext.set("ndkVersionDefault", "25.0.8221429-beta2") // "24.0.8215888") - project.ext.set("ndkVersionDefault", "24.0.8215888") + project.ext.set("ndkVersionDefault", "26.0.10792818") // Put all build related files into a single top level build dir. buildDir = file("${rootDir.absolutePath}/../../build/android/${project.name}") + + dependencies { + } } task clean(type: Delete) { delete rootProject.buildDir } + +/* +task lintCheck() { + getAllTasks(true).each { + def lintTasks = it.value.findAll { it.name.contains("lint") } + lintTasks.each { + it.enabled = false + } + } +} + +afterEvaluate { + Set result = tasks.findAll { task -> task.name.contains(':lint') } + result.each { Task task -> + printLn("disabled task {task.name}") + task.enabled = false + } +} +*/ diff --git a/project/android/cmake/AddShadersDir.cmake b/project/android/cmake/AddShadersDir.cmake deleted file mode 100644 index 300439b..0000000 --- a/project/android/cmake/AddShadersDir.cmake +++ /dev/null @@ -1,170 +0,0 @@ -# -# Build shaders -# Add everything with .frag .vert .comp extension from the shaders/ directory and build using Vulkan shader compiler. -# - -# Make sure we have the Vulkan compiler -find_program( - GLSL_VALIDATOR - glslangValidator - DOC "Vulkan Shader Compiler (glslangValidator) (is Vulkan SDK installed?)" - REQUIRED -) - -# Make sure we have the DXC compiler. See if it is installed alngside Vulkan first. -if(DEFINED ENV{VULKAN_SDK}) - cmake_path(SET VULKAN_SDK_PATH NORMALIZE $ENV{VULKAN_SDK}) - find_program( - DXC_EXE - dxc - HINTS ${VULKAN_SDK_PATH} - PATH_SUFFIXES bin - NO_DEFAULT_PATH - DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" - OPTIONAL - ) -endif() - -# We couldnt find dxc installed with Vulkan, look for it on the path (should find the Windows SDK version) -find_program( - DXC_EXE - dxc - DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" - REQUIRED -) - -# Runs the command to get the pluginval version (more recent versions of dcx support --version, older ones dont and print the version inside -help) -if (NOT DEFINED ENV{DXC_VERSION}) - message("DCX compiler found at: ${DXC_EXE}") - execute_process(COMMAND ${DXC_EXE} --help - OUTPUT_VARIABLE DXC_VERSION_RAW - ERROR_VARIABLE DXC_VERSION_RAW) - string(REGEX MATCH "Version: ([^\r\n]+)[\r\n]" - DXC_VERSION ${DXC_VERSION_RAW}) - string(REGEX REPLACE "Version: (.*)" - "\\1" - DXC_VERSION ${DXC_VERSION}) - message( "DXC version: ${DXC_VERSION}" ) - set(ENV{DXC_VERSION} ${DXC_VERSION}) -endif() - - -# Custom shader include direcotry -if(DEFINED SHADER_INCLUDE) - list(TRANSFORM SHADER_INCLUDE PREPEND "-I") -endif() - -# Ensure we have a place to put the .d dependency files emitted by the compiler -set(DEPENDS_PATH ${CMAKE_CURRENT_BINARY_DIR}/Media/Shaders/) -file(MAKE_DIRECTORY ${DEPENDS_PATH}) - -# -# Scan through shaders directory looking for shader source files and generate build commands for them -# -file(GLOB files "shaders/*.vert" "shaders/*.frag" "shaders/*.comp") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env vulkan1.1 ${file} -o ${OUTPUT_FILENAME}.spv --depfile ${OUTPUT_SHADER_DEP} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - -# Ray Tracing shaders need to target Vulkan 1.2 -file(GLOB files "shaders/*.rgen" "shaders/*.rint" "shaders/*.rahit" "shaders/*.rchit" "shaders/*.rmiss" "shaders/*.rcall") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env spirv1.4 ${file} -o ${OUTPUT_FILENAME}.spv --depfile ${OUTPUT_SHADER_DEP} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - -# Hlsl files (compiled to SPIR-V). Entry point assumed to me "main". -file(GLOB files "shaders/*.comp.hlsl") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} ${DXC_EXE} - COMMAND ${DXC_EXE} ${SHADER_INCLUDE} -I. -T cs_6_7 -spirv -fspv-target-env=vulkan1.1 -enable-16bit-types -E main -Fo ${OUTPUT_FILENAME}.spv ${file} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - - -file(GLOB files "shaders/*.json") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}) - add_custom_command( - OUTPUT ${OUTPUT_JSON} - MAIN_DEPENDENCY ${file} - COMMAND ${CMAKE_COMMAND} -E copy ${file} ${OUTPUT_JSON} - COMMENT "Copying ... ${file}" to ${OUTPUT_JSON} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_JSON) -endforeach() - -# Aliased shaders compile copies of other shaders (but allow for #define setting) -file(GLOB_RECURSE files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "shaders/*.alias") -foreach(file ${files}) - set(INPUT_ALIAS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) - get_filename_component(OUTPUT_FILENAME ${file} NAME_WLE) - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - cmake_path(NATIVE_PATH INPUT_ALIAS NORMALIZE INPUT_ALIAS_NATIVE) - cmake_path(NATIVE_PATH OUTPUT_SHADER NORMALIZE OUTPUT_SHADER_NATIVE) - cmake_path(NATIVE_PATH OUTPUT_SHADER_DEP NORMALIZE OUTPUT_SHADER_DEP_NATIVE) - - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${INPUT_ALIAS} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMENT "Aliasing ... ${INPUT_ALIAS}" to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP}) - COMMAND echo Aliasing ... ${INPUT_ALIAS} to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP}) - COMMAND ${CMAKE_COMMAND} -DINPUT_ALIAS=${INPUT_ALIAS} -DOUTPUT_SHADER=${OUTPUT_SHADER} -DOUTPUT_SHADER_DEP=${OUTPUT_SHADER_DEP} -DGLSL_VALIDATOR=${GLSL_VALIDATOR} -DSHADER_INCLUDE=${SHADER_INCLUDE} -P ${CMAKE_CURRENT_LIST_DIR}/CompileAlias.cmake - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_JSON) -endforeach() - -# -# Add shaders (sources) in to a 'Shaders' folder for Visual Studio -# -source_group( "Shader Files" FILES ${SHADERS_SRC} ) diff --git a/project/android/cmake/CompileAlias.cmake b/project/android/cmake/CompileAlias.cmake deleted file mode 100644 index d9eccc2..0000000 --- a/project/android/cmake/CompileAlias.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# -# Parse and build shader alias -# -# Inputs: -# INPUT_ALIAS -# OUTPUT_SHADER -# OUTPUT_SHADER_DEP - -file(READ ${INPUT_ALIAS} ALIAS_JSON) - -string(JSON INPUT_SHADER GET ${ALIAS_JSON} Shader) -string(JSON INPUT_DEFINES GET ${ALIAS_JSON} Defines) -string(JSON TARGET_ENV ERROR_VARIABLE JSON_ERROR GET ${ALIAS_JSON} TargetEnv ${ALIAS_JSON}) -if(NOT ${JSON_ERROR} EQUAL "NOTFOUND") - set(TARGET_ENV "vulkan1.1") -endif() - -# expand out the "Defines: []" JSON array -set(DEFINES "") -string(JSON INPUT_DEFINES_COUNT LENGTH ${INPUT_DEFINES}) -if(INPUT_DEFINES_COUNT GREATER 0) - math(EXPR INPUT_DEFINES_COUNT "${INPUT_DEFINES_COUNT} - 1") - foreach(DEFINE_IDX RANGE ${INPUT_DEFINES_COUNT}) - string(JSON DEFINE GET ${INPUT_DEFINES} ${DEFINE_IDX}) - list(APPEND DEFINES "-D${DEFINE}") - endforeach() -endif() - -message("Defines ${DEFINES}") - -cmake_path(REMOVE_FILENAME INPUT_ALIAS OUTPUT_VARIABLE INPUT_ALIAS_PATH) -cmake_path(APPEND I ${INPUT_ALIAS_PATH} ${INPUT_SHADER}) -set(INPUT_SHADER ${I}) - -#message("Shader ${INPUT_SHADER}") -#message("Defines ${INPUT_DEFINES}") -#message("GLSL_VALIDATOR ${GLSL_VALIDATOR}") - -execute_process( - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env ${TARGET_ENV} ${DEFINES} ${INPUT_SHADER} -o ${OUTPUT_SHADER} --depfile ${OUTPUT_SHADER_DEP} - #COMMAND_ECHO STDOUT - COMMAND_ERROR_IS_FATAL ANY -) diff --git a/project/android/cmake/FrameworkApplicationHelper.cmake b/project/android/cmake/FrameworkApplicationHelper.cmake index 97b74ed..c820f96 100644 --- a/project/android/cmake/FrameworkApplicationHelper.cmake +++ b/project/android/cmake/FrameworkApplicationHelper.cmake @@ -9,13 +9,22 @@ # PROJECT_NAME - name of the application being compiled (from the 'project(...)' command) # FRAMEWORK_LIB - name of the helper framework library (eg framework_vulkan or framework_dx12) +if(ANDROID) + # Android does not include the config file in it's top level build file (since that is gradle) so pull into each framework project seperately + include(../../../ConfigLocal) +endif() + +if(NOT DEFINED FRAMEWORK_LIB) + set(FRAMEWORK_LIB framework_vulkan) +endif() + +set(CMAKE_MODULE_PATH "${FRAMEWORK_DIR}/cmake" ${CMAKE_MODULE_PATH}) # Windows and Android differ in terms of output. # Windows generates an executable, Android generates a library (that the AndroidManifest references) if(WIN32) set( TARGET_NAME ${PROJECT_NAME} ) add_executable( ${TARGET_NAME} WIN32 ${CPP_SRC} ${SHADERS_SRC} ${NATVIS_SCHEMA}) - add_dependencies( ${TARGET_NAME} buildTimestamp ) target_compile_definitions( ${TARGET_NAME} PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS ) set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") elseif(ANDROID) @@ -23,39 +32,56 @@ elseif(ANDROID) add_library( ${TARGET_NAME} SHARED ${CPP_SRC} ${SHADERS_SRC} ) target_compile_definitions(${TARGET_NAME} PRIVATE OS_ANDROID) target_compile_options(${TARGET_NAME} PRIVATE -Wno-nullability-completeness;-Wno-deprecated-volatile;-Wno-deprecated-anon-enum-enum-conversion) - - # Generate build time stamp as 2 part process. CMake file that exe depends upon and the header file that cmake writes the header to be included by code. This way we only rebuild what is needed. - file (WRITE ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "string(TIMESTAMP TIMEZ UTC)\n") - file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(WRITE ../../buildtimestamp.h \"#ifndef _BUILDTIMESTAMP_H_\\n\")\n") - file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#define _BUILDTIMESTAMP_H_\\n\\n\")\n") - file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"// Automatically built by build process. Do NOT check into version control.\\n\\n\")\n") - file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#define BUILD_TIMESTAMP \\\"\${TIMEZ}\\\"\\n\\n\")\n") - file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#endif // _BUILDTIMESTAMP_H_\\n\")\n") - add_custom_target ( - buildTimestamp - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/buildTimestamp.cmake - ADD_DEPENDENCIES ${CMAKE_BINARY_DIR}/buildTimestamp.cmake) - add_dependencies( ${TARGET_NAME} buildTimestamp ) + target_compile_options(${TARGET_NAME} PRIVATE "$<$:-O3>" "$<$:-O3>") # Android also needs app-glue generating (and linking before the framework) - add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) - target_link_libraries( ${TARGET_NAME} app-glue ) + add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + target_link_libraries( ${TARGET_NAME} app-glue) # Export ANativeActivity_onCreate(), see https://github.com/android-ndk/ndk/issues/381. set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") # FRAMEWORK_DIR is set by the calling gradle script (needed for Android) - add_subdirectory( ${FRAMEWORK_DIR} framework ) - +# add_subdirectory( ${FRAMEWORK_DIR} framework ) else() message(FATAL_ERROR "(currently) Unsupported platform") endif() -if(NOT DEFINED FRAMEWORK_LIB) - set(FRAMEWORK_LIB framework_vulkan) -endif() - # # Link the framework (to our application) # -target_link_libraries( ${TARGET_NAME} ${FRAMEWORK_LIB} ) +#target_link_libraries( ${TARGET_NAME} ${FRAMEWORK_LIB} ) +target_link_libraries( ${TARGET_NAME} ${CMAKE_CURRENT_BINARY_DIR}/../../../../../android/framework/${CMAKE_BUILD_TYPE}/lib${FRAMEWORK_LIB}.a ) +#if(FRAMEWORK_framework_external_KTX-Software) + target_link_libraries( ${TARGET_NAME} ${CMAKE_CURRENT_BINARY_DIR}/../../../../../android/framework/${CMAKE_BUILD_TYPE}/libktx_read.a ) +#endif() +target_link_libraries( ${TARGET_NAME} ${CMAKE_CURRENT_BINARY_DIR}/../../../../../android/framework/${CMAKE_BUILD_TYPE}/libframework.a ) +target_link_libraries( ${TARGET_NAME} ${CMAKE_CURRENT_BINARY_DIR}/../../../../../android/framework/${CMAKE_BUILD_TYPE}/libframework_base.a ) +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/code) +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/external) +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/external/glm) # so code can do #include "glm/mat3x3.hpp" etc +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/external/json/single_include) +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/external/imgui) +find_package(Vulkan REQUIRED) +set_target_properties(Vulkan::Vulkan PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "") # remove the Vulkan incude paths from the local VulkanSDK +target_include_directories(${TARGET_NAME} PUBLIC ../../framework/external/Vulkan-Headers/include) # point the framework to the Vulkan includes that we have as a submodule +target_link_libraries(${TARGET_NAME} Vulkan::Vulkan) +target_link_libraries(${TARGET_NAME} android log) + +target_include_directories(${TARGET_NAME} PRIVATE .) + +set(DEFAULT_LOCAL_SHADER_DESTINATION "build/Media/Shaders" CACHE INTERNAL "Default local shader destionation") +set(DEFAULT_LOCAL_MESH_DESTINATION "build/Media/Meshes" CACHE INTERNAL "Default local mesh destionation") +set(DEFAULT_LOCAL_TEXTURE_DESTINATION "build/Media/Textures" CACHE INTERNAL "Default local texture destionation") +set(DEFAULT_LOCAL_MISC_DESTINATION "build/Media/Misc" CACHE INTERNAL "Default local misc destionation") + +macro(inject_root_asset_path) + if(${ARGC} GREATER 0) + set(GLOBAL_ASSET_BASE_PATH "${ARGV0}" CACHE INTERNAL "Global asset base path for asset packaging") + else() + set(GLOBAL_ASSET_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../framework/external/GameSampleAssets" CACHE INTERNAL "Global asset base path for asset packaging") + endif() +endmacro() -include_directories(.) +macro(register_local_asset_path var_name relative_path) + set(${var_name} "${relative_path}" CACHE INTERNAL "Global asset local path for asset packaging") + add_compile_definitions(${var_name}_PATH="${relative_path}") +endmacro() \ No newline at end of file diff --git a/project/android/cmake/ModelPackager.cmake b/project/android/cmake/ModelPackager.cmake deleted file mode 100644 index d62477a..0000000 --- a/project/android/cmake/ModelPackager.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# -# Model Packager -# Copy model files from specified path to media path. -# - -function(add_gltf _path) - - if(NOT EXISTS "${_path}.gltf") - message(FATAL_ERROR "ModelPackager -> Couldn't find .gltf file on given path ${_path}") - return() - endif() - - file(COPY "${_path}.gltf" DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Media/Meshes/) - - if(EXISTS "${_path}.bin") - file(COPY "${_path}.bin" DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Media/Meshes/) - endif() - -endfunction() \ No newline at end of file diff --git a/project/android/cmake/TexturePackager.cmake b/project/android/cmake/TexturePackager.cmake deleted file mode 100644 index 2c77429..0000000 --- a/project/android/cmake/TexturePackager.cmake +++ /dev/null @@ -1,127 +0,0 @@ -# -# Texture Packager -# Convert PNG textures from the specified path into its media equivalent. -# - -function(add_textures_from_path _path) - - # Attempt to find the converter tool, else display warning message - SET(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") - if(NOT EXISTS ${CONVERTER_TOOL}) - message(WARNING "TexturePackager -> Texture converter tool wasn't found, sample textures will not be generated (Run '03_BuildTools.bat' if that's desired) - ${CONVERTER_TOOL}") - return() - endif() - - # Ensure the texture path exist - SET(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") - if(NOT EXISTS ${TEXTURE_PATH}) - file(MAKE_DIRECTORY ${TEXTURE_PATH}) - endif() - - MESSAGE("Converting textures from path: '${_path}'") - - # For each PNG file -> Convert using the tool - file(GLOB png_textures "${_path}/*.png") - foreach(file ${png_textures}) - - get_filename_component(OUTPUT_FILENAME ${file} NAME_WE) - - SET(OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - SET(PARAMS - "--genmipmap" - --scale 1.0 - "--verbose" - "${OUTPUT_PATH}" - "${file}" - ) - - if(EXISTS ${OUTPUT_PATH}) - # MESSAGE("Ignoring already converted texture: '${OUTPUT_FILENAME}'") - continue() - endif() - - # MESSAGE("Converting texture: '${OUTPUT_FILENAME}' to KTX") - - execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} ERROR_VARIABLE CONV_ERROR RESULT_VARIABLE CONV_RETVAL) - - if(CONV_ERROR) - message(WARNING "TexturePackager -> ${CONV_ERROR}") - endif() - - endforeach() - - # For each KTX file -> Copy - file(GLOB ktx_textures "${_path}/*.ktx") - foreach(file ${ktx_textures}) - - get_filename_component(OUTPUT_FILENAME ${file} NAME_WE) - SET(OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - if(EXISTS ${OUTPUT_PATH}) - # MESSAGE("Ignoring existing texture: '${OUTPUT_FILENAME}'") - continue() - endif() - - SET(OUTPUT_PATH "${TEXTURE_PATH}/") - - # MESSAGE("Copying KTX texture: '${OUTPUT_FILENAME}'") - file(COPY ${file} DESTINATION ${OUTPUT_PATH}) - - endforeach() - -endfunction() - -function(add_texture _texture_path) - - if(NOT EXISTS ${_texture_path}) - message(WARNING "TexturePackager -> Requested texture doesn't exist: ${_texture_path}") - return() - endif() - - # Ensure the texture path exist - SET(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") - if(NOT EXISTS ${TEXTURE_PATH}) - file(MAKE_DIRECTORY ${TEXTURE_PATH}) - endif() - - get_filename_component(OUTPUT_FILENAME ${_texture_path} NAME_WE) - get_filename_component(OUTPUT_EXT ${_texture_path} EXT) - SET(COPY_OUTPUT_PATH "${TEXTURE_PATH}/") - SET(DST_OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - if(EXISTS ${DST_OUTPUT_PATH}) - # MESSAGE("Ignoring existing texture: '${OUTPUT_FILENAME}'") - return() - endif() - - if(OUTPUT_EXT STREQUAL ".ktx") - MESSAGE("Copying KTX texture: '${OUTPUT_FILENAME}'") - file(COPY ${_texture_path} DESTINATION ${COPY_OUTPUT_PATH}) - return() - endif() - - SET(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") - - if(NOT EXISTS ${CONVERTER_TOOL}) - message(WARNING "TexturePackager -> Texture converter tool wasn't found, sample textures will not be generated (Run '03_BuildTools.bat' if that's desired) - ${CONVERTER_TOOL}") - return() - endif() - - MESSAGE("Converting texture: '${OUTPUT_FILENAME}' to KTX") - - SET(PARAMS - "--genmipmap" - --scale 1.0 - "--verbose" - "${DST_OUTPUT_PATH}" - "${_texture_path}" - ) - - execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} ERROR_VARIABLE CONV_ERROR RESULT_VARIABLE CONV_RETVAL) - - if(CONV_ERROR) - message(WARNING "TexturePackager -> ${CONV_ERROR}") - endif() - -endfunction() \ No newline at end of file diff --git a/project/android/gradle.properties b/project/android/gradle.properties index 0d12a9d..880319e 100644 --- a/project/android/gradle.properties +++ b/project/android/gradle.properties @@ -1 +1,2 @@ -org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 \ No newline at end of file +org.gradle.jvmargs=-Xmx2g + diff --git a/project/android/gradle/wrapper/gradle-wrapper.jar b/project/android/gradle/wrapper/gradle-wrapper.jar index e708b1c..7454180 100644 Binary files a/project/android/gradle/wrapper/gradle-wrapper.jar and b/project/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/project/android/gradle/wrapper/gradle-wrapper.properties b/project/android/gradle/wrapper/gradle-wrapper.properties index 669386b..a595206 100644 --- a/project/android/gradle/wrapper/gradle-wrapper.properties +++ b/project/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/project/android/gradlew b/project/android/gradlew index f5feea6..c53aefa 100644 --- a/project/android/gradlew +++ b/project/android/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# SPDX-License-Identifier: Apache-2.0 -# ############################################################################## # @@ -34,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». +# * expansions $var, ${var}, ${var:-default}, ${var+SET}, +# ${var#prefix}, ${var%suffix}, and $( cmd ); +# * compound commands having a testable exit status, especially case; +# * various built-in commands including command, set, and ulimit. # # Important for patching: # @@ -57,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -82,12 +80,13 @@ do esac done -# This is normally unused -# shellcheck disable=SC2034 +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -134,29 +133,22 @@ location of your Java installation." fi else JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,15 +193,11 @@ if "$cygwin" || "$msys" ; then done fi - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -217,12 +205,6 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/project/android/gradlew.bat b/project/android/gradlew.bat index 9d21a21..107acd3 100644 --- a/project/android/gradlew.bat +++ b/project/android/gradlew.bat @@ -13,10 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem -@if "%DEBUG%"=="" @echo off +@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -27,8 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused +if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -43,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute +if "%ERRORLEVEL%" == "0" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail @@ -59,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail @@ -78,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd +if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/project/android/jniLibs/arm64-v8a/README.md b/project/android/jniLibs/arm64-v8a/README.md new file mode 100644 index 0000000..2b6f5c9 --- /dev/null +++ b/project/android/jniLibs/arm64-v8a/README.md @@ -0,0 +1,3 @@ +Place libVkLayer_khronos_validation.so in this folder to enable Vulkan Validation layers in Android samples (requires rebuild of sample .apks) + +Pre-built validation layer libraries are provided by Khronos at the [official Khronos validation layer release](https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases) page. diff --git a/project/android/settings.gradle b/project/android/settings.gradle index d9d9e04..425d47a 100644 --- a/project/android/settings.gradle +++ b/project/android/settings.gradle @@ -1,3 +1,75 @@ -include ':empty', ':hello-gltf', ':hdrSwapchain', ':meshShaderKHR', ':SubPass', ':shaderResolveTonemap', ':rotatedCopy', ':BloomImageProcessing', ':rayPipelineGI', ':rayPipelineShadows', ':rayQueryShadows',':rayReflections',':sgsr', ':sgsr2', ':graph_pipelines' +//============================================================================= +// +// Copyright (c) QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== -project(':graph_pipelines').projectDir = new File('../../samples/graph_pipelines') +// Load local properties +ext { + frameworkProps = new Properties() +} +file("../../ConfigLocal.properties").withInputStream { ext.frameworkProps.load(it) } + +// Assume the framework is always enabled! +include ':framework' +project(':framework').projectDir = new File('../../framework') + +if(false) { + include ':samplesExternals' + project(':samplesExternals').projectDir = new File('../../samples/cmake') +} + +// Helper to add project for sample if it is enabled in settings +def add_sample_subdirectory(baseName, projectName, folder) { + + def value = ext.frameworkProps.getProperty("FRAMEWORK_" + baseName + "_" + projectName) ?: "true" + if (value == 'true') + { + logger.warn("Adding " + baseName + ": " + projectName) + def gradleProjectName = ':' + projectName + include gradleProjectName + project(gradleProjectName).projectDir = new File(folder + '/build') + } + else { + logger.warn("Skipping " + baseName + ": " + projectName) + } +} + +// Map of project-specific dependencies +// Add here any dependency that should match with the project name +def projectDependencies = [ + dsp_offload: [ + condition: { -> System.getenv('HEXAGON_SDK_ROOT') != null }, + message: "NOTE: dsp_offload sample requires Hexagon SDK to be installed (and HEXAGON_SDK_ROOT environment variable)." + ], + sdp_cli: [ + condition: { -> false }, + message: "NOTE: dsdp_cli cannot be built for Android, it's a Windows-only app." + ] +] + +// Dynamically scan samples and tests folders +['samples', 'tests'].each { base -> + def baseDir = new File(rootDir, "../../" + base) + if (baseDir.exists() && baseDir.isDirectory()) { + baseDir.eachDir { subDir -> + def buildGradle = new File(subDir, 'build/build.gradle') + if (buildGradle.exists()) { + def projectName = subDir.name + def folderPath = "../../" + base + "/" + projectName + + def depsInfo = projectDependencies[projectName] + if (depsInfo) { + if (!depsInfo.condition()) { + logger.warn("Skipping " + base + ": " + projectName + " — " + depsInfo.message) + return + } + add_sample_subdirectory(base, projectName, folderPath) + } else { + add_sample_subdirectory(base, projectName, folderPath) + } + } + } + } +} \ No newline at end of file diff --git a/project/linux/CMakeLists.txt b/project/linux/CMakeLists.txt new file mode 100644 index 0000000..87f2ad2 --- /dev/null +++ b/project/linux/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required (VERSION 3.25) + +project (SampleFramework) + +# Include top level configuration +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/../../ConfigLocal.cmake) + message(FATAL_ERROR "ConfigLocal.cmake does not exits - run \"python configure.py\" in the top level directory to generate this file.") +endif() +include(../../ConfigLocal.cmake) + + +# Turn exceptions off (msvc) +# Done in this top-level class as it needs to apply to libraries and applications globally. +# Even in 2020 exception support removes compiler/optimizer oppertunities to remove 'redundant' instructions (eg look at dissassembly for move constructors). +# Also output debug information on release (Windows) builds so the Release build is debuggable. +if(false) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_RELEASE} /ZI") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") + set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") + add_definitions(-D_HAS_EXCEPTIONS=0) +endif() +endif() + +# Determine which graphics API we can support and set appropriate defines +set(FRAMEWORK_ENABLE_VULKAN OFF) +set(FRAMEWORK_ENABLE_DX12 OFF) +set(FRAMEWORK_WINDOWS_ARM64 OFF) +if(ANDROID) + # Android target (Vulkan only) + set(FRAMEWORK_ENABLE_VULKAN ON) +elseif(WIN32) + # Windows target (Dx12 for sure, maybe Vulkan too) + set(FRAMEWORK_ENABLE_DX12 ON) + #if (NOT CMAKE_GENERATOR_PLATFORM MATCHES "ARM64") + set(FRAMEWORK_ENABLE_VULKAN ON) + #endif() + if (CMAKE_GENERATOR_PLATFORM MATCHES "ARM64") + set(FRAMEWORK_WINDOWS_ARM64 ON) + endif() +elseif(UNIX) + set(FRAMEWORK_ENABLE_VULKAN ON) +endif() + +message("FRAMEWORK_ENABLE_VULKAN - ${FRAMEWORK_ENABLE_VULKAN}") +message("FRAMEWORK_ENABLE_DX12 - ${FRAMEWORK_ENABLE_DX12}") + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Add in all the child subdirectories +set(FRAMEWORK_DIR ../../framework) +set(FRAMEWORK_DOWNLOAD_EXTERNALS On CACHE BOOL "Pull down the framework external dependencies" FORCE) +add_subdirectory( ${FRAMEWORK_DIR} framework ) +set(FRAMEWORK_DOWNLOAD_EXTERNALS Off CACHE BOOL "Pull down the framework external dependencies" FORCE) + +add_subdirectory( ../../samples samples) diff --git a/project/linux/build.sh b/project/linux/build.sh new file mode 100644 index 0000000..93919a4 --- /dev/null +++ b/project/linux/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +pushd . > '/dev/null'; +SCRIPT_PATH="${BASH_SOURCE[0]:-$0}"; +while [ -h "$SCRIPT_PATH" ]; +do + cd "$( dirname -- "$SCRIPT_PATH"; )"; + SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )"; +done + +cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null'; +SCRIPT_PATH="$( pwd; )"; + +mkdir solution +cd solution + +cmake -G "Unix Makefiles" .. +cmake --build . --config Debug +#cmake --build . --config Release +popd diff --git a/project/linux/cmake/FrameworkApplicationHelper.cmake b/project/linux/cmake/FrameworkApplicationHelper.cmake new file mode 100644 index 0000000..7d9cf58 --- /dev/null +++ b/project/linux/cmake/FrameworkApplicationHelper.cmake @@ -0,0 +1,48 @@ +# +# Build the application, linking against the framework. +# + +# Input: +# CPP_SRC - list of cpp/hpp files +# SHADERS_SRC - list of 'shader' (.vert, .frag, .comp) source files +# FRAMEWORK_LIB - name of the framework to link against (eg framework_vulkan) +# NATVIS_SCHEMA - list of application specific Visual Studio visualization schemas (for debuffer) +# PROJECT_NAME - name of the application being compiled (from the 'project(...)' command) + +# Windows and Android differ in terms of output. +# Windows generates an executable, Android generates a library (that the AndroidManifest references) +if(WIN32) + set( TARGET_NAME ${PROJECT_NAME} ) + add_executable( ${TARGET_NAME} WIN32 ${CPP_SRC} ${SHADERS_SRC} ${NATVIS_SCHEMA}) + target_compile_definitions( ${TARGET_NAME} PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS ) + set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +elseif(ANDROID) + set( TARGET_NAME native-lib ) + add_library( ${TARGET_NAME} SHARED ${CPP_SRC} ${SHADERS_SRC} ) + target_compile_definitions(${TARGET_NAME} PRIVATE OS_ANDROID) + + # Android also needs app-glue generating (and linking before the framework) + add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) + target_link_libraries( ${TARGET_NAME} app-glue ) + # Export ANativeActivity_onCreate(), see https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + # FRAMEWORK_DIR is set by the calling gradle script (needed for Android) + add_subdirectory( ${FRAMEWORK_DIR} framework ) +elseif(UNIX) + set( TARGET_NAME ${PROJECT_NAME} ) + add_executable( ${TARGET_NAME} ${CPP_SRC} ${SHADERS_SRC} ${NATVIS_SCHEMA}) + target_compile_definitions( ${TARGET_NAME} PRIVATE OS_LINUX) +else() + message(FATAL_ERROR "(currently) Unsupported platform") +endif() + +if(NOT DEFINED FRAMEWORK_LIB) + set(FRAMEWORK_LIB framework_vulkan) +endif() + +# +# Link the framework (to our application) +# +target_link_libraries( ${TARGET_NAME} ${FRAMEWORK_LIB} ) + +include_directories(.) diff --git a/project/tools/.gitignore b/project/tools/.gitignore new file mode 100644 index 0000000..8036bcd --- /dev/null +++ b/project/tools/.gitignore @@ -0,0 +1,7 @@ +external + +# executables (linux) +ktxinfo +libktx.so.0.0.0 +toktx + diff --git a/project/tools/CMakeLists.txt b/project/tools/CMakeLists.txt index 8f8c496..d760e4f 100644 --- a/project/tools/CMakeLists.txt +++ b/project/tools/CMakeLists.txt @@ -1,6 +1,9 @@ -cmake_minimum_required (VERSION 3.21) +cmake_minimum_required (VERSION 3.25 FATAL_ERROR) # 3.7 added FindVulkan, 3.10 supported MSVC c++17 (without manually setting flags), 3.19 (maybe before) supported C++20 on Win/Android, 3.21 added Android NDK support with NDK r23 although we are not currently using it!, 3.25 for fetchontent features +cmake_policy(VERSION 3.7) -project(vkSampleFrameworkTools) +project(vkSampleFrameworkTools C CXX) + +set(CMAKE_CXX_STANDARD 20) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD OFF) @@ -13,7 +16,7 @@ function(add_subdirectory_with_folder _folder_name _folder) endfunction() # Framework -# set(FRAMEWORK_DIR ../../framework) +set(FRAMEWORK_DIR ../../framework) # add_subdirectory( ${FRAMEWORK_DIR} framework ) add_custom_target(ktxTools ALL) @@ -23,7 +26,7 @@ set(KTX_FEATURE_TESTS OFF) set(KTX_FEATURE_TOOLS ON) set(KTX_FEATURE_GL_UPLOAD OFF) set(KTX_FEATURE_VULKAN OFF) -add_subdirectory_with_folder("KTX-Software" ../../framework/external/KTX-Software KTX-Software EXCLUDE_FROM_ALL) +add_subdirectory_with_folder("KTX-Software" ${FRAMEWORK_DIR}/external/KTX-Software KTX-Software EXCLUDE_FROM_ALL) add_dependencies(ktxTools toktx ktxinfo) diff --git a/project/tools/build.bat b/project/tools/build.bat index 83eb1c6..2115100 100644 --- a/project/tools/build.bat +++ b/project/tools/build.bat @@ -1,10 +1,36 @@ -@pushd ..\.. -@cd framework\external\KTX-Software -@git fetch origin 'refs/tags/*:refs/tags/*' -@popd -@mkdir solution -@pushd solution +@echo off +cd /D "%~dp0" +mkdir solution +pushd ..\.. +cd framework\external\KTX-Software +git fetch origin 'refs/tags/*:refs/tags/*' +popd +mkdir solution +pushd solution + + +echo Looking for Visual Studio... +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [17.0,18.0] -format value -property displayName |findstr 2022 +if %ERRORLEVEL%==0 goto :FOUND2022 +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0,17.0] -format value -property displayName |findstr 2019 +if %ERRORLEVEL%==0 goto :FOUND2019 +echo Not found VS2019 or VS2022 using 'vswhere'. Attempting to create VS2019 solution anyways. + +:FOUND2019 cmake.exe -G "Visual Studio 16 2019" .. -cmake.exe --build . --config Debug +if %ERRORLEVEL% ==0 goto :BUILD +popd +echo. +echo Could not build Visual Studio 2019 .sln files. Check above errors (Visual Studio Pro 2022 or 2019 supported) +goto :EOF + +:FOUND2022 +cmake.exe -G "Visual Studio 17 2022" .. +if %ERRORLEVEL% ==0 goto :BUILD +echo Could not build Visual Studio 2022 .sln files. Check above errors. +popd +goto :EOF + +:BUILD cmake.exe --build . --config Release @popd diff --git a/project/tools/build.sh b/project/tools/build.sh new file mode 100644 index 0000000..aeda57f --- /dev/null +++ b/project/tools/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +pushd . > '/dev/null'; +SCRIPT_PATH="${BASH_SOURCE[0]:-$0}"; +while [ -h "$SCRIPT_PATH" ]; +do + cd "$( dirname -- "$SCRIPT_PATH"; )"; + SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )"; +done + +cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null'; +SCRIPT_PATH="$( pwd; )"; + +mkdir solution +cd solution + + +cmake -G "Unix Makefiles" .. +cmake --build . --config Release + +popd diff --git a/project/tools/clean.bat b/project/tools/clean.bat new file mode 100644 index 0000000..f9c57c5 --- /dev/null +++ b/project/tools/clean.bat @@ -0,0 +1,2 @@ +@echo off +rmdir /s /q solution diff --git a/project/windows/CMakeLists.txt b/project/windows/CMakeLists.txt index ef1ce4c..fa6229e 100644 --- a/project/windows/CMakeLists.txt +++ b/project/windows/CMakeLists.txt @@ -1,6 +1,12 @@ -cmake_minimum_required (VERSION 3.21) +cmake_minimum_required (VERSION 3.25) -project (vkSampleFramework) +project (SampleFramework) + +# Include top level configuration +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/../../ConfigLocal.cmake) + message(FATAL_ERROR "\nConfigLocal.cmake does not exits - run \"python Configure.py\" in the top level directory to generate this file.") +endif() +include(../../ConfigLocal.cmake) # Turn exceptions off (msvc) # Done in this top-level class as it needs to apply to libraries and applications globally. @@ -39,35 +45,15 @@ endif() message("FRAMEWORK_ENABLE_VULKAN - ${FRAMEWORK_ENABLE_VULKAN}") message("FRAMEWORK_ENABLE_DX12 - ${FRAMEWORK_ENABLE_DX12}") -# Generate build time stamp as 2 part process. CMake file that exe depends upon and the header file that cmake writes the header to be included by code. This way we only rebuild what is needed. -file (WRITE ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "string(TIMESTAMP TIMEZ UTC)\n") -file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(WRITE ../../buildtimestamp.h \"#ifndef _BUILDTIMESTAMP_H_\\n\")\n") -file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#define _BUILDTIMESTAMP_H_\\n\\n\")\n") -file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"// Automatically built by build process. Do NOT check into version control.\\n\\n\")\n") -file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#define BUILD_TIMESTAMP \\\"\${TIMEZ}\\\"\\n\\n\")\n") -file (APPEND ${CMAKE_BINARY_DIR}/buildTimestamp.cmake "file(APPEND ../../buildTimestamp.h \"#endif // _BUILDTIMESTAMP_H_\\n\")\n") -add_custom_target ( - buildTimestamp - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/buildTimestamp.cmake - ADD_DEPENDENCIES ${CMAKE_BINARY_DIR}/buildTimestamp.cmake) - set_property(GLOBAL PROPERTY USE_FOLDERS ON) -# Add in all the child subdirectories +# Add in all the framework subdirectory set(FRAMEWORK_DIR ../../framework) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${FRAMEWORK_DIR}/cmake") add_subdirectory( ${FRAMEWORK_DIR} framework ) -if(FRAMEWORK_ENABLE_VULKAN) - add_subdirectory(../../samples/empty/ samples/empty) - add_subdirectory(../../samples/hdrSwapchain/ samples/hdrSwapchain) - add_subdirectory(../../samples/rotatedCopy/ samples/rotatedCopy) - add_subdirectory(../../samples/shaderResolveTonemap/ samples/shaderResolveTonemap) - add_subdirectory(../../samples/hello-gltf/ samples/hello-gltf) - add_subdirectory(../../samples/SubPass/ samples/SubPass) - add_subdirectory(../../samples/BloomImageProcessing/ samples/BloomImageProcessing) - add_subdirectory(../../samples/rayQueryShadows/ samples/rayQueryShadows) - add_subdirectory(../../samples/rayReflections/ samples/rayReflections) - add_subdirectory(../../samples/sgsr/ samples/sgsr) - add_subdirectory(../../samples/sdp-cli/ samples/sdp-cli) - add_subdirectory(../../samples/sgsr2/ samples/sgsr2) - add_subdirectory(../../samples/graph_pipelines/ samples/graph_pipelines) -endif() + +# Add the tests +add_subdirectory( ../../tests tests) +# Add the samples +add_subdirectory( ../../samples samples) + diff --git a/project/windows/CmakePresets.json b/project/windows/CmakePresets.json new file mode 100644 index 0000000..7e5875e --- /dev/null +++ b/project/windows/CmakePresets.json @@ -0,0 +1,95 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "base", + "displayName": "Basic", + "description": "Build using Ninja generator", + "generator": "Ninja", + "hidden": true, + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" } + }, + + { + "name": "x64", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { }, + "hidden": true + }, + { + "name": "ARM64", + "architecture": { + "value": "arm64", + "strategy": "external" + }, + "cacheVariables": { }, + "hidden": true + }, + + { + "name": "Debug", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }, + "hidden": true + }, + { + "name": "Release", + "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" }, + "hidden": true + }, + + { + "name": "MSVC", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + { + "name": "Clang", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang-cl.exe" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + + { + "name": "Win10", + "cacheVariables": { + }, + "hidden": true + }, + + { "name": "x64-Debug-Win10" , "description": "MSVC x64 (Debug) Windows", "inherits": [ "base", "x64", "Debug", "MSVC", "Win10" ] }, + { "name": "x64-Release-Win10" , "description": "MSVC x64 (Release) Windows", "inherits": [ "base", "x64", "Release", "MSVC", "Win10" ] }, + { "name": "arm64-Debug-Win10" , "description": "MSVC ARM64 (Debug) Windows", "inherits": [ "base", "ARM64", "Debug", "MSVC", "Win10" ] }, + { "name": "arm64-Release-Win10", "description": "MSVC ARM64 (Release) Windows", "inherits": [ "base", "ARM64", "Release", "MSVC", "Win10" ] }, + + { "name": "x64-Debug-Win10-Clang" , "description": "Clang x64 (Debug) Windows", "inherits": [ "base", "x64", "Debug", "Clang", "Win10" ] }, + { "name": "x64-Release-Win10-Clang" , "description": "Clang x64 (Release) Windows", "inherits": [ "base", "x64", "Release", "Clang", "Win10" ] }, + { "name": "arm64-Debug-Win10-Clang" , "description": "Clang ARM64 (Debug) Windows", "inherits": [ "base", "ARM64", "Debug", "Clang", "Win10" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } }, + { "name": "arm64-Release-Win10-Clang", "description": "Clang ARM64 (Release) Windows", "inherits": [ "base", "ARM64", "Release", "Clang", "Win10" ], "environment": { "CXXFLAGS": "--target=arm64-pc-windows-msvc" } } + ], + "testPresets": [ + { "name": "x64-Debug-Win10" , "configurePreset": "x64-Debug-Win10" }, + { "name": "x64-Release-Win10" , "configurePreset": "x64-Release-Win10" }, + { "name": "arm64-Debug-Win10" , "configurePreset": "arm64-Debug-Win10" }, + { "name": "arm64-Release-Win10", "configurePreset": "arm64-Release-Win10" }, + + { "name": "x64-Debug-Win10-Clang" , "configurePreset": "x64-Debug-Win10-Clang" }, + { "name": "x64-Release-Win10-Clang" , "configurePreset": "x64-Release-Win10-Clang" }, + { "name": "arm64-Debug-Win10-Clang" , "configurePreset": "arm64-Debug-Win10-Clang" }, + { "name": "arm64-Release-Win10-Clang", "configurePreset": "arm64-Release-Win10-Clang" } + ] +} diff --git a/project/windows/build.bat b/project/windows/build.bat index 8ea0072..a150cd1 100644 --- a/project/windows/build.bat +++ b/project/windows/build.bat @@ -1,9 +1,40 @@ -@mkdir solution -@pushd solution +@echo off +cd /D "%~dp0" +mkdir solution +pushd solution + +echo Looking for Visual Studio... +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [17.0,18.0] -format value -property displayName |findstr 2022 +if %ERRORLEVEL%==0 goto :FOUND2022 +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0,17.0] -format value -property displayName |findstr 2019 +if %ERRORLEVEL%==0 goto :FOUND2019 +echo Not found VS2019 or VS2022 using 'vswhere'. Attempting to create VS2019 solution anyways. + +:FOUND2019 cmake.exe -G "Visual Studio 16 2019" .. -@echo Replacing build platform version with 10.0 so generated vcxproj files do not target a specific plaform version. -@rem there is literally no way to do this in CMake3.17, will always want to specify one of the versions on the machine generating the vcxproj files. -@powershell -Command "gci . *.vcxproj -recurse | Where-Object {$_.PSParentPath -notmatch \"CMakeFiles\"} | ForEach {(Get-Content $_.FullName | ForEach {$_ -replace \"WindowsTargetPlatformVersion.*^<\", \"WindowsTargetPlatformVersion^>10.0^<\"}) | Set-Content $_.Fullname"} +if %ERRORLEVEL% ==0 goto :BUILD +popd +echo. +echo Could not build Visual Studio .sln files. Check above errors (Visual Studio Pro 2022 or 2019 supported) +goto :EOF + +:FOUND2022 +cmake.exe -G "Visual Studio 17 2022" .. +if %ERRORLEVEL% ==0 goto :BUILD +popd +echo. +echo Could not build Visual Studio .sln files. Check above errors (Visual Studio Pro 2022 or 2019 supported) +goto :EOF + +:BUILD +echo Starting debug build. +echo . cmake.exe --build . --config Debug -cmake.exe --build . --config Release -@popd +rem echo Starting release build. +rem echo . +rem cmake.exe --build . --config Release +popd + +echo. +echo Visual Studio solution written to %CD%\solution\SampleFramework.sln +echo. diff --git a/project/windows/buildArm64.bat b/project/windows/buildArm64.bat index 152249d..84e8c0d 100644 --- a/project/windows/buildArm64.bat +++ b/project/windows/buildArm64.bat @@ -1,6 +1,46 @@ -@mkdir solutionArm64 -@pushd solutionArm64 +@echo off +cd /D "%~dp0" +mkdir solutionArm64 +pushd solutionArm64 +echo Looking for Visual Studio... +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [17.0,18.0] -format value -property displayName |findstr 2022 +if %ERRORLEVEL%==0 goto :FOUND2022 +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [16.0,17.0] -format value -property displayName |findstr 2019 +if %ERRORLEVEL%==0 goto :FOUND2019 +echo Not found VS2019 or VS2022 using 'vswhere'. Attempting to create VS2019 solution anyways. + +:FOUND2019 +echo Creating Visual Studio 2019, ARM64 solution +echo. +echo. cmake.exe -G "Visual Studio 16 2019" -A "ARM64" .. +if %ERRORLEVEL% ==0 goto :BUILD +popd +echo. +echo Could not build Visual Studio 2019 .sln files. Check above errors (Visual Studio Pro 2022 or 2019 supported) +goto :EOF + +:FOUND2022 +echo Creating Visual Studio 2022, ARM64 solution +echo. +echo. +cmake.exe -G "Visual Studio 17 2022" -A "ARM64" .. +if %ERRORLEVEL% ==0 goto :BUILD +echo Could not build Visual Studio 2022 .sln files. Check above errors. +popd +goto :EOF + +:BUILD +echo Visual Studio solution created in project\windows\solutionArm64\ +echo Starting debug build. +echo . cmake.exe --build . --config Debug +echo Starting release build. +echo . cmake.exe --build . --config Release -@popd +popd + +echo. +echo Visual Studio solution written to %CD%\solutionArm64\SampleFramework.sln +echo. + diff --git a/project/windows/clean.bat b/project/windows/clean.bat new file mode 100644 index 0000000..71deeda --- /dev/null +++ b/project/windows/clean.bat @@ -0,0 +1,7 @@ +pushd . +cd /D %~dp0 +rmdir /s /q solution +rmdir /s /q ..\..\samples\external\.fetchcontent +rmdir /s /q ..\..\samples\fidelityFx\external\FidelityFX-FSR2\build +popd + diff --git a/project/windows/cmake/AddShadersDir.cmake b/project/windows/cmake/AddShadersDir.cmake deleted file mode 100644 index 300439b..0000000 --- a/project/windows/cmake/AddShadersDir.cmake +++ /dev/null @@ -1,170 +0,0 @@ -# -# Build shaders -# Add everything with .frag .vert .comp extension from the shaders/ directory and build using Vulkan shader compiler. -# - -# Make sure we have the Vulkan compiler -find_program( - GLSL_VALIDATOR - glslangValidator - DOC "Vulkan Shader Compiler (glslangValidator) (is Vulkan SDK installed?)" - REQUIRED -) - -# Make sure we have the DXC compiler. See if it is installed alngside Vulkan first. -if(DEFINED ENV{VULKAN_SDK}) - cmake_path(SET VULKAN_SDK_PATH NORMALIZE $ENV{VULKAN_SDK}) - find_program( - DXC_EXE - dxc - HINTS ${VULKAN_SDK_PATH} - PATH_SUFFIXES bin - NO_DEFAULT_PATH - DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" - OPTIONAL - ) -endif() - -# We couldnt find dxc installed with Vulkan, look for it on the path (should find the Windows SDK version) -find_program( - DXC_EXE - dxc - DOC "Microsoft Shader compiler (dxc) (is Vulkan SDK installed?)" - REQUIRED -) - -# Runs the command to get the pluginval version (more recent versions of dcx support --version, older ones dont and print the version inside -help) -if (NOT DEFINED ENV{DXC_VERSION}) - message("DCX compiler found at: ${DXC_EXE}") - execute_process(COMMAND ${DXC_EXE} --help - OUTPUT_VARIABLE DXC_VERSION_RAW - ERROR_VARIABLE DXC_VERSION_RAW) - string(REGEX MATCH "Version: ([^\r\n]+)[\r\n]" - DXC_VERSION ${DXC_VERSION_RAW}) - string(REGEX REPLACE "Version: (.*)" - "\\1" - DXC_VERSION ${DXC_VERSION}) - message( "DXC version: ${DXC_VERSION}" ) - set(ENV{DXC_VERSION} ${DXC_VERSION}) -endif() - - -# Custom shader include direcotry -if(DEFINED SHADER_INCLUDE) - list(TRANSFORM SHADER_INCLUDE PREPEND "-I") -endif() - -# Ensure we have a place to put the .d dependency files emitted by the compiler -set(DEPENDS_PATH ${CMAKE_CURRENT_BINARY_DIR}/Media/Shaders/) -file(MAKE_DIRECTORY ${DEPENDS_PATH}) - -# -# Scan through shaders directory looking for shader source files and generate build commands for them -# -file(GLOB files "shaders/*.vert" "shaders/*.frag" "shaders/*.comp") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env vulkan1.1 ${file} -o ${OUTPUT_FILENAME}.spv --depfile ${OUTPUT_SHADER_DEP} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - -# Ray Tracing shaders need to target Vulkan 1.2 -file(GLOB files "shaders/*.rgen" "shaders/*.rint" "shaders/*.rahit" "shaders/*.rchit" "shaders/*.rmiss" "shaders/*.rcall") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env spirv1.4 ${file} -o ${OUTPUT_FILENAME}.spv --depfile ${OUTPUT_SHADER_DEP} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - -# Hlsl files (compiled to SPIR-V). Entry point assumed to me "main". -file(GLOB files "shaders/*.comp.hlsl") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${file} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMAND echo Compiling shader ... ${file} to ${OUTPUT_SHADER} ${DXC_EXE} - COMMAND ${DXC_EXE} ${SHADER_INCLUDE} -I. -T cs_6_7 -spirv -fspv-target-env=vulkan1.1 -enable-16bit-types -E main -Fo ${OUTPUT_FILENAME}.spv ${file} - COMMAND ${CMAKE_COMMAND} -E rename ${OUTPUT_FILENAME}.spv ${OUTPUT_SHADER} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_SHADER) - unset(OUTPUT_FILENAME) -endforeach() - - -file(GLOB files "shaders/*.json") -foreach(file ${files}) - get_filename_component(OUTPUT_FILENAME ${file} NAME) - - set(OUTPUT_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}) - add_custom_command( - OUTPUT ${OUTPUT_JSON} - MAIN_DEPENDENCY ${file} - COMMAND ${CMAKE_COMMAND} -E copy ${file} ${OUTPUT_JSON} - COMMENT "Copying ... ${file}" to ${OUTPUT_JSON} - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_JSON) -endforeach() - -# Aliased shaders compile copies of other shaders (but allow for #define setting) -file(GLOB_RECURSE files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "shaders/*.alias") -foreach(file ${files}) - set(INPUT_ALIAS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) - get_filename_component(OUTPUT_FILENAME ${file} NAME_WLE) - set(OUTPUT_SHADER ${CMAKE_CURRENT_SOURCE_DIR}/Media/Shaders/${OUTPUT_FILENAME}.spv) - set(OUTPUT_SHADER_DEP ${DEPENDS_PATH}${OUTPUT_FILENAME}.spv.d) - cmake_path(NATIVE_PATH INPUT_ALIAS NORMALIZE INPUT_ALIAS_NATIVE) - cmake_path(NATIVE_PATH OUTPUT_SHADER NORMALIZE OUTPUT_SHADER_NATIVE) - cmake_path(NATIVE_PATH OUTPUT_SHADER_DEP NORMALIZE OUTPUT_SHADER_DEP_NATIVE) - - add_custom_command( - OUTPUT ${OUTPUT_SHADER} - MAIN_DEPENDENCY ${INPUT_ALIAS} - DEPFILE ${OUTPUT_SHADER_DEP} - COMMENT "Aliasing ... ${INPUT_ALIAS}" to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP}) - COMMAND echo Aliasing ... ${INPUT_ALIAS} to ${OUTPUT_SHADER} (dependency file ${OUTPUT_SHADER_DEP}) - COMMAND ${CMAKE_COMMAND} -DINPUT_ALIAS=${INPUT_ALIAS} -DOUTPUT_SHADER=${OUTPUT_SHADER} -DOUTPUT_SHADER_DEP=${OUTPUT_SHADER_DEP} -DGLSL_VALIDATOR=${GLSL_VALIDATOR} -DSHADER_INCLUDE=${SHADER_INCLUDE} -P ${CMAKE_CURRENT_LIST_DIR}/CompileAlias.cmake - ) - list(APPEND SHADERS_SRC ${file}) - - unset(OUTPUT_JSON) -endforeach() - -# -# Add shaders (sources) in to a 'Shaders' folder for Visual Studio -# -source_group( "Shader Files" FILES ${SHADERS_SRC} ) diff --git a/project/windows/cmake/CompileAlias.cmake b/project/windows/cmake/CompileAlias.cmake deleted file mode 100644 index d9eccc2..0000000 --- a/project/windows/cmake/CompileAlias.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# -# Parse and build shader alias -# -# Inputs: -# INPUT_ALIAS -# OUTPUT_SHADER -# OUTPUT_SHADER_DEP - -file(READ ${INPUT_ALIAS} ALIAS_JSON) - -string(JSON INPUT_SHADER GET ${ALIAS_JSON} Shader) -string(JSON INPUT_DEFINES GET ${ALIAS_JSON} Defines) -string(JSON TARGET_ENV ERROR_VARIABLE JSON_ERROR GET ${ALIAS_JSON} TargetEnv ${ALIAS_JSON}) -if(NOT ${JSON_ERROR} EQUAL "NOTFOUND") - set(TARGET_ENV "vulkan1.1") -endif() - -# expand out the "Defines: []" JSON array -set(DEFINES "") -string(JSON INPUT_DEFINES_COUNT LENGTH ${INPUT_DEFINES}) -if(INPUT_DEFINES_COUNT GREATER 0) - math(EXPR INPUT_DEFINES_COUNT "${INPUT_DEFINES_COUNT} - 1") - foreach(DEFINE_IDX RANGE ${INPUT_DEFINES_COUNT}) - string(JSON DEFINE GET ${INPUT_DEFINES} ${DEFINE_IDX}) - list(APPEND DEFINES "-D${DEFINE}") - endforeach() -endif() - -message("Defines ${DEFINES}") - -cmake_path(REMOVE_FILENAME INPUT_ALIAS OUTPUT_VARIABLE INPUT_ALIAS_PATH) -cmake_path(APPEND I ${INPUT_ALIAS_PATH} ${INPUT_SHADER}) -set(INPUT_SHADER ${I}) - -#message("Shader ${INPUT_SHADER}") -#message("Defines ${INPUT_DEFINES}") -#message("GLSL_VALIDATOR ${GLSL_VALIDATOR}") - -execute_process( - COMMAND ${GLSL_VALIDATOR} ${SHADER_INCLUDE} -I. -V --quiet --target-env ${TARGET_ENV} ${DEFINES} ${INPUT_SHADER} -o ${OUTPUT_SHADER} --depfile ${OUTPUT_SHADER_DEP} - #COMMAND_ECHO STDOUT - COMMAND_ERROR_IS_FATAL ANY -) diff --git a/project/windows/cmake/FrameworkApplicationHelper.cmake b/project/windows/cmake/FrameworkApplicationHelper.cmake index 51efe48..d2271c8 100644 --- a/project/windows/cmake/FrameworkApplicationHelper.cmake +++ b/project/windows/cmake/FrameworkApplicationHelper.cmake @@ -7,14 +7,18 @@ # SHADERS_SRC - list of 'shader' (.vert, .frag, .comp) source files # FRAMEWORK_LIB - name of the framework to link against (eg framework_vulkan) # NATVIS_SCHEMA - list of application specific Visual Studio visualization schemas (for debuffer) +# FRAMEWORK_LIB - name of the helper framework library (eg framework_vulkan or framework_dx12) # PROJECT_NAME - name of the application being compiled (from the 'project(...)' command) +if(NOT DEFINED FRAMEWORK_LIB) + set(FRAMEWORK_LIB framework_vulkan) +endif() + # Windows and Android differ in terms of output. # Windows generates an executable, Android generates a library (that the AndroidManifest references) if(WIN32) set( TARGET_NAME ${PROJECT_NAME} ) add_executable( ${TARGET_NAME} WIN32 ${CPP_SRC} ${SHADERS_SRC} ${NATVIS_SCHEMA}) - add_dependencies( ${TARGET_NAME} buildTimestamp ) target_compile_definitions( ${TARGET_NAME} PRIVATE OS_WINDOWS;_CRT_SECURE_NO_WARNINGS ) set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") elseif(ANDROID) @@ -43,3 +47,21 @@ endif() target_link_libraries( ${TARGET_NAME} ${FRAMEWORK_LIB} ) include_directories(.) + +set(DEFAULT_LOCAL_SHADER_DESTINATION "build/Media/Shaders" CACHE INTERNAL "Default local shader destionation") +set(DEFAULT_LOCAL_MESH_DESTINATION "build/Media/Meshes" CACHE INTERNAL "Default local mesh destionation") +set(DEFAULT_LOCAL_TEXTURE_DESTINATION "build/Media/Textures" CACHE INTERNAL "Default local texture destionation") +set(DEFAULT_LOCAL_MISC_DESTINATION "build/Media/Misc" CACHE INTERNAL "Default local misc destionation") + +macro(inject_root_asset_path) + if(${ARGC} GREATER 0) + set(GLOBAL_ASSET_BASE_PATH "${ARGV0}" CACHE INTERNAL "Global asset base path for asset packaging") + else() + set(GLOBAL_ASSET_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../framework/external/GameSampleAssets" CACHE INTERNAL "Global asset base path for asset packaging") + endif() +endmacro() + +macro(register_local_asset_path var_name relative_path) + set(${var_name} "${relative_path}" CACHE INTERNAL "Global asset local path for asset packaging") + add_compile_definitions(${var_name}_PATH="${relative_path}") +endmacro() \ No newline at end of file diff --git a/project/windows/cmake/ModelPackager.cmake b/project/windows/cmake/ModelPackager.cmake deleted file mode 100644 index d62477a..0000000 --- a/project/windows/cmake/ModelPackager.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# -# Model Packager -# Copy model files from specified path to media path. -# - -function(add_gltf _path) - - if(NOT EXISTS "${_path}.gltf") - message(FATAL_ERROR "ModelPackager -> Couldn't find .gltf file on given path ${_path}") - return() - endif() - - file(COPY "${_path}.gltf" DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Media/Meshes/) - - if(EXISTS "${_path}.bin") - file(COPY "${_path}.bin" DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/Media/Meshes/) - endif() - -endfunction() \ No newline at end of file diff --git a/project/windows/cmake/TexturePackager.cmake b/project/windows/cmake/TexturePackager.cmake deleted file mode 100644 index 2c77429..0000000 --- a/project/windows/cmake/TexturePackager.cmake +++ /dev/null @@ -1,127 +0,0 @@ -# -# Texture Packager -# Convert PNG textures from the specified path into its media equivalent. -# - -function(add_textures_from_path _path) - - # Attempt to find the converter tool, else display warning message - SET(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") - if(NOT EXISTS ${CONVERTER_TOOL}) - message(WARNING "TexturePackager -> Texture converter tool wasn't found, sample textures will not be generated (Run '03_BuildTools.bat' if that's desired) - ${CONVERTER_TOOL}") - return() - endif() - - # Ensure the texture path exist - SET(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") - if(NOT EXISTS ${TEXTURE_PATH}) - file(MAKE_DIRECTORY ${TEXTURE_PATH}) - endif() - - MESSAGE("Converting textures from path: '${_path}'") - - # For each PNG file -> Convert using the tool - file(GLOB png_textures "${_path}/*.png") - foreach(file ${png_textures}) - - get_filename_component(OUTPUT_FILENAME ${file} NAME_WE) - - SET(OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - SET(PARAMS - "--genmipmap" - --scale 1.0 - "--verbose" - "${OUTPUT_PATH}" - "${file}" - ) - - if(EXISTS ${OUTPUT_PATH}) - # MESSAGE("Ignoring already converted texture: '${OUTPUT_FILENAME}'") - continue() - endif() - - # MESSAGE("Converting texture: '${OUTPUT_FILENAME}' to KTX") - - execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} ERROR_VARIABLE CONV_ERROR RESULT_VARIABLE CONV_RETVAL) - - if(CONV_ERROR) - message(WARNING "TexturePackager -> ${CONV_ERROR}") - endif() - - endforeach() - - # For each KTX file -> Copy - file(GLOB ktx_textures "${_path}/*.ktx") - foreach(file ${ktx_textures}) - - get_filename_component(OUTPUT_FILENAME ${file} NAME_WE) - SET(OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - if(EXISTS ${OUTPUT_PATH}) - # MESSAGE("Ignoring existing texture: '${OUTPUT_FILENAME}'") - continue() - endif() - - SET(OUTPUT_PATH "${TEXTURE_PATH}/") - - # MESSAGE("Copying KTX texture: '${OUTPUT_FILENAME}'") - file(COPY ${file} DESTINATION ${OUTPUT_PATH}) - - endforeach() - -endfunction() - -function(add_texture _texture_path) - - if(NOT EXISTS ${_texture_path}) - message(WARNING "TexturePackager -> Requested texture doesn't exist: ${_texture_path}") - return() - endif() - - # Ensure the texture path exist - SET(TEXTURE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Media/Textures") - if(NOT EXISTS ${TEXTURE_PATH}) - file(MAKE_DIRECTORY ${TEXTURE_PATH}) - endif() - - get_filename_component(OUTPUT_FILENAME ${_texture_path} NAME_WE) - get_filename_component(OUTPUT_EXT ${_texture_path} EXT) - SET(COPY_OUTPUT_PATH "${TEXTURE_PATH}/") - SET(DST_OUTPUT_PATH "${TEXTURE_PATH}/${OUTPUT_FILENAME}.ktx") - - if(EXISTS ${DST_OUTPUT_PATH}) - # MESSAGE("Ignoring existing texture: '${OUTPUT_FILENAME}'") - return() - endif() - - if(OUTPUT_EXT STREQUAL ".ktx") - MESSAGE("Copying KTX texture: '${OUTPUT_FILENAME}'") - file(COPY ${_texture_path} DESTINATION ${COPY_OUTPUT_PATH}) - return() - endif() - - SET(CONVERTER_TOOL "${CMAKE_CURRENT_SOURCE_DIR}/../../project/tools/toktx.exe") - - if(NOT EXISTS ${CONVERTER_TOOL}) - message(WARNING "TexturePackager -> Texture converter tool wasn't found, sample textures will not be generated (Run '03_BuildTools.bat' if that's desired) - ${CONVERTER_TOOL}") - return() - endif() - - MESSAGE("Converting texture: '${OUTPUT_FILENAME}' to KTX") - - SET(PARAMS - "--genmipmap" - --scale 1.0 - "--verbose" - "${DST_OUTPUT_PATH}" - "${_texture_path}" - ) - - execute_process(COMMAND "${CONVERTER_TOOL}" ${PARAMS} ERROR_VARIABLE CONV_ERROR RESULT_VARIABLE CONV_RETVAL) - - if(CONV_ERROR) - message(WARNING "TexturePackager -> ${CONV_ERROR}") - endif() - -endfunction() \ No newline at end of file diff --git a/samples/BloomImageProcessing/01_Install_APK.bat b/samples/BloomImageProcessing/01_Install_APK.bat deleted file mode 100644 index efe3031..0000000 --- a/samples/BloomImageProcessing/01_Install_APK.bat +++ /dev/null @@ -1,18 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -adb uninstall com.quic.BloomImageProcessing - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\BloomImageProcessing\outputs\apk\debug\BloomImageProcessing-debug.apk -@echo **************************************** -adb install -r -g ..\..\build\android\BloomImageProcessing\outputs\apk\debug\BloomImageProcessing-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/BloomImageProcessing/02_InstallConfig.bat b/samples/BloomImageProcessing/02_InstallConfig.bat deleted file mode 100644 index 6b98adf..0000000 --- a/samples/BloomImageProcessing/02_InstallConfig.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off - -adb push %~dp0/app_config.txt /sdcard/Android/data/com.quic.BloomImageProcessing/files/app_config.txt - -pause diff --git a/samples/BloomImageProcessing/CMakeLists.txt b/samples/BloomImageProcessing/CMakeLists.txt deleted file mode 100644 index 8b183cb..0000000 --- a/samples/BloomImageProcessing/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (BloomImageProcessing C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/bloom-image-processing.cpp - code/main/bloom-image-processing.hpp -) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/BloomImageProcessing/build.gradle b/samples/BloomImageProcessing/build.gradle deleted file mode 100644 index 74e107e..0000000 --- a/samples/BloomImageProcessing/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.BloomImageProcessing" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/BloomImageProcessing/project/android/res/values/strings.xml b/samples/BloomImageProcessing/project/android/res/values/strings.xml deleted file mode 100644 index 074670f..0000000 --- a/samples/BloomImageProcessing/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Bloom Image Processing - diff --git a/samples/BloomImageProcessing/shaders/BlurBase.frag b/samples/BloomImageProcessing/shaders/BlurBase.frag deleted file mode 100644 index 879a6e9..0000000 --- a/samples/BloomImageProcessing/shaders/BlurBase.frag +++ /dev/null @@ -1,56 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 450 - -#if ENABLE_QCOM_IMAGE_PROCESSING -#extension GL_QCOM_image_processing : require -#endif // ENABLE_QCOM_IMAGE_PROCESSING - -precision mediump float; precision mediump int; - -layout(location = 0) noperspective in vec2 in_TEXCOORD0; - -layout(set = 0, binding = 0, std140) uniform WeightInfo -{ - vec4 weights[8]; -} _Globals; - - -layout(set = 0, binding = 1) uniform texture2D SourceTexture; -layout(set = 0, binding = 2) uniform sampler SourceSampler; - -#if ENABLE_QCOM_IMAGE_PROCESSING - layout(set = 0, binding = 3) uniform texture2DArray BloomWeightTexture; - layout(set = 0, binding = 4) uniform sampler BloomWeightSampler; -#endif // ENABLE_QCOM_IMAGE_PROCESSING - -layout(location = 0) out vec4 FragColor; - -void main() -{ -#if ENABLE_QCOM_IMAGE_PROCESSING - FragColor = textureWeightedQCOM(sampler2D(SourceTexture, SourceSampler), in_TEXCOORD0, sampler2DArray(BloomWeightTexture, BloomWeightSampler)); -#else -#if VERT_PASS - const float StepX = 0; - const float StepY = 1.0 / 1080.0; -#else - const float StepX = 1.0 / 1920.0; - const float StepY = 0; -#endif // VERT_PASS - int WeightSize = int(_Globals.weights[0][0]); - FragColor = vec4(0); - for (int ww = 0; ww < WeightSize; ++ww) - { - float coordIndex = float(ww - WeightSize / 2); - ivec2 weightIndex = ivec2((ww + 1) / 4, (ww + 1) % 4); - FragColor += texture(sampler2D(SourceTexture, SourceSampler), in_TEXCOORD0 + vec2(coordIndex * StepX, coordIndex * StepY)) * _Globals.weights[weightIndex.x][weightIndex.y]; - } -#endif // ENABLE_QCOM_IMAGE_PROCESSING -} diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt new file mode 100644 index 0000000..1a8d1e1 --- /dev/null +++ b/samples/CMakeLists.txt @@ -0,0 +1,29 @@ +project (samples C CXX) + +set(CMAKE_CXX_STANDARD 20) + +# From the list of sample projects determine which are enabled (by ConfigLocal.cmake - which sets a variable per sample) +# if a variable is not found for a sample it is assumed to be enabled. +function(add_sample_subdirectory _SAMPLE_FOLDER _SAMPLE_NAME) + if(FRAMEWORK_samples AND (NOT DEFINED FRAMEWORK_samples_${_SAMPLE_NAME} OR FRAMEWORK_samples_${_SAMPLE_NAME})) + if(NOT DEFINED FRAMEWORK_samples_${_SAMPLE_NAME}) + message(STATUS "Skipping sample: " ${_SAMPLE_NAME} " (config flag not found - please add to Config.txt and re-run configuration script)") + return() + else() + message(STATUS "Adding sample: " ${_SAMPLE_NAME}) + endif() + add_subdirectory(${_SAMPLE_FOLDER} ${_SAMPLE_NAME}) + else() + message(STATUS "Skipping sample: " ${_SAMPLE_NAME}) + endif() +endfunction() + +# Automatically discover sample projects +file(GLOB SAMPLE_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */) + +foreach(SAMPLE_DIR ${SAMPLE_DIRS}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SAMPLE_DIR}/CMakeLists.txt" OR + EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SAMPLE_DIR}/cmakelists.txt") + add_sample_subdirectory(${SAMPLE_DIR} ${SAMPLE_DIR}) + endif() +endforeach() \ No newline at end of file diff --git a/samples/README.md b/samples/README.md index f863a38..17be11b 100644 --- a/samples/README.md +++ b/samples/README.md @@ -2,37 +2,99 @@ Unless noted all samples run on Windows and Android. -Each sample might have it's own assets dependencies or build instructions, you can find this information on the sample subfolder. - ## [empty](empty) -Empty app. Minimal app linked against Framework. + +Empty app. Minimal app linked against Framework. ## [hello-gltf](hello-gltf) + Scene (gltf) loading app. Implements a working scene with camera movement and minimal lightning. -This sample demonstrates the most basic usage of the Framework to produce a native Vulkan application and it is designed to be small and simple and meant as a starting point for developers to expand its functionality. -## [SubPass](SubPass) -SubPass sample demos the use of vulkan subpasses to perform a filmic tonemapping operator (on a simple forward rendered scene) and the impact on bandwidth and performance with subpass. +## [AODemo](AODemo) + +Vulkan implementation of Neural Network Ambient Occlusion. + +## [FrameworkTest](FrameworkTest) + +Simple test project that initializes the Vulkan Framework and displays a textured sphere. + +## [MLClothApp](MLClothApp) + +Sample project using machine learning to lower cloth simulation cost. + +## [deferredLpac](deferredLpac) + +App that renders a (reasonably) complex scene using forward rendering and compute shaders. + +Where LPAC (Low Priority Asyncronous Compute) is available the Compute jobs will be done on a low priority queue during shadow pass z-buffer write. -## [BloomImageProcessing](BloomImageProcessing) -This sample demonstrates how to use the VK_QCOM_Image_Processing extension in a simple bloom shader. -The extension provides support for high order filtering and general advanced image processing, features in high demand as screen sizes get larger and more and more post-processing techniques are developed. For more information, please visit https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_QCOM_image_processing.html +## [DspOffload](dspOffload) + +App illustrating how the Hexagon DSP can be used to run graphics tasks and write results to GPU accessable Android Hardware Buffers. + +## [forward](forward) + +App illustrating a resonably complex forward rendered scene. ## [hdrSwapchain](hdrSwapchain) -HDRSwapchain demonstrates the use of different swapchain image formats and colorspaces. Has a gui dropdown that allows for switching buffer formats on the fly. -Also demonstrates Qualcomm Vulkan render-pass transform extension VK_QCOM_render_pass_transform. -## [RayQueryShadows](RayQueryShadows) -Uses the Vulkan Ray Tracing extensions to implement shadows from a point light source. -Requires drivers with the 'final' Vulkan Ray Tracing api (Dec 2020 onwards). +Demonstrates the use of different swapchain image formats and colorspaces. Has a gui dropdown that allows for switching buffer formats on the fly. + +Also demonstrates Qualcomm Vulkan render-pass transform extension [VK_QCOM_render_pass_transform](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_QCOM_render_pass_transform.html) + +## [rayQueryShadows](rayQueryShadows) -## [RayReflections](RayReflections) -Uses the Vulkan Ray Tracing extensions to implement reflections +Uses Vulkan Ray Tracing extension (VK_KHR_ray_tracing) to implement shadows using Ray Queries. + +Currently Windows only. ## [rotatedCopy](rotatedCopy) -Sample to initialize and use the 'VK_QCOM_rotated_copy_commands' Vulkan extension. -Extension may/will need enabling on older Qualcomm Vulkan drivers. Sample does nothing useful on non Qualcomm hardware. + +Uses VK_QCOM_rotated_copy_commands (and VK_KHR_copy_commands2) extension to blit from a (lower resolution) intermediate render target to the device framebuffer rotated to match the devices native orientation (and thus avoiding the Android SurfaceFlinger doing an additional rotation/composition step). + +## [shaderResolve](shaderResolve) + +Uses VK_QCOM_render_pass_shader_resolve extension to implement MSAA and order-independent transparency in a deferred renderer. ## [shaderResolveTonemap](shaderResolveTonemap) -ShaderResolveTonemap Uses VK_QCOM_render_pass_shader_resolve to perform a filmic tonemapping operator (on a simple forward rendered scene) as part of the MSAA resolve. -Optionally runs the tonemap/resolve as a subpass of the main scene pass. Has onscreen UI controls to modify MSAA sample counts and to enable/disable the shader resolve and use of subpasses (for measuring GPU subpass/shader-resolve efficiency). \ No newline at end of file + +Uses VK_QCOM_render_pass_shader_resolve to perform a filmic tonemapping operator (on a simple forward rendered scene) as part of the MSAA resolve. + +Optionally runs the tonemap/resolve as a subpass of the main scene pass. Has onscreen UI controls to modify MSAA sample counts and to enable/disable the shader resolve and use of subpasses (for measuring GPU subpass/shader-resolve efficiency). + +## [atmospherics](atmospherics) + +Atmospheric lighting. + +# Configuration + +Each sample can be configured by adding an 'app_config.txt' file in the root of the relevant sample (ie samples/forward/app_config.txt). + +On Android the app_config.txt needs to be pushed to device, into /sdcard/Android/data/ANDROID_APP_ID/files/. , many samples have a batch file to do this (eg 07_InstallConfig.bat). + +If this file is missing or empty the sample application should run with 'reasonable' defaults. + +Samples share a set of common settings and can define additional settings specific to the sample's functionality. + +## Common config settings + +gFramesToRender = x + +Render a specific number of frames before exiting the app. x should be in integer. 0 (default) will render 'forever'. + +# File handling + +## Windows + +Executables are compiled to project\windows\solution\samples\APPLICATION\Debug\APPLICATION.exe + +Executables should be run from the samples\APPLICATION folder and data files (textures, models, shaders) are loaded from the Media subfolder. The Visual Studio solution is pre-configured to run the exe from the correct folder. + +## Android + +Apk application bundles are complied to build\android\APPLICATION\outputs\apk\debug\APPLICATION-debug.apk + +So long as the sample's Media files were prepared (02_PrepareMedia.bat) before building the apk, the apk is stand-alone and contains the application executable and Media files. + +If desired any files in the Media folder can be 'overridden' by copying the relevant file to /sdcard/Android/data/ANDROID_APP_ID/files/. with the expected folder path. Eg you can copy a shader file from Media\Shaders\. to /sdcard/Android/data/ANDROID_APP_ID/files/Media/Shaders/. and see your new shader code when the application is re-launched. + diff --git a/samples/SubPass/01_CompileShaders.bat b/samples/SubPass/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/SubPass/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/SubPass/02_Install_APK.bat b/samples/SubPass/02_Install_APK.bat deleted file mode 100644 index 2f53c8f..0000000 --- a/samples/SubPass/02_Install_APK.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\SubPass\outputs\apk\debug\SubPass-debug.apk -@echo **************************************** -call adb install -r -t ..\..\build\android\SubPass\outputs\apk\debug\SubPass-debug.apk -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/SubPass/CMakeLists.txt b/samples/SubPass/CMakeLists.txt deleted file mode 100644 index 6bc65a2..0000000 --- a/samples/SubPass/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (SubPass C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/SubPass.cpp - code/main/SubPass.hpp -) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Copy required models to local folders -# -include(ModelPackager) - -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/SubPass/build.gradle b/samples/SubPass/build.gradle deleted file mode 100644 index a4d5e2d..0000000 --- a/samples/SubPass/build.gradle +++ /dev/null @@ -1,90 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - defaultConfig { - applicationId "com.quic.subpass" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${project.rootDir}", "-DFRAMEWORK_DIR=${project.rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/empty/build.gradle b/samples/empty/build.gradle deleted file mode 100644 index 8b85955..0000000 --- a/samples/empty/build.gradle +++ /dev/null @@ -1,73 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - defaultConfig { - applicationId "com.quic.frameworkempty" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${project.rootDir}", "-DFRAMEWORK_DIR=${project.rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - assets.srcDirs = ['assets'] - - // Uncomment this to enable validation - //jniLibs { - // srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" - //} - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - -} diff --git a/samples/graph_pipelines/Adb_Logcat_graph_pipelines.bat b/samples/graph_pipelines/Adb_Logcat_graph_pipelines.bat deleted file mode 100644 index 674f5fd..0000000 --- a/samples/graph_pipelines/Adb_Logcat_graph_pipelines.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat (grep "graph_pipelines")... -call adb logcat -c -call adb logcat | FIND /I "graph_pipelines" - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause diff --git a/samples/graph_pipelines/CMakeLists.txt b/samples/graph_pipelines/CMakeLists.txt index b8e7fac..e1e1eab 100644 --- a/samples/graph_pipelines/CMakeLists.txt +++ b/samples/graph_pipelines/CMakeLists.txt @@ -19,30 +19,49 @@ if(NOT DEFINED PROJECT_ROOT_DIR) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - # # Do all the build steps for a Framework application. # needs Framework_dir and project_name variables. # include(FrameworkApplicationHelper) +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") +register_local_asset_path(MISC_DESTINATION "${DEFAULT_LOCAL_MISC_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + # # Copy required models to local folders # include(ModelPackager) -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) # # Convert and copy textures to local folders # include(TexturePackager) -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/graph_pipelines/CompileShaders.bat b/samples/graph_pipelines/CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/graph_pipelines/CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/graph_pipelines/Install_APK.bat b/samples/graph_pipelines/Install_APK.bat index c390062..62ab3c4 100644 --- a/samples/graph_pipelines/Install_APK.bat +++ b/samples/graph_pipelines/Install_APK.bat @@ -1,13 +1,21 @@ @echo off cd /D "%~dp0" +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + @echo. @echo **************************************** -@echo Install ..\..\build\android\graph_pipelines\outputs\apk\debug\graph_pipelines-debug.apk +@echo Installing APK for project: %project_name% @echo **************************************** -call adb install -r -t ..\..\build\android\graph_pipelines\outputs\apk\debug\graph_pipelines-debug.apk + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + @echo. @echo **************************************** @echo Done! @echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/graph_pipelines/build.gradle b/samples/graph_pipelines/build.gradle deleted file mode 100644 index c85b9b7..0000000 --- a/samples/graph_pipelines/build.gradle +++ /dev/null @@ -1,91 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - namespace "com.qualcomm.sgs.graph_pipelines" - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.graph_pipelines" - minSdkVersion 26 - targetSdkVersion 33 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/graph_pipelines/code/main/application.cpp b/samples/graph_pipelines/code/main/application.cpp index 207a518..ec0d80c 100644 --- a/samples/graph_pipelines/code/main/application.cpp +++ b/samples/graph_pipelines/code/main/application.cpp @@ -23,6 +23,9 @@ #include "mesh/meshLoader.hpp" #include "system/math_common.hpp" #include "texture/textureManager.hpp" +#include "vulkan/extensionLib.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" #include "imgui.h" #include @@ -45,8 +48,8 @@ namespace { static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_HUD", "RP_BLIT" }; - glm::vec3 gCameraStartPos = glm::vec3(26.48f, 20.0f, -5.21f); - glm::vec3 gCameraStartRot = glm::vec3(0.0f, 110.0f, 0.0f); + glm::vec3 gCameraStartPos = glm::vec3(0.0f, 3.5f, 0.0f); + glm::vec3 gCameraStartRot = glm::vec3(0.0f, 0.0f, 0.0f); float gFOV = PI_DIV_4; float gNearPlane = 1.0f; @@ -54,9 +57,8 @@ namespace float gNormalAmount = 0.3f; float gNormalMirrorReflectAmount = 0.05f; - const char* gModelAssetPath = "Media\\Models\\PipelineCache.bin"; - const char* gMuseumAssetPath = "Media\\Meshes\\Museum.gltf"; - const char* gTextureFolder = "Media\\Textures\\"; + const char* gSceneAssetGraphModel = "PipelineCache.bin"; + const char* gSceneAssetModel = "SteamPunkSauna.gltf"; static uint32_t FindMemoryType(VkPhysicalDevice& physicalDevice, uint32_t type_bits, VkMemoryPropertyFlags properties) { @@ -96,12 +98,16 @@ void Application::PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& //----------------------------------------------------------------------------- { ApplicationHelperBase::PreInitializeSetVulkanConfiguration(config); - config.RequiredExtension(); - config.RequiredExtension(); - config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); - config.OptionalExtension(); - config.OptionalExtension(); + // config.RequiredExtension(); + // config.RequiredExtension(); + // config.RequiredExtension(); + + config.OptionalExtension(); + config.OptionalExtension(); } //----------------------------------------------------------------------------- @@ -116,19 +122,26 @@ bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) m_IsGraphPipelinesSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_ARM_TENSORS_EXTENSION_NAME) && GetVulkan()->HasLoadedVulkanDeviceExtension(VK_ARM_DATA_GRAPH_EXTENSION_NAME); - // TODO: Remove when supported by the driver + // If Ext_VK_ARM_data_graph->AvailableFeatures.dataGraph is supported, force graph pipeline support here while that + // isn't fully supported publicly by the driver #if defined(OS_ANDROID) { - auto* Ext_VK_ARM_tensors = static_cast(GetVulkan()->m_DeviceExtensions.GetExtension(VK_ARM_TENSORS_EXTENSION_NAME)); - auto* Ext_VK_ARM_data_graph = static_cast(GetVulkan()->m_DeviceExtensions.GetExtension(VK_ARM_DATA_GRAPH_EXTENSION_NAME)); - auto fpGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(GetVulkan()->GetVulkanInstance(), "vkGetDeviceProcAddr"); - if (Ext_VK_ARM_tensors && Ext_VK_ARM_data_graph && fpGetDeviceProcAddr) + auto* Ext_VK_ARM_tensors = static_cast(GetVulkan()->m_DeviceExtensions.GetExtension(VK_ARM_TENSORS_EXTENSION_NAME)); + auto* Ext_VK_ARM_data_graph = static_cast(GetVulkan()->m_DeviceExtensions.GetExtension(VK_ARM_DATA_GRAPH_EXTENSION_NAME)); + if (Ext_VK_ARM_tensors && Ext_VK_ARM_data_graph && Ext_VK_ARM_data_graph->AvailableFeatures.dataGraph) { m_IsGraphPipelinesSupported = true; } } #endif + // If for some reason we are able to enable the extension, but no graph queue was detected, disable + // graph pipelines here (this check probably isn't necessary) + if (!GetVulkan()->IsDataGraphQueueSupported()) + { + m_IsGraphPipelinesSupported = false; + } + // NOTE: You should configure these according to what the model expects. m_RenderResolution = glm::ivec2(960, 540); m_UpscaledResolution = glm::ivec2(1920, 1080); // It just happens that this aligns with the default values of gSurfaceWidth and gSurfaceHeight. @@ -198,11 +211,6 @@ bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) return false; } - if (!GetVulkan()->IsDataGraphQueueSupported()) - { - return false; - } - return true; } @@ -240,7 +248,7 @@ void Application::Destroy() // Render passes / Semaphores for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].RenderPass, nullptr); + m_RenderPassData[whichPass].RenderContext.clear(); vkDestroySemaphore(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].PassCompleteSemaphore, nullptr); } @@ -322,8 +330,6 @@ bool Application::CreateTensors() } auto& vulkan = *GetVulkan(); - const auto& tensor_extension = vulkan.GetExtension(); - assert(tensor_extension != nullptr); LOGI("Creating Tensors..."); @@ -375,7 +381,7 @@ bool Application::CreateTensors() .pQueueFamilyIndices = nullptr }; - if (tensor_extension->m_vkCreateTensorARM(vulkan.m_VulkanDevice, &tensorInfo, nullptr, &targetTensor.tensor) != VK_SUCCESS) + if (vkCreateTensorARM(vulkan.m_VulkanDevice, &tensorInfo, nullptr, &targetTensor.tensor) != VK_SUCCESS) { return false; } @@ -400,7 +406,7 @@ bool Application::CreateTensors() }; VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; - tensor_extension->m_vkGetTensorMemoryRequirementsARM(vulkan.m_VulkanDevice, &memReqInfo, &memReq); + vkGetTensorMemoryRequirementsARM(vulkan.m_VulkanDevice, &memReqInfo, &memReq); #else VkDeviceTensorMemoryRequirementsARM deviceMemReqInfo = @@ -410,7 +416,7 @@ bool Application::CreateTensors() .pCreateInfo = &tensorInfo }; VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; - tensor_extension->m_vkGetDeviceTensorMemoryRequirementsARM(vulkan.m_VulkanDevice, &deviceMemReqInfo, &memReq); + vkGetDeviceTensorMemoryRequirementsARM(vulkan.m_VulkanDevice, &deviceMemReqInfo, &memReq); #endif // TENSOR ALIASED BUFFER // @@ -471,7 +477,7 @@ bool Application::CreateTensors() LOGI("Binding Tensor Memory"); - if(tensor_extension->m_vkBindTensorMemoryARM(vulkan.m_VulkanDevice, 1, &bindInfo) != VK_SUCCESS) + if(vkBindTensorMemoryARM(vulkan.m_VulkanDevice, 1, &bindInfo) != VK_SUCCESS) { return false; } @@ -496,7 +502,7 @@ bool Application::CreateTensors() .format = targetTensor.tensorDescription.format }; - if (tensor_extension->m_vkCreateTensorViewARM(vulkan.m_VulkanDevice, &viewInfo, nullptr, &targetTensor.tensorView) != VK_SUCCESS) + if (vkCreateTensorViewARM(vulkan.m_VulkanDevice, &viewInfo, nullptr, &targetTensor.tensorView) != VK_SUCCESS) { return false; } @@ -641,14 +647,13 @@ bool Application::CreateGraphPipeline() } auto& vulkan = *GetVulkan(); - const auto& data_graph_extension = vulkan.GetExtension(); - assert(data_graph_extension != nullptr); LOGI("Loading file model from disk..."); std::vector modelData; { - if (!m_AssetManager->LoadFileIntoMemory(gModelAssetPath, modelData)) + const auto sceneAssetGraphModel = std::filesystem::path(MISC_DESTINATION_PATH).append(gSceneAssetGraphModel).string(); + if (!m_AssetManager->LoadFileIntoMemory(sceneAssetGraphModel, modelData)) { LOGE("Failed to load Model file, disabling the Graph Pipelines extension"); m_IsGraphPipelinesSupported = false; @@ -739,7 +744,7 @@ bool Application::CreateGraphPipeline() .pResourceInfos = resourceInfos }; - if (data_graph_extension->m_vkCreateDataGraphPipelinesARM( + if (vkCreateDataGraphPipelinesARM( vulkan.m_VulkanDevice, VK_NULL_HANDLE, m_GraphPipelineInstance.pipelineCache, @@ -761,7 +766,7 @@ bool Application::CreateGraphPipeline() .dataGraphPipeline = m_GraphPipelineInstance.graphPipeline }; - if (data_graph_extension->m_vkCreateDataGraphPipelineSessionARM( + if (vkCreateDataGraphPipelineSessionARM( vulkan.m_VulkanDevice, &sessionInfo, nullptr, @@ -778,7 +783,7 @@ bool Application::CreateGraphPipeline() bindReqsInfo.sType = VK_STRUCTURE_TYPE_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_REQUIREMENTS_INFO_ARM; bindReqsInfo.session = m_GraphPipelineInstance.graphSession; - if (data_graph_extension->m_vkGetDataGraphPipelineSessionBindPointRequirementsARM(vulkan.m_VulkanDevice, &bindReqsInfo, &bindReqsCount, NULL) != VK_SUCCESS) + if (vkGetDataGraphPipelineSessionBindPointRequirementsARM(vulkan.m_VulkanDevice, &bindReqsInfo, &bindReqsCount, NULL) != VK_SUCCESS) { return false; } @@ -789,7 +794,7 @@ bool Application::CreateGraphPipeline() bindReqs[i].sType = VK_STRUCTURE_TYPE_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_REQUIREMENT_ARM; } - if (data_graph_extension->m_vkGetDataGraphPipelineSessionBindPointRequirementsARM(vulkan.m_VulkanDevice, &bindReqsInfo, &bindReqsCount, bindReqs.data()) != VK_SUCCESS) + if (vkGetDataGraphPipelineSessionBindPointRequirementsARM(vulkan.m_VulkanDevice, &bindReqsInfo, &bindReqsCount, bindReqs.data()) != VK_SUCCESS) { return false; } @@ -813,7 +818,7 @@ bool Application::CreateGraphPipeline() memReqsInfo.objectIndex = j; VkMemoryRequirements2 memReqs = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; - data_graph_extension->m_vkGetDataGraphPipelineSessionMemoryRequirementsARM(vulkan.m_VulkanDevice, &memReqsInfo, &memReqs); + vkGetDataGraphPipelineSessionMemoryRequirementsARM(vulkan.m_VulkanDevice, &memReqsInfo, &memReqs); VkMemoryAllocateInfo info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; info.pNext = nullptr; @@ -838,7 +843,7 @@ bool Application::CreateGraphPipeline() bindMem.objectIndex = j; bindMem.memory = m_GraphPipelineInstance.sessionMemory[memCount]; - if (data_graph_extension->m_vkBindDataGraphPipelineSessionMemoryARM(vulkan.m_VulkanDevice, 1, &bindMem) != VK_SUCCESS) + if (vkBindDataGraphPipelineSessionMemoryARM(vulkan.m_VulkanDevice, 1, &bindMem) != VK_SUCCESS) { return false; } @@ -864,14 +869,11 @@ bool Application::CreateGraphPipeline() //----------------------------------------------------------------------------- void Application::CopyImageToTensor( CommandListVulkan& cmdList, - TextureVulkan& srcImage, + const TextureVulkan& srcImage, VkImageLayout currentLayout, const GraphPipelineTensor& tensorBinding) //----------------------------------------------------------------------------- { - const auto& synchronization2_extension = GetVulkan()->GetExtension(); - assert(synchronization2_extension != nullptr); - VkImageMemoryBarrier2 imageBarrierToTransfer = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, @@ -900,7 +902,7 @@ void Application::CopyImageToTensor( .pImageMemoryBarriers = &imageBarrierToTransfer }; - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); VkBufferImageCopy copyRegion = { @@ -930,18 +932,18 @@ void Application::CopyImageToTensor( std::swap(imageBarrierToTransfer.srcAccessMask, imageBarrierToTransfer.dstAccessMask); std::swap(imageBarrierToTransfer.srcStageMask, imageBarrierToTransfer.dstStageMask); - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); } //----------------------------------------------------------------------------- void Application::CopyTensorToImage( CommandListVulkan& cmdList, - TextureVulkan& dstImage, + const TextureVulkan& dstImage, VkImageLayout currentLayout, const GraphPipelineTensor& tensorBinding) //----------------------------------------------------------------------------- { - const auto& synchronization2_extension = GetVulkan()->GetExtension(); + const auto& synchronization2_extension = GetVulkan()->GetExtension(); assert(synchronization2_extension != nullptr); VkImageMemoryBarrier2 imageBarrierToTransfer = @@ -972,7 +974,7 @@ void Application::CopyTensorToImage( .pImageMemoryBarriers = &imageBarrierToTransfer }; - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); VkBufferImageCopy copyRegion = { @@ -1001,20 +1003,19 @@ void Application::CopyTensorToImage( std::swap(imageBarrierToTransfer.srcAccessMask, imageBarrierToTransfer.dstAccessMask); std::swap(imageBarrierToTransfer.srcStageMask, imageBarrierToTransfer.dstStageMask); - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfo); } - //----------------------------------------------------------------------------- void Application::CopyImageToImageBlit( - CommandListVulkan& cmdList, - TextureVulkan& srcImage, - VkImageLayout srcLayout, - TextureVulkan& dstImage, - VkImageLayout dstFinalLayout) + CommandListVulkan& cmdList, + const TextureVulkan& srcImage, + VkImageLayout srcLayout, + const TextureVulkan& dstImage, + VkImageLayout dstFinalLayout) //----------------------------------------------------------------------------- { - const auto& synchronization2_extension = GetVulkan()->GetExtension(); + const auto& synchronization2_extension = GetVulkan()->GetExtension(); assert(synchronization2_extension != nullptr); VkImageMemoryBarrier2 dstBarrier = @@ -1045,7 +1046,7 @@ void Application::CopyImageToImageBlit( .pImageMemoryBarriers = &dstBarrier }; - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfoDst); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfoDst); // Blit image VkImageBlit blitRegion = {}; @@ -1082,7 +1083,7 @@ void Application::CopyImageToImageBlit( dstBarrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; dstBarrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; - synchronization2_extension->m_vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfoDst); + vkCmdPipelineBarrier2KHR(cmdList.m_VkCommandBuffer, &depInfoDst); } @@ -1090,10 +1091,10 @@ void Application::CopyImageToImageBlit( bool Application::LoadShaders() //----------------------------------------------------------------------------- { - m_ShaderManager = std::make_unique>(*GetVulkan()); + m_ShaderManager = std::make_unique(*GetVulkan()); m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); - m_MaterialManager = std::make_unique>(); + m_MaterialManager = std::make_unique(*GetVulkan()); LOGI("******************************"); LOGI("Loading Shaders..."); @@ -1101,12 +1102,12 @@ bool Application::LoadShaders() typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "SceneOpaque", "Media\\Shaders\\SceneOpaque.json" }, - tIdAndFilename { "SceneTransparent", "Media\\Shaders\\SceneTransparent.json" } + { tIdAndFilename { "Blit", "Blit.json" }, + tIdAndFilename { "SceneOpaque", "SceneOpaque.json" }, + tIdAndFilename { "SceneTransparent", "SceneTransparent.json" } }) { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); LOGI("Please verify if you have all required assets on the sample media folder"); @@ -1131,10 +1132,14 @@ bool Application::CreateRenderTargets() TextureFormat vkDesiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); TextureFormat desiredDepthFormat = vkDesiredDepthFormat; - const TextureFormat MainColorType[] = { TextureFormat::R8G8B8_UNORM }; + // Note: R8G8B8_UNORM is used here since that's what the upscaling model expects, if no upscaling will ever + // be performed, just default to the usual R8G8B8A8_SRGB format + // It's likely R8G8B8_UNORM isn't supported where graph pipelines aren't also supported + const TextureFormat MainColorType[] = { m_IsGraphPipelinesSupported ? TextureFormat::R8G8B8_UNORM : TextureFormat::R8G8B8A8_SRGB }; const TEXTURE_TYPE MainTextureType[] = { TEXTURE_TYPE::TT_RENDER_TARGET_TRANSFERSRC }; // Needed for tensor copy from operation. const TEXTURE_TYPE UpscaledTextureType = TEXTURE_TYPE::TT_CPU_UPDATE; // Needed for tensor copy to operation. const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_SRGB }; + const Msaa MSAA[] = { Msaa::Samples1 }; if (!m_RenderPassData[RP_SCENE].RenderTarget.Initialize( pVulkan, @@ -1142,14 +1147,14 @@ bool Application::CreateRenderTargets() m_RenderResolution.y, MainColorType, desiredDepthFormat, - VK_SAMPLE_COUNT_1_BIT, "Scene RT", - MainTextureType)) + MainTextureType, + MSAA)) { LOGE("Unable to create scene render target"); return false; } - + { CreateTexObjectInfo createInfo{}; createInfo.uiWidth = m_UpscaledResolution.x; @@ -1158,14 +1163,20 @@ bool Application::CreateRenderTargets() createInfo.Format = MainColorType[0]; createInfo.TexType = UpscaledTextureType; createInfo.pName = "Upscaled RT"; - createInfo.Msaa = VK_SAMPLE_COUNT_1_BIT; + createInfo.Msaa = Msaa::Samples1; createInfo.FilterMode = SamplerFilter::Linear; m_UpscaledImageResult = CreateTextureObject(*GetVulkan(), createInfo); } // Notice no depth on the HUD RT - if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) + if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize( + pVulkan, + gSurfaceWidth, + gSurfaceHeight, + HudColorType, + TextureFormat::UNDEFINED, + "HUD RT")) { LOGE("Unable to create hud render target"); return false; @@ -1201,43 +1212,53 @@ bool Application::InitUniforms() bool Application::InitAllRenderPasses() //----------------------------------------------------------------------------- { - Vulkan* const pVulkan = GetVulkan(); + Vulkan& vulkan = *GetVulkan(); // ColorInputUsage | ClearDepthRenderPass | ColorOutputUsage | DepthOutputUsage | ClearColor - m_RenderPassData[RP_SCENE].PassSetup = { RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, {}}; - m_RenderPassData[RP_HUD].PassSetup = { RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; - m_RenderPassData[RP_BLIT].PassSetup = { RenderPassInputUsage::DontCare, true, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}}; + m_RenderPassData[RP_SCENE].RenderPassSetup = { RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, {}}; + m_RenderPassData[RP_HUD].RenderPassSetup = { RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; + m_RenderPassData[RP_BLIT].RenderPassSetup = { RenderPassInputUsage::DontCare, true, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}}; - TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; - auto swapChainColorFormat = std::span({ &surfaceFormat, 1 }); - auto swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; + TextureFormat surfaceFormat = vulkan.m_SurfaceFormat; + auto swapChainColorFormat = std::span({ &surfaceFormat, 1 }); + auto swapChainDepthFormat = vulkan.m_SwapchainDepth.format; LOGI("******************************"); LOGI("Initializing Render Passes... "); LOGI("******************************"); - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + for (uint32_t whichPass = 0; whichPass < RP_BLIT; whichPass++) { - bool isSwapChainRenderPass = whichPass == RP_BLIT; + std::span colorFormats = m_RenderPassData[whichPass].RenderTarget.m_pLayerFormats; + TextureFormat depthFormat = m_RenderPassData[whichPass].RenderTarget.m_DepthFormat; - std::span colorFormats = isSwapChainRenderPass ? swapChainColorFormat : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = isSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; - - const auto& passSetup = m_RenderPassData[whichPass].PassSetup; + const auto& passSetup = m_RenderPassData[whichPass].RenderPassSetup; + auto& passData = m_RenderPassData[whichPass]; - if (!pVulkan->CreateRenderPass( + RenderPass renderPass; + if (!vulkan.CreateRenderPass( { colorFormats }, depthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, passSetup.ColorInputUsage, passSetup.ColorOutputUsage, passSetup.ClearDepthRenderPass, passSetup.DepthOutputUsage, - & m_RenderPassData[whichPass].RenderPass)) + renderPass)) { return false; } - + Framebuffer framebuffer; + framebuffer.Initialize( vulkan, + renderPass, + passData.RenderTarget.m_ColorAttachments, + &passData.RenderTarget.m_DepthAttachment, + sRenderPassNames[whichPass] ); + passData.RenderContext.push_back({std::move(renderPass), {}/*pipeline*/, std::move(framebuffer), sRenderPassNames[whichPass]}); + } + for (auto whichBuffer = 0; whichBuffer < vulkan.GetSwapchainBufferCount(); ++whichBuffer) + { + m_RenderPassData[RP_BLIT].RenderContext.push_back( {vulkan.m_SwapchainRenderPass.Copy(), {}, vulkan.GetSwapchainFramebuffer( whichBuffer ), "RP_BLIT"} ); } return true; @@ -1248,12 +1269,12 @@ bool Application::InitGui(uintptr_t windowHandle) //----------------------------------------------------------------------------- { const auto& hudRenderTarget = m_RenderPassData[RP_HUD].RenderTarget; - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPassData[RP_HUD].RenderPass); - if (!m_Gui->Initialize(windowHandle, hudRenderTarget[0].m_Width, hudRenderTarget[0].m_Height)) + m_Gui = std::make_unique(*GetVulkan(), m_RenderPassData[RP_HUD].RenderContext[0].GetRenderPass().Copy()); + if (!m_Gui->Initialize(windowHandle, TextureFormat::R8G8B8A8_UNORM, hudRenderTarget.m_Width, hudRenderTarget.m_Height)) { return false; } - + return true; } @@ -1279,12 +1300,13 @@ bool Application::LoadMeshObjects() LOGI("Loading and preparing the museum..."); LOGI("***********************************"); - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - - auto* whiteTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\white_d.ktx", m_SamplerEdgeClamp); - auto* blackTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\black_d.ktx", m_SamplerEdgeClamp); - auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\normal_default.ktx", m_SamplerEdgeClamp); + m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory(TEXTURE_DESTINATION_PATH)); + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* blackTexture = m_TextureManager->GetOrLoadTexture("black_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture("normal_default.ktx", m_SamplerRepeat, prefixTextureDir); + if (!whiteTexture || !blackTexture || !normalDefaultTexture) { LOGE("Failed to load supporting textures"); @@ -1310,14 +1332,17 @@ bool Application::LoadMeshObjects() auto MaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef)->std::optional { - auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerEdgeClamp); - auto* normalTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.bumpFilename, m_SamplerEdgeClamp); - auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.emissiveFilename, m_SamplerEdgeClamp); - auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.specMapFilename, m_SamplerEdgeClamp); + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const PathManipulator_ChangeExtension changeTextureExt{".ktx"}; + + auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* normalTexture = m_TextureManager->GetOrLoadTexture(materialDef.bumpFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(materialDef.emissiveFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(materialDef.specMapFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); bool alphaCutout = materialDef.alphaCutout; bool transparent = materialDef.transparent; - const Shader* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; + const auto* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; ObjectMaterialParameters objectMaterial; objectMaterial.objectFragUniformData.Color.r = static_cast(materialDef.baseColorFactor[0]); @@ -1332,8 +1357,8 @@ bool Application::LoadMeshObjects() return std::nullopt; } - auto shaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *targetShader, NUM_VULKAN_BUFFERS, - [&](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto shaderMaterial = m_MaterialManager->CreateMaterial(*targetShader, NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { @@ -1352,9 +1377,10 @@ bool Application::LoadMeshObjects() return { metallicRoughnessTexture ? metallicRoughnessTexture : blackTexture }; } + assert(false); return {}; }, - [&](const std::string& bufferName) -> tPerFrameVkBuffer + [&](const std::string& bufferName) -> PerFrameBufferVulkan { if (bufferName == "Vert") { @@ -1369,6 +1395,7 @@ bool Application::LoadMeshObjects() return { m_LightUniform.buf.GetVkBuffer() }; } + assert(false); return {}; } ); @@ -1380,20 +1407,19 @@ bool Application::LoadMeshObjects() const auto loaderFlags = 0; // No instancing const bool ignoreTransforms = (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0; - MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(gMuseumAssetPath); - MeshObjectIntermediateGltfProcessor meshObjectProcessor(gMuseumAssetPath, ignoreTransforms, glm::vec3(1.0f,1.0f,1.0f)); + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(sceneAssetPath); + MeshObjectIntermediateGltfProcessor meshObjectProcessor(sceneAssetPath, ignoreTransforms, glm::vec3(1.0f, 1.0f, 1.0f)); CameraGltfProcessor meshCameraProcessor{}; - if (!MeshLoader::LoadGltf(*m_AssetManager, gMuseumAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || - !DrawableLoader::CreateDrawables(*pVulkan, - std::move(meshObjectProcessor.m_meshObjects), - { &m_RenderPassData[RP_SCENE].RenderPass, 1 }, - &sRenderPassNames[RP_SCENE], - MaterialLoader, - m_SceneDrawables, - {}, // RenderPassMultisample - loaderFlags, - {})) // RenderPassSubpasses + if (!MeshLoader::LoadGltf(*m_AssetManager, sceneAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || + !DrawableLoader::CreateDrawables( + *pVulkan, + std::move(meshObjectProcessor.m_meshObjects), + m_RenderPassData[RP_SCENE].RenderContext, + MaterialLoader, + m_SceneDrawables, + loaderFlags)) { LOGE("Error Loading the museum gltf file"); LOGI("Please verify if you have all required assets on the sample media folder"); @@ -1412,12 +1438,12 @@ bool Application::LoadMeshObjects() LOGI("Creating Quad mesh..."); LOGI("*********************"); - MeshObject blitQuadMesh; + Mesh blitQuadMesh; MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); // Blit Material - auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pBlitQuadShader, pVulkan->m_SwapchainImageCount, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitQuadShader, pVulkan->m_SwapchainImageCount, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { @@ -1425,18 +1451,18 @@ bool Application::LoadMeshObjects() } else if (texName == "Overlay") { - return { &m_RenderPassData[RP_HUD].RenderTarget[0].m_ColorAttachments[0] }; + return { &m_RenderPassData[RP_HUD].RenderTarget.GetColorAttachments()[0] }; } return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer + [this](const std::string& bufferName) -> PerFrameBufferVulkan { return {}; } ); m_BlitQuadDrawable = std::make_unique(*pVulkan, std::move(blitQuadShaderMaterial)); - if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderPass, sRenderPassNames[RP_BLIT], std::move(blitQuadMesh))) + if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderContext[0], std::move(blitQuadMesh))) { return false; } @@ -1473,14 +1499,14 @@ bool Application::InitCommandBuffers() m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.resize(pVulkan->m_SwapchainImageCount); char szName[256]; - const VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + const CommandListBase::Type CmdBuffLevel = CommandListBase::Type::Secondary; for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[whichPass].PassCmdBuffer.size(); whichBuffer++) { // The Pass Command Buffer => Primary sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) + if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Primary)) { return false; } @@ -1500,7 +1526,7 @@ bool Application::InitCommandBuffers() m_GraphPipelineCommandLists.resize(NUM_VULKAN_BUFFERS); for (auto& graphPipelineCommandList : m_GraphPipelineCommandLists) { - if (!graphPipelineCommandList.Initialize(GetVulkan(), "Graph Pipeline CMD Buffer", VK_COMMAND_BUFFER_LEVEL_PRIMARY, Vulkan::eDataGraphQueue)) + if (!graphPipelineCommandList.Initialize(GetVulkan(), "Graph Pipeline CMD Buffer", CommandListBase::Type::Primary, Vulkan::eDataGraphQueue)) { return false; } @@ -1561,8 +1587,8 @@ bool Application::BuildCmdBuffers() { auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - uint32_t targetWidth = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - uint32_t targetHeight = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; + uint32_t targetWidth = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget.GetWidth(); + uint32_t targetHeight = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget.GetHeight(); VkViewport viewport = {}; viewport.x = 0.0f; @@ -1579,11 +1605,10 @@ bool Application::BuildCmdBuffers() scissor.extent.height = targetHeight; // Set up some values that change based on render pass - VkRenderPass whichRenderPass = renderPassData.RenderPass; - VkFramebuffer whichFramebuffer = bisSwapChainRenderPass ? pVulkan->m_SwapchainBuffers[whichBuffer].framebuffer : renderPassData.RenderTarget[0].m_FrameBuffer; + VkFramebuffer whichFramebuffer = bisSwapChainRenderPass ? pVulkan->m_SwapchainBuffers[whichBuffer].framebuffer : renderPassData.RenderContext[0].GetFramebuffer()->m_FrameBuffer; // Objects (can render into any pass except Blit) - if (!cmdBufer.Begin(whichFramebuffer, whichRenderPass, bisSwapChainRenderPass)) + if (!cmdBufer.Begin(whichFramebuffer, renderPassData.RenderContext[0].GetRenderPass(), bisSwapChainRenderPass)) { return false; } @@ -1764,7 +1789,7 @@ void Application::Render(float fltDiffTime) { CopyImageToTensor( m_RenderPassData[RP_SCENE].PassCmdBuffer[whichBuffer], - m_RenderPassData[RP_SCENE].RenderTarget.m_RenderTargets[0].m_ColorAttachments[0], + m_RenderPassData[RP_SCENE].RenderTarget.GetColorAttachments()[0], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, m_InputTensor); } @@ -1772,7 +1797,7 @@ void Application::Render(float fltDiffTime) { CopyImageToImageBlit( m_RenderPassData[RP_SCENE].PassCmdBuffer[whichBuffer], - m_RenderPassData[RP_SCENE].RenderTarget.m_RenderTargets[0].m_ColorAttachments[0], + m_RenderPassData[RP_SCENE].RenderTarget.GetColorAttachments()[0], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, m_UpscaledImageResult, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); @@ -1787,9 +1812,6 @@ void Application::Render(float fltDiffTime) // Data Graph preparation + dispatch for Upscaling if (isUpscalingActive) { - const auto& data_graph_extension = GetVulkan()->GetExtension(); - assert(data_graph_extension != nullptr); - m_GraphPipelineCommandLists[whichBuffer].Begin(); vkCmdBindPipeline( @@ -1811,7 +1833,7 @@ void Application::Render(float fltDiffTime) dispatchInfo.sType = VK_STRUCTURE_TYPE_DATA_GRAPH_PIPELINE_DISPATCH_INFO_ARM; dispatchInfo.flags = 0; - data_graph_extension->m_vkCmdDispatchDataGraphARM( + vkCmdDispatchDataGraphARM( m_GraphPipelineCommandLists[whichBuffer].m_VkCommandBuffer, m_GraphPipelineInstance.graphSession, &dispatchInfo); @@ -1828,7 +1850,7 @@ void Application::Render(float fltDiffTime) if (m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget[0].m_FrameBuffer); + guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderContext[0].GetFramebuffer()->m_FrameBuffer); if (guiCommandBuffer != VK_NULL_HANDLE) { BeginRenderPass(whichBuffer, RP_HUD, currentVulkanBuffer.swapchainPresentIdx); @@ -1909,10 +1931,10 @@ void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, u switch (whichPass) { case RP_SCENE: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; + framebuffer = m_RenderPassData[whichPass].RenderContext[0].GetFramebuffer()->m_FrameBuffer; break; case RP_HUD: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; + framebuffer = m_RenderPassData[whichPass].RenderContext[0].GetFramebuffer()->m_FrameBuffer; break; case RP_BLIT: framebuffer = pVulkan->m_SwapchainBuffers[WhichSwapchainImage].framebuffer; @@ -1927,16 +1949,16 @@ void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, u VkRect2D passArea = {}; passArea.offset.x = 0; passArea.offset.y = 0; - passArea.extent.width = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - passArea.extent.height = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; + passArea.extent.width = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget.GetWidth(); + passArea.extent.height = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget.GetHeight(); TextureFormat swapChainColorFormat = pVulkan->m_SurfaceFormat; auto swapChainColorFormats = std::span({ &swapChainColorFormat, 1 }); TextureFormat swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; - std::span colorFormats = bisSwapChainRenderPass ? swapChainColorFormats : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = bisSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; + std::span colorFormats = bisSwapChainRenderPass ? swapChainColorFormats : m_RenderPassData[whichPass].RenderTarget.GetLayerFormats(); + TextureFormat depthFormat = bisSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget.GetDepthFormat(); - VkClearColorValue clearColor = { renderPassData.PassSetup.ClearColor[0], renderPassData.PassSetup.ClearColor[1], renderPassData.PassSetup.ClearColor[2], renderPassData.PassSetup.ClearColor[3] }; + VkClearColorValue clearColor = { renderPassData.RenderPassSetup.ClearColor[0], renderPassData.RenderPassSetup.ClearColor[1], renderPassData.RenderPassSetup.ClearColor[2], renderPassData.RenderPassSetup.ClearColor[3] }; m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].BeginRenderPass( passArea, @@ -1945,7 +1967,7 @@ void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, u { &clearColor , 1 }, (uint32_t)colorFormats.size(), depthFormat != TextureFormat::UNDEFINED, - m_RenderPassData[whichPass].RenderPass, + m_RenderPassData[whichPass].RenderContext[0].GetRenderPass(), bisSwapChainRenderPass, framebuffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); diff --git a/samples/graph_pipelines/code/main/application.hpp b/samples/graph_pipelines/code/main/application.hpp index 577a480..9d769a1 100644 --- a/samples/graph_pipelines/code/main/application.hpp +++ b/samples/graph_pipelines/code/main/application.hpp @@ -14,10 +14,6 @@ #define NUM_SPOT_LIGHTS 4 -class ShaderManager; -class MaterialManager; -class Drawable; - enum RENDER_PASS { RP_SCENE = 0, @@ -101,8 +97,8 @@ struct PassSetupInfo struct PassData { // Pass internal data - PassSetupInfo PassSetup; - VkRenderPass RenderPass = VK_NULL_HANDLE; + PassSetupInfo RenderPassSetup; + std::vector> RenderContext; // context per framebuffer (some passes might all point to the same framebuffers) // Recorded objects that are set to be drawn on this pass std::vector< CommandListVulkan> ObjectsCmdBuffer; @@ -115,7 +111,7 @@ struct PassData // Render targed used by the underlying render pass // note: The blit pass uses the backbuffer directly instead this RT - CRenderTargetArray<1> RenderTarget; + RenderTarget RenderTarget; }; // ********************** @@ -191,22 +187,22 @@ class Application : public ApplicationHelperBase void CopyImageToTensor( CommandListVulkan& cmdList, - TextureVulkan& srcImage, + const TextureVulkan& srcImage, VkImageLayout currentLayout, const GraphPipelineTensor& tensorBinding); void CopyTensorToImage( CommandListVulkan& cmdList, - TextureVulkan& dstImage, + const TextureVulkan& dstImage, VkImageLayout currentLayout, const GraphPipelineTensor& tensorBinding); void CopyImageToImageBlit( CommandListVulkan& cmdList, - TextureVulkan& srcImage, - VkImageLayout srcLayout, - TextureVulkan& dstImage, - VkImageLayout dstFinalLayout); + const TextureVulkan& srcImage, + VkImageLayout srcLayout, + const TextureVulkan& dstImage, + VkImageLayout dstFinalLayout); private: diff --git a/samples/graph_pipelines/install_config.bat b/samples/graph_pipelines/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/graph_pipelines/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/graph_pipelines/project/android/res/values/strings.xml b/samples/graph_pipelines/project/android/res/values/strings.xml index 2197b89..6ecb278 100644 --- a/samples/graph_pipelines/project/android/res/values/strings.xml +++ b/samples/graph_pipelines/project/android/res/values/strings.xml @@ -1,4 +1,4 @@ - Graph Pipelines + SGS Graph Pipelines diff --git a/samples/hdrSwapchain/01_CompileShaders.bat b/samples/hdrSwapchain/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/hdrSwapchain/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/hdrSwapchain/02_Install_APK.bat b/samples/hdrSwapchain/02_Install_APK.bat deleted file mode 100644 index e27ab5a..0000000 --- a/samples/hdrSwapchain/02_Install_APK.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\hdrSwapchain\outputs\apk\debug\hdrSwapchain-debug.apk -@echo **************************************** -call adb install -r -t ..\..\build\android\hdrSwapchain\outputs\apk\debug\hdrSwapchain-debug.apk -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/hdrSwapchain/02_PrepareMedia.bat b/samples/hdrSwapchain/02_PrepareMedia.bat deleted file mode 100644 index 2537d54..0000000 --- a/samples/hdrSwapchain/02_PrepareMedia.bat +++ /dev/null @@ -1,65 +0,0 @@ -rem @echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\default_ddn.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_env.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_irradiance.ktx Media\Textures\. - -..\..\project\tools\simpletextureconverter.exe ..\..\vkSampleFrameworkAssets\shared\Media\Textures\nnao_f0.tga Media\Textures\nnao_f0.ktx -F R8G8B8A8Unorm -nomip -..\..\project\tools\simpletextureconverter.exe ..\..\vkSampleFrameworkAssets\shared\Media\Textures\nnao_f1.tga Media\Textures\nnao_f1.ktx -F R8G8B8A8Unorm -nomip -..\..\project\tools\simpletextureconverter.exe ..\..\vkSampleFrameworkAssets\shared\Media\Textures\nnao_f2.tga Media\Textures\nnao_f2.ktx -F R8G8B8A8Unorm -nomip -..\..\project\tools\simpletextureconverter.exe ..\..\vkSampleFrameworkAssets\shared\Media\Textures\nnao_f3.tga Media\Textures\nnao_f3.ktx -F R8G8B8A8Unorm -nomip - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skybox_Separate.* Media\Objects\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\BistroGltfFused Media\Objects\BistroGltfFused -call :ConvertTextures Media\Objects\BistroGltfFused 2048 - -echo. -echo Cleaning up afer texture conversion -echo. -pushd Media -del /s /q /f *.png *.jpg *.tga -popd - -goto:eof - - -:ConvertTextures -rem %1 is directory, %2 is size at which we downsample (in bytes) -rem Convert png images to ktx textures. -rem Larger textures are mipped and reduced in size, (very) small textures are not -SET SIZELIMIT=1000 -pushd %1 -for /R %%i in (*.png) do ( - echo %%i|findstr /i /L "Normal">nul - if errorlevel 1 ( - call :ConvertTexture %%i %2 R8G8B8A8UnormSrgb - ) else ( - rem Normal Texture (output linear 'color' values) - call :ConvertTexture %%i %2 R8G8B8A8Unorm - ) -) -popd -goto :eof - -:ConvertTexture -rem %1 is file, %2 is size at which we downsample (in bytes), %3 is the output data format -rem Convert png image to ktx texture. -if %~z1 LSS %2 ( - echo "Not scaling (or mipping) %~f1 (too small)" - ..\..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -nomip -) else ( - ..\..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -w 25%% -h 25%% -) -goto :eof diff --git a/samples/hdrSwapchain/04_Install_APK.bat b/samples/hdrSwapchain/04_Install_APK.bat deleted file mode 100644 index e846651..0000000 --- a/samples/hdrSwapchain/04_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\hdrSwapchain\outputs\apk\debug\hdrSwapchain-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\hdrSwapchain\outputs\apk\debug\hdrSwapchain-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/hdrSwapchain/06_Adb_Logcat.bat b/samples/hdrSwapchain/06_Adb_Logcat.bat deleted file mode 100644 index 6b89aff..0000000 --- a/samples/hdrSwapchain/06_Adb_Logcat.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat... -call adb logcat -c -call adb logcat - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause \ No newline at end of file diff --git a/samples/hdrSwapchain/CMakeLists.txt b/samples/hdrSwapchain/CMakeLists.txt deleted file mode 100644 index 8574d9a..0000000 --- a/samples/hdrSwapchain/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (hdrSwapchain C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/hdrSwapchain.cpp - code/main/hdrSwapchain.hpp - code/main/materials.hpp -) -set(FRAMEWORK_LIB framework_vulkan) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Copy required models to local folders -# -include(ModelPackager) - -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/hdrSwapchain/README.md b/samples/hdrSwapchain/README.md deleted file mode 100644 index 94c7940..0000000 --- a/samples/hdrSwapchain/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# HDRSwapchain Sample - -![Screenshot](img/screenshot_1.PNG) - -![Screenshot](img/screenshot_2.PNG) - -## Overview - -HDRSwapchain demonstrates the use of different swapchain image formats and colorspaces. Has a gui dropdown that allows for switching buffer formats on the fly. -Also demonstrates Qualcomm Vulkan render-pass transform extension VK_QCOM_render_pass_transform. - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - -And convert the needed textures and shaders to the correct format using the batch file below: - -``` -02_PrepareMedia.bat -``` - -Note: The sample assumes the existence of supporting assets under the **'Media'** folder. These assets are not currently distributed with the framework. -The framework team is working to build a centralized asset repository that should minimize these requirements in the near future. - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_Install_APK.bat -``` - -If desired, you can keep track of any logging by running one of the logcat batch files (which you can find on the current directory). - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. diff --git a/samples/hdrSwapchain/build.gradle b/samples/hdrSwapchain/build.gradle deleted file mode 100644 index 1074477..0000000 --- a/samples/hdrSwapchain/build.gradle +++ /dev/null @@ -1,96 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.hdrswapchain" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } -// main.assets { -// srcDirs = ['assets'] -// } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.21.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/hdrSwapchain/img/screenshot_1.PNG b/samples/hdrSwapchain/img/screenshot_1.PNG deleted file mode 100644 index 67828b9..0000000 Binary files a/samples/hdrSwapchain/img/screenshot_1.PNG and /dev/null differ diff --git a/samples/hdrSwapchain/img/screenshot_2.PNG b/samples/hdrSwapchain/img/screenshot_2.PNG deleted file mode 100644 index 814f966..0000000 Binary files a/samples/hdrSwapchain/img/screenshot_2.PNG and /dev/null differ diff --git a/samples/hdrSwapchain/shaders/ShadowShared.h b/samples/hdrSwapchain/shaders/ShadowShared.h deleted file mode 100644 index 34685fa..0000000 --- a/samples/hdrSwapchain/shaders/ShadowShared.h +++ /dev/null @@ -1,128 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Shared Implementation of Shadow mapping code. -// - -//----------------------------------------------------------------------------- -float GetInShadow(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - float fBias = 0.001; - float NormalizedCoord = (LocalShadowCoord.z / LocalShadowCoord.w) - fBias; - - // Flag for in shadow - // Step returns 0.0 of second parameter is less that first parameter. Returns 1.0 otherwise - -// float InShadow = (LocalShadowCoord.x >= 0.0 && LocalShadowCoord.x < 1.0 && LocalShadowCoord.y >= 0.0 && LocalShadowCoord.y < 1.0) ? 1 : 0; - float InShadow = ( texture( u_ShadowMap, LocalShadowCoord.xy ) ).r; - InShadow = min(1.0, step(0.0, InShadow - NormalizedCoord) + smoothstep(0.7, 1.0, InShadow)); - float RetVal = InShadow; - - return RetVal; -} - -#ifdef USE_VARIANCE_SHADOWMAP -//----------------------------------------------------------------------------- -float GetInShadowVSM(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - // Variance Shadow Map. - float NormalizedCoord = LocalShadowCoord.z + 0.02; - - vec2 DepthAndDepthSq = texture(u_ShadowMap, LocalShadowCoord.xy).xy; - float Variance = DepthAndDepthSq.y - (DepthAndDepthSq.x * DepthAndDepthSq.x); - Variance = max( Variance, 0.01 ); // Clamp variance to help with self-shadowing - - float edgeFade = smoothstep(0.40, 0.50, max(abs(LocalShadowCoord.x - 0.5), abs(LocalShadowCoord.y - 0.5))); //1 outside 'edge' of shadow map, 0 inside he map area - float Distance = NormalizedCoord - DepthAndDepthSq.x; - if (Distance < 0.0) - return 1.0;//not shadowed - return max(edgeFade, Variance / (Variance + (Distance * Distance))); -} -#endif // USE_VARIANCE_SHADOWMAP - -#ifdef USE_PCF_SAMPLING -//----------------------------------------------------------------------------- -float GetInShadowPCF(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - float RetVal = 0.0; - - // Set up the blur coordinates... - vec4 blurTexCoords[16]; - blurTexCoords[ 0] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 1] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 2] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 3] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 4] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 5] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 6] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 7] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 8] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 9] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 10] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 11] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 12] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 13] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 14] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 15] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - - // ...and then read the values - RetVal += GetInShadow(blurTexCoords[ 0]); - RetVal += GetInShadow(blurTexCoords[ 1]); - RetVal += GetInShadow(blurTexCoords[ 2]); - RetVal += GetInShadow(blurTexCoords[ 3]); - - RetVal += GetInShadow(blurTexCoords[ 4]); - RetVal += GetInShadow(blurTexCoords[ 5]); - RetVal += GetInShadow(blurTexCoords[ 6]); - RetVal += GetInShadow(blurTexCoords[ 7]); - - RetVal += GetInShadow(blurTexCoords[ 8]); - RetVal += GetInShadow(blurTexCoords[ 9]); - RetVal += GetInShadow(blurTexCoords[ 10]); - RetVal += GetInShadow(blurTexCoords[ 11]); - - RetVal += GetInShadow(blurTexCoords[ 12]); - RetVal += GetInShadow(blurTexCoords[ 13]); - RetVal += GetInShadow(blurTexCoords[ 14]); - RetVal += GetInShadow(blurTexCoords[ 15]); - - // Just took 16 samples so divide to get average - RetVal /= 16.0 ; - - return RetVal; -} -#endif // USE_PCF_SAMPLING - -//----------------------------------------------------------------------------- -float GetShadowAmount(vec4 ShadowCoord) -//----------------------------------------------------------------------------- -{ - // Shadow Calculations (textureProj returns 1.0 for pass (not in shadow) and 0.0 for fail (in shadow)) -#if defined(USE_VARIANCE_SHADOWMAP) - float ShadowAmount = GetInShadowVSM(ShadowCoord); -#elif defined(USE_PCF_SAMPLING) - float ShadowAmount = GetInShadowPCF(ShadowCoord); -#else - float ShadowAmount = GetInShadow(ShadowCoord); -#endif - - //float MinShadow = 0.5f; - //ShadowAmount.w = max(ShadowAmount.w, MinShadow); - - ShadowAmount = mix(0.5, 1.0, ShadowAmount); - return ShadowAmount; -} - diff --git a/samples/hdrSwapchain/shaders/Skybox.frag b/samples/hdrSwapchain/shaders/Skybox.frag deleted file mode 100644 index c963e5d..0000000 --- a/samples/hdrSwapchain/shaders/Skybox.frag +++ /dev/null @@ -1,53 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 - -// Texture Locations -#define SHADER_ENVIRONMENT_TEXTURE_LOC 1 - -// Textures -layout(set = 0, binding = SHADER_ENVIRONMENT_TEXTURE_LOC) uniform samplerCube u_EnvironmentTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 6) in vec4 v_VertColor; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Diffuse Color - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = v_VertColor.xyzw; - - // ******************************** - // Skybox Color - // ******************************** - vec3 CubeUV = normalize(v_WorldNorm); - vec3 ReflectMapColor = textureLod(u_EnvironmentTex, CubeUV, 0).rgb; - - // ******************************** - // Final Color - // ******************************** - FragColor = vec4(DiffuseColor.xyz * ReflectMapColor.rgb, DiffuseColor.w); -} - diff --git a/samples/hdr_swapchain/CMakeLists.txt b/samples/hdr_swapchain/CMakeLists.txt new file mode 100644 index 0000000..7019793 --- /dev/null +++ b/samples/hdr_swapchain/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required (VERSION 3.21) + +project (hdr_swapchain C CXX) +set(CMAKE_CXX_STANDARD 20) + +# +# Source files included in this application. +# + +set(CPP_SRC code/main/application.cpp + code/main/application.hpp + code/main/materials.hpp +) +set(FRAMEWORK_LIB framework_vulkan) + +# +# Setup the module path to include the 'project directory' (project/windows or project/android) +# +if(NOT DEFINED PROJECT_ROOT_DIR) + set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) + +# +# Do all the build steps for a Framework application. +# needs Framework_dir and project_name variables. +# +include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + +# +# Copy required models to local folders +# +include(ModelPackager) + +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) + +# +# Convert and copy textures to local folders +# +include(TexturePackager) + +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/hdrSwapchain/code/main/hdrSwapchain.cpp b/samples/hdr_swapchain/code/main/application.cpp similarity index 82% rename from samples/hdrSwapchain/code/main/hdrSwapchain.cpp rename to samples/hdr_swapchain/code/main/application.cpp index cdff196..33a42fb 100644 --- a/samples/hdrSwapchain/code/main/hdrSwapchain.cpp +++ b/samples/hdr_swapchain/code/main/application.cpp @@ -1,23 +1,21 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== -#include "hdrSwapchain.hpp" +#include "application.hpp" #include "camera/cameraController.hpp" #include "camera/cameraControllerTouch.hpp" #include "gui/imguiVulkan.hpp" #include "main/applicationEntrypoint.hpp" -#include "material/computable.hpp" -#include "material/drawable.hpp" -#include "material/material.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/shader.hpp" +#include "material/shaderManagerT.hpp" #include "memory/memoryManager.hpp" #include "memory/vulkan/bufferObject.hpp" #include "memory/vulkan/indexBufferObject.hpp" @@ -28,11 +26,13 @@ #include "vulkan/vulkan.hpp" #include "vulkan/TextureFuncts.h" #include -#include "imgui.h" +#include "imgui/imgui.h" #include #include +VAR(char*, gSceneAssetModel, "SteamPunkSauna.gltf", kVariableNonpersistent); + // Global Variables From Config File bool gRenderShadows = true; bool gRenderHud = true; @@ -41,10 +41,10 @@ bool gAsyncComputeNNAO = true; glm::vec4 gClearColor = glm::vec4(0.3f, 0.3f, 0.3f, 1.0f); -glm::vec3 gCameraStartPos = glm::vec3(7.86955, 13.5954, 0.022805); +glm::vec3 gCameraStartPos = glm::vec3(0.0f, 3.5f, 0.0f); glm::vec3 gCameraStartRot = glm::vec3(0.0f, 0.0f, 0.0f); -glm::vec3 gShadowLightPos = glm::vec3(0.0f, 50.0f, 30.0f); -glm::vec3 gShadowLightTarget = glm::vec3(269.0f, 0.0f, -254.f); +glm::vec3 gShadowLightPos = glm::vec3(111.0f, 420.0f, -423.0f); // sponza etc +glm::vec3 gShadowLightTarget = glm::vec3(269.0f, 0.0f, -254.f); // sponza etc float gFOV = PI_DIV_4; float gNearPlane = 1.0f; @@ -89,7 +89,7 @@ Application::Application() // The Object m_ObjectScale = 1.00f; - m_ObjectWorldPos = glm::vec3(0.0f, -0.5f, 0.0f); + m_ObjectWorldPos = glm::vec3(0.0f, -0.5f, 0.0f); //Sponza etc // The Skybox m_SkyboxScale = 1500.0f; @@ -98,9 +98,6 @@ Application::Application() // Pass Semaphores m_PassCompleteSemaphore.fill(VK_NULL_HANDLE); - // Render passes - m_RenderPass.fill(VK_NULL_HANDLE); - // Compute m_VsmAsyncComputeCanStartSemaphore = VK_NULL_HANDLE; m_VsmAsyncComputeCompleteSemaphore = VK_NULL_HANDLE; @@ -140,10 +137,6 @@ Application::~Application() vkDestroySemaphore(pVulkan->m_VulkanDevice, m_VsmAsyncComputeCanStartSemaphore, NULL); m_VsmAsyncComputeCanStartSemaphore = VK_NULL_HANDLE; - // Textures - ReleaseTexture(*pVulkan, &m_TexWhite); - ReleaseTexture(*pVulkan, &m_DefaultNormal); - ReleaseTexture(*pVulkan, &m_ComputeIntermediateHalfTarget); ReleaseTexture(*pVulkan, &m_ComputeIntermediateHalf2Target); ReleaseTexture(*pVulkan, &m_ComputeIntermediateQuarterTarget); @@ -270,30 +263,30 @@ bool Application::LoadMeshObjects() const auto* pComputeShader = m_ShaderManager->GetShader("VarianceShadowMap"); assert(pComputeShader); - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pComputeShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { + auto material = m_MaterialManager->CreateMaterial(*pComputeShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> MaterialManagerBase::tPerFrameTexInfo { if (texName == "ShadowDepth") - return { &m_Shadows[0].GetDepthTexture(0) }; + return { &m_Shadows[0].GetDepthTexture() }; else if (texName == "VarianceShadowMap") return { &m_VsmTarget }; assert(0); return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { + [this](const std::string& bufferName) -> PerFrameBufferVulkan { return { m_ComputeCtrlUniform.buf.GetVkBuffer() }; }); - m_VsmComputable = std::make_unique(*pVulkan, std::move(material)); - if (!m_VsmComputable->Init()) + auto vsmComputable = std::make_unique(*pVulkan, std::move(material)); + if (!vsmComputable->Init()) { LOGE("Error Creating VSM computable..."); - m_VsmComputable.reset(); } else { - m_VsmComputable->SetDispatchGroupCount(0, { 1, m_VsmTarget.Height,1 }); - m_VsmComputable->SetDispatchGroupCount(1, { m_VsmTarget.Width, 1, 1 }); + vsmComputable->SetDispatchGroupCount(0, { 1, m_VsmTarget.Height,1 }); + vsmComputable->SetDispatchGroupCount(1, { m_VsmTarget.Width, 1, 1 }); } + vsmComputable = std::move(vsmComputable); } } @@ -302,22 +295,25 @@ bool Application::LoadMeshObjects() const auto* pComputeShader = m_ShaderManager->GetShader("NNAO"); assert(pComputeShader); - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pComputeShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { + auto material = m_MaterialManager->CreateMaterial(*pComputeShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> MaterialManagerBase::tPerFrameTexInfo { if (texName == "NNAOout") return { &m_NNAORenderTarget }; if (texName == "NNAOtmp") return { &m_NNAOTempTarget }; else if (texName == "Depth") - return { &m_GBufferRT[0].m_DepthAttachment }; + return { &m_GBufferRT.m_DepthAttachment }; else if (texName == "Normal") - return { &m_GBufferRT[0].m_ColorAttachments[1] }; + return { &m_GBufferRT.m_ColorAttachments[1] }; else { // Assume anything else is a request for Filter texture used by NNAO. - std::string filename = "./Media/Textures/"; - filename.append(texName); + std::string filename = texName; filename.append(".ktx"); - auto* ptexture = m_TextureManager->GetOrLoadTexture(texName, *m_AssetManager, filename, m_SamplerRepeat); + + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const PathManipulator_ChangeExtension changeTextureExt{ ".ktx" }; + + auto* ptexture = m_TextureManager->GetOrLoadTexture(texName, filename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); if (ptexture) return { ptexture }; // File not loaded and not found in already loaded list, since these are Neural Network weights can't just wing it! @@ -325,40 +321,40 @@ bool Application::LoadMeshObjects() return {}; } }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_NNAOCtrlUniform.vkBuffers }; + [this](const std::string& bufferName) -> PerFrameBufferVulkan { + return { m_NNAOCtrlUniform.bufferHandles }; }); - m_NNAOComputable = std::make_unique(*pVulkan, std::move(material)); - if (!m_NNAOComputable->Init()) + auto NNAOComputable = std::make_unique(*pVulkan, std::move(material)); + if (!NNAOComputable->Init()) { LOGE("Error Creating VSM computable..."); - m_NNAOComputable.reset(); } else { - m_NNAOComputable->SetDispatchGroupCount(0, { (m_NNAORenderTarget.Width + 31) / 32, (m_NNAORenderTarget.Height + 31) / 32,1 }); - m_NNAOComputable->SetDispatchGroupCount(1, { (m_NNAORenderTarget.Width + 63) / 64, m_NNAORenderTarget.Height,1 }); - m_NNAOComputable->SetDispatchGroupCount(2, { m_NNAORenderTarget.Width, (m_NNAORenderTarget.Height + 63) / 64,1 }); + NNAOComputable->SetDispatchGroupCount(0, { (m_NNAORenderTarget.Width + 31) / 32, (m_NNAORenderTarget.Height + 31) / 32,1 }); + NNAOComputable->SetDispatchGroupCount(1, { (m_NNAORenderTarget.Width + 63) / 64, m_NNAORenderTarget.Height,1 }); + NNAOComputable->SetDispatchGroupCount(2, { m_NNAORenderTarget.Width, (m_NNAORenderTarget.Height + 63) / 64,1 }); } + NNAOComputable = std::move(NNAOComputable); } LOGI("Creating Light mesh..."); - MeshObject lightMesh; + Mesh lightMesh; MeshHelper::CreateScreenSpaceMesh(GetVulkan()->GetMemoryManager(), 0, &lightMesh); const auto* pLightShader = m_ShaderManager->GetShader("Light"); assert(pLightShader); - auto lightShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pLightShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { + auto lightShaderMaterial = m_MaterialManager->CreateMaterial(*pLightShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> const MaterialManagerBase::tPerFrameTexInfo { if (texName == "Albedo") { - return { &m_GBufferRT[0].m_ColorAttachments[0] }; + return { &m_GBufferRT.m_ColorAttachments[0] }; } else if (texName == "Normal") { - return { &m_GBufferRT[0].m_ColorAttachments[1] }; + return { &m_GBufferRT.m_ColorAttachments[1] }; } else if (texName == "Depth") { - return { &m_GBufferRT[0].m_DepthAttachment }; + return { &m_GBufferRT.m_DepthAttachment }; } else if (texName == "AO") { return { &m_NNAORenderTarget }; @@ -367,19 +363,19 @@ bool Application::LoadMeshObjects() return { &m_VsmTarget }; } else if (texName == "ShadowDepth") { - return { &m_Shadows[0].GetDepthTexture(0) }; + return { &m_Shadows[0].GetDepthTexture() }; } assert(0); return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { + [this](const std::string& bufferName) -> PerFrameBufferVulkan { //BlitFragCB - return { m_LightFragUniform.vkBuffers }; + return { m_LightFragUniform.bufferHandles }; } ); m_LightDrawable = std::make_unique(*pVulkan, std::move(lightShaderMaterial)); - if (!m_LightDrawable->Init( m_RenderPass[RP_LIGHT], sRenderPassNames[RP_LIGHT], std::move(lightMesh))) + if (!m_LightDrawable->Init( m_RenderPass[RP_LIGHT], {}, sRenderPassNames[RP_LIGHT], std::move(lightMesh))) { LOGE("Error Creating Light drawable..."); } @@ -387,33 +383,33 @@ bool Application::LoadMeshObjects() LOGI("Creating Blit mesh..."); //glm::vec4 PosLLRadius = glm::vec4(-1.0f, -1.0f, 2.0f, 2.0f); //glm::vec4 UVLLRadius = glm::vec4(0.0f, 0.0f, 1.0f, 1.0f); - MeshObject blitMesh; + Mesh blitMesh; MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitMesh); const auto* pBlitShader = m_ShaderManager->GetShader("Blit"); assert(pBlitShader); - auto blitShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pBlitShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { + auto blitShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> const MaterialManagerBase::tPerFrameTexInfo { if (texName == "Diffuse") { - return { &m_MainRT[0].m_ColorAttachments[0] }; + return { &m_MainRT.m_ColorAttachments[0] }; } else if (texName == "Bloom") { return { &m_BloomRenderTarget }; } else if (texName == "Overlay") { - return { &m_HudRT[0].m_ColorAttachments[0] }; + return { &m_HudRT.m_ColorAttachments[0] }; } assert(0); return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { + [this](const std::string& bufferName) -> PerFrameBufferVulkan { //BlitFragCB - return { m_BlitFragUniform.vkBuffers }; + return { m_BlitFragUniform.bufferHandles }; } ); m_BlitDrawable = std::make_unique(*pVulkan, std::move(blitShaderMaterial)); - if (!m_BlitDrawable->Init( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], std::move(blitMesh))) + if (!m_BlitDrawable->Init( m_RenderPass[RP_BLIT], {}, sRenderPassNames[RP_BLIT], std::move(blitMesh))) { LOGE("Error Creating Blit drawable..."); } @@ -428,7 +424,6 @@ void Application::Destroy() Vulkan* const pVulkan = GetVulkan(); // Meshes - m_SkyboxDrawable.reset(); m_LightDrawable.reset(); m_BlitDrawable.reset(); m_ComputableTest.reset(); @@ -455,6 +450,10 @@ void Application::Destroy() ReleaseUniformBuffer(pVulkan, m_BlitFragUniform); ReleaseUniformBuffer(pVulkan, m_LightFragUniform); + // Passes + for (auto& pass : m_RenderPass) + pass = {}; + // Finally call into base class destroy FrameworkApplicationBase::Destroy(); } @@ -571,7 +570,7 @@ void Application::Render(float fltDiffTime) if (gRenderHud && m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(CurrentVulkanBuffer.idx, m_HudRT[0].m_FrameBuffer); + guiCommandBuffer = GetGui()->Render(CurrentVulkanBuffer.idx, m_HudRT.m_FrameBuffer); if (guiCommandBuffer != VK_NULL_HANDLE) { BeginRenderPass(RP_HUD); @@ -607,12 +606,16 @@ bool Application::LoadTextures() { Vulkan* const pVulkan = GetVulkan(); + m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory(TEXTURE_DESTINATION_PATH)); + + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + // Load 'loose' textures - m_TextureManager->GetOrLoadTexture("Environment", *m_AssetManager, "./Media/Textures/simplesky_env.ktx", m_SamplerRepeat); - m_TextureManager->GetOrLoadTexture("Irradiance", *m_AssetManager, "./Media/Textures/simplesky_irradiance.ktx", m_SamplerRepeat); + m_TextureManager->GetOrLoadTexture("Environment", "simplesky_env.ktx", m_SamplerRepeat, prefixTextureDir); + m_TextureManager->GetOrLoadTexture("Irradiance", "simplesky_irradiance.ktx", m_SamplerRepeat, prefixTextureDir); - m_TexWhite = LoadKTXTexture(pVulkan, *m_AssetManager, "./Media/Textures/white_d.ktx", SamplerAddressMode::Repeat); - m_DefaultNormal = LoadKTXTexture(pVulkan, *m_AssetManager, "./Media/Textures/normal_default.ktx", SamplerAddressMode::Repeat); + m_TexWhite = static_cast(m_TextureManager->GetOrLoadTexture("white_d.ktx", SamplerAddressMode::Repeat, prefixTextureDir)); + m_DefaultNormal = static_cast(m_TextureManager->GetOrLoadTexture("default_ddn.ktx", SamplerAddressMode::Repeat, prefixTextureDir)); return true; } @@ -631,7 +634,7 @@ bool Application::CreateRenderTargets() // Setup the GBuffer const TextureFormat GbufferColorType[] = { TextureFormat::R8G8B8A8_UNORM/*Albedo*/, TextureFormat::R16G16B16A16_SFLOAT/*Normals*/ }; - if (!m_GBufferRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, GbufferColorType, DesiredDepthFormat, VK_SAMPLE_COUNT_1_BIT, "GBuffer RT")) + if (!m_GBufferRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, GbufferColorType, DesiredDepthFormat, Msaa::Samples1, "GBuffer RT")) { LOGE("Unable to create gbuffer render target"); } @@ -639,7 +642,7 @@ bool Application::CreateRenderTargets() // Setup the 'main' (compositing) buffer const TextureFormat MainColorType[] = { TextureFormat::R16G16B16A16_SFLOAT }; - if (!m_MainRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, m_GBufferRT/*inherit depth*/, VK_SAMPLE_COUNT_1_BIT, "Main RT")) + if (!m_MainRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, DesiredDepthFormat, Msaa::Samples1, "Main RT")) { LOGE("Unable to create main render target"); } @@ -647,7 +650,7 @@ bool Application::CreateRenderTargets() // Setup the HUD render target (no depth) const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_UNORM }; - if (!m_HudRT.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) + if (!m_HudRT.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, Msaa::Samples1, "HUD RT")) { LOGE("Unable to create hud render target"); } @@ -696,16 +699,16 @@ bool Application::LoadShaders() typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "ObjectDeferred", "Media\\Shaders\\ObjectDeferred.json" }, - tIdAndFilename { "ObjectEmissive", "Media\\Shaders\\ObjectEmissive.json" }, - tIdAndFilename { "Skybox", "Media\\Shaders\\Skybox.json" }, - tIdAndFilename { "Light", "Media\\Shaders\\Light.json" }, - tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "VarianceShadowMap", "Media\\Shaders\\VarianceShadowMap.json" }, - tIdAndFilename { "NNAO", "Media\\Shaders\\NNAO.json" } + { tIdAndFilename { "ObjectDeferred", "ObjectDeferred.json" }, + tIdAndFilename { "ObjectEmissive", "ObjectEmissive.json" }, + tIdAndFilename { "Skybox", "Skybox.json" }, + tIdAndFilename { "Light", "Light.json" }, + tIdAndFilename { "Blit", "Blit.json" }, + tIdAndFilename { "VarianceShadowMap", "VarianceShadowMap.json" }, + tIdAndFilename { "NNAO", "NNAO.json" } }) { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); } @@ -744,26 +747,24 @@ bool Application::InitUniforms() m_ComputeCtrl.height = gRenderHeight / 4; CreateUniformBuffer(pVulkan, m_ComputeCtrlUniformQuarter, &m_ComputeCtrl); - // Room lights - m_LightFragUniformData.LightPositions[0] = glm::vec4(-38.0f, 19.0f, -44.f, 200.0f); - m_LightFragUniformData.LightColors[0] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); - m_LightFragUniformData.LightPositions[1] = glm::vec4(-20.0f, 19.0f, -54.f, 200.0f); - m_LightFragUniformData.LightColors[1] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); - m_LightFragUniformData.LightPositions[2] = glm::vec4(20.0f, 30.0f, -40.0f, 200.0f); + // Bar lights + m_LightFragUniformData.LightPositions[0] = glm::vec4(711.0f, 254.0f, -275.0f, 10000.0f); + m_LightFragUniformData.LightColors[0] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); + m_LightFragUniformData.LightPositions[1] = glm::vec4(650.0f, 250.0f, -400.0f, 10000.0f); + m_LightFragUniformData.LightColors[1] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); + m_LightFragUniformData.LightPositions[2] = glm::vec4(875.0f, 250.0f, -190.0f, 10000.0f); m_LightFragUniformData.LightColors[2] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - m_LightFragUniformData.LightPositions[3] = glm::vec4(10.0f, 40.0f, -20.0f, 200.0f); + m_LightFragUniformData.LightPositions[3] = glm::vec4(1025.0f, 250.0f, -100.0f, 10000.0f); m_LightFragUniformData.LightColors[3] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - - // Spot lights - m_LightFragUniformData.LightPositions[4] = glm::vec4(-1.6f, 15.2f, -67.0f, 1000.0f); - m_LightFragUniformData.LightColors[4] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - m_LightFragUniformData.LightPositions[5] = glm::vec4(1.2f, 15.2f, -67.0f, 1000.0f); - m_LightFragUniformData.LightColors[5] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - m_LightFragUniformData.LightPositions[6] = glm::vec4(-17.2f, 13.0f, -48.0f, 1000.0f); - m_LightFragUniformData.LightColors[6] = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - m_LightFragUniformData.LightPositions[7] = glm::vec4(61.f, 30.f, -84.f, 1000.0f); + // Room lights + m_LightFragUniformData.LightPositions[4] = glm::vec4(500.f, 300.f, -50.f, 100000.0f);// 5.0f * 1000, 0.5f * 1000, 3.0f * 1000, 1.0f);// 493.f, 178.0f, -137.6f, 1.0f); + m_LightFragUniformData.LightColors[4] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); + m_LightFragUniformData.LightPositions[5] = glm::vec4(800.f, 300.f, 100.f, 100000.0f);// 5.0f * 1000, 0.5f * 1000, 3.0f * 1000, 1.0f);// 493.f, 178.0f, -137.6f, 1.0f); + m_LightFragUniformData.LightColors[5] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); + m_LightFragUniformData.LightPositions[6] = glm::vec4(426.f, 300.f, -490.f, 100000.0f);// 5.0f * 1000, 0.5f * 1000, 3.0f * 1000, 1.0f);// 493.f, 178.0f, -137.6f, 1.0f); + m_LightFragUniformData.LightColors[6] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); + m_LightFragUniformData.LightPositions[7] = glm::vec4(610.f, 300.f, -846.f, 100000.0f);// 5.0f * 1000, 0.5f * 1000, 3.0f * 1000, 1.0f);// 493.f, 178.0f, -137.6f, 1.0f); m_LightFragUniformData.LightColors[7] = glm::vec4(0.7f, 1.0f, 1.0f, 1.0f); - m_LightFragUniformData.AmbientColor = glm::vec4(0.15f, 0.15f, 0.15f, 1.0f); m_LightFragUniformData.AmbientOcclusionScale = 1.0f; @@ -916,40 +917,40 @@ bool Application::BuildCmdBuffers() for (uint32_t WhichBuffer = 0; WhichBuffer < pVulkan->m_SwapchainImageCount; WhichBuffer++) { // Set up some values that change based on render pass - VkRenderPass WhichRenderPass = VK_NULL_HANDLE; - VkFramebuffer WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; + const RenderPass* WhichRenderPass = nullptr; + VkFramebuffer WhichFramebuffer = pVulkan->GetSwapchainFramebuffer(WhichBuffer).m_FrameBuffer; switch (WhichPass) { case RP_GBUFFER: - WhichRenderPass = m_GBufferRT.m_RenderPass; - WhichFramebuffer = m_GBufferRT[0].m_FrameBuffer; + WhichRenderPass = &m_RenderPass[RP_GBUFFER]; + WhichFramebuffer = m_GBufferRT.m_FrameBuffer; break; case RP_SHADOW: WhichFramebuffer = m_Shadows[0].GetFramebuffer(); - WhichRenderPass = m_Shadows[0].GetRenderPass(); + WhichRenderPass = &m_RenderPass[RP_SHADOW]; break; case RP_LIGHT: - WhichRenderPass = m_MainRT.m_RenderPass; - WhichFramebuffer = m_MainRT[0].m_FrameBuffer; + WhichRenderPass = &m_RenderPass[RP_LIGHT]; + WhichFramebuffer = m_MainRT.m_FrameBuffer; break; case RP_HUD: - WhichRenderPass = m_HudRT.m_RenderPass; - WhichFramebuffer = m_HudRT[0].m_FrameBuffer; + WhichRenderPass = &m_RenderPass[RP_HUD]; + WhichFramebuffer = m_HudRT.m_FrameBuffer; break; case RP_BLIT: - WhichRenderPass = m_RenderPass[WhichPass]; - WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; + WhichRenderPass = &m_RenderPass[WhichPass]; + WhichFramebuffer = pVulkan->GetSwapchainFramebuffer(WhichBuffer).m_FrameBuffer; break; } if (WhichPass == RP_LIGHT) { // Light deferred gbuffer - if (!m_LightCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass)) + if (!m_LightCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, *WhichRenderPass)) { return false; } @@ -960,7 +961,7 @@ bool Application::BuildCmdBuffers() if (WhichPass == RP_BLIT) { // Blit (only in the blit pass) - if (!m_BlitCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass, true/*swapchain renderpass*/)) + if (!m_BlitCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, *WhichRenderPass, true/*swapchain renderpass*/)) { return false; } @@ -970,7 +971,7 @@ bool Application::BuildCmdBuffers() else { // Objects (can render into any pass except Blit) - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Begin(WhichFramebuffer, WhichRenderPass)) + if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Begin(WhichFramebuffer, *WhichRenderPass)) { return false; } @@ -985,11 +986,6 @@ bool Application::BuildCmdBuffers() { AddDrawableToCmdBuffers(drawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, pVulkan->m_SwapchainImageCount); } - // Add the skybox (last) - if (m_SkyboxDrawable) - { - AddDrawableToCmdBuffers(*m_SkyboxDrawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, pVulkan->m_SwapchainImageCount); - } // and end their pass for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) @@ -1167,14 +1163,14 @@ bool Application::InitCommandBuffers() { // The Pass Command Buffer => Primary sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_PassCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) + if (!m_PassCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, CommandList::Type::Primary)) { return false; } // Model => Secondary sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } @@ -1183,62 +1179,62 @@ bool Application::InitCommandBuffers() // Blit => Secondary sprintf(szName, "Blit (%s; Buffer %d of %d)", GetPassName(RP_BLIT), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_BlitCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_BlitCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // Light => Secondary sprintf(szName, "Light (%s; Buffer %d of %d)", GetPassName(RP_BLIT), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_LightCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_LightCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // Variant Shadow Map calculation - compute in the ASYNC COMPUTE queue sprintf(szName, "VSM generation ASYNC (Buffer 1 of 1)"); - if (!m_VsmAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY, computeQueueIndex/*compute*/)) + if (!m_VsmAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Primary, computeQueueIndex/*compute*/)) { return false; } // Variant Shadow Map calculation - compute but in graphics (regular) queue sprintf(szName, "VSM generation (Buffer 1 of 1)"); - if (!m_VsmComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_VsmComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // DiffractionDownsample => Secondary (?) sprintf(szName, "DiffractionDownsample (Buffer 1 of 1)"); - if (!m_DiffractionDownsampleCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_DiffractionDownsampleCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // BloomDownsample => Secondary (?) sprintf(szName, "BloomDownsample (Buffer 1 of 1)"); - if (!m_BloomComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_BloomComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // BloomDownsample => Secondary (?) sprintf(szName, "BloomDownsample ASYNC (Buffer 1 of 1)"); - if (!m_BloomAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY, computeQueueIndex/*compute*/)) + if (!m_BloomAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Primary, computeQueueIndex/*compute*/)) { return false; } // NNAO => Secondary (?) sprintf(szName, "NNAO (Buffer 1 of 1)"); - if (!m_NNAOComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) + if (!m_NNAOComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Secondary)) { return false; } // NNAO => Secondary (?) sprintf(szName, "NNAO ASYNC (Buffer 1 of 1)"); - if (!m_NNAOAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY, computeQueueIndex/*compute*/)) + if (!m_NNAOAsyncComputeCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, CommandList::Type::Primary, computeQueueIndex/*compute*/)) { return false; } @@ -1258,10 +1254,10 @@ bool Application::InitAllRenderPasses() uint32_t ShadowTargetWidth, ShadowTargetHeight; m_Shadows[0].GetTargetSize(ShadowTargetWidth, ShadowTargetHeight); - m_PassSetup[RP_GBUFFER] = { m_GBufferRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, gClearColor,m_GBufferRT[0].m_Width, m_GBufferRT[0].m_Height }; + m_PassSetup[RP_GBUFFER] = { m_GBufferRT.m_pLayerFormats, m_GBufferRT.m_DepthFormat, RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, gClearColor,m_GBufferRT.m_Width, m_GBufferRT.m_Height }; m_PassSetup[RP_SHADOW] = { {}, m_Shadows[0].GetDepthFormat(0), RenderPassInputUsage::DontCare,true, RenderPassOutputUsage::Discard, RenderPassOutputUsage::StoreReadOnly, {}, ShadowTargetWidth, ShadowTargetHeight }; - m_PassSetup[RP_LIGHT] = { m_MainRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_MainRT[0].m_Width, m_MainRT[0].m_Height }; - m_PassSetup[RP_HUD] = { m_HudRT[0].m_pLayerFormats, m_HudRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_HudRT[0].m_Width, m_HudRT[0].m_Height }; + m_PassSetup[RP_LIGHT] = { m_MainRT.m_pLayerFormats, m_GBufferRT.m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_MainRT.m_Width, m_MainRT.m_Height }; + m_PassSetup[RP_HUD] = { m_HudRT.m_pLayerFormats, m_HudRT.m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_HudRT.m_Width, m_HudRT.m_Height }; m_PassSetup[RP_BLIT] = { {pVulkan->m_SurfaceFormat}, pVulkan->m_SwapchainDepth.format, RenderPassInputUsage::DontCare,false, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}, GetVulkan()->m_SurfaceWidth, GetVulkan()->m_SurfaceHeight }; LOGI("******************************"); @@ -1274,24 +1270,28 @@ bool Application::InitAllRenderPasses() if (WhichPass == RP_SHADOW) { - m_RenderPass[WhichPass] = m_Shadows[0].GetRenderPass(); + m_RenderPass[RP_SHADOW] = m_Shadows[0].GetRenderContext().GetRenderPass().Copy(); } else { if (!GetVulkan()->CreateRenderPass({PassSetup.ColorFormats}, PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, PassSetup.ColorInputUsage, PassSetup.ColorOutputUsage, PassSetup.ClearDepthRenderPass, PassSetup.DepthOutputUsage, - &m_RenderPass[WhichPass])) + m_RenderPass[WhichPass])) return false; } // LOGI(" Render Pass (%s; Buffer %d of %d) => 0x%x", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS, m_RenderPass[WhichPass][WhichBuffer]); } // Which Pass + m_GBufferRT.InitializeFrameBuffer(GetVulkan(), m_RenderPass[RP_GBUFFER]); + m_MainRT.InitializeFrameBuffer(GetVulkan(), m_RenderPass[RP_LIGHT]); + m_HudRT.InitializeFrameBuffer(GetVulkan(), m_RenderPass[RP_HUD]); + return true; } @@ -1302,7 +1302,7 @@ bool Application::InitDrawables() { LOGI("Creating Test Drawable..."); - const auto& bufferLoader = [&](const std::string& bufferSlotName) -> tPerFrameVkBuffer { + const auto& bufferLoader = [&](const std::string& bufferSlotName) -> PerFrameBufferVulkan { if (bufferSlotName == "Vert") { return { m_ObjectVertUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; @@ -1340,15 +1340,22 @@ bool Application::InitDrawables() return false; } - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); + std::array renderContexts + { + RenderContext(m_RenderPass[RP_GBUFFER].Copy(), Framebuffer(), sRenderPassNames[RP_GBUFFER]), + RenderContext(m_RenderPass[RP_SHADOW].Copy(), Framebuffer(), sRenderPassNames[RP_SHADOW]), + RenderContext(m_RenderPass[RP_LIGHT].Copy(), Framebuffer(), sRenderPassNames[RP_LIGHT]), + RenderContext(m_RenderPass[RP_HUD].Copy(), Framebuffer(), sRenderPassNames[RP_HUD]), + RenderContext(m_RenderPass[RP_BLIT].Copy(), Framebuffer(), sRenderPassNames[RP_BLIT]) + }; if (1) { - auto bistroTextureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const MaterialPass::tPerFrameTexInfo + auto textureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const MaterialManagerBase::tPerFrameTexInfo { if (textureSlotName == "ShadowDepth") { - return { &m_Shadows[0].GetDepthTexture(0) }; + return { &m_Shadows[0].GetDepthTexture() }; } else if (textureSlotName == "ShadowVarianceDepth") { @@ -1364,19 +1371,49 @@ bool Application::InitDrawables() if (pTexture) return { pTexture }; // File not loaded, use default - return { &m_TexWhite }; + return { m_TexWhite }; } const bool normalMap = (textureSlotName == "Normal"); const bool specMap = !normalMap && (textureSlotName == "SpecMap"); - // See if we can get the filename from the loaded material definition + // See if we can get the filename from the loaded material definition. Take a copy so we can manipulate as needed. std::string textureName = specMap ? materialDef.specMapFilename : (normalMap ? materialDef.bumpFilename : materialDef.diffuseFilename); - auto* pTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, textureName, m_SamplerEdgeClamp); + if (textureName.empty() && normalMap) + { + textureName = materialDef.diffuseFilename; + size_t diff = textureName.find("_diff"); + diff = diff != -1 ? diff : textureName.find("_Diff"); + if (diff != -1) + { + textureName.replace(diff, 5, "_ddna"); + } + else + { + size_t period = textureName.find("."); + if (period != -1) + { + textureName.insert(period, "_ddna"); + } + } + } + + // does not exist - load the new texture. + //std::string filename("./Media/Objects/Bistro/Interior/"); + std::string filename("./Media/Objects/BistroGltfFused/"); + filename.append(textureName); + size_t tga = filename.find(".tga"); + tga = (tga != -1) ? tga : filename.find(".png"); + if (tga != -1) + { + filename.replace(tga, filename.size() - tga, ".ktx"); + } + auto* pTexture = m_TextureManager->GetOrLoadTexture(textureName, filename, m_SamplerRepeat); + if (!pTexture) { // File not loaded, use default - return { (normalMap ? &m_DefaultNormal : &m_TexWhite) }; + return { (normalMap ? m_DefaultNormal : m_TexWhite) }; } else { @@ -1387,49 +1424,29 @@ bool Application::InitDrawables() const auto& bistroMaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef) -> std::optional { using namespace std::placeholders; - if (!materialDef.emissiveFilename.empty()) + //if (materialDef.alphaCutout) + //{ + // return m_MaterialManager->CreateMaterial(*pCutoutShader, std::bind(textureLoader, std::cref(materialDef), _1), bufferLoader); + //} + //else + //if (materialDef.diffuseFilename.find("MASTER_Interior_01_Floor_Tile_Hexagonal_BLENDSHADER") != -1) + if (materialDef.diffuseFilename.find("MASTER_Interior_01_Plaster2_BaseColor") != -1) + { + // This is a roof piece that points upwards and so messes with shadows (remove it) + return {}; + } + else if (!materialDef.emissiveFilename.empty()) { - return m_MaterialManager->CreateMaterial(*GetVulkan(), *pEmissiveShader, NUM_VULKAN_BUFFERS, std::bind(bistroTextureLoader, std::cref(materialDef), _1), bufferLoader); + return m_MaterialManager->CreateMaterial(*pEmissiveShader, NUM_VULKAN_BUFFERS, std::bind(textureLoader, std::cref(materialDef), _1), bufferLoader); } else { - return m_MaterialManager->CreateMaterial(*GetVulkan(), *pOpaqueShader, NUM_VULKAN_BUFFERS, std::bind(bistroTextureLoader, std::cref(materialDef), _1), bufferLoader); + return m_MaterialManager->CreateMaterial(*pOpaqueShader, NUM_VULKAN_BUFFERS, std::bind(textureLoader, std::cref(materialDef), _1), bufferLoader); } }; - DrawableLoader::LoadDrawables( *GetVulkan(), *m_AssetManager, m_RenderPass, sRenderPassNames, "./Media/Meshes/Museum.gltf", bistroMaterialLoader, m_SceneObject, {}, DrawableLoader::LoaderFlags::None, {} ); - } - - { - MeshObject mesh; - if (LoadGLTF("./Media/Objects/Skybox_Separate.gltf", 0, &mesh)) - { - const auto* pSkyboxShader = m_ShaderManager->GetShader("Skybox"); - assert(pSkyboxShader); - auto material = m_MaterialManager->CreateMaterial(*GetVulkan(), *pSkyboxShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - if (texName == "Environment") - { - return { m_TextureManager->GetTexture("Environment") }; - } - else { - assert(0); - return {}; - } - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "Vert") { - return { m_SkyboxVertUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; - } - else { - assert(0); - return {}; - } - }); - uint32_t skyboxRenderPassBits = (1 << RP_LIGHT);// | (1 << RP_REFLECT); - m_SkyboxDrawable = std::make_unique(*GetVulkan(), std::move(material)); - m_SkyboxDrawable->Init(m_RenderPass, sRenderPassNames, skyboxRenderPassBits, std::move(mesh)); - } + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + DrawableLoader::LoadDrawables(*GetVulkan(), *m_AssetManager, renderContexts, sceneAssetPath, bistroMaterialLoader, m_SceneObject, DrawableLoader::LoaderFlags::None, {}); } return true; @@ -1461,9 +1478,9 @@ bool Application::InitGui(uintptr_t windowHandle) //----------------------------------------------------------------------------- { // Gui - assert(m_RenderPass[RP_HUD] != VK_NULL_HANDLE); - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPass[RP_HUD]); - if (!m_Gui->Initialize(windowHandle, m_HudRT[0].m_Width, m_HudRT[0].m_Height)) + assert(m_RenderPass[RP_HUD]); + m_Gui = std::make_unique(*GetGfxApi(), m_RenderPass[RP_HUD].Copy()); + if (!m_Gui->Initialize(windowHandle, m_HudRT.m_pLayerFormats[0], m_HudRT.m_Width, m_HudRT.m_Height)) { return false; } @@ -1601,24 +1618,23 @@ bool Application::ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat) return false; } - vkDestroyRenderPass(GetVulkan()->m_VulkanDevice, m_RenderPass[RP_BLIT], nullptr); - m_RenderPass[RP_BLIT] = VK_NULL_HANDLE; + m_RenderPass[RP_BLIT] = {}; auto& PassSetup = m_PassSetup[RP_BLIT]; PassSetup.ColorFormats = { GetVulkan()->m_SurfaceFormat }; - PassSetup.DepthFormat = { GetVulkan()->m_SwapchainDepth.format }; + PassSetup.DepthFormat = GetVulkan()->m_SwapchainDepth.format; if (!GetVulkan()->CreateRenderPass({ PassSetup.ColorFormats }, PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, PassSetup.ColorInputUsage, PassSetup.ColorOutputUsage, PassSetup.ClearDepthRenderPass, PassSetup.DepthOutputUsage, - &m_RenderPass[RP_BLIT])) + m_RenderPass[RP_BLIT])) return false; - if (!m_BlitDrawable->ReInit( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], nullptr, nullptr)) + if (!m_BlitDrawable->ReInit( RenderContext(m_RenderPass[RP_BLIT].Copy(), Framebuffer(), sRenderPassNames[RP_BLIT]))) { return false; } @@ -1641,7 +1657,7 @@ bool Application::ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat) for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; ++WhichBuffer) { - if (!m_BlitCmdBuffer[WhichBuffer].Begin(GetVulkan()->m_SwapchainBuffers[WhichBuffer].framebuffer, m_RenderPass[RP_BLIT], true/*swapchain renderpass*/)) + if (!m_BlitCmdBuffer[WhichBuffer].Begin(GetVulkan()->GetSwapchainFramebuffer(WhichBuffer).m_FrameBuffer, m_RenderPass[RP_BLIT], true/*swapchain renderpass*/)) { return false; } @@ -1689,19 +1705,19 @@ void Application::BeginRenderPass(RENDER_PASS WhichPass) switch (WhichPass) { case RP_GBUFFER: - Framebuffer = m_GBufferRT[0].m_FrameBuffer; + Framebuffer = m_GBufferRT.m_FrameBuffer; break; case RP_SHADOW: Framebuffer = m_Shadows[0].GetFramebuffer(); break; case RP_LIGHT: - Framebuffer = m_MainRT[0].m_FrameBuffer; + Framebuffer = m_MainRT.m_FrameBuffer; break; case RP_HUD: - Framebuffer = m_HudRT[0].m_FrameBuffer; + Framebuffer = m_HudRT.m_FrameBuffer; break; case RP_BLIT: - Framebuffer = pVulkan->m_SwapchainBuffers[m_CurrentVulkanBuffer].framebuffer; + Framebuffer = pVulkan->GetSwapchainFramebuffer(m_CurrentVulkanBuffer).m_FrameBuffer; break; default: assert(0); diff --git a/samples/hdrSwapchain/code/main/hdrSwapchain.hpp b/samples/hdr_swapchain/code/main/application.hpp similarity index 78% rename from samples/hdrSwapchain/code/main/hdrSwapchain.hpp rename to samples/hdr_swapchain/code/main/application.hpp index 93fcec9..94d477f 100644 --- a/samples/hdrSwapchain/code/main/hdrSwapchain.hpp +++ b/samples/hdr_swapchain/code/main/application.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once /// @@ -31,13 +31,11 @@ #include // Forward declarations -class ShaderManager; -template class MaterialManagerT; -class MaterialPass; -class Material; -class Computable; -class Drawable; -class DrawablePass; +class ShaderManagerBase; +template class MaterialManager; +class MaterialPassBase; +class MaterialBase; +class ComputableBase; class Gui; enum RENDER_PASS @@ -94,7 +92,7 @@ class Application : public ApplicationHelperBase void BeginRenderPass(RENDER_PASS WhichPass); void AddPassCommandBuffers(RENDER_PASS WhichPass); void AddPassCommandBuffers(RENDER_PASS WhichPass, std::span SubCommandBuffers); - void AddPassCommandBuffer(RENDER_PASS WhichPass, Wrap_VkCommandBuffer& SubCommandBuffer) + void AddPassCommandBuffer(RENDER_PASS WhichPass, CommandListVulkan& SubCommandBuffer) { AddPassCommandBuffers(WhichPass, {&SubCommandBuffer.m_VkCommandBuffer, 1}); } @@ -115,19 +113,18 @@ class Application : public ApplicationHelperBase bool m_bEncodeSRGB; // Drawables - std::unique_ptr m_SkyboxDrawable; std::vector m_SceneObject; std::unique_ptr m_LightDrawable; std::unique_ptr m_BlitDrawable; // Computables - std::unique_ptr m_VsmComputable; - std::unique_ptr m_ComputableTest; - std::unique_ptr m_BloomComputable; - std::unique_ptr m_NNAOComputable; + std::unique_ptr m_VsmComputable; + std::unique_ptr m_ComputableTest; + std::unique_ptr m_BloomComputable; + std::unique_ptr m_NNAOComputable; // Textures - TextureVulkan m_TexWhite; - TextureVulkan m_DefaultNormal; + const TextureVulkan* m_TexWhite; + const TextureVulkan* m_DefaultNormal; // Light Stuff glm::vec4 m_LightColor; @@ -147,7 +144,7 @@ class Application : public ApplicationHelperBase UniformT m_ObjectFragUniform[NUM_RENDER_PASSES][NUM_VULKAN_BUFFERS]; ObjectFragUB m_ObjectFragUniformData; - Wrap_VkCommandBuffer m_ObjectCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; + CommandListVulkan m_ObjectCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; // ********************** // The Skybox @@ -161,7 +158,7 @@ class Application : public ApplicationHelperBase // ********************** // Deferred Lighting // ********************** - Wrap_VkCommandBuffer m_LightCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_LightCmdBuffer[NUM_VULKAN_BUFFERS]; struct LightFragCtrl { static const int cNUM_LIGHTS = 8; glm::mat4 ProjectionInv; @@ -192,7 +189,7 @@ class Application : public ApplicationHelperBase int sRGB = 0; // 1 - apply srgb conversion in output blit shader, 0 passthrough color } m_BlitFragUniformData; UniformArrayT m_BlitFragUniform; - Wrap_VkCommandBuffer m_BlitCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_BlitCmdBuffer[NUM_VULKAN_BUFFERS]; // ********************** // Compute @@ -229,13 +226,13 @@ class Application : public ApplicationHelperBase TextureVulkan m_NNAORenderTarget; TextureVulkan m_NNAOTempTarget; - Wrap_VkCommandBuffer m_VsmComputeCmdBuffer[NUM_VULKAN_BUFFERS]; // Command buffer to run VSM compute commands on the (regular) graphics queue - Wrap_VkCommandBuffer m_VsmAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; // Command buffer for VSM on Async Compute queue (needs separate command buffer since command pool is tied to the destination queue) - Wrap_VkCommandBuffer m_DiffractionDownsampleCmdBuffer[NUM_VULKAN_BUFFERS]; - Wrap_VkCommandBuffer m_BloomComputeCmdBuffer[NUM_VULKAN_BUFFERS]; - Wrap_VkCommandBuffer m_BloomAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; - Wrap_VkCommandBuffer m_NNAOComputeCmdBuffer[NUM_VULKAN_BUFFERS]; - Wrap_VkCommandBuffer m_NNAOAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_VsmComputeCmdBuffer[NUM_VULKAN_BUFFERS]; // Command buffer to run VSM compute commands on the (regular) graphics queue + CommandListVulkan m_VsmAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; // Command buffer for VSM on Async Compute queue (needs separate command buffer since command pool is tied to the destination queue) + CommandListVulkan m_DiffractionDownsampleCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_BloomComputeCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_BloomAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_NNAOComputeCmdBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_NNAOAsyncComputeCmdBuffer[NUM_VULKAN_BUFFERS]; // ********************** @@ -255,7 +252,7 @@ class Application : public ApplicationHelperBase } m_PassSetup[NUM_RENDER_PASSES]; - Wrap_VkCommandBuffer m_PassCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; + CommandListVulkan m_PassCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; std::array m_PassCompleteSemaphore; VkSemaphore m_VsmAsyncComputeCanStartSemaphore; VkSemaphore m_VsmAsyncComputeCompleteSemaphore; @@ -265,11 +262,11 @@ class Application : public ApplicationHelperBase VkSemaphore m_NNAOAsyncComputeCompleteSemaphore; // Don't actually need a render pass for each Vulkan Buffer, just one per pass - // These are NOT the same vkrenderpass's as in the CRenderTargetArray's but should be compatible with them (are allowed dfferent load/clear parameters on the atachments) - std::array m_RenderPass; + // These are NOT the same vkrenderpass's as in the RenderTargetArray's but should be compatible with them (are allowed dfferent load/clear parameters on the atachments) + std::array m_RenderPass; // Render targets for each pass - CRenderTargetArray<1> m_GBufferRT; - CRenderTargetArray<1> m_MainRT; - CRenderTargetArray<1> m_HudRT; + RenderTarget m_GBufferRT; + RenderTarget m_MainRT; + RenderTarget m_HudRT; }; diff --git a/samples/hdrSwapchain/code/main/materials.hpp b/samples/hdr_swapchain/code/main/materials.hpp similarity index 91% rename from samples/hdrSwapchain/code/main/materials.hpp rename to samples/hdr_swapchain/code/main/materials.hpp index c52c1b7..e6d9178 100644 --- a/samples/hdrSwapchain/code/main/materials.hpp +++ b/samples/hdr_swapchain/code/main/materials.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once #include "vulkan/vulkan.hpp" diff --git a/samples/hdr_swapchain/install_apk.bat b/samples/hdr_swapchain/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/hdr_swapchain/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/hdr_swapchain/install_config.bat b/samples/hdr_swapchain/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/hdr_swapchain/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/rotatedCopy/project/android/AndroidManifest.xml b/samples/hdr_swapchain/project/android/AndroidManifest.xml similarity index 97% rename from samples/rotatedCopy/project/android/AndroidManifest.xml rename to samples/hdr_swapchain/project/android/AndroidManifest.xml index 13133ac..bc9a085 100644 --- a/samples/rotatedCopy/project/android/AndroidManifest.xml +++ b/samples/hdr_swapchain/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -21,7 +20,7 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="landscape" - android:theme="@style/Theme.NoTitleBar.NoActionBar.Fullscreen"> + android:theme="@style/Theme.NoTitleBar.NoActionBar.Fullscreen" android:exported="true"> + + SGS HDR Swapchain + diff --git a/samples/hdrSwapchain/project/android/res/values/styles.xml b/samples/hdr_swapchain/project/android/res/values/styles.xml similarity index 100% rename from samples/hdrSwapchain/project/android/res/values/styles.xml rename to samples/hdr_swapchain/project/android/res/values/styles.xml diff --git a/samples/hdrSwapchain/shaders/Blit.frag b/samples/hdr_swapchain/shaders/Blit.frag similarity index 94% rename from samples/hdrSwapchain/shaders/Blit.frag rename to samples/hdr_swapchain/shaders/Blit.frag index 81b9a9d..7005436 100644 --- a/samples/hdrSwapchain/shaders/Blit.frag +++ b/samples/hdr_swapchain/shaders/Blit.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== // Blit.frag diff --git a/samples/hdrSwapchain/shaders/Blit.json b/samples/hdr_swapchain/shaders/Blit.json similarity index 100% rename from samples/hdrSwapchain/shaders/Blit.json rename to samples/hdr_swapchain/shaders/Blit.json diff --git a/samples/rayQueryShadows/shaders/Blit.vert b/samples/hdr_swapchain/shaders/Blit.vert similarity index 52% rename from samples/rayQueryShadows/shaders/Blit.vert rename to samples/hdr_swapchain/shaders/Blit.vert index cdd4f93..59fe126 100644 --- a/samples/rayQueryShadows/shaders/Blit.vert +++ b/samples/hdr_swapchain/shaders/Blit.vert @@ -1,23 +1,32 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== + +// Blit.vert #version 400 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable #define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_TEXCOORD0 1 +#define SHADER_ATTRIB_LOC_NORMAL 1 +#define SHADER_ATTRIB_LOC_TEXCOORD0 2 +#define SHADER_ATTRIB_LOC_COLOR 3 +#define SHADER_ATTRIB_LOC_TANGENT 4 layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; +layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; +layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; +layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Varying's layout (location = 0) out vec2 v_TexCoord; +layout (location = 1) out vec4 v_VertColor; void main() { @@ -25,4 +34,7 @@ void main() vec4 TempPos = vec4(a_Position.xyz, 1.0); gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); v_TexCoord = vec2(a_TexCoord.xy); + + // Color is simple attribute color + v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); } diff --git a/samples/hdrSwapchain/shaders/Light.frag b/samples/hdr_swapchain/shaders/Light.frag similarity index 96% rename from samples/hdrSwapchain/shaders/Light.frag rename to samples/hdr_swapchain/shaders/Light.frag index bda76bb..e00aaec 100644 --- a/samples/hdrSwapchain/shaders/Light.frag +++ b/samples/hdr_swapchain/shaders/Light.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== // Blit.frag diff --git a/samples/hdrSwapchain/shaders/Light.json b/samples/hdr_swapchain/shaders/Light.json similarity index 100% rename from samples/hdrSwapchain/shaders/Light.json rename to samples/hdr_swapchain/shaders/Light.json diff --git a/samples/rayReflections/shaders/Light.vert b/samples/hdr_swapchain/shaders/Light.vert similarity index 81% rename from samples/rayReflections/shaders/Light.vert rename to samples/hdr_swapchain/shaders/Light.vert index a60bb2d..2efac1a 100644 --- a/samples/rayReflections/shaders/Light.vert +++ b/samples/hdr_swapchain/shaders/Light.vert @@ -1,10 +1,12 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== + +// Blit.vert #version 400 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/NNAO.comp b/samples/hdr_swapchain/shaders/NNAO.comp similarity index 96% rename from samples/hdrSwapchain/shaders/NNAO.comp rename to samples/hdr_swapchain/shaders/NNAO.comp index effed88..a04326c 100644 --- a/samples/hdrSwapchain/shaders/NNAO.comp +++ b/samples/hdr_swapchain/shaders/NNAO.comp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 450 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/NNAO.json b/samples/hdr_swapchain/shaders/NNAO.json similarity index 100% rename from samples/hdrSwapchain/shaders/NNAO.json rename to samples/hdr_swapchain/shaders/NNAO.json diff --git a/samples/hdrSwapchain/shaders/NNAOFilter_horiz.comp b/samples/hdr_swapchain/shaders/NNAOFilter_horiz.comp similarity index 93% rename from samples/hdrSwapchain/shaders/NNAOFilter_horiz.comp rename to samples/hdr_swapchain/shaders/NNAOFilter_horiz.comp index e3e51d0..39856df 100644 --- a/samples/hdrSwapchain/shaders/NNAOFilter_horiz.comp +++ b/samples/hdr_swapchain/shaders/NNAOFilter_horiz.comp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 450 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/NNAOFilter_vertical.comp b/samples/hdr_swapchain/shaders/NNAOFilter_vertical.comp similarity index 93% rename from samples/hdrSwapchain/shaders/NNAOFilter_vertical.comp rename to samples/hdr_swapchain/shaders/NNAOFilter_vertical.comp index 030a60a..a1f15ad 100644 --- a/samples/hdrSwapchain/shaders/NNAOFilter_vertical.comp +++ b/samples/hdr_swapchain/shaders/NNAOFilter_vertical.comp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 450 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/ObjectDeferred.frag b/samples/hdr_swapchain/shaders/ObjectDeferred.frag similarity index 93% rename from samples/hdrSwapchain/shaders/ObjectDeferred.frag rename to samples/hdr_swapchain/shaders/ObjectDeferred.frag index 083bc2a..405470d 100644 --- a/samples/hdrSwapchain/shaders/ObjectDeferred.frag +++ b/samples/hdr_swapchain/shaders/ObjectDeferred.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 diff --git a/samples/hdrSwapchain/shaders/ObjectDeferred.json b/samples/hdr_swapchain/shaders/ObjectDeferred.json similarity index 97% rename from samples/hdrSwapchain/shaders/ObjectDeferred.json rename to samples/hdr_swapchain/shaders/ObjectDeferred.json index 57bc895..ee62b12 100644 --- a/samples/hdrSwapchain/shaders/ObjectDeferred.json +++ b/samples/hdr_swapchain/shaders/ObjectDeferred.json @@ -1,5 +1,5 @@ { - "$schema": "shaderSchema.json", + "$schema": "../../../framework/schema/shaderSchema.json", "Passes": [ { "Name": "RP_GBUFFER", diff --git a/samples/hdrSwapchain/shaders/ObjectDeferred.vert b/samples/hdr_swapchain/shaders/ObjectDeferred.vert similarity index 91% rename from samples/hdrSwapchain/shaders/ObjectDeferred.vert rename to samples/hdr_swapchain/shaders/ObjectDeferred.vert index bf5be30..aa6ad99 100644 --- a/samples/hdrSwapchain/shaders/ObjectDeferred.vert +++ b/samples/hdr_swapchain/shaders/ObjectDeferred.vert @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/ObjectEmissive.frag b/samples/hdr_swapchain/shaders/ObjectEmissive.frag similarity index 90% rename from samples/hdrSwapchain/shaders/ObjectEmissive.frag rename to samples/hdr_swapchain/shaders/ObjectEmissive.frag index c684cb1..6a7acee 100644 --- a/samples/hdrSwapchain/shaders/ObjectEmissive.frag +++ b/samples/hdr_swapchain/shaders/ObjectEmissive.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 diff --git a/samples/hdrSwapchain/shaders/ObjectEmissive.json b/samples/hdr_swapchain/shaders/ObjectEmissive.json similarity index 96% rename from samples/hdrSwapchain/shaders/ObjectEmissive.json rename to samples/hdr_swapchain/shaders/ObjectEmissive.json index 138211f..852448d 100644 --- a/samples/hdrSwapchain/shaders/ObjectEmissive.json +++ b/samples/hdr_swapchain/shaders/ObjectEmissive.json @@ -1,5 +1,5 @@ { - "$schema": "shaderSchema.json", + "$schema": "../../../framework/schema/shaderSchema.json", "Passes": [ { "Name": "RP_LIGHT", diff --git a/samples/hdrSwapchain/shaders/ShadowGen.vert b/samples/hdr_swapchain/shaders/ShadowGen.vert similarity index 80% rename from samples/hdrSwapchain/shaders/ShadowGen.vert rename to samples/hdr_swapchain/shaders/ShadowGen.vert index 24921c0..35a0262 100644 --- a/samples/hdrSwapchain/shaders/ShadowGen.vert +++ b/samples/hdr_swapchain/shaders/ShadowGen.vert @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/rayReflections/shaders/ShadowShared.h b/samples/hdr_swapchain/shaders/ShadowShared.h similarity index 93% rename from samples/rayReflections/shaders/ShadowShared.h rename to samples/hdr_swapchain/shaders/ShadowShared.h index 0150d45..30f625a 100644 --- a/samples/rayReflections/shaders/ShadowShared.h +++ b/samples/hdr_swapchain/shaders/ShadowShared.h @@ -1,10 +1,8 @@ -//============================================================================================================ -// +// ShadowShared.h + // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Shared Implementation of Shadow mapping code. // -//============================================================================================================ //----------------------------------------------------------------------------- float GetInShadow(vec4 LocalShadowCoord) diff --git a/samples/rayQueryShadows/shaders/Skybox.frag b/samples/hdr_swapchain/shaders/Skybox.frag similarity index 85% rename from samples/rayQueryShadows/shaders/Skybox.frag rename to samples/hdr_swapchain/shaders/Skybox.frag index c963e5d..c8d4708 100644 --- a/samples/rayQueryShadows/shaders/Skybox.frag +++ b/samples/hdr_swapchain/shaders/Skybox.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 diff --git a/samples/hdrSwapchain/shaders/Skybox.json b/samples/hdr_swapchain/shaders/Skybox.json similarity index 100% rename from samples/hdrSwapchain/shaders/Skybox.json rename to samples/hdr_swapchain/shaders/Skybox.json diff --git a/samples/hdrSwapchain/shaders/Skybox.vert b/samples/hdr_swapchain/shaders/Skybox.vert similarity index 88% rename from samples/hdrSwapchain/shaders/Skybox.vert rename to samples/hdr_swapchain/shaders/Skybox.vert index f68539d..a3e21db 100644 --- a/samples/hdrSwapchain/shaders/Skybox.vert +++ b/samples/hdr_swapchain/shaders/Skybox.vert @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/VarianceShadowMap.json b/samples/hdr_swapchain/shaders/VarianceShadowMap.json similarity index 93% rename from samples/hdrSwapchain/shaders/VarianceShadowMap.json rename to samples/hdr_swapchain/shaders/VarianceShadowMap.json index a46ad0e..5bef425 100644 --- a/samples/hdrSwapchain/shaders/VarianceShadowMap.json +++ b/samples/hdr_swapchain/shaders/VarianceShadowMap.json @@ -1,5 +1,5 @@ { - "$schema": "shaderSchema.json", + "$schema": "../../../framework/schema/shaderSchema.json", "Passes": [ { "Name": "vsm_horizontal", diff --git a/samples/hdrSwapchain/shaders/VarianceShadowMap1024_horizontal.comp b/samples/hdr_swapchain/shaders/VarianceShadowMap1024_horizontal.comp similarity index 91% rename from samples/hdrSwapchain/shaders/VarianceShadowMap1024_horizontal.comp rename to samples/hdr_swapchain/shaders/VarianceShadowMap1024_horizontal.comp index 5969284..886ac2f 100644 --- a/samples/hdrSwapchain/shaders/VarianceShadowMap1024_horizontal.comp +++ b/samples/hdr_swapchain/shaders/VarianceShadowMap1024_horizontal.comp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 450 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hdrSwapchain/shaders/VarianceShadowMap1024_vertical.comp b/samples/hdr_swapchain/shaders/VarianceShadowMap1024_vertical.comp similarity index 90% rename from samples/hdrSwapchain/shaders/VarianceShadowMap1024_vertical.comp rename to samples/hdr_swapchain/shaders/VarianceShadowMap1024_vertical.comp index 453a85a..d6e2b91 100644 --- a/samples/hdrSwapchain/shaders/VarianceShadowMap1024_vertical.comp +++ b/samples/hdr_swapchain/shaders/VarianceShadowMap1024_vertical.comp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 450 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/hello-gltf/01_CompileShaders.bat b/samples/hello-gltf/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/hello-gltf/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/hello-gltf/02_Install_APK.bat b/samples/hello-gltf/02_Install_APK.bat deleted file mode 100644 index e189963..0000000 --- a/samples/hello-gltf/02_Install_APK.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\hello-gltf\outputs\apk\debug\hello-gltf-debug.apk -@echo **************************************** -call adb install -r -t ..\..\build\android\hello-gltf\outputs\apk\debug\hello-gltf-debug.apk -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/hello-gltf/03_Adb_Logcat_HelloGLTF.bat b/samples/hello-gltf/03_Adb_Logcat_HelloGLTF.bat deleted file mode 100644 index 21936e2..0000000 --- a/samples/hello-gltf/03_Adb_Logcat_HelloGLTF.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat (grep "hellogltf")... -call adb logcat -c -call adb logcat | FIND /I "hellogltf" - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause diff --git a/samples/hello-gltf/README.md b/samples/hello-gltf/README.md deleted file mode 100644 index 9b1226c..0000000 --- a/samples/hello-gltf/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Hello-GLTF Sample - -![Screenshot](img/screenshot.png) - -## Overview - -Hello GLTF sample demonstrates the most basic usage of the Framework to produce a native Vulkan application and it is designed to be small and simple and meant as a starting point for developers to expand its functionality. - -It is recommended that this sample is used as a starting point for other applications using this Framework. To do so, this folder can be copied to a desired location and the respective changes to point the source and include files to the Framework `src` and `include` folders are addressed. For simplicity, the folder of the new sample can be created alongside the `hello-gltf` sample within the `samples` folder. This way, no additional modifications are required in any configuration file to build it out of the box. - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - -And convert the needed textures and shaders to the correct format using the batch file below: - -``` -02_PrepareMedia.bat -``` - -Note: The sample assumes there are user provided asset files at the following path: **'Media\Meshes\Museum.gltf'** and **'Media\Meshes\Museum.bin'**. -Texture dependencies from this asset should be added to **'Media\Textures\'** and are required to have the *.ktx* extension. -There are 3 extra require supporting textures that should also go to the same texture path listed above: **white_d.ktx**, **black_d.ktx** and **normal_default.ktx**. -The framework team is working to build a centralized asset repository that should minimize these requirements in the near future. - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_CopyMediaToDevice.bat -03_Install_APK.bat -``` - -If desired, you can keep track of any logging by running one of the logcat batch files (which you can find on the current directory). - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. diff --git a/samples/hello-gltf/build.gradle b/samples/hello-gltf/build.gradle deleted file mode 100644 index 6c6e1ea..0000000 --- a/samples/hello-gltf/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.hellogltf" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/hello-gltf/code/main/application.cpp b/samples/hello-gltf/code/main/application.cpp deleted file mode 100644 index bc6da8c..0000000 --- a/samples/hello-gltf/code/main/application.cpp +++ /dev/null @@ -1,987 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -/// -/// Sample app demonstrating the loading of a .gltf file (hello world) -/// - -#include "application.hpp" -#include "main/applicationEntrypoint.hpp" -#include "camera/cameraController.hpp" -#include "camera/cameraControllerTouch.hpp" -#include "camera/cameraData.hpp" -#include "camera/cameraGltfLoader.hpp" -#include "gui/imguiVulkan.hpp" -#include "material/drawable.hpp" -#include "material/vulkan/shaderModule.hpp" -#include "material/shaderManagerT.hpp" -#include "material/materialManager.hpp" -#include "material/vulkan/specializationConstantsLayout.hpp" -#include "mesh/meshHelper.hpp" -#include "mesh/meshLoader.hpp" -#include "system/math_common.hpp" -#include "texture/textureManager.hpp" -#include "imgui.h" - -#include -#include -#include - -namespace -{ - static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_HUD", "RP_BLIT" }; - - glm::vec3 gCameraStartPos = glm::vec3(26.48f, 20.0f, -5.21f); - glm::vec3 gCameraStartRot = glm::vec3(0.0f, 110.0f, 0.0f); - - float gFOV = PI_DIV_4; - float gNearPlane = 1.0f; - float gFarPlane = 1800.0f; - float gNormalAmount = 0.3f; - float gNormalMirrorReflectAmount = 0.05f; - - const char* gMuseumAssetPath = "Media\\Meshes\\Museum.gltf"; - const char* gTextureFolder = "Media\\Textures\\"; -} - -/// -/// @brief Implementation of the Application entrypoint (called by the framework) -/// @return Pointer to Application (derived from @FrameworkApplicationBase). -/// Creates the Application class. Ownership is passed to the calling (framework) function. -/// -FrameworkApplicationBase* Application_ConstructApplication() -{ - return new Application(); -} - -Application::Application() : ApplicationHelperBase() -{ -} - -Application::~Application() -{ -} - -//----------------------------------------------------------------------------- -bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) -//----------------------------------------------------------------------------- -{ - if (!ApplicationHelperBase::Initialize( windowHandle, hInstance )) - { - return false; - } - - if (!InitializeCamera()) - { - return false; - } - - if (!InitializeLights()) - { - return false; - } - - if (!LoadShaders()) - { - return false; - } - - if (!InitUniforms()) - { - return false; - } - - if (!CreateRenderTargets()) - { - return false; - } - - if (!InitAllRenderPasses()) - { - return false; - } - - if (!InitGui(windowHandle)) - { - return false; - } - - if (!LoadMeshObjects()) - { - return false; - } - - if (!InitCommandBuffers()) - { - return false; - } - - if (!InitLocalSemaphores()) - { - return false; - } - - if (!BuildCmdBuffers()) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -void Application::Destroy() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - // Uniform Buffers - ReleaseUniformBuffer(pVulkan, &m_ObjectVertUniform); - ReleaseUniformBuffer(pVulkan, &m_LightUniform); - - for (auto& [hash, objectUniform] : m_ObjectFragUniforms) - { - ReleaseUniformBuffer(pVulkan, &objectUniform.objectFragUniform); - } - - // Cmd buffers - for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - for (auto& cmdBuffer : m_RenderPassData[whichPass].PassCmdBuffer) - { - cmdBuffer.Release(); - } - - for (auto& cmdBuffer : m_RenderPassData[whichPass].ObjectsCmdBuffer) - { - cmdBuffer.Release(); - } - - m_RenderPassData[whichPass].RenderTarget.Release(); - } - - // Render passes / Semaphores - for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].RenderPass, nullptr); - vkDestroySemaphore(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].PassCompleteSemaphore, nullptr); - } - - // Drawables - m_SceneDrawables.clear(); - m_BlitQuadDrawable.reset(); - - // Internal - m_ShaderManager.reset(); - m_MaterialManager.reset(); - m_CameraController.reset(); - m_AssetManager.reset(); - - ApplicationHelperBase::Destroy(); -} - -//----------------------------------------------------------------------------- -bool Application::InitializeLights() -//----------------------------------------------------------------------------- -{ - m_LightUniformData.SpotLights_pos[0] = glm::vec4(-6.900000f, 32.299999f, -1.900000f, 1.0f); - m_LightUniformData.SpotLights_pos[1] = glm::vec4(3.300000f, 26.900000f, 7.600000f, 1.0f); - m_LightUniformData.SpotLights_pos[2] = glm::vec4(12.100000f, 41.400002f, -2.800000f, 1.0f); - m_LightUniformData.SpotLights_pos[3] = glm::vec4(-5.400000f, 18.500000f, 28.500000f, 1.0f); - - m_LightUniformData.SpotLights_dir[0] = glm::vec4(-0.534696f, -0.834525f, 0.132924f, 0.0f); - m_LightUniformData.SpotLights_dir[1] = glm::vec4(0.000692f, -0.197335f, 0.980336f, 0.0f); - m_LightUniformData.SpotLights_dir[2] = glm::vec4(0.985090f, -0.172016f, 0.003000f, 0.0f); - m_LightUniformData.SpotLights_dir[3] = glm::vec4(0.674125f, -0.295055f, -0.677125f, 0.0f); - - m_LightUniformData.SpotLights_color[0] = glm::vec4(1.000000f, 1.000000f, 1.000000f, 3.000000f); - m_LightUniformData.SpotLights_color[1] = glm::vec4(1.000000f, 1.000000f, 1.000000f, 3.500000f); - m_LightUniformData.SpotLights_color[2] = glm::vec4(1.000000f, 1.000000f, 1.000000f, 2.000000f); - m_LightUniformData.SpotLights_color[3] = glm::vec4(1.000000f, 1.000000f, 1.000000f, 2.800000f); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitializeCamera() -//----------------------------------------------------------------------------- -{ - LOGI("******************************"); - LOGI("Initializing Camera..."); - LOGI("******************************"); - - m_Camera.SetPosition(gCameraStartPos, glm::quat(gCameraStartRot * TO_RADIANS)); - m_Camera.SetAspect(float(gRenderWidth) / float(gRenderHeight)); - m_Camera.SetFov(gFOV); - m_Camera.SetClipPlanes(gNearPlane, gFarPlane); - - // Camera Controller // - -#if defined(OS_ANDROID) - typedef CameraControllerTouch tCameraController; -#else - typedef CameraController tCameraController; -#endif - - auto cameraController = std::make_unique(); - if (!cameraController->Initialize(gRenderWidth, gRenderHeight)) - { - return false; - } - - m_CameraController = std::move(cameraController); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadShaders() -//----------------------------------------------------------------------------- -{ - m_ShaderManager = std::make_unique>(*GetVulkan()); - m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); - - m_MaterialManager = std::make_unique>(); - - LOGI("******************************"); - LOGI("Loading Shaders..."); - LOGI("******************************"); - - typedef std::pair tIdAndFilename; - for (const tIdAndFilename& i : - { tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "SceneOpaque", "Media\\Shaders\\SceneOpaque.json" }, - tIdAndFilename { "SceneTransparent", "Media\\Shaders\\SceneTransparent.json" } - }) - { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) - { - LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); - LOGI("Please verify if you have all required assets on the sample media folder"); - LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); - return false; - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::CreateRenderTargets() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - LOGI("**************************"); - LOGI("Creating Render Targets..."); - LOGI("**************************"); - - TextureFormat vkDesiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); - TextureFormat desiredDepthFormat = vkDesiredDepthFormat; - - const TextureFormat MainColorType[] = { TextureFormat::R8G8B8A8_SRGB }; - const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_SRGB }; - - if (!m_RenderPassData[RP_SCENE].RenderTarget.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, desiredDepthFormat, VK_SAMPLE_COUNT_1_BIT, "Scene RT")) - { - LOGE("Unable to create scene render target"); - return false; - } - - // Notice no depth on the HUD RT - if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) - { - LOGE("Unable to create hud render target"); - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitUniforms() -//----------------------------------------------------------------------------- -{ - LOGI("******************************"); - LOGI("Initializing Uniforms..."); - LOGI("******************************"); - - Vulkan* const pVulkan = GetVulkan(); - - if (!CreateUniformBuffer(pVulkan, m_ObjectVertUniform)) - { - return false; - } - - if (!CreateUniformBuffer(pVulkan, m_LightUniform)) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitAllRenderPasses() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - // ColorInputUsage | ClearDepthRenderPass | ColorOutputUsage | DepthOutputUsage | ClearColor - m_RenderPassData[RP_SCENE].PassSetup = { RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, {}}; - m_RenderPassData[RP_HUD].PassSetup = { RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; - m_RenderPassData[RP_BLIT].PassSetup = { RenderPassInputUsage::DontCare, true, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}}; - - TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; - auto swapChainColorFormat = std::span({ &surfaceFormat, 1 }); - auto swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; - - LOGI("******************************"); - LOGI("Initializing Render Passes... "); - LOGI("******************************"); - - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - bool isSwapChainRenderPass = whichPass == RP_BLIT; - - std::span colorFormats = isSwapChainRenderPass ? swapChainColorFormat : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = isSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; - - const auto& passSetup = m_RenderPassData[whichPass].PassSetup; - - if (!pVulkan->CreateRenderPass( - { colorFormats }, - depthFormat, - VK_SAMPLE_COUNT_1_BIT, - passSetup.ColorInputUsage, - passSetup.ColorOutputUsage, - passSetup.ClearDepthRenderPass, - passSetup.DepthOutputUsage, - & m_RenderPassData[whichPass].RenderPass)) - { - return false; - } - - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitGui(uintptr_t windowHandle) -//----------------------------------------------------------------------------- -{ - const auto& hudRenderTarget = m_RenderPassData[RP_HUD].RenderTarget; - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPassData[RP_HUD].RenderPass); - if (!m_Gui->Initialize(windowHandle, hudRenderTarget[0].m_Width, hudRenderTarget[0].m_Height)) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadMeshObjects() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - LOGI("***********************"); - LOGI("Initializing Meshes... "); - LOGI("***********************"); - - const auto* pSceneOpaqueShader = m_ShaderManager->GetShader("SceneOpaque"); - const auto* pSceneTransparentShader = m_ShaderManager->GetShader("SceneTransparent"); - const auto* pBlitQuadShader = m_ShaderManager->GetShader("Blit"); - if (!pSceneOpaqueShader || !pSceneTransparentShader || !pBlitQuadShader) - { - return false; - } - - LOGI("***********************************"); - LOGI("Loading and preparing the museum..."); - LOGI("***********************************"); - - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - - auto* whiteTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\white_d.ktx", m_SamplerEdgeClamp); - auto* blackTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\black_d.ktx", m_SamplerEdgeClamp); - auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\normal_default.ktx", m_SamplerEdgeClamp); - - if (!whiteTexture || !blackTexture || !normalDefaultTexture) - { - LOGE("Failed to load supporting textures"); - return false; - } - - auto UniformBufferLoader = [&](const ObjectMaterialParameters& objectMaterialParameters) -> ObjectMaterialParameters& - { - auto hash = objectMaterialParameters.GetHash(); - - auto iter = m_ObjectFragUniforms.try_emplace(hash, ObjectMaterialParameters()); - if (iter.second) - { - iter.first->second.objectFragUniformData = objectMaterialParameters.objectFragUniformData; - if (!CreateUniformBuffer(pVulkan, iter.first->second.objectFragUniform)) - { - LOGE("Failed to create object uniform buffer"); - } - } - - return iter.first->second; - }; - - auto MaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef)->std::optional - { - auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerEdgeClamp); - auto* normalTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.bumpFilename, m_SamplerEdgeClamp); - auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.emissiveFilename, m_SamplerEdgeClamp); - auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.specMapFilename, m_SamplerEdgeClamp); - bool alphaCutout = materialDef.alphaCutout; - bool transparent = materialDef.transparent; - - const Shader* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; - - ObjectMaterialParameters objectMaterial; - objectMaterial.objectFragUniformData.Color.r = static_cast(materialDef.baseColorFactor[0]); - objectMaterial.objectFragUniformData.Color.g = static_cast(materialDef.baseColorFactor[1]); - objectMaterial.objectFragUniformData.Color.b = static_cast(materialDef.baseColorFactor[2]); - objectMaterial.objectFragUniformData.Color.a = static_cast(materialDef.baseColorFactor[3]); - objectMaterial.objectFragUniformData.ORM.b = static_cast(materialDef.metallicFactor); - objectMaterial.objectFragUniformData.ORM.g = static_cast(materialDef.roughnessFactor); - - if (diffuseTexture == nullptr || normalTexture == nullptr) - { - return std::nullopt; - } - - auto shaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *targetShader, NUM_VULKAN_BUFFERS, - [&](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo - { - if (texName == "Diffuse") - { - return { diffuseTexture ? diffuseTexture : whiteTexture }; - } - if (texName == "Normal") - { - return { normalTexture ? normalTexture : normalDefaultTexture }; - } - if (texName == "Emissive") - { - return { emissiveTexture ? emissiveTexture : blackTexture }; - } - if (texName == "MetallicRoughness") - { - return { metallicRoughnessTexture ? metallicRoughnessTexture : blackTexture }; - } - - return {}; - }, - [&](const std::string& bufferName) -> tPerFrameVkBuffer - { - if (bufferName == "Vert") - { - return { m_ObjectVertUniform.buf.GetVkBuffer() }; - } - else if (bufferName == "Frag") - { - return { UniformBufferLoader(objectMaterial).objectFragUniform.buf.GetVkBuffer() }; - } - else if (bufferName == "Light") - { - return { m_LightUniform.buf.GetVkBuffer() }; - } - - return {}; - } - ); - - return shaderMaterial; - }; - - - const auto loaderFlags = 0; // No instancing - const bool ignoreTransforms = (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0; - - MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(gMuseumAssetPath); - MeshObjectIntermediateGltfProcessor meshObjectProcessor(gMuseumAssetPath, ignoreTransforms, glm::vec3(1.0f,1.0f,1.0f)); - CameraGltfProcessor meshCameraProcessor{}; - - if (!MeshLoader::LoadGltf(*m_AssetManager, gMuseumAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || - !DrawableLoader::CreateDrawables(*pVulkan, - std::move(meshObjectProcessor.m_meshObjects), - { &m_RenderPassData[RP_SCENE].RenderPass, 1 }, - &sRenderPassNames[RP_SCENE], - MaterialLoader, - m_SceneDrawables, - {}, // RenderPassMultisample - loaderFlags, - {})) // RenderPassSubpasses - { - LOGE("Error Loading the museum gltf file"); - LOGI("Please verify if you have all required assets on the sample media folder"); - LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); - return false; - } - - if (!meshCameraProcessor.m_cameras.empty()) - { - const auto& camera = meshCameraProcessor.m_cameras[0]; - m_Camera.SetPosition(camera.Position, camera.Orientation); - } - - - LOGI("*********************"); - LOGI("Creating Quad mesh..."); - LOGI("*********************"); - - MeshObject blitQuadMesh; - MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); - - // Blit Material - auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pBlitQuadShader, pVulkan->m_SwapchainImageCount, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo - { - if (texName == "Diffuse") - { - return { &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0] }; - } - else if (texName == "Overlay") - { - return { &m_RenderPassData[RP_HUD].RenderTarget[0].m_ColorAttachments[0] }; - } - return {}; - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer - { - return {}; - } - ); - - m_BlitQuadDrawable = std::make_unique(*pVulkan, std::move(blitQuadShaderMaterial)); - if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderPass, sRenderPassNames[RP_BLIT], std::move(blitQuadMesh))) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitCommandBuffers() -//----------------------------------------------------------------------------- -{ - LOGI("*******************************"); - LOGI("Initializing Command Buffers..."); - LOGI("*******************************"); - - Vulkan* const pVulkan = GetVulkan(); - - auto GetPassName = [](uint32_t whichPass) - { - if (whichPass >= sRenderPassNames.size()) - { - LOGE("GetPassName() called with unknown pass (%d)!", whichPass); - return "RP_UNKNOWN"; - } - - return sRenderPassNames[whichPass]; - }; - - m_RenderPassData[RP_SCENE].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_BLIT].PassCmdBuffer.resize(pVulkan->m_SwapchainImageCount); - m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.resize(pVulkan->m_SwapchainImageCount); - - char szName[256]; - const VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[whichPass].PassCmdBuffer.size(); whichBuffer++) - { - // The Pass Command Buffer => Primary - sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) - { - return false; - } - - // Model => Secondary - sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CmdBuffLevel)) - { - return false; - } - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitLocalSemaphores() -//----------------------------------------------------------------------------- -{ - LOGI("********************************"); - LOGI("Initializing Local Semaphores..."); - LOGI("********************************"); - - const VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_RenderPassData[whichPass].PassCompleteSemaphore); - if (!CheckVkError("vkCreateSemaphore()", retVal)) - { - return false; - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::BuildCmdBuffers() -//----------------------------------------------------------------------------- -{ - LOGI("***************************"); - LOGI("Building Command Buffers..."); - LOGI("****************************"); - - Vulkan* const pVulkan = GetVulkan(); - - // Begin recording - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; - - for (uint32_t whichBuffer = 0; whichBuffer < renderPassData.ObjectsCmdBuffer.size(); whichBuffer++) - { - auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - - uint32_t targetWidth = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - uint32_t targetHeight = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)targetWidth; - viewport.height = (float)targetHeight; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = targetWidth; - scissor.extent.height = targetHeight; - - // Set up some values that change based on render pass - VkRenderPass whichRenderPass = renderPassData.RenderPass; - VkFramebuffer whichFramebuffer = bisSwapChainRenderPass ? pVulkan->m_SwapchainBuffers[whichBuffer].framebuffer : renderPassData.RenderTarget[0].m_FrameBuffer; - - // Objects (can render into any pass except Blit) - if (!cmdBufer.Begin(whichFramebuffer, whichRenderPass, bisSwapChainRenderPass)) - { - return false; - } - vkCmdSetViewport(cmdBufer.m_VkCommandBuffer, 0, 1, &viewport); - vkCmdSetScissor(cmdBufer.m_VkCommandBuffer, 0, 1, &scissor); - } - } - - // Scene drawables - for (const auto& sceneDrawable : m_SceneDrawables) - { - AddDrawableToCmdBuffers(sceneDrawable, m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.size())); - } - - // Blit quad drawable - AddDrawableToCmdBuffers(*m_BlitQuadDrawable.get(), m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.size())); - - // End recording - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - auto& renderPassData = m_RenderPassData[whichPass]; - - for (uint32_t whichBuffer = 0; whichBuffer < renderPassData.ObjectsCmdBuffer.size(); whichBuffer++) - { - auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - if (!cmdBufer.End()) - { - return false; - } - } - } - - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateGui() -//----------------------------------------------------------------------------- -{ - if (m_Gui) - { - m_Gui->Update(); - ImGuiIO& io = ImGui::GetIO(); - - if (ImGui::Begin("FPS", (bool*)nullptr, ImGuiWindowFlags_NoTitleBar)) - { - ImGui::Text("FPS: %.1f", m_CurrentFPS); - ImGui::Text("Camera [%f, %f, %f]", m_Camera.Position().x, m_Camera.Position().y, m_Camera.Position().z); - ImGui::DragFloat3("Sun Dir", &m_LightUniformData.LightDirection.x, 0.01f, -1.0f, 1.0f); - ImGui::DragFloat3("Sun Color", &m_LightUniformData.LightColor.x, 0.01f, 0.0f, 1.0f); - ImGui::DragFloat("Sun Intensity", &m_LightUniformData.LightColor.w, 0.1f, 0.0f, 100.0f); - ImGui::DragFloat3("Ambient Color", &m_LightUniformData.AmbientColor.x, 0.01f, 0.0f, 1.0f); - - for (int i = 0; i < NUM_SPOT_LIGHTS; i++) - { - std::string childName = std::string("Spot Light ").append(std::to_string(i+1)); - ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", childName.c_str()); - - if (ImGui::CollapsingHeader(childName.c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) - { - ImGui::PushID(i); - - ImGui::DragFloat3("Pos", &m_LightUniformData.SpotLights_pos[i].x, 0.1f); - ImGui::DragFloat3("Dir", &m_LightUniformData.SpotLights_dir[i].x, 0.01f, -1.0f, 1.0f); - ImGui::DragFloat3("Color", &m_LightUniformData.SpotLights_color[i].x, 0.01f, 0.0f, 1.0f); - ImGui::DragFloat("Intensity", &m_LightUniformData.SpotLights_color[i].w, 0.1f, 0.0f, 100.0f); - - ImGui::PopID(); - } - - ImDrawList* list = ImGui::GetWindowDrawList(); - - glm::vec3 LightDirNotNormalized = m_LightUniformData.SpotLights_dir[i]; - LightDirNotNormalized = glm::normalize(LightDirNotNormalized); - m_LightUniformData.SpotLights_dir[i] = glm::vec4(LightDirNotNormalized, 0.0f); - } - - glm::vec3 LightDirNotNormalized = m_LightUniformData.LightDirection; - LightDirNotNormalized = glm::normalize(LightDirNotNormalized); - m_LightUniformData.LightDirection = glm::vec4(LightDirNotNormalized, 0.0f); - } - ImGui::End(); - - return; - } -} - -//----------------------------------------------------------------------------- -bool Application::UpdateUniforms(uint32_t whichBuffer) -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - // Vert data - { - glm::mat4 LocalModel = glm::mat4(1.0f); - LocalModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); - LocalModel = glm::scale(LocalModel, glm::vec3(1.0f)); - glm::mat4 LocalMVP = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix() * LocalModel; - - m_ObjectVertUniformData.MVPMatrix = LocalMVP; - m_ObjectVertUniformData.ModelMatrix = LocalModel; - UpdateUniformBuffer(pVulkan, m_ObjectVertUniform, m_ObjectVertUniformData); - } - - // Frag data - for (auto& [hash, objectUniform] : m_ObjectFragUniforms) - { - UpdateUniformBuffer(pVulkan, objectUniform.objectFragUniform, objectUniform.objectFragUniformData); - } - - // Light data - { - glm::mat4 CameraViewInv = glm::inverse(m_Camera.ViewMatrix()); - glm::mat4 CameraProjection = m_Camera.ProjectionMatrix(); - glm::mat4 CameraProjectionInv = glm::inverse(CameraProjection); - - m_LightUniformData.ProjectionInv = CameraProjectionInv; - m_LightUniformData.ViewInv = CameraViewInv; - m_LightUniformData.ViewProjectionInv = glm::inverse(CameraProjection * m_Camera.ViewMatrix()); - m_LightUniformData.ProjectionInvW = glm::vec4(CameraProjectionInv[0].w, CameraProjectionInv[1].w, CameraProjectionInv[2].w, CameraProjectionInv[3].w); - m_LightUniformData.CameraPos = glm::vec4(m_Camera.Position(), 0.0f); - - UpdateUniformBuffer(pVulkan, m_LightUniform, m_LightUniformData); - } - - return true; -} - -//----------------------------------------------------------------------------- -void Application::Render(float fltDiffTime) -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - // Obtain the next swap chain image for the next frame. - auto currentVulkanBuffer = pVulkan->SetNextBackBuffer(); - uint32_t whichBuffer = currentVulkanBuffer.idx; - - // ******************************** - // Application Draw() - Begin - // ******************************** - - UpdateGui(); - - // Update camera - m_Camera.UpdateController(fltDiffTime * 10.0f, *m_CameraController); - m_Camera.UpdateMatrices(); - - // Update uniform buffers with latest data - UpdateUniforms(whichBuffer); - - // First time through, wait for the back buffer to be ready - std::span pWaitSemaphores = { ¤tVulkanBuffer.semaphore, 1 }; - - const VkPipelineStageFlags DefaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - - // RP_SCENE - { - BeginRenderPass(whichBuffer, RP_SCENE, currentVulkanBuffer.swapchainPresentIdx); - AddPassCommandBuffer(whichBuffer, RP_SCENE); - EndRenderPass(whichBuffer, RP_SCENE); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_SCENE, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_SCENE].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_SCENE].PassCompleteSemaphore, 1 }; - } - - // RP_HUD - VkCommandBuffer guiCommandBuffer = VK_NULL_HANDLE; - if (m_Gui) - { - // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget[0].m_FrameBuffer); - if (guiCommandBuffer != VK_NULL_HANDLE) - { - BeginRenderPass(whichBuffer, RP_HUD, currentVulkanBuffer.swapchainPresentIdx); - vkCmdExecuteCommands(m_RenderPassData[RP_HUD].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &guiCommandBuffer); - EndRenderPass(whichBuffer, RP_HUD); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_HUD, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }; - } - } - - // Blit Results to the screen - { - BeginRenderPass(whichBuffer, RP_BLIT, currentVulkanBuffer.swapchainPresentIdx); - AddPassCommandBuffer(whichBuffer, RP_BLIT); - EndRenderPass(whichBuffer, RP_BLIT); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_BLIT, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1 }, currentVulkanBuffer.fence); - pWaitSemaphores = { &m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1 }; - } - - // Queue is loaded up, tell the driver to start processing - pVulkan->PresentQueue(pWaitSemaphores, currentVulkanBuffer.swapchainPresentIdx); - - // ******************************** - // Application Draw() - End - // ******************************** -} - -//----------------------------------------------------------------------------- -void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, uint32_t WhichSwapchainImage) -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; - - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Reset()) - { - LOGE("Pass (%d) command buffer Reset() failed !", whichPass); - } - - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Begin()) - { - LOGE("Pass (%d) command buffer Begin() failed !", whichPass); - } - - VkFramebuffer framebuffer = nullptr; - switch (whichPass) - { - case RP_SCENE: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_HUD: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_BLIT: - framebuffer = pVulkan->m_SwapchainBuffers[WhichSwapchainImage].framebuffer; - break; - default: - framebuffer = nullptr; - break; - } - - assert(framebuffer != nullptr); - - VkRect2D passArea = {}; - passArea.offset.x = 0; - passArea.offset.y = 0; - passArea.extent.width = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - passArea.extent.height = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - TextureFormat swapChainColorFormat = pVulkan->m_SurfaceFormat; - auto swapChainColorFormats = std::span({ &swapChainColorFormat, 1 }); - TextureFormat swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; - std::span colorFormats = bisSwapChainRenderPass ? swapChainColorFormats : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = bisSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; - - VkClearColorValue clearColor = { renderPassData.PassSetup.ClearColor[0], renderPassData.PassSetup.ClearColor[1], renderPassData.PassSetup.ClearColor[2], renderPassData.PassSetup.ClearColor[3] }; - - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].BeginRenderPass( - passArea, - 0.0f, - 1.0f, - { &clearColor , 1 }, - (uint32_t)colorFormats.size(), - depthFormat != TextureFormat::UNDEFINED, - m_RenderPassData[whichPass].RenderPass, - bisSwapChainRenderPass, - framebuffer, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); -} - - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffer(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - if (m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_NumDrawCalls) - { - vkCmdExecuteCommands(m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_VkCommandBuffer); - } -} - -//----------------------------------------------------------------------------- -void Application::EndRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].EndRenderPass(); -} - -//----------------------------------------------------------------------------- -void Application::SubmitRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].End(); - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].QueueSubmit(WaitSemaphores, WaitDstStageMasks, SignalSemaphores, CompletionFence); -} diff --git a/samples/hello-gltf/code/main/application.hpp b/samples/hello-gltf/code/main/application.hpp deleted file mode 100644 index 7d8a78b..0000000 --- a/samples/hello-gltf/code/main/application.hpp +++ /dev/null @@ -1,190 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -/// -/// Sample app demonstrating the loading of a .gltf file (hello world) -/// -#pragma once - -#include "main/applicationHelperBase.hpp" -#include "memory/vulkan/uniform.hpp" -#include "vulkan/commandBuffer.hpp" -#include - -#define NUM_SPOT_LIGHTS 4 - -class ShaderManager; -class MaterialManager; -class Drawable; - -enum RENDER_PASS -{ - RP_SCENE = 0, - RP_HUD, - RP_BLIT, - NUM_RENDER_PASSES -}; - -// ********************** -// Uniform Buffers -// ********************** -struct ObjectVertUB -{ - glm::mat4 MVPMatrix; - glm::mat4 ModelMatrix; - glm::mat4 ShadowMatrix; -}; - -struct ObjectFragUB -{ - glm::vec4 Color; - glm::vec4 ORM; -}; - -struct LightUB -{ - glm::mat4 ProjectionInv; - glm::mat4 ViewInv; - glm::mat4 ViewProjectionInv; // ViewInv * ProjectionInv - glm::vec4 ProjectionInvW; // w components of ProjectionInv - glm::vec4 CameraPos; - - glm::vec4 LightDirection = glm::vec4(-0.564000f, 0.826000f, 0.000000f, 0.0f); - glm::vec4 LightColor = glm::vec4(1.000000f, 1.000000f, 1.000000f, 1.000000); - - glm::vec4 SpotLights_pos[NUM_SPOT_LIGHTS]; - glm::vec4 SpotLights_dir[NUM_SPOT_LIGHTS]; - glm::vec4 SpotLights_color[NUM_SPOT_LIGHTS]; - - glm::vec4 AmbientColor = glm::vec4(0.340000f, 0.340000f, 0.340000f, 0.0f); - - int Width; - int Height; -}; - -// ********************** -// Render Pass -// ********************** -struct PassSetupInfo -{ - RenderPassInputUsage ColorInputUsage; - bool ClearDepthRenderPass; - RenderPassOutputUsage ColorOutputUsage; - RenderPassOutputUsage DepthOutputUsage; - glm::vec4 ClearColor; -}; - -struct PassData -{ - // Pass internal data - PassSetupInfo PassSetup; - VkRenderPass RenderPass = VK_NULL_HANDLE; - - // Recorded objects that are set to be drawn on this pass - std::vector< CommandListVulkan> ObjectsCmdBuffer; - - // Command buffer used to dispatch the render pass - std::vector< CommandListVulkan> PassCmdBuffer; - - // Indicates the completing of the underlying render pass - VkSemaphore PassCompleteSemaphore = VK_NULL_HANDLE; - - // Render targed used by the underlying render pass - // note: The blit pass uses the backbuffer directly instead this RT - CRenderTargetArray<1> RenderTarget; -}; - -// ********************** -// Application -// ********************** -class Application : public ApplicationHelperBase -{ - struct ObjectMaterialParameters - { - UniformT objectFragUniform; - ObjectFragUB objectFragUniformData; - - std::size_t GetHash() const - { - auto hash_combine = [](std::size_t seed, const float& v) -> std::size_t - { - std::hash hasher; - seed ^= hasher(v) + 0x9e3228b9 + (seed << 6) + (seed >> 2); - return seed; - }; - - std::size_t result = 0; - result = hash_combine(result, objectFragUniformData.Color.x); - result = hash_combine(result, objectFragUniformData.Color.y); - result = hash_combine(result, objectFragUniformData.Color.z); - result = hash_combine(result, objectFragUniformData.Color.w); - result = hash_combine(result, objectFragUniformData.ORM.r); - result = hash_combine(result, objectFragUniformData.ORM.g); - result = hash_combine(result, objectFragUniformData.ORM.b); - result = hash_combine(result, objectFragUniformData.ORM.a); - - return result; - }; - }; - -public: - Application(); - ~Application() override; - - // ApplicationHelperBase - virtual bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; - virtual void Destroy() override; - virtual void Render(float fltDiffTime) override; - -private: - - // Application - Initialization - bool InitializeLights(); - bool InitializeCamera(); - bool LoadShaders(); - bool CreateRenderTargets(); - bool InitUniforms(); - bool InitAllRenderPasses(); - bool InitGui(uintptr_t windowHandle); - bool LoadMeshObjects(); - bool InitCommandBuffers(); - bool InitLocalSemaphores(); - bool BuildCmdBuffers(); - -private: - - // Application - Frame - void BeginRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, uint32_t WhichSwapchainImage); - void AddPassCommandBuffer(uint32_t WhichBuffer, RENDER_PASS WhichPass); - void EndRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass); - void SubmitRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence = (VkFence)nullptr); - void UpdateGui(); - bool UpdateUniforms(uint32_t WhichBuffer); - -private: - - // Render passes - std::array< PassData, NUM_RENDER_PASSES> m_RenderPassData; - - // UBOs - UniformT m_ObjectVertUniform; - ObjectVertUB m_ObjectVertUniformData; - UniformT m_LightUniform; - LightUB m_LightUniformData; - std::unordered_map m_ObjectFragUniforms; - - // Drawables - std::vector m_SceneDrawables; - std::unique_ptr m_BlitQuadDrawable; - - // Shaders - std::unique_ptr m_ShaderManager; - - // Materials - std::unique_ptr m_MaterialManager; -}; diff --git a/samples/hello-gltf/img/screenshot.png b/samples/hello-gltf/img/screenshot.png deleted file mode 100644 index 13f6aa8..0000000 Binary files a/samples/hello-gltf/img/screenshot.png and /dev/null differ diff --git a/samples/hello-gltf/shaders/SceneOpaque.frag b/samples/hello-gltf/shaders/SceneOpaque.frag deleted file mode 100644 index a0615de..0000000 --- a/samples/hello-gltf/shaders/SceneOpaque.frag +++ /dev/null @@ -1,259 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 -#define SHADER_LIGHT_UBO_LOCATION 2 - -// Texture Locations -#define SHADER_DIFFUSE_TEXTURE_LOC 3 -#define SHADER_NORMAL_TEXTURE_LOC 4 -#define SHADER_EMISSIVE_TEXTURE_LOC 5 -#define SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC 6 - -#define NUM_SPOT_LIGHTS (4) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - vec4 Color; - vec4 ORM; - -} FragCB; - -// Light uniform -layout(std140, set = 0, binding = SHADER_LIGHT_UBO_LOCATION) uniform LightConstantsBuff -{ - mat4 ProjectionInv; - mat4 ViewInv; - mat4 ViewProjectionInv; // ViewInv * ProjectionInv - // mat4 WorldToShadow; - vec4 ProjectionInvW; // w components of ProjectionInv - vec4 CameraPos; - - vec4 LightDirection; - vec4 LightColor; - - // Spotlight data - vec4 SpotLights_pos[NUM_SPOT_LIGHTS]; - vec4 SpotLights_dir[NUM_SPOT_LIGHTS]; - vec4 SpotLights_color[NUM_SPOT_LIGHTS]; - - vec4 AmbientColor; - - float AmbientOcclusionScale; - int Width; - int Height; - -} LightCB; - -#ifndef PI -#define PI (3.14159265359) -#endif - -// Textures -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; -layout(set = 0, binding = SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC) uniform sampler2D u_MetallicRoughnessTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 3) in vec3 v_WorldTan; -layout (location = 4) in vec3 v_WorldBitan; -layout (location = 5) in vec4 v_ShadowCoord; -layout (location = 6) in vec4 v_VertColor; - -// Output color -layout (location = 0) out vec4 FragColor; - -//----------------------------------------------------------------------------- -vec4 ScreenToView(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = LightCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - return ViewSpacePosition; -} - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ViewSpacePosition = ScreenToView(ScreenCoord, Depth); - - vec4 WorldSpacePosition = LightCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - -//----------------------------------------------------------------------------- -float FSchlick(float f0, float f90, float u) -//----------------------------------------------------------------------------- -{ - return f0 + (f90 - f0) * pow(1.0 - u, 5.0); -} - -//----------------------------------------------------------------------------- -vec3 FSchlick(vec3 f0, float f90, float u) -//----------------------------------------------------------------------------- -{ - return f0 + (f90 - f0) * pow(1.0 - u, 5.0); -} - -//----------------------------------------------------------------------------- -void CalcBRDF(vec3 EyeDir, vec3 Normal, vec3 LightDir, vec3 AlbedoColor, float Roughness, float Metallic, out vec3 f_diffuse, out vec3 f_specular, out vec3 f0) -//----------------------------------------------------------------------------- -{ - vec3 H = normalize(LightDir + EyeDir); - //float NL = max(0.0, dot(Normal, LightDir)); - float NV = max(0.0, dot(Normal, EyeDir)); - float LH = max(0.0, dot(LightDir, H)); - float NH = max(0.0, dot(Normal, H)); - float VH = max(0.0, dot(EyeDir, H)); - - float gltfDielectricSpecular = 0.04; - - vec3 c_diff = mix(AlbedoColor.rgb * (1 - gltfDielectricSpecular), vec3(0.0)/*diffuse color is black in a metallic material*/, Metallic); - f0 = mix(vec3(0.04, 0.04, 0.04), AlbedoColor.rgb, Metallic); - float alpha = Roughness * Roughness; - - vec3 F = FSchlick(f0, 1.0/*f90*/, VH); - f_diffuse = (1 - F) * (1 / PI) * c_diff; - - // D - GGX microfacet distribution - float alphaSqr = alpha * alpha; - float denom = NH * NH * (alphaSqr - 1.0) + 1.0f; - float D = alphaSqr / (PI * denom * denom); - - // V - float k = alpha / 2.0f; - float k2 = k * k; - float invK2 = 1.0f - k2; - float Vis = 1.0 / (LH * LH * invK2 + k2); - - f_specular = F * Vis * D; - -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Base (albedo) color - // ******************************** - // Get color from the color texture - - vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); - DiffuseColor.xyzw *= FragCB.Color.xyzw; - - if(DiffuseColor.a < 0.5) - { - discard; - } - - // Adjust by vertex color. - DiffuseColor.xyzw *= v_VertColor.xyzw; - - vec4 Emissive = texture( u_EmissiveTex, v_TexCoord.xy ); - vec4 MetallicRoughness = texture( u_MetallicRoughnessTex, v_TexCoord.xy ); - - // Get base normal from the bump texture - vec3 Normal = texture( u_NormalTex, v_TexCoord.xy ).rgb; - Normal = Normal * 2.0 - 1.0; - - mat3 TBN = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); - Normal = normalize(TBN * Normal); - - float Depth = gl_FragCoord.z; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - vec3 EyeDir = normalize(LightCB.CameraPos.xyz - v_WorldPos.xyz); - - vec3 L = -LightCB.LightDirection.xyz; - - vec3 f_diffuse = vec3(0.0); - vec3 f_specular = vec3(0.0); - vec3 f0 = vec3(0.0); - - CalcBRDF( - EyeDir, - Normal, - L, - DiffuseColor.rgb, - MetallicRoughness.g * FragCB.ORM.g, - MetallicRoughness.b * FragCB.ORM.b, - f_diffuse, - f_specular, - f0); - - // Spot lights - vec3 spot_diffuse = vec3(0.0); - vec3 spot_specular = vec3(0.0); - for(int l=0;lCreateRenderPass(bloomFormat, TextureFormat::UNDEFINED, Msaa::Samples1, RenderPassInputUsage::Clear, RenderPassOutputUsage::StoreReadOnly, true, RenderPassOutputUsage::StoreReadOnly, renderPass)) + { + LOGE("Unable to create render pass id: %d", ii); + return false; + } + + if (!m_IntermediateRts[ii].Initialize(pVulkan, w, h, bloomFormat, TextureFormat::UNDEFINED, Msaa::Samples1, rtNames) + || !m_IntermediateRts[ii].InitializeFrameBuffer(pVulkan, renderPass)) { LOGE("Unable to create main render target"); + return false; } + + m_RenderContexts[ii] = RenderContext(std::move(renderPass), m_IntermediateRts[ii].GetFrameBuffer(), rtNames); } } //-------------------------------------------------------------------------- @@ -129,7 +147,12 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc // Setup Shaders for (uint32_t ss = 0; ss < ShaderPair_Count; ++ss) { - LoadShader(pVulkan, *m_AssetManager, &m_shaders[ss], ShaderSets[ss][0], m_bUseExtension ? ShaderSets[ss][2] : ShaderSets[ss][1]); + LoadShader( + pVulkan, + *m_AssetManager, + &m_shaders[ss], + std::filesystem::path(SHADER_DESTINATION_PATH).append(ShaderSets[ss][0]).string(), + std::filesystem::path(SHADER_DESTINATION_PATH).append(m_bUseExtension ? ShaderSets[ss][2] : ShaderSets[ss][1]).string()); } //-------------------------------------------------------------------------- @@ -152,7 +175,7 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc { for (uint32_t ii = 0; ii < NumWeightImages; ++ii) { - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM; + //VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM;//no longer used const TextureFormat weightFormat = TextureFormat::R16_SFLOAT; VkImageViewSampleWeightCreateInfoQCOM weightViewInfo = {}; @@ -184,15 +207,12 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_FILTER_NEAREST, usageFlags, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); #endif - m_weightTextures[ii] = - apiCast(m_TextureManager->CreateTextureFromBuffer( - *pVulkan, pHalfTexData, gBlurFilterSize * sizeof(float16), - gBlurFilterSize, 1, 1, weightFormat, - SamplerAddressMode::ClampEdge, - SamplerFilter::Nearest, - nullptr, - (uint32_t)usageFlags)); - + m_weightTextures[ii] = apiCast( CreateTextureFromBuffer( static_cast(*pVulkan), + pHalfTexData, gBlurFilterSize * sizeof( float16 ), + gBlurFilterSize, 1, 1, weightFormat, + SamplerAddressMode::ClampEdge, + SamplerFilter::Nearest, + nullptr ) ); weightViewInfo.filterCenter.x = gBlurFilterSize / 2; weightViewInfo.filterCenter.y = 0; @@ -209,14 +229,12 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc usageFlags, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); #endif - m_weightTextures[ii] = - apiCast(m_TextureManager->CreateTextureFromBuffer( - *pVulkan, pHalfTexData, gBlurFilterSize * sizeof(float16), + m_weightTextures[ii] = apiCast( CreateTextureFromBuffer( + static_cast(*pVulkan), pHalfTexData, gBlurFilterSize * sizeof(float16), 1, gBlurFilterSize, 1, weightFormat, SamplerAddressMode::ClampEdge, SamplerFilter::Nearest, - nullptr, - (uint32_t)usageFlags)); + "nullptr" ) ); weightViewInfo.filterCenter.x = 0; weightViewInfo.filterCenter.y = gBlurFilterSize / 2; @@ -234,14 +252,15 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc } } - + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + if (/*pVulkan->IsTextureFormatSupported(TextureFormat::ASTC_4x4_UNORM_BLOCK)*/ false) { - m_sourceTexture = apiCast(m_TextureManager->GetOrLoadTexture(*m_AssetManager, TEXTUREFILE("painting_astc.ktx"), SamplerAddressMode::ClampEdge)); + m_sourceTexture = apiCast(m_TextureManager->GetOrLoadTexture(TEXTUREFILE("painting_astc.ktx"), SamplerAddressMode::ClampEdge, prefixTextureDir)); } else { - m_sourceTexture = apiCast(m_TextureManager->GetOrLoadTexture(*m_AssetManager, TEXTUREFILE("painting.ktx"), SamplerAddressMode::ClampEdge)); + m_sourceTexture = apiCast(m_TextureManager->GetOrLoadTexture(TEXTUREFILE("painting.ktx"), SamplerAddressMode::ClampEdge, prefixTextureDir)); } //-------------------------------------------------------------------------- @@ -295,6 +314,7 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc m_passes[pp] = new PassInfo(pVulkan, &GlobalPassInfo[pp]); m_passes[pp]->pRt = &m_IntermediateRts[pp]; + m_passes[pp]->pRc = &m_RenderContexts[pp]; m_passes[pp]->pUniform = NULL; m_passes[pp]->renderArea.width = m_passes[pp]->pPassInfo->renderWidth; m_passes[pp]->renderArea.height = m_passes[pp]->pPassInfo->renderHeight; @@ -309,7 +329,7 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc case ShaderPair_Blur_Horizontal: { m_passes[pp]->pUniform = &m_uniforms[pp]; - m_passes[pp]->vInputViews.push_back(m_IntermediateRts[pp - 1][0].m_ColorAttachments[0].GetVkImageView()); + m_passes[pp]->vInputViews.push_back(m_IntermediateRts[pp - 1].m_ColorAttachments[0].GetVkImageView()); if (m_bUseExtension) { @@ -320,7 +340,7 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc } case ShaderPair_Blur_Vertical: m_passes[pp]->pUniform = &m_uniforms[pp]; - m_passes[pp]->vInputViews.push_back(m_IntermediateRts[pp - 1][0].m_ColorAttachments[0].GetVkImageView()); + m_passes[pp]->vInputViews.push_back(m_IntermediateRts[pp - 1].m_ColorAttachments[0].GetVkImageView()); if (m_bUseExtension) { m_passes[pp]->vInputViews.push_back(m_weightTextureViews[1]); @@ -329,10 +349,11 @@ bool BloomImageprocessing::Initialize(uintptr_t windowHandle, uintptr_t hInstanc break; case ShaderPair_Display: m_passes[pp]->pRt = NULL; // &m_IntermediateRts[Pass_Display]; + // m_passes[pp]->pRc = NULL; // &m_RenderContexts[Pass_Display]; m_passes[pp]->renderArea.width = pVulkan->m_SurfaceWidth; // k_FullImageWidth; m_passes[pp]->renderArea.height = pVulkan->m_SurfaceHeight; // k_FullImageHeight; m_passes[pp]->vInputViews.push_back(m_sourceTexture->GetVkImageView()); - m_passes[pp]->vInputViews.push_back((*m_passes[pp -1]->pRt)[0].m_ColorAttachments[0].GetVkImageView()); + m_passes[pp]->vInputViews.push_back((*m_passes[pp -1]->pRt).m_ColorAttachments[0].GetVkImageView()); break; } } @@ -411,12 +432,12 @@ void BloomImageprocessing::FinalizePass(PassInfo* pPass) if (pPass->pRt == NULL) { - pPass->renderpass = pVulkan->m_SwapchainRenderPass; + pPass->renderpass = pVulkan->m_SwapchainRenderPass.mRenderPass; } else { - pPass->renderpass = pPass->pRt->m_RenderPass; - pPass->fbo = pPass->pRt->m_RenderTargets[0].m_FrameBuffer; + pPass->renderpass = pPass->pRc->GetRenderPass().mRenderPass; + pPass->fbo = pPass->pRc->GetFramebuffer()->m_FrameBuffer; } // Descriptor Set Layouyt @@ -642,28 +663,30 @@ void BloomImageprocessing::FinalizePass(PassInfo* pPass) ds.depthWriteEnable = VK_FALSE; pVulkan->CreatePipeline( - VK_NULL_HANDLE, - &visci, + VK_NULL_HANDLE, //pipeline cache + &visci, // vertex input state pPass->pLayout, - pPass->renderpass, - 0/*subpass*/, - &rs, - &ds, - nullptr, - nullptr, - {}, - nullptr, - nullptr, + *pPass->pRc, + &rs, // rasterization + &ds, // depth stencil + nullptr, // blend + nullptr, // multisample + nullptr, // ia + {}, // dynamic states + nullptr, // viewport + nullptr, // scissor + VK_NULL_HANDLE, // task shader + VK_NULL_HANDLE, // mesh shader pPass->pShaderInfo->VertShaderModule.GetVkShaderModule(), pPass->pShaderInfo->FragShaderModule.GetVkShaderModule(), - nullptr, - false, - VK_NULL_HANDLE, - & pPass->pipeline); + nullptr, // specialization + false, // allow derivation + VK_NULL_HANDLE, // derive from + &pPass->pipeline); } -void BloomImageprocessing::DrawPass(Wrap_VkCommandBuffer* cmd, const PassInfo* pPass, uint32_t idx) +void BloomImageprocessing::DrawPass(CommandBuffer* cmd, const PassInfo* pPass, uint32_t idx) { Vulkan* pVulkan = GetVulkan(); @@ -689,7 +712,7 @@ void BloomImageprocessing::DrawPass(Wrap_VkCommandBuffer* cmd, const PassInfo* p pPass->pRt == NULL ? true : false, pPass->renderpass, pPass->pRt == NULL ? true : false, pPass->pRt == NULL - ? pVulkan->m_SwapchainBuffers[idx].framebuffer + ? pVulkan->m_SwapchainBuffers[idx].framebuffer.m_FrameBuffer : pPass->fbo, VK_SUBPASS_CONTENTS_INLINE); @@ -716,7 +739,7 @@ void BloomImageprocessing::DrawPass(Wrap_VkCommandBuffer* cmd, const PassInfo* p void BloomImageprocessing::BuildCmdBuffer(uint32_t idx) { - Wrap_VkCommandBuffer* pCmdBuf = &m_commandBuffers[idx]; + CommandBuffer* pCmdBuf = &m_commandBuffers[idx]; Vulkan* pVulkan = GetVulkan(); diff --git a/samples/BloomImageProcessing/code/main/bloom-image-processing.hpp b/samples/image_processing/code/main/application.hpp similarity index 91% rename from samples/BloomImageProcessing/code/main/bloom-image-processing.hpp rename to samples/image_processing/code/main/application.hpp index fe48cf8..9a8816d 100644 --- a/samples/BloomImageProcessing/code/main/bloom-image-processing.hpp +++ b/samples/image_processing/code/main/application.hpp @@ -15,13 +15,6 @@ /// DOES NOT initialize Vulkan. /// -// Workaround for windows builds -#if OS_WINDOWS -#ifndef VK_ENABLE_BETA_EXTENSIONS -#define VK_ENABLE_BETA_EXTENSIONS 1 -#endif // VK_ENABLE_BETA_EXTENSIONS -#endif // OS_WINDOWS - #include "main/applicationHelperBase.hpp" #include "memory/vulkan/uniform.hpp" #include "vulkan/commandBuffer.hpp" @@ -119,7 +112,8 @@ class BloomImageprocessing : public ApplicationHelperBase { Vulkan* pVulkan; const Uniform* pUniform; - const CRenderTargetArray<1>* pRt; + const RenderTarget* pRt; + const RenderContext* pRc; std::vector vInputViews; const ShaderInfo* pShaderInfo; VkExtent2D renderArea; @@ -173,18 +167,19 @@ class BloomImageprocessing : public ApplicationHelperBase static const uint32_t NumWeightImages = 2; - CRenderTargetArray<1> m_IntermediateRts[Pass_Count]; + RenderTarget m_IntermediateRts[Pass_Count]; + RenderContext m_RenderContexts[Pass_Count]; Uniform m_uniforms[Pass_Count]; ShaderInfo m_shaders[ShaderPair_Count]; - std::unique_ptr> m_weightTextures[NumWeightImages]; + std::unique_ptr> m_weightTextures[NumWeightImages]; VkImageView m_weightTextureViews[NumWeightImages]; - const TextureT* m_sourceTexture = nullptr; + const Texture* m_sourceTexture = nullptr; PassInfo* m_passes[Pass_Count]; - Wrap_VkCommandBuffer m_commandBuffers[NUM_VULKAN_BUFFERS]; + CommandBuffer m_commandBuffers[NUM_VULKAN_BUFFERS]; void FinalizePass(PassInfo* pPass); - void DrawPass(Wrap_VkCommandBuffer* cmd, const PassInfo* pPass, uint32_t idx); + void DrawPass(CommandBuffer* cmd, const PassInfo* pPass, uint32_t idx); void BuildCmdBuffer(uint32_t idx); static inline uint64_t Factorial(uint32_t n) @@ -203,7 +198,7 @@ class BloomImageprocessing : public ApplicationHelperBase VkPhysicalDeviceImageProcessingPropertiesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_PROCESSING_PROPERTIES_QCOM> { static constexpr auto Name = VK_QCOM_IMAGE_PROCESSING_EXTENSION_NAME; - Ext_VK_QCOM_image_processing(VulkanExtension::eStatus status = VulkanExtension::eRequired) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) + Ext_VK_QCOM_image_processing(VulkanExtensionStatus status = VulkanExtensionStatus::eRequired) : VulkanDeviceFeaturePropertiesExtensionHelper(Name, status) {} void PopulateRequestedFeatures() override diff --git a/samples/image_processing/install_apk.bat b/samples/image_processing/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/image_processing/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/image_processing/install_config.bat b/samples/image_processing/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/image_processing/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/SubPass/project/android/AndroidManifest.xml b/samples/image_processing/project/android/AndroidManifest.xml similarity index 98% rename from samples/SubPass/project/android/AndroidManifest.xml rename to samples/image_processing/project/android/AndroidManifest.xml index 2cae4c1..0b80db5 100644 --- a/samples/SubPass/project/android/AndroidManifest.xml +++ b/samples/image_processing/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -23,7 +22,7 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:exported="true"> + + SGS Image Processing + diff --git a/samples/BloomImageProcessing/shaders/BlurBase-Horizontal-Ext.frag b/samples/image_processing/shaders/BlurBase-Horizontal-Ext.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/BlurBase-Horizontal-Ext.frag rename to samples/image_processing/shaders/BlurBase-Horizontal-Ext.frag index 18409cf..70aba48 100644 --- a/samples/BloomImageProcessing/shaders/BlurBase-Horizontal-Ext.frag +++ b/samples/image_processing/shaders/BlurBase-Horizontal-Ext.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/BlurBase-Horizontal.frag b/samples/image_processing/shaders/BlurBase-Horizontal.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/BlurBase-Horizontal.frag rename to samples/image_processing/shaders/BlurBase-Horizontal.frag index f28c163..af8a89e 100644 --- a/samples/BloomImageProcessing/shaders/BlurBase-Horizontal.frag +++ b/samples/image_processing/shaders/BlurBase-Horizontal.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/BlurBase-Vertical-Ext.frag b/samples/image_processing/shaders/BlurBase-Vertical-Ext.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/BlurBase-Vertical-Ext.frag rename to samples/image_processing/shaders/BlurBase-Vertical-Ext.frag index 18409cf..70aba48 100644 --- a/samples/BloomImageProcessing/shaders/BlurBase-Vertical-Ext.frag +++ b/samples/image_processing/shaders/BlurBase-Vertical-Ext.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/BlurBase-Vertical.frag b/samples/image_processing/shaders/BlurBase-Vertical.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/BlurBase-Vertical.frag rename to samples/image_processing/shaders/BlurBase-Vertical.frag index 99afc1d..20c07e4 100644 --- a/samples/BloomImageProcessing/shaders/BlurBase-Vertical.frag +++ b/samples/image_processing/shaders/BlurBase-Vertical.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/Display.frag b/samples/image_processing/shaders/Display.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/Display.frag rename to samples/image_processing/shaders/Display.frag index 2f5aaa8..be209dc 100644 --- a/samples/BloomImageProcessing/shaders/Display.frag +++ b/samples/image_processing/shaders/Display.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/Downsample-Ext.frag b/samples/image_processing/shaders/Downsample-Ext.frag similarity index 94% rename from samples/BloomImageProcessing/shaders/Downsample-Ext.frag rename to samples/image_processing/shaders/Downsample-Ext.frag index 315f914..1d44ffd 100644 --- a/samples/BloomImageProcessing/shaders/Downsample-Ext.frag +++ b/samples/image_processing/shaders/Downsample-Ext.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/Downsample.frag b/samples/image_processing/shaders/Downsample.frag similarity index 96% rename from samples/BloomImageProcessing/shaders/Downsample.frag rename to samples/image_processing/shaders/Downsample.frag index 3caa3e7..8cdc40d 100644 --- a/samples/BloomImageProcessing/shaders/Downsample.frag +++ b/samples/image_processing/shaders/Downsample.frag @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/BloomImageProcessing/shaders/VertexShader.vert b/samples/image_processing/shaders/VertexShader.vert similarity index 93% rename from samples/BloomImageProcessing/shaders/VertexShader.vert rename to samples/image_processing/shaders/VertexShader.vert index 7d7e4ea..b24eebf 100644 --- a/samples/BloomImageProcessing/shaders/VertexShader.vert +++ b/samples/image_processing/shaders/VertexShader.vert @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/rayQueryShadows/01_CompileShaders.bat b/samples/rayQueryShadows/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/rayQueryShadows/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rayQueryShadows/02_Install_APK.bat b/samples/rayQueryShadows/02_Install_APK.bat deleted file mode 100644 index 5102c35..0000000 --- a/samples/rayQueryShadows/02_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\rayQueryShadows\outputs\apk\debug\rayQueryShadows-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\rayQueryShadows\outputs\apk\debug\rayQueryShadows-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rayQueryShadows/02_PrepareMedia.bat b/samples/rayQueryShadows/02_PrepareMedia.bat deleted file mode 100644 index 19d8dec..0000000 --- a/samples/rayQueryShadows/02_PrepareMedia.bat +++ /dev/null @@ -1,72 +0,0 @@ -rem @echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\default_ddn.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_env.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_irradiance.ktx Media\Textures\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skybox_Separate.* Media\Objects\. - -rem xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\BistroGltfFused Media\Objects\BistroGltfFused -rem call :ConvertTextures Media\Objects\BistroGltfFused 2048 - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\BistroGltfExterior Media\Objects\BistroExteriorGltf -call :ConvertTextures Media\Objects\BistroExteriorGltf 2048 - -echo. -echo Cleaning up afer texture conversion -echo. -pushd Media -del /s /q /f *.png *.jpg *.tga *.dds -popd - -goto:eof - - -:ConvertTextures -rem %1 is directory, %2 is size at which we downsample (in bytes) -rem Convert png images to ktx textures. -rem Larger textures are mipped and reduced in size, (very) small textures are not -SET SIZELIMIT=1000 -pushd %1 -for /R %%i in (*.png) do ( - echo %%i|findstr /i /L "Normal">nul - if errorlevel 1 ( - call :ConvertTexture %%i %2 R8G8B8A8UnormSrgb - ) else ( - rem Normal Texture (output linear 'color' values) - call :ConvertTexture %%i %2 R8G8B8A8Unorm - ) -) -for /R %%i in (*.dds) do ( - echo %%i|findstr /i /L "Normal">nul - if errorlevel 1 ( - call :ConvertTexture %%i %2 R8G8B8A8UnormSrgb - ) else ( - rem Normal Texture (output linear 'color' values) - call :ConvertTexture %%i %2 R8G8B8A8Unorm - ) -) -popd -goto :eof - -:ConvertTexture -rem %1 is file, %2 is size at which we downsample (in bytes), %3 is the output data format -rem Convert png image to ktx texture. -if %~z1 LSS %2 ( - echo "Not scaling (or mipping) %~f1 (too small)" - ..\..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -nomip -) else ( - ..\..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -w 25%% -h 25%% -) -goto :eof diff --git a/samples/rayQueryShadows/02_PrepareMediaKtx2.bat b/samples/rayQueryShadows/02_PrepareMediaKtx2.bat deleted file mode 100644 index dad403b..0000000 --- a/samples/rayQueryShadows/02_PrepareMediaKtx2.bat +++ /dev/null @@ -1,72 +0,0 @@ -rem @echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\default_ddn.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_env.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_irradiance.ktx Media\Textures\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skybox_Separate.* Media\Objects\. - -rem xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\BistroGltfFused Media\Objects\BistroGltfFused -rem call :ConvertTextures Media\Objects\BistroGltfFused 2048 - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\BistroGltfExterior Media\Objects\BistroExteriorGltf -call :ConvertTextures Media\Objects\BistroExteriorGltf 2048 - -echo. -echo Cleaning up afer texture conversion -echo. -pushd Media -del /s /q /f *.png *.jpg *.tga *.dds -popd - -goto:eof - - -:ConvertTextures -rem %1 is directory, %2 is size at which we downsample (in bytes) -rem Convert png images to ktx textures. -rem Larger textures are mipped and reduced in size, (very) small textures are not -SET SIZELIMIT=1000 -pushd %1 -for /R %%i in (*.png) do ( - echo %%i|findstr /i /L "Normal">nul - if errorlevel 1 ( - call :ConvertTexture %%i %2 - ) else ( - rem Normal Texture (adjust compression for 'normal node' but keep the 3 components rather than compressing to 2 components which needs a shader change!) - call :ConvertTexture %%i %2 --input_swizzle rgb1 --normal_mode --assign_oetf linear - ) -) -for /R %%i in (*.dds) do ( - echo %%i|findstr /i /L "Normal">nul - if errorlevel 1 ( - call :ConvertTexture %%i %2 - ) else ( - rem Normal Texture (adjust compression for 'normal node' but keep the 3 components rather than compressing to 2 components which needs a shader change!) - call :ConvertTexture %%i %2 --input_swizzle rgb1 --normal_mode --assign_oetf linear - ) -) -popd -goto :eof - -:ConvertTexture -rem %1 is file, %2 is size at which we downsample (in bytes), %3... are additional options (eg for normal maps) passed to the converter -rem Convert png image to ktx texture. -if %~z1 LSS %2 ( - echo "Not scaling (or mipping) %~f1 (too small)" - ..\..\..\..\..\project\tools\toktx.exe --encode etc1s %3 %4 %5 %6 %7 %8 %9 "%~dpn1.ktx" "%~f1" -) else ( - ..\..\..\..\..\project\tools\toktx.exe --encode etc1s --genmipmap %3 %4 %5 %6 %7 %8 %9 "%~dpn1.ktx" "%~f1" -) -goto :eof diff --git a/samples/rayQueryShadows/04_Install_APK.bat b/samples/rayQueryShadows/04_Install_APK.bat deleted file mode 100644 index 5102c35..0000000 --- a/samples/rayQueryShadows/04_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\rayQueryShadows\outputs\apk\debug\rayQueryShadows-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\rayQueryShadows\outputs\apk\debug\rayQueryShadows-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rayQueryShadows/05_PrepareWindowsZip.bat b/samples/rayQueryShadows/05_PrepareWindowsZip.bat deleted file mode 100644 index b49c1e7..0000000 --- a/samples/rayQueryShadows/05_PrepareWindowsZip.bat +++ /dev/null @@ -1,8 +0,0 @@ -@del rayQueryShadows.zip -copy /y ..\..\project\windows\solution\samples\rayQueryShadows\Debug\rayQueryShadows.exe rayQueryShadows_debug.exe -copy /y ..\..\project\windows\solution\samples\rayQueryShadows\Release\rayQueryShadows.exe rayQueryShadows_release.exe -copy /y ..\..\project\windows\solutionArm64\samples\rayQueryShadows\Debug\rayQueryShadows.exe rayQueryShadows_arm64_debug.exe -copy /y ..\..\project\windows\solutionArm64\samples\rayQueryShadows\Release\rayQueryShadows.exe rayQueryShadows_arm64_release.exe -zip -r rayQueryShadows.zip Media rayQueryShadows_debug.exe rayQueryShadows_release.exe rayQueryShadows_arm64_debug.exe rayQueryShadows_arm64_release.exe -del /q rayQueryShadows_debug.exe rayQueryShadows_release.exe rayQueryShadows_arm64_debug.exe rayQueryShadows_arm64_release.exe - diff --git a/samples/rayQueryShadows/06_Adb_Logcat.bat b/samples/rayQueryShadows/06_Adb_Logcat.bat deleted file mode 100644 index 6b89aff..0000000 --- a/samples/rayQueryShadows/06_Adb_Logcat.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat... -call adb logcat -c -call adb logcat - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause \ No newline at end of file diff --git a/samples/rayQueryShadows/07_InstallConfig.bat b/samples/rayQueryShadows/07_InstallConfig.bat deleted file mode 100644 index de62246..0000000 --- a/samples/rayQueryShadows/07_InstallConfig.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off - -adb push ./app_config.txt /sdcard/Android/data/com.qualcomm.sgs.rayqueryshadows/files/app_config.txt - -pause diff --git a/samples/rayQueryShadows/CMakeLists.txt b/samples/rayQueryShadows/CMakeLists.txt deleted file mode 100644 index 288f9cf..0000000 --- a/samples/rayQueryShadows/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (rayQueryShadows C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/rayqueryshadows.cpp - code/main/rayqueryshadows.hpp - code/main/materials.hpp -) - - - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Copy required models to local folders -# -include(ModelPackager) - -# ThaiCarving GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/ThaiCarving/ThaiCarving) - -# Add supporting GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/Floor_Separate) -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/Skybox_Separate) -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/UVSphere_Separate) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/ThaiCarving/Textures_png) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/rayQueryShadows/README.md b/samples/rayQueryShadows/README.md deleted file mode 100644 index 54aad0f..0000000 --- a/samples/rayQueryShadows/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# Ray Query Shadows sample - -![Screenshot](img/screenshot.PNG) - -## Overview - -Uses the Vulkan Ray Tracing extensions to implement shadows from a point light source in a non-trivial scene. - -Requires drivers with the 'final' Vulkan Ray Tracing api (Dec 2020 onwards). - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - - -Note: The sample assumes the existence of supporting assets under the **'Media'** folder. These assets are not currently distributed with the framework. - - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_Install_APK.bat -``` - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. - - -## Running - -Windows: -- From this directory (samples\rayQueryShadows) execute `..\..\project\windows\solution\samples\rayQueryShadows\Debug\rayQueryShadows.exe` -- (you can also compile/run by opening `project\windows\solution\vkSampleFramework.sln` in Visual Studio and running rayQueryShadows) - -Android: -- This sample runs on hardware that exposes the Vulkan Acceleration Structure and Ray Query extensions - -## Configuration - -The sample will run with reasonable defaults but the user can override settings by placing a `app_config.txt` text file in the sample root folder (eg samples\rayQueryShadows\app_config.txt) - -This file can also be pushed to Android device (takes precidence of the app_config.txt in the apk install): -
-adb push ./app_config.txt /sdcard/Android/data/com.quic.rayqueryshadows/files/app_config.txt
-
- -Example contents: -
gRenderHud = true
-gCameraStartPos = { -1012, 1047, 568 }
-gCameraStartRot = { 77.0, 49.0, 73.0 }
-gShadowLightPos = { 111.0, 420.0, -423.0 }
-gShadowLightTarget = { 269.0, 0.0, -254.0 }
-gShadowPosition = {-208.0, 422.6, 464.9 }
-
-
-gSurfaceWidth = 1280
-gSurfaceHeight = 720
-
-gRenderWidth = 1280
-gRenderHeight = 720
-
-gEnableValidation = true
-
-
- -Additional options: -- gCreateCulledAccelerationStructure=false (default true). - - Disables the acceletration structure culling and instead creates the acceleration structures with all BLAS instances (entire scene). - -- gFragmentShaderRTShadows (default false) - - Enable lights/shadows run in additional fragment shader passes (renders a sphere for each 'additional' light and raytraces shadows for all gbuffer pixels touched by the light) - -- gEnableValidation (default true) - - Enable Khronos Vulkan validation layer, disable to reduce CPU load (samples should aim to be free of Validation errors) - -- gRenderShadow (default true) - - Disable rendering of shadows (ie disable ray queries). Screen will be rendered as if everything is in shadow! - -- gFixedFrameRate (default 0) - - Set a fixed frame rate for the application. CPU sleep() will be added every frame so on average the application hits the target (assuming it is lower than the maximum achievable on the target device). Frame time reported to the application's Update is fixed (derived from gFixedFrameRate). Value of 0 disables this feature and runs the application at its maximum speed. - -- gFifoPresentMode (default false) - - When set to true changes Vulkan's swapchain present mode to 'VK_PRESENT_MODE_FIFO_KHR'. Will fix the application's maximum present frequency (and thus application fps) to that of the display. - -- gRayQueryFragmentShader (default false) - - When set to true runs the fullscreen ray queried shadow pass in a fullscreen fragment shader (vs compute when false) \ No newline at end of file diff --git a/samples/rayQueryShadows/app_config.txt b/samples/rayQueryShadows/app_config.txt deleted file mode 100644 index 24a56e8..0000000 --- a/samples/rayQueryShadows/app_config.txt +++ /dev/null @@ -1,6 +0,0 @@ -gFramesToRender=0 -#gCameraMoveSpeed=100 - -gRasterizedShadow=false -gRasterizedShadowCulled=true - diff --git a/samples/rayQueryShadows/build.gradle b/samples/rayQueryShadows/build.gradle deleted file mode 100644 index e437ab6..0000000 --- a/samples/rayQueryShadows/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.rayqueryshadows" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/rayQueryShadows/code/main/materials.hpp b/samples/rayQueryShadows/code/main/materials.hpp deleted file mode 100644 index 0cf44ae..0000000 --- a/samples/rayQueryShadows/code/main/materials.hpp +++ /dev/null @@ -1,141 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#pragma once - -#include "vulkan/vulkan.hpp" - -#include "system/os_common.h" -#include "system/glm_common.hpp" - -//============================================================================= -// Uniform Buffers -//============================================================================= - - -// ********************** -// The Objects -// ********************** -struct ObjectVertUB -{ - glm::mat4 VPMatrix; - glm::vec4 AnimationRotation; // Used if running the 'animated' version of the shaders -}; - -struct ObjectFragUB -{ - glm::vec4 Color; - - // X: Normal Height - // Y: Normal Mirror Reflect Amount - // Z: Reflect Fresnel Min - // W: Reflect Fresnel Max - glm::vec4 NormalHeight; -}; - - -// ********************** -// The Skybox -// ********************** -struct SkyboxVertUB -{ - glm::mat4 MVPMatrix; - glm::mat4 ModelMatrix; - glm::vec4 Color; -}; - - -// ********************** -// Deferred Lighting Fullscreen pass -// ********************** -struct LightFragCtrl { - glm::mat4 ProjectionInv; - glm::mat4 ViewInv; - glm::mat4 WorldToShadow;// for rasterized depth shadow - glm::vec4 CameraPos; - - glm::vec4 PointLightPosition; // w is intensity - glm::vec4 PointLightColor; - - // Directional Light - glm::vec4 DirectionalLightDirection; - glm::vec4 DirectionalLightColor; - - glm::vec4 AmbientColor; - - float PointLightRadius; // of the light itself (not the area of influence) - float PointLightCutoff; - - float SpecScale; - float SpecPower; - float irradianceAmount; - float irradianceMipLevel; - float AmbientOcclusionScale; - int Width; - int Height; -}; - - -// ********************** -// Additional deferred pass light objects (additional lights in scene) -// ********************** -struct PointLightUB{ - // Vertex shader - glm::mat4 MVPMatrix; - - // Fragment shader (could split in to 2 uniform buffers, one per shader stage) - glm::mat4 ProjectionInv; - glm::mat4 ViewInv; - glm::vec4 CameraPos; - glm::vec3 LightPosition; - float LightIntensity; - glm::vec4 LightColor; - float LightRadius; // Radius of the physical light (not its range of influence) - float LightCutoff; // Cutoff brightness for the light - float SpecScale; - float SpecPower; - glm::vec2 WindowSize; -}; - - -// ********************** -// Post/Blit -// ********************** -struct BlitFragCtrl { - float Bloom = 0.0f; - float Diffuse = 1.0f; // 0 to 2 range (dark to white) - int sRGB = 0; // 1 - apply srgb conversion in output blit shader, 0 passthrough color -}; - - -// ********************** -// Compute (ray traced shadow) -// ********************** -struct ShadowRQCtrl { - // X: Screen Width - // Y: Screen Height - // Z: One Width Pixel - // W: One Height Pixel - glm::vec4 ScreenSize; - // Camera inverse projection - glm::mat4 ProjectionInv; - // Camera inverse view - glm::mat4 ViewInv; - // Light(shadow) world position (only used for spotlight or pointlight) - glm::vec4 LightWorldPos; - // Light(shadow) world direction (only used for spotlight or directional light) - glm::vec4 LightWorldDirection; -} m_ShadowRQCtrl; - -// ********************** -// Compute (mesh animation) -// ********************** -struct MeshAnimatorUB -{ - glm::vec4 AnimationRotation; -}; diff --git a/samples/rayQueryShadows/code/main/rayQueryShadows.cpp b/samples/rayQueryShadows/code/main/rayQueryShadows.cpp deleted file mode 100644 index 006965c..0000000 --- a/samples/rayQueryShadows/code/main/rayQueryShadows.cpp +++ /dev/null @@ -1,2702 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "rayQueryShadows.hpp" -#include "camera/cameraController.hpp" -#include "camera/cameraControllerTouch.hpp" -#include "gui/imguiVulkan.hpp" -#include "main/applicationEntrypoint.hpp" -#include "material/computable.hpp" -#include "material/drawable.hpp" -#include "material/material.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" -#include "memory/memoryManager.hpp" -#include "memory/vulkan/bufferObject.hpp" -#include "memory/vulkan/drawIndirectBufferObject.hpp" -#include "memory/vulkan/indexBufferObject.hpp" -#include "memory/vulkan/vertexBufferObject.hpp" -#include "mesh/meshHelper.hpp" -#include "mesh/instanceGenerator.hpp" -#include "system/math_common.hpp" -#include "texture/textureManager.hpp" -#include "vulkan/vulkan.hpp" -#include "vulkan/extensionHelpers.hpp" -#include "vulkan/TextureFuncts.h" -#include "vulkan/timerSimple.hpp" -#include "vulkanRT/vulkanRT.hpp" -#include "vulkanRT/sceneRT.hpp" -#include "vulkanRT/meshUpdateRT.hpp" -#include "imgui.h" - -#include - -#include "vulkanRT/meshObjectRT.hpp" - - -// Global Variables From Config File -VAR(bool, gRenderHud, true, kVariableNonpersistent); -VAR(bool, gRenderShadow, true, kVariableNonpersistent); -VAR(bool, gAdvancedMode, true, kVariableNonpersistent); // Show full gui and enable stat output - -VAR(glm::vec3, gCameraStartPos, glm::vec3(-0.0f, 15.0f, 40.0f), kVariableNonpersistent); -VAR(glm::vec3, gCameraStartRot, glm::vec3(0.0f, 0.0f, 0.0f), kVariableNonpersistent); // in degrees - -// VAR(glm::vec3, gShadowPosition, glm::vec3(0.f, 40.0f, 60.0f), kVariableNonpersistent); // Position of 'main' ray queried shadow -// VAR(glm::vec3, gShadowPosition, glm::vec3(0.35f, 0.0f, 70.0f), kVariableNonpersistent); // Position of 'main' ray queried shadow -VAR(glm::vec3, gShadowPosition, glm::vec3(22.35f, 0.0f, 70.0f), kVariableNonpersistent); // Position of 'main' ray queried shadow - -VAR(float, gShadowRadius, 300.0f, kVariableNonpersistent); // Calculated Radius of the shadow(debug) -VAR(float, gShadowRadiusCutoff, 0.5f, kVariableNonpersistent); // Amount to multiply shadow radius by when generating the acceleration structure containing possible shadow casters (reduces the complexity of the scene we are tracing through) -VAR(bool, gShadowDirectionalLight, false, kVariableNonpersistent); // Use a directional light when running ray query (uses same light direction as the gRasterizedShadow option) -VAR(float, gLightEmitterRadius, 300.0f, kVariableNonpersistent); // Size of the light emitter -VAR(float, gLightAttenuationCutoff, 0.3f, kVariableNonpersistent); // Minimum Light intensity (used to calculate light/shadow radius) -VAR( bool, gCreateCulledAccelerationStructure, true, kVariableNonpersistent ); // Enable/disable creation of the acceleration structure that is culled (can only trace the entire scene when off) -VAR(bool, gDisableAccelerationStructureCull, false, kVariableNonpersistent ); // Enable/disable culling based on radius (just trace against entire scene when off) -VAR(bool, gForceAccelerationStructureRegen, false, kVariableNonpersistent ); // Regenerate the top level acceleration structure(s) every frame when enabled (do only on chenges if disabled) -VAR(bool, gUpdateAccelerationStructureInPlace, true, kVariableNonpersistent); // For updated Acceleration Structures determine if they should be updated in-place (single AS) or ping-pong between two AS -VAR(bool, gAdditionalShadows, true, kVariableNonpersistent); // Enable lights/shadows run in additional fragment shader passes (renders a sphere for each 'additional' light and raytraces shadows for all gbuffer pixels touched by the light) -VAR(bool, gRayQueryFragmentShader, false, kVariableNonpersistent); // Run the main light ray query on fragment shader -VAR(bool, gAnimateBLAS, true, kVariableNonpersistent); // Run compute animation that updates some of the bottom level acceleration structures every frame -VAR(bool, gBatchUpdateBLAS, false, kVariableNonpersistent); // Batch the updating of the BLAS into a single cmd (rather than cmd per updated object). Needs gAnimateBLAS=true - -VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); -VAR(float, gNearPlane, 1.0f, kVariableNonpersistent); -VAR(float, gFarPlane, 1050.0f, kVariableNonpersistent); - -VAR(bool, gRasterizedShadow, false, kVariableNonpersistent); // Use rasterized (depth buffer) rather than ray queried shadows -VAR(bool, gRasterizedShadowCulled, true, kVariableNonpersistent); // -VAR(glm::vec3, gRasterizedShadowPosition, glm::vec3(300.f, 1047.f, 900.f), kVariableNonpersistent); -VAR(glm::vec3, gRasterizedShadowTarget, glm::vec3(200.f, 24.f, 330.f), kVariableNonpersistent); -VAR(float, gRasterizedShadowFarPlane, 2500.f, kVariablePermanent); - -VAR(float, gSpecularExponent, 50.0f, kVariableNonpersistent); -VAR(float, gSpecularScale, 1.2f, kVariableNonpersistent); -VAR(float, gIrradianceAmount, 0.2f, kVariableNonpersistent); -VAR(float, gIrradianceMip, 1.0f, kVariableNonpersistent); -VAR(float, gNormalAmount, 1.0f, kVariableNonpersistent); -VAR(float, gNormalMirrorReflectAmount, 0.05f, kVariableNonpersistent); - -VAR(glm::vec4, gClearColor, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), kVariableNonpersistent); - -VAR(char*, gSceneObjectName, "Media/Meshes/ThaiCarving.gltf", kVariableNonpersistent); -VAR(char*, gAnimatedMaterialName, "Foliage_Paris_Flowers_BaseColor", kVariableNonpersistent); - -static const char* const sRenderPassNames[NUM_RENDER_PASSES] = { "RP_GBUFFER", "RP_RAYSHADOW", "RP_RASTERSHADOW", "RP_LIGHT", "RP_HUD", "RP_BLIT" }; -static_assert(RP_GBUFFER == 0, "Check order of sRenderPassNames"); -static_assert(RP_BLIT == 5, "Check order of sRenderPassNames"); -static_assert(NUM_RENDER_PASSES == sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0]), "mismatched sRenderPassNames"); - -// Helper -class TestMeshAnimator final : public MeshUpdateRT -{ - TestMeshAnimator(const TestMeshAnimator&) = delete; - TestMeshAnimator& operator=(const TestMeshAnimator&) = delete; -public: - TestMeshAnimator(TestMeshAnimator&&) noexcept; - TestMeshAnimator(); - - bool Create(Vulkan& vulkan, const MeshObjectIntermediate& meshObject, const AccelerationStructureUpdateable* blas, bool createScratch); - VkBuffer GetOriginalVertexVkBuffer() const { return m_OriginalVertices.GetVkBuffer(); } - - void UpdateAS(VulkanRT& vulkanRT, VkCommandBuffer cmdBuffer) const { MeshUpdateRT::UpdateAS(vulkanRT, cmdBuffer, *m_BLAS); } - void PrepareUpdateASData(VkAccelerationStructureBuildGeometryInfoKHR& asBuildGeometryInfo, VkAccelerationStructureGeometryKHR& asGeometry, VkAccelerationStructureBuildRangeInfoKHR& asBuildRangeInfo) const { MeshUpdateRT::PrepareUpdateASData(asBuildGeometryInfo, asGeometry, asBuildRangeInfo, *m_BLAS); } - -protected: - BufferT m_OriginalVertices; - const AccelerationStructureUpdateable* m_BLAS = nullptr; -}; - -TestMeshAnimator::TestMeshAnimator() : MeshUpdateRT() -{ -} - -TestMeshAnimator::TestMeshAnimator(TestMeshAnimator&& other) noexcept - : MeshUpdateRT(std::move(other)) - , m_OriginalVertices(std::move(other.m_OriginalVertices)) - , m_BLAS(other.m_BLAS) -{ - other.m_BLAS = nullptr; -} - -bool TestMeshAnimator::Create(Vulkan& vulkan, const MeshObjectIntermediate& meshObject, const AccelerationStructureUpdateable* blas, bool createScratch) -{ - if (!MeshUpdateRT::Create(vulkan, meshObject, createScratch ? blas->GetUpdateScratchSize() : 0)) - return false; - - m_BLAS = blas; - m_OriginalVertices = MeshObjectRT::CreateRtVertexBuffer(vulkan.GetMemoryManager(), meshObject, BufferUsageFlags::Storage); - return true; -} - - -// -// Implementation of the Application entrypoint (called by the framework) -// Construct the Application class -// -//----------------------------------------------------------------------------- -FrameworkApplicationBase* Application_ConstructApplication() -//----------------------------------------------------------------------------- -{ - return new Application(); -} - -//----------------------------------------------------------------------------- -Application::Application() -//----------------------------------------------------------------------------- - : ApplicationHelperBase() - , m_bEncodeSRGB(false) - , m_TotalDrawCalls(0) - , m_TotalTriangles(0) - , m_vulkanRT(*GetVulkan()) -{ - // Render passes - m_RenderPass.fill(VK_NULL_HANDLE); -} - -//----------------------------------------------------------------------------- -Application::~Application() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - // Semaphores - if (m_BlitCompleteSemaphore != VK_NULL_HANDLE) - vkDestroySemaphore(pVulkan->m_VulkanDevice, m_BlitCompleteSemaphore, NULL); - m_BlitCompleteSemaphore = VK_NULL_HANDLE; - - // Passes - m_RenderPass[RP_RASTERSHADOW] = VK_NULL_HANDLE; // shadow pass owned by m_Shadows. - for (auto& pass : m_RenderPass) - { - if (pass != VK_NULL_HANDLE) - vkDestroyRenderPass(pVulkan->m_VulkanDevice, pass, nullptr); - pass = VK_NULL_HANDLE; - } - - // Compute - - // Textures - ReleaseTexture(*pVulkan, &m_ShadowRayQueryComputeOutput); - - for (auto& uniform : m_AdditionalDeferredLightsSharedUniform) - ReleaseUniformBuffer(pVulkan, uniform); - - for(auto& updatable : m_sceneAnimatedMeshObjects) - updatable.Destroy(*pVulkan, m_vulkanRT); -} - -//----------------------------------------------------------------------------- -int Application::PreInitializeSelectSurfaceFormat(std::span formats) -//----------------------------------------------------------------------------- -{ - // On Snapdragon if the surfaceflinger has to do the rotation to the display native orientation then it will do it at 8bit colordepth. - // To avoid this we need to enable the 'pre-rotation' of the display (and the use of VK_QCOM_render_pass_transform so we dont have to rotate our buffers/passes manually). - GetVulkan()->m_UseRenderPassTransform = true; - - // We want to select a SRGB output format (if one exists) unless running on hlm (does not support srgb output) - TextureFormat idealFormat = gRunOnHLM ? TextureFormat::B8G8R8A8_UNORM : TextureFormat::B8G8R8A8_SRGB; - int index = 0; - for (const auto& format : formats) - { - if (format.format == idealFormat) - return index; - ++index; - } - return -1; -} - -//----------------------------------------------------------------------------- -void Application::PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& appConfig) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::PreInitializeSetVulkanConfiguration( appConfig ); - m_vulkanRT.RegisterRequiredVulkanLayerExtensions(appConfig, true/*query only*/); - appConfig.RequiredExtension(); - appConfig.RequiredExtension(); - appConfig.RequiredExtension(); -} - -//----------------------------------------------------------------------------- -bool Application::Initialize(uintptr_t windowHandle, uintptr_t instanceHandle) -//----------------------------------------------------------------------------- -{ - if (!ApplicationHelperBase::Initialize(windowHandle, instanceHandle)) - { - return false; - } - auto* pVulkan = GetVulkan(); - - // Camera - InitCamera(); - m_Camera.SetPosition(gCameraStartPos, glm::quat(gCameraStartRot * TO_RADIANS)); - m_Camera.SetFov(gFOV); - m_Camera.SetClipPlanes(gNearPlane, gFarPlane); - - // The Skybox - m_SkyboxScale = gFarPlane * 0.95f; - - // Set the current surface format - m_RequestedSurfaceFormat = {pVulkan->m_SurfaceFormat, pVulkan->m_SurfaceColorSpace}; - m_bEncodeSRGB = !FormatIsSrgb(pVulkan->m_SurfaceFormat); // if we have an srgb buffer then the output doesnt need to be encoded (hardware will do it for us) - - // Profiling - static constexpr uint32_t cMaxTimersPerFrame = 16; - m_GpuTimerPool = std::make_unique(*pVulkan); - m_GpuTimerPool->Initialize(cMaxTimersPerFrame); - - if (!LoadShaders()) - return false; - - if (!LoadTextures()) - return false; - - if (!CreateRenderTargets()) - return false; - - if (!InitShadowMap()) - return false; - - if (!InitLighting()) - return false; - - if (!InitUniforms()) - return false; - - if (!InitAllRenderPasses()) - return false; - - if (!InitMaterials()) - return false; - - if (!InitGui(windowHandle)) - return false; - - if (!LoadMeshObjects()) - return false; - - if (!VULKAN_RT_USE_STUB && !LoadRayTracingObjects()) - return false; - - if (!InitCommandBuffers()) - return false; - - if (!InitDrawables()) - return false; - - InitHdr(); - - if (!InitLocalSemaphores()) - return false; - - if (!BuildCmdBuffers()) - return false; - - LOGE("Initalize workers!!"); - - m_GameThreadWorker.Initialize("GameThreadWorker", 1); - - m_CompletedThreadOutput = {}; - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadMeshObjects() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - m_ShadowRayQueryComputeOutput = CreateTextureObject(*pVulkan, gRenderWidth, gRenderHeight, TextureFormat::R8_UNORM, TEXTURE_TYPE::TT_COMPUTE_TARGET, "ShadowRQDest"); - - // - // Create the 'Light' drawable mesh. - // Fullscreen quad that does the gbuffer lighting pass (fullscreen pass of gbuffer, outputting a lit color buffer). - LOGI("Creating Light mesh..."); - - // Lambda to populate textures requested by the material. - const auto textureLoader = - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { - if (texName == "Albedo") { - return { &m_GBufferRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Normal") { - return { &m_GBufferRT[0].m_ColorAttachments[1] }; - } - else if (texName == "Depth") { - return { &m_GBufferRT[0].m_DepthAttachment }; - } - else if (texName == "ShadowDepth") { - return { &m_Shadows[0].GetDepthTexture() }; - } - else if (texName == "ShadowVSM") { - return { m_ShadowVSM.GetVSMTexture() }; - } - else if (texName == "ShadowRT") { - if (gRayQueryFragmentShader) - return { &m_ShadowRT[0].m_ColorAttachments[0] }; - else - return { &m_ShadowRayQueryComputeOutput }; - } - else if (texName == "Environment" || texName == "Irradiance") - { - auto texture = m_TextureManager->GetTexture( texName ); - if (texture) - return { texture }; - // File not loaded, use default - return { m_TexWhite }; - } - assert(0); - return {}; - }; - - // Light pass using Ray Queried shadow texture - { - const auto* pLightShader = m_ShaderManager->GetShader("Light"); - assert(pLightShader); - auto lightShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pLightShader, NUM_VULKAN_BUFFERS, textureLoader, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - //LightFragCtrl - return { m_LightFragUniform.vkBuffers }; - }); - - Mesh lightMesh; - MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pLightShader->m_shaderDescription->m_vertexFormats, &lightMesh); - - m_LightDrawable = std::make_unique(*pVulkan, std::move(lightShaderMaterial)); - if (!m_LightDrawable->Init(m_RenderPass[RP_LIGHT], sRenderPassNames[RP_LIGHT], std::move(lightMesh))) - { - LOGE("Error Creating Light drawable..."); - } - } - - // Light pass using 'traditional' rasterized shadow - { - const auto* pLightShader = m_ShaderManager->GetShader("LightRasterizedShadows"); - assert(pLightShader); - auto lightShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pLightShader, NUM_VULKAN_BUFFERS, textureLoader, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - //LightFragCtrl - return { m_LightFragUniform.vkBuffers }; - }); - - MeshObject lightMesh; - MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pLightShader->m_shaderDescription->m_vertexFormats, &lightMesh); - - m_LightRasterizedShadowDrawable = std::make_unique(*pVulkan, std::move(lightShaderMaterial)); - if (!m_LightRasterizedShadowDrawable->Init(m_RenderPass[RP_LIGHT], sRenderPassNames[RP_LIGHT], std::move(lightMesh))) - { - LOGE("Error Creating Light drawable..."); - } - } - - // - // Create the 'Blit' drawable mesh. - // Fullscreen quad that does the final composite (hud and scene) for output. - LOGI("Creating Blit mesh..."); - - const auto* pBlitShader = m_ShaderManager->GetShader("Blit"); - assert(pBlitShader); - auto blitShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pBlitShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { - if (texName == "Diffuse") { - return { &m_MainRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Overlay") { - return { &m_HudRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Bloom") { - return { m_TexWhite }; - } - assert(0); - return {}; - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - //BlitFragCB - return { m_BlitFragUniform.buf.GetVkBuffer() }; - } - ); - - MeshObject blitMesh; - MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pBlitShader->m_shaderDescription->m_vertexFormats, &blitMesh); - - m_BlitDrawable = std::make_unique(*pVulkan, std::move(blitShaderMaterial)); - if (!m_BlitDrawable->Init( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], std::move(blitMesh))) - { - LOGE("Error Creating Blit drawable..."); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadRayTracingObjects() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - // Load the Geometry for Ray Tracing! and populate m_sceneRayTracable - if (!m_vulkanRT.Init()) - { - LOGE("Vulkan Ray Tracing Extension was not available...exiting"); - return false; - } - - std::vector candidateMeshes = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gSceneObjectName); - if( candidateMeshes.empty() ) - { - LOGE("Could not load the Ray Tracing mesh object: %s", gSceneObjectName); - return false; - } - - // (optionally) go through and find meshes that match each other (automated instancing). - auto instancedMeshes = MeshInstanceGenerator::FindInstances( std::move(candidateMeshes) ); - candidateMeshes.clear(); - - auto sceneRayTracable = std::make_unique(*pVulkan, m_vulkanRT); - - m_sceneObjectTriangleCounts.clear(); - - for (const auto& instancedMesh : instancedMeshes) - { - if( !instancedMesh.mesh.m_Materials.empty() && - (instancedMesh.mesh.m_Materials[0].transparent || !instancedMesh.mesh.m_Materials[0].emissiveFilename.empty()) ) - continue; - - // Find animated meshes (flowers!) - const bool updateable = instancedMesh.mesh.m_Materials[0].diffuseFilename.find(gAnimatedMaterialName) != std::string::npos; - auto updateMode = MeshObjectRT::UpdateMode::NotUpdatable; - if (updateable && gUpdateAccelerationStructureInPlace) - updateMode = MeshObjectRT::UpdateMode::InPlace; - else if (updateable && !gUpdateAccelerationStructureInPlace) - updateMode = MeshObjectRT::UpdateMode::PingPong; - - MeshObjectRT meshRayTracable; - if (!meshRayTracable.Create(*pVulkan, m_vulkanRT, instancedMesh.mesh, updateMode)) - return false; - const auto& id = sceneRayTracable->AddObject(std::move(meshRayTracable)); - if (!id) - return false; - for (const auto& instance : instancedMesh.instances) - { - sceneRayTracable->AddInstance(id, instance.transform); - } - m_sceneObjectTriangleCounts.emplace( id, instancedMesh.mesh.CalcNumTriangles() ); - - // Create the object that does the animation/update - if (updateable) - { - const MeshObjectRT* pMeshObjectRT = sceneRayTracable->FindObject(id); - assert(pMeshObjectRT); - - auto& updatableObject = m_sceneAnimatedMeshObjects.emplace_back(); - if (!updatableObject.Create(*pVulkan, instancedMesh.mesh, pMeshObjectRT, true/*always create a scratch per updateable, may not be used if we are batching updates together*/)) - { - assert(0); - m_sceneAnimatedMeshObjects.pop_back(); - } - } - } - - const auto blasUpdateMode = gUpdateAccelerationStructureInPlace ? MeshObjectRT::UpdateMode::InPlace : MeshObjectRT::UpdateMode::PingPong; - - if (gCreateCulledAccelerationStructure) - { - // Create the acceleration structure for Ray Tracing (this top level acceleration structure is updated depending on cpu culling). - auto sceneRayTracableCulled = std::make_unique( *pVulkan, m_vulkanRT ); - if (!sceneRayTracableCulled->CreateAccelerationStructure( blasUpdateMode, sceneRayTracable->GetNumKnownInstances() )) - { - LOGE( "Error Creating Scene AccelerationStructure..." ); - return false; - } - m_sceneRayTracableCulled = std::move( sceneRayTracableCulled ); - } - else - { - // Create the top level acceleration structure for just ray tracing the entire scene (no cpu culling) - sceneRayTracable->CreateAccelerationStructure(); - } - - m_sceneRayTracable = std::move(sceneRayTracable); - - // Create the Computable that runs the ShadowRQ shader (which does the ray queried shadows on the scene acceleration structure). - if (true)// ShadowRQ - { - const auto* pComputeShader = m_ShaderManager->GetShader("ShadowRQ"); - assert(pComputeShader); - - for (auto directionalLight : { false, true }) // make a computable for both light types - { - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pComputeShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - if (texName == "Output") - return { &m_ShadowRayQueryComputeOutput }; - else if (texName == "Depth") - return { &m_GBufferRT[0].m_DepthAttachment }; - else if (texName == "Normal") - return { &m_GBufferRT[0].m_ColorAttachments[1] }; - else { - assert(0); - return {}; - } - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_ShadowRQCtrlUniform.vkBuffers }; - }, - nullptr, - [this](const std::string& accelStructureName) -> MaterialPass::tPerFrameVkAccelerationStructure { - return { m_sceneRayTracableCulled ? m_sceneRayTracableCulled->GetVkAccelerationStructure() : m_sceneRayTracable->GetVkAccelerationStructure() }; - }, - [directionalLight](const std::string& specializationConstantName) -> const VertexElementData { - return VertexElementData{ VertexFormat::Element::ElementType::t::Boolean, directionalLight }; - }); - - auto computable = std::make_unique(*pVulkan, std::move(material)); - if (!computable->Init()) - { - LOGE("Error Creating ShadowRQ computable..."); - computable.reset(); - } - else - { - computable->SetDispatchGroupCount(0, { (m_ShadowRayQueryComputeOutput.Width + 31) / 32, (m_ShadowRayQueryComputeOutput.Height + 31) / 32,1 }); - } - if (directionalLight) - m_ShadowDirectionalLightRQComputable = std::move(computable); - else - m_ShadowPointLightRQComputable = std::move(computable); - } - } - - // Create the fullscreen drawable for ShadowRQFrag shader (which does the ray queried shadows on the scene acceleration structure). - if (true) - { - const auto* pShader = m_ShaderManager->GetShader("ShadowRQFrag"); - assert(pShader); - - for (auto directionalLight : { false, true }) // make a drawable for both light types - { - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - if (texName == "Output") - return { &m_ShadowRayQueryComputeOutput }; - else if (texName == "Depth") - return { &m_GBufferRT[0].m_DepthAttachment }; - else if (texName == "Normal") - return { &m_GBufferRT[0].m_ColorAttachments[1] }; - else { - assert(0); - return {}; - } - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_ShadowRQCtrlUniform.vkBuffers }; - }, - nullptr, - [this](const std::string& accelStructureName) -> MaterialPass::tPerFrameVkAccelerationStructure { - return { m_sceneRayTracableCulled ? m_sceneRayTracableCulled->GetVkAccelerationStructure() : m_sceneRayTracable->GetVkAccelerationStructure() }; - }, - [this, directionalLight](const std::string& specializationConstantName) -> const VertexElementData { - return VertexElementData{ VertexFormat::Element::ElementType::t::Boolean, directionalLight }; - }); - - MeshObject fullscreenMesh; - MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pShader->m_shaderDescription->m_vertexFormats, &fullscreenMesh); - - auto drawable = std::make_unique(*pVulkan, std::move(material)); - if (!drawable->Init(m_RenderPass[RP_RAYSHADOW], sRenderPassNames[RP_RAYSHADOW], std::move(fullscreenMesh))) - { - LOGE("Error Creating Shadow Ray Query drawable..."); - } - else - { - if (directionalLight) - m_ShadowDirectionalLightRQDrawable = std::move(drawable); - else - m_ShadowPointLightRQDrawable = std::move(drawable); - } - } - } - - // - // Setup the 'additional' lights (with Ray Traced shadows) - // - const size_t NumAdditionalLights = m_AdditionalDeferredLightsData.size(); - - const auto* pPointLightShader = m_ShaderManager->GetShader("PointLight"); - assert(pPointLightShader); - - m_AdditionalDeferredLightsSharedUniform.resize(NumAdditionalLights); - for( auto& uniformArray : m_AdditionalDeferredLightsSharedUniform ) - { - PointLightUB uniformData{}; - if (!CreateUniformBuffer(pVulkan, uniformArray, &uniformData)) - { - LOGE("Error Creating AdditionalDeferredLightsSharedUniform buffer..."); - return false; - } - } - - m_AdditionalDeferredLightDrawables.reserve(NumAdditionalLights); - m_AdditionalDeferredLightShadowRQ.reserve(NumAdditionalLights); - - for( int WhichLight=0; WhichLightGetNumKnownInstances() )) - { - LOGE( "Error Creating light shadow AccelerationStructure..." ); - return false; - } - - auto deferredLightMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pPointLightShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - if (texName == "Albedo") { - return { &m_GBufferRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Normal") { - return { &m_GBufferRT[0].m_ColorAttachments[1] }; - } - else if (texName == "Depth") { - return { &m_GBufferRT[0].m_DepthAttachment }; - } - else { - assert(0); - return {}; - } - }, - [this, WhichLight](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_AdditionalDeferredLightsSharedUniform[WhichLight].vkBuffers }; - }, - nullptr, - [this, WhichLight](const std::string& accelStructureName) -> MaterialPass::tPerFrameVkAccelerationStructure { - return { m_AdditionalDeferredLightShadowRQ[WhichLight].GetVkAccelerationStructure() }; - } - ); - auto& drawable = m_AdditionalDeferredLightDrawables.emplace_back( *pVulkan, std::move(deferredLightMaterial) ); - - const auto sphereMeshObjectIntermediate = MeshObjectIntermediate::LoadGLTF( *m_AssetManager, "./Media/Meshes/Skybox_Separate.gltf" ); - if( sphereMeshObjectIntermediate.empty() ) - { - LOGE( "Error loading light sphere..." ); - return false; - } - - MeshObject sphereMesh; - if (!MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), sphereMeshObjectIntermediate[0], 0, pPointLightShader->m_shaderDescription->m_vertexFormats, &sphereMesh)) - { - LOGE( "Error creating light sphere..." ); - return false; - } - - if( !drawable.Init( m_RenderPass[RP_LIGHT], sRenderPassNames[RP_LIGHT], std::move( sphereMesh ) ) ) - { - LOGE( "Error Initializing AdditionalDeferredLightDrawables..." ); - return false; - } - } - - // Create the computables that do the acceleration structure vertex updates (animated vertices). - { - const auto* pAnimateShader = m_ShaderManager->GetShader("AnimateBuffer"); - assert(pAnimateShader); - - m_sceneAnimatedMeshComputables.reserve(m_sceneAnimatedMeshObjects.size()); - - for (const auto& updatableObject : m_sceneAnimatedMeshObjects) - { - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pAnimateShader, 1, - nullptr, - [&updatableObject, this](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "InputVertexData") - return { updatableObject.GetOriginalVertexVkBuffer() }; - else if (bufferName == "OutputVertexData") - return { updatableObject.GetVertexVkBuffer() }; - else if (bufferName == "Uniform") - return { m_sceneAnimatedMeshUniform.vkBuffers }; - assert(0); - return {}; - }, - nullptr - ); - - auto& computable = m_sceneAnimatedMeshComputables.emplace_back(*pVulkan, std::move(material)); - if (!computable.Init()) - { - LOGE("Error Creating Scene Update computable..."); - m_sceneAnimatedMeshComputables.pop_back(); - } - else - { - computable.SetDispatchThreadCount(0, { updatableObject.GetNumVertices(), 1, 1 }); - } - } - } - - return true; -} - -//----------------------------------------------------------------------------- -void Application::Destroy() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - // Threads - m_GameThreadWorker.FinishAllWork(); - m_GameThreadWorker.Terminate(); - - pVulkan->WaitUntilIdle(); - - // Command buffers - for (auto& bufferArray : m_PassCmdBuffer) - for (auto& buffer : bufferArray) - buffer.Release(); - - // Meshes - m_SkyboxDrawable.reset(); - m_ShadowRasterizedDrawable.reset(); - m_ShadowPointLightRQDrawable.reset(); - m_ShadowDirectionalLightRQDrawable.reset(); - m_LightDrawable.reset(); - m_LightRasterizedShadowDrawable.reset(); - m_BlitDrawable.reset(); - m_SceneDrawables.clear(); - m_ShadowPointLightRQComputable.reset(); - m_ShadowDirectionalLightRQComputable.reset(); - m_ShadowRasterizedCullingComputable.reset(); - m_sceneRayTracableCulled.reset(); - m_sceneRayTracable.reset(); - m_AdditionalDeferredLightDrawables.clear(); - m_AdditionalDeferredLightShadowRQ.clear(); - m_ShadowVSM.Release(*pVulkan); - - // Scene - - // Uniform Buffers - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - ReleaseUniformBuffer(pVulkan, m_ObjectVertUniform[WhichPass]); - ReleaseUniformBuffer(pVulkan, m_ObjectFragUniform[WhichPass]); - ReleaseUniformBuffer(pVulkan, m_SkyboxVertUniform[WhichPass]); - } // WhichPass - - ReleaseUniformBuffer(pVulkan, m_ShadowRQCtrlUniform); - ReleaseUniformBuffer(pVulkan, &m_BlitFragUniform); - ReleaseUniformBuffer(pVulkan, m_LightFragUniform); - ReleaseUniformBuffer(pVulkan, m_ShadowInstanceCullingCameraUniform); - ReleaseUniformBuffer(pVulkan, &m_ShadowInstanceCullingBuffer); - m_ShadowInstanceCulledData.Destroy(); - - for (auto& uniform : m_AdditionalDeferredLightsSharedUniform) - ReleaseUniformBuffer(pVulkan, uniform); - m_AdditionalDeferredLightsSharedUniform.clear(); - - ReleaseUniformBuffer(pVulkan, m_sceneAnimatedMeshUniform); - - if (m_GpuTimerPool) - m_GpuTimerPool->Destroy(); - m_GpuTimerPool.reset(); - - // Finally call into base class destroy - ApplicationHelperBase::Destroy(); -} - -//----------------------------------------------------------------------------- -void Application::RunGameThread(const GameThreadInputParam& rThreadParam, GameThreadOutputParam& rThreadOutput) -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - Vulkan* pVulkan = GetVulkan(); - uint32_t WhichFrame = rThreadParam.CurrentBuffer.idx; - - // Clean up command buffers from last run - m_RenderPassSubmitData[WhichFrame].clear(); - - // take the gpu timer mutex (as we read back the timers and will also be modifying cpu timer data in this thread, and dont want to conflict with another thread doing logging) - std::lock_guard timerPoolLock(m_GpuTimerPoolMutex); - - // Read back timers - UpdateProfiling(WhichFrame); - - // If anything changes with the lights (Position, orientation, color, brightness, etc.) - UpdateLighting(rThreadParam.ElapsedTime); - - UpdateShadowMap(rThreadParam.ElapsedTime); - - // Update uniform buffers with latest data - UpdateUniforms(WhichFrame, rThreadParam.ElapsedTime); - - // Set where we are reading the shadow from. - if (gRayQueryFragmentShader) - m_LightDrawable->GetMaterial().UpdateDescriptorSetBinding(WhichFrame, "Shadow", m_ShadowRT[0].m_ColorAttachments[0] ); - else - m_LightDrawable->GetMaterial().UpdateDescriptorSetBinding(WhichFrame, "Shadow", m_ShadowRayQueryComputeOutput ); - - m_LightCmdBuffer[WhichFrame].Reset(); - m_LightCmdBuffer[WhichFrame].Begin( m_MainRT[0].m_FrameBuffer, m_MainRT.m_RenderPass ); - - VkRect2D Scissor = {}; - Scissor.extent.width = m_PassSetup[RP_LIGHT].TargetWidth; - Scissor.extent.height = m_PassSetup[RP_LIGHT].TargetHeight; - VkViewport Viewport = { - .width = (float)Scissor.extent.width, - .height = (float)Scissor.extent.height, - .minDepth = 0.0f, - .maxDepth = 1.0f - }; - vkCmdSetViewport( m_LightCmdBuffer[WhichFrame].m_VkCommandBuffer, 0, 1, &Viewport ); - vkCmdSetScissor( m_LightCmdBuffer[WhichFrame].m_VkCommandBuffer, 0, 1, &Scissor ); - if (gRasterizedShadow) - AddDrawableToCmdBuffers(*m_LightRasterizedShadowDrawable.get(), &m_LightCmdBuffer[WhichFrame], 1, 1, WhichFrame); - else - AddDrawableToCmdBuffers( *m_LightDrawable.get(), &m_LightCmdBuffer[WhichFrame], 1, 1, WhichFrame ); - m_LightCmdBuffer[WhichFrame].End(); - - // First pass, waits for the back buffer to be ready. - std::span pBackbufferWaitSemaphores = { &rThreadParam.CurrentBuffer.semaphore, 1 }; - // Blit pass, signals that the last submit is done - std::span pBlitCompleteSemaphore = { &m_BlitCompleteSemaphore, 1 }; - - static const VkPipelineStageFlags DefaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - - int gpuFrameTimerId; - { - // GBuffer render pass (gbuffer 'laydown' and shadow generation). - auto& renderPassLocal = BeginCommandBuffer( RP_GBUFFER, WhichFrame, pBackbufferWaitSemaphores, DefaultGfxWaitDstStageMasks, { }, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - gpuFrameTimerId = renderPassLocal.CmdBuff.StartGpuTimer("GPU Frame"); - BeginRenderPass( renderPassLocal ); - AddPassCommandBuffers( renderPassLocal ); - EndRenderPass( renderPassLocal ); - renderPassLocal.CmdBuff.End(); - } - - if (gRenderShadow) - { - if (gRasterizedShadow) - { - // Shadow 'depth laydown' pass - auto& renderPassLocal = BeginCommandBuffer(RP_RASTERSHADOW, WhichFrame, {}, {}, {}, VK_SUBPASS_CONTENTS_INLINE); - - if (gRasterizedShadowCulled) - { - // If we are doing rasterized shadow culling then we need to add that computable! - AddComputableToCmdBuffer(*m_ShadowRasterizedCullingComputable, renderPassLocal.CmdBuff); - - // Barrier to ensure compute finished before rasterizing the shadows. - VkMemoryBarrier barrier{ VK_STRUCTURE_TYPE_MEMORY_BARRIER }; - barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; - vkCmdPipelineBarrier(renderPassLocal.CmdBuff.m_VkCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr); - } - - BeginRenderPass(renderPassLocal); - gpuFrameTimerId = renderPassLocal.CmdBuff.StartGpuTimer("Rasterized Shadows"); - AddPassCommandBuffers(renderPassLocal); - renderPassLocal.CmdBuff.StopGpuTimer(gpuFrameTimerId); - EndRenderPass(renderPassLocal); - - // Execute shadow sampling (VSM) compute shaders - m_ShadowVSM.AddComputableToCmdBuffer(renderPassLocal.CmdBuff); - - renderPassLocal.CmdBuff.End(); - } - else - { - auto& renderPassLocal = BeginCommandBuffer(RP_RAYSHADOW, WhichFrame, {}, {}, {}, VK_SUBPASS_CONTENTS_INLINE); - - VkCommandBuffer vkCommandBuffer = renderPassLocal.CmdBuff.m_VkCommandBuffer; - - // Run any animation Computables - bool forceASUpdate = false; - if (gAnimateBLAS && !m_sceneAnimatedMeshComputables.empty()) - { - for (const auto& computable : m_sceneAnimatedMeshComputables) - { - AddComputableToCmdBuffer(computable, renderPassLocal.CmdBuff); - } - - // Barrier to ensure compute has written all the buffers before building BLAS. - VkMemoryBarrier barrier{ VK_STRUCTURE_TYPE_MEMORY_BARRIER }; - barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; - vkCmdPipelineBarrier(vkCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); - - // If anything is animated in the overall scene force an update of the culled Top Level AS. Potentially this is overkill (does not take into account which objects are culled). - forceASUpdate = true; - } - - auto asUpdateTimerId = renderPassLocal.CmdBuff.StartGpuTimer("Acceleration Structure Update (total)"); - - // Build/update all the updateable bottom level acceleration structures - if (forceASUpdate && !m_sceneAnimatedMeshObjects.empty()) - { - char timerName[64]; - sprintf(timerName, "Animated BLAS Updates (%d updates)", (int)m_sceneAnimatedMeshObjects.size()); - auto blasUpdateTimerId = renderPassLocal.CmdBuff.StartGpuTimer(timerName); - - if (gBatchUpdateBLAS) - { - // Batch together the BLAS updates into one Vulkan command - std::vector< VkAccelerationStructureBuildGeometryInfoKHR> blasUpdateBuildGeometryInfos; - std::vector< VkAccelerationStructureGeometryKHR> blasUpdateGeometries; - std::vector< VkAccelerationStructureBuildRangeInfoKHR> blasUpdateBuildRangeInfos; - std::vector pBlasUpdateBuildRangeInfos; - - const uint32_t numAnimatedBlas = (uint32_t)m_sceneAnimatedMeshObjects.size(); - blasUpdateBuildGeometryInfos.reserve(numAnimatedBlas); - blasUpdateGeometries.reserve(numAnimatedBlas); - blasUpdateBuildRangeInfos.reserve(numAnimatedBlas); - pBlasUpdateBuildRangeInfos.reserve(numAnimatedBlas); - - for (const auto& updateableBottomLevelObjectIt : m_sceneAnimatedMeshObjects) - { - updateableBottomLevelObjectIt.PrepareUpdateASData(blasUpdateBuildGeometryInfos.emplace_back(), blasUpdateGeometries.emplace_back(), blasUpdateBuildRangeInfos.emplace_back()); - pBlasUpdateBuildRangeInfos.push_back(&blasUpdateBuildRangeInfos.back()); - } - m_vulkanRT.vkCmdBuildAccelerationStructuresKHR(vkCommandBuffer, numAnimatedBlas, blasUpdateBuildGeometryInfos.data(), pBlasUpdateBuildRangeInfos.data()); - } - else - { - // Do each BLAS update as a seperate command - for (const auto& updateableBottomLevelObjectIt : m_sceneAnimatedMeshObjects) - { - updateableBottomLevelObjectIt.UpdateAS(m_vulkanRT, vkCommandBuffer); - } - } - - renderPassLocal.CmdBuff.StopGpuTimer(blasUpdateTimerId); - - // Barrier to ensure BLAS are all updated before updating or building TLAS. - VkMemoryBarrier barrier{ VK_STRUCTURE_TYPE_MEMORY_BARRIER }; - barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; - vkCmdPipelineBarrier(vkCommandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); - } - - // Build the shadow acceleration structures (if requested to). - auto tlasUpdateTimerId = renderPassLocal.CmdBuff.StartGpuTimer("Main TLAS Update"); - if (m_sceneRayTracableCulled) - m_sceneRayTracableCulled->UpdateAS(vkCommandBuffer, forceASUpdate); - renderPassLocal.CmdBuff.StopGpuTimer(tlasUpdateTimerId); - - // Build the acceleration structures (if requested to) for the 'additional' lights. - if (gAdditionalShadows) - { - for (uint32_t WhichLight = 0; WhichLight < m_AdditionalDeferredLightShadowRQ.size(); ++WhichLight) - { - char timerName[64]; - sprintf(timerName, "AdditionalLight %u TLAS Update", WhichLight + 1); - auto tlasUpdateTimerId = renderPassLocal.CmdBuff.StartGpuTimer(timerName); - auto& sceneRT = m_AdditionalDeferredLightShadowRQ[WhichLight]; - sceneRT.UpdateAS(renderPassLocal.CmdBuff.m_VkCommandBuffer); - renderPassLocal.CmdBuff.StopGpuTimer(tlasUpdateTimerId); - } - } - - // Finish up the timer around all the Acceleration Structure updates - renderPassLocal.CmdBuff.StopGpuTimer(asUpdateTimerId); - - if (gRayQueryFragmentShader) - { - auto timerId = renderPassLocal.CmdBuff.StartGpuTimer("ShadowRayQuery Frag"); - BeginRenderPass(renderPassLocal); - if (gShadowDirectionalLight) - AddDrawableToCmdBuffers(*m_ShadowDirectionalLightRQDrawable, &renderPassLocal.CmdBuff, 1, 1, WhichFrame); - else - AddDrawableToCmdBuffers(*m_ShadowPointLightRQDrawable, &renderPassLocal.CmdBuff, 1, 1, WhichFrame); - EndRenderPass(renderPassLocal); - renderPassLocal.CmdBuff.StopGpuTimer(timerId); - } - else - { - // Execue the Shadow Ray Tracing commands on the main (graphics) queue. - auto timerId = renderPassLocal.CmdBuff.StartGpuTimer("ShadowRayQuery Compute"); - if (gShadowDirectionalLight) - AddComputableToCmdBuffer(*m_ShadowDirectionalLightRQComputable, &renderPassLocal.CmdBuff, 1, 1, m_GpuTimerPool.get()); - else - AddComputableToCmdBuffer(*m_ShadowPointLightRQComputable, &renderPassLocal.CmdBuff, 1, 1, m_GpuTimerPool.get()); - renderPassLocal.CmdBuff.StopGpuTimer(timerId); - } - renderPassLocal.CmdBuff.End(); - } - } - - { - // Lighting pass (Gbuffer resolve and emissives) - auto& renderPassLocal = BeginRenderPass(RP_LIGHT, WhichFrame, {}, {}, {}, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - // Add all the lights and anything else rendered to the light render pass (eg emissives) - AddPassCommandBuffers( renderPassLocal ); - EndRenderPass( renderPassLocal ); - renderPassLocal.CmdBuff.End(); - } - - VkCommandBuffer guiCommandBuffer = VK_NULL_HANDLE; - if (gRenderHud && m_Gui) - { - // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(WhichFrame, m_HudRT[0].m_FrameBuffer); - if (guiCommandBuffer != VK_NULL_HANDLE) - { - auto& renderPassLocal = BeginRenderPass(RP_HUD, WhichFrame, {}, {}, {}, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - AddPassCommandBuffers( renderPassLocal, { &guiCommandBuffer,1 } ); - EndRenderPass( renderPassLocal ); - renderPassLocal.CmdBuff.End(); - } - } - - // Blit Results to the screen - { - auto& renderPassLocal = BeginRenderPass(RP_BLIT, WhichFrame, {}, {}, pBlitCompleteSemaphore, VK_SUBPASS_CONTENTS_INLINE); - AddDrawableToCmdBuffers(*m_BlitDrawable, &renderPassLocal.CmdBuff, 1, 1, WhichFrame); - EndRenderPass( renderPassLocal ); - renderPassLocal.CmdBuff.StopGpuTimer( gpuFrameTimerId ); // end of frame - m_GpuTimerPool->ReadResults( renderPassLocal.CmdBuff.m_VkCommandBuffer, WhichFrame ); - renderPassLocal.CmdBuff.End(); - } - - rThreadOutput.Fence = rThreadParam.CurrentBuffer.fence; - rThreadOutput.SwapchainPresentIndx = rThreadParam.CurrentBuffer.swapchainPresentIdx; - rThreadOutput.WhichFrame = WhichFrame; -} - -//----------------------------------------------------------------------------- -void Application::Render(float fltDiffTime) -//----------------------------------------------------------------------------- -{ - // Reset Metrics - m_TotalDrawCalls = 0; - m_TotalTriangles = 0; - - static bool positive = false; - if (positive) - { - //gShadowPosition.x += fltDiffTime * 30.0f; - - if (std::abs(gShadowPosition.x) > 40) - { - positive = false; - } - } - else - { - //gShadowPosition.x -= fltDiffTime * 30.0f; - - if (std::abs(gShadowPosition.x) > 40) - { - positive = true; - } - } - - - - // Grab the vulkan wrapper - Vulkan* pVulkan = GetVulkan(); - - // See if we want to change backbuffer surface format - if (m_RequestedSurfaceFormat.colorSpace != pVulkan->m_SurfaceColorSpace || m_RequestedSurfaceFormat.format != pVulkan->m_SurfaceFormat) - { - ChangeSurfaceFormat(m_RequestedSurfaceFormat); - } - - // Obtain the next swap chain image for the next frame. - auto CurrentVulkanBuffer = pVulkan->SetNextBackBuffer(); - - // - // Run any Updates that do not modify GPU resources. - // - - UpdateCamera(fltDiffTime); - - // Wait for game thread to finish... - m_GameThreadWorker.FinishAllWork(); - - // - // We are running single-threaded here... - // - - UpdateGui(); - - if (gFramesToRender == 0) - { - // - // Runing 'forever'. - // - // Do rendering 'threaded'. - // - - // ... grab info from the last run ... - int FrameToSubmit = m_CompletedThreadOutput.WhichFrame; - int SwapchainPresentIndx = m_CompletedThreadOutput.SwapchainPresentIndx; - VkFence FrameToRenderFence = m_CompletedThreadOutput.Fence; - - // Start thread updating the next frame. - GameThreadInputParam gameThreadParam{ CurrentVulkanBuffer, fltDiffTime }; - - m_GameThreadWorker.DoWork2( [](Application* pThis, Application::GameThreadInputParam ThreadParam) { - pThis->RunGameThread(ThreadParam, pThis->m_CompletedThreadOutput); - }, this, std::move(gameThreadParam)); - - // Submit and present thread work from last frame (potentially while the thread worker is generating the next frame). - if (FrameToSubmit >= 0) - { - auto finishedSemaphore = SubmitGameThreadWork(FrameToSubmit, FrameToRenderFence); - PresentQueue(finishedSemaphore, SwapchainPresentIndx); - } - } - else - { - // - // Running a (pre)defined number of frames. - // - // Run syncronously so that we render the exact number of requested frames. - // - GameThreadInputParam gameThreadParam{ CurrentVulkanBuffer, fltDiffTime }; - RunGameThread(gameThreadParam, m_CompletedThreadOutput); - - // Submit and present this frame - auto finishedSemaphore = SubmitGameThreadWork(m_CompletedThreadOutput.WhichFrame, m_CompletedThreadOutput.Fence); - PresentQueue(finishedSemaphore, m_CompletedThreadOutput.SwapchainPresentIndx); - } - - // ******************************** - // Application Draw() - End - // ******************************** -} - -//----------------------------------------------------------------------------- -bool Application::LoadTextures() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_ChangeExtension{ ".ktx" }); - - // Load 'loose' textures - m_TextureManager->GetOrLoadTexture("Environment" , *m_AssetManager, "./Media/Textures/simplesky_env.ktx", m_SamplerRepeat); - m_TextureManager->GetOrLoadTexture("Irradiance", *m_AssetManager, "./Media/Textures/simplesky_irradiance.ktx", m_SamplerRepeat); - //m_loadedTextures.emplace("Environment", *m_AssetManager, "./Media/Textures/simplesky_env.ktx", SamplerAddressMode::Repeat)); - //m_loadedTextures.emplace("Irradiance", *m_AssetManager, "./Media/Textures/.ktx", SamplerAddressMode::Repeat)); - - m_TexWhite = m_TextureManager->GetOrLoadTexture("White", *m_AssetManager, "./Media/Textures/white_d.ktx", m_SamplerRepeat); - m_DefaultNormal = m_TextureManager->GetOrLoadTexture("Normal", *m_AssetManager, "./Media/Textures/normal_default.ktx", m_SamplerRepeat); - - if (!m_TexWhite || !m_DefaultNormal) - { - return false; - } - - return true; -} - -bool Application::CreateRenderTargets() -//----------------------------------------------------------------------------- -{ - LOGI("******************************"); - LOGI("Creating Render Targets..."); - LOGI("******************************"); - - Vulkan* pVulkan = GetVulkan(); - - const TextureFormat DesiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); - - // Setup the GBuffer - const TextureFormat GbufferColorType[] = { TextureFormat::R8G8B8A8_SRGB/*Albedo*/, TextureFormat::R16G16B16A16_SFLOAT/*Normals*/ }; - - if (!m_GBufferRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, GbufferColorType, DesiredDepthFormat, VK_SAMPLE_COUNT_1_BIT, "GBuffer RT")) - { - LOGE("Unable to create gbuffer render target"); - } - - // Setup the 'shadow' (ray query result) buffer - const TextureFormat ShadowColorType[] = { TextureFormat::R8_UNORM }; - - if (!m_ShadowRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, ShadowColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "Shadow RT")) - { - LOGE("Unable to create shadow render target"); - } - - // Setup the 'main' (compositing) buffer - const TextureFormat MainColorType[] = { TextureFormat::R16G16B16A16_SFLOAT }; - - if (!m_MainRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, m_GBufferRT/*inherit depth*/, VK_SAMPLE_COUNT_1_BIT, "Main RT")) - { - LOGE("Unable to create main render target"); - } - - // Setup the HUD render target (no depth) - const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_UNORM }; - - if (!m_HudRT.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) - { - LOGE("Unable to create hud render target"); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitShadowMap() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - for (uint32_t i = 0; i < cNumShadows; ++i) - { - if (!m_Shadows[i].Initialize(*pVulkan, gShadowMapWidth, gShadowMapHeight, false)) - { - LOGE("Unable to create shadow (render target?)"); - return false; - } - if (gRasterizedShadowFarPlane > gFarPlane) - gRasterizedShadowFarPlane = gFarPlane; - m_Shadows[i].SetLightPos(gRasterizedShadowPosition, gRasterizedShadowTarget); - m_Shadows[i].SetEyeClipPlanes(m_Camera.Fov(), m_Camera.NearClip(), m_Camera.Aspect(), gRasterizedShadowFarPlane); - } - - if (!m_ShadowVSM.Initialize(*pVulkan, *m_ShaderManager, *m_MaterialManager, m_Shadows[0])) - { - LOGE("Unable to create shadow VSM"); - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitLighting() -//----------------------------------------------------------------------------- -{ - m_LightColor = { 1.0f, 0.98f, 0.73f, gSpecularExponent }; - - m_AdditionalDeferredLightsData.clear(); - m_AdditionalDeferredLightsData.push_back( { glm::vec3( 768.7, 440.f, 877.4), 1.0f, 10.0f } ); - m_AdditionalDeferredLightsData.push_back( { glm::vec3( -433.9f, 440.f, -420.f), 1.0f, 10.0f } ); - //Handled by the Light.frag pass: m_AdditionalDeferredLightsData.push_back( { glm::vec3( -211.f, 440.f, 470.f ), 1.0f, 10.0f } ); - m_AdditionalDeferredLightsData.push_back( { glm::vec3( -966, 440.f, 210 ), 1.0f, 10.0f } ); - m_AdditionalDeferredLightsData.push_back( { glm::vec3( -177.0, 440.f, 1000 ), 1.0f, 10.0f } ); - m_AdditionalDeferredLightsData.push_back( { glm::vec3( 375.f, 440.f, -2130.f ), 1.0f, 10.0f } ); - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadShaders() -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - - m_ShaderManager->RegisterRenderPassNames({ sRenderPassNames, sRenderPassNames + (sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0])) }); - - LOGI("Loading Shaders..."); - - typedef std::pair tIdAndFilename; - for (const tIdAndFilename& i : - { tIdAndFilename { "ObjectDeferred", "Media\\Shaders\\ObjectDeferred.json" }, - tIdAndFilename { "ObjectEmissive", "Media\\Shaders\\ObjectEmissive.json" }, - tIdAndFilename { "ObjectAnimated", "Media\\Shaders\\ObjectAnimated.json" }, - tIdAndFilename { "Skybox", "Media\\Shaders\\Skybox.json" }, - tIdAndFilename { "Light", "Media\\Shaders\\Light.json" }, - tIdAndFilename { "RasterizedShadowCull", "Media\\Shaders\\RasterizedShadowCull.json" }, - tIdAndFilename { "LightRasterizedShadows", "Media\\Shaders\\LightRasterizedShadows.json" }, - tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "ShadowRQ", "Media\\Shaders\\ShadowRQ.json" }, - tIdAndFilename { "ShadowRQFrag", "Media\\Shaders\\ShadowRQFrag.json" }, - tIdAndFilename { "VarianceShadowMap", "Media\\Shaders\\VarianceShadowMap.json" }, - tIdAndFilename { "PointLight", "Media\\Shaders\\PointLight.json" }, - tIdAndFilename { "AnimateBuffer", "Media\\Shaders\\AnimateBuffer.json" } - }) - { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) - { - LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitUniforms() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - // These are only created here, they are not set to initial values - LOGI("Creating uniform buffers..."); - - m_ObjectVertUniformData = {}; - - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - CreateUniformBuffer(pVulkan, m_ObjectVertUniform[WhichPass]); - CreateUniformBuffer(pVulkan, m_ObjectFragUniform[WhichPass]); - CreateUniformBuffer(pVulkan, m_SkyboxVertUniform[WhichPass]); - } - - // Streetlights (outdoor) - m_LightFragUniformData.PointLightPosition = glm::vec4(-211.f, 440.f, 470.f, 25.0f); - m_LightFragUniformData.PointLightColor = glm::vec4(1.0f, 0.8f, 0.7f, 1.0f); - m_LightFragUniformData.DirectionalLightDirection = glm::vec4( glm::normalize(gRasterizedShadowTarget - gRasterizedShadowPosition), 0.0f ); - m_LightFragUniformData.DirectionalLightColor = glm::vec4(0.9f, 0.8f, 1.0f, 1.0f); - m_LightFragUniformData.AmbientColor = glm::vec4(0.15f, 0.15f, 0.15f, 1.0f); - m_LightFragUniformData.PointLightRadius = gLightEmitterRadius; - m_LightFragUniformData.PointLightCutoff = gLightAttenuationCutoff; - m_LightFragUniformData.SpecScale = gSpecularScale; - m_LightFragUniformData.SpecPower = gSpecularExponent; - m_LightFragUniformData.irradianceAmount = gIrradianceAmount; - m_LightFragUniformData.irradianceMipLevel = gIrradianceMip; - m_LightFragUniformData.AmbientOcclusionScale = 1.0f; - - CreateUniformBuffer(pVulkan, m_ShadowRQCtrlUniform, &m_ShadowRQCtrl); - CreateUniformBuffer(pVulkan, m_BlitFragUniform, &m_BlitFragUniformData); - CreateUniformBuffer(pVulkan, m_LightFragUniform, &m_LightFragUniformData); - - m_sceneAnimatedMeshUniformData = {}; - CreateUniformBuffer(pVulkan, m_sceneAnimatedMeshUniform, &m_sceneAnimatedMeshUniformData); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::UpdateUniforms(uint32_t WhichBuffer, float ElapsedTime) -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - // Special View matrix for Skybox - glm::mat4 SkyboxViewMatrix = glm::mat4_cast(m_Camera.Rotation()); - glm::mat4 SkyboxMVP = m_Camera.ProjectionMatrix() * SkyboxViewMatrix; - - // ******************************** - // Object - // ******************************** - glm::mat4 ObjectVP = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix(); - - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // Skip passes which do not rasterize object geometry. - if (WhichPass != RP_RASTERSHADOW && WhichPass != RP_GBUFFER) - continue; - - switch (WhichPass) - { - case RP_RASTERSHADOW: - m_ObjectVertUniformData.VPMatrix = m_Shadows[0].GetViewProj(); - break; - case RP_GBUFFER: - m_ObjectVertUniformData.VPMatrix = ObjectVP; - break; - default: - LOGE("***** %s Not Handled! *****", GetPassName(WhichPass)); - break; - } - - if (gAnimateBLAS) - { - m_ObjectVertUniformData.AnimationRotation.x += ElapsedTime * 4.0f; - m_ObjectVertUniformData.AnimationRotation.y += ElapsedTime * 4.1f; - m_ObjectVertUniformData.AnimationRotation.z += ElapsedTime * 0.5f; - m_ObjectVertUniformData.AnimationRotation.w += ElapsedTime * 0.52f; - if (m_ObjectVertUniformData.AnimationRotation.x > PI_MUL_2) - m_ObjectVertUniformData.AnimationRotation.x -= PI_MUL_2; - if (m_ObjectVertUniformData.AnimationRotation.y > PI_MUL_2) - m_ObjectVertUniformData.AnimationRotation.y -= PI_MUL_2; - if (m_ObjectVertUniformData.AnimationRotation.z > PI_MUL_2) - m_ObjectVertUniformData.AnimationRotation.z -= PI_MUL_2; - if (m_ObjectVertUniformData.AnimationRotation.w > PI_MUL_2) - m_ObjectVertUniformData.AnimationRotation.w -= PI_MUL_2; - } - - UpdateUniformBuffer(pVulkan, m_ObjectVertUniform[WhichPass], m_ObjectVertUniformData, WhichBuffer); - - m_ObjectFragUniformData.Color = glm::vec4(1.0f,1.0f,1.0f, 1.0f); // White by default - - m_ObjectFragUniformData.NormalHeight = glm::vec4(gNormalAmount, gNormalMirrorReflectAmount, 0.0f, 0.0f); - - UpdateUniformBuffer(pVulkan, m_ObjectFragUniform[WhichPass], m_ObjectFragUniformData, WhichBuffer); - } - - // ******************************** - // Rayqueried Shadow - // ******************************** - m_ShadowRQCtrl.ScreenSize = glm::vec4((float)m_ShadowRayQueryComputeOutput.Width, (float)m_ShadowRayQueryComputeOutput.Height, 1.0f / (float)m_ShadowRayQueryComputeOutput.Width, 1.0f / (float)m_ShadowRayQueryComputeOutput.Height); - glm::mat4 CameraProjectionInv = glm::inverse(m_Camera.ProjectionMatrix()); - m_ShadowRQCtrl.ProjectionInv = CameraProjectionInv; - m_ShadowRQCtrl.ViewInv= glm::inverse(m_Camera.ViewMatrix()); - //m_ShadowRQCtrl.LightWorldPos = m_Shadows[0].GetLightPos(); // Rasterized shadow position - m_ShadowRQCtrl.LightWorldPos = gShadowDirectionalLight ? glm::vec4(gRasterizedShadowPosition, 1.0) : glm::vec4(gShadowPosition, 1.0); - m_ShadowRQCtrl.LightWorldDirection = glm::vec4(glm::normalize(gRasterizedShadowTarget - gRasterizedShadowPosition), 0.0f); - - UpdateUniformBuffer(pVulkan, m_ShadowRQCtrlUniform, m_ShadowRQCtrl, WhichBuffer); - - // ******************************** - // Animated meshes - // ******************************** - m_sceneAnimatedMeshUniformData.AnimationRotation = m_ObjectVertUniformData.AnimationRotation; - UpdateUniformBuffer(pVulkan, m_sceneAnimatedMeshUniform, m_sceneAnimatedMeshUniformData, WhichBuffer); - - // ******************************** - // Skybox - // ******************************** - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // No uniform buffers for HUD or BLIT since objects not in HUD and BLIT - if (WhichPass == RP_HUD || WhichPass == RP_BLIT) - continue; - - glm::mat4 LocalModel = glm::scale(glm::vec3(m_SkyboxScale, m_SkyboxScale, m_SkyboxScale)); - - // Special View matrix for Skybox (always centered on the view position) - SkyboxViewMatrix = glm::mat4_cast(m_Camera.Rotation()); - glm::mat4 SkyboxMVP = m_Camera.ProjectionMatrix() * SkyboxViewMatrix * LocalModel; - - m_SkyboxVertUniformData.MVPMatrix = SkyboxMVP; - m_SkyboxVertUniformData.ModelMatrix = LocalModel; - m_SkyboxVertUniformData.Color = glm::vec4(0.9f, 0.9f, 0.9f, 1.0f); // White by default - UpdateUniformBuffer(pVulkan, m_SkyboxVertUniform[WhichPass], m_SkyboxVertUniformData, WhichBuffer); - } - - // ******************************** - // Fullscreen Light pass - // ******************************** - - glm::mat4 CameraViewInv = glm::inverse(m_Camera.ViewMatrix()); - m_LightFragUniformData.ProjectionInv = CameraProjectionInv; - m_LightFragUniformData.ViewInv = CameraViewInv; - m_LightFragUniformData.WorldToShadow = m_Shadows[0].GetViewProj(); - m_LightFragUniformData.CameraPos = glm::vec4( m_Camera.Position(), 0.0f ); - - UpdateUniformBuffer(pVulkan, m_LightFragUniform, m_LightFragUniformData, WhichBuffer); - - // ******************************** - // 'Additional' Lights - // ******************************** - { - glm::mat4 LocalVP = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix(); - PointLightUB pointLightUniformData{}; - for(uint32_t WhichLight=0; WhichLight= m_AdditionalDeferredLightsSharedUniform.size()) - break; - - lightdata.Radius = gLightEmitterRadius * (sqrtf( lightdata.Intensity / gLightAttenuationCutoff ) - 1.0f); - - pointLightUniformData.MVPMatrix = LocalVP * glm::translate( glm::vec3( lightdata.Position ) ) * glm::scale( glm::vec3(lightdata.Radius, lightdata.Radius, lightdata.Radius) ); - pointLightUniformData.ProjectionInv = CameraProjectionInv; - pointLightUniformData.ViewInv = CameraViewInv; - - pointLightUniformData.LightPosition = lightdata.Position; - pointLightUniformData.LightIntensity = lightdata.Intensity; - pointLightUniformData.LightCutoff = gLightAttenuationCutoff; - pointLightUniformData.LightRadius = gLightEmitterRadius; - pointLightUniformData.LightColor = m_LightColor; - pointLightUniformData.SpecScale = m_LightFragUniformData.SpecScale; - pointLightUniformData.SpecPower = m_LightFragUniformData.SpecPower; - pointLightUniformData.WindowSize = glm::vec2( m_GBufferRT.m_RenderTargets[0].m_Width, m_GBufferRT.m_RenderTargets[0].m_Height ); - UpdateUniformBuffer(pVulkan, m_AdditionalDeferredLightsSharedUniform[WhichLight], pointLightUniformData, WhichBuffer); - } - } - - // ******************************** - // Rasterized shadow culling - // ******************************** - if (gRasterizedShadow && gRasterizedShadowCulled) - { - ShadowCullCameraCtrl rasterizedShadowCullCtrl{}; - auto projMtx = m_Shadows[0].GetProjection(); - rasterizedShadowCullCtrl.MVPMatrix = m_Shadows[0].GetViewProj(); - const ViewFrustum cameraFrustum(projMtx, m_Shadows[0].GetView()); - memcpy(rasterizedShadowCullCtrl.CullPlanes, cameraFrustum.GetPlanes(), sizeof(rasterizedShadowCullCtrl.CullPlanes)); - UpdateUniformBuffer(pVulkan, m_ShadowInstanceCullingCameraUniform, rasterizedShadowCullCtrl, WhichBuffer); - } - - - // ******************************** - // Blit - // ******************************** - - m_BlitFragUniformData.sRGB = m_bEncodeSRGB ? 1 : 0; - UpdateUniformBuffer(pVulkan, m_BlitFragUniform, m_BlitFragUniformData); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::BuildCmdBuffers() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - LOGI("******************************"); - LOGI("Building Command Buffers..."); - LOGI("******************************"); - - uint32_t TargetWidth = pVulkan->m_SurfaceWidth; - uint32_t TargetHeight = pVulkan->m_SurfaceHeight; - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // If viewport and scissor are dynamic we need to add them to the secondary buffers. - const uint32_t TargetWidth = m_PassSetup[WhichPass].TargetWidth; - const uint32_t TargetHeight = m_PassSetup[WhichPass].TargetHeight; - - VkViewport Viewport = {}; - Viewport.x = 0.0f; - Viewport.y = 0.0f; - Viewport.width = (float)TargetWidth; - Viewport.height = (float)TargetHeight; - Viewport.minDepth = 0.0f; - Viewport.maxDepth = 1.0f; - - VkRect2D Scissor = {}; - Scissor.offset.x = 0; - Scissor.offset.y = 0; - Scissor.extent.width = TargetWidth; - Scissor.extent.height = TargetHeight; - - for (uint32_t WhichBuffer = 0; WhichBuffer < pVulkan->m_SwapchainImageCount; WhichBuffer++) - { - // Set up some values that change based on render pass - VkRenderPass WhichRenderPass = VK_NULL_HANDLE; - VkFramebuffer WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - switch (WhichPass) - { - case RP_GBUFFER: - WhichRenderPass = m_GBufferRT.m_RenderPass; - WhichFramebuffer = m_GBufferRT[0].m_FrameBuffer; - break; - - case RP_RASTERSHADOW: - WhichRenderPass = m_Shadows[0].GetRenderPass(); - WhichFramebuffer = m_Shadows[0].GetFramebuffer(); - break; - - case RP_RAYSHADOW: - WhichRenderPass = m_ShadowRT.m_RenderPass; - WhichFramebuffer = m_ShadowRT[0].m_FrameBuffer; - break; - - case RP_LIGHT: - WhichRenderPass = m_MainRT.m_RenderPass; - WhichFramebuffer = m_MainRT[0].m_FrameBuffer; - break; - - case RP_HUD: - WhichRenderPass = m_HudRT.m_RenderPass; - WhichFramebuffer = m_HudRT[0].m_FrameBuffer; - break; - - case RP_BLIT: - WhichRenderPass = m_RenderPass[WhichPass]; - WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - break; - } - - if (WhichPass == RP_LIGHT) - { - // Main (primary) lighting pass applied to the gbuffer - if (!m_LightCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass)) - { - return false; - } - vkCmdSetViewport(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Scissor); - - // Additional lights applied to the gbuffer (additive after the m_LightCmdBuffer pass) - if (!m_AdditionalLightsCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass)) - { - return false; - } - vkCmdSetViewport(m_AdditionalLightsCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_AdditionalLightsCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Scissor); - } - - // Objects - can render into any pass (except Blit) - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Begin(WhichFramebuffer, WhichRenderPass)) - { - return false; - } - vkCmdSetViewport(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer, 0, 1, &Scissor); - } // Which Buffer - } // Which Pass - - // Run through the drawables (each one may be in multiple command buffers) - for (const auto& drawable : m_SceneDrawables) - { - AddDrawableToCmdBuffers(drawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, pVulkan->m_SwapchainImageCount); - } - // Add the 'combined' rasterized shadow drawable (monolithic indexed and instanced drawable) - if (m_ShadowRasterizedDrawable) - { - AddDrawableToCmdBuffers(*m_ShadowRasterizedDrawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, pVulkan->m_SwapchainImageCount); - } - // Add the skybox (last) - if (m_SkyboxDrawable) - { - AddDrawableToCmdBuffers(*m_SkyboxDrawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, pVulkan->m_SwapchainImageCount); - } - - // and end their pass - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - for (uint32_t WhichBuffer = 0; WhichBuffer < pVulkan->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].End()) - { - return false; - } - } - } - - // Add the main light - AddDrawableToCmdBuffers( *m_LightDrawable.get(), m_LightCmdBuffer, 1, pVulkan->m_SwapchainImageCount ); - - // Add the 'additional' lights - for(uint32_t additionalLightIndex = 0; additionalLightIndex < (uint32_t) m_AdditionalDeferredLightDrawables.size(); ++additionalLightIndex) - { - const auto& drawable = m_AdditionalDeferredLightDrawables[additionalLightIndex]; - char timerName[64]; - sprintf(timerName, "AdditionalLight %u RayQuery", additionalLightIndex+1 ); - for (uint32_t WhichBuffer = 0; WhichBuffer < pVulkan->m_SwapchainImageCount; ++WhichBuffer) - { - auto timerId = m_AdditionalLightsCmdBuffer[WhichBuffer].StartGpuTimer(timerName); - AddDrawableToCmdBuffers(drawable, &m_AdditionalLightsCmdBuffer[WhichBuffer], 1, 1); - m_AdditionalLightsCmdBuffer[WhichBuffer].StopGpuTimer(timerId); - } - } - for (uint32_t WhichBuffer = 0; WhichBuffer < pVulkan->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_LightCmdBuffer[WhichBuffer].End()) - { - return false; - } - if (!m_AdditionalLightsCmdBuffer[WhichBuffer].End()) - { - return false; - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitLocalSemaphores() -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - Vulkan* pVulkan = GetVulkan(); - - VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - RetVal = vkCreateSemaphore(pVulkan->m_VulkanDevice, &SemaphoreInfo, NULL, &m_BlitCompleteSemaphore); - if (!CheckVkError("vkCreateSemaphore()", RetVal)) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -const char* Application::GetPassName(uint32_t WhichPass) -//----------------------------------------------------------------------------- -{ - if (WhichPass >= sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0])) - { - LOGE("GetPassName() called with unknown pass (%d)!", WhichPass); - return "RP_UNKNOWN"; - } - return sRenderPassNames[WhichPass]; -} - -//----------------------------------------------------------------------------- -bool Application::InitMaterials() -//----------------------------------------------------------------------------- -{ - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitCommandBuffers() -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - char szName[256]; - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // The Pass Command Buffer => Primary - sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_PassCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY, Vulkan::eGraphicsQueue, m_GpuTimerPool.get())) - { - return false; - } - - // Model => Secondary - sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) - { - return false; - } - - } - } - - // Light => Secondary - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - sprintf( szName, "Light (Buffer %d of %d)", WhichBuffer + 1, NUM_VULKAN_BUFFERS ); - if (!m_LightCmdBuffer[WhichBuffer].Initialize( pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY, Vulkan::eGraphicsQueue, m_GpuTimerPool.get() )) - { - return false; - } - } - - // AdditionalLight => Secondary - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - sprintf(szName, "Additional Lights (%s; Buffer %d of %d)", GetPassName(RP_LIGHT), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_AdditionalLightsCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY, Vulkan::eGraphicsQueue, m_GpuTimerPool.get())) - { - return false; - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitAllRenderPasses() -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - Vulkan* pVulkan = GetVulkan(); - - uint32_t ShadowTargetWidth, ShadowTargetHeight; - m_Shadows[0].GetTargetSize(ShadowTargetWidth, ShadowTargetHeight); - - // Fill in the Setup data - m_PassSetup[RP_GBUFFER] = { m_GBufferRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, gClearColor,m_GBufferRT[0].m_Width, m_GBufferRT[0].m_Height }; - m_PassSetup[RP_RASTERSHADOW] = { {}, m_Shadows[0].GetDepthFormat(), RenderPassInputUsage::DontCare,true, RenderPassOutputUsage::Discard, RenderPassOutputUsage::StoreReadOnly,{}, ShadowTargetWidth, ShadowTargetHeight }; - m_PassSetup[RP_RAYSHADOW]={ m_ShadowRT[0].m_pLayerFormats, m_ShadowRT[0].m_DepthFormat, RenderPassInputUsage::DontCare,false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_ShadowRT[0].m_Width, m_ShadowRT[0].m_Height }; - m_PassSetup[RP_LIGHT] = { m_MainRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_MainRT[0].m_Width, m_MainRT[0].m_Height }; - m_PassSetup[RP_HUD] = { m_HudRT[0].m_pLayerFormats, m_HudRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_HudRT[0].m_Width, m_HudRT[0].m_Height }; - m_PassSetup[RP_BLIT] = { {pVulkan->m_SurfaceFormat}, pVulkan->m_SwapchainDepth.format, RenderPassInputUsage::DontCare,false, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}, pVulkan->m_SurfaceWidth, pVulkan->m_SurfaceHeight }; - - LOGI("******************************"); - LOGI("Initializing Render Passes... "); - LOGI("******************************"); - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - const auto& PassSetup = m_PassSetup[WhichPass]; - bool IsSwapChainRenderPass = WhichPass == RP_BLIT; - - if (WhichPass == RP_RASTERSHADOW) - m_RenderPass[RP_RASTERSHADOW] = m_Shadows[0].GetRenderPass(); - else if (PassSetup.ColorFormats.size() > 0 || PassSetup.DepthFormat != TextureFormat::UNDEFINED) - { - if (!pVulkan->CreateRenderPass({PassSetup.ColorFormats}, - PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, - PassSetup.ColorInputUsage, - PassSetup.ColorOutputUsage, - PassSetup.ClearDepthRenderPass, - PassSetup.DepthOutputUsage, - &m_RenderPass[WhichPass])) - return false; - } - - - // LOGI(" Render Pass (%s; Buffer %d of %d) => 0x%x", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS, m_RenderPass[WhichPass][WhichBuffer]); - } // Which Pass - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitDrawables() -//----------------------------------------------------------------------------- -{ - LOGI("Creating Test Drawable..."); - Vulkan* pVulkan = GetVulkan(); - - const auto& bufferLoader = [&](const std::string& bufferSlotName) -> tPerFrameVkBuffer { - if (bufferSlotName == "Vert") - { - return { m_ObjectVertUniform[RP_GBUFFER].vkBuffers }; - } - else if (bufferSlotName == "VertShadow") - { - return { m_ObjectVertUniform[RP_RASTERSHADOW].vkBuffers }; - } - else if (bufferSlotName == "Frag") - { - return { m_ObjectFragUniform[RP_GBUFFER].vkBuffers }; - } - else - { - assert(0); - return {}; - } - }; - - const auto* pOpaqueShader = m_ShaderManager->GetShader("ObjectDeferred"); - if (!pOpaqueShader) - { - // Error (bad shaderName) - return false; - } - - const auto* pEmissiveShader = m_ShaderManager->GetShader("ObjectEmissive"); - if (!pEmissiveShader) - { - // Error (bad shaderName) - return false; - } - - const auto* pAnimatedShader = m_ShaderManager->GetShader("ObjectAnimated"); - if (!pAnimatedShader) - { - // Error (bad shaderName) - return false; - } - - auto modelTextureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const MaterialPass::tPerFrameTexInfo - { - if (textureSlotName == "Environment" || textureSlotName == "Irradiance") - { - auto* texture = m_TextureManager->GetTexture(textureSlotName); - if (texture == nullptr) - return { m_TexWhite }; - return { texture }; - } - const bool normalMap = (textureSlotName == "Normal"); - const bool specMap = !normalMap && (textureSlotName == "SpecMap"); - const bool emissiveMap = !normalMap && !specMap && (textureSlotName == "Emissive"); - - // See if we can get the filename from the loaded material definition. Take a copy so we can manipulate as needed. - std::string textureName = specMap ? materialDef.specMapFilename : (normalMap ? materialDef.bumpFilename : (emissiveMap ? materialDef.emissiveFilename : materialDef.diffuseFilename)); - - if (textureName.empty() && specMap) - { - textureName = materialDef.bumpFilename; - size_t normal = textureName.find("_Normal."); - if (normal != -1) - { - textureName.replace(normal, 8, "_Specular."); - } - else - { - textureName.clear(); - } - } - - auto* texture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, textureName, m_SamplerRepeat, PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - if (texture==nullptr) - { - // File not loaded, use default - return { (normalMap ? m_DefaultNormal : m_TexWhite) }; - } - return { texture }; - }; - - const auto& modelMaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef) -> std::optional - { - using namespace std::placeholders; - if (materialDef.diffuseFilename.find(gAnimatedMaterialName) != std::string::npos) - { - return m_MaterialManager->CreateMaterial(*pVulkan, *pAnimatedShader, NUM_VULKAN_BUFFERS, std::bind(modelTextureLoader, std::cref(materialDef), _1), bufferLoader); - } - else if (!materialDef.emissiveFilename.empty()) - { - return m_MaterialManager->CreateMaterial(*pVulkan, *pEmissiveShader, NUM_VULKAN_BUFFERS, std::bind(modelTextureLoader, std::cref(materialDef), _1), bufferLoader); - } - else - { - return m_MaterialManager->CreateMaterial(*pVulkan, *pOpaqueShader, NUM_VULKAN_BUFFERS, std::bind(modelTextureLoader, std::cref(materialDef), _1), bufferLoader); - } - }; - - // Load .gltf file - const uint32_t loaderFlags = DrawableLoader::LoaderFlags::FindInstances | DrawableLoader::LoaderFlags::IgnoreHierarchy; - auto fatObjects = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gSceneObjectName, (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0); - // Print some debug - DrawableLoader::PrintStatistics(fatObjects); - // Pre-load the textures (likely to be faster than laoading one at a time, may do some threading etc) - m_TextureManager->BatchLoad(*m_AssetManager, MeshObjectIntermediate::ExtractTextureNames(fatObjects), m_SamplerRepeat, PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - - // See if we can find instances, we assume there is no instance information in the gltf! - auto instancedFatObjects = (loaderFlags & DrawableLoader::LoaderFlags::FindInstances) ? MeshInstanceGenerator::FindInstances(std::move(fatObjects)) : MeshInstanceGenerator::NullFindInstances(std::move(fatObjects)); - - // Make a single model mesh for the culled shadow (single material, multiple instances) - if (gRasterizedShadow) - { - auto fatObjects = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gSceneObjectName, (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0); - // See if we can find instances, we assume there is no instance information in the gltf! - auto instancedFatObjects = (loaderFlags & DrawableLoader::LoaderFlags::FindInstances) ? MeshInstanceGenerator::FindInstances(std::move(fatObjects)) : MeshInstanceGenerator::NullFindInstances(std::move(fatObjects)); - - if (!instancedFatObjects.empty()) - { - size_t totalVerts = 0; - size_t totalIndices = 0; - size_t totalInstances = 0; - - for (const auto& instancedObject : instancedFatObjects) - { - totalVerts += instancedObject.mesh.m_VertexBuffer.size(); - totalIndices += instancedObject.mesh.CalcNumTriangles() * 3; - totalInstances += instancedObject.instances.size(); - } - - std::vector instances; - instances.reserve(totalInstances); - std::vector< VkDrawIndexedIndirectCommand> indirectCommands; - indirectCommands.reserve(totalInstances); - MeshObjectIntermediate::tVertexBuffer vertices; - vertices.reserve(totalVerts); - std::vector indices; - indices.reserve(totalIndices); - - for (const auto& instancedObject : instancedFatObjects) - { - indirectCommands.emplace_back(VkDrawIndexedIndirectCommand{ .indexCount = (uint32_t)instancedObject.mesh.CalcNumTriangles() * 3, - .instanceCount = (uint32_t)instancedObject.instances.size(), - .firstIndex = (uint32_t)indices.size(), - .vertexOffset = (int32_t)vertices.size(), - .firstInstance = (uint32_t)instances.size() }); - // Add the instances - std::transform(instancedObject.instances.begin(), instancedObject.instances.end(), std::back_inserter(instances), [](auto instance) { return instance.transform; }); - // Add the vertices - vertices.insert(vertices.end(), instancedObject.mesh.m_VertexBuffer.begin(), instancedObject.mesh.m_VertexBuffer.end()); - // Add the indices (some finagling due to potentially different index buffer formats) - const size_t startIdx = indices.size(); - std::visit([&indices, &instancedObject](auto& v) { - using T = std::decay_t; - if constexpr (std::is_same_v>) - { - indices.insert(indices.end(), v.begin(), v.end()); - } - else if constexpr (std::is_same_v>) - { - std::transform(v.begin(), v.end(), std::back_inserter(indices), [](auto idx) -> uint32_t { return idx; }); - } - else if constexpr (!std::is_same_v ) - { - for (uint32_t i = 0; i < instancedObject.mesh.m_VertexBuffer.size(); ++i) - indices.push_back(i); - } - }, instancedObject.mesh.m_IndexBuffer); - } - - MeshObjectIntermediate MeshObjectIntermediate{}; - MeshObjectIntermediate.m_VertexBuffer = std::move(vertices); - MeshObjectIntermediate.m_IndexBuffer = std::move(indices); - - // Now create the Vulkan representations - const auto* pRasterizedShadowShader = pOpaqueShader; - assert(pRasterizedShadowShader); - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pRasterizedShadowShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - return { m_TexWhite }; - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - return { m_ObjectVertUniform[RP_RASTERSHADOW].vkBuffers }; - }); - - MeshObject mesh; - const auto& vertexFormats = pRasterizedShadowShader->m_shaderDescription->m_vertexFormats; - MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate, 0, vertexFormats, &mesh); - MeshObjectIntermediate.Release(); - - const auto instanceFormatIt = std::find_if(vertexFormats.cbegin(), vertexFormats.cend(), - [](const VertexFormat& f) { return f.inputRate == VertexFormat::eInputRate::Instance; }); - - VertexBufferObject vertexInstanceBuffer; - vertexInstanceBuffer.Initialize(&pVulkan->GetMemoryManager(), instances.size(), instances.data(), false, BufferUsageFlags::Vertex); - - m_ShadowInstanceUnculledDrawIndirectBuffer = std::make_unique(true); - struct { - uint32_t count; - uint32_t _pad[15]; - } indirectPrequelData{ .count = (uint32_t)indirectCommands.size() }; // indirect draw count, padded to 64 bytes - m_ShadowInstanceUnculledDrawIndirectBuffer->Initialize(&pVulkan->GetMemoryManager(), indirectCommands.size(), indirectCommands.data(), &indirectPrequelData, BufferUsageFlags::Storage | BufferUsageFlags::Indirect); - - DrawIndirectBufferObject culledDrawIndirectBufferObject{ true }; - - if (gRasterizedShadowCulled) - { - // For culling make an indirect draw buffer that we will update from a compute shader. - size_t worseCaseIndirectDraws = indirectCommands.size() + (instances.size() - indirectCommands.size()) / 2; - culledDrawIndirectBufferObject.Initialize(&pVulkan->GetMemoryManager(), worseCaseIndirectDraws, nullptr, nullptr, BufferUsageFlags::Storage | BufferUsageFlags::Indirect); - // Buffer to contain the intermediate array of visible instances - m_ShadowInstanceCulledData.Initialize(&pVulkan->GetMemoryManager(), 16/*header*/ + sizeof(uint32_t) * instances.size(), BufferUsageFlags::Storage, nullptr); - - // Create a storage buffer with instance centers and radii (in a vec4 per instance) and a 16byte header containing instance count. - std::vector instanceCullingData; - instanceCullingData.resize(4 /* 4 byte instance count followed by pad */ + instances.size() * 4 /* vec4 per instance*/); - instanceCullingData[0] = (uint32_t) instanceCullingData.size(); - glm::vec4* pInstanceCenterAndRadii = (glm::vec4*) &instanceCullingData[4]; - for (auto instance: instances) - *pInstanceCenterAndRadii++ = glm::vec4(instance[0][3], instance[1][3], instance[2][3], 10.0f/*hack*/); - CreateUniformBuffer(pVulkan, &m_ShadowInstanceCullingBuffer, instanceCullingData.size() * sizeof(uint32_t), instanceCullingData.data(), BufferUsageFlags::Storage); - - // Camera uniform (with culling planes) - CreateUniformBuffer(pVulkan, m_ShadowInstanceCullingCameraUniform); - - const auto* pComputeShader = m_ShaderManager->GetShader("RasterizedShadowCull"); - auto cullingMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pComputeShader, NUM_VULKAN_BUFFERS, - nullptr, - [this, &culledDrawIndirectBufferObject](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "CameraUniform") - return { m_ShadowInstanceCullingCameraUniform.vkBuffers }; - else if (bufferName == "UnculledInstances") - return { m_ShadowInstanceCullingBuffer.buf.GetVkBuffer() }; - else if (bufferName == "CulledInstances") - return { m_ShadowInstanceCulledData.GetVkBuffer() }; - else if (bufferName == "UnculledIndirectDraw") - return { m_ShadowInstanceUnculledDrawIndirectBuffer->GetVkBuffer() }; - else if (bufferName == "CulledIndirectDraw") - return { culledDrawIndirectBufferObject.GetVkBuffer() }; - assert(0); - return {}; - }); - - m_ShadowRasterizedCullingComputable = std::make_unique(*pVulkan, std::move(cullingMaterial)); - if (!m_ShadowRasterizedCullingComputable->Init()) - { - LOGE("Error Creating ShadowRasterizedCulling computable..."); - m_ShadowRasterizedCullingComputable.reset(); - } - else - { - m_ShadowRasterizedCullingComputable->SetDispatchGroupCount(0, { 1,1,1 }); // reset - m_ShadowRasterizedCullingComputable->SetDispatchGroupCount(1, { 1, uint32_t(instances.size() + 63) / 64, 1 }); // cull instances - m_ShadowRasterizedCullingComputable->SetDispatchGroupCount(2, { 1, uint32_t(m_ShadowInstanceUnculledDrawIndirectBuffer->GetNumDraws() + 63) / 64, 1 }); // generate indirect - } - } - else - { - // If not culling just use the unculled indirect buffer - culledDrawIndirectBufferObject = std::move(*m_ShadowInstanceUnculledDrawIndirectBuffer); - m_ShadowInstanceUnculledDrawIndirectBuffer.reset(); - } - - uint32_t shadowRenderPassBits = (1 << RP_RASTERSHADOW);// | (1 << RP_REFLECT); - const char* renderPassNames[NUM_RENDER_PASSES]{}; - renderPassNames[RP_RASTERSHADOW] = "RP_RASTERSHADOW_CULLED"; - - m_ShadowRasterizedDrawable = std::make_unique(*pVulkan, std::move(material)); - m_ShadowRasterizedDrawable->Init(m_RenderPass, renderPassNames, shadowRenderPassBits, std::move(mesh), std::move(vertexInstanceBuffer), std::move(culledDrawIndirectBufferObject)); - } - } - - // Make drawables for each instanced mesh in the scene (and load the materials). This is what we use for the gbuffer draw. - if (!DrawableLoader::CreateDrawables(*pVulkan, std::move(instancedFatObjects), m_RenderPass, sRenderPassNames, modelMaterialLoader, m_SceneDrawables, {}, loaderFlags, {})) - { - LOGE("Error initializing Drawable: %s", gSceneObjectName); - return false; - } - - { - MeshObject mesh; - if (LoadGLTF("./Media/Meshes/Skybox_Separate.gltf", 0, &mesh)) - { - const auto* pSkyboxShader = m_ShaderManager->GetShader("Skybox"); - assert(pSkyboxShader); - auto material = m_MaterialManager->CreateMaterial(*pVulkan, *pSkyboxShader, NUM_VULKAN_BUFFERS, - [this](const std::string& textureName) -> MaterialPass::tPerFrameTexInfo { - auto texture = m_TextureManager->GetTexture(textureName); - if (texture == nullptr) - { - assert(0); - return {}; - } - return { texture }; - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "Vert") { - return { m_SkyboxVertUniform[RP_GBUFFER].vkBuffers }; - } - else { - assert(0); - return {}; - } - }); - uint32_t skyboxRenderPassBits = (1 << RP_LIGHT);// | (1 << RP_REFLECT); - m_SkyboxDrawable = std::make_unique(*pVulkan, std::move(material)); - m_SkyboxDrawable->Init(m_RenderPass, sRenderPassNames, skyboxRenderPassBits, std::move(mesh)); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitHdr() -//----------------------------------------------------------------------------- -{ - // Set the color profile - VkHdrMetadataEXT AuthoringProfile = { VK_STRUCTURE_TYPE_HDR_METADATA_EXT }; - AuthoringProfile.displayPrimaryRed.x = 0.680f; - AuthoringProfile.displayPrimaryRed.y = 0.320f; - AuthoringProfile.displayPrimaryGreen.x = 0.265f; - AuthoringProfile.displayPrimaryGreen.y = 0.690f; - AuthoringProfile.displayPrimaryBlue.x = 0.150f; - AuthoringProfile.displayPrimaryBlue.y = 0.060f; - AuthoringProfile.whitePoint.x = 0.3127f; - AuthoringProfile.whitePoint.y = 0.3290f; - AuthoringProfile.maxLuminance = 80.0f;// 1000.f; - AuthoringProfile.minLuminance = 0.001f; - AuthoringProfile.maxContentLightLevel = 2000.f; - AuthoringProfile.maxFrameAverageLightLevel = 1000.f; - return GetVulkan()->SetSwapchainHrdMetadata(AuthoringProfile); -} - -//----------------------------------------------------------------------------- -bool Application::InitGui(uintptr_t windowHandle) -//----------------------------------------------------------------------------- -{ - // Gui - assert(m_RenderPass[RP_HUD] != VK_NULL_HANDLE); - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPass[RP_HUD]); - if (!m_Gui->Initialize(windowHandle, m_HudRT[0].m_Width, m_HudRT[0].m_Height)) - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateGui() -//----------------------------------------------------------------------------- -{ - if (gRenderHud && m_Gui) - { - // Update Gui - m_Gui->Update(); - - // Begin our window. - static bool settingsOpen = true; - if (gAdvancedMode) - { - ImGui::SetNextWindowSize(ImVec2((gRenderWidth * 3.0f) / 4.0f, 500.f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2(gRenderWidth / 8.0f, gRenderHeight / 2.0f), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Settings", &settingsOpen, (ImGuiWindowFlags)0)) - { - if (ImGui::CollapsingHeader("Camera")) - { - auto position = m_Camera.Position(); - auto rotation = m_Camera.Rotation(); - auto rotationDeg = glm::eulerAngles(rotation) * TO_DEGREES; - if (ImGui::InputFloat3("Position", &position.x, "%.1f")) - { - m_Camera.SetPosition(position, rotation); - } - if (ImGui::InputFloat3("Rotation", &rotationDeg.x, "%.1f")) - { - rotation = glm::quat(rotationDeg * TO_RADIANS); - m_Camera.SetPosition(position, rotation); - } - bool changed = ImGui::SliderFloat("Near Plane", &gNearPlane, 0.01f, gFarPlane - 0.01f); - changed |= ImGui::SliderFloat("Far Plane", &gFarPlane, gNearPlane + 0.02f, 15000.f); - if (changed) - { - m_Camera.SetClipPlanes(gNearPlane, gFarPlane); - m_Shadows[0].SetEyeClipPlanes(m_Camera.Fov(), m_Camera.NearClip(), m_Camera.Aspect(), gRasterizedShadowFarPlane); - } - } - - if (!gRasterizedShadow) - { - if (gCreateCulledAccelerationStructure) - ImGui::Checkbox("Disable RT Cull", &gDisableAccelerationStructureCull); - ImGui::Checkbox("Force RayTrace AS regen", &gForceAccelerationStructureRegen); - ImGui::Checkbox("Main RQ shadow use fragment shader", &gRayQueryFragmentShader); - ImGui::Checkbox("Main RQ shadow Directional light", &gShadowDirectionalLight); - ImGui::Checkbox("Enable additional shadows/lights (frag)", &gAdditionalShadows); - ImGui::Checkbox("Enable RT BLAS vertex animation", &gAnimateBLAS); - ImGui::SliderFloat3("Light Pos", &gShadowPosition.x, -100.0f, 100.0f); - ImGui::SliderFloat("Shadow Cull Radius", &gShadowRadius, 1.0f, 2000.0f); - ImGui::SliderFloat("Shadow Radius Cutoff", &gShadowRadiusCutoff, 0.01f, 1.0f); - ImGui::SliderFloat("Light Emitter Radius", &gLightEmitterRadius, 1.0f, 1000.0f); - ImGui::SliderFloat("Light Cutoff", &gLightAttenuationCutoff, 0.01f, 1.0f); - - m_LightFragUniformData.PointLightRadius = gLightEmitterRadius; - m_LightFragUniformData.PointLightCutoff = gLightAttenuationCutoff; - - float pointLightIntensity = m_LightFragUniformData.PointLightPosition.w; - ImGui::SliderFloat("Point Intensity", &pointLightIntensity, 0.0f, 300.0f); - m_LightFragUniformData.PointLightPosition.w = pointLightIntensity; - for (auto& d : m_AdditionalDeferredLightsData) - d.Intensity = pointLightIntensity; - } - else - { - if (ImGui::CollapsingHeader("Raster Shadow")) - { - bool changed = ImGui::InputFloat3("Shadow Position", &gRasterizedShadowPosition.x, "%.1f"); - changed |= ImGui::InputFloat3("Shadow Target", &gRasterizedShadowTarget.x, "%.1f"); - if (changed) - { - m_Shadows[0].SetLightPos(gRasterizedShadowPosition, gRasterizedShadowTarget); - } - if (ImGui::SliderFloat("Shadow FarPlane", &gRasterizedShadowFarPlane, 0.1f, gFarPlane)) - { - m_Shadows[0].SetEyeClipPlanes(m_Camera.Fov(), m_Camera.NearClip(), m_Camera.Aspect(), gRasterizedShadowFarPlane); - } - } - } - - ImGui::SliderFloat("SpecScale", &m_LightFragUniformData.SpecScale, 0.0f, 8.0f); - ImGui::SliderFloat("SpecPower", &m_LightFragUniformData.SpecPower, 1.0f, 100.0f); - ImGui::SliderFloat("irradianceAmount", &m_LightFragUniformData.irradianceAmount, 0.0f, 2.0f); - ImGui::SliderFloat("irradianceMip", &m_LightFragUniformData.irradianceMipLevel, 0.0f, 8.0f); - ImGui::SliderFloat("Brightness", &m_BlitFragUniformData.Diffuse, 0.0f, 10.0f); - - } - ImGui::End(); - } - - if (ImGui::Begin("FPS", (bool*)nullptr, ImGuiWindowFlags_NoTitleBar)) - { - ImGui::Text("FPS: %.1f", m_CurrentFPS); - - if (gAdvancedMode) - { - if (ImGui::TreeNode("Stats")) - { - if (gRasterizedShadow) - { - ImGui::Text("Rasterized Shadows"); - } - else - { - char InstancesString[256]; - int offset = snprintf(InstancesString, sizeof(InstancesString), "RT Instances: %u", (uint32_t)(m_sceneRayTracableCulled ? m_sceneRayTracableCulled->GetNumInstances() : m_sceneRayTracable->GetNumInstances())); - for (const auto& rt : m_AdditionalDeferredLightShadowRQ) - { - if (offset < sizeof(InstancesString)) - offset += snprintf(InstancesString + offset, sizeof(InstancesString) - offset, ",%u", (uint32_t)rt.GetNumInstances()); - } - ImGui::Text("%s", InstancesString); - - size_t totalTriangles = 0; - if (m_sceneRayTracableCulled) - { - for (const auto& instance : m_sceneRayTracableCulled->GetInstances()) - totalTriangles += m_sceneObjectTriangleCounts[instance.first]; - } - else - { - for (const auto& instance : m_sceneRayTracable->GetInstances()) - totalTriangles += m_sceneObjectTriangleCounts[instance.first]; - } - offset = snprintf(InstancesString, sizeof(InstancesString), "RT Triangles: %u", (uint32_t)totalTriangles); - ImGui::Text("%s", InstancesString); - - uint32_t updatedTriangles = 0; - for (const auto& animatedObject : m_sceneAnimatedMeshObjects) - updatedTriangles += animatedObject.GetPrimitiveCount(); - - if (gAnimateBLAS) - ImGui::Text("RT BLAS Updates: %u (%u triangles)", (uint32_t)m_sceneAnimatedMeshObjects.size(), updatedTriangles); - } - ImGui::Text("%s", sm_BuildTimestamp); - ImGui::TreePop(); - } - } - } - ImGui::End(); - - return; - } -} - -//----------------------------------------------------------------------------- -void Application::UpdateCamera(float ElapsedTime) -//----------------------------------------------------------------------------- -{ - m_Camera.UpdateController(ElapsedTime, *m_CameraController); - m_Camera.UpdateMatrices(); -} - -//----------------------------------------------------------------------------- -void Application::UpdateLighting(float ElapsedTime) -//----------------------------------------------------------------------------- -{ - if( m_sceneRayTracable ) - { - if( m_sceneRayTracableCulled ) - { - // Calculate the light/shadow's radius (based on the light cutoff, radius, and intensity) - // gShadowRadius = gLightEmitterRadius * (sqrtf( m_LightFragUniformData.PointLightPosition.w / gLightAttenuationCutoff ) - 1.0f); - // Cull the 'main' shadow ray tracable scene based on the radius. - UpdateSceneRTCulled( *m_sceneRayTracableCulled, gShadowPosition, gShadowRadius * gShadowRadiusCutoff ); - } - - if(gAdditionalShadows) - { - for( uint32_t WhichLight = 0; WhichLight < m_AdditionalDeferredLightShadowRQ.size(); ++WhichLight ) - { - auto& sceneRT = m_AdditionalDeferredLightShadowRQ[WhichLight]; - UpdateSceneRTCulled( sceneRT, glm::vec3( m_AdditionalDeferredLightsData[WhichLight].Position ), m_AdditionalDeferredLightsData[WhichLight].Radius * gShadowRadiusCutoff); - } - } - } -} - -//----------------------------------------------------------------------------- -void Application::UpdateShadowMap(float ElapsedTime) -//----------------------------------------------------------------------------- -{ - m_Shadows[0].Update(m_Camera.ViewMatrix()); -} - -//----------------------------------------------------------------------------- -void Application::UpdateSceneRTCulled( SceneRTCulled& sceneRTCulled, glm::vec3 lightPosition, float lightRadius ) -//----------------------------------------------------------------------------- -{ - BBoxTest lightBoundingBox( lightPosition, glm::vec3( lightRadius, lightRadius, lightRadius ) ); - SphereTest lightSphere( lightPosition, lightRadius ); - ViewFrustum viewFrustum( m_Camera.ProjectionMatrix(), m_Camera.ViewMatrix() ); - FrustumTest viewFrustumTest( viewFrustum ); - - sceneRTCulled.SetForceRegenerateAccelerationStructure( gForceAccelerationStructureRegen ); - - if( gDisableAccelerationStructureCull ) - { - // Update the ray tracing top level Acceleration Structure without culling (add everything to the AS). - sceneRTCulled.Update( *m_sceneRayTracable, [&lightBoundingBox, &viewFrustumTest]( const glm::vec4& center, const glm::vec4& halfSize ) { - return OctreeBase::eQueryResult::Inside; - } ); - } - else - { - auto lightInViewspace = viewFrustumTest( lightBoundingBox.m_boxCenter, lightBoundingBox.m_boxHalfSize ); - switch( lightInViewspace ) { - case OctreeBase::eQueryResult::Inside: - // - // Light area completely in view space - consider all objects in the light area (any other objects in view frustum won't be lit by this). - // - sceneRTCulled.Update( *m_sceneRayTracable, lightSphere ); - break; - case OctreeBase::eQueryResult::Outside: - // - // Light area completely outside view - nothing we can see should be lit by this light (so no shadow occluders). - // Lambda (should) compile out to remove the Octree test entirely. - // - sceneRTCulled.Update( *m_sceneRayTracable, []( const glm::vec4&, const glm::vec4& ) { return OctreeBase::eQueryResult::Outside; } ); - break; - case OctreeBase::eQueryResult::Partial: - if( viewFrustumTest( lightBoundingBox.m_boxCenter ) == OctreeBase::eQueryResult::Inside ) - { - // - // Light center (emission point) is inside the view frustum, cull against both the light area and the view frustum. - // - sceneRTCulled.Update( *m_sceneRayTracable, [&lightSphere, &viewFrustumTest]( const glm::vec4& center, const glm::vec4& halfSize ) { - // Lambda for the combined BoundingBox and View Frustum test. Called by the update to determine visibility of ray traced objects. - auto bbResult = lightSphere( center, halfSize ); - if( bbResult == OctreeBase::eQueryResult::Outside ) - return OctreeBase::eQueryResult::Outside; - else - { - auto frustumResult = viewFrustumTest( center, halfSize ); - if( frustumResult == OctreeBase::eQueryResult::Outside ) - return OctreeBase::eQueryResult::Outside; - else if( frustumResult == OctreeBase::eQueryResult::Partial || bbResult == OctreeBase::eQueryResult::Partial ) - return OctreeBase::eQueryResult::Partial; - } - return OctreeBase::eQueryResult::Inside; - } ); - } - else - { - // - // Light center is outside the view frustum but light volume is partially inside the frustum. - // Ideally we would cull against the area containing both, and the area between that and the center of the light area. - // would reduce the required light area by at least half, sometimes much more. - // For now just use the entire light volume and be done! - sceneRTCulled.Update( *m_sceneRayTracable, lightBoundingBox ); - } - break; - } - } -} - -//----------------------------------------------------------------------------- -void Application::LogFps() -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::LogFps(); - if (m_GpuTimerPool) - { - std::scoped_lock timerPoolLock(m_GpuTimerPoolMutex); - - if (gAdvancedMode) - m_GpuTimerPool->Log2(m_GpuTimerPool->GetResults()); - // Reset the GPU timers so average is only between 'LogFps' calls - m_GpuTimerPool->ResetTimers(-1/*dont want to skip in-flight timers*/); - } -} - -//----------------------------------------------------------------------------- -void Application::UpdateProfiling(uint32_t WhichFrame) -//----------------------------------------------------------------------------- -{ - if (m_GpuTimerPool) - { - m_GpuTimerPool->UpdateResults(WhichFrame); - } -} - -//----------------------------------------------------------------------------- -bool Application::ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - if (!pVulkan->ChangeSurfaceFormat(newSurfaceFormat)) - { - return false; - } - - // We need to modify the blit render pass (the only one that touches the output framebuffer). - // RenderPass needs to be compatible with the framebuffer's format. - // m_PassCmdBuffer[RP_BLIT] is good, gets rebuilt every frame - - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_RenderPass[RP_BLIT], nullptr); - m_RenderPass[RP_BLIT] = VK_NULL_HANDLE; - - auto& PassSetup = m_PassSetup[RP_BLIT]; - PassSetup.ColorFormats = { pVulkan->m_SurfaceFormat }; - PassSetup.DepthFormat = { pVulkan->m_SwapchainDepth.format }; - - if (!pVulkan->CreateRenderPass({ PassSetup.ColorFormats }, - PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, - PassSetup.ColorInputUsage, - PassSetup.ColorOutputUsage, - PassSetup.ClearDepthRenderPass, - PassSetup.DepthOutputUsage, - &m_RenderPass[RP_BLIT])) - return false; - - if (!m_BlitDrawable->ReInit( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], nullptr, nullptr)) - { - return false; - } - - InitHdr(); - - return true; -} - -//----------------------------------------------------------------------------- -Application::RenderPassData& Application::BeginRenderPass(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkSubpassContents SubpassContents) -//----------------------------------------------------------------------------- -{ - return BeginRenderPass(BeginCommandBuffer(WhichPass, WhichBuffer, WaitSemaphores, WaitDstStageMasks, SignalSemaphores, SubpassContents)); -} - -//----------------------------------------------------------------------------- -Application::RenderPassData& Application::BeginCommandBuffer(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkSubpassContents SubpassContents) -//----------------------------------------------------------------------------- -{ - assert( WaitSemaphores.size() == WaitDstStageMasks.size() ); - - RenderPassData& renderPassData = m_RenderPassSubmitData[WhichBuffer].emplace_back( RenderPassData { - m_PassCmdBuffer[WhichBuffer][WhichPass].m_Name, - WhichBuffer, - WhichPass, - m_PassCmdBuffer[WhichBuffer][WhichPass], - SubpassContents, - {WaitSemaphores.begin(), WaitSemaphores.end()}, - {WaitDstStageMasks.begin(), WaitDstStageMasks.end()}, - {SignalSemaphores.begin(), SignalSemaphores.end()}, - } ); - - const auto& passSetup = m_PassSetup[WhichPass]; - - // Reset the primary command buffer... - if (!renderPassData.CmdBuff.Reset()) - { - assert(0); - } - - // ... begin the primary command buffer ... - if (!renderPassData.CmdBuff.Begin()) - { - assert(0); - } - - return renderPassData; -} - -//----------------------------------------------------------------------------- -Application::RenderPassData& Application::BeginRenderPass(Application::RenderPassData& renderPassData) -//----------------------------------------------------------------------------- -{ - BeginRenderPass(renderPassData.CmdBuff, renderPassData.WhichPass, renderPassData.WhichBuffer, renderPassData.SubpassContents); - - return renderPassData; -} - -//----------------------------------------------------------------------------- -void Application::BeginRenderPass(Wrap_VkCommandBuffer& CmdBuf, RENDER_PASS WhichPass, uint32_t WhichBuffer, VkSubpassContents SubpassContents) -//----------------------------------------------------------------------------- -{ - Vulkan* pVulkan = GetVulkan(); - - if( m_RenderPass[WhichPass] != VK_NULL_HANDLE ) - { - const auto& passSetup = m_PassSetup[WhichPass]; - - VkFramebuffer Framebuffer = VK_NULL_HANDLE; - switch( WhichPass ) - { - case RP_GBUFFER: - Framebuffer = m_GBufferRT[0].m_FrameBuffer; - break; - case RP_RASTERSHADOW: - Framebuffer = m_Shadows[0].GetFramebuffer(); - break; - case RP_RAYSHADOW: - Framebuffer = m_ShadowRT[0].m_FrameBuffer; - break; - case RP_LIGHT: - Framebuffer = m_MainRT[0].m_FrameBuffer; - break; - case RP_HUD: - Framebuffer = m_HudRT[0].m_FrameBuffer; - break; - case RP_BLIT: - Framebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - break; - default: - assert( 0 ); - } - - VkRect2D PassArea = {}; - PassArea.offset.x = 0; - PassArea.offset.y = 0; - PassArea.extent.width = passSetup.TargetWidth; - PassArea.extent.height = passSetup.TargetHeight; - - VkClearColorValue clearColor[1] {{ passSetup.ClearColor[0], passSetup.ClearColor[1], passSetup.ClearColor[2], passSetup.ClearColor[3] }}; - bool IsSwapChainRenderPass = WhichPass == RP_BLIT; - - CmdBuf.BeginRenderPass( PassArea, 0.0f, 1.0f, clearColor, (uint32_t) passSetup.ColorFormats.size(), passSetup.DepthFormat != TextureFormat::UNDEFINED, m_RenderPass[WhichPass], IsSwapChainRenderPass, Framebuffer, SubpassContents ); - } -} - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffers(const RenderPassData& renderPassData) -//----------------------------------------------------------------------------- -{ - std::vector SubCommandBuffers; - SubCommandBuffers.reserve(3); - - // Add sub commands to render list - - const uint32_t WhichBuffer = renderPassData.WhichBuffer; - const uint32_t WhichPass = renderPassData.WhichPass; - - if (WhichPass == RP_LIGHT) - { - // Do the light commands - SubCommandBuffers.push_back(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer); - m_TotalDrawCalls += m_LightCmdBuffer[WhichBuffer].m_NumDrawCalls; - m_TotalTriangles += m_LightCmdBuffer[WhichBuffer].m_NumTriangles; - - // 'Additional' lights go after the LightCmd buffer (assuming they are enabled) - if(gAdditionalShadows) - { - SubCommandBuffers.push_back(m_AdditionalLightsCmdBuffer[WhichBuffer].m_VkCommandBuffer); - m_TotalDrawCalls += m_AdditionalLightsCmdBuffer[WhichBuffer].m_NumDrawCalls; - m_TotalTriangles += m_AdditionalLightsCmdBuffer[WhichBuffer].m_NumTriangles; - } - } - - if (m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumDrawCalls) - { - SubCommandBuffers.push_back(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer); - m_TotalDrawCalls += m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumDrawCalls; - m_TotalTriangles += m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumTriangles; - } - - // Add all subcommands - AddPassCommandBuffers(renderPassData, SubCommandBuffers); -} - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffers(const RenderPassData& RenderPassData, std::span SubCommandBuffers) -//----------------------------------------------------------------------------- -{ - // Make sure there is something to execue - if (SubCommandBuffers.size() == 0) - { - // Technically, this may not be an error. For now, let it fall through and submit nothing - LOGE("Error! Being asked to add pass command buffers but nothing is in the list!"); - } - else - { - vkCmdExecuteCommands(RenderPassData.CmdBuff.m_VkCommandBuffer, (uint32_t)SubCommandBuffers.size(), SubCommandBuffers.data()); - } -} - -//----------------------------------------------------------------------------- -void Application::EndRenderPass(const RenderPassData& RenderPassData) -//----------------------------------------------------------------------------- -{ - // End the render pass. - if( m_RenderPass[RenderPassData.WhichPass] != VK_NULL_HANDLE ) - { - RenderPassData.CmdBuff.EndRenderPass(); - } -} - -//----------------------------------------------------------------------------- -std::span Application::SubmitGameThreadWork(uint32_t WhichBuffer, VkFence CompletionFence) -//----------------------------------------------------------------------------- -{ - const auto& SubmitList = m_RenderPassSubmitData[WhichBuffer]; - uint32_t NumCmdBuffers = (uint32_t)SubmitList.size(); - -#ifdef ENABLE_PROFILING - PROFILE_ENTER(GROUP_VKFRAMEWORK, 0, "SubmitGameThreadWork: %d Buffers", NumCmdBuffers); -#endif // ENABLE_PROFILING - - const uint32_t NumPasses = (uint32_t) SubmitList.size(); - - for(uint32_t WhichPass = 0; WhichPass < NumPasses; ++WhichPass) - { - auto& OneSubmit = SubmitList[WhichPass]; - bool LastPass = (WhichPass == NumPasses - 1); - -#ifdef ENABLE_PROFILING - PROFILE_ENTER(GROUP_VKFRAMEWORK, 0, "Submit Cmd Buffer: %s", OneSubmit.Desc.c_str()); -#endif // ENABLE_PROFILING - - OneSubmit.CmdBuff.QueueSubmitT( OneSubmit.WaitSemaphores, OneSubmit.WaitDstStageMasks, OneSubmit.SignalSemaphores, LastPass ? CompletionFence : VK_NULL_HANDLE ); - -#ifdef ENABLE_PROFILING - PROFILE_EXIT(GROUP_VKFRAMEWORK); // "Submit Cmd Buffer: %s" -#endif // ENABLE_PROFILING - } - -#ifdef ENABLE_PROFILING - PROFILE_EXIT(GROUP_VKFRAMEWORK); // "SubmitGameThreadWork: %d Buffers" -#endif // ENABLE_PROFILING - - return !m_RenderPassSubmitData[WhichBuffer].empty() ? std::span{m_RenderPassSubmitData[WhichBuffer].rbegin()->SignalSemaphores} : std::span{}; -} diff --git a/samples/rayQueryShadows/code/main/rayQueryShadows.hpp b/samples/rayQueryShadows/code/main/rayQueryShadows.hpp deleted file mode 100644 index 3481dbc..0000000 --- a/samples/rayQueryShadows/code/main/rayQueryShadows.hpp +++ /dev/null @@ -1,291 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#pragma once -#include "main/applicationHelperBase.hpp" -#include "materials.hpp" -#include "vulkan/MeshObject.h" -#include "vulkan/commandBuffer.hpp" -#include "vulkan/TextureFuncts.h" -#include "vulkanRT/vulkanRT.hpp" -#include "system/glm_common.hpp" -#include "camera/camera.hpp" -#include "memory/vulkan/bufferObject.hpp" -#include "memory/vulkan/uniform.hpp" -#include "shadow/shadowVulkan.hpp" -#include "shadow/shadowVsm.hpp" -#include "shadow/shadowVulkan.hpp" -#include "system/Worker.h" - -#include -#include -#include -#include - -// Forward declarations -//class TextureVulkan; -class MaterialPass; -class Material; -class Computable; -class Drawable; -class DrawablePass; -template class DrawIndirectBuffer; -using DrawIndirectBufferObject = DrawIndirectBuffer; -class Gui; -class SceneRTCullable; -class SceneRTCulled; -class TestMeshAnimator; -class TimerPoolSimple; - -enum RENDER_PASS -{ - RP_GBUFFER = 0, - RP_RAYSHADOW, - RP_RASTERSHADOW, - RP_LIGHT, - RP_HUD, - RP_BLIT, - NUM_RENDER_PASSES -}; - -// Class -class Application : public ApplicationHelperBase -{ -public: - Application(); - ~Application() override; - - // Override FrameworkApplicationBase - int PreInitializeSelectSurfaceFormat(std::span) override; - void PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) override; - bool Initialize(uintptr_t windowHandle, uintptr_t instanceHandle) override; - void Destroy() override; - void Render(float fltDiffTime) override; - void LogFps() override; - - // Application - bool LoadMeshObjects(); - bool LoadRayTracingObjects(); - bool LoadTextures(); - bool CreateRenderTargets(); - bool InitShadowMap(); - bool InitLighting(); - bool LoadShaders(); - bool InitUniforms(); - bool UpdateUniforms(uint32_t WhichBuffer, float ElapsedTime); - bool InitMaterials(); - bool InitCommandBuffers(); - bool InitAllRenderPasses(); - bool InitDrawables(); - bool InitHdr(); - bool InitGui(uintptr_t windowHandle); - - bool BuildCmdBuffers(); - bool InitLocalSemaphores(); - - const char* GetPassName(uint32_t WhichPass); - - void UpdateGui(); - void UpdateCamera(float ElapsedTime); - void UpdateLighting(float ElapsedTime); - void UpdateShadowMap(float ElapsedTime); - void UpdateSceneRTCulled(SceneRTCulled& sceneRTCulled, glm::vec3 lightPosition, float lightRadius); - void UpdateProfiling(uint32_t WhichFrame); - - bool ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat); - - struct RenderPassData - { - static constexpr uint32_t cnMaxPassSemaphores = 2; - std::string Desc; - uint32_t WhichBuffer; - RENDER_PASS WhichPass; - Wrap_VkCommandBuffer& CmdBuff; - VkSubpassContents SubpassContents; - std::vector WaitSemaphores{}; - std::vector WaitDstStageMasks{}; - std::vector SignalSemaphores{}; - }; - std::vector m_RenderPassSubmitData[NUM_VULKAN_BUFFERS]; - - RenderPassData& BeginRenderPass(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkSubpassContents SubpassContents); - RenderPassData& BeginCommandBuffer(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores, VkSubpassContents SubpassContents); - RenderPassData& BeginRenderPass(Application::RenderPassData&); - void BeginRenderPass(Wrap_VkCommandBuffer& CmdBuf, RENDER_PASS WhichPass, uint32_t WhichBuffer, VkSubpassContents SubpassContents); - - void AddPassCommandBuffers(const RenderPassData& RenderPassData); - void AddPassCommandBuffers(const RenderPassData& RenderPassData, std::span SubCommandBuffers); - void EndRenderPass(const RenderPassData& RenderPassData); - // Submits the queued up m_RenderPassSubmitData, Returns semaphore(s) signalled by the final pass. - std::span SubmitGameThreadWork( uint32_t WhichBuffer, VkFence CompletionFence ); - -protected: - // Threading data - struct GameThreadInputParam - { - // Input - Vulkan::BufferIndexAndFence CurrentBuffer; - float ElapsedTime; - }; - - struct GameThreadOutputParam - { - // Output - int WhichFrame = -1; - int SwapchainPresentIndx; - VkFence Fence; - } m_CompletedThreadOutput; - - CWorker m_GameThreadWorker; - - // Game thread entry point. - void RunGameThread( const GameThreadInputParam& rThreadParam, GameThreadOutputParam& rThreadOutput); - -private: - - // Metrics - uint32_t m_TotalDrawCalls; - uint32_t m_TotalTriangles; - - // Requested surface format (for changing formats) - SurfaceFormat m_RequestedSurfaceFormat; - // sRGB output (done in blit shader) on/off - bool m_bEncodeSRGB; - - // Drawables - std::unique_ptr m_SkyboxDrawable; - std::vector m_SceneDrawables; - std::unique_ptr m_ShadowRasterizedDrawable; - std::unique_ptr m_ShadowPointLightRQDrawable; - std::unique_ptr m_ShadowDirectionalLightRQDrawable; - std::unique_ptr m_LightDrawable; - std::unique_ptr m_LightRasterizedShadowDrawable; - std::unique_ptr m_BlitDrawable; - // Computables - std::unique_ptr m_ShadowPointLightRQComputable; - std::unique_ptr m_ShadowDirectionalLightRQComputable; - std::unique_ptr m_ShadowRasterizedCullingComputable; - - // Ray tracing - VulkanRT m_vulkanRT; - std::unique_ptr m_sceneRayTracable; - std::unique_ptr m_sceneRayTracableCulled; - - // Ray trace vertex buffer update (animation) - std::vector m_sceneAnimatedMeshObjects; - std::vector m_sceneAnimatedMeshComputables; - MeshAnimatorUB m_sceneAnimatedMeshUniformData; - UniformArrayT m_sceneAnimatedMeshUniform; - - std::map m_sceneObjectTriangleCounts; // RT objectId to triangle count (for one instance). - - // Textures - const Texture* m_TexWhite; - const Texture* m_DefaultNormal; - - // Light Stuff - glm::vec4 m_LightColor; - - // ********************** - // The Objects - // ********************** - ObjectVertUB m_ObjectVertUniformData; - UniformArrayT m_ObjectVertUniform[NUM_RENDER_PASSES]; - ObjectFragUB m_ObjectFragUniformData; - UniformArrayT m_ObjectFragUniform[NUM_RENDER_PASSES]; - Wrap_VkCommandBuffer m_ObjectCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; - - // ********************** - // The Skybox - // ********************** - float m_SkyboxScale; - UniformArrayT m_SkyboxVertUniform[NUM_RENDER_PASSES]; - SkyboxVertUB m_SkyboxVertUniformData; - - // ********************** - // Deferred Lighting Fullscreen pass - // ********************** - Wrap_VkCommandBuffer m_LightCmdBuffer[NUM_VULKAN_BUFFERS]; - LightFragCtrl m_LightFragUniformData; - UniformArrayT m_LightFragUniform; - - // ********************** - // Additional deferred pass light objects (additional lights in scene) - // ********************** - Wrap_VkCommandBuffer m_AdditionalLightsCmdBuffer[NUM_VULKAN_BUFFERS]; - struct PointLightInstance{ - glm::vec3 Position; - float Radius; - float Intensity; - }; - std::vector m_AdditionalDeferredLightsData; // w is light intensity - std::vector m_AdditionalDeferredLightDrawables; - std::vector m_AdditionalDeferredLightShadowRQ; - std::vector> m_AdditionalDeferredLightsSharedUniform; - - // ********************** - // Post/Blit - // ********************** - BlitFragCtrl m_BlitFragUniformData; - UniformT m_BlitFragUniform; - Wrap_VkCommandBuffer m_BlitCmdBuffer[NUM_VULKAN_BUFFERS]; - - // ********************** - // Compute Ray Queried shadow) - // ********************** - ShadowRQCtrl m_ShadowRQCtrl; - UniformArrayT m_ShadowRQCtrlUniform; - TextureVulkan m_ShadowRayQueryComputeOutput; - - // ********************** - // Pass Stuff - // ********************** - struct PassSetup { - - std::vector ColorFormats; - TextureFormat DepthFormat; - RenderPassInputUsage ColorInputUsage; - bool ClearDepthRenderPass; - RenderPassOutputUsage ColorOutputUsage; - RenderPassOutputUsage DepthOutputUsage; - glm::vec4 ClearColor; - uint32_t TargetWidth; - uint32_t TargetHeight; - - } m_PassSetup[NUM_RENDER_PASSES]; - - Wrap_VkCommandBuffer m_PassCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; - VkSemaphore m_BlitCompleteSemaphore = VK_NULL_HANDLE; - - // Don't actually need a render pass for each Vulkan Buffer, just one per pass - // These are NOT the same vkrenderpass's as in the CRenderTargetArray's but should be compatible with them (are allowed dfferent load/clear parameters on the atachments) - std::array m_RenderPass; - - // Render targets for each pass - CRenderTargetArray<1> m_GBufferRT; - CRenderTargetArray<1> m_ShadowRT; - CRenderTargetArray<1> m_MainRT; - CRenderTargetArray<1> m_HudRT; - - // Shadow Map stuff - static const int cNumShadows = 1; - std::array m_Shadows; - ShadowVSM m_ShadowVSM; - struct ShadowCullCameraCtrl { - glm::mat4 MVPMatrix; ///< model view projection matrix for entire meshlet object - glm::vec4 CullPlanes[6]; ///< camera culling planes - }; - UniformArrayT m_ShadowInstanceCullingCameraUniform; - std::unique_ptr m_ShadowInstanceUnculledDrawIndirectBuffer; - UniformVulkan m_ShadowInstanceCullingBuffer; - BufferT m_ShadowInstanceCulledData; - - // Profiling - std::unique_ptr m_GpuTimerPool; - std::mutex m_GpuTimerPoolMutex; -}; diff --git a/samples/rayQueryShadows/img/screenshot.PNG b/samples/rayQueryShadows/img/screenshot.PNG deleted file mode 100644 index 6d8c417..0000000 Binary files a/samples/rayQueryShadows/img/screenshot.PNG and /dev/null differ diff --git a/samples/rayQueryShadows/project/android/res/values/strings.xml b/samples/rayQueryShadows/project/android/res/values/strings.xml deleted file mode 100644 index a525db4..0000000 --- a/samples/rayQueryShadows/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Ray-Queried Shadows - diff --git a/samples/rayQueryShadows/shaders/AnimateBuffer.comp b/samples/rayQueryShadows/shaders/AnimateBuffer.comp deleted file mode 100644 index 3d8814f..0000000 --- a/samples/rayQueryShadows/shaders/AnimateBuffer.comp +++ /dev/null @@ -1,60 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Animate Buffer compute shader -// Procedurally animates an array of vertex data (in a storage buffer) -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable - -// Buffer locations -#define SHADER_INPUTVERTICES_LOC 0 -#define SHADER_OUTPUTVERTICES_LOC 1 -#define SHADER_UNIFORM_LOC 2 - -#define WORKGROUP_SIZE (128) -layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1 ) in; - -layout(std430, binding = SHADER_INPUTVERTICES_LOC) restrict buffer inputVertexData -{ - vec3 positions[]; -} InputVertexData; - -layout(std430, binding = SHADER_OUTPUTVERTICES_LOC) restrict buffer outputVertexData -{ - vec3 positions[]; -} OutputVertexData; - -layout(std140, binding = SHADER_UNIFORM_LOC) restrict uniform Uniform -{ - vec4 AnimationRotation; -} UniformData; - - -#define PI (3.14159) - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec3 LocalPosition = InputVertexData.positions[gl_GlobalInvocationID.x]; - - vec3 offset, tmp; - offset.x = modf(LocalPosition.x*0.01, tmp.x); - offset.y = 0.0; - offset.z = modf(LocalPosition.z*0.01, tmp.y); - float sway = clamp(LocalPosition.y, 0.0, -2.0) * 1.0; - LocalPosition.x = LocalPosition.x + sin(UniformData.AnimationRotation.z)*sway + 1.0*sin(UniformData.AnimationRotation.x + 2.0 * PI * offset.x); - LocalPosition.z = LocalPosition.z + sin(UniformData.AnimationRotation.w)*sway + 1.0*sin(UniformData.AnimationRotation.y + 2.0 * PI * offset.z); - - OutputVertexData.positions[gl_GlobalInvocationID.x] = LocalPosition; -} diff --git a/samples/rayQueryShadows/shaders/Blit.frag b/samples/rayQueryShadows/shaders/Blit.frag deleted file mode 100644 index 1f6373d..0000000 --- a/samples/rayQueryShadows/shaders/Blit.frag +++ /dev/null @@ -1,135 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_FRAG_UBO_LOCATION 0 -#define SHADER_DIFFUSE_TEXTURE_LOC 1 -#define SHADER_BLOOM_TEXTURE_LOC 2 -#define SHADER_OVERLAY_TEXTURE_LOC 3 - -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_BLOOM_TEXTURE_LOC) uniform sampler2D u_BloomTex; -layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - // Mix amount of Bloom - float Bloom; - // Mix amount of main scene - float Diffuse; - // Enable(1) conversion of color to sRGB. - int sRGB; -} FragCB; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // Simple brightness - DiffuseColor.rgb *= FragCB.Diffuse; - - // Get the Bloom value - vec4 BloomColor = texture( u_BloomTex, LocalTexCoord.xy ) * FragCB.Bloom; - - // ******************************** - // Alpha Blending - // ******************************** - FragColor.rgb = (DiffuseColor.rgb + BloomColor.rgb); - FragColor.a = 1.0; - - // Color mapping - FragColor.rgb = ACESFilmic(FragColor.rgb); - - // Get the Overlay value - vec4 OverlayColor = texture( u_OverlayTex, LocalTexCoord.xy ); - FragColor.rgb = FragColor.rgb*(1.0-OverlayColor.a) + OverlayColor.rgb; - - // ******************************** - // sRGB conversion (if speed was a factor we would probably hardcode and have 2 blit shaders) - // ******************************** - if (FragCB.sRGB == 1) - { - FragColor.r = ToSCRGB(FragColor.r); - FragColor.g = ToSCRGB(FragColor.g); - FragColor.b = ToSCRGB(FragColor.b); - } - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // FragColor.xyz = (1.0 - OverlayColor.a) * DiffuseColor.xyz + OverlayColor.a * OverlayColor.xyz; - // FragColor = vec4(DiffuseColor.xyz, DiffuseColor.a); - // FragColor = vec4(OverlayColor.xyz, OverlayColor.a); - // FragColor.a = 1.0; - // FragColor = vec4(0.8, 0.2, 0.8, 1.0); -} - diff --git a/samples/rayQueryShadows/shaders/Blit.json b/samples/rayQueryShadows/shaders/Blit.json deleted file mode 100644 index b7806ef..0000000 --- a/samples/rayQueryShadows/shaders/Blit.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_BLIT", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/Blit.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "BlitFragCB" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Bloom" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Overlay" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/Fullscreen.vert b/samples/rayQueryShadows/shaders/Fullscreen.vert deleted file mode 100644 index c194c21..0000000 --- a/samples/rayQueryShadows/shaders/Fullscreen.vert +++ /dev/null @@ -1,29 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_TEXCOORD0 1 - -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; - -// Varying's -layout (location = 0) out vec2 v_TexCoord; - -void main() -{ - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = vec4(a_Position.xyz, 1.0); - - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.xy); -} diff --git a/samples/rayQueryShadows/shaders/Light.frag b/samples/rayQueryShadows/shaders/Light.frag deleted file mode 100644 index 799e1fd..0000000 --- a/samples/rayQueryShadows/shaders/Light.frag +++ /dev/null @@ -1,180 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -// Buffer binding locations -#define SHADER_FRAG_UBO_LOCATION 0 -#define SHADER_ALBEDO_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_RT_SHADOW_TEXTURE_LOC 3 -#define SHADER_IRRADIANCE_TEXTURE_LOC 4 - -layout(set = 0, binding = SHADER_ALBEDO_TEXTURE_LOC) uniform sampler2D u_AlbedoTex; // Albedo RGB, Specular is in A -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout(set = 0, binding = SHADER_RT_SHADOW_TEXTURE_LOC) uniform sampler2D u_ShadowTex; // ray trace/query generated shadow texture -layout(set = 0, binding = SHADER_IRRADIANCE_TEXTURE_LOC) uniform samplerCube u_IrradianceTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - mat4 ProjectionInv; - mat4 ViewInv; - mat4 WorldToShadow; // For rasterized depth shadow - vec4 CameraPos; - - // Point Light - vec4 PointLightPosition; - vec4 PointLightColor; - - // Directional Light - vec4 DirectionalLightDirection; - vec4 DirectionalLightColor; - - vec4 AmbientColor; - - float PointLightRadius; // of the light itself (not the area of influence) - float PointLightCutoff; - - float SpecScale; - float SpecPower; - float irradianceAmount; - float irradianceMipLevel; - float AmbientOcclusionScale; - int Width; - int Height; - -} FragCB; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - - -//----------------------------------------------------------------------------- -float LightFalloff(vec3 WorldToLightVec, float LightRadius, float LightCutoff, float LightIntensity) -//----------------------------------------------------------------------------- -{ - // calculate normalized light vector and distance to sphere light surface - vec3 L = WorldToLightVec; - float distance = length(L); - float d = max(distance - LightRadius, 0.0); - L /= distance; - - // calculate basic attenuation - float denom = (d/LightRadius) + 1.0; - float LightAttenuation = 1.0 / (denom*denom); - - // scale and bias attenuation - LightAttenuation = LightIntensity * (LightAttenuation - (LightCutoff / LightIntensity)) / (1.0 - (LightCutoff / LightIntensity)); - LightAttenuation = max(LightAttenuation, 0); - - return LightAttenuation; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Albedo Color - // ******************************** - vec4 AlbedoColor = texture( u_AlbedoTex, LocalTexCoord.xy ); - - // ******************************** - // Normal - // ******************************** - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 Normal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - //float Depth = texture( u_DepthTex, LocalTexCoord.xy ).x; // no longer using u_DepthTex so we (poentially) avoid a depth resolve operation before reading (instead gbuffer Normal.w has the depth) - - // ******************************** - // Image Based Lighting - // ******************************** - // Irradiance replaces ambient - vec3 IrradianceLookup = vec3(Normal.x, -Normal.y, Normal.z); - vec3 IrradianceMapColor = textureLod(u_IrradianceTex, IrradianceLookup, FragCB.irradianceMipLevel).rgb; - vec3 Irradiance = FragCB.irradianceAmount * IrradianceMapColor.xyz; - - //FragColor.rgb = AlbedoColor.rgb; - FragColor.a = 1.0; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // ******************************** - // Shadow - // ******************************** -#if 1 - // Raytraced Shadow - float ShadowAtten = texture( u_ShadowTex, LocalTexCoord.xy ).x; -#else - float ShadowAtten = 1.0; -#endif - - vec3 EyeDir = normalize(FragCB.CameraPos.xyz - WorldPos); - - vec3 LightAmt = vec3(0.0);//Ambient; - - { - vec4 LightPos = FragCB.PointLightPosition; //w is intensity, xyz world position - vec3 WorldToLightVec = LightPos.xyz - WorldPos; - float LightDist2 = dot(WorldToLightVec, WorldToLightVec); - vec3 WorldToLightNorm = normalize(WorldToLightVec); - - float LightAtten = LightFalloff(WorldToLightVec, FragCB.PointLightRadius, FragCB.PointLightCutoff, FragCB.PointLightPosition.w); - - float LightAng = max(0.0, dot(WorldToLightNorm, Normal)); - - // Spec (blinn-phong) - vec3 LightDir = WorldToLightNorm; - vec3 HalfVector = normalize(LightDir + EyeDir); - float Spec = AlbedoColor.a/*spec scale*/ * pow(max(dot(Normal,HalfVector),0.0), FragCB.SpecPower) * FragCB.SpecScale; - - LightAmt += ShadowAtten * FragCB.PointLightColor.rgb * (Spec + LightAng ) * LightAtten; - } - - FragColor.rgb = AlbedoColor.rgb * (LightAmt + Irradiance); - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - //FragColor.rgb = Irradiance; - //FragColor.rgb = Normal; - //FragColor.rgb = WorldPos; - //FragColor.rgb = vec3(1.0, 0.0f, 0.0);//AlbedoColor.rgb; - //FragColor.rgb = vec3(AmbientOcclusion,AmbientOcclusion,AmbientOcclusion); - //FragColor.a = 1.0; - // FragColor = vec4(0.8, 0.2, 0.8, 1.0); - //FragColor = vec4(0.0, 0.0, 0.0, 1.0); -} - diff --git a/samples/rayQueryShadows/shaders/Light.json b/samples/rayQueryShadows/shaders/Light.json deleted file mode 100644 index 1103480..0000000 --- a/samples/rayQueryShadows/shaders/Light.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/Light.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "LightFragCB" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Albedo" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "ShadowRT" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Irradiance" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/Light.vert b/samples/rayQueryShadows/shaders/Light.vert deleted file mode 100644 index cdd4f93..0000000 --- a/samples/rayQueryShadows/shaders/Light.vert +++ /dev/null @@ -1,28 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_TEXCOORD0 1 - -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; - -// Varying's -layout (location = 0) out vec2 v_TexCoord; - -void main() -{ - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = vec4(a_Position.xyz, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.xy); -} diff --git a/samples/rayQueryShadows/shaders/LightRasterizedShadows.frag b/samples/rayQueryShadows/shaders/LightRasterizedShadows.frag deleted file mode 100644 index 056a533..0000000 --- a/samples/rayQueryShadows/shaders/LightRasterizedShadows.frag +++ /dev/null @@ -1,196 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -// Buffer binding locations -#define SHADER_FRAG_UBO_LOCATION 0 -#define SHADER_ALBEDO_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_DEPTH_SHADOW_TEXTURE_LOC 3 -#define SHADER_IRRADIANCE_TEXTURE_LOC 4 - -layout(set = 0, binding = SHADER_ALBEDO_TEXTURE_LOC) uniform sampler2D u_AlbedoTex; // Albedo RGB, Specular is in A -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout(set = 0, binding = SHADER_DEPTH_SHADOW_TEXTURE_LOC) uniform sampler2D u_ShadowMap; // 'traditional' rasterized shadow depth map -layout(set = 0, binding = SHADER_IRRADIANCE_TEXTURE_LOC) uniform samplerCube u_IrradianceTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// We run all the lights in the same shader, ideally we would render each light individually (using their screen space bounding area) and add each lights contribution seperately - can go to 100s of lights that way. -#define NUM_POINT_LIGHTS 4 -#define NUM_SPOT_LIGHTS 4 - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - mat4 ProjectionInv; - mat4 ViewInv; - mat4 WorldToShadow; // For rasterized depth shadow - vec4 CameraPos; - - // Point Light - vec4 PointLightPosition; - vec4 PointLightColor; - - // Directional Light - vec4 DirectionalLightDirection; - vec4 DirectionalLightColor; - - vec4 AmbientColor; - - float PointLightRadius; // of the light itself (not the area of influence) - float PointLightCutoff; - - float SpecScale; - float SpecPower; - float irradianceAmount; - float irradianceMipLevel; - float AmbientOcclusionScale; - int Width; - int Height; - -} FragCB; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// Shadow code -#define USE_VARIANCE_SHADOWMAP -#define SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE (1) /* no shadow distance fade */ -#include "ShadowShared.h" - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - - -//----------------------------------------------------------------------------- -float LightFalloff(vec3 WorldToLightVec, float LightRadius, float LightCutoff, float LightIntensity) -//----------------------------------------------------------------------------- -{ - // calculate normalized light vector and distance to sphere light surface - vec3 L = WorldToLightVec; - float distance = length(L); - float d = max(distance - LightRadius, 0.0); - L /= distance; - - // calculate basic attenuation - float denom = (d/LightRadius) + 1.0; - float LightAttenuation = 1.0 / (denom*denom); - - // scale and bias attenuation - LightAttenuation = LightIntensity * (LightAttenuation - (LightCutoff / LightIntensity)) / (1.0 - (LightCutoff / LightIntensity)); - LightAttenuation = max(LightAttenuation, 0); - - return LightAttenuation; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Albedo Color - // ******************************** - vec4 AlbedoColor = texture( u_AlbedoTex, LocalTexCoord.xy ); - - // ******************************** - // Normal - // ******************************** - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 Normal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - //float Depth = texture( u_DepthTex, LocalTexCoord.xy ).x; // no longer using u_DepthTex so we (poentially) avoid a depth resolve operation before reading (instead gbuffer Normal.w has the depth) - - // ******************************** - // Image Based Lighting - // ******************************** - // Irradiance replaces ambient - vec3 IrradianceLookup = vec3(Normal.x, -Normal.y, Normal.z); - vec3 IrradianceMapColor = textureLod(u_IrradianceTex, IrradianceLookup, FragCB.irradianceMipLevel).rgb; - vec3 Irradiance = FragCB.irradianceAmount * IrradianceMapColor.xyz; - - //FragColor.rgb = AlbedoColor.rgb; - FragColor.a = 1.0; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // ******************************** - // Shadow - // ******************************** - vec4 ShadowPos = FragCB.WorldToShadow * vec4(WorldPos, 1.0); - // convert -1 to 1 screen co-ord range to texture 0 to 1. - ShadowPos.xy = ShadowPos.xy * 0.5 + 0.5; - // Lookup the Shadow amount - float ShadowAtten = GetInShadowVSM(ShadowPos); - - vec3 EyeDir = normalize(FragCB.CameraPos.xyz - WorldPos); - - vec3 LightAmt = vec3(0.0);//Ambient; - - // Point light - { - vec4 LightPos = FragCB.PointLightPosition; //w is intensity, xyz world position - vec3 WorldToLightVec = LightPos.xyz - WorldPos; - float LightDist2 = dot(WorldToLightVec, WorldToLightVec); - vec3 WorldToLightNorm = normalize(WorldToLightVec); - - float LightAtten = LightFalloff(WorldToLightVec, FragCB.PointLightRadius, FragCB.PointLightCutoff, FragCB.PointLightPosition.w); - - float LightAng = max(0.0, dot(WorldToLightNorm, Normal)); - - // Spec (blinn-phong) - vec3 LightDir = WorldToLightNorm; - vec3 HalfVector = normalize(LightDir + EyeDir); - float Spec = AlbedoColor.a/*spec scale*/ * pow(max(dot(Normal,HalfVector),0.0), FragCB.SpecPower) * FragCB.SpecScale; - - LightAmt += /*ShadowAtten * */ FragCB.PointLightColor.rgb * (Spec + LightAng ) * LightAtten; - } - - // Directional light - { - float LightAng = max(0.0, -dot( FragCB.DirectionalLightDirection.xyz, Normal)); - LightAmt += ShadowAtten * LightAng * FragCB.DirectionalLightColor.rgb; - } - - FragColor.rgb = AlbedoColor.rgb * (LightAmt + Irradiance); - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - //FragColor.rgb = Irradiance; - //FragColor.rgb = Normal; - //FragColor.rgb = WorldPos; - //FragColor.rgb = vec3(1.0, 0.0f, 0.0);//AlbedoColor.rgb; - //FragColor.rgb = vec3(AmbientOcclusion,AmbientOcclusion,AmbientOcclusion); - //FragColor.a = 1.0; - // FragColor = vec4(0.8, 0.2, 0.8, 1.0); - //FragColor = vec4(0.0, 0.0, 0.0, 1.0); -} - diff --git a/samples/rayQueryShadows/shaders/LightRasterizedShadows.json b/samples/rayQueryShadows/shaders/LightRasterizedShadows.json deleted file mode 100644 index 4fd39b5..0000000 --- a/samples/rayQueryShadows/shaders/LightRasterizedShadows.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/LightRasterizedShadows.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "LightFragCB" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Albedo" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "ShadowDepth" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Irradiance" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/ObjectAnimated.json b/samples/rayQueryShadows/shaders/ObjectAnimated.json deleted file mode 100644 index ece5c7d..0000000 --- a/samples/rayQueryShadows/shaders/ObjectAnimated.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_GBUFFER", - "Shaders": { - "Vertex": "Media/Shaders/ObjectAnimated.vert.spv", - "Fragment": "Media/Shaders/ObjectDeferred.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Frag" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "SpecMap" ] - } - ] - } - ], - "VertexBindings": [ "Position", "Attributes", "Instances" ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthCompareOp": "LessEqual" - }, - "Outputs": [ - { "BlendEnable": false }, - { "BlendEnable": false } - ] - }, - { - "Name": "RP_RASTERSHADOW", - "Shaders": { - "Vertex": "Media/Shaders/ShadowGenAnimated.vert.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "VertShadow" ] - } - ] - } - ], - "VertexBindings": [ "Position", "Instances" ], - "FixedFunction": { - "CullFrontFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthClampEnable": true, - "DepthBiasEnable": false, - "DepthBiasConstant": 1.25, - "DepthBiasClamp": 0.0, - "DepthBiasSlope": 1.75 - } - } - ], - "Vertex": [ - { - "Span": 12, - "Name": "Position", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Attributes", - "Elements": [ - { - "Name": "Normal", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 20, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 36, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Instances", - "Rate": "Instance", - "Elements": [ - { - "Name": "Transform0", - "Offset": 0, - "Type": "Vec4" - }, - { - "Name": "Transform1", - "Offset": 16, - "Type": "Vec4" - }, - { - "Name": "Transform2", - "Offset": 32, - "Type": "Vec4" - } - ] - } - - ] -} - diff --git a/samples/rayQueryShadows/shaders/ObjectAnimated.vert b/samples/rayQueryShadows/shaders/ObjectAnimated.vert deleted file mode 100644 index f1b03ec..0000000 --- a/samples/rayQueryShadows/shaders/ObjectAnimated.vert +++ /dev/null @@ -1,80 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 -#define SHADER_ATTRIB_LOC_INSTANCE_MATRIX 5 /* also 6 and 7 */ - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Vertex rate data -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -// Instance rate data -layout (location = SHADER_ATTRIB_LOC_INSTANCE_MATRIX ) in mat3x4 a_InstanceMatrix3x4; // use a mat3x4 (row major, ie transposed from 'normal' GLSL, but less input slots) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 VPMatrix; - vec4 AnimationRotation; -} VertAnimatedCB; - -// Varying's -layout (location = 0) out vec2 v_TexCoord; -layout (location = 1) out vec3 v_WorldPos; -layout (location = 2) out vec3 v_WorldNorm; -layout (location = 3) out vec3 v_WorldTan; -layout (location = 4) out vec3 v_WorldBitan; -layout (location = 5) out vec4 v_VertColor; - -#define PI (3.14159) - - -void main() -{ - vec3 LocalPosition = a_Position; - - vec3 offset, tmp; - offset.x = modf(LocalPosition.x*0.01, tmp.x); - offset.y = 0.0; - offset.z = modf(LocalPosition.z*0.01, tmp.y); - float sway = clamp(LocalPosition.y, 0.0, -2.0) * 1.0; - LocalPosition.x = LocalPosition.x + sin(VertAnimatedCB.AnimationRotation.z)*sway + 1.0*sin(VertAnimatedCB.AnimationRotation.x + 2.0 * PI * offset.x); - LocalPosition.z = LocalPosition.z + sin(VertAnimatedCB.AnimationRotation.w)*sway + 1.0*sin(VertAnimatedCB.AnimationRotation.y + 2.0 * PI * offset.z); - - vec3 WorldPosition = vec4(LocalPosition, 1.0) * a_InstanceMatrix3x4; // a_InstanceMatrix is 3x4 to reduce number of input binding slots needed. Because it is transposed (from the normal glsl expectation of a 4x3) we do a post multiply. - vec4 TempPos = VertAnimatedCB.VPMatrix * vec4( WorldPosition, 1.0); - - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); - - - // Need Position in world space - v_WorldPos = WorldPosition; - - // Need Normal, Tangent, and Bitangent in world space. Normalize since the a_InstanceMatrix3x4 may be scaled. - v_WorldNorm = normalize(vec4(a_Normal.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldTan = normalize(vec4(a_Tangent.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldBitan = cross(v_WorldNorm, v_WorldTan); - - // Color is simple attribute color - v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); -} diff --git a/samples/rayQueryShadows/shaders/ObjectDeferred.frag b/samples/rayQueryShadows/shaders/ObjectDeferred.frag deleted file mode 100644 index 4cc561a..0000000 --- a/samples/rayQueryShadows/shaders/ObjectDeferred.frag +++ /dev/null @@ -1,110 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -// Define available features - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Texture Locations -#define SHADER_DIFFUSE_TEXTURE_LOC 2 -#define SHADER_NORMAL_TEXTURE_LOC 3 -#define SHADER_SPECMAP_TEXTURE_LOC 4 - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - vec4 Color; - - // X: Normal Height - // Y: Normal Mirror Reflect Amount - // Z: Not Used - // W: Not Used - vec4 NormalHeight; - -} FragCB; - -#define NORMAL_HEIGHT FragCB.NormalHeight.x -#define NORMAL_MIRROR_AMOUNT FragCB.NormalHeight.y - -#define REFLECT_MIP_LEVEL FragCB.ReflectParams.x -#define REFLECT_AMOUNT FragCB.ReflectParams.y -#define REFLECT_FRESNEL_MIN FragCB.ReflectParams.z -#define REFLECT_FRESNEL_MAX FragCB.ReflectParams.w - -// Textures -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout(set = 0, binding = SHADER_SPECMAP_TEXTURE_LOC) uniform sampler2D u_SpecMapTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 3) in vec3 v_WorldTan; -layout (location = 4) in vec3 v_WorldBitan; -layout (location = 5) in vec4 v_VertColor; - -// Output color -layout (location = 0) out vec4 FragColor; -// Output Normal -layout (location = 1) out vec4 FragNormal; - - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Base (albedo) color - // ******************************** - // Get color from the color texture - vec4 AlbedoColor = texture( u_DiffuseTex, v_TexCoord.xy ); - AlbedoColor.xyzw *= FragCB.Color.xyzw; - - // Adjust by vertex color. - AlbedoColor.xyzw *= v_VertColor.xyzw; - - // Get specular value(s) from the SpecMap texture - vec4 SpecMapValue = texture( u_SpecMapTex, v_TexCoord.xy ); - - // Get base normal from the bump texture - vec4 NormTexValue = texture( u_NormalTex, v_TexCoord.xy ); - vec3 N = NormTexValue.xyz * 2.0 - 1.0; - - N.xy *= NORMAL_HEIGHT; - N = normalize(N); - // N = vec3(0.0, 0.0, 1.0); // If we want to hard code - - // Need matrix to convert to tangent space - // vec3 binormal = cross(v_WorldNorm, v_WorldTan); - // mat3 WorldToTan = mat3(normalize(v_WorldTan), normalize(binormal), normalize(v_WorldNorm)); - mat3 WorldToTan = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); - - // Convert the bump normal to tangent space - vec3 BumpNormal = normalize(WorldToTan * N); - - // Write out the color and put specular in the Alpha - FragColor = vec4(AlbedoColor.rgb, SpecMapValue.g); - // Write out the Normal and put depth value in the 'alpha' channel - FragNormal = vec4(BumpNormal.xyz, 1.0 - gl_FragCoord.z); - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // FragColor = vec4(Specular, Specular, Specular, 1.0); - // FragColor = vec4(BumpNormal.xyz, 1.0); - // FragColor = ShadowAmount; -} - diff --git a/samples/rayQueryShadows/shaders/ObjectDeferred.json b/samples/rayQueryShadows/shaders/ObjectDeferred.json deleted file mode 100644 index edfcf0e..0000000 --- a/samples/rayQueryShadows/shaders/ObjectDeferred.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_GBUFFER", - "Shaders": { - "Vertex": "Media/Shaders/ObjectDeferred.vert.spv", - "Fragment": "Media/Shaders/ObjectDeferred.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Frag" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "SpecMap" ] - } - ] - } - ], - "VertexBindings": [ "Position", "Attributes", "Instances" ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthCompareOp": "LessEqual" - }, - "Outputs": [ - { "BlendEnable": false }, - { "BlendEnable": false } - ] - }, - { - "xName": "RP_RASTERSHADOW_CULLED", - "Name": "RP_RASTERSHADOW", - "Shaders": { - "Vertex": "Media/Shaders/ShadowGen.vert.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "VertShadow" ] - } - ] - } - ], - "VertexBindings": [ "Position", "Instances" ], - "FixedFunction": { - "CullFrontFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthClampEnable": true, - "DepthBiasEnable": false, - "DepthBiasConstant": 1.25, - "DepthBiasClamp": 0.0, - "DepthBiasSlope": 1.75 - } - } - ], - "Vertex": [ - { - "Span": 12, - "Name": "Position", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Attributes", - "Elements": [ - { - "Name": "Normal", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 20, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 36, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Instances", - "Rate": "Instance", - "Elements": [ - { - "Name": "Transform0", - "Offset": 0, - "Type": "Vec4" - }, - { - "Name": "Transform1", - "Offset": 16, - "Type": "Vec4" - }, - { - "Name": "Transform2", - "Offset": 32, - "Type": "Vec4" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/ObjectDeferred.vert b/samples/rayQueryShadows/shaders/ObjectDeferred.vert deleted file mode 100644 index 4c8886b..0000000 --- a/samples/rayQueryShadows/shaders/ObjectDeferred.vert +++ /dev/null @@ -1,73 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 -#define SHADER_ATTRIB_LOC_INSTANCE_MATRIX 5 /* also 6 and 7 */ - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Vertex rate data -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -// Instance rate data -layout (location = SHADER_ATTRIB_LOC_INSTANCE_MATRIX ) in mat3x4 a_InstanceMatrix3x4; // use a mat3x4 (row major, ie transposed from 'normal' GLSL, but less input slots) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 VPMatrix; -} VertCB; - -const mat4 biasMat = mat4( - 0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.5, 0.5, 0.0, 1.0 ); - -// Varying's -layout (location = 0) out vec2 v_TexCoord; -layout (location = 1) out vec3 v_WorldPos; -layout (location = 2) out vec3 v_WorldNorm; -layout (location = 3) out vec3 v_WorldTan; -layout (location = 4) out vec3 v_WorldBitan; -layout (location = 5) out vec4 v_VertColor; - - -void main() -{ - vec3 WorldPosition = vec4(a_Position, 1.0) * a_InstanceMatrix3x4; // a_InstanceMatrix is 3x4 to reduce number of input binding slots needed. Because it is transposed (from the normal glsl expectation of a 4x3) we do a post multiply. - - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = VertCB.VPMatrix * vec4( WorldPosition, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); - - // Need Position in world space - v_WorldPos = WorldPosition; - - // Need Normal, Tangent, and Bitangent in world space. Normalize since the a_InstanceMatrix3x4 may be scaled. - v_WorldNorm = normalize(vec4(a_Normal.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldTan = normalize(vec4(a_Tangent.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldBitan = cross(v_WorldNorm, v_WorldTan); - - // Color is simple attribute color - v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); -} diff --git a/samples/rayQueryShadows/shaders/ObjectEmissive.frag b/samples/rayQueryShadows/shaders/ObjectEmissive.frag deleted file mode 100644 index 29c2782..0000000 --- a/samples/rayQueryShadows/shaders/ObjectEmissive.frag +++ /dev/null @@ -1,99 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Define available features -#define ENABLE_SHADOWS -#define ENABLE_REFLECTIONS -#define ENABLE_REFRACTION - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Texture Locations -#define SHADER_EMISSIVE_TEXTURE_LOC 2 - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - vec4 Color; - vec4 EyePos; - vec4 LightDir; - vec4 LightColor; - - // X: Normal Height - // Y: Normal Mirror Reflect Amount - // Z: Not Used - // W: Not Used - vec4 NormalHeight; - - vec4 MirrorAmount; - - // X: Screen Width - // Y: Screen Height - // Z: One Width Pixel - // W: One Height Pixel - vec4 ScreenSize; - - // X: Shadow Width - // Y: Shadow Height - // Z: One Width Pixel - // W: One Height Pixel - vec4 ShadowSize; - - // X: Mip Level - // Y: Reflect Amount - // Z: Reflect Fresnel Min - // W: Reflect Fresnel Max - vec4 ReflectParams; - - // X: Mip Level - // Y: IBL Amount - // Z: Not Used - // W: Not Used - vec4 IrradianceParams; -} FragCB; - -// Textures -layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 3) in vec3 v_WorldTan; -layout (location = 4) in vec3 v_WorldBitan; -layout (location = 5) in vec4 v_VertColor; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Base Lighting - // ******************************** - // Get emissive color from the color texture - vec4 EmissiveColor = texture( u_EmissiveTex, v_TexCoord.xy ); - //EmissiveColor.xyzw *= FragCB.Color.xyzw; - - // Adjust by vertex color. - //EmissiveColor.xyzw *= v_VertColor.xyzw; - - vec3 FinalColor = EmissiveColor.xyz * 4.0; - - // Write out the color and put depth value in the alpha channel - FragColor = vec4(FinalColor.rgb, FragCB.Color.a); -} diff --git a/samples/rayQueryShadows/shaders/ObjectEmissive.json b/samples/rayQueryShadows/shaders/ObjectEmissive.json deleted file mode 100644 index d971957..0000000 --- a/samples/rayQueryShadows/shaders/ObjectEmissive.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/ObjectDeferred.vert.spv", - "Fragment": "Media/Shaders/ObjectEmissive.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Frag" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Emissive" ] - } - ] - } - ], - "VertexBindings": [ "VB0", "Instances" ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthCompareOp": "LessEqual", - "BlendEnable": false - } - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 24, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Instances", - "Rate": "Instance", - "Elements": [ - { - "Name": "Transform0", - "Offset": 0, - "Type": "Vec4" - }, - { - "Name": "Transform1", - "Offset": 16, - "Type": "Vec4" - }, - { - "Name": "Transform2", - "Offset": 32, - "Type": "Vec4" - } - ] - } - - ] -} diff --git a/samples/rayQueryShadows/shaders/PointLight.frag b/samples/rayQueryShadows/shaders/PointLight.frag deleted file mode 100644 index 1603126..0000000 --- a/samples/rayQueryShadows/shaders/PointLight.frag +++ /dev/null @@ -1,178 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_EXT_ray_query : enable - -// Buffer binding locations -#define SHADER_FRAG_UBO_LOCATION 0 -#define SHADER_ALBEDO_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_RAY_TRACE_AS_LOC 3 - -layout(set = 0, binding = SHADER_ALBEDO_TEXTURE_LOC) uniform sampler2D u_AlbedoTex; // Albedo RGB, Specular is in A -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout(set = 0, binding = SHADER_RAY_TRACE_AS_LOC) uniform accelerationStructureEXT as_RayTrace; - -// Processing just one light against the gbuffer. - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform PointLightUniformBuff -{ - // - // Vertex shader - // - mat4 MVPMatrix; - - // - // Fragment shader (could split in to 2 uniform buffers, one per shader stage) - // - mat4 ProjectionInv; - mat4 ViewInv; - - vec4 CameraPos; - vec3 LightPosition; - float LightIntensity; - vec4 LightColor; - float LightRadius; // Radius of the physical light (not its range of influence) - float LightCutoff; // Cutoff brightness for the light - float SpecScale; - float SpecPower; - vec2 WindowSize; -} PointLightUB; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = PointLightUB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = PointLightUB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - - -//----------------------------------------------------------------------------- -float LightFalloff(vec3 WorldToLightVec, float LightRadius, float LightCutoff, float LightIntensity) -//----------------------------------------------------------------------------- -{ - // calculate normalized light vector and distance to sphere light surface - vec3 L = WorldToLightVec; - float distance = length(L); - float d = max(distance - LightRadius, 0.0); - L /= distance; - - // calculate basic attenuation - float denom = (d/LightRadius) + 1.0; - float LightAttenuation = 1.0 / (denom*denom); - - // scale and bias attenuation - LightAttenuation = LightIntensity * (LightAttenuation - (LightCutoff / LightIntensity)) / (1.0 - (LightCutoff / LightIntensity)); - LightAttenuation = max(LightAttenuation, 0); - - return LightAttenuation; -} - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = gl_FragCoord.xy / PointLightUB.WindowSize; - - // ******************************** - // Albedo Color - // ******************************** - vec4 AlbedoColor = texture( u_AlbedoTex, LocalTexCoord.xy ); - - // ******************************** - // Normal (and depth) - // ******************************** - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 WorldNormal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - - FragColor.a = 1.0; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - // Add in a little bias (along the surface normal) to account for z-depth accuracy. - WorldPos += WorldNormal; - - // ******************************** - // Raytraced Shadow - // ******************************** - float ShadowAtten = 1.0f; // Assume not in shadow - - // Calculate how far away the light is (and its direction) - vec3 WorldToLightVec = PointLightUB.LightPosition.xyz - WorldPos; - float LightDistance = length(WorldToLightVec) - PointLightUB.LightRadius; - vec3 DirectionToLight = normalize(WorldToLightVec); - - float MinDistance = 0.1; - - // Setup the Shadow ray query (to the light). - { - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, as_RayTrace, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF/*cullMask*/, WorldPos, MinDistance, DirectionToLight, LightDistance); - - // Traverse the query. - while(rayQueryProceedEXT(rayQuery)) - { - } - - // Determine if the shadow query collided. - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) - { - // Got an intersection == Shadow - //float intersectionDistance = rayQueryGetIntersectionTEXT(rayQuery, true); - ShadowAtten = 0.0;//intersectionDistance / (LightDistance + 0.1); - } - } - - // ******************************** - // Light falloff - // ******************************** - - float LightAtten = LightFalloff(WorldToLightVec, PointLightUB.LightRadius, PointLightUB.LightCutoff, PointLightUB.LightIntensity); - - vec3 EyeDir = normalize(PointLightUB.CameraPos.xyz - WorldPos); - - vec3 WorldToLightNorm = normalize(WorldToLightVec); - - float LightAng = max(0.0, dot(WorldToLightNorm, WorldNormal)); - - // Spec (blinn-phong) - vec3 LightDir = WorldToLightNorm; - vec3 HalfVector = normalize(LightDir + EyeDir); - float Spec = AlbedoColor.a/*spec scale*/ * pow(max(dot(WorldNormal,HalfVector),0.0), PointLightUB.SpecPower) * PointLightUB.SpecScale; - - vec3 LightAmt = ShadowAtten * PointLightUB.LightColor.rgb * (Spec + LightAng ) * LightAtten; - - FragColor.rgb = AlbedoColor.rgb * LightAmt; - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - //FragColor.rgb = Irradiance; - //FragColor.rgb = WorldNormal; - //FragColor.rgb = vec3(1.0, 0.0f, 0.0);//AlbedoColor.rgb; - //FragColor.rgb = vec3(AmbientOcclusion,AmbientOcclusion,AmbientOcclusion); - //FragColor.a = 1.0; - //FragColor = vec4(0.8, 0.2, 0.8, 1.0); - //FragColor = vec4(LightAmt.x, LightAmt.y, 1.0, 1.0); -} - diff --git a/samples/rayQueryShadows/shaders/PointLight.json b/samples/rayQueryShadows/shaders/PointLight.json deleted file mode 100644 index a009e2c..0000000 --- a/samples/rayQueryShadows/shaders/PointLight.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/PointLight.vert.spv", - "Fragment": "Media/Shaders/PointLight.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex", "Fragment" ], - "Count": 1, - "Names": [ "PointLightFragCB" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Albedo" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "AccelerationStructure", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "AccelerationStructure" ] - } - ] - } - ], - "FixedFunction": { - "xDepthCompareOp": "LessEqual", - "CullBackFace": true, - "xCullFrontFace": true, - "DepthWriteEnable": false - }, - "Outputs": [ - { - "BlendEnable": true, - "SrcColorBlendFactor": "One", - "DstColorBlendFactor": "One" - } - ], - "VertexBindings" : [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/PointLight.vert b/samples/rayQueryShadows/shaders/PointLight.vert deleted file mode 100644 index b611809..0000000 --- a/samples/rayQueryShadows/shaders/PointLight.vert +++ /dev/null @@ -1,50 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 - -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform PointLightUniformBuff -{ - // - // Vertex shader - // - mat4 MVPMatrix; - - // - // Fragment shader (could split in to 2 uniform buffers, one per shader stage) - // - mat4 ProjectionInv; - mat4 ViewInv; - - vec4 CameraPos; - vec3 LightPosition; - float LightIntensity; - vec4 LightColor; - float LightRadius; // Radius of the physical light (not its range of influence) - float LightCutoff; // Cutoff brightness for the light - float SpecScale; - float SpecPower; - vec2 WindowSize; -} PointLightUB; - -void main() -{ - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = PointLightUB.MVPMatrix * vec4(a_Position.xyz, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); -} diff --git a/samples/rayQueryShadows/shaders/RasterizedShadowCull.json b/samples/rayQueryShadows/shaders/RasterizedShadowCull.json deleted file mode 100644 index dd95f40..0000000 --- a/samples/rayQueryShadows/shaders/RasterizedShadowCull.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RasterizedShadowReset", - "Shaders": { - "Compute": "Media/Shaders/RasterizedShadowCullReset.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "StorageBuffer", - "ReadOnly": false, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CulledInstances" ] - }, - { - "Type": "StorageBuffer", - "ReadOnly": false, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CulledIndirectDraw" ] - } - ] - } - ] - }, - { - "Name": "RasterizedShadowCull", - "Shaders": { - "Compute": "Media/Shaders/RasterizedShadowCullInstances.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CameraUniform" ] - }, - { - "Type": "StorageBuffer", - "ReadOnly": true, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "UnculledInstances" ] - }, - { - "Type": "StorageBuffer", - "ReadOnly": false, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CulledInstances" ] - } - ] - } - ] - }, - { - "Name": "RasterizedShadowCullWriteIndirect", - "Shaders": { - "Compute": "Media/Shaders/RasterizedShadowCullWriteIndirect.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "StorageBuffer", - "ReadOnly": true, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "UnculledIndirectDraw" ] - }, - { - "Type": "StorageBuffer", - "ReadOnly": true, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CulledInstances" ] - }, - { - "Type": "StorageBuffer", - "ReadOnly": false, - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "CulledIndirectDraw" ] - } - ] - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/RasterizedShadowCullInstances.comp b/samples/rayQueryShadows/shaders/RasterizedShadowCullInstances.comp deleted file mode 100644 index 9883413..0000000 --- a/samples/rayQueryShadows/shaders/RasterizedShadowCullInstances.comp +++ /dev/null @@ -1,83 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Instance Culling shader for rasterized shadow pass. -// Takes an array of instance matrixes and outputs visibility for each one! -// Output data is -1 (for culled) or the running tally of the number of visible instances, will be used to crunch the instance data down. -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_GOOGLE_include_directive : enable -#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable -#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable -#extension GL_EXT_shader_8bit_storage : enable - -// Uniform buffer locations -#define CAMERA_UBO_LOCATION 0 -// Texture Locations -// Storage Locations -#define UNCULLED_INSTANCES_STORAGE_LOCATION 1 -#define CULLED_INSTANCES_STORAGE_LOCATION 2 - -// Compute workgroup layout -#define WORKGROUP_SIZE (64) -layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1 ) in; - -// Inputs layout -layout(std140, set = 0, binding = CAMERA_UBO_LOCATION) uniform CameraUniformBuff -{ - mat4 MVPMatrix; - vec4 CullPlanes[6]; -} CameraUB; - -layout(std430, set = 0, binding = UNCULLED_INSTANCES_STORAGE_LOCATION) readonly buffer UnculledInstancesInput -{ - uint numInstances; // number of instances in the centerAndRadius array - uint _pad1,_pad2,_pad3; - vec4 centerAndRadius[]; // Instance centers and radii. Unbounded size, must be last item in buffer. -} UnculledInstancesInputSB; - -// Outputs -layout(std430, set = 0, binding = CULLED_INSTANCES_STORAGE_LOCATION) buffer CulledInstancesOutput -{ - int numVisibleInstances; // tally of things marked as visible in visibleInstances[] (atomic) - uint _pad1,_pad2,_pad3; - int visibleInstances[]; // -1 (not visible) or current value of numVisibleInstances (if visible). -} CulledInstancesOutputSB; - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - uint instanceIdx = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * WORKGROUP_SIZE; - - if (instanceIdx < UnculledInstancesInputSB.numInstances) - { - vec4 CenterAndRadius = UnculledInstancesInputSB.centerAndRadius[instanceIdx]; - - // Cull using seperable planes method - for( int i=0; i<6; i++ ) - { - if (dot( CameraUB.CullPlanes[i], vec4(CenterAndRadius.xyz, 1.0f)) < -CenterAndRadius.w ) - { - // Not visible (outside this plane) - CulledInstancesOutputSB.visibleInstances[instanceIdx] = -1; - return; - } - } - // If all tests passed sphere is PROBABLY visible (there is a fringe case where the sphere should be culled and isn't, implement extra checks if it is seen as a problem!) - - // Write the instance as being visible - int visibleInstanceCount = atomicAdd( CulledInstancesOutputSB.numVisibleInstances, 1 ); - CulledInstancesOutputSB.visibleInstances[instanceIdx] = visibleInstanceCount; - } -} diff --git a/samples/rayQueryShadows/shaders/RasterizedShadowCullReset.comp b/samples/rayQueryShadows/shaders/RasterizedShadowCullReset.comp deleted file mode 100644 index 9c4abf6..0000000 --- a/samples/rayQueryShadows/shaders/RasterizedShadowCullReset.comp +++ /dev/null @@ -1,66 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Reset the culling data prior to writing. -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_GOOGLE_include_directive : enable -#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable -#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable -#extension GL_EXT_shader_8bit_storage : enable - -// Uniform buffer locations -// Texture Locations -// Storage Locations -#define CULLED_INSTANCES_STORAGE_LOCATION 0 -#define CULLED_INDIRECT_STORAGE_LOC 1 - -// Compute workgroup layout -#define WORKGROUP_SIZE 1 -layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1 ) in; - -struct VkDrawIndexedIndirectCommand -{ - uint indexCount; - uint instanceCount; - uint firstIndex; - uint vertexOffset; - uint firstInstance; -}; - -// Inputs layout - -// Outputs -layout(std430, set = 0, binding = CULLED_INSTANCES_STORAGE_LOCATION) buffer CulledInstancesOutput -{ - uint numVisibleInstances; // number of instances in visibleInstances[] - uint _pad1,_pad2,_pad3; - int visibleInstances[]; // -1 (not visible) or position within range [0...numVisibleInstances-1] (ie which visible instance this is) -} CulledInstancesOutputSB; - -layout(std430, set = 0, binding = CULLED_INDIRECT_STORAGE_LOC) buffer CulledIndirectOutput -{ - uint numIndirect; // atomic count of number of visible meshlets - uint _pad1,_pad2,_pad3; - uint _pad4[12]; // pad to align the indirect commands to 64 byte boundary! - VkDrawIndexedIndirectCommand indirectCommands[]; // Draw indirect commands for visible meshes, must be last item in buffer -} CulledIndirectOutputSB; - - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - CulledInstancesOutputSB.numVisibleInstances = 0; - CulledIndirectOutputSB.numIndirect = 0; -} diff --git a/samples/rayQueryShadows/shaders/RasterizedShadowCullWriteIndirect.comp b/samples/rayQueryShadows/shaders/RasterizedShadowCullWriteIndirect.comp deleted file mode 100644 index e51d483..0000000 --- a/samples/rayQueryShadows/shaders/RasterizedShadowCullWriteIndirect.comp +++ /dev/null @@ -1,107 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_GOOGLE_include_directive : enable -#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable -#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable -#extension GL_EXT_shader_8bit_storage : enable - -// Uniform buffer locations -// Texture Locations -// Storage Locations -#define UNCULLED_INDIRECT_STORAGE_LOCATION 0 -#define CULLED_INSTANCES_STORAGE_LOCATION 1 -#define CULLED_INDIRECT_STORAGE_LOC 2 - -// Compute workgroup layout -#define WORKGROUP_SIZE (64) -layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1 ) in; - -struct VkDrawIndexedIndirectCommand -{ - uint indexCount; - uint instanceCount; - uint firstIndex; - uint vertexOffset; - uint firstInstance; -}; - -// Inputs layout -layout(std430, set = 0, binding = UNCULLED_INDIRECT_STORAGE_LOCATION) readonly buffer UnculledIndirectInput -{ - uint numIndirect; // number of indirect structs - uint _pad1,_pad2,_pad3; - uint _pad4[12]; // pad to align the indirect commands to 64 byte boundary! - VkDrawIndexedIndirectCommand indirectCommands[]; // Draw indirect commands for unculled meshes, must be last item in buffer -} UnculledIndirectInputSB; - -layout(std430, set = 0, binding = CULLED_INSTANCES_STORAGE_LOCATION) readonly buffer CulledInstancesInput -{ - uint numVisibleInstances; // number of instances in visibleInstances[] - uint _pad1,_pad2,_pad3; - int visibleInstances[]; // -1 (not visible) or position within range [0...numVisibleInstances-1] (ie which visible instance this is) -} CulledInstancesInputSB; - -// Outputs - maps to buffers input into vkCmdDrawIndexedIndirectCount - draw count buffer is at offset 0, draw parameter buffer at offset 16 (offsetof(visible)) -layout(std430, set = 0, binding = CULLED_INDIRECT_STORAGE_LOC) buffer CulledIndirectOutput -{ - uint numIndirect; // atomic count of number of visible meshlets - uint _pad1,_pad2,_pad3; - uint _pad4[12]; // pad to align the indirect commands to 64 byte boundary! - VkDrawIndexedIndirectCommand indirectCommands[]; // Draw indirect commands for visible meshes, must be last item in buffer -} CulledIndirectOutputSB; - - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - uint indirectIdx = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * WORKGROUP_SIZE; - - if (indirectIdx < UnculledIndirectInputSB.numIndirect) - { - VkDrawIndexedIndirectCommand IndirectCommand = UnculledIndirectInputSB.indirectCommands[indirectIdx]; - - uint lastInstance = IndirectCommand.firstInstance + IndirectCommand.instanceCount; - IndirectCommand.instanceCount = 0; - for(uint i = IndirectCommand.firstInstance; i < lastInstance; ++i) - { - if (CulledInstancesInputSB.visibleInstances[i] != -1) - { - ++IndirectCommand.instanceCount; - } - else if (IndirectCommand.instanceCount != 0) - { - // Write a new indirect command for the last portion of the indirect draw. - // Potentially we have 'swiss cheesing' of our instances, causing more culled indirect draws than the unculled input (but fewer instances). - uint visibleIndirectCount = atomicAdd( CulledIndirectOutputSB.numIndirect, 1 ); - CulledIndirectOutputSB.indirectCommands[visibleIndirectCount] = IndirectCommand; - IndirectCommand.instanceCount = 0; - IndirectCommand.firstInstance = i + 1; - } - else - { - // Command is currently empty so keep bumping the start instance. - IndirectCommand.firstInstance = i + 1; - } - } - if (IndirectCommand.instanceCount != 0) - { - // Write a new indirect command for remaining indirect draw. - uint visibleIndirectCount = atomicAdd( CulledIndirectOutputSB.numIndirect, 1 ); - CulledIndirectOutputSB.indirectCommands[visibleIndirectCount] = IndirectCommand; - } - } -} diff --git a/samples/rayQueryShadows/shaders/ShadowGen.vert b/samples/rayQueryShadows/shaders/ShadowGen.vert deleted file mode 100644 index f16996e..0000000 --- a/samples/rayQueryShadows/shaders/ShadowGen.vert +++ /dev/null @@ -1,38 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_INSTANCE_MATRIX 1 /* also 2 and 3 */ - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Vertex rate data -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -// Instance rate data -layout (location = SHADER_ATTRIB_LOC_INSTANCE_MATRIX ) in mat3x4 a_InstanceMatrix3x4; // use a mat3x4 (row major, ie transposed from 'normal' GLSL, but less input slots) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 MVPMatrix; -} VertCB; - -// Varying's -// None! - -void main() -{ - vec3 WorldPosition = vec4(a_Position.xyz, 1.0) * a_InstanceMatrix3x4; // a_InstanceMatrix is 3x4 to reduce number of input binding slots needed. Because it is transposed (from the normal glsl expectation of a 4x3) we do a post multiply. - gl_Position = VertCB.MVPMatrix * vec4(WorldPosition, 1.0); -} diff --git a/samples/rayQueryShadows/shaders/ShadowGenAnimated.vert b/samples/rayQueryShadows/shaders/ShadowGenAnimated.vert deleted file mode 100644 index fa8043b..0000000 --- a/samples/rayQueryShadows/shaders/ShadowGenAnimated.vert +++ /dev/null @@ -1,49 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_INSTANCE_MATRIX 1 /* also 2 and 3 */ - -#define SHADER_VERT_UBO_LOCATION 0 - -// Vertex rate data -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; -// Instance rate data -layout (location = SHADER_ATTRIB_LOC_INSTANCE_MATRIX ) in mat3x4 a_InstanceMatrix3x4; // use a mat3x4 (row major, ie transposed from 'normal' GLSL, but less input slots) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 VPMatrix; - vec4 AnimationRotation; -} VertAnimatedCB; - -#define PI (3.14159) - - -void main() -{ - vec3 LocalPosition = a_Position; - - vec3 offset, tmp; - offset.x = modf(LocalPosition.x*0.01, tmp.x); - offset.y = 0.0; - offset.z = modf(LocalPosition.z*0.01, tmp.y); - float sway = clamp(LocalPosition.y, 0.0, -2.0) * 1.0; - LocalPosition.x = LocalPosition.x + sin(VertAnimatedCB.AnimationRotation.z)*sway + 1.0*sin(VertAnimatedCB.AnimationRotation.x + 2.0 * PI * offset.x); - LocalPosition.z = LocalPosition.z + sin(VertAnimatedCB.AnimationRotation.w)*sway + 1.0*sin(VertAnimatedCB.AnimationRotation.y + 2.0 * PI * offset.z); - - vec3 WorldPosition = vec4(LocalPosition, 1.0) * a_InstanceMatrix3x4; // a_InstanceMatrix is 3x4 to reduce number of input binding slots needed. Because it is transposed (from the normal glsl expectation of a 4x3) we do a post multiply. - vec4 TempPos = VertAnimatedCB.VPMatrix * vec4( WorldPosition, 1.0); - - gl_Position = TempPos; -} diff --git a/samples/rayQueryShadows/shaders/ShadowRQ.comp b/samples/rayQueryShadows/shaders/ShadowRQ.comp deleted file mode 100644 index 87f773e..0000000 --- a/samples/rayQueryShadows/shaders/ShadowRQ.comp +++ /dev/null @@ -1,136 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Rayqueried shadow compute shader. -// Uses screen depth buffer and normal buffer (from gbuffer pass) to determine if the rendered screen pixel is -// visible from the light casting position (a ray query is run from the screen world position to the light world position). -// The output buffer is written with 1.0 if the pixel is visible from the light (unshadowed) and 0.1 otherwise (in shadow). -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -//#extension GL_EXT_ray_tracing : enable -#extension GL_EXT_ray_query : enable - -// Uniform buffer locations -#define SHADER_FRAG_UBO_LOCATION 0 -// Texture Locations -#define SHADER_DEPTH_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_RAY_TRACE_AS_LOC 3 -#define SHADER_OUTPUT_IMAGE_LOC 4 - -#define WORKGROUP_SIZE (32) -layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in; - -layout (binding = SHADER_DEPTH_TEXTURE_LOC) uniform sampler2D TexPositionDepth; -layout (binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout (binding = SHADER_RAY_TRACE_AS_LOC, set = 0) uniform accelerationStructureEXT rayTraceAS; //set=0 required (compiler issue) https://github.com/KhronosGroup/glslang/issues/2247 -layout (binding = SHADER_OUTPUT_IMAGE_LOC, rgba8) uniform image2D imageDest; - -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - // X: Screen Width - // Y: Screen Height - // Z: One Width Pixel - // W: One Height Pixel - vec4 ScreenSize; - - mat4 ProjectionInv; - mat4 ViewInv; - - vec4 LightWorldPos; // xyz = position of point light, OR position that determines length of directional probe ( dot(world position to light position, LightWorldDirection) ) - vec4 LightWorldDirection; // xyz = normalized direction (if cDirectionalLight = true) - -} FragCB; - -layout(constant_id = 0) const bool cDirectionalLight = false; // false = pointlight, true = directional - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - ivec2 LocalTexel = ivec2(gl_GlobalInvocationID.xy); - vec2 LocalTexCoord = vec2( FragCB.ScreenSize.zw * (vec2(LocalTexel) + vec2(0.5,0.5)) ); - - // Normal (and depth) from gbuffer - vec4 NormalWithDepth = texelFetch( u_NormalTex, LocalTexel, 0 ); - vec3 WorldNormal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // Add in a little bias (along the surface normal) to account for z-depth accuracy. - WorldPos += WorldNormal; - - // Calculate how far away the light is (and its direction) - float LightDistance; - vec3 DirectionToLight; - if (cDirectionalLight) - { - vec3 PixelWorldToLightWorld = FragCB.LightWorldPos.xyz - WorldPos; - DirectionToLight = -FragCB.LightWorldDirection.xyz; - LightDistance = dot(PixelWorldToLightWorld, DirectionToLight); - } - else - { - // Point light - LightDistance = distance(WorldPos, FragCB.LightWorldPos.xyz) - 20.0; - DirectionToLight = normalize(FragCB.LightWorldPos.xyz - WorldPos); - } - - float minDistance = 0.1; - vec3 outColor = vec3(1.0,1.0,1.0); - - // Setup the Shadow ray query (to the light). - { - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, rayTraceAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF/*cullMask*/, WorldPos, minDistance, DirectionToLight, LightDistance); - - // Traverse the query. - while(rayQueryProceedEXT(rayQuery)) - { - } - - // Determine if the shadow query collided. - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) - { - // Got an intersection == Shadow - - float intersectionDistance = rayQueryGetIntersectionTEXT(rayQuery, true); - - outColor *= 0.1;//intersectionDistance / (LightDistance + 0.1); - } - } - - /* Output */ - vec4 FragColor; - FragColor.rgb = outColor; - FragColor.a = 1.0; - - imageStore(imageDest, LocalTexel, FragColor); -} diff --git a/samples/rayQueryShadows/shaders/ShadowRQ.json b/samples/rayQueryShadows/shaders/ShadowRQ.json deleted file mode 100644 index 954a02c..0000000 --- a/samples/rayQueryShadows/shaders/ShadowRQ.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "ShadowRayQuery", - "Shaders": { - "Compute": "Media/Shaders/ShadowRQ.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "Uniform" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "Depth" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "AccelerationStructure", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "AccelerationStructure" ] - }, - { - "Type": "ImageStorage", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "Output" ] - } - ] - } - ], - "SpecializationConstants": [ - { - "Name": "DirectionalLight", - "Type": "Boolean" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/ShadowRQFrag.frag b/samples/rayQueryShadows/shaders/ShadowRQFrag.frag deleted file mode 100644 index 9325ed3..0000000 --- a/samples/rayQueryShadows/shaders/ShadowRQFrag.frag +++ /dev/null @@ -1,135 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Raytraced shadow compute shader. -// Uses screen depth buffer and normal buffer (from gbuffer pass) to determine if the rendered screen pixel is -// visible from the light casting position (a ray query is run from the screen world position to the light world position). -// The output buffer is written with 1.0 if the pixel is visible from the light (unshadowed) and 0.1 otherwise (in shadow). -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -//#extension GL_EXT_ray_tracing : enable -#extension GL_EXT_ray_query : enable - -// Uniform buffer locations -#define SHADER_FRAG_UBO_LOCATION 0 -// Texture Locations -#define SHADER_DEPTH_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_RAY_TRACE_AS_LOC 3 - -layout (binding = SHADER_DEPTH_TEXTURE_LOC) uniform sampler2D TexPositionDepth; -layout (binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout (binding = SHADER_RAY_TRACE_AS_LOC, set = 0) uniform accelerationStructureEXT rayTraceAS; //set=0 required (compiler issue) https://github.com/KhronosGroup/glslang/issues/2247 - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - // X: Screen Width - // Y: Screen Height - // Z: One Width Pixel - // W: One Height Pixel - vec4 ScreenSize; - - mat4 ProjectionInv; - mat4 ViewInv; - - vec4 LightWorldPos; // xyz = position of point light, OR position that determines length of directional probe ( dot(world position to light position, LightWorldDirection) ) - vec4 LightWorldDirection; // xyz = normalized direction (if cDirectionalLight = true) - -} FragCB; - -layout(constant_id = 0) const bool cDirectionalLight = false; // false = pointlight, true = directional - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - //vec2 LocalTexCoord = vec2( FragCB.ScreenSize.z * gl_GlobalInvocationID.x, FragCB.ScreenSize.w * gl_GlobalInvocationID.y); - - // Normal (and depth) from gbuffer - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 WorldNormal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // Add in a little bias (along the surface normal) to account for z-depth accuracy. - WorldPos += WorldNormal; - - // Calculate how far away the light is (and its direction) - float LightDistance; - vec3 DirectionToLight; - if (cDirectionalLight) - { - vec3 PixelWorldToLightWorld = FragCB.LightWorldPos.xyz - WorldPos; - DirectionToLight = -FragCB.LightWorldDirection.xyz; - LightDistance = dot(PixelWorldToLightWorld, DirectionToLight); - } - else - { - // Point light - LightDistance = distance(WorldPos, FragCB.LightWorldPos.xyz) - 20.0; - DirectionToLight = normalize(FragCB.LightWorldPos.xyz - WorldPos); - } - - float minDistance = 0.1; - vec3 outColor = vec3(1.0,1.0,1.0); - - // Setup the Shadow ray query (to the light). - { - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, rayTraceAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF/*cullMask*/, WorldPos, minDistance, DirectionToLight, LightDistance); - - // Traverse the query. - while(rayQueryProceedEXT(rayQuery)) - { - } - - // Determine if the shadow query collided. - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) - { - // Got an intersection == Shadow - - float intersectionDistance = rayQueryGetIntersectionTEXT(rayQuery, true); - - outColor *= 0.1;//intersectionDistance / (LightDistance + 0.1); - } - } - - /* Output */ - FragColor.rgb = outColor; - FragColor.a = 1.0; -} diff --git a/samples/rayQueryShadows/shaders/ShadowRQFrag.json b/samples/rayQueryShadows/shaders/ShadowRQFrag.json deleted file mode 100644 index 0e9abe5..0000000 --- a/samples/rayQueryShadows/shaders/ShadowRQFrag.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_RAYSHADOW", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/ShadowRQFrag.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Uniform" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Depth" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - }, - { - "Type": "AccelerationStructure", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "AccelerationStructure" ] - } - ] - } - ], - "SpecializationConstants": [ - { - "Name": "DirectionalLight", - "Type": "Boolean" - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/ShadowRT.comp b/samples/rayQueryShadows/shaders/ShadowRT.comp deleted file mode 100644 index 525599a..0000000 --- a/samples/rayQueryShadows/shaders/ShadowRT.comp +++ /dev/null @@ -1,125 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -// -// Raytraced shadow compute shader. -// Uses screen depth buffer and normal buffer (from gbuffer pass) to determine if the rendered screen pixel is -// visible from the light casting position (a ray query is run from the screen world position to the light world position). -// The output buffer is written with 1.0 if the pixel is visible from the light (unshadowed) and 0.1 otherwise (in shadow). -// - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -//#extension GL_EXT_ray_tracing : enable -#extension GL_EXT_ray_query : enable - -// Uniform buffer locations -#define SHADER_FRAG_UBO_LOCATION 0 -// Texture Locations -#define SHADER_DEPTH_TEXTURE_LOC 1 -#define SHADER_NORMAL_TEXTURE_LOC 2 -#define SHADER_RAY_TRACE_AS_LOC 3 -#define SHADER_OUTPUT_IMAGE_LOC 4 - -#define WORKGROUP_SIZE (32) -layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in; - -layout (binding = SHADER_DEPTH_TEXTURE_LOC) uniform sampler2D TexPositionDepth; -layout (binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; -layout (binding = SHADER_RAY_TRACE_AS_LOC, set = 0) uniform accelerationStructureEXT rayTraceAS; //set=0 required (compiler issue) https://github.com/KhronosGroup/glslang/issues/2247 -layout (binding = SHADER_OUTPUT_IMAGE_LOC, rgba8) uniform image2D imageDest; - -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - // Dest image size (used in subsequent filter stages) - int width; - int height; - - // X: Screen Width - // Y: Screen Height - // Z: One Width Pixel - // W: One Height Pixel - vec4 ScreenSize; - - mat4 CameraProjection; - mat4 ProjectionInv; - mat4 ViewInv; - - vec4 LightWorldPos; - -} FragCB; - - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2( FragCB.ScreenSize.zw * (vec2(gl_GlobalInvocationID.xy) + vec2(0.5,0.5)) ); - - // Normal (and depth) from gbuffer - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 WorldNormal = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - - // Determine World position of pixel - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // Add in a little bias (along the surface normal) to account for z-depth accuracy. - WorldPos += WorldNormal; - - // Calculate how far away the light is (and its direction) - float LightDistance = distance(WorldPos, FragCB.LightWorldPos.xyz) - 20.0; - vec3 DirectionToLight = normalize(FragCB.LightWorldPos.xyz - WorldPos); - - float minDistance = 0.1; - vec3 outColor = vec3(1.0,1.0,1.0); - - // Setup the Shadow ray query (to the light). - { - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, rayTraceAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF/*cullMask*/, WorldPos, minDistance, DirectionToLight, LightDistance); - - // Traverse the query. - while(rayQueryProceedEXT(rayQuery)) - { - } - - // Determine if the shadow query collided. - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) - { - // Got an intersection == Shadow - - float intersectionDistance = rayQueryGetIntersectionTEXT(rayQuery, true); - - outColor *= 0.1;//intersectionDistance / (LightDistance + 0.1); - } - } - - /* Output */ - vec4 FragColor; - FragColor.rgb = outColor; - FragColor.a = 1.0; - - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y), FragColor); -} diff --git a/samples/rayQueryShadows/shaders/ShadowShared.h b/samples/rayQueryShadows/shaders/ShadowShared.h deleted file mode 100644 index 5f4f8cf..0000000 --- a/samples/rayQueryShadows/shaders/ShadowShared.h +++ /dev/null @@ -1,140 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#ifndef _SHADOWSHARED_H_ -#define _SHADOWSHARED_H_ - -// -// Shared Implementation of Shadow mapping code. -// - -//----------------------------------------------------------------------------- -float GetInShadow(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - float fBias = 0.001; - float NormalizedCoord = (LocalShadowCoord.z / LocalShadowCoord.w) - fBias; - - // Flag for in shadow - // Step returns 0.0 of second parameter is less that first parameter. Returns 1.0 otherwise - -// float InShadow = (LocalShadowCoord.x >= 0.0 && LocalShadowCoord.x < 1.0 && LocalShadowCoord.y >= 0.0 && LocalShadowCoord.y < 1.0) ? 1 : 0; - float InShadow = ( texture( u_ShadowMap, LocalShadowCoord.xy ) ).r; - InShadow = min(1.0, step(0.0, InShadow - NormalizedCoord) + smoothstep(0.7, 1.0, InShadow)); - - return InShadow; -} - -#ifdef USE_VARIANCE_SHADOWMAP -#ifndef SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE -#define SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE 0 -#endif // SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE - -//----------------------------------------------------------------------------- -float GetInShadowVSM(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - // Variance Shadow Map. - float NormalizedCoord = LocalShadowCoord.z + 0.02; - - vec2 DepthAndDepthSq = texture(u_ShadowMap, LocalShadowCoord.xy).xy; - float Variance = DepthAndDepthSq.y - (DepthAndDepthSq.x * DepthAndDepthSq.x); - Variance = max( Variance, 0.01 ); // Clamp variance to help with self-shadowing - - float edgeFade = smoothstep(0.40, 0.50, max(abs(LocalShadowCoord.x - 0.5), abs(LocalShadowCoord.y - 0.5))); //1 outside 'edge' of shadow map, 0 inside he map area - float Distance = NormalizedCoord - DepthAndDepthSq.x; - if (Distance < 0.0) - return 1.0;//not shadowed -#if SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE - return max(edgeFade, Variance / (Variance + (Distance * Distance))); -#else - return max(edgeFade, Variance); -#endif // SHADOW_VARIANCE_DISTANCE_ATTEN_DISABLE -} -#endif // USE_VARIANCE_SHADOWMAP - -#ifdef USE_PCF_SAMPLING -//----------------------------------------------------------------------------- -float GetInShadowPCF(vec4 LocalShadowCoord) -//----------------------------------------------------------------------------- -{ - float RetVal = 0.0; - - // Set up the blur coordinates... - vec4 blurTexCoords[16]; - blurTexCoords[ 0] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 1] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 2] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 3] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, -1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 4] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 5] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 6] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 7] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, -0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 8] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 9] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 10] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 11] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, 0.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - blurTexCoords[ 12] = LocalShadowCoord + vec4(-1.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 13] = LocalShadowCoord + vec4(-0.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 14] = LocalShadowCoord + vec4( 0.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - blurTexCoords[ 15] = LocalShadowCoord + vec4( 1.5 * FragCB.ShadowSize.z, 1.5 * FragCB.ShadowSize.w, 0.0, 0.0); - - - // ...and then read the values - RetVal += GetInShadow(blurTexCoords[ 0]); - RetVal += GetInShadow(blurTexCoords[ 1]); - RetVal += GetInShadow(blurTexCoords[ 2]); - RetVal += GetInShadow(blurTexCoords[ 3]); - - RetVal += GetInShadow(blurTexCoords[ 4]); - RetVal += GetInShadow(blurTexCoords[ 5]); - RetVal += GetInShadow(blurTexCoords[ 6]); - RetVal += GetInShadow(blurTexCoords[ 7]); - - RetVal += GetInShadow(blurTexCoords[ 8]); - RetVal += GetInShadow(blurTexCoords[ 9]); - RetVal += GetInShadow(blurTexCoords[ 10]); - RetVal += GetInShadow(blurTexCoords[ 11]); - - RetVal += GetInShadow(blurTexCoords[ 12]); - RetVal += GetInShadow(blurTexCoords[ 13]); - RetVal += GetInShadow(blurTexCoords[ 14]); - RetVal += GetInShadow(blurTexCoords[ 15]); - - // Just took 16 samples so divide to get average - RetVal /= 16.0 ; - - return RetVal; -} -#endif // USE_PCF_SAMPLING - -//----------------------------------------------------------------------------- -float GetShadowAmount(vec4 ShadowCoord) -//----------------------------------------------------------------------------- -{ - // Shadow Calculations (textureProj returns 1.0 for pass (not in shadow) and 0.0 for fail (in shadow)) -#if defined(USE_VARIANCE_SHADOWMAP) - float ShadowAmount = GetInShadowVSM(ShadowCoord); -#elif defined(USE_PCF_SAMPLING) - float ShadowAmount = GetInShadowPCF(ShadowCoord); -#else - float ShadowAmount = GetInShadow(ShadowCoord); -#endif - - //float MinShadow = 0.5f; - //ShadowAmount.w = max(ShadowAmount.w, MinShadow); - - ShadowAmount = mix(0.5, 1.0, ShadowAmount); - return ShadowAmount; -} - -#endif // _SHADOWSHARED_H_ - diff --git a/samples/rayQueryShadows/shaders/Skybox.json b/samples/rayQueryShadows/shaders/Skybox.json deleted file mode 100644 index 309c74d..0000000 --- a/samples/rayQueryShadows/shaders/Skybox.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/Skybox.vert.spv", - "Fragment": "Media/Shaders/Skybox.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Environment" ] - } - ] - } - ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": false - }, - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 24, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" - } - ] - } - ] -} diff --git a/samples/rayQueryShadows/shaders/Skybox.vert b/samples/rayQueryShadows/shaders/Skybox.vert deleted file mode 100644 index f68539d..0000000 --- a/samples/rayQueryShadows/shaders/Skybox.vert +++ /dev/null @@ -1,60 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 -// #define SHADER_ATTRIB_LOC_BITANGENT 5 -#define NUM_SHADER_ATTRIB_LOCATIONS 6 - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 - -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 MVPMatrix; - mat4 ModelMatrix; - vec4 Color; -} VertCB; - -// Varying's -layout (location = 0) out vec2 v_TexCoord; -layout (location = 1) out vec3 v_WorldPos; -layout (location = 2) out vec3 v_WorldNorm; -layout (location = 6) out vec4 v_VertColor; - - -void main() -{ - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = VertCB.MVPMatrix * vec4(a_Position.xyz, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); - - // Need Position in world space - v_WorldPos = (VertCB.ModelMatrix * vec4(a_Position.xyz, 1.0)).xyz; - - // Need Normal in world space (Normals point inward on current model) - v_WorldNorm = -(VertCB.ModelMatrix * vec4(a_Normal.xyz, 0.0)).xyz; - - // Color is simple attribute color multiplied by constant color - v_VertColor.xyzw = vec4(a_Color.xyz * VertCB.Color.xyz, VertCB.Color.w); -} diff --git a/samples/rayQueryShadows/shaders/VarianceShadowMap.json b/samples/rayQueryShadows/shaders/VarianceShadowMap.json deleted file mode 100644 index 675d571..0000000 --- a/samples/rayQueryShadows/shaders/VarianceShadowMap.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "vsm_horizontal", - "Shaders": { - "Compute": "Media/Shaders/VarianceShadowMap1024_horizontal.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "ImageSampler", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "ShadowDepth" ] - }, - { - "Type": "ImageStorage", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "VarianceShadowMapIntermediate" ] - } - ] - } - ], - "WorkGroup": { "LocalSize": [16,16,1]} - }, - { - "Name": "vsm_vertical", - "Shaders": { - "Compute": "Media/Shaders/VarianceShadowMap1024_vertical.comp.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "ImageSampler", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "VarianceShadowMapIntermediate" ] - }, - { - "Type": "ImageStorage", - "Stages": [ "Compute" ], - "Count": 1, - "Names": [ "VarianceShadowMap" ] - } - ] - } - ], - "WorkGroup": { "LocalSize": [16,8,1]} - } - ] -} diff --git a/samples/rayQueryShadows/shaders/VarianceShadowMap1024_horizontal.comp b/samples/rayQueryShadows/shaders/VarianceShadowMap1024_horizontal.comp deleted file mode 100644 index 28c11c4..0000000 --- a/samples/rayQueryShadows/shaders/VarianceShadowMap1024_horizontal.comp +++ /dev/null @@ -1,112 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable -#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable - -// -// VarianceShadowMap Filter -// First pass generates the depth & depth squared values with a horizontal filter kernel. -// - -#define WORKGROUP_SIZE_X (8) -#define WORKGROUP_SIZE_Y (8) - -precision mediump float; -precision mediump int; - -layout (local_size_x = WORKGROUP_SIZE_X, local_size_y = WORKGROUP_SIZE_Y, local_size_z = 1 ) in; -//layout(set = 0, binding = 0, r32f) uniform readonly image2D imageSrc; -layout(set = 0, binding = 0/*, r32f*/) uniform mediump sampler2D imageSrc; -layout(set = 0, binding = 1, rgba16f) uniform mediump image2D imageIntemediateDest; - -const int cFilterWidth = 4; -const float cfFilterWidth = 4.0; -const float coefficients[] = {0.0162162162HF, 0.0540540541HF, 0.1216216216HF, 0.1945945946HF, 0.2270270270HF, 0.1945945946HF, 0.1216216216HF, 0.0540540541HF, 0.0162162162HF}; - -const int cMaxLinePizels = WORKGROUP_SIZE_X; -const int cMaxLinePixels = cMaxLinePizels + cFilterWidth; - -void main() { -#if 1 - - // - // Raw texture read (gather) version - // - vec2 gatheredDepth[2*(cFilterWidth+1)]; // x component is upper line, y component is lower line - // Grab (cFilterWidth+1)*2 horizontal depth values, for 2 (vertical) rows (4 pixels at a time using gather) - // Gather access pattern is: A B - // R G - vec2 vCenterTex = vec2((gl_GlobalInvocationID.x)*2 + 0.5, gl_GlobalInvocationID.y*2 + 0.5)/1024.0f; - for(int i=0;i<=cFilterWidth;++i) - { - vec4 d = textureGatherOffset(imageSrc, vCenterTex, ivec2(i - cfFilterWidth, 0 )); - gatheredDepth[i*2+0] = vec2(d.w, d.x); - gatheredDepth[i*2+1] = vec2(d.z, d.y); - } - - vec4 sum = vec4(0.0f); - vec4 sum2 = vec4(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - float c = coefficients[(i= cMaxLinePixels - cFilterWidth/2) - { - sDepthLine[cFilterWidth + cMaxLinePixels - 1 - gl_GlobalInvocationID.x] = depth; - } - - barrier(); - - // Now do the averaging for our 4 pixels -#if 1 - vec4 sumD = vec4(0.0f); - vec4 sumD2 = vec4(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - float c = coefficients[i]; - vec4 d = sDepthLine[gl_GlobalInvocationID.x + i]; - sumD += c * d; // sum depth - sumD2 += c * d * d; // sum depth squared - } -#else - float d = sDepthLine[gl_GlobalInvocationID.x + cFilterWidth]; - vec2 sum = vec2(d, d*d); -#endif - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2,gl_GlobalInvocationID.y*2), vec4(sumD.w, sumD2.w, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2+1,gl_GlobalInvocationID.y*2), vec4(sumD.z, sumD2.z, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2,gl_GlobalInvocationID.y*2+1), vec4(sumD.x, sumD2.x, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2+1,gl_GlobalInvocationID.y*2+1), vec4(sumD.y, sumD2.y, 0.0, 0.0)); -#endif -} diff --git a/samples/rayQueryShadows/shaders/VarianceShadowMap1024_vertical.comp b/samples/rayQueryShadows/shaders/VarianceShadowMap1024_vertical.comp deleted file mode 100644 index 08aba6b..0000000 --- a/samples/rayQueryShadows/shaders/VarianceShadowMap1024_vertical.comp +++ /dev/null @@ -1,227 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable -#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable - -// -// VarianceShadowMap Filter -// Second pass filters vertically (separable blur). -// - -#define WORKGROUP_SIZE_X (8) -#define WORKGROUP_SIZE_Y (8) - -precision mediump float; -precision mediump int; // removing this causes Adreno compiler to optimize out all but one texelFetchOffset (as of June2021) - -layout (local_size_x = WORKGROUP_SIZE_X, local_size_y = WORKGROUP_SIZE_Y, local_size_z = 1 ) in; -//layout(set = 0, binding = 0, rg32f) uniform image2D image; -layout(set = 0, binding = 0) uniform mediump sampler2D imageIntemediateSrc; -layout(set = 0, binding = 1, rg16f) uniform mediump image2D imageDest; - -const int cFilterWidth = 4; -const float cfFilterWidth = 4.0; -const mediump float coefficients[] = {0.0162162162HF, 0.0540540541HF, 0.1216216216HF, 0.1945945946HF, 0.2270270270HF, 0.1945945946HF, 0.1216216216HF, 0.0540540541HF, 0.0162162162HF}; -//const int cFilterWidth = 10; -//const float cfFilterWidth = 10.0; -//const float coefficients[] = {0.000539, 0.001533, 0.003908, 0.008925, 0.018255, 0.033446, 0.054891, 0.080693, 0.106259, 0.125337, 0.132429, 0.125337, 0.106259, 0.080693, 0.054891, 0.033446, 0.018255, 0.008925, 0.003908, 0.001533, 0.000539}; - -//const int cMaxLinePixels = WORKGROUP_SIZE_Y; -//const int cNumPixelsCache = cMaxLinePixels + cFilterWidth*2; -//shared vec2 sDepthLine[cNumPixelsCache]; - -void main() { - -#if 1 - // - // Brute texture reads of packed format (using texelFetch) - // - - // RGBA is such that AB (WZ) are horizontally adjacent pairs of (filtered) depth values on top line, RG (XY) are horizontally adjacent pairs of (filtered) depth^2 values on top line - // So we filter 2 pixels at a time! - - ivec2 inTexCoord = ivec2(gl_GlobalInvocationID.xy); - - // Read and average (vertically) for our two (horizontal) pixels - vec4 sum = texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,0 - cfFilterWidth)) * coefficients[0]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,1 - cfFilterWidth)) * coefficients[1]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,2 - cfFilterWidth)) * coefficients[2]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,3 - cfFilterWidth)) * coefficients[3]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,4 - cfFilterWidth)) * coefficients[4]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,5 - cfFilterWidth)) * coefficients[3]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,6 - cfFilterWidth)) * coefficients[2]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,7 - cfFilterWidth)) * coefficients[1]; - sum += texelFetchOffset(imageIntemediateSrc, inTexCoord, 0, ivec2(0,8 - cfFilterWidth)) * coefficients[0]; - - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2, gl_GlobalInvocationID.y), vec4(sum.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*2+1, gl_GlobalInvocationID.y), vec4(sum.zw, 0.0, 0.0)); -#elif 1 - // - // Brute texture reads of packed format (using textureGather) - // - - // textureGather gets one channel of data for a square of 4 pixels. - // Channel 0: pixel (0,0) - // Channel 1: pixel (0,1) - // Channel 2: pixel (1,0) - // Channel 3: pixel (1,1) - // Gather RGBA is such that AB (WZ) are horizontally adjacent pairs of (filtered) depth values on top line, RG (XY) are horizontally adjacent pairs of (filtered) depth^2 values on top line - - vec4 gatheredDepth0[2*(cFilterWidth+1)]; - vec4 gatheredDepth1[2*(cFilterWidth+1)]; - for(int i=0;i<=cFilterWidth;++i) - { - vec2 inTexCoord = vec2((gl_GlobalInvocationID.x*2 + 0.5)/512.0, ((gl_GlobalInvocationID.y+i)*2 - cfFilterWidth + 0.5)/1024.0); - vec4 d0 = textureGather(imageIntemediateSrc, inTexCoord, 0); - vec4 d1 = textureGather(imageIntemediateSrc, inTexCoord, 1); - vec4 d2 = textureGather(imageIntemediateSrc, inTexCoord, 2); - vec4 d3 = textureGather(imageIntemediateSrc, inTexCoord, 3); - - gatheredDepth0[i*2+0] = vec4(d0.w, d0.x, d1.w, d1.x); // depth, depth^2 for pixel and x+1 pixel - gatheredDepth1[i*2+0] = vec4(d0.z, d0.y, d1.z, d1.y); // depth, depth^2 for x+2 and x+3 pixel - - gatheredDepth0[i*2+1] = vec4(d2.w, d2.x, d3.w, d3.x); // depth, depth^2 for x,y+1 and x+1,y+1 pixel - gatheredDepth1[i*2+1] = vec4(d2.z, d2.y, d3.z, d3.y); // depth, depth^2 for x+2,y+1 and x+3,y+1 pixel - } - - // Now do the averaging for our pixel - vec4 sum00 = vec4(0.0f); - vec4 sum01 = vec4(0.0f); - vec4 sum10 = vec4(0.0f); - vec4 sum11 = vec4(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - sum00 += gatheredDepth0[i] * coefficients[i]; - sum10 += gatheredDepth0[i+1] * coefficients[i]; - sum01 += gatheredDepth1[i] * coefficients[i]; - sum11 += gatheredDepth1[i+1] * coefficients[i]; - } - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4, gl_GlobalInvocationID.y*2), vec4(sum00.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+1, gl_GlobalInvocationID.y*2), vec4(sum00.zw, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+2, gl_GlobalInvocationID.y*2), vec4(sum01.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+3, gl_GlobalInvocationID.y*2), vec4(sum01.zw, 0.0, 0.0)); - - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4, gl_GlobalInvocationID.y*2+1), vec4(sum10.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+1, gl_GlobalInvocationID.y*2+1), vec4(sum10.zw, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+2, gl_GlobalInvocationID.y*2+1), vec4(sum11.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+3, gl_GlobalInvocationID.y*2+1), vec4(sum11.zw, 0.0, 0.0)); -#elif 1 - // - // Brute force texture gathers - // - - // textureGather gets one channel of data for a square of 4 pixels. - // Channel 0: pixel (0,0) - // Channel 1: pixel (0,1) - // Channel 2: pixel (1,0) - // Channel 3: pixel (1,1) - // Gather RGBA is such that AB (WZ) are horizontally adjacent pairs of (filtered) depth values on top line, RG (XY) are horizontally adjacent pairs of (filtered) depth^2 values on top line - - vec4 gatheredDepth0[2*(cFilterWidth+1)]; - vec4 gatheredDepth1[2*(cFilterWidth+1)]; - for(int i=0;i<=cFilterWidth;++i) - { - vec2 inTexCoord = vec2((gl_GlobalInvocationID.x*2 + 0.5)/512.0, ((gl_GlobalInvocationID.y+i)*2 - cfFilterWidth + 0.5)/1024.0); - vec4 d0 = textureGather(imageIntemediateSrc, inTexCoord, 0); - vec4 d1 = textureGather(imageIntemediateSrc, inTexCoord, 1); - vec4 d2 = textureGather(imageIntemediateSrc, inTexCoord, 2); - vec4 d3 = textureGather(imageIntemediateSrc, inTexCoord, 3); - - gatheredDepth0[i*2+0] = vec4(d0.w, d0.x, d1.w, d1.x); // depth, depth^2 for pixel and x+1 pixel - gatheredDepth1[i*2+0] = vec4(d0.z, d0.y, d1.z, d1.y); // depth, depth^2 for x+2 and x+3 pixel - - gatheredDepth0[i*2+1] = vec4(d2.w, d2.x, d3.w, d3.x); // depth, depth^2 for x,y+1 and x+1,y+1 pixel - gatheredDepth1[i*2+1] = vec4(d2.z, d2.y, d3.z, d3.y); // depth, depth^2 for x+2,y+1 and x+3,y+1 pixel - } - - // Now do the averaging for our pixel - vec4 sum00 = vec4(0.0f); - vec4 sum01 = vec4(0.0f); - vec4 sum10 = vec4(0.0f); - vec4 sum11 = vec4(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - sum00 += gatheredDepth0[i] * coefficients[i]; - sum10 += gatheredDepth0[i+1] * coefficients[i]; - sum01 += gatheredDepth1[i] * coefficients[i]; - sum11 += gatheredDepth1[i+1] * coefficients[i]; - } - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4, gl_GlobalInvocationID.y*2), vec4(sum00.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+1, gl_GlobalInvocationID.y*2), vec4(sum00.zw, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+2, gl_GlobalInvocationID.y*2), vec4(sum01.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+3, gl_GlobalInvocationID.y*2), vec4(sum01.zw, 0.0, 0.0)); - - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4, gl_GlobalInvocationID.y*2+1), vec4(sum10.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+1, gl_GlobalInvocationID.y*2+1), vec4(sum10.zw, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+2, gl_GlobalInvocationID.y*2+1), vec4(sum11.xy, 0.0, 0.0)); - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x*4+3, gl_GlobalInvocationID.y*2+1), vec4(sum11.zw, 0.0, 0.0)); - -#elif 1 - // - // Brute force texture reads (intermediate input packed) - // - - // Now do the averaging for our pixel - vec2 sum = vec2(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - vec2 depth = texture(imageSrc, vec2(gl_GlobalInvocationID.x + 0.5,gl_GlobalInvocationID.y - cfFilterWidth + i + 0.5)/1024.0).xy; - sum += depth * coefficients[i]; - } - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y), vec4(sum, 0.0, 0.0)); - -#elif 1 - // - // Brute force texture reads - // - - // Now do the averaging for our pixel - vec2 sum = vec2(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - vec2 depth = texture(imageSrc, vec2(gl_GlobalInvocationID.x + 0.5,gl_GlobalInvocationID.y - cfFilterWidth + i + 0.5)/1024.0).xy; - sum += depth * coefficients[i]; - } - imageStore(imageDest, ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y), vec4(sum, 0.0, 0.0)); -#else - // - // Barrier/group-shared memory version - // - - // read pixel (depth and depth squared), write to cache and wait for all the pixels in this line to finish - vec4 depth = imageLoad(image, ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y)); - sDepthLine[gl_GlobalInvocationID.y + cFilterWidth] = depth.xy; - // mirrror the edge values - if (gl_GlobalInvocationID.y < cFilterWidth) - { - sDepthLine[cFilterWidth - 1 - gl_GlobalInvocationID.y] = depth.xy; - } - else if (gl_GlobalInvocationID.y >= cMaxLinePixels - cFilterWidth) - { - sDepthLine[cFilterWidth + cMaxLinePixels*2 - 1 - gl_GlobalInvocationID.y] = depth.xy; - } - - // wait for all workgroup threads to be finished - barrier(); - - // Now do the averaging for our pixel -#if 1 - vec2 sum = vec2(0.0f); - for(int i=0;i<=cFilterWidth*2;++i) - { - sum += sDepthLine[gl_GlobalInvocationID.y + i] * coefficients[i]; - } -#else - vec2 sum = sDepthLine[gl_GlobalInvocationID.y + cFilterWidth]; -#endif - imageStore(image, ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y), vec4(sum, 0.0, 0.0)); -#endif -} diff --git a/samples/rayReflections/01_CompileShaders.bat b/samples/rayReflections/01_CompileShaders.bat deleted file mode 100644 index 4bea48a..0000000 --- a/samples/rayReflections/01_CompileShaders.bat +++ /dev/null @@ -1,42 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.rgen shaders\*.rint shaders\*.rahit shaders\*.rchit shaders\*.rmiss shaders\*.rcall) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V --target-env spirv1.4 %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rayReflections/02_PrepareMedia.bat b/samples/rayReflections/02_PrepareMedia.bat deleted file mode 100644 index 2a0ffb1..0000000 --- a/samples/rayReflections/02_PrepareMedia.bat +++ /dev/null @@ -1,75 +0,0 @@ -@echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\default_ddn.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_env.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\bbb_splash.ktx Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\bbb_splash.png Media\Textures\. - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\stub_*.tga Media\Textures\. -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\flat_n.tga Media\Textures\. - -call :ConvertTextures Media\Textures 2048 - - -xcopy ..\..\vkSampleFrameworkAssets\shared\Media\Textures\simplesky_irradiance.ktx Media\Textures\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skybox_Separate.* Media\Objects\. -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\UVSphere_Separate.* Media\Objects\. -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Floor_Separate.* Media\Objects\. - -echo. -echo Cleaning up afer texture conversion -echo. -pushd Media -del /s /q /f *.png *.jpg *.tga -popd - -goto:eof - - -:ConvertTextures -rem %1 is directory, %2 is size at which we downsample (in bytes) -rem Convert png images to ktx textures. -rem Larger textures are mipped and reduced in size, (very) small textures are not -SET SIZELIMIT=1000 -pushd %1 -for /R %%i in (*.png) do ( - call :ConvertTexture %%i %2 R8G8B8A8Unorm -) - -for /R %%i in (*.jpg) do ( - call :ConvertTexture %%i %2 R8G8B8A8Unorm -) - -for /R %%i in (*.tga) do ( - call :ConvertTexture %%i %2 R8G8B8A8Unorm -) - -popd -goto :eof - -:ConvertTexture -rem %1 is file, %2 is size at which we downsample (in bytes), %3 is the output data format -rem Convert png image to ktx texture. -if %~z1 LSS %2 ( - echo "Not scaling (or mipping) %~f1 (too small)" - ..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -nomip -) else ( - rem echo %cd% => Current directory is Media/Textures! OR "call :ConvertTextures Media\Textures 2048" - rem Media/Textures needs ..\..\..\..\project\tools\simpletextureconverter.exe - rem Media/Objects/Sponza needs ..\..\..\..\..\project\tools\simpletextureconverter.exe - rem echo %~f1 - ..\..\..\..\project\tools\simpletextureconverter.exe "%~f1" "%~dpn1.ktx" -F %3 -w 25%% -h 25%% -) -goto :eof diff --git a/samples/rayReflections/04_Install_APK.bat b/samples/rayReflections/04_Install_APK.bat deleted file mode 100644 index 378ddf0..0000000 --- a/samples/rayReflections/04_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\rayReflections\outputs\apk\debug\rayReflections-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\rayReflections\outputs\apk\debug\rayReflections-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rayReflections/CMakeLists.txt b/samples/rayReflections/CMakeLists.txt deleted file mode 100644 index 8122bcf..0000000 --- a/samples/rayReflections/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (rayReflections C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/rayReflections.cpp - code/main/rayReflections.hpp - code/main/SphereCamera.cpp - code/main/SphereCamera.h - code/main/materials.hpp -) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Copy required models to local folders -# -include(ModelPackager) - -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/Floor_Separate) -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/Skybox_Separate) -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/General/UVSphere_Separate) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/rayReflections/README.md b/samples/rayReflections/README.md deleted file mode 100644 index 6ee2aa8..0000000 --- a/samples/rayReflections/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Ray Reflections sample - -![Screenshot](img/screenshot.PNG) - -## Overview - -Vulkan Ray Tracing: Reflections - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - - -Note: The sample assumes there are user provided asset files at the following path: **'Media\Meshes\Museum.gltf'** and **'Media\Meshes\Museum.bin'**. -Texture dependencies from this asset should be added to **'Media\Textures\'** and are required to have the *.ktx* extension. -There are 3 extra require supporting textures that should also go to the same texture path listed above: **white_d.ktx**, **black_d.ktx** and **normal_default.ktx**. -The framework team is working to build a centralized asset repository that should minimize these requirements in the near future. - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_Install_APK.bat -``` - -If desired, you can keep track of any logging by running one of the logcat batch files (which you can find on the current directory). - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. - - -## Running - -Windows: -- From this directory (samples\rayReflections) execute `..\..\project\windows\solution\samples\rayReflections\Debug\rayReflections.exe` -- (you can also compile/run by opening `project\windows\solution\vkSampleFramework.sln` in Visual Studio and pointing the debug 'Working Directory' to samples\rayReflections) - -Android: -- This sample runs on hardware that exposes the Vulkan Acceleration Structure and Ray Query extensions - -## Configuration - -The sample will run with reasonable defaults but the user can override settings by placing a `app_config.txt` text file in the sample root folder (eg samples\rayReflections\app_config.txt) diff --git a/samples/rayReflections/build.gradle b/samples/rayReflections/build.gradle deleted file mode 100644 index 0176c35..0000000 --- a/samples/rayReflections/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.rayreflections" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/rayReflections/code/main/SphereCamera.cpp b/samples/rayReflections/code/main/SphereCamera.cpp deleted file mode 100644 index 6e8e4b3..0000000 --- a/samples/rayReflections/code/main/SphereCamera.cpp +++ /dev/null @@ -1,325 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#include "SphereCamera.h" - -//============================================================================= -// CONSTANTS -//============================================================================= -const float kCameraMomentum = 2000.0f; // Time (ms) it takes the camera to stop moving after touches stop -const float kCameraMomentumScale = 0.0625f; // Multiplication factor to get from screen movement to world movement - -//============================================================================= -// GLOBALS -//============================================================================= - -//----------------------------------------------------------------------------- -float L_SphereCamClamp(float x, float a, float b) -//----------------------------------------------------------------------------- -{ - return x < a ? a : (x > b ? b : x); -} - - - -//----------------------------------------------------------------------------- -CSphereCamera::CSphereCamera() -//----------------------------------------------------------------------------- -{ - m_uiLastTouchTime = 0; - m_LastTouchPos = glm::vec3(0.0f, 0.0f, 0.0f); - m_CurrentTouchDelta = glm::vec3(0.0f, 0.0f, 0.0f); - - m_iScreenWidth = 1280; - m_iScreenHeight = 720; - - // Sphere Camera - m_Position = glm::vec3(0.0f, 0.0f, 6.0f); - m_Distance = 6.0f; - m_MinDistance = 1.0f; - m_MaxDistance = 200.0f; - - m_LookAt = glm::vec3(0.0f, 0.0f, 0.0f);; - m_Pitch = 0.0f; - m_MinPitch = MIN_CAMERA_PITCH; - m_MaxPitch = MAX_CAMERA_PITCH; - - m_Yaw = 0.0f; - m_TouchID = -1; - - m_MoveVelocity = 0.25f; - m_TouchDisabled = false; - - // Time Variables - m_uiElapsedTime = 0; - m_uiLastTime = OS_GetTimeMS(); - - m_Aspect = 1.0f; - m_FOV = PI * 0.25f; - m_NearClip = 1.0f; - m_FarClip = 100.0f; -} - -//----------------------------------------------------------------------------- -CSphereCamera::~CSphereCamera() -//----------------------------------------------------------------------------- -{ -} - -//----------------------------------------------------------------------------- -void CSphereCamera::TouchDownEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - if (m_TouchDisabled) - return; - - if(m_TouchID >= 0) - { - // Ignore this second touch down event - return; - } - - m_uiLastTouchTime = OS_GetTimeMS(); - - float fltPosX = +2.0f * (float)xPos/(float)(m_iScreenWidth - 1) - 1.0f; - float fltPosY = -2.0f * (float)yPos/(float)(m_iScreenHeight - 1) + 1.0f; - - // LogInfo("(Spam) TouchDownEvent(%d): (%0.2f, %0.2f) => (%0.2f, %0.2f)", iPointerID, xPos, yPos, fltPosX, fltPosY); - - m_TouchID = iPointerID; - - m_LastTouchPos = glm::vec3(fltPosX, fltPosY, 0.0f); - m_CurrentTouchDelta = glm::vec3(0.0f, 0.0f, 0.0f); - - //LogInfo("(Spam) *******************************************"); - //LogInfo("(Spam) Current Touch Delta: (%0.3f, %0.3f, %0.3f)", m_CurrentTouchDelta[0], m_CurrentTouchDelta[1], m_CurrentTouchDelta[2]); - //LogInfo("(Spam) *******************************************"); - -} - -//----------------------------------------------------------------------------- -void CSphereCamera::TouchMoveEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - if (m_TouchDisabled) - return; - - if(m_TouchID != iPointerID) - { - // Ignore this second touch move event - return; - } - - float fltPosX = +2.0f * (float)xPos/(float)(m_iScreenWidth - 1) - 1.0f; - float fltPosY = -2.0f * (float)yPos/(float)(m_iScreenHeight - 1) + 1.0f; - - // LogInfo("(Spam) TouchMoveEvent(%d): (%0.2f, %0.2f) => (%0.2f, %0.2f)", iPointerID, xPos, yPos, fltPosX, fltPosY); - - // Momentum camera - uint32_t TimeNow = OS_GetTimeMS(); - uint32_t ElapsedTime = TimeNow - m_uiLastTouchTime; - - // The base problem is that on PC we get multiple move messages for every draw message. - // On Android it is one to one. - if(ElapsedTime != 0) - { - // This puts the current delta as a speed - m_CurrentTouchDelta = glm::vec3(fltPosX, fltPosY, 0.0f); - m_CurrentTouchDelta -= m_LastTouchPos; - m_CurrentTouchDelta *= 1000.0f; - m_CurrentTouchDelta /= (float)ElapsedTime; - } - - // Can update this AFTER updating the current move delta - m_uiLastTouchTime = OS_GetTimeMS(); - m_LastTouchPos = glm::vec3(fltPosX, fltPosY, 0.0f); -} - -//----------------------------------------------------------------------------- -void CSphereCamera::TouchUpEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - if (m_TouchDisabled) - return; - - if(m_TouchID != iPointerID) - { - // Ignore this second touch up event - return; - } - - m_uiLastTouchTime = OS_GetTimeMS(); - - float fltPosX = +2.0f * (float)xPos/(float)(m_iScreenWidth - 1) - 1.0f; - float fltPosY = -2.0f * (float)yPos/(float)(m_iScreenHeight - 1) + 1.0f; - m_LastTouchPos = glm::vec3(fltPosX, fltPosY, 0.0f); - - //LogInfo("TouchUpEvent(%d): (%0.2f, %0.2f) => (%0.2f, %0.2f)", iPointerID, xPos, yPos, fltPosX, fltPosY); - - m_TouchID = -1; -} - -//----------------------------------------------------------------------------- -void CSphereCamera::Tick(float ElapsedTime) -//----------------------------------------------------------------------------- -{ - if (m_TouchDisabled) - return; - - float CameraVelocity = 0.0f; - - // Handle the Momentum - uint32_t TimeNow = OS_GetTimeMS(); - - if (m_uiLastTouchTime == 0) - m_uiLastTouchTime = TimeNow; - - uint32_t TouchElapsedTime = TimeNow - m_uiLastTouchTime; - - // Handle Movement - if(m_TouchID >= 0) - { - CameraVelocity = m_MoveVelocity * ElapsedTime; - - // LogInfo("(Spam) Camera Movement Velocity: %0.2f", CameraVelocity); - - float Inverse = -1.0f; - m_Pitch += Inverse * CameraVelocity * m_CurrentTouchDelta[1]; - m_Yaw += Inverse * CameraVelocity * m_CurrentTouchDelta[0]; - } - - - // Don't add momentum if still touching - if(m_TouchID < 0 && TouchElapsedTime != 0) - { - float SwipeLerp = 1.0f - (float)TouchElapsedTime / kCameraMomentum; - SwipeLerp = L_SphereCamClamp(SwipeLerp, 0.0f, 1.0f); - - if(SwipeLerp > 0.0) - { - glm::vec3 CurrentDelta = SwipeLerp * kCameraMomentumScale * m_CurrentTouchDelta; - // LogInfo("(Spam) Current Delta (Swipe Lerp = %0.3f): (%0.3f, %0.3f, %0.3f)", SwipeLerp, m_CurrentTouchDelta[0], m_CurrentTouchDelta[1], m_CurrentTouchDelta[2]); - - // This control method only works on Android due to the fact that on PC - // there are many move updates per frame and some get lost. - float Inverse = -1.0f; - float YawAdjust = Inverse * m_MoveVelocity * CurrentDelta[0]; - m_Yaw += YawAdjust; - - // LogInfo("(Spam) YawAdjust = %0.2f", YawAdjust); - - // Don't want momentum on vertical part - // m_Pitch += Inverse * CameraVelocity * CurrentDelta[1]; - } - } - - - // Want to limit the pitch - m_Pitch = L_SphereCamClamp(m_Pitch, m_MinPitch, m_MaxPitch); - - // Want to limit the distance - m_Distance = L_SphereCamClamp(m_Distance, m_MinDistance, m_MaxDistance); - - // First rotate around X-Axis to by Pitch amount - m_Position = glm::vec3(0.0f, 0.0f, m_Distance); - - glm::mat4 PitchTrans = glm::rotate(glm::mat4(1.0f), m_Pitch, glm::vec3(1.0f, 0.0f, 0.0f)); - m_Position = PitchTrans * glm::vec4(m_Position, 0.0); - - // Now rotate around the Y-Axis by Yaw amount - glm::mat4 YawTrans = glm::rotate(glm::mat4(1.0f), -m_Yaw, glm::vec3(0.0f, 1.0f, 0.0f)); - m_Position = YawTrans * glm::vec4(m_Position, 0.0); - - // Adjust by LookAt - m_Position += m_LookAt; - - // Rebuild the view matrix based on changes to the camera - glm::vec3 LocalUp = glm::vec3(0.0f, 1.0f, 0.0f); - m_ViewMatrix = glm::lookAtRH(m_Position, m_LookAt, LocalUp); - - - m_ProjectionMatrix = glm::perspectiveRH(m_FOV, m_Aspect, m_NearClip, m_FarClip); - - glm::mat4 viewProj = m_ProjectionMatrix * m_ViewMatrix; - m_InverseViewProjection = glm::inverse(viewProj); -} - -//----------------------------------------------------------------------------- -void CSphereCamera::SetPosition(glm::vec3&Position) -//----------------------------------------------------------------------------- -{ - m_Position = Position; - - // X-Value is getting inverted from FPS camera and I don't want - // to debug it now :) - // m_Position[0] *= -1.0f; - - // Update Pitch, Yaw, Distance, etc. - UpdateValues(); -} - -//----------------------------------------------------------------------------- -void CSphereCamera::SetLookAt(glm::vec3&LookAt) -//----------------------------------------------------------------------------- -{ - m_LookAt = LookAt; - - // Update Pitch, Yaw, Distance, etc. - UpdateValues(); -} - -//----------------------------------------------------------------------------- -void CSphereCamera::UpdateValues() -//----------------------------------------------------------------------------- -{ - // Set Pitch and Yaw so we start looking where we want - // Note +Z in GL is -Y in math - m_Yaw = atan2(-m_Position[2] - m_LookAt[2], m_Position[0] - m_LookAt[0]); - m_Yaw += PI; // Since really looking other way from point - m_Yaw -= PI_DIV_2; // Since camera wants to start looking down -Z or 90 degrees - if(m_Yaw < 0.0f) - m_Yaw += PI_MUL_2; - - float YawDegrees = m_Yaw * TO_DEGREES; - - // For Yaw, we need to start with the hypotenuse in XZ plane - float TempX = m_Position[0] - m_LookAt[0]; - float TempZ = m_Position[2] - m_LookAt[2]; - - float BaseLength = sqrt(TempX * TempX + TempZ * TempZ); - m_Pitch = atan2(m_LookAt[1] - m_Position[1], BaseLength); - - float PitchDegrees = m_Pitch * TO_DEGREES; - - // Distance - m_Distance = length(m_Position - m_LookAt); -} - - -//----------------------------------------------------------------------------- -void CSphereCamera::GetViewMatrix(glm::mat4 &RetMatrix) -//----------------------------------------------------------------------------- -{ -#ifdef KILL_THIS - glm::vec3 LocalRight = glm::vec3( cosf(m_Yaw), 0.0f, sinf(m_Yaw) ); - glm::vec3 LocalForward = glm::vec3( cosf(m_Yaw + PI_DIV_2), sinf(m_Pitch), -sinf(m_Yaw + PI_DIV_2) ); - LocalForward = normalize(LocalForward); - glm::vec3 LocalUp = cross(LocalRight, LocalForward); -#endif // KILL_THIS - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - glm::vec3 LocalUp = glm::vec3(0.0f, 1.0f, 0.0f); - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - - // Rebuild the view matrix based on changes to the camera - m_ViewMatrix = glm::lookAtRH(m_Position, m_LookAt, LocalUp); - - RetMatrix = m_ViewMatrix; -} - diff --git a/samples/rayReflections/code/main/SphereCamera.h b/samples/rayReflections/code/main/SphereCamera.h deleted file mode 100644 index 4516d03..0000000 --- a/samples/rayReflections/code/main/SphereCamera.h +++ /dev/null @@ -1,131 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include "system/glm_common.hpp" -#include "system/math_common.hpp" -#include "system/os_common.h" - - -//============================================================================= -// CONSTANTS -//============================================================================= -#define MIN_CAMERA_PITCH (-7.0f * PI / 16.0f) -#define MAX_CAMERA_PITCH ( 7.0f * PI / 16.0f) - -#define CAMERA_ROTATE_SPEED PI_DIV_2 // PI -#define CAMERA_ZOOM_SPEED 5.0f - -//============================================================================= -// TYPEDEFS -//============================================================================= - -//============================================================================= -// CSphereCamera -//============================================================================= -class CSphereCamera -{ - // ----------------------- - // Constructors - // ----------------------- -public: - CSphereCamera(); - ~CSphereCamera(); - - // ----------------------- - // Implementation - // ----------------------- -public: - void SetScreenSize(int iWidth, int iHeight) { m_iScreenWidth = iWidth; m_iScreenHeight = iHeight; } - - void SetPosition(glm::vec3 &Position); - void SetLookAt(glm::vec3&LookAt); - - void UpdateValues(); - - // void SetPosition(glm::vec3 &Position) { m_Position = Position; } - void SetDistance(float NewDist) { m_Distance = NewDist; } - void SetMinDistance(float NewDist) { m_MinDistance = NewDist; } - void SetMaxDistance(float NewDist) { m_MaxDistance = NewDist; } - - float GetMoveVelocity() { return m_MoveVelocity; } - void SetMoveVelocity(float NewVelocity) { m_MoveVelocity = NewVelocity; } - - void GetPosition(glm::vec3&RetPosition) { RetPosition = m_Position; } - - float GetPitch() { return m_Pitch; } - void SetPitch(float NewPitch) { m_Pitch = NewPitch; } - void SetMinPitch(float NewPitch) { m_MinPitch = NewPitch; } - void SetMaxPitch(float NewPitch) { m_MaxPitch = NewPitch; } - - float GetYaw() { return m_Yaw; } - void SetYaw(float NewYaw) { m_Yaw = NewYaw; } - - void TouchDownEvent(int iPointerID, float xPos, float yPos); - void TouchMoveEvent(int iPointerID, float xPos, float yPos); - void TouchUpEvent(int iPointerID, float xPos, float yPos); - void Tick(float ElapsedTime); - - void SetAspect(float aspect) { m_Aspect = aspect; } - void SetFov(float fov) { m_FOV = fov; } - void SetClipPlanes(float NewNear, float NewFar) { m_NearClip = NewNear; m_FarClip = NewFar; } - - void GetViewMatrix(glm::mat4 &RetMatrix); - glm::mat4 ViewMatrix() const { return m_ViewMatrix; } - glm::vec3 Position() const { return m_Position; } - glm::mat4 ProjectionMatrix() const { return m_ProjectionMatrix; } - glm::mat4 InverseViewProjection() const { return m_InverseViewProjection; }///<@returns the inverse of the view projection matrix - - -private: - - // ----------------------- - // Attributes - // ----------------------- - -public: - - // Screen Size - int m_iScreenWidth; - int m_iScreenHeight; - - // Sphere Camera - glm::vec3 m_Position; - float m_Distance; - float m_MinDistance; - float m_MaxDistance; - glm::vec3 m_LookAt; - float m_Pitch; - float m_MinPitch; - float m_MaxPitch; - float m_Yaw; - int m_TouchID; - - float m_MoveVelocity; - bool m_TouchDisabled; - - // Time Variables - uint32_t m_uiElapsedTime; - uint32_t m_uiLastTime; - - // Momentum camera - uint32_t m_uiLastTouchTime; - glm::vec3 m_LastTouchPos; - glm::vec3 m_CurrentTouchDelta; - - // Camera Frustum Values - float m_Aspect; - float m_FOV; - float m_NearClip; - float m_FarClip; - - // The Projection and View matrix updated in Tick() - glm::mat4 m_ProjectionMatrix; - glm::mat4 m_ViewMatrix; - glm::mat4 m_InverseViewProjection; -}; diff --git a/samples/rayReflections/code/main/materials.hpp b/samples/rayReflections/code/main/materials.hpp deleted file mode 100644 index c1d986c..0000000 --- a/samples/rayReflections/code/main/materials.hpp +++ /dev/null @@ -1,211 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include "vulkan/vulkan.hpp" - -#include "system/os_common.h" -#include "system/glm_common.hpp" - -//============================================================================= -// Uniform Buffers -//============================================================================= - - -// ********************** -// The Objects -// ********************** -struct ObjectVertUB -{ - glm::mat4 VPMatrix; -}; - -struct ObjectFragUB -{ - glm::vec4 Color; - - // X: Max Ray Bounces? - // Y: Not Used - // Z: Not Used - // W: Not Used - glm::vec4 MaterialProps; -}; - - -// ********************** -// The Skybox -// ********************** -struct SkyboxVertUB -{ - glm::mat4 MVPMatrix; - glm::mat4 ModelMatrix; - glm::vec4 Color; -}; - -// ********************** -// Texture Display -// ********************** -struct TexDispVertUB -{ - glm::mat4 VPMatrix; - glm::mat4 ModelMatrix; - glm::vec4 Color; -}; - -// ********************** -// Deferred Lighting Fullscreen pass -// ********************** -struct LightFragCtrl -{ - // Needed to convert from Depth to World Position - glm::mat4 ProjectionInv; - glm::mat4 ViewInv; - - // To apply a tint to the final results - glm::vec4 ShaderTint; - - // Standard lighting - glm::vec4 EyePos; - glm::vec4 LightDir; - glm::vec4 LightColor; - - // X: Mip Level - // Y: Reflect Amount - // Z: Reflect Fresnel Min - // W: Reflect Fresnel Max - glm::vec4 ReflectParams; - - // X: Mip Level - // Y: IBL Amount - // Z: Not Used - // W: Not Used - glm::vec4 IrradianceParams; - - // X: Ray Min Distance - // Y: Ray Max Distance - // Z: Max Ray Bounces - // W: Ray Blend Amount (Mirror Amount) - glm::vec4 RayParam01; - - // X: Shadow Amount - // Y: Minumum Normal Dot Light - // Z: Not Used - // W: Not Used - glm::vec4 LightParams; -}; - - -// ********************** -// Post/Blit -// ********************** -struct BlitFragCtrl { - float Bloom = 0.0f; - float Diffuse = 1.0f; // 0 to 2 range (dark to white) - int sRGB = 0; // 1 - apply srgb conversion in output blit shader, 0 passthrough color -}; - - -// ********************** -// The Probe Ray Output -// ********************** -struct ProbeRayOutUB -{ - // Rays are deterministic based on number of rays. - // Each frame they are rotated by a random transform. - glm::mat4 RayTransform; - - // Spacing of the probes in world coordinates - glm::vec4 ProbeOrigin; - glm::vec4 ProbeCounts; - glm::vec4 ProbeSpacing; - - // X: Rays Per Probe - // Y: Total Number of Probes - // Z: Ray Min Distance - // W: Ray Max Distance - glm::vec4 RayParam01; - - // X: Irradiance Texels (Not Counting Border) - // Y: Distance Texels (Not Counting Border) - // Z: Not Used - // W: Not Used - glm::vec4 RayParam02; - - // X: Gamma Encode Power - // Y: Gamma Inverse Power - // Z: Shadow Amount - // W: Not Used - glm::vec4 RayParam03; - - // X: Blend Lerp - // Y: Max Probe Change - // Z: Max Brightness - // W: Distance Exponent - glm::vec4 RayParam04; - - // Environment Settings - glm::vec4 LightDir; - glm::vec4 LightColor; -}; - -// ********************** -// The Scene Data -// ********************** -struct SceneData -{ - // WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! - // mat4 MUST be on 16 byte boundary or the validation layer complains - // Therefore, can't start with an "int" value in this structure without padding - - // The transform for this instance - glm::mat4 Transform; - - // The transform used for normals (Inverse Transpose) - // See http://www.pbr-book.org/3ed-2018/Geometry_and_Transformations/Applying_Transformations.html#Normals - glm::mat4 TransformIT; - - // The index into the list of meshes. - // This could really be put in VkAccelerationStructureInstanceKHR.instanceCustomIndex. - // For now, the custom index will point to the array of ScenData structures and - // then this will point to meshes. - // Rays are deterministic based on number of rays. - // Each frame they are rotated by a random transform. - uint32_t MeshIndex; - - - // Material information - int32_t DiffuseTex; // -1 means no entry - int32_t NormalTex; // -1 means no entry - - int32_t Padding; // So lines up on 16 - -}; - -struct SceneDataInternal -{ - // The index into the list of meshes. - // This could really be put in VkAccelerationStructureInstanceKHR.instanceCustomIndex. - // For now, the custom index will point to the array of ScenData structures and - // then this will point to meshes. - // Rays are deterministic based on number of rays. - // Each frame they are rotated by a random transform. - uint32_t MeshIndex; - - // The transform for this instance - glm::mat4 Transform; - - // The transform used for normals (Inverse Transpose) - // See http://www.pbr-book.org/3ed-2018/Geometry_and_Transformations/Applying_Transformations.html#Normals - glm::mat4 TransformIT; - - // Material information - std::string DiffuseTex; - std::string NormalTex; -}; - - diff --git a/samples/rayReflections/code/main/rayReflections.cpp b/samples/rayReflections/code/main/rayReflections.cpp deleted file mode 100644 index 09f344d..0000000 --- a/samples/rayReflections/code/main/rayReflections.cpp +++ /dev/null @@ -1,2564 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#include "rayReflections.hpp" -#include "camera/cameraController.hpp" -#include "camera/cameraControllerTouch.hpp" -#include "gui/imguiVulkan.hpp" -#include "main/applicationEntrypoint.hpp" -#include "material/computable.hpp" -#include "material/drawable.hpp" -#include "material/material.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" -#include "memory/memoryManager.hpp" -#include "memory/vulkan/bufferObject.hpp" -#include "memory/vulkan/indexBufferObject.hpp" -#include "memory/vulkan/vertexBufferObject.hpp" -#include "mesh/instanceGenerator.hpp" -#include "mesh/meshHelper.hpp" -#include "system/math_common.hpp" -#include "texture/vulkan/textureManager.hpp" -#include "vulkan/vulkan.hpp" -#include "vulkan/TextureFuncts.h" -#include "vulkanRT/vulkanRT.hpp" -#include "vulkanRT/sceneRT.hpp" -#include "vulkanRT/traceable.hpp" -#include "imgui.h" - -#include -#include - -#include "vulkanRT/meshObjectRT.hpp" - -VAR(bool, gRenderHud, true, kVariableNonpersistent); - -// General Settings -VAR(glm::vec3, gCameraStartPos, glm::vec3(0.0f, 2.0f, 5.0f), kVariableNonpersistent); -VAR(glm::vec3, gStartCameraLook, glm::vec3(0.0f, 2.0f, 0.0f), kVariableNonpersistent); - -VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); -VAR(float, gNearPlane, 0.01f, kVariableNonpersistent); -VAR(float, gFarPlane, 50.0f, kVariableNonpersistent); - -// Objects in the scene -VAR(uint32_t, gNumObjects, 2, kVariableNonpersistent); -VAR(char*, gObjectName, "Media/Meshes/UVSphere_Separate.gltf", kVariableNonpersistent); -VAR(char*, gObjectDiffuse, "stub_green", kVariableNonpersistent); -VAR(glm::vec3, gObjectScale, glm::vec3(1.0f, 1.0f, 1.0f), kVariableNonpersistent); -VAR(float, gObjectHeight, 1.2f, kVariableNonpersistent); -VAR(float, gSpacingRadius, 2.0f, kVariableNonpersistent); - -VAR(char*, gFloorName, "Media/Meshes/Floor_Separate.gltf", kVariableNonpersistent); -VAR(glm::vec3, gFloorPos, glm::vec3(0.0f, 0.0f, 0.0f), kVariableNonpersistent); -VAR(glm::vec3, gFloorScale, glm::vec3(8.0f, 8.0f, 8.0f), kVariableNonpersistent); -VAR(char*, gFloorDiffuse, "bbb_splash", kVariableNonpersistent); - -// Environment Settings -VAR(glm::vec3, gLightDirA, glm::vec3(-0.5f, -1.0f, -0.7f), kVariableNonpersistent); -VAR(glm::vec3, gLightDirB, glm::vec3(-0.5f, -1.0f, -0.7f), kVariableNonpersistent); -VAR(float, gLightDirChangeRate, 0.0f, kVariableNonpersistent); - -VAR(glm::vec4, gLightColorA, glm::vec4(1.0f, 1.0f, 1.0f, 128.0f), kVariableNonpersistent); -VAR(glm::vec4, gLightColorB, glm::vec4(1.0f, 1.0f, 1.0f, 128.0f), kVariableNonpersistent); -VAR(float, gLightColorChangeRate, 0.0f, kVariableNonpersistent); - -// Log Settings - -// Misc. Settings -VAR(float, gRayMinDistance, 0.05f, kVariableNonpersistent); -VAR(float, gRayMaxDistance, 10000.0f, kVariableNonpersistent); -VAR(uint32_t, gSphereRayBounces, 2, kVariableNonpersistent); -VAR(uint32_t, gFloorRayBounces, 2, kVariableNonpersistent); -VAR(float, gRayBlendAmount, 0.5f, kVariableNonpersistent); - -VAR(float, gShadowAmount, 0.25f, kVariableNonpersistent); -VAR(float, gMinNormDotLight, 0.0f, kVariableNonpersistent); - -VAR(glm::vec4, gClearColor, glm::vec4(0.3f, 0.3f, 0.3f, 1.0f), kVariableNonpersistent); - -static const char* const sRenderPassNames[NUM_RENDER_PASSES] = { "RP_GBUFFER", "RP_RAYTRACE", "RP_LIGHT", "RP_HUD", "RP_BLIT" }; -static_assert(RP_GBUFFER == 0, "Check order of sRenderPassNames"); -static_assert(RP_BLIT == 4, "Check order of sRenderPassNames"); -static_assert(NUM_RENDER_PASSES == sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0]), "mismatched sRenderPassNames"); - -uint32_t guiShiftPressed = 0; -uint32_t guiUpPressed = 0; -uint32_t guiDownPressed = 0; -uint32_t guiLeftPressed = 0; -uint32_t guiRightPressed = 0; - -uint32_t guiAPressed = 0; -uint32_t guiSPressed = 0; -uint32_t guiWPressed = 0; -uint32_t guiDPressed = 0; -uint32_t guiQPressed = 0; -uint32_t guiEPressed = 0; - -uint32_t guiSpacePressedTime = 0; -uint32_t guiSpacePressedThisFrame = 0; - -#define KEY_THIS_FRAME_TIME 250 - - -//----------------------------------------------------------------------------- -float L_Lerp(float t, float a, float b) -//----------------------------------------------------------------------------- -{ - return a + t * (b - a); -} - -//----------------------------------------------------------------------------- -glm::vec3 L_Lerp(float t, glm::vec3 a, glm::vec3 b) -//----------------------------------------------------------------------------- -{ - return a + t * (b - a); -} - -//----------------------------------------------------------------------------- -glm::vec4 L_Lerp(float t, glm::vec4 a, glm::vec4 b) -//----------------------------------------------------------------------------- -{ - return a + t * (b - a); -} - - -// -// Implementation of the Application entrypoint (called by the framework) -// Construct the Application class -// -//----------------------------------------------------------------------------- -FrameworkApplicationBase* Application_ConstructApplication() -//----------------------------------------------------------------------------- -{ - return new Application(); -} - -//----------------------------------------------------------------------------- -Application::Application() -//----------------------------------------------------------------------------- - : ApplicationHelperBase() - , m_bEncodeSRGB(false) - , m_TotalDrawCalls(0) - , m_TotalTriangles(0) - , m_vulkanRT(*GetVulkan()) -{ - // Pass Semaphores - m_PassCompleteSemaphore.fill(VK_NULL_HANDLE); - - // Render passes - m_RenderPass.fill(VK_NULL_HANDLE); - - // Want to track if the scene data size changes - m_SceneDataArray.clear(); - m_SceneDataInternalArray.clear(); - - m_SceneTextures.clear(); - - m_RayMeshArray.clear(); - - m_RayIndicesArray.clear(); - - m_LastUpdateTime = 0; - m_LightChangePaused = false; - m_LightDirUpdateTime = 0; - m_LightDirReverse = false; - m_LightColorUpdateTime = 0; - m_LightColorReverse = false; -} - - -//----------------------------------------------------------------------------- -Application::~Application() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // Pass Semaphores - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - if (m_PassCompleteSemaphore[WhichPass] != VK_NULL_HANDLE) - { - vkDestroySemaphore(pVulkan->m_VulkanDevice, m_PassCompleteSemaphore[WhichPass], NULL); - } - m_PassCompleteSemaphore[WhichPass] = VK_NULL_HANDLE; - } - - // Passes - for (auto& pass : m_RenderPass) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, pass, nullptr); - pass = VK_NULL_HANDLE; - } - - // Compute - - // Textures - ReleaseTexture(*pVulkan, &m_TexWhite); - ReleaseTexture(*pVulkan, &m_DefaultNormal); - - // ReleaseTexture(*pVulkan, &m_TexBunnySplash); - - for (auto& OneMeshBuffer : m_RayMeshBuffers) - { - OneMeshBuffer->Destroy(); - } - m_RayMeshBuffers.clear(); - m_RayMeshVkBuffers.clear(); - - for (auto& OneIndiceBuffer : m_RayIndiceBuffers) - { - OneIndiceBuffer->Destroy(); - } - m_RayIndiceBuffers.clear(); - m_RayIndiceVkBuffers.clear(); - -} - -//----------------------------------------------------------------------------- -int Application::PreInitializeSelectSurfaceFormat(std::span formats) -//----------------------------------------------------------------------------- -{ - // On Snapdragon if the surfaceflinger has to do the rotation to the display native orientation then it will do it at 8bit colordepth. - // To avoid this we need to enable the 'pre-rotation' of the display (and the use of VK_QCOM_render_pass_transform so we dont have to rotate our buffers/passes manually). - GetVulkan()->m_UseRenderPassTransform = true; - - // We want to select a SRGB output format (if one exists) - int index = 0; - for (const auto& format : formats) - { - // HLM doesn't support SRGB format - if (gRunOnHLM) - { - if (format.format == TextureFormat::B8G8R8A8_UNORM) - return index; - } - else - { - if (format.format == TextureFormat::B8G8R8A8_SRGB) - return index; - } - ++index; - } - return -1; -} - -//----------------------------------------------------------------------------- -void Application::PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) -//----------------------------------------------------------------------------- -{ - bool rayQueryOnly = true; - ApplicationHelperBase::PreInitializeSetVulkanConfiguration(config); - m_vulkanRT.RegisterRequiredVulkanLayerExtensions(config, rayQueryOnly); -} - -//----------------------------------------------------------------------------- -bool Application::Initialize( uintptr_t windowHandle, uintptr_t hInstance ) -//----------------------------------------------------------------------------- -{ - if (!ApplicationHelperBase::Initialize( windowHandle, hInstance )) - { - return false; - } - - m_SphereCamera.SetScreenSize(gRenderWidth, gRenderHeight); - m_SphereCamera.SetPosition(gCameraStartPos); - m_SphereCamera.SetLookAt(gStartCameraLook); - - // Camera - m_SphereCamera.SetPosition(gCameraStartPos); - m_SphereCamera.SetAspect(float(gRenderWidth) / float(gRenderHeight)); - m_SphereCamera.SetFov(gFOV); - m_SphereCamera.SetClipPlanes(gNearPlane, gFarPlane); - - // The Skybox - m_SkyboxScale = gFarPlane * 0.95f; - - // Camera Controller - if (!ApplicationHelperBase::InitCamera()) - { - return false; - } - - // Set the current surface format - m_RequestedSurfaceFormat = {GetVulkan()->m_SurfaceFormat, GetVulkan()->m_SurfaceColorSpace}; - m_bEncodeSRGB = FormatIsSrgb(GetVulkan()->m_SurfaceFormat) == false; // if we have an srgb buffer then the output doesnt need to be encoded (hardware will do it for us) - - if (!LoadConstantShaders()) - return false; - - if (!LoadTextures()) - return false; - - if (!CreateRenderTargets()) - return false; - - if (!InitShadowMap()) - return false; - - if (!InitLighting()) - return false; - - if (!InitUniforms()) - return false; - - if (!InitAllRenderPasses()) - return false; - - if (!InitMaterials()) - return false; - - if (!InitGui(windowHandle)) - return false; - - if (!LoadMeshObjects()) - return false; - - if (!LoadRayTracingObjects()) - return false; - - if (!InitCommandBuffers()) - return false; - - if (!InitDrawables()) - return false; - - if (!CreateRayInputs()) - return false; - - // if (!InitSceneData()) - // return false; - - if (!LoadDynamicShaders()) - return false; - - if (!LoadDynamicRayTracingObjects()) - return false; - - if (!gRunOnHLM && !InitHdr()) - return false; - - if (!InitLocalSemaphores()) - return false; - - if (!BuildCmdBuffers()) - return false; - - LOGE("Initalize lighting!!"); - - LOGE("Initalize workers!!"); - - m_GameThreadWorker.Initialize("GameThreadWorker", 1); - - m_CompletedThreadOutput = {}; - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadMeshObjects() -//----------------------------------------------------------------------------- -{ - // - // Create the 'Blit' drawable mesh. - // Fullscreen quad that does the final composite (hud and scene) for output. - LOGI("Creating Blit mesh..."); - MeshObject blitMesh; - MeshHelper::CreateScreenSpaceMesh(GetVulkan()->GetMemoryManager(), 0, &blitMesh); - - const auto* pBlitShader = m_ShaderManager->GetShader("Blit"); - assert(pBlitShader); - auto blitShaderMaterial = m_MaterialManager->CreateMaterial(*GetVulkan(), *pBlitShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { - if (texName == "Diffuse") { - return { &m_MainRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Overlay") { - return { &m_HudRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Bloom") { - return { &m_TexWhite }; - } - assert(0); - return {}; - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - //BlitFragCB - return { m_BlitFragUniform[0].buf.GetVkBuffer() }; - } - ); - - m_BlitDrawable = std::make_unique(*GetVulkan(), std::move(blitShaderMaterial)); - if (!m_BlitDrawable->Init( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], std::move(blitMesh))) - { - LOGE("Error Creating Blit drawable..."); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadRayTracingObjects() -//----------------------------------------------------------------------------- -{ - // Load the Geometry for Ray Tracing! and populate m_sceneRayTracable - if (!m_vulkanRT.Init()) - { - LOGE("Vulkan Ray Tracing Extension was not available...exiting"); - return false; - } - - // Might be overkill, but may eventually need different scales in each axis - std::vector candidateMeshes; - - // Load the objects in scene - float StepRadians = PI_MUL_2 / (float)gNumObjects; - for (uint32_t WhichObject = 0; WhichObject < gNumObjects; WhichObject++) - { - float OneX = gSpacingRadius * cosf((float)WhichObject * StepRadians); - float OneY = gObjectHeight; - float OneZ = gSpacingRadius * sinf((float)WhichObject * StepRadians); - - // This is really the same object, just load it multiple times and will instance later - std::vector MeshObject = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gObjectName, false, gObjectScale); - for (MeshObjectIntermediate& OneMesh : MeshObject) - { - OneMesh.m_Transform[3] = glm::vec4(OneX, OneY, OneZ, 1.0f); - OneMesh.m_Materials[0].diffuseFilename = gObjectDiffuse; - OneMesh.m_Materials[0].bumpFilename = std::string("stub_normal"); - } - candidateMeshes.insert(candidateMeshes.end(), std::make_move_iterator(MeshObject.begin()), std::make_move_iterator(MeshObject.end())); - MeshObject.erase(MeshObject.begin(), MeshObject.end()); - } - - // Load the floor/base of the scene - { - // LOGI("Loading Ray Tracing mesh object: %s...", gFloorName); - std::vector MeshObject = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gFloorName, false, gFloorScale); - for (MeshObjectIntermediate& OneMesh : MeshObject) - { - if (OneMesh.m_Materials.size() < 1) - OneMesh.m_Materials.resize(1); - - OneMesh.m_Transform = glm::rotate(OneMesh.m_Transform, -PI_DIV_2, glm::vec3(1.0f, 0.0f, 0.0f)); - OneMesh.m_Transform = glm::rotate(OneMesh.m_Transform, PI, glm::vec3(0.0f, 1.0f, 0.0f)); - - OneMesh.m_Transform[3] = glm::vec4(gFloorPos, 1.0f); - OneMesh.m_Materials[0].diffuseFilename = gFloorDiffuse; - OneMesh.m_Materials[0].bumpFilename = std::string("stub_normal"); - } - candidateMeshes.insert(candidateMeshes.end(), std::make_move_iterator(MeshObject.begin()), std::make_move_iterator(MeshObject.end())); - MeshObject.erase(MeshObject.begin(), MeshObject.end()); - } - - // (optionally) go through and find meshes that match each other (automated instancing). - auto instancedMeshes = MeshInstanceGenerator::NullFindInstances( std::move(candidateMeshes) ); - candidateMeshes.clear(); - - // Need the mesh data for each object to pass into ray tracer as storage buffer -#define FILL_4_FROM_2(Dst, Src, Pad) {\ - Dst[0] = Src[0];\ - Dst[1] = Src[1];\ - Dst[2] = Pad; \ - Dst[3] = Pad;\ - } - -#define FILL_4_FROM_3(Dst, Src, Pad) {\ - Dst[0] = Src[0];\ - Dst[1] = Src[1];\ - Dst[2] = Src[2]; \ - Dst[3] = Pad;\ - } - - for (const auto& OneMesh : instancedMeshes) - { - // **************************** - // Vertices - // **************************** - { - RayObject NewRayObject; - for (const auto& OneVertex : OneMesh.mesh.m_VertexBuffer) - { - RayVertex NewRayVert; - FILL_4_FROM_3(NewRayVert.position, OneVertex.position, 1.0f); // THIS MUST HAVE 1.0 IN W OR TRANSFORM WILL NOT WORK :( - FILL_4_FROM_3(NewRayVert.normal, OneVertex.normal, 0.0f); - // FILL_4_FROM_3(NewRayVert.color, OneVertex.color, 0.0f); - FILL_4_FROM_2(NewRayVert.uv0, OneVertex.uv0, 0.0f); - // FILL_4_FROM_3(NewRayVert.tangent, OneVertex.tangent, 0.0f); - // FILL_4_FROM_3(NewRayVert.bitangent, OneVertex.bitangent, 0.0f); - - NewRayObject.push_back(NewRayVert); - } - - m_RayMeshArray.push_back(NewRayObject); - - // Create the buffer for this object - BufferVulkan* pNewBuffer = new BufferVulkan; - BufferUsageFlags Usage = BufferUsageFlags::Storage | BufferUsageFlags::ShaderDeviceAddress; - if (!pNewBuffer || !pNewBuffer->Initialize(&GetVulkan()->GetMemoryManager(), NewRayObject.size() * sizeof(RayVertex), Usage, NewRayObject.data())) - { - LOGE("Error! Unable to allocate %d * %d bytes for ray shader vertex data!", (uint32_t) NewRayObject.size(), (uint32_t) sizeof(RayVertex)); - return false; - } - - m_RayMeshBuffers.push_back(pNewBuffer); - m_RayMeshVkBuffers.push_back(pNewBuffer->GetVkBuffer()); - } // Vertices context - - // **************************** - // Indices - // **************************** - { - // std::vector < RayObjectIndices> m_RayIndicesArray; - // std::vector m_RayIndiceBuffers; - // std::vector m_RayIndiceVkBuffers; - RayObjectIndices NewObjectIndices; - if (OneMesh.mesh.m_IndexBuffer.index() == 1) - { - // 32-Bit Values - const auto& DataAsVec32 = std::get>(OneMesh.mesh.m_IndexBuffer); - for (const auto& OneEntry : DataAsVec32) - { - NewObjectIndices.push_back(OneEntry); - } - } - else if (OneMesh.mesh.m_IndexBuffer.index() == 2) - { - // 16-Bit Values - const auto& DataAsVec16 = std::get>(OneMesh.mesh.m_IndexBuffer); - for (const auto& OneEntry : DataAsVec16) - { - NewObjectIndices.push_back(OneEntry); - } - } - else - { - LOGE("Error! Mesh index buffer has unknown type!"); - return false; - } - - m_RayIndicesArray.push_back(NewObjectIndices); - - // Create the buffer for this object - BufferVulkan* pNewBuffer = new BufferVulkan; - BufferUsageFlags Usage = BufferUsageFlags::Storage | BufferUsageFlags::ShaderDeviceAddress; - if (!pNewBuffer || !pNewBuffer->Initialize(&GetVulkan()->GetMemoryManager(), NewObjectIndices.size() * sizeof(uint32_t), Usage, NewObjectIndices.data())) - { - LOGE("Error! Unable to allocate %d * %d bytes for ray shader vertex data!", (uint32_t) NewObjectIndices.size(), (uint32_t) sizeof(uint32_t)); - return false; - } - - m_RayIndiceBuffers.push_back(pNewBuffer); - m_RayIndiceVkBuffers.push_back(pNewBuffer->GetVkBuffer()); - } // Indices context - } - - auto sceneRayTracable = std::make_unique(*GetVulkan(), m_vulkanRT); - auto sceneRayTracableCulled = std::make_unique(*GetVulkan(), m_vulkanRT); - - uint32_t WhichMesh = 0; - uint32_t WhichInstance = 0; - for (const auto& instancedMesh : instancedMeshes) - { - if( !instancedMesh.mesh.m_Materials.empty() && - (instancedMesh.mesh.m_Materials[0].transparent || !instancedMesh.mesh.m_Materials[0].emissiveFilename.empty()) ) - continue; - - MeshObjectRT meshRayTracable; - if (!meshRayTracable.Create(*GetVulkan(), m_vulkanRT, instancedMesh.mesh)) - return false; - const auto& id = sceneRayTracable->AddObject(std::move(meshRayTracable)); - if (!id) - return false; - - if (instancedMesh.mesh.m_Materials.size() != 1) - { - LOGE("Error! Instanced mesh does NOT have a single material!"); - return false; - } - - WhichInstance = 0; - for (const auto& instance : instancedMesh.instances) - { - sceneRayTracable->AddInstance(id, instance.transform); - - SceneDataInternal OneEntry; - OneEntry.MeshIndex = WhichMesh; - - // The instance transform is a mat3x4, row major. - // In other words, 3 rows of 4 entrys that look like the first - // 3 rows of a normal transformation matrix, with is column major. - // Therefore, we need to create a column major mat4 from row major mat3x4 - OneEntry.Transform = glm::mat4(1.0f); - for (uint32_t WhichCol = 0; WhichCol < 4; WhichCol++) - { - OneEntry.Transform[WhichCol][0] = instance.transform[0][WhichCol]; - OneEntry.Transform[WhichCol][1] = instance.transform[1][WhichCol]; - OneEntry.Transform[WhichCol][2] = instance.transform[2][WhichCol]; - } - - // The transform used for normals (Inverse Transpose) - // See http://www.pbr-book.org/3ed-2018/Geometry_and_Transformations/Applying_Transformations.html#Normals - // glm inverseTranspose() doesn't exist, even though in documentation :( - OneEntry.TransformIT = glm::transpose(glm::inverse(OneEntry.Transform)); - - // Set up the material for this entry (Textures are not loaded yet. Save texture name in internal structure) - OneEntry.DiffuseTex = instancedMesh.mesh.m_Materials[0].diffuseFilename; - OneEntry.NormalTex = instancedMesh.mesh.m_Materials[0].bumpFilename; - - m_SceneDataInternalArray.push_back(OneEntry); - - WhichInstance++; - - } // WhichInstance - - WhichMesh++; - } // WhichMesh - - - // Create the acceleration structure for Ray Tracing. - if( !sceneRayTracableCulled->CreateAccelerationStructure( MeshObjectRT::UpdateMode::InPlace, sceneRayTracable->GetNumKnownInstances() ) ) - { - LOGE( "Error Creating Scene AccelerationStructure..." ); - return false; - } - - m_sceneRayTracable = std::move(sceneRayTracable); - m_sceneRayTracableCulled = std::move(sceneRayTracableCulled); - - return true; -} - -//----------------------------------------------------------------------------- -void Application::Destroy() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - pVulkan->QueueWaitIdle(); - - // Threads - m_GameThreadWorker.FinishAllWork(); - m_GameThreadWorker.Terminate(); - - // Command buffers - for (auto& bufferArray : m_PassCmdBuffer) - for (auto& buffer : bufferArray) - buffer.Release(); - - // Meshes - m_SkyboxDrawable.reset(); - m_LightDrawable.reset(); - m_BlitDrawable.reset(); - m_SceneDrawables.clear(); - m_sceneRayTracableCulled.reset(); - m_sceneRayTracable.reset(); - - // Shaders - m_ShaderManager.reset(); - - // Scene - - // Uniform Buffers - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - ReleaseUniformBuffer(pVulkan, &m_ObjectVertUniform[WhichPass][WhichBuffer]); - ReleaseUniformBuffer(pVulkan, &m_SphereFragUniform[WhichPass][WhichBuffer]); - ReleaseUniformBuffer(pVulkan, &m_FloorFragUniform[WhichPass][WhichBuffer]); - - ReleaseUniformBuffer(pVulkan, &m_SkyboxVertUniform[WhichPass][WhichBuffer]); - } // WhichBuffer - } // WhichPass - - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - ReleaseUniformBuffer(pVulkan, &m_BlitFragUniform[WhichBuffer]); - ReleaseUniformBuffer(pVulkan, &m_LightFragUniform[WhichBuffer]); - } // WhichBuffer - - ReleaseUniformBuffer(pVulkan, &m_SceneDataUniform); - - // Finally call into base class destroy - FrameworkApplicationBase::Destroy(); -} - -//----------------------------------------------------------------------------- -void Application::RunGameThread(const GameThreadInputParam& rThreadParam, GameThreadOutputParam& rThreadOutput) -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - auto* const pVulkan = GetVulkan(); - uint32_t WhichFrame = rThreadParam.CurrentBuffer.idx; - - // Clean up command buffers from last run - m_RenderPassSubmitData[WhichFrame].clear(); - - // If anything changes with the lights (Position, orientation, color, brightness, etc.) - UpdateLighting(rThreadParam.ElapsedTime); - - // Update uniform buffers with latest data - UpdateUniforms(WhichFrame); - - // First pass, waits for the back buffer to be ready. - std::span pWaitSemaphores = { &rThreadParam.CurrentBuffer.semaphore, 1 }; - - static const VkPipelineStageFlags DefaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - - // ********************** - // RP_GBUFFER - // ********************** - { - // GBuffer render pass (gbuffer 'laydown' and shadow generation). - auto& renderPassLocal = BeginRenderPass( RP_GBUFFER, WhichFrame, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_PassCompleteSemaphore[RP_GBUFFER],1 } ); - AddPassCommandBuffers( renderPassLocal ); - EndRenderPass( renderPassLocal ); - - pWaitSemaphores = { &m_PassCompleteSemaphore[RP_GBUFFER],1 }; - } - - // ********************** - // RP_RAYTRACE - // ********************** - if (true) - { - auto& renderPassLocal = BeginRenderPass(RP_RAYTRACE, WhichFrame, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_PassCompleteSemaphore[RP_RAYTRACE],1 }); - - // Build the shadow acceleration structures (if requested to). - m_sceneRayTracableCulled->UpdateAS( renderPassLocal.CmdBuff.m_VkCommandBuffer ); - - EndRenderPass( renderPassLocal ); - - pWaitSemaphores = {&m_PassCompleteSemaphore[RP_RAYTRACE], 1}; - } - - // ********************** - // RP_LIGHT - // ********************** - { - // Lighting pass (Gbuffer resolve and emissives) - auto& renderPassLocal = BeginRenderPass(RP_LIGHT, WhichFrame, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_PassCompleteSemaphore[RP_LIGHT],1 }); - AddPassCommandBuffers( renderPassLocal ); - EndRenderPass( renderPassLocal ); - - pWaitSemaphores = { &m_PassCompleteSemaphore[RP_LIGHT], 1 }; - } - - // ********************** - // RP_HUD - // ********************** - VkCommandBuffer guiCommandBuffer = VK_NULL_HANDLE; - if (gRenderHud && m_Gui) - { - // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(WhichFrame, m_HudRT[0].m_FrameBuffer); - if (guiCommandBuffer != VK_NULL_HANDLE) - { - auto& renderPassLocal = BeginRenderPass(RP_HUD, WhichFrame, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_PassCompleteSemaphore[RP_HUD],1 }); - AddPassCommandBuffers( renderPassLocal, { &guiCommandBuffer,1 } ); - EndRenderPass( renderPassLocal ); - - pWaitSemaphores = { &m_PassCompleteSemaphore[RP_HUD], 1 }; - } - } - - // ********************** - // RP_BLIT - // ********************** - // Blit Results to the screen - { - auto& renderPassLocal = BeginRenderPass(RP_BLIT, WhichFrame, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_PassCompleteSemaphore[RP_BLIT],1 }); - AddPassCommandBuffers( renderPassLocal ); - EndRenderPass( renderPassLocal ); - pWaitSemaphores = { &m_PassCompleteSemaphore[RP_BLIT], 1 }; - } - - rThreadOutput.Fence = rThreadParam.CurrentBuffer.fence; - rThreadOutput.SwapchainPresentIndx = rThreadParam.CurrentBuffer.swapchainPresentIdx; - rThreadOutput.WhichFrame = WhichFrame; -} - -//----------------------------------------------------------------------------- -void Application::Render(float fltDiffTime) -//----------------------------------------------------------------------------- -{ - // Reset Metrics - m_TotalDrawCalls = 0; - m_TotalTriangles = 0; - - // Grab the vulkan wrapper - auto* const pVulkan = GetVulkan(); - - // See if we want to change backbuffer surface format - if (m_RequestedSurfaceFormat.colorSpace != pVulkan->m_SurfaceColorSpace || m_RequestedSurfaceFormat.format != pVulkan->m_SurfaceFormat) - { - ChangeSurfaceFormat(m_RequestedSurfaceFormat); - } - - // Obtain the next swap chain image for the next frame. - auto CurrentVulkanBuffer = pVulkan->SetNextBackBuffer(); - - // ******************************** - // Application Draw() - Begin - // ******************************** - - UpdateCamera(fltDiffTime); - - // Wait for game thread to finish... - m_GameThreadWorker.FinishAllWork(); - - // - // We are running single-threaded here... - // - UpdateGui(); - - if (true || gFramesToRender == 0) - { - // - // Runing 'forever'. - // - // Do rendering 'threaded'. - // - - // ... grab info from the last run ... - int FrameToSubmit = m_CompletedThreadOutput.WhichFrame; - int SwapchainPresentIndx = m_CompletedThreadOutput.SwapchainPresentIndx; - VkFence FrameToRenderFence = m_CompletedThreadOutput.Fence; - - // Start thread updating the next frame. - GameThreadInputParam gameThreadParam{ CurrentVulkanBuffer, fltDiffTime }; - - m_GameThreadWorker.DoWork2( [](Application* pThis, Application::GameThreadInputParam ThreadParam) { - pThis->RunGameThread(ThreadParam, pThis->m_CompletedThreadOutput); - }, this, std::move(gameThreadParam)); - - // Submit and present thread work from last frame (potentially while the thread worker is generating the next frame). - if (FrameToSubmit >= 0) - { - auto finishedSemaphore = SubmitGameThreadWork(FrameToSubmit, FrameToRenderFence); - PresentQueue(finishedSemaphore, SwapchainPresentIndx); - } - } - else - { - // - // Running a (pre)defined number of frames. - // - // Run syncronously so that we render the exact number of requested frames. - // - GameThreadInputParam gameThreadParam{ CurrentVulkanBuffer, fltDiffTime }; - RunGameThread(gameThreadParam, m_CompletedThreadOutput); - - // Submit and present this frame - std::span finishedSemaphore = SubmitGameThreadWork(m_CompletedThreadOutput.WhichFrame, m_CompletedThreadOutput.Fence); - PresentQueue(finishedSemaphore, m_CompletedThreadOutput.SwapchainPresentIndx); - } - - // ******************************** - // Application Draw() - End - // ******************************** -} - -//----------------------------------------------------------------------------- -void Application::KeyDownEvent(uint32_t key) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::KeyDownEvent(key); - - uint32_t TimeNow = OS_GetTimeMS(); - -#ifdef OS_WINDOWS - switch (key) - { - case VK_SPACE: - // Only set this if enough time goes by - if (guiSpacePressedThisFrame == 0 && TimeNow - guiSpacePressedTime > KEY_THIS_FRAME_TIME) - { - // Newly Pressed this frame - guiSpacePressedThisFrame = 1; - guiSpacePressedTime = OS_GetTimeMS(); - } - break; - - case VK_SHIFT: - guiShiftPressed = 1; - break; - - case VK_UP: - case VK_NUMPAD8: - guiUpPressed = 1; - break; - - case VK_DOWN: - case VK_NUMPAD2: - guiDownPressed = 1; - break; - - case VK_LEFT: - case VK_NUMPAD4: - guiLeftPressed = 1; - break; - - case VK_RIGHT: - case VK_NUMPAD6: - guiRightPressed = 1; - break; - - case 'A': guiAPressed = 1; break; - case 'S': guiSPressed = 1; break; - case 'W': guiWPressed = 1; break; - case 'D': guiDPressed = 1; break; - case 'Q': guiQPressed = 1; break; - case 'E': guiEPressed = 1; break; - } -#endif // OS_WINDOWS -} - -//----------------------------------------------------------------------------- -void Application::KeyRepeatEvent(uint32_t key) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::KeyRepeatEvent(key); -} - -//----------------------------------------------------------------------------- -void Application::KeyUpEvent(uint32_t key) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::KeyUpEvent(key); - - uint32_t TimeNow = OS_GetTimeMS(); - -#ifdef OS_WINDOWS - switch (key) - { - case VK_SPACE: - // Key is up so reset flag - guiSpacePressedThisFrame = 0; - break; - - case VK_SHIFT: - guiShiftPressed = 0; - break; - - case VK_UP: - case VK_NUMPAD8: - guiUpPressed = 0; - break; - - case VK_DOWN: - case VK_NUMPAD2: - guiDownPressed = 0; - break; - - case VK_LEFT: - case VK_NUMPAD4: - guiLeftPressed = 0; - break; - - case VK_RIGHT: - case VK_NUMPAD6: - guiRightPressed = 0; - break; - - case 'A': guiAPressed = 0; break; - case 'S': guiSPressed = 0; break; - case 'W': guiWPressed = 0; break; - case 'D': guiDPressed = 0; break; - case 'Q': guiQPressed = 0; break; - case 'E': guiEPressed = 0; break; - - } -#endif // OS_WINDOWS - -} - -//----------------------------------------------------------------------------- -void Application::TouchDownEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::TouchDownEvent(iPointerID, xPos, yPos); - - m_LightChangePaused = !m_LightChangePaused; - - m_SphereCamera.TouchDownEvent(iPointerID, xPos, yPos); - - // if (gLightColorA[0] == 1.0f) - // { - // gLightColorA = glm::vec4(0.1f, 0.1f, 0.1f, 2.25f); - // gLightColorB = glm::vec4(0.1f, 0.1f, 0.1f, 2.25f); - // } - // else - // { - // gLightColorA = glm::vec4(1.0f, 1.0f, 1.0f, 2.25f); - // gLightColorB = glm::vec4(1.0f, 1.0f, 1.0f, 2.25f); - // } -} - -//----------------------------------------------------------------------------- -void Application::TouchMoveEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::TouchMoveEvent(iPointerID, xPos, yPos); - - m_SphereCamera.TouchMoveEvent(iPointerID, xPos, yPos); -} - -//----------------------------------------------------------------------------- -void Application::TouchUpEvent(int iPointerID, float xPos, float yPos) -//----------------------------------------------------------------------------- -{ - ApplicationHelperBase::TouchUpEvent(iPointerID, xPos, yPos); - - m_SphereCamera.TouchUpEvent(iPointerID, xPos, yPos); -} - - -//----------------------------------------------------------------------------- -bool Application::LoadTextures() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // Load 'loose' textures - m_TextureManager->GetOrLoadTexture("Environment", *m_AssetManager, "./Media/Textures/simplesky_env.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("Irradiance", *m_AssetManager, "./Media/Textures/simplesky_irradiance.ktx", SamplerAddressMode::Repeat); - - m_TexWhite = LoadKTXTexture(GetVulkan(), *m_AssetManager, "./Media/Textures/white_d.ktx", SamplerAddressMode::Repeat); - m_DefaultNormal = LoadKTXTexture(GetVulkan(), *m_AssetManager, "./Media/Textures/normal_default.ktx", SamplerAddressMode::Repeat); - - if (m_TexWhite.IsEmpty() || m_DefaultNormal.IsEmpty()) - { - return false; - } - - // m_TexBunnySplash = LoadKTXTexture(GetVulkan(), *m_AssetManager, "./Media/Textures/bbb_splash.ktx", SamplerAddressMode::ClampEdge); - // m_loadedTextures.emplace("Bunny", *m_AssetManager, "./Media/Textures/bbb_splash.ktx", SamplerAddressMode::ClampEdge); - - m_TextureManager->GetOrLoadTexture("bbb_splash", *m_AssetManager, "./Media/Textures/stub_checker.ktx", SamplerAddressMode::ClampEdge); - m_TextureManager->GetOrLoadTexture("stub_red", *m_AssetManager, "./Media/Textures/stub_red.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_green", *m_AssetManager, "./Media/Textures/stub_green.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_blue", *m_AssetManager, "./Media/Textures/stub_blue.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_pink", *m_AssetManager, "./Media/Textures/stub_pink.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_black", *m_AssetManager, "./Media/Textures/stub_black.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_white", *m_AssetManager, "./Media/Textures/stub_white.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_checker", *m_AssetManager, "./Media/Textures/stub_checker.ktx", SamplerAddressMode::Repeat); - m_TextureManager->GetOrLoadTexture("stub_normal", *m_AssetManager, "./Media/Textures/normal_default.ktx", SamplerAddressMode::Repeat); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::CreateRayInputs() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // Need the textures in an array that can be passed to shader - m_SceneTextures.clear(); - - // Lookup for m_SceneTextures while we fill it with texture pointers. - std::map sceneTextureIndices; - - auto getLoadedTextureIndex = [this, &sceneTextureIndices]( const std::string& name ) -> size_t { - auto [it, emplaced] = sceneTextureIndices.try_emplace( name, 0 ); - if (emplaced) - { - const Texture* pTexture = m_TextureManager->GetTexture( name ); - assert( pTexture ); // We expect to find all the textures we are looking for! - size_t index = &m_SceneTextures.emplace_back( pTexture ) - m_SceneTextures.data(); - it->second = index; - } - return it->second; - }; - - // After textures are loaded we can fill out scene data. - // Loop through the internal structure and fill in the blanks - - for (auto& OneInternal : m_SceneDataInternalArray) - { - SceneData NewSceneData; - NewSceneData.MeshIndex = OneInternal.MeshIndex; - - NewSceneData.Transform = OneInternal.Transform; - NewSceneData.TransformIT = OneInternal.TransformIT; - - - NewSceneData.DiffuseTex = getLoadedTextureIndex(OneInternal.DiffuseTex); - NewSceneData.NormalTex = getLoadedTextureIndex(OneInternal.NormalTex); - - m_SceneDataArray.push_back(NewSceneData); - } - - // Make the actual scene data buffer (vkBuffer to pass to material) - uint32_t dataSize = (uint32_t)(sizeof(SceneData) * m_SceneDataArray.size()); - BufferUsageFlags usage = BufferUsageFlags::Storage | BufferUsageFlags::Uniform; - if (!CreateUniformBuffer(pVulkan, &m_SceneDataUniform, dataSize, m_SceneDataArray.data(), usage)) - { - LOGE("Unable to create uniform buffer for scene data!"); - assert(0); - return false; - } - - return true; -} - -bool Application::CreateRenderTargets() -//----------------------------------------------------------------------------- -{ - LOGI("******************************"); - LOGI("Creating Render Targets..."); - LOGI("******************************"); - - auto* const pVulkan = GetVulkan(); - - const TextureFormat DesiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); - - // Setup the GBuffer - const TextureFormat GbufferColorType[] = { - TextureFormat::R8G8B8A8_UNORM, // Albedo - TextureFormat::R16G16B16A16_SFLOAT, // Normals - TextureFormat::R8G8B8A8_UNORM, // Material Properties - }; - - if (!m_GBufferRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, GbufferColorType, DesiredDepthFormat, VK_SAMPLE_COUNT_1_BIT, "GBuffer RT")) - { - LOGE("Unable to create gbuffer render target"); - } - - // Setup the 'main' (compositing) buffer - const TextureFormat MainColorType[] = { TextureFormat::R16G16B16A16_SFLOAT }; - - if (!m_MainRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, m_GBufferRT/*inherit depth*/, VK_SAMPLE_COUNT_1_BIT, "Main RT")) - { - LOGE("Unable to create main render target"); - } - - // Setup the HUD render target (no depth) - const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_UNORM }; - - // Surface Width/Height can be 0 and on Android it is set to surface width - // On Windows is is still left at 0. - uint32_t HudWidth = gSurfaceWidth; - if(HudWidth == 0) - HudWidth = gRenderWidth; - - uint32_t HudHeight = gSurfaceHeight; - if (HudHeight == 0) - HudHeight = gRenderHeight; - - if (!m_HudRT.Initialize(pVulkan, HudWidth, HudHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) - { - LOGE("Unable to create hud render target"); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitShadowMap() -//----------------------------------------------------------------------------- -{ - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitLighting() -//----------------------------------------------------------------------------- -{ - m_LightColor = { 1.0f, 0.98f, 0.73f, 50.0f }; - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadConstantShaders() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - m_ShaderManager->RegisterRenderPassNames({ sRenderPassNames, sRenderPassNames + (sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0])) }); - - LOGI("Loading Shaders..."); - - typedef std::pair tIdAndFilename; - for (const tIdAndFilename& i : - { tIdAndFilename { "ObjectDeferred", "Media\\Shaders\\ObjectDeferred.json" }, - tIdAndFilename { "Skybox", "Media\\Shaders\\Skybox.json" }, - // tIdAndFilename { "Light", "Media\\Shaders\\Light.json" }, - tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" } - }) - { - LOGI(" %s", i.second.c_str()); - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) - { - LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadDynamicShaders() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - LOGI("Loading Shaders (With dynamic elements)..."); - - typedef std::pair tIdAndFilename; - for (const tIdAndFilename& i : - { - tIdAndFilename { "Light", "Media\\Shaders\\Light.json" }, - }) - { - LOGI(" %s", i.second.c_str()); - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) - { - LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadDynamicRayTracingObjects() -//----------------------------------------------------------------------------- -{ - if (true) // Light - { - // - // Create the 'Light' drawable mesh. - // Fullscreen quad that does the gbuffer lighting pass (fullscreen pass of gbuffer, outputting a lit color buffer). - LOGI("Creating Light mesh..."); - MeshObject lightMesh; - MeshHelper::CreateScreenSpaceMesh(GetVulkan()->GetMemoryManager(), 0, &lightMesh); - - const auto* pLightShader = m_ShaderManager->GetShader("Light"); - assert(pLightShader); - auto lightShaderMaterial = m_MaterialManager->CreateMaterial(*GetVulkan(), *pLightShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { - if (texName == "TexArray") { - return m_SceneTextures; - } - else if (texName == "Albedo") { - return { &m_GBufferRT[0].m_ColorAttachments[0] }; - } - else if (texName == "Normal") { - return { &m_GBufferRT[0].m_ColorAttachments[1] }; - } - else if (texName == "MatProps") { - return { &m_GBufferRT[0].m_ColorAttachments[2] }; - } - else if (texName == "Depth") { - return { &m_GBufferRT[0].m_DepthAttachment }; - } - else if (texName == "Environment") - { - auto* pTexture = m_TextureManager->GetTexture(texName); - if (pTexture) - return { pTexture }; - - assert(0); - return {}; - } - else if (texName == "Irradiance") - { - auto* pTexture = m_TextureManager->GetTexture(texName); - if (pTexture) - return { pTexture }; - - assert(0); - return {}; - } - else - { - LOGE("Texture not handled in Light material: %s", texName.c_str()); - assert(0); - return {}; - } - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "FragUB") - { - // This same uniform is used for both RT and Irradiance - return { m_LightFragUniform[0].buf.GetVkBuffer() }; - } - else if (bufferName == "SceneData") - { - return { m_SceneDataUniform.buf.GetVkBuffer() }; - } - else if (bufferName == "VertData") - { - return m_RayMeshVkBuffers; - } - else if (bufferName == "IndiceData") - { - return m_RayIndiceVkBuffers; - } - else - { - LOGE("Buffer not handled in Light material: %s", bufferName.c_str()); - assert(0); - return {}; - } - }, - nullptr, - [this](const std::string& accelStructureName) -> MaterialPass::tPerFrameVkAccelerationStructure { - return { m_sceneRayTracableCulled->GetVkAccelerationStructure() }; - } - ); - - m_LightDrawable = std::make_unique(*GetVulkan(), std::move(lightShaderMaterial)); - if (!m_LightDrawable->Init(m_RenderPass[RP_LIGHT], sRenderPassNames[RP_LIGHT], std::move(lightMesh))) - { - LOGE("Error Creating Light drawable..."); - } - - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitUniforms() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // These are only created here, they are not set to initial values - LOGI("Creating uniform buffers..."); - - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - CreateUniformBuffer(pVulkan, m_ObjectVertUniform[WhichPass][WhichBuffer]); - CreateUniformBuffer(pVulkan, m_SphereFragUniform[WhichPass][WhichBuffer]); - CreateUniformBuffer(pVulkan, m_FloorFragUniform[WhichPass][WhichBuffer]); - - CreateUniformBuffer(pVulkan, m_SkyboxVertUniform[WhichPass][WhichBuffer]); - } // WhichBuffer - } // WhichPass - - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - CreateUniformBuffer(pVulkan, m_BlitFragUniform[WhichBuffer]); - CreateUniformBuffer(pVulkan, m_LightFragUniform[WhichBuffer]); - } - - return true; -} - -//----------------------------------------------------------------------------- -glm::mat4 Application::CreateRandomRotation() -//----------------------------------------------------------------------------- -{ -// #ifdef TEST_METHOD - // Rotate randomly around all three axis - glm::mat4 RetVal = glm::identity(); - - float OneRotation = PI_MUL_2 * (float)rand() / (float)RAND_MAX; - RetVal = glm::rotate(RetVal, OneRotation, glm::vec3(1.0f, 0.0f, 0.0f)); - - OneRotation = PI_MUL_2 * (float)rand() / (float)RAND_MAX; - RetVal = glm::rotate(RetVal, OneRotation, glm::vec3(0.0f, 1.0f, 0.0f)); - - OneRotation = PI_MUL_2 * (float)rand() / (float)RAND_MAX; - RetVal = glm::rotate(RetVal, OneRotation, glm::vec3(0.0f, 0.0f, 1.0f)); - - // #error LOGE("Probe Ray Transform NOT random!"); - // RetVal = glm::identity(); - - return RetVal; -// #endif // TEST_METHOD - -#ifdef LONG_METHOD - // #error Create three random rotations and multiply - - glm::mat4 RetVal = glm::identity(); - - // Random around the pole in [0, 1] - float ThetaVal = PI_MUL_2 * (float)rand() / (float)RAND_MAX; - float cosTheta = std::cosf(ThetaVal); - float sinTheta = std::sinf(ThetaVal); - - // Random direction to deflect pole in [0, 1] - float PhiVal = PI_MUL_2 * (float)rand() / (float)RAND_MAX; - float cosPhi = std::cosf(PhiVal); - float sinPhi = std::sinf(PhiVal); - - // Random amount of pole deflection - float ZVal = (float)rand() / (float)RAND_MAX; - - float sq3 = 2.f * std::sqrtf(ZVal * (1.f - ZVal)); - - float s2 = 2.f * ZVal * sinTheta * sinTheta - 1.f; - float c2 = 2.f * ZVal * cosTheta * cosTheta - 1.f; - float sc = 2.f * ZVal * sinTheta * cosTheta; - - // Create the random rotation matrix - float _11 = cosTheta * c2 - sinTheta * sc; - float _12 = sinTheta * c2 + cosTheta * sc; - float _13 = sq3 * cosTheta; - - float _21 = cosTheta * sc - sinTheta * s2; - float _22 = sinTheta * sc + cosTheta * s2; - float _23 = sq3 * sinTheta; - - float _31 = cosTheta * (sq3 * cosTheta) - sinTheta * (sq3 * sinTheta); - float _32 = sinTheta * (sq3 * cosTheta) + cosTheta * (sq3 * sinTheta); - float _33 = 1.f - 2.f * ZVal; - - // Set columns to output - RetVal[0] = glm::vec4(_11, _12, _13, 0.0f); - RetVal[1] = glm::vec4(_21, _22, _23, 0.0f); - RetVal[2] = glm::vec4(_31, _32, _33, 0.0f); - RetVal[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - - // #error LOGE("Probe Ray Transform NOT random!"); - // RetVal = glm::identity(); - - return RetVal; -#endif // LONG_METHOD - -} - -//----------------------------------------------------------------------------- -bool Application::UpdateUniforms(uint32_t WhichBuffer) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // Things that change over time - if(m_LastUpdateTime == 0) - m_LastUpdateTime = OS_GetTimeMS(); - - uint32_t TimeNow = OS_GetTimeMS(); - uint32_t TimeDiff = TimeNow - m_LastUpdateTime; - m_LastUpdateTime = TimeNow; - -#ifdef OS_WINDOWS - if (guiSpacePressedThisFrame == 1) - { - m_LightChangePaused = !m_LightChangePaused; - } -#endif // OS_WINDOWS - - - // For debugging, want to control how big a time jump we can get - if (TimeDiff > 100) - TimeDiff = 100; - - if(!m_LightChangePaused) - m_LightDirUpdateTime += TimeDiff; - if (gLightDirChangeRate != 0.0f && m_LightDirUpdateTime > (uint32_t)(gLightDirChangeRate * 1000.0f)) - { - // Hit the end. Pull off "extra" and reverse direction. - m_LightDirUpdateTime -= (uint32_t)(gLightDirChangeRate * 1000.0f); - m_LightDirReverse = !m_LightDirReverse; - } - - if (!m_LightChangePaused) - m_LightColorUpdateTime += TimeDiff; - if (gLightColorChangeRate != 0.0f && m_LightColorUpdateTime > (uint32_t)(gLightColorChangeRate * 1000.0f)) - { - // Hit the end. Pull off "extra" and reverse direction. - m_LightColorUpdateTime -= (uint32_t)(gLightColorChangeRate * 1000.0f); - m_LightColorReverse = !m_LightColorReverse; - } - - // Want light direction to be normalized - glm::vec3 LocalLightDir; - float LightDirLerp = 1.0f; - if(gLightDirChangeRate != 0.0) - LightDirLerp = (float)m_LightDirUpdateTime / (gLightDirChangeRate * 1000.0f); - if(m_LightDirReverse) - LocalLightDir = L_Lerp(LightDirLerp, gLightDirB, gLightDirA); - else - LocalLightDir = L_Lerp(LightDirLerp, gLightDirA, gLightDirB); - - LocalLightDir = glm::normalize(LocalLightDir); - - glm::vec4 LocalLightColor; - float LightColorLerp = 1.0f; - if(gLightColorChangeRate != 0.0f) - LightColorLerp = (float)m_LightColorUpdateTime / (gLightColorChangeRate * 1000.0f); - if(m_LightColorReverse) - LocalLightColor = L_Lerp(LightColorLerp, gLightColorB, gLightColorA); - else - LocalLightColor = L_Lerp(LightColorLerp, gLightColorA, gLightColorB); - - - // Special View matrix for Skybox - glm::mat4 SkyboxViewMatrix; - - // ******************************** - // Object - // ******************************** - glm::mat4 ObjectVP = m_SphereCamera.ProjectionMatrix() * m_SphereCamera.ViewMatrix(); - - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // No uniform buffers for HUD or BLIT since objects not in HUD and BLIT - if (WhichPass == RP_HUD || WhichPass == RP_BLIT || WhichPass == RP_LIGHT || WhichPass == RP_RAYTRACE) - continue; - - switch (WhichPass) - { - case RP_GBUFFER: - m_ObjectVertUniformData.VPMatrix = ObjectVP; - break; - default: - LOGE("***** %s Not Handled! *****", GetPassName(WhichPass)); - break; - } - - UpdateUniformBuffer(pVulkan, m_ObjectVertUniform[WhichPass][WhichBuffer], m_ObjectVertUniformData); - - m_ObjectFragUniformData.Color = glm::vec4(0.9f, 0.9f, 0.9f, 1.0f); // White by default - m_ObjectFragUniformData.MaterialProps = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f); // Max Ray Bounces (Really up to 255). Minimized by config variable - UpdateUniformBuffer(pVulkan, m_SphereFragUniform[WhichPass][WhichBuffer], m_ObjectFragUniformData); - - m_ObjectFragUniformData.Color = glm::vec4(0.9f, 0.9f, 0.9f, 1.0f); // White by default - m_ObjectFragUniformData.MaterialProps = glm::vec4(gFloorRayBounces / 255.0f, 0.0f, 0.0f, 0.0f); // Config Ray Bounces - UpdateUniformBuffer(pVulkan, m_FloorFragUniform[WhichPass][WhichBuffer], m_ObjectFragUniformData); - } - - // ******************************** - // Skybox - // ******************************** - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // No uniform buffers for HUD or BLIT since objects not in HUD and BLIT - if (WhichPass == RP_HUD || WhichPass == RP_BLIT) - continue; - - glm::mat4 LocalModel = glm::scale(glm::vec3(m_SkyboxScale, m_SkyboxScale, m_SkyboxScale)); - - // Special View matrix for Skybox (always centered on the view position) - SkyboxViewMatrix = m_SphereCamera.ViewMatrix(); - // m_SphereCamera.GetViewMatrix(SkyboxViewMatrix); - SkyboxViewMatrix[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - glm::mat4 SkyboxMVP = m_SphereCamera.ProjectionMatrix() * SkyboxViewMatrix * LocalModel; - - m_SkyboxVertUniformData.MVPMatrix = SkyboxMVP; - m_SkyboxVertUniformData.ModelMatrix = LocalModel; - m_SkyboxVertUniformData.Color = glm::vec4(0.9f, 0.9f, 0.9f, 1.0f); // White by default - UpdateUniformBuffer(pVulkan, m_SkyboxVertUniform[WhichPass][WhichBuffer], m_SkyboxVertUniformData); - } - - // ******************************** - // Fullscreen Light pass - // ******************************** - - glm::mat4 CameraViewInv = glm::inverse(m_SphereCamera.ViewMatrix()); - glm::mat4 CameraProjectionInv = glm::inverse(m_SphereCamera.ProjectionMatrix()); - - m_LightFragUniformData.ProjectionInv = CameraProjectionInv; - m_LightFragUniformData.ViewInv = CameraViewInv; - - m_LightFragUniformData.ShaderTint = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - - m_LightFragUniformData.EyePos = glm::vec4(m_SphereCamera.Position(), 1.0f); - m_LightFragUniformData.LightDir = glm::vec4(LocalLightDir, 1.0f); - m_LightFragUniformData.LightColor = LocalLightColor; - - // X: Mip Level - // Y: Reflect Amount - // Z: Reflect Fresnel Min - // W: Reflect Fresnel Max - m_LightFragUniformData.ReflectParams = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); - - // X: Mip Level - // Y: IBL Amount - // Z: Not Used - // W: Not Used - m_LightFragUniformData.IrradianceParams = glm::vec4(1.0f, 0.2f, 0.0f, 0.0f); - - // X: Ray Min Distance - // Y: Ray Max Distance - // Z: Max Ray Bounces - // W: Ray Blend Amount (Mirror Amount) - m_LightFragUniformData.RayParam01 = glm::vec4(gRayMinDistance, gRayMaxDistance, (float)gSphereRayBounces, gRayBlendAmount); - - // X: Shadow Amount - // Y: Minumum Normal Dot Light - // Z: Not Used - // W: Not Used - m_LightFragUniformData.LightParams = glm::vec4(gShadowAmount, gMinNormDotLight, 0.0f, 0.0f); - - UpdateUniformBuffer(pVulkan, m_LightFragUniform[WhichBuffer], m_LightFragUniformData); - - // ******************************** - // Blit - // ******************************** - - m_BlitFragUniformData.sRGB = m_bEncodeSRGB ? 1 : 0; - UpdateUniformBuffer(pVulkan, m_BlitFragUniform[WhichBuffer], m_BlitFragUniformData); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::BuildCmdBuffers() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - LOGI("******************************"); - LOGI("Building Command Buffers..."); - LOGI("******************************"); - - uint32_t TargetWidth = GetVulkan()->m_SurfaceWidth; - uint32_t TargetHeight = GetVulkan()->m_SurfaceHeight; - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // If viewport and scissor are dynamic we need to add them to the secondary buffers. - const uint32_t TargetWidth = m_PassSetup[WhichPass].TargetWidth; - const uint32_t TargetHeight = m_PassSetup[WhichPass].TargetHeight; - - VkViewport Viewport = {}; - Viewport.x = 0.0f; - Viewport.y = 0.0f; - Viewport.width = (float)TargetWidth; - Viewport.height = (float)TargetHeight; - Viewport.minDepth = 0.0f; - Viewport.maxDepth = 1.0f; - - VkRect2D Scissor = {}; - Scissor.offset.x = 0; - Scissor.offset.y = 0; - Scissor.extent.width = TargetWidth; - Scissor.extent.height = TargetHeight; - - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; WhichBuffer++) - { - // Set up some values that change based on render pass - VkRenderPass WhichRenderPass = VK_NULL_HANDLE; - VkFramebuffer WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - switch (WhichPass) - { - case RP_GBUFFER: - WhichRenderPass = m_GBufferRT.m_RenderPass; - WhichFramebuffer = m_GBufferRT[0].m_FrameBuffer; - break; - - case RP_LIGHT: - WhichRenderPass = m_MainRT.m_RenderPass; - WhichFramebuffer = m_MainRT[0].m_FrameBuffer; - break; - - case RP_HUD: - WhichRenderPass = m_HudRT.m_RenderPass; - WhichFramebuffer = m_HudRT[0].m_FrameBuffer; - break; - - case RP_BLIT: - WhichRenderPass = m_RenderPass[WhichPass]; - WhichFramebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - break; - } - - if (WhichPass == RP_LIGHT) - { - // Light deferred gbuffer - if (!m_LightCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass)) - { - return false; - } - vkCmdSetViewport(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Scissor); - } - - if (WhichPass == RP_BLIT) - { - // Blit (only in the blit pass) - if (!m_BlitCmdBuffer[WhichBuffer].Begin(WhichFramebuffer, WhichRenderPass, true/*swapchain renderpass*/)) - { - return false; - } - vkCmdSetViewport(m_BlitCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_BlitCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Scissor); - } - else - { - // Objects (can render into any pass except Blit) - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Begin(WhichFramebuffer, WhichRenderPass)) - { - return false; - } - vkCmdSetViewport(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer, 0, 1, &Scissor); - } - } // Which Buffer - } // Which Pass - - // Run through the drawables (each one may be in multiple command buffers) - for (const auto& drawable : m_SceneDrawables) - { - AddDrawableToCmdBuffers(drawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, GetVulkan()->m_SwapchainImageCount); - } - - // Add the skybox (last) - if (m_SkyboxDrawable) - { - AddDrawableToCmdBuffers(*m_SkyboxDrawable, &m_ObjectCmdBuffer[0][0], NUM_RENDER_PASSES, GetVulkan()->m_SwapchainImageCount); - } - - // and end their pass - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - if (WhichPass != RP_BLIT) - { - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].End()) - { - return false; - } - } - } - } - - // Do the light commands - AddDrawableToCmdBuffers(*m_LightDrawable.get(), m_LightCmdBuffer, 1, GetVulkan()->m_SwapchainImageCount); - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_LightCmdBuffer[WhichBuffer].End()) - { - return false; - } - } - - // Do the blit commands - AddDrawableToCmdBuffers(*m_BlitDrawable.get(), m_BlitCmdBuffer, 1, GetVulkan()->m_SwapchainImageCount); - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_BlitCmdBuffer[WhichBuffer].End()) - { - return false; - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitLocalSemaphores() -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - auto* const pVulkan = GetVulkan(); - - VkSemaphoreCreateInfo SemaphoreInfo = {}; - SemaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - SemaphoreInfo.pNext = NULL; - SemaphoreInfo.flags = 0; - - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - RetVal = vkCreateSemaphore(pVulkan->m_VulkanDevice, &SemaphoreInfo, NULL, &m_PassCompleteSemaphore[WhichPass]); - if (!CheckVkError("vkCreateSemaphore()", RetVal)) - { - return false; - } - } - - return true; -} - -//----------------------------------------------------------------------------- -const char* Application::GetPassName(uint32_t WhichPass) -//----------------------------------------------------------------------------- -{ - if (WhichPass >= sizeof(sRenderPassNames) / sizeof(sRenderPassNames[0])) - { - LOGE("GetPassName() called with unknown pass (%d)!", WhichPass); - return "RP_UNKNOWN"; - } - return sRenderPassNames[WhichPass]; -} - -//----------------------------------------------------------------------------- -bool Application::InitMaterials() -//----------------------------------------------------------------------------- -{ - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitCommandBuffers() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - char szName[256]; - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - // The Pass Command Buffer => Primary - sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_PassCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) - { - return false; - } - - // Model => Secondary - sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_ObjectCmdBuffer[WhichBuffer][WhichPass].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) - { - return false; - } - - } - } - // Blit => Secondary - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - sprintf(szName, "Blit (%s; Buffer %d of %d)", GetPassName(RP_BLIT), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_BlitCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) - { - return false; - } - } - - // Light => Secondary - for (uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++) - { - sprintf(szName, "Light (%s; Buffer %d of %d)", GetPassName(RP_LIGHT), WhichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_LightCmdBuffer[WhichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_SECONDARY)) - { - return false; - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitAllRenderPasses() -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - auto* const pVulkan = GetVulkan(); - - // Fill in the Setup data - m_PassSetup[RP_GBUFFER] = { m_GBufferRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Store, gClearColor,m_GBufferRT[0].m_Width, m_GBufferRT[0].m_Height }; - m_PassSetup[RP_RAYTRACE] ={ {}, TextureFormat::UNDEFINED, RenderPassInputUsage::DontCare,false, RenderPassOutputUsage::Discard, RenderPassOutputUsage::Discard, {}, m_GBufferRT[0].m_Width, m_GBufferRT[0].m_Height }; - m_PassSetup[RP_LIGHT] = { m_MainRT[0].m_pLayerFormats, m_GBufferRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_MainRT[0].m_Width, m_MainRT[0].m_Height }; - m_PassSetup[RP_HUD] = { m_HudRT[0].m_pLayerFormats, m_HudRT[0].m_DepthFormat, RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}, m_HudRT[0].m_Width, m_HudRT[0].m_Height }; - m_PassSetup[RP_BLIT] = { {pVulkan->m_SurfaceFormat}, pVulkan->m_SwapchainDepth.format, RenderPassInputUsage::DontCare,false, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}, GetVulkan()->m_SurfaceWidth, GetVulkan()->m_SurfaceHeight }; - - LOGI("******************************"); - LOGI("Initializing Render Passes... "); - LOGI("******************************"); - for (uint32_t WhichPass = 0; WhichPass < NUM_RENDER_PASSES; WhichPass++) - { - const auto& PassSetup = m_PassSetup[WhichPass]; - bool IsSwapChainRenderPass = WhichPass == RP_BLIT; - - if (PassSetup.ColorFormats.size() > 0 || PassSetup.DepthFormat != TextureFormat::UNDEFINED) - { - if (!GetVulkan()->CreateRenderPass({PassSetup.ColorFormats}, - PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, - PassSetup.ColorInputUsage, - PassSetup.ColorOutputUsage, - PassSetup.ClearDepthRenderPass, - PassSetup.DepthOutputUsage, - &m_RenderPass[WhichPass])) - return false; - } - - // LOGI(" Render Pass (%s; Buffer %d of %d) => 0x%x", GetPassName(WhichPass), WhichBuffer + 1, NUM_VULKAN_BUFFERS, m_RenderPass[WhichPass][WhichBuffer]); - } // Which Pass - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::InitDrawables() -//----------------------------------------------------------------------------- -{ - // Part of the kludge to get callback function to return the correct buffer - const uint32_t Mat_Sphere = 5150; - const uint32_t Mat_Floor = 1999; - - const auto& bufferLoader = [&](const uint32_t MaterialID, const std::string& bufferSlotName) -> tPerFrameVkBuffer { - if (bufferSlotName == "Vert") - { - return { m_ObjectVertUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; - } - else if (bufferSlotName == "Frag") - { - switch (MaterialID) - { - case Mat_Sphere: - return { m_SphereFragUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; - case Mat_Floor: - return { m_FloorFragUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; - default: - assert(0); - return {}; - } - - } - else - { - assert(0); - return {}; - } - }; - - const auto* pOpaqueShader = m_ShaderManager->GetShader("ObjectDeferred"); - if (!pOpaqueShader) - { - // Error (bad shaderName) - return false; - } - - // m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media/Textures/" }, PathManipulator_ChangeExtension{ ".ktx" }); - - if (1) - { - auto sceneTextureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const MaterialPass::tPerFrameTexInfo - { - - std::string textureName; - bool normalMap = false; - - if (textureSlotName == "Diffuse") - { - textureName = materialDef.diffuseFilename; - } - else if (textureSlotName == "Normal") - { - normalMap = true; - textureName = materialDef.bumpFilename; - if (textureName.empty()) - { - LOGE("No texture specified for slot (Using flat default): %s", textureSlotName.c_str()); - return { &m_DefaultNormal }; - } - } - else if (textureSlotName == "SpecMap") - { - textureName = materialDef.specMapFilename; - if (textureName.empty()) - { - LOGE("No texture specified for slot (Using white default): %s", textureSlotName.c_str()); - return { &m_TexWhite }; - } - } - else if (textureSlotName == "Environment") - { - auto* pTexture = m_TextureManager->GetTexture(textureSlotName); - if (pTexture) - return { pTexture }; - - // File not loaded, use default - LOGE("No texture loaded for slot: %s", textureSlotName.c_str()); - assert(0); - return {}; - } - else if (textureSlotName == "Irradiance") - { - auto* pTexture = m_TextureManager->GetTexture(textureSlotName); - if (pTexture) - return { pTexture }; - - // File not loaded, use default - LOGE("No texture loaded for slot: %s", textureSlotName.c_str()); - assert(0); - return {}; - } - else - { - LOGE("Texture slot NOT handled: %s", textureSlotName.c_str()); - assert(0); - } - - if (textureName.empty()) - { - LOGE("No texture specified for slot (Using white default): %s", textureSlotName.c_str()); - // LOGE(" Diffuse: %s", materialDef.diffuseFilename.c_str()); - // LOGE(" Bump: %s", materialDef.bumpFilename.c_str()); - // LOGE(" Emissive: %s", materialDef.emissiveFilename.c_str()); - // LOGE(" SpecMap: %s", materialDef.specMapFilename.c_str()); - // LOGE(" alphaCutout: %s", materialDef.alphaCutout ? "True" : "False"); - // LOGE(" Transparent: %s", materialDef.transparent ? "True" : "False"); - return { &m_TexWhite }; - } - - auto pTexture = m_TextureManager->GetTexture(textureName); - if (!pTexture) - { - LOGE("Referenced texture has NOT been loaded: %s", textureName.c_str()); - return { &m_TexWhite }; - } - - if (!pTexture) - { - if (normalMap) - { - LOGE("NormalMap texture not found (Using flat default): %s", textureName.c_str()); - return { &m_DefaultNormal }; - } - - // File not loaded, use default - // #error This is incorrect. The texture array size will now be one less than the count! Material will crash. - LOGE("Texture not found (using white default): %s", textureName.c_str()); - LOGE(" ERROR! This will cause the texture list to ray tracing to crash!"); - return { &m_TexWhite }; - } - else - { - return { pTexture }; - } - - }; - - // This Material ID is kind of a kludge to get the callback function to assign the correct buffer - uint32_t MaterialID = Mat_Sphere; - const auto& sceneMaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef) -> std::optional - { - using namespace std::placeholders; - return m_MaterialManager->CreateMaterial(*GetVulkan(), *pOpaqueShader, NUM_VULKAN_BUFFERS, - std::bind(sceneTextureLoader, std::cref(materialDef), _1), - std::bind(bufferLoader, MaterialID, _1)); - }; - - // DrawableLoader::LoadDrawables(*GetVulkan(), *m_AssetManager, m_RenderPass, sRenderPassNames, gObjectName01, sceneMaterialLoader, m_SceneDrawables, {}, DrawableLoader::LoaderFlags::FindInstances/*|DrawableLoader::LoaderFlags::BakeTransforms*/, {}, gObjectScale01); - // DrawableLoader::LoadDrawables(*GetVulkan(), *m_AssetManager, m_RenderPass, sRenderPassNames, gObjectName02, sceneMaterialLoader, m_SceneDrawables, {}, DrawableLoader::LoaderFlags::FindInstances/*|DrawableLoader::LoaderFlags::BakeTransforms*/, {}, gObjectScale02); - - // Can't use DrawableLoader::LoadDrawables because GLTF does not contain material information :( - // So load the GLTF, set materials, and then create the drawable - uint32_t loaderFlags = 0; // i.e. DrawableLoader::LoaderFlags::FindInstances - bool ignoreTransforms = false; - - // Load the objects in scene - float StepRadians = PI_MUL_2 / (float)gNumObjects; - for (uint32_t WhichObject = 0; WhichObject < gNumObjects; WhichObject++) - { - float OneX = gSpacingRadius * cosf((float)WhichObject * StepRadians); - float OneY = gObjectHeight; - float OneZ = gSpacingRadius * sinf((float)WhichObject * StepRadians); - - // This is really the same object, just load it multiple times and will instance later - std::vector MeshObject = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gObjectName, false, gObjectScale); - for (MeshObjectIntermediate& OneMesh : MeshObject) - { - OneMesh.m_Transform[3] = glm::vec4(OneX, OneY, OneZ, 1.0f); - OneMesh.m_Materials[0].diffuseFilename = gObjectDiffuse; - OneMesh.m_Materials[0].bumpFilename = std::string("stub_normal"); - } - - MaterialID = Mat_Sphere; - DrawableLoader::CreateDrawables(*GetVulkan(), std::move(MeshObject), m_RenderPass, sRenderPassNames, sceneMaterialLoader, m_SceneDrawables, {}, loaderFlags, {}); - MeshObject.erase(MeshObject.begin(), MeshObject.end()); - } - - // Load the floor/base of the scene - { - // LOGI("Loading Ray Tracing mesh object: %s...", gFloorName); - std::vector MeshObject = MeshObjectIntermediate::LoadGLTF(*m_AssetManager, gFloorName, false, gFloorScale); - for (MeshObjectIntermediate& OneMesh : MeshObject) - { - if (OneMesh.m_Materials.size() < 1) - OneMesh.m_Materials.resize(1); - - OneMesh.m_Transform = glm::rotate(OneMesh.m_Transform, -PI_DIV_2, glm::vec3(1.0f, 0.0f, 0.0f)); - OneMesh.m_Transform = glm::rotate(OneMesh.m_Transform, PI, glm::vec3(0.0f, 1.0f, 0.0f)); - - OneMesh.m_Transform[3] = glm::vec4(gFloorPos, 1.0f); - OneMesh.m_Materials[0].diffuseFilename = gFloorDiffuse; - OneMesh.m_Materials[0].bumpFilename = std::string("stub_normal"); - } - - MaterialID = Mat_Floor; - DrawableLoader::CreateDrawables(*GetVulkan(), std::move(MeshObject), m_RenderPass, sRenderPassNames, sceneMaterialLoader, m_SceneDrawables, {}, loaderFlags, {}); - MeshObject.erase(MeshObject.begin(), MeshObject.end()); - } - - } - - // ********************** - // Skybox - // ********************** - { - MeshObject mesh; - if (LoadGLTF("./Media/Meshes/Skybox_Separate.gltf", 0, &mesh)) - { - const auto* pSkyboxShader = m_ShaderManager->GetShader("Skybox"); - assert(pSkyboxShader); - auto material = m_MaterialManager->CreateMaterial(*GetVulkan(), *pSkyboxShader, NUM_VULKAN_BUFFERS, - [this](const std::string& texName) -> MaterialPass::tPerFrameTexInfo { - if (texName == "Environment") - { - return { m_TextureManager->GetTexture("Environment") }; - } - else { - assert(0); - return {}; - } - }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer { - if (bufferName == "Vert") { - return { m_SkyboxVertUniform[RP_GBUFFER][0].buf.GetVkBuffer() }; - } - else { - assert(0); - return {}; - } - }); - uint32_t skyboxRenderPassBits = (1 << RP_LIGHT);// | (1 << RP_REFLECT); - m_SkyboxDrawable = std::make_unique(*GetVulkan(), std::move(material)); - m_SkyboxDrawable->Init(m_RenderPass, sRenderPassNames, skyboxRenderPassBits, std::move(mesh)); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitSceneData() -//----------------------------------------------------------------------------- -{ - // Moved to CreateRayInputs()! - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitHdr() -//----------------------------------------------------------------------------- -{ - // Set the color profile - VkHdrMetadataEXT AuthoringProfile = {}; - AuthoringProfile.sType = VK_STRUCTURE_TYPE_HDR_METADATA_EXT; - AuthoringProfile.displayPrimaryRed.x = 0.680f; - AuthoringProfile.displayPrimaryRed.y = 0.320f; - AuthoringProfile.displayPrimaryGreen.x = 0.265f; - AuthoringProfile.displayPrimaryGreen.y = 0.690f; - AuthoringProfile.displayPrimaryBlue.x = 0.150f; - AuthoringProfile.displayPrimaryBlue.y = 0.060f; - AuthoringProfile.whitePoint.x = 0.3127f; - AuthoringProfile.whitePoint.y = 0.3290f; - AuthoringProfile.maxLuminance = 80.0f;// 1000.f; - AuthoringProfile.minLuminance = 0.001f; - AuthoringProfile.maxContentLightLevel = 2000.f; - AuthoringProfile.maxFrameAverageLightLevel = 1000.f; - return GetVulkan()->SetSwapchainHrdMetadata(AuthoringProfile); -} - -//----------------------------------------------------------------------------- -bool Application::InitGui(uintptr_t windowHandle) -//----------------------------------------------------------------------------- -{ - // Gui - assert(m_RenderPass[RP_HUD] != VK_NULL_HANDLE); - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPass[RP_HUD]); - if (!m_Gui->Initialize(windowHandle, m_HudRT[0].m_Width, m_HudRT[0].m_Height)) - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateGui() -//----------------------------------------------------------------------------- -{ - if (gRenderHud && m_Gui) - { - // Update Gui - m_Gui->Update(); - - // Trying to decrease alpha on ImGui window but not working :9 - ImGui::SetNextWindowBgAlpha(1.0f); - ImGui::GetStyle().Alpha = 1.0f; - - // Begin our window. - static bool settingsOpen = true; - ImGui::SetNextWindowSize(ImVec2((gRenderWidth * 3.0f) / 4.0f, 500.f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2(gRenderWidth / 8.0f, gRenderHeight / 2.0f), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Settings", &settingsOpen, (ImGuiWindowFlags)0)) - { - // if (ImGui::InputFloat3("Light Direction", &gLightDir.x, "%.1f")) - // { - // uint32_t uiDebug = 5150; - // } - - // if (ImGui::InputFloat4("Light Color", &gLightColorB.x, "%.1f")) - // { - // uint32_t uiDebug = 5150; - // } - - if (ImGui::InputInt("Max Bounces", (int *)&gSphereRayBounces)) - { - uint32_t uiDebug = 5150; - } - - if (ImGui::SliderFloat("Blend Amount", &gRayBlendAmount, 0.0f, 1.0f)) - { - uint32_t uiDebug = 5150; - } - - // if (ImGui::InputFloat("Blend Amount", &gRayBlendAmount)) - // { - // uint32_t uiDebug = 5150; - // } - - } - ImGui::End(); - - if (ImGui::Begin("FPS", (bool*)nullptr, ImGuiWindowFlags_NoTitleBar)) - { - int offset = 0; - ImGui::Text("FPS: %.1f", m_CurrentFPS); - } - ImGui::End(); - - return; - } -} - -//----------------------------------------------------------------------------- -void Application::UpdateCamera(float ElapsedTime) -//----------------------------------------------------------------------------- -{ -#ifdef OS_WINDOWS - if (guiAPressed) - m_SphereCamera.m_Yaw += CAMERA_ROTATE_SPEED * ElapsedTime; - if (guiDPressed) - m_SphereCamera.m_Yaw -= CAMERA_ROTATE_SPEED * ElapsedTime; - - if (!guiShiftPressed) - { - if (guiWPressed) - m_SphereCamera.m_Distance -= CAMERA_ZOOM_SPEED * ElapsedTime; - if (guiSPressed) - m_SphereCamera.m_Distance += CAMERA_ZOOM_SPEED * ElapsedTime; - } - else - { - if (guiWPressed) - m_SphereCamera.m_Pitch -= CAMERA_ROTATE_SPEED * ElapsedTime; - if (guiSPressed) - m_SphereCamera.m_Pitch += CAMERA_ROTATE_SPEED * ElapsedTime; - } -#endif // OS_WINDOWS - - m_SphereCamera.Tick(ElapsedTime); - -} - - -//----------------------------------------------------------------------------- -void Application::UpdateLighting(float ElapsedTime) -//----------------------------------------------------------------------------- -{ - if( m_sceneRayTracable ) - { - if( m_sceneRayTracableCulled ) - { - UpdateSceneRTCulled( *m_sceneRayTracableCulled ); - } - } -} - - -//----------------------------------------------------------------------------- -void Application::UpdateSceneRTCulled( SceneRTCulled& sceneRTCulled ) -//----------------------------------------------------------------------------- -{ - glm::vec3 lightPosition = glm::vec3(-208.f, 422.6f, 464.9f); // Not really used - BBoxTest lightBoundingBox( lightPosition, glm::vec3( 100.0f, 100.0f, 100.0f) ); // Not really used - ViewFrustum viewFrustum(m_SphereCamera.ProjectionMatrix(), m_SphereCamera.ViewMatrix()); - FrustumTest viewFrustumTest( viewFrustum ); - - sceneRTCulled.SetForceRegenerateAccelerationStructure( false ); - - // Can NOT do culling because it breaks the GI lighting ray trace - if( true ) - { - // Update the ray tracing top level Acceleration Structure without culling (add everything to the AS). - sceneRTCulled.Update( *m_sceneRayTracable, [&lightBoundingBox, &viewFrustumTest]( const glm::vec4& center, const glm::vec4& halfSize ) { - return OctreeBase::eQueryResult::Inside; - } ); - } -} - -//----------------------------------------------------------------------------- -bool Application::ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat) -//----------------------------------------------------------------------------- -{ - if (!GetVulkan()->ChangeSurfaceFormat(newSurfaceFormat)) - { - return false; - } - - // We need to modify the blit render pass (the only one that touches the output framebuffer). - // RenderPass needs to be compatible with the framebuffer's format. - // m_PassCmdBuffer[RP_BLIT] is good, gets rebuilt every frame - - for (auto& blitCmdBuffer : m_BlitCmdBuffer) - { - if (!blitCmdBuffer.Reset()) - return false; - } - - vkDestroyRenderPass(GetVulkan()->m_VulkanDevice, m_RenderPass[RP_BLIT], nullptr); - m_RenderPass[RP_BLIT] = VK_NULL_HANDLE; - - auto& PassSetup = m_PassSetup[RP_BLIT]; - PassSetup.ColorFormats = { GetVulkan()->m_SurfaceFormat }; - PassSetup.DepthFormat = { GetVulkan()->m_SwapchainDepth.format }; - - if (!GetVulkan()->CreateRenderPass({ PassSetup.ColorFormats }, - PassSetup.DepthFormat, - VK_SAMPLE_COUNT_1_BIT, - PassSetup.ColorInputUsage, - PassSetup.ColorOutputUsage, - PassSetup.ClearDepthRenderPass, - PassSetup.DepthOutputUsage, - &m_RenderPass[RP_BLIT])) - return false; - - if (!m_BlitDrawable->ReInit( m_RenderPass[RP_BLIT], sRenderPassNames[RP_BLIT], nullptr, nullptr)) - { - return false; - } - - // Rebuild the m_BlitCmdBuffer - - VkViewport Viewport = {}; - Viewport.x = 0.0f; - Viewport.y = 0.0f; - Viewport.width = (float)PassSetup.TargetWidth; - Viewport.height = (float)PassSetup.TargetHeight; - Viewport.minDepth = 0.0f; - Viewport.maxDepth = 1.0f; - - VkRect2D Scissor = {}; - Scissor.offset.x = 0; - Scissor.offset.y = 0; - Scissor.extent.width = PassSetup.TargetWidth; - Scissor.extent.height = PassSetup.TargetHeight; - - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; ++WhichBuffer) - { - if (!m_BlitCmdBuffer[WhichBuffer].Begin(GetVulkan()->m_SwapchainBuffers[WhichBuffer].framebuffer, m_RenderPass[RP_BLIT], true/*swapchain renderpass*/)) - { - return false; - } - - vkCmdSetViewport(m_BlitCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Viewport); - vkCmdSetScissor(m_BlitCmdBuffer[WhichBuffer].m_VkCommandBuffer, 0, 1, &Scissor); - } - // Add the blit commands - AddDrawableToCmdBuffers(*m_BlitDrawable.get(), m_BlitCmdBuffer, 1, GetVulkan()->m_SwapchainImageCount); - // End the blit command buffer - for (uint32_t WhichBuffer = 0; WhichBuffer < GetVulkan()->m_SwapchainImageCount; WhichBuffer++) - { - if (!m_BlitCmdBuffer[WhichBuffer].End()) - { - return false; - } - } - - if (!gRunOnHLM && !InitHdr()) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -Application::RenderPassData& Application::BeginRenderPass(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - assert( WaitSemaphores.size() == WaitDstStageMasks.size() ); - - RenderPassData& renderPassData = m_RenderPassSubmitData[WhichBuffer].emplace_back( RenderPassData{ - m_PassCmdBuffer[WhichBuffer][WhichPass].m_Name, - WhichBuffer, - WhichPass, - m_PassCmdBuffer[WhichBuffer][WhichPass], - {WaitSemaphores.begin(), WaitSemaphores.end()}, - {WaitDstStageMasks.begin(), WaitDstStageMasks.end()}, - {SignalSemaphores.begin(), SignalSemaphores.end()}, - } ); - - const auto& passSetup = m_PassSetup[WhichPass]; - - // Reset the primary command buffer... - if (!renderPassData.CmdBuff.Reset()) - { - // What should be done here? - } - - // ... begin the primary command buffer ... - if (!renderPassData.CmdBuff.Begin()) - { - // What should be done here? - } - - if( m_RenderPass[WhichPass] != VK_NULL_HANDLE ) - { - VkFramebuffer Framebuffer; - switch( WhichPass ) - { - case RP_GBUFFER: - Framebuffer = m_GBufferRT[0].m_FrameBuffer; - break; - case RP_LIGHT: - Framebuffer = m_MainRT[0].m_FrameBuffer; - break; - case RP_HUD: - Framebuffer = m_HudRT[0].m_FrameBuffer; - break; - case RP_BLIT: - Framebuffer = pVulkan->m_SwapchainBuffers[WhichBuffer].framebuffer; - break; - default: - assert( 0 ); - } - - VkRect2D PassArea = {}; - PassArea.offset.x = 0; - PassArea.offset.y = 0; - PassArea.extent.width = passSetup.TargetWidth; - PassArea.extent.height = passSetup.TargetHeight; - - VkClearColorValue clearColor[1] = { passSetup.ClearColor[0], passSetup.ClearColor[1], passSetup.ClearColor[2], passSetup.ClearColor[3] }; - bool IsSwapChainRenderPass = WhichPass == RP_BLIT; - VkSubpassContents SubContents = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS; - - renderPassData.CmdBuff.BeginRenderPass( PassArea, 0.0f, 1.0f, clearColor, (uint32_t) passSetup.ColorFormats.size(), passSetup.DepthFormat != TextureFormat::UNDEFINED, m_RenderPass[WhichPass], IsSwapChainRenderPass, Framebuffer, SubContents ); - } - - return renderPassData; -} - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffers(const RenderPassData& renderPassData) -//----------------------------------------------------------------------------- -{ - std::vector SubCommandBuffers; - SubCommandBuffers.reserve(2); - - // Add sub commands to render list - - const uint32_t WhichBuffer = renderPassData.WhichBuffer; - const uint32_t WhichPass = renderPassData.WhichPass; - - if (WhichPass == RP_LIGHT) - { - // Light cmd buffer is added before the m_ObjectCmdBuffer as there may be some objects rendering to the RP_LIGHT pass after the initial light pass (eg emissives) - SubCommandBuffers.push_back(m_LightCmdBuffer[WhichBuffer].m_VkCommandBuffer); - m_TotalDrawCalls += m_LightCmdBuffer[WhichBuffer].m_NumDrawCalls; - m_TotalTriangles += m_LightCmdBuffer[WhichBuffer].m_NumTriangles; - } - - if (m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumDrawCalls) - { - SubCommandBuffers.push_back(m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_VkCommandBuffer); - m_TotalDrawCalls += m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumDrawCalls; - m_TotalTriangles += m_ObjectCmdBuffer[WhichBuffer][WhichPass].m_NumTriangles; - } - - if (WhichPass == RP_BLIT) - { - assert(SubCommandBuffers.empty()); // We currently don't handle m_ObjectCmdBuffer having commands to go in the blit pass, would need to recreate the cmd buffer when the renderpass changes (which will happen when the backbuffer surface format is switched) - SubCommandBuffers.push_back(m_BlitCmdBuffer[WhichBuffer].m_VkCommandBuffer); - m_TotalDrawCalls += m_BlitCmdBuffer[WhichBuffer].m_NumDrawCalls; - m_TotalTriangles += m_BlitCmdBuffer[WhichBuffer].m_NumTriangles; - } - - // Add all subcommands - AddPassCommandBuffers(renderPassData, SubCommandBuffers); -} - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffers(const RenderPassData& RenderPassData, std::span SubCommandBuffers) -//----------------------------------------------------------------------------- -{ - // Make sure there is something to execue - if (SubCommandBuffers.size() == 0) - { - // Technically, this may not be an error. For now, let it fall through and submit nothing - LOGE("Error! Being asked to add pass command buffers but nothing is in the list!"); - } - else - { - vkCmdExecuteCommands(RenderPassData.CmdBuff.m_VkCommandBuffer, (uint32_t)SubCommandBuffers.size(), SubCommandBuffers.data()); - } -} - -//----------------------------------------------------------------------------- -void Application::EndRenderPass(const RenderPassData& RenderPassData) -//----------------------------------------------------------------------------- -{ - // End the render pass. - if( m_RenderPass[RenderPassData.WhichPass] != VK_NULL_HANDLE ) - { - RenderPassData.CmdBuff.EndRenderPass(); - } - // Stop recording the command buffer. - RenderPassData.CmdBuff.End(); -} - -//----------------------------------------------------------------------------- -std::span Application::SubmitGameThreadWork(uint32_t WhichBuffer, VkFence CompletionFence) -//----------------------------------------------------------------------------- -{ - VkResult RetVal = VK_SUCCESS; - auto* const pVulkan = GetVulkan(); - - const auto& SubmitList = m_RenderPassSubmitData[WhichBuffer]; - uint32_t NumCmdBuffers = (uint32_t)SubmitList.size(); - -#ifdef ENABLE_PROFILING - PROFILE_ENTER(GROUP_VKFRAMEWORK, 0, "SubmitGameThreadWork: %d Buffers", NumCmdBuffers); -#endif // ENABLE_PROFILING - - const uint32_t NumPasses = (uint32_t) SubmitList.size(); - - for(uint32_t WhichPass = 0; WhichPass < NumPasses; ++WhichPass) - { - auto& OneSubmit = SubmitList[WhichPass]; - bool LastPass = (WhichPass == NumPasses - 1); - -#ifdef ENABLE_PROFILING - PROFILE_ENTER(GROUP_VKFRAMEWORK, 0, "Submit Cmd Buffer: %s", OneSubmit.Desc.c_str()); -#endif // ENABLE_PROFILING - - OneSubmit.CmdBuff.QueueSubmitT( OneSubmit.WaitSemaphores, OneSubmit.WaitDstStageMasks, OneSubmit.SignalSemaphores, LastPass ? CompletionFence : VK_NULL_HANDLE ); - -#ifdef ENABLE_PROFILING - PROFILE_EXIT(GROUP_VKFRAMEWORK); // "Submit Cmd Buffer: %s" -#endif // ENABLE_PROFILING - } - -#ifdef ENABLE_PROFILING - PROFILE_EXIT(GROUP_VKFRAMEWORK); // "SubmitGameThreadWork: %d Buffers" -#endif // ENABLE_PROFILING - - return !m_RenderPassSubmitData[WhichBuffer].empty() ? std::span{m_RenderPassSubmitData[WhichBuffer].rbegin()->SignalSemaphores} : std::span{}; -} diff --git a/samples/rayReflections/code/main/rayReflections.hpp b/samples/rayReflections/code/main/rayReflections.hpp deleted file mode 100644 index 879cdeb..0000000 --- a/samples/rayReflections/code/main/rayReflections.hpp +++ /dev/null @@ -1,312 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once -#include "main/applicationHelperBase.hpp" -#include "materials.hpp" -#include "camera/camera.hpp" -#include "memory/vulkan/uniform.hpp" -#include "system/glm_common.hpp" -#include "system/Worker.h" -#include "vulkan/MeshObject.h" -#include "vulkan/commandBuffer.hpp" -#include "vulkan/TextureFuncts.h" -#include "vulkanRT/vulkanRT.hpp" - -#include -#include -#include -#include - -#include "SphereCamera.h" - -// Forward declarations -class ShaderManager; -class MaterialManager; -//class TextureVulkan; -class MaterialPass; -class Material; -class Computable; -class Drawable; -class Traceable; -class Gui; -class SceneRTCullable; -class SceneRTCulled; -template class BufferT; -using BufferVulkan = BufferT; - -//class VertexBufferObject; - -enum RENDER_PASS -{ - RP_GBUFFER = 0, - RP_RAYTRACE, - RP_LIGHT, - RP_HUD, - RP_BLIT, - NUM_RENDER_PASSES -}; - -// The vertex data passed to ray trace shader -struct RayVertex -{ - // Here is validation error if no padding: - // Vulkan Debug : (Error)DEVICE = > Validation: Validation Error : [UNASSIGNED - CoreValidation - Shader - InconsistentSpirv] Object 0 : - // handle = 0x20b2557b2a8, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x6bbb14 | SPIR - V module not valid: Structure id 322 decorated - // as BufferBlock for variable in Uniform storage class must follow relaxed storage buffer layout rules : member 0 contains an array with - // stride 72 not satisfying alignment to 16 - - // Here is the validation error if elements are on odd boundaries: - // Vulkan Debug : (Error)DEVICE = > Validation: Validation Error : [UNASSIGNED - CoreValidation - Shader - InconsistentSpirv] Object 0 : - // handle = 0x1fa92f98f48, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x6bbb14 | SPIR - V module not valid: Structure id 320 decorated - // as BufferBlock for variable in Uniform storage class must follow relaxed storage buffer layout rules : member 1 is an improperly - // straddling vector at offset 12 - // VertData = OpTypeStruct v3float v3float v4float v2float v3float v3float v2float - - // Here is the validation error if an element is not 16 bytes: - // Vulkan Debug: (Error) DEVICE => Validation: Validation Error: [ UNASSIGNED-CoreValidation-Shader-InconsistentSpirv ] Object 0: - // handle = 0x1df48f285f8, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x6bbb14 | SPIR-V module not valid: Structure id 320 decorated - // as BufferBlock for variable in Uniform storage class must follow relaxed storage buffer layout rules: member 4 is an improperly - // straddling vector at offset 56 - // VertData = OpTypeStruct % v4float % v4float % v4float % v2float % v4float % v4float % v2float - float position[4]; - float normal[4]; - float uv0[4]; -}; - -typedef std::vector RayObject; - -typedef std::vector RayObjectIndices; - -// Class -class Application : public ApplicationHelperBase -{ -public: - Application(); - ~Application() override; - - // Override ApplicationHelperBase - int PreInitializeSelectSurfaceFormat(std::span) override; - void PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) override; - bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; - void Destroy() override; - void Render(float fltDiffTime) override; - - void KeyDownEvent(uint32_t key) override; - void KeyRepeatEvent(uint32_t key) override; - void KeyUpEvent(uint32_t key) override; - void TouchDownEvent(int iPointerID, float xPos, float yPos) override; - void TouchMoveEvent(int iPointerID, float xPos, float yPos) override; - void TouchUpEvent(int iPointerID, float xPos, float yPos) override; - - // Application - bool LoadMeshObjects(); - bool LoadRayTracingObjects(); - bool LoadTextures(); - bool CreateRayInputs(); - bool CreateRenderTargets(); - bool InitShadowMap(); - bool InitLighting(); - bool LoadConstantShaders(); - bool LoadDynamicShaders(); - bool LoadDynamicRayTracingObjects(); - bool InitUniforms(); - glm::mat4 CreateRandomRotation(); - bool UpdateUniforms(uint32_t WhichBuffer); - bool InitMaterials(); - bool InitCommandBuffers(); - bool InitAllRenderPasses(); - bool InitDrawables(); - bool InitSceneData(); - bool InitHdr(); - bool InitGui(uintptr_t windowHandle); - - bool BuildCmdBuffers(); - bool InitLocalSemaphores(); - - const char* GetPassName(uint32_t WhichPass); - - void UpdateGui(); - void UpdateCamera(float ElapsedTime); - void UpdateLighting(float ElapsedTime); - void UpdateShadowMap(float ElapsedTime); - void UpdateSceneRTCulled( SceneRTCulled& sceneRTCulled ); - - bool ChangeSurfaceFormat(SurfaceFormat newSurfaceFormat); - - struct RenderPassData - { - static constexpr uint32_t cnMaxPassSemaphores = 2; - std::string Desc; - uint32_t WhichBuffer; - RENDER_PASS WhichPass; - Wrap_VkCommandBuffer& CmdBuff; - std::vector WaitSemaphores{}; - std::vector WaitDstStageMasks{}; - std::vector SignalSemaphores{}; - }; - std::vector m_RenderPassSubmitData[NUM_VULKAN_BUFFERS]; - - RenderPassData& BeginRenderPass(RENDER_PASS WhichPass, uint32_t WhichBuffer, const std::span WaitSemaphores, const std::span WaitDstStageMasks, const std::span SignalSemaphores); - void AddPassCommandBuffers(const RenderPassData& RenderPassData); - void AddPassCommandBuffers(const RenderPassData& RenderPassData, std::span SubCommandBuffers); - void EndRenderPass(const RenderPassData& RenderPassData); - // Submits the queued up m_RenderPassSubmitData, Returns semaphore(s) signalled by the final pass. - std::span SubmitGameThreadWork( uint32_t WhichBuffer, VkFence CompletionFence ); - -protected: - // Threading data - struct GameThreadInputParam - { - // Input - Vulkan::BufferIndexAndFence CurrentBuffer; - float ElapsedTime; - }; - - struct GameThreadOutputParam - { - // Output - int WhichFrame = -1; - int SwapchainPresentIndx; - VkFence Fence; - } m_CompletedThreadOutput; - - CWorker m_GameThreadWorker; - - // Game thread entry point. - void RunGameThread( const GameThreadInputParam& rThreadParam, GameThreadOutputParam& rThreadOutput); - -private: - // Metrics - uint32_t m_TotalDrawCalls; - uint32_t m_TotalTriangles; - - // Requested surface format (for changing formats) - SurfaceFormat m_RequestedSurfaceFormat; - // sRGB output (done in blit shader) on/off - bool m_bEncodeSRGB; - - // Drawables - std::unique_ptr m_SkyboxDrawable; - std::vector m_SceneDrawables; - std::unique_ptr m_LightDrawable; - std::unique_ptr m_BlitDrawable; - - // Ray tracing - VulkanRT m_vulkanRT; - std::unique_ptr m_sceneRayTracable; - std::unique_ptr m_sceneRayTracableCulled; - - // Textures - std::map m_loadedTextures; - TextureVulkan m_TexWhite; - TextureVulkan m_DefaultNormal; - // TextureVulkan m_TexBunnySplash; - - // Materials - - // Light Stuff - glm::vec4 m_LightColor; - - // ********************** - // The Objects - // ********************** - ObjectVertUB m_ObjectVertUniformData; - UniformT m_ObjectVertUniform[NUM_RENDER_PASSES][NUM_VULKAN_BUFFERS]; - - ObjectFragUB m_ObjectFragUniformData; - UniformT m_SphereFragUniform[NUM_RENDER_PASSES][NUM_VULKAN_BUFFERS]; - UniformT m_FloorFragUniform[NUM_RENDER_PASSES][NUM_VULKAN_BUFFERS]; - - Wrap_VkCommandBuffer m_ObjectCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; - - // ********************** - // The Skybox - // ********************** - float m_SkyboxScale; - UniformT m_SkyboxVertUniform[NUM_RENDER_PASSES][NUM_VULKAN_BUFFERS]; - SkyboxVertUB m_SkyboxVertUniformData; - - // ********************** - // Deferred Lighting Fullscreen pass - // ********************** - Wrap_VkCommandBuffer m_LightCmdBuffer[NUM_VULKAN_BUFFERS]; - LightFragCtrl m_LightFragUniformData; - UniformT m_LightFragUniform[NUM_VULKAN_BUFFERS]; - - // ********************** - // Post/Blit - // ********************** - BlitFragCtrl m_BlitFragUniformData; - UniformT m_BlitFragUniform[NUM_VULKAN_BUFFERS]; - Wrap_VkCommandBuffer m_BlitCmdBuffer[NUM_VULKAN_BUFFERS]; - - // ********************** - // Pass Stuff - // ********************** - struct PassSetup { - - std::vector ColorFormats; - TextureFormat DepthFormat; - RenderPassInputUsage ColorInputUsage; - bool ClearDepthRenderPass; - RenderPassOutputUsage ColorOutputUsage; - RenderPassOutputUsage DepthOutputUsage; - glm::vec4 ClearColor; - uint32_t TargetWidth; - uint32_t TargetHeight; - - } m_PassSetup[NUM_RENDER_PASSES]; - - Wrap_VkCommandBuffer m_PassCmdBuffer[NUM_VULKAN_BUFFERS][NUM_RENDER_PASSES]; - std::array m_PassCompleteSemaphore; - - // Don't actually need a render pass for each Vulkan Buffer, just one per pass - // These are NOT the same vkrenderpass's as in the CRenderTargetArray's but should be compatible with them (are allowed dfferent load/clear parameters on the atachments) - std::array m_RenderPass; - - // Render targets for each pass - CRenderTargetArray<1> m_GBufferRT; - CRenderTargetArray<1> m_MainRT; - CRenderTargetArray<1> m_HudRT; - - // Scene information - std::vector m_SceneDataArray; - std::vector m_SceneDataInternalArray; - UniformVulkan m_SceneDataUniform; - - // Texture array passed to shader - // std::vector m_SceneTextures; - std::vector m_SceneTextures; - - // Mesh objects passed to the shader - std::vector m_RayMeshArray; - std::vector m_RayMeshBuffers; - std::vector m_RayMeshVkBuffers; - - std::vector m_RayIndicesArray; - std::vector m_RayIndiceBuffers; - std::vector m_RayIndiceVkBuffers; - - // ********************** - // Misc. - // ********************** - - // Change over time parameters - uint32_t m_LastUpdateTime; - bool m_LightChangePaused; - - uint32_t m_LightDirUpdateTime; - bool m_LightDirReverse; - - uint32_t m_LightColorUpdateTime; - bool m_LightColorReverse; - - // Our local camera - CSphereCamera m_SphereCamera; - -}; diff --git a/samples/rayReflections/img/screenshot.PNG b/samples/rayReflections/img/screenshot.PNG deleted file mode 100644 index be379a6..0000000 Binary files a/samples/rayReflections/img/screenshot.PNG and /dev/null differ diff --git a/samples/rayReflections/project/android/AndroidManifest.xml b/samples/rayReflections/project/android/AndroidManifest.xml deleted file mode 100644 index 2ae8581..0000000 --- a/samples/rayReflections/project/android/AndroidManifest.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/rayReflections/project/android/res/values/strings.xml b/samples/rayReflections/project/android/res/values/strings.xml deleted file mode 100644 index aeef08a..0000000 --- a/samples/rayReflections/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Ray Reflections - diff --git a/samples/rayReflections/project/android/res/values/styles.xml b/samples/rayReflections/project/android/res/values/styles.xml deleted file mode 100644 index f591923..0000000 --- a/samples/rayReflections/project/android/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/samples/rayReflections/shaders/Blit.frag b/samples/rayReflections/shaders/Blit.frag deleted file mode 100644 index 04d9513..0000000 --- a/samples/rayReflections/shaders/Blit.frag +++ /dev/null @@ -1,133 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_FRAG_UBO_LOCATION 0 -#define SHADER_DIFFUSE_TEXTURE_LOC 1 -#define SHADER_BLOOM_TEXTURE_LOC 2 -#define SHADER_OVERLAY_TEXTURE_LOC 3 - -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_BLOOM_TEXTURE_LOC) uniform sampler2D u_BloomTex; -layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec4 v_VertColor; - -// Uniforms -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - // Mix amount of Bloom - float Bloom; - // Mix amount of main scene - float Diffuse; - // Enable(1) conversion of color to sRGB. - int sRGB; -} FragCB; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); - - // Multiply by vertex color. - // DiffuseColor.xyzw *= v_VertColor.xyzw; - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // Simple brightness - // DiffuseColor.rgb *= FragCB.Diffuse; - - // Get the Bloom value - // vec4 BloomColor = texture( u_BloomTex, LocalTexCoord.xy ) * FragCB.Bloom; - - // ******************************** - // Alpha Blending - // ******************************** - // FragColor.rgb = (DiffuseColor.rgb + BloomColor.rgb); - FragColor.rgb = (DiffuseColor.rgb); - FragColor.a = 1.0; - - // Color mapping - // FragColor.rgb = ACESFilmic(FragColor.rgb); - - // Get the Overlay value - vec4 OverlayColor = texture( u_OverlayTex, LocalTexCoord.xy ); - FragColor.rgb = FragColor.rgb*(1.0-OverlayColor.a) + OverlayColor.rgb; - - // ******************************** - // sRGB conversion (if speed was a factor we would probably hardcode and have 2 blit shaders) - // ******************************** - if (FragCB.sRGB == 1) - { - FragColor.r = ToSCRGB(FragColor.r); - FragColor.g = ToSCRGB(FragColor.g); - FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/rayReflections/shaders/Light.frag b/samples/rayReflections/shaders/Light.frag deleted file mode 100644 index 295c637..0000000 --- a/samples/rayReflections/shaders/Light.frag +++ /dev/null @@ -1,721 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 460 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -#extension GL_EXT_ray_query : enable -#extension GL_EXT_nonuniform_qualifier : enable -#extension GL_EXT_scalar_block_layout : enable - -// Can kill these after final shader -#define PHI_CONST 0.618034 -#define PI_CONST 3.14159265359 -#define PI_MUL_2 6.28318530718 - -//********************************************************* -// Structures -//********************************************************* -//----------------------------------------------------------------------------- -struct SceneData -//----------------------------------------------------------------------------- -{ - // WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! - // mat4 MUST be on 16 byte boundary or the validation layer complains - // Therefore, can't start with an "int" value in this structure without padding - - // The transform for this instance - mat4 Transform; - - // The transform used for normals (Inverse Transpose) - // See http://www.pbr-book.org/3ed-2018/Geometry_and_Transformations/Applying_Transformations.html#Normals - mat4 TransformIT; - - // The index into the list of meshes. - int MeshIndex; - - // Material information - int DiffuseTex; // -1 means no entry - int NormalTex; // -1 means no entry - - int Padding; // So lines up on 16 -}; - -//----------------------------------------------------------------------------- -struct VertData -//----------------------------------------------------------------------------- -{ - vec4 position; - vec4 normal; - vec4 uv0; -}; - -//----------------------------------------------------------------------------- -struct BounceInfo -//----------------------------------------------------------------------------- -{ - vec4 WorldPos; - vec4 WorldDir; -}; - -//----------------------------------------------------------------------------- -struct RayPayload -//----------------------------------------------------------------------------- -{ - // Flag for hit or miss - uint IsHit; - - // Geometry instance ids - int PrimitiveIndex; - int InstanceID; - int InstCustIndex; - // in int gl_GeometryIndexEXT; - - // World space parameters - vec3 RayOrigin; - vec3 RayDirection; - - // Ray hit info - float HitDist; - uint IsFrontFacing; - - // Barycentric Coordinates - vec3 BaryCoords; -}; - -//----------------------------------------------------------------------------- -struct PixelPayload -//----------------------------------------------------------------------------- -{ - // TODO: Set this up for Hit/Miss shader - float HitDist; - - // Vertex Data - vec4 WorldPos; - vec4 WorldNorm; - vec4 Tangent; - vec4 Bitan; - vec4 Color; - - // Bump version of normal - vec4 BumpNorm; - - // Texture Data - vec4 DiffuseTexValue; - vec4 NormalTexValue; - - // Results of Ray Trace - // vec4 RayTraceColor; - - // Misc Data - // mat4 Transform; -}; - -//********************************************************* -// Uniform/Texture Bindngs -//********************************************************* -// These binding locations are set by order they are defined in <>.json - -layout(std140, binding = 0, set = 0) uniform FragConstantsBuff -{ - // Needed to convert from Depth to World Position - mat4 ProjectionInv; - mat4 ViewInv; - - // To apply a tint to the final results - vec4 ShaderTint; - - // Standard lighting - vec4 EyePos; - vec4 LightDir; // XYZ + Power/Exponent - vec4 LightColor; // RBG + Power/Exponent - - // X: Mip Level - // Y: Reflect Amount - // Z: Reflect Fresnel Min - // W: Reflect Fresnel Max - vec4 ReflectParams; - - // X: Mip Level - // Y: IBL Amount - // Z: Not Used - // W: Not Used - vec4 IrradianceParams; - - // X: Ray Min Distance - // Y: Ray Max Distance - // Z: Max Ray Bounces - // W: Ray Blend Amount (Mirror Amount) - vec4 RayParam01; - - // X: Shadow Amount - // Y: Minumum Normal Dot Light - // Z: Not Used - // W: Not Used - vec4 LightParams; - -} FragCB; - -#define SHADER_TINT FragCB.ShaderTint - -#define EYE_POS FragCB.EyePos -#define LIGHT_DIR FragCB.LightDir -#define LIGHT_COLOR FragCB.LightColor.rgb -#define LIGHT_POWER FragCB.LightColor.w - -#define REFLECT_MIP_LEVEL FragCB.ReflectParams.x -#define REFLECT_AMOUNT FragCB.ReflectParams.y -#define REFLECT_FRESNEL_MIN FragCB.ReflectParams.z -#define REFLECT_FRESNEL_MAX FragCB.ReflectParams.w - -#define IRRADIANCE_MIP_LEVEL FragCB.IrradianceParams.x -#define IRRADIANCE_AMOUNT FragCB.IrradianceParams.y - -#define RAY_MIN_DIST FragCB.RayParam01.x -#define RAY_MAX_DIST FragCB.RayParam01.y -#define RAY_MAX_BOUNCES FragCB.RayParam01.z -#define RAY_BLEND_AMOUNT FragCB.RayParam01.w - -#define SHADOW_AMOUNT FragCB.LightParams.x -#define MIN_NORM_DOT_LIGHT FragCB.LightParams.y - - -layout (binding = 1, set = 0) uniform accelerationStructureEXT rayTraceAS; //set=0 required (compiler issue) https://github.com/KhronosGroup/glslang/issues/2247 -layout (binding = 2, set = 0) uniform sampler2D TexArray[]; -layout (binding = 3, set = 0, scalar) buffer SceneDataDef { SceneData Data[]; } SceneDataCB; -layout (binding = 4, set = 0, scalar) buffer VertDataDef { VertData Data[]; } VertDataCB[]; -layout (binding = 5, set = 0) buffer IndiceDataDef { uint Data[]; } IndiceDataCB[]; -layout (binding = 6, set = 0) uniform sampler2D u_AlbedoTex; -layout (binding = 7, set = 0) uniform sampler2D u_NormalTex; -layout (binding = 8, set = 0) uniform sampler2D u_MatPropsTex; -layout (binding = 9, set = 0) uniform samplerCube u_EnvironmentTex; -layout (binding = 10, set = 0) uniform samplerCube u_IrradianceTex; - -//********************************************************* -// Varying's -//********************************************************* -layout (location = 0) in vec2 v_TexCoord; - -//********************************************************* -// Finally, the output color -//********************************************************* -layout (location = 0) out vec4 FragColor; - -//********************************************************* -// Functions -//********************************************************* - -//----------------------------------------------------------------------------- -vec3 ScreenToWorld(vec2 ScreenCoord/*0-1 range*/, float Depth/*0-1*/) -//----------------------------------------------------------------------------- -{ - vec4 ClipSpacePosition = vec4((ScreenCoord * 2.0) - vec2(1.0), Depth, 1.0); - ClipSpacePosition.y = -ClipSpacePosition.y; - vec4 ViewSpacePosition = FragCB.ProjectionInv * ClipSpacePosition; - - // Perspective division - ViewSpacePosition /= vec4(ViewSpacePosition.w); - - vec4 WorldSpacePosition = FragCB.ViewInv * ViewSpacePosition; - return WorldSpacePosition.xyz; -} - -//----------------------------------------------------------------------------- -float InShadow(vec3 RayStartPos, vec3 RayDirection, vec3 SurfaceNormal) -//----------------------------------------------------------------------------- -{ - // TODO: Make this bias a configurable parameter - vec3 LocalStart = RayStartPos + 0.01 * SurfaceNormal; - - // Set up the ray query - rayQueryEXT rayQuery; - uint RayFlags = gl_RayFlagsTerminateOnFirstHitEXT; - rayQueryInitializeEXT(rayQuery, rayTraceAS, RayFlags, 0xFF, LocalStart, RAY_MIN_DIST, RayDirection, 100.0 * RAY_MAX_DIST); - - // Traverse until done - while(rayQueryProceedEXT(rayQuery)) - { - // Spec does this in the sample: - // if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionTriangleEXT) - // { - // rayQueryConfirmIntersectionEXT(rayQuery); - // } - } - - // What are the results of the traversal? - float RetVal = 0.0; - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) - { - // Didn't hit anything - RetVal = 0.0; - } - else if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) - { - // Hit something. - // Lowering this value from 1.0 allows "ambient" in the shadow areas - RetVal = SHADOW_AMOUNT; - } - - return RetVal; -} - -//----------------------------------------------------------------------------- -vec3 GetShadowMultiplier(vec3 RayStartPos, vec3 RayDirection, vec3 SurfaceNormal) -//----------------------------------------------------------------------------- -{ - // TODO: Make this bias a configurable parameter - vec3 LocalStart = RayStartPos + 0.01 * SurfaceNormal; - - // Set up the ray query - rayQueryEXT rayQuery; - uint RayFlags = gl_RayFlagsTerminateOnFirstHitEXT; - rayQueryInitializeEXT(rayQuery, rayTraceAS, RayFlags, 0xFF, LocalStart, RAY_MIN_DIST, RayDirection, 100.0 * RAY_MAX_DIST); - - // Traverse until done - while(rayQueryProceedEXT(rayQuery)) - { - // Spec does this in the sample: - // if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionTriangleEXT) - // { - // rayQueryConfirmIntersectionEXT(rayQuery); - // } - } - - // What are the results of the traversal? - vec3 RetVal = vec3(1.0, 1.0, 1.0); - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) - { - // Didn't hit anything - RetVal = vec3(1.0, 1.0, 1.0); - } - else if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) - { - // Hit something. - // Lowering this value from 1.0 allows "ambient" in the shadow areas - RetVal = vec3(SHADOW_AMOUNT, SHADOW_AMOUNT, SHADOW_AMOUNT); - } - - return RetVal; -} - -//----------------------------------------------------------------------------- -vec4 ShadePixel(PixelPayload PixelData) -//----------------------------------------------------------------------------- -{ - vec4 FragColor = vec4(0.0, 1.0, 0.0, 1.0); - - vec4 DiffuseColor = PixelData.DiffuseTexValue; - DiffuseColor.xyzw *= SHADER_TINT; - - vec3 WorldNorm = PixelData.WorldNorm.xyz; - vec3 WorldPos = PixelData.WorldPos.xyz; - - // ******************************** - // Shadows - // ******************************** - // Go from pixel position towards the light (hence negative light direction) - vec3 ShadowAmount = GetShadowMultiplier(WorldPos.xyz, -LIGHT_DIR.xyz, WorldNorm.xyz); - - // ******************************** - // Base Lighting - // ******************************** - float DiffuseAmount = max(MIN_NORM_DOT_LIGHT, dot(WorldNorm.xyz, -LIGHT_DIR.xyz)); - vec3 LightTotal = DiffuseAmount * DiffuseColor.xyz * LIGHT_COLOR.xyz; - - // For specular, we need Half vector - vec3 EyeDir = normalize(EYE_POS.xyz - WorldPos.xyz); - vec3 Half = normalize(EyeDir - LIGHT_DIR.xyz); - float Specular = max(0.0, dot(WorldNorm.xyz, Half)); - Specular = pow(Specular, LIGHT_POWER); - - // Adjust specular by shadows (Not using 1.0 as edge in case numerical precision issues) - // Specular *= (1.0 - step(ShadowAmount.w, 0.95)); - - // ******************************** - // Reflection - // ******************************** - vec3 N = WorldNorm.xyz; - vec3 V = EyeDir.xyz; - vec3 ReflectionVec = normalize( reflect(-V, N) ); - - float ReflectionFresnel = 0.0; - // float ReflectionFresnel = clamp(((clamp((1.0f - dot(N, V)), 0.0, 1.0) - REFLECT_FRESNEL_MIN)/(REFLECT_FRESNEL_MAX - REFLECT_FRESNEL_MIN)), 0.0, 1.0); - ReflectionFresnel = clamp(1.0f - dot(N, V), 0.0, 1.0); - ReflectionFresnel = (ReflectionFresnel - REFLECT_FRESNEL_MIN)/(REFLECT_FRESNEL_MAX - REFLECT_FRESNEL_MIN); - ReflectionFresnel = clamp(ReflectionFresnel, 0.0, 1.0); - - vec4 EnvironmentColor = textureLod(u_EnvironmentTex, ReflectionVec.xyz, REFLECT_MIP_LEVEL); - - // ******************************** - // Image Based Lighting - // ******************************** - // N.z *= -1.0; - // vec4 IrradianceColor = textureLod(u_IrradianceTex, ReflectionVec.xyz, REFLECT_MIP_LEVEL); - // - // // IBL is added to amount already calculated from dynamic lights - // LightTotal += IRRADIANCE_AMOUNT * IrradianceColor.xyz; - - // ******************************** - // Start adding in the colors - // ******************************** - // Adjust by shadows - LightTotal.rgb *= ShadowAmount.xyz; - - vec3 FinalColor = REFLECT_AMOUNT * ReflectionFresnel * EnvironmentColor.rgb + - LightTotal + - Specular * LIGHT_COLOR; - - FragColor = vec4(FinalColor.rgb, 1.0); - - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // FragColor = vec4(PixelData.DiffuseTexValue.rgb, 1.0); - // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - - return FragColor; -} - -//----------------------------------------------------------------------------- -RayPayload CreateHitPayload(rayQueryEXT rayQuery) -//----------------------------------------------------------------------------- -{ - RayPayload PayloadData; - - const bool IsCommitted = true; - - // Read as much information as we can about the hit - // (https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GLSL_EXT_ray_tracing.txt) - - // Flag that this is a hit vector - PayloadData.IsHit = 1; - - // Geometry instance ids - PayloadData.PrimitiveIndex = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, IsCommitted); - PayloadData.InstanceID = rayQueryGetIntersectionInstanceIdEXT(rayQuery, IsCommitted); - PayloadData.InstCustIndex = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, IsCommitted); - // in int gl_GeometryIndexEXT; - - // World space parameters - PayloadData.RayOrigin = rayQueryGetWorldRayOriginEXT(rayQuery); - PayloadData.RayDirection = rayQueryGetWorldRayDirectionEXT(rayQuery); - - // Ray hit info - // const uint gl_HitKindFrontFacingTriangleEXT = 0xFEU; - // const uint gl_HitKindBackFacingTriangleEXT = 0xFFU; - PayloadData.HitDist = rayQueryGetIntersectionTEXT(rayQuery, IsCommitted); - PayloadData.IsFrontFacing = 1; - bool IsFrontFacing = rayQueryGetIntersectionFrontFaceEXT(rayQuery, IsCommitted); - if(!IsFrontFacing) - PayloadData.IsFrontFacing = 0; - - // Barycentric Coordinates - // Floating point barycentric coordinates of current intersection of ray. - // Three Barycentric coordinates are such that their sum is 1. - // This gives only two and expects us to calculate the third - vec2 TwoBaryCoords = rayQueryGetIntersectionBarycentricsEXT(rayQuery, IsCommitted); - PayloadData.BaryCoords = vec3(1.0 - TwoBaryCoords.x - TwoBaryCoords.y, TwoBaryCoords.x, TwoBaryCoords.y); - - return PayloadData; -} - -//----------------------------------------------------------------------------- -BounceInfo GetNextBounceInfo(RayPayload OnePayload) -//----------------------------------------------------------------------------- -{ - BounceInfo RetVal; - - PixelPayload PixelData; - - const bool IsCommitted = true; - - // What is the base mesh? - int MeshIndx = SceneDataCB.Data[OnePayload.InstCustIndex].MeshIndex; - - // Indices for the triangle that was hit - ivec3 Indices = ivec3( IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 0], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 1], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 2]); - - // Vertices for the triangle that was hit - VertData v0 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.x]; - VertData v1 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.y]; - VertData v2 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.z]; - - // Should have everything we need to fill in the payload - // Based on vertex data of triangle and Barycentric coordinates we can figure hit values - RetVal.WorldPos = (v0.position * OnePayload.BaryCoords.x) + (v1.position * OnePayload.BaryCoords.y) + (v2.position * OnePayload.BaryCoords.z); - RetVal.WorldPos = SceneDataCB.Data[OnePayload.InstCustIndex].Transform * RetVal.WorldPos; - - vec4 WorldNorm = (v0.normal * OnePayload.BaryCoords.x) + (v1.normal * OnePayload.BaryCoords.y) + (v2.normal * OnePayload.BaryCoords.z); - WorldNorm = vec4(WorldNorm.xyz, 0.0); - WorldNorm = SceneDataCB.Data[OnePayload.InstCustIndex].Transform * WorldNorm; - WorldNorm = normalize(WorldNorm); - - // The new bounce direction is the reflection of incoming around normal - vec3 ReflectionVec = normalize( reflect(OnePayload.RayDirection, WorldNorm.xyz) ); - RetVal.WorldDir = vec4(ReflectionVec, 0.0); - - // Adjust the starting position - // TODO: Make this bias a configurable parameter - RetVal.WorldPos.xyz = RetVal.WorldPos.xyz + 0.01 * WorldNorm.xyz; - - return RetVal; -} - - -//----------------------------------------------------------------------------- -vec4 GetRayHitNormal(RayPayload OnePayload) -//----------------------------------------------------------------------------- -{ - const bool IsCommitted = true; - - vec4 RetNorm = vec4(0.0, 0.0, 1.0, 1.0); - - // What is the base mesh? - int MeshIndx = SceneDataCB.Data[OnePayload.InstCustIndex].MeshIndex; - - // Indices for the triangle that was hit - ivec3 Indices = ivec3( IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 0], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 1], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 2]); - - // Vertices for the triangle that was hit - VertData v0 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.x]; - VertData v1 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.y]; - VertData v2 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.z]; - - // Based on vertex data of triangle and Barycentric coordinates we can figure hit values - - RetNorm = (v0.normal * OnePayload.BaryCoords.x) + (v1.normal * OnePayload.BaryCoords.y) + (v2.normal * OnePayload.BaryCoords.z); - RetNorm = vec4(RetNorm.xyz, 0.0); - RetNorm = SceneDataCB.Data[OnePayload.InstCustIndex].Transform * RetNorm; - RetNorm = normalize(RetNorm ); - - return RetNorm; -} - -//----------------------------------------------------------------------------- -vec4 ProcessRayHit(RayPayload OnePayload) -//----------------------------------------------------------------------------- -{ - PixelPayload PixelData; - - const bool IsCommitted = true; - - vec4 FragColor = vec4(1.0, 1.0, 1.0, 1.0); - - // What is the base mesh? - int MeshIndx = SceneDataCB.Data[OnePayload.InstCustIndex].MeshIndex; - - // Indices for the triangle that was hit - ivec3 Indices = ivec3( IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 0], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 1], - IndiceDataCB[nonuniformEXT(MeshIndx)].Data[3 * OnePayload.PrimitiveIndex + 2]); - - // Vertices for the triangle that was hit - VertData v0 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.x]; - VertData v1 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.y]; - VertData v2 = VertDataCB[nonuniformEXT(MeshIndx)].Data[Indices.z]; - - // Should have everything we need to fill in the payload - // Based on vertex data of triangle and Barycentric coordinates we can figure hit values - PixelData.WorldPos = (v0.position * OnePayload.BaryCoords.x) + (v1.position * OnePayload.BaryCoords.y) + (v2.position * OnePayload.BaryCoords.z); - PixelData.WorldPos = SceneDataCB.Data[OnePayload.InstCustIndex].Transform * PixelData.WorldPos; - - PixelData.WorldNorm = (v0.normal * OnePayload.BaryCoords.x) + (v1.normal * OnePayload.BaryCoords.y) + (v2.normal * OnePayload.BaryCoords.z); - PixelData.WorldNorm = vec4(PixelData.WorldNorm.xyz, 0.0); - // PixelData.WorldNorm = SceneDataCB.Data[OnePayload.InstCustIndex].TransformIT * PixelData.WorldNorm; - PixelData.WorldNorm = SceneDataCB.Data[OnePayload.InstCustIndex].Transform * PixelData.WorldNorm; - PixelData.WorldNorm = normalize(PixelData.WorldNorm ); - - // PixelData.Color = (v0.color * OnePayload.BaryCoords.x) + (v1.color * OnePayload.BaryCoords.y) + (v2.color * OnePayload.BaryCoords.z); - PixelData.Color = vec4(0.8, 0.8, 0.8, 1.0); - - vec2 UV = (v0.uv0.xy * OnePayload.BaryCoords.x) + (v1.uv0.xy * OnePayload.BaryCoords.y) + (v2.uv0.xy * OnePayload.BaryCoords.z); - - // What are the textures? - int DiffuseTex = SceneDataCB.Data[OnePayload.InstCustIndex].DiffuseTex; - int NormalTex = SceneDataCB.Data[OnePayload.InstCustIndex].NormalTex; - - // TODO: Should the default tex color value be passed in? - PixelData.DiffuseTexValue = vec4(0.8, 0.8, 0.8, 1.0); - if(DiffuseTex >= 0) - { - // Try getting the lowest LOD from the texture - // Later: Not lowest, but take about half - // int NumMipLevels = textureQueryLevels(TexArray[nonuniformEXT(DiffuseTex)]); - // PixelData.DiffuseTexValue = textureLod(TexArray[nonuniformEXT(DiffuseTex)], UV, NumMipLevels / 2); - - // Get whatever mip the driver gives us - PixelData.DiffuseTexValue = texture(TexArray[nonuniformEXT(DiffuseTex)], UV); - } - - // Default normal is flat bump map - PixelData.NormalTexValue = vec4(0.0, 0.0, 1.0, 1.0); - // if(NormalTex >= 0) - // { - // // Try getting the lowest LOD from the texture - // int NumMipLevels = textureQueryLevels(TexArray[nonuniformEXT(NormalTex)]); - // PixelData.NormalTexValue= textureLod(TexArray[nonuniformEXT(DiffuseTex)], UV, (NumMipLevels - 1)); - // - // // PixelData.NormalTexValue = texture(TexArray[nonuniformEXT(NormalTex)], UV); - // } - - // Turn off bump for now - PixelData.BumpNorm = PixelData.WorldNorm; - - // PixelData.Transform = SceneDataCB.Data[OnePayload.InstCustIndex].Transform; - // PixelData.TransformIT = SceneDataCB.Data[OnePayload.InstCustIndex].TransformIT; - - FragColor = ShadePixel(PixelData); - - return FragColor; -} - -//----------------------------------------------------------------------------- -vec4 GetRayColor(vec3 RayStartPos, vec3 RayDirection, vec3 SurfaceNormal, vec4 MaterialProps) -//----------------------------------------------------------------------------- -{ - // TODO: (1,1,1) will create a "white" based on the blend amount. - // (0,0,0) "looks" correct, but is it really? - vec4 RetVal = vec4(0.0, 0.0, 0.0, 1.0); - - // TODO: Make this bias a configurable parameter - vec3 LocalStart = RayStartPos + 0.01 * SurfaceNormal; - vec3 LocalDirection = RayDirection; - - BounceInfo NextBounce; - NextBounce.WorldPos = vec4(LocalStart, 0.0); - NextBounce.WorldDir = vec4(LocalDirection, 0.0); - - // Set up the ray query - rayQueryEXT rayQuery; - uint RayFlags = gl_RayFlagsNoneEXT; // NOT this =>gl_RayFlagsTerminateOnFirstHitEXT; - - int MatRayBounces = int(MaterialProps.x * 255.0); - int MaxBounces = min(MatRayBounces, int(RAY_MAX_BOUNCES)); - int WhichBounce = 0; - while(WhichBounce < MaxBounces) - { - // Bump my bounce count here (not at bottom) so logic below is correct - WhichBounce = WhichBounce + 1; - - rayQueryInitializeEXT(rayQuery, rayTraceAS, RayFlags, 0xFF, NextBounce.WorldPos.xyz, RAY_MIN_DIST, NextBounce.WorldDir.xyz, 100.0 * RAY_MAX_DIST); - - // Traverse until done - while(rayQueryProceedEXT(rayQuery)) - { - // Spec does this in the sample: - // if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionTriangleEXT) - // { - // rayQueryConfirmIntersectionEXT(rayQuery); - // } - } - - // What are the results of the traversal? - if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) - { - // Didn't hit anything. - // Return the skybox value - WhichBounce = int(RAY_MAX_BOUNCES); - vec4 EnvironmentColor = textureLod(u_EnvironmentTex, NextBounce.WorldDir.xyz, REFLECT_MIP_LEVEL); - RetVal = mix(RetVal, EnvironmentColor, RAY_BLEND_AMOUNT); - } - else if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT && - rayQueryGetIntersectionFrontFaceEXT(rayQuery, true)) - { - // Hit something. - RayPayload OnePayload = CreateHitPayload(rayQuery); - - if(WhichBounce < int(RAY_MAX_BOUNCES)) - { - // Need to keep bouncing - // TODO: This is wrong! Need to ACCUMULATE colors! Testing for now - NextBounce = GetNextBounceInfo(OnePayload); - } - else - { - // We always get the color value and accumulate it - // RetVal = ProcessRayHit(OnePayload); - } - - // What about shadows? Are they handled? - vec4 OneBounceColor = ProcessRayHit(OnePayload); - RetVal = mix(RetVal, OneBounceColor, RAY_BLEND_AMOUNT); - } - } // WhichBounce - - return RetVal; -} - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - PixelPayload PixelData; - - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Material Properties - // ******************************** - vec4 MaterialProps = texture( u_MatPropsTex, LocalTexCoord.xy ); - - - // ******************************** - // GBuffer Color, Normal, and Position - // ******************************** - vec4 DiffuseColor = texture( u_AlbedoTex, LocalTexCoord.xy ); - DiffuseColor.xyzw *= SHADER_TINT; - - vec4 NormalWithDepth = texture( u_NormalTex, LocalTexCoord.xy ); - vec3 WorldNorm = NormalWithDepth.xyz; - float Depth = 1.0 - NormalWithDepth.w; - - vec3 WorldPos = ScreenToWorld( LocalTexCoord, Depth ); - - // ******************************** - // Reflection - // ******************************** - vec3 EyeDir = normalize(EYE_POS.xyz - WorldPos.xyz); - vec3 N = WorldNorm.xyz; - vec3 V = EyeDir.xyz; - vec3 ReflectionVec = normalize( reflect(-V, N) ); - - vec4 RayTraceColor = GetRayColor(WorldPos.xyz, ReflectionVec.xyz, WorldNorm.xyz, MaterialProps); - - // ******************************** - // Fill in pixel data and shade - // ******************************** - PixelData.WorldPos = vec4(WorldPos, 1.0); - PixelData.WorldNorm = vec4(WorldNorm, 0.0); - - // TODO: These are not filled in for deferred renderer - PixelData.Tangent = vec4(1.0, 0.0, 0.0, 1.0); - PixelData.Bitan = vec4(0.0, 1.0, 0.0, 1.0); - PixelData.Color = vec4(0.8, 0.8, 0.8, 1.0); - PixelData.BumpNorm = PixelData.WorldNorm; // This is in bump space - - PixelData.DiffuseTexValue = DiffuseColor; - PixelData.NormalTexValue = PixelData.WorldNorm; - - FragColor = ShadePixel(PixelData); - - FragColor = mix(FragColor, RayTraceColor, RAY_BLEND_AMOUNT); - - // Debug! Debug! Debug! Debug! Debug! Debug! Debug! Debug! Debug! - // Debug! Debug! Debug! Debug! Debug! Debug! Debug! Debug! Debug! - // FragColor = RayTraceColor; - - // vec4 MaterialProps = texture( u_MatPropsTex, LocalTexCoord.xy ); - // FragColor = vec4(MaterialProps.rgb, 1.0); - -} - diff --git a/samples/rayReflections/shaders/Light.json b/samples/rayReflections/shaders/Light.json deleted file mode 100644 index 9cd8fe2..0000000 --- a/samples/rayReflections/shaders/Light.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/Light.vert.spv", - "Fragment": "Media/Shaders/Light.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { "Type": "UniformBuffer", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "FragUB" ] }, - { "Type": "AccelerationStructure", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "AccelerationStructure" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 0, "Names": [ "TexArray" ] }, - { "Type": "StorageBuffer", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "SceneData" ] }, - { "Type": "StorageBuffer", "Stages": [ "Fragment" ], "Count": 0, "Names": [ "VertData" ] }, - { "Type": "StorageBuffer", "Stages": [ "Fragment" ], "Count": 0, "Names": [ "IndiceData" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Albedo" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Normal" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "MatProps" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Environment" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Irradiance" ] } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 24, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" - } - ] - } - ] -} diff --git a/samples/rayReflections/shaders/ObjectDeferred.frag b/samples/rayReflections/shaders/ObjectDeferred.frag deleted file mode 100644 index 417d3aa..0000000 --- a/samples/rayReflections/shaders/ObjectDeferred.frag +++ /dev/null @@ -1,97 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -// Define available features - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Texture Locations -#define SHADER_DIFFUSE_TEXTURE_LOC 2 -#define SHADER_NORMAL_TEXTURE_LOC 3 - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ - vec4 Color; - - // X: Max Ray Bounces? - // Y: Not Used - // Z: Not Used - // W: Not Used - vec4 MaterialProps; - -} FragCB; - -#define NORMAL_HEIGHT FragCB.NormalHeight.x -#define NORMAL_MIRROR_AMOUNT FragCB.NormalHeight.y - -// Textures -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 3) in vec3 v_WorldTan; -layout (location = 4) in vec3 v_WorldBitan; -layout (location = 5) in vec4 v_VertColor; - -// Output color -layout (location = 0) out vec4 FragColor; -// Output Normal -layout (location = 1) out vec4 FragNormal; -// Output Material Properties -layout (location = 2) out vec4 FragMatProps; - - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Base (albedo) color - // ******************************** - // Get color from the color texture - vec4 AlbedoColor = texture( u_DiffuseTex, v_TexCoord.xy ); - AlbedoColor.xyzw *= FragCB.Color.xyzw; - - // Adjust by vertex color. - AlbedoColor.xyzw *= v_VertColor.xyzw; - - // Get base normal from the bump texture - vec4 NormTexValue = texture( u_NormalTex, v_TexCoord.xy ); - vec3 N = NormTexValue.xyz * 2.0 - 1.0; - - // Need matrix to convert to tangent space - // vec3 binormal = cross(v_WorldNorm, v_WorldTan); - // mat3 WorldToTan = mat3(normalize(v_WorldTan), normalize(binormal), normalize(v_WorldNorm)); - mat3 WorldToTan = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); - - // Convert the bump normal to tangent space - vec3 BumpNormal = normalize(WorldToTan * N); - - // Write out the color - FragColor = vec4(AlbedoColor.rgb, 1.0); - - // Write out the Normal and put depth value in the 'alpha' channel - FragNormal = vec4(BumpNormal.xyz, 1.0 - gl_FragCoord.z); - - // Material Properties - FragMatProps = FragCB.MaterialProps; -} - diff --git a/samples/rayReflections/shaders/ObjectDeferred.json b/samples/rayReflections/shaders/ObjectDeferred.json deleted file mode 100644 index 9a30226..0000000 --- a/samples/rayReflections/shaders/ObjectDeferred.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_GBUFFER", - "Shaders": { - "Vertex": "Media/Shaders/ObjectDeferred.vert.spv", - "Fragment": "Media/Shaders/ObjectDeferred.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { "Type": "UniformBuffer", "Stages": [ "Vertex" ], "Count": 1, "Names": [ "Vert" ] }, - { "Type": "UniformBuffer", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Frag" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Diffuse" ] }, - { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, "Names": [ "Normal" ] } - ] - } - ], - "VertexBindings": [ "Position", "Attributes", "Instances" ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true, - "DepthCompareOp": "LessEqual" - }, - "Outputs": [ - { "BlendEnable": false }, - { "BlendEnable": false }, - { "BlendEnable": false } - ] - } - ], - "Vertex": [ - { - "Span": 12, - "Name": "Position", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Attributes", - "Elements": [ - { - "Name": "Normal", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 20, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 36, - "Type": "Vec3" - } - ] - }, - { - "Span": 48, - "Name": "Instances", - "Rate": "Instance", - "Elements": [ - { - "Name": "Transform0", - "Offset": 0, - "Type": "Vec4" - }, - { - "Name": "Transform1", - "Offset": 16, - "Type": "Vec4" - }, - { - "Name": "Transform2", - "Offset": 32, - "Type": "Vec4" - } - ] - } - ] -} diff --git a/samples/rayReflections/shaders/ObjectDeferred.vert b/samples/rayReflections/shaders/ObjectDeferred.vert deleted file mode 100644 index cb0e27d..0000000 --- a/samples/rayReflections/shaders/ObjectDeferred.vert +++ /dev/null @@ -1,74 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 -#define SHADER_ATTRIB_LOC_INSTANCE_MATRIX 5 /* also 6 and 7 */ - -// These start back over at 0! -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Vertex rate data -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -// Instance rate data -layout (location = SHADER_ATTRIB_LOC_INSTANCE_MATRIX ) in mat3x4 a_InstanceMatrix3x4; // use a mat3x4 (row major, ie transposed from 'normal' GLSL, but less input slots) - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff -{ - mat4 VPMatrix; -} VertCB; - -const mat4 biasMat = mat4( - 0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.5, 0.5, 0.0, 1.0 ); - -// Varying's -layout (location = 0) out vec2 v_TexCoord; -layout (location = 1) out vec3 v_WorldPos; -layout (location = 2) out vec3 v_WorldNorm; -layout (location = 3) out vec3 v_WorldTan; -layout (location = 4) out vec3 v_WorldBitan; -layout (location = 5) out vec4 v_VertColor; - - -void main() -{ - vec3 WorldPosition = vec4(a_Position, 1.0) * a_InstanceMatrix3x4; // a_InstanceMatrix is 3x4 to reduce number of input binding slots needed. Because it is transposed (from the normal glsl expectation of a 4x3) we do a post multiply. - - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = VertCB.VPMatrix * vec4( WorldPosition, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); - - // Need Position in world space - v_WorldPos = WorldPosition; - - // Need Normal, Tangent, and Bitangent in world space. Normalize since the a_InstanceMatrix3x4 may be scaled. - v_WorldNorm = normalize(vec4(a_Normal.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldTan = normalize(vec4(a_Tangent.xyz, 0.0) * a_InstanceMatrix3x4); - v_WorldBitan = cross(v_WorldNorm, v_WorldTan); - - // Color is simple attribute color - v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); - -} diff --git a/samples/rayReflections/shaders/Skybox.frag b/samples/rayReflections/shaders/Skybox.frag deleted file mode 100644 index c963e5d..0000000 --- a/samples/rayReflections/shaders/Skybox.frag +++ /dev/null @@ -1,53 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 - -// Texture Locations -#define SHADER_ENVIRONMENT_TEXTURE_LOC 1 - -// Textures -layout(set = 0, binding = SHADER_ENVIRONMENT_TEXTURE_LOC) uniform samplerCube u_EnvironmentTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 6) in vec4 v_VertColor; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Diffuse Color - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = v_VertColor.xyzw; - - // ******************************** - // Skybox Color - // ******************************** - vec3 CubeUV = normalize(v_WorldNorm); - vec3 ReflectMapColor = textureLod(u_EnvironmentTex, CubeUV, 0).rgb; - - // ******************************** - // Final Color - // ******************************** - FragColor = vec4(DiffuseColor.xyz * ReflectMapColor.rgb, DiffuseColor.w); -} - diff --git a/samples/rayReflections/shaders/Skybox.json b/samples/rayReflections/shaders/Skybox.json deleted file mode 100644 index 309c74d..0000000 --- a/samples/rayReflections/shaders/Skybox.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_LIGHT", - "Shaders": { - "Vertex": "Media/Shaders/Skybox.vert.spv", - "Fragment": "Media/Shaders/Skybox.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Environment" ] - } - ] - } - ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": false - }, - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 24, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" - } - ] - } - ] -} diff --git a/samples/rotatedCopy/01_CompileShaders.bat b/samples/rotatedCopy/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/rotatedCopy/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rotatedCopy/02_Install_APK.bat b/samples/rotatedCopy/02_Install_APK.bat deleted file mode 100644 index 5fe94bd..0000000 --- a/samples/rotatedCopy/02_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\rotatedCopy\outputs\apk\debug\rotatedCopy-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\rotatedCopy\outputs\apk\debug\rotatedCopy-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rotatedCopy/02_PrepareMedia.bat b/samples/rotatedCopy/02_PrepareMedia.bat deleted file mode 100644 index 943b515..0000000 --- a/samples/rotatedCopy/02_PrepareMedia.bat +++ /dev/null @@ -1,17 +0,0 @@ -@echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skull_Separate.* Media\Objects\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. - -..\..\project\tools\simpletextureconverter ..\..\vkSampleFrameworkAssets\shared\Media\Textures\Skull_Diffuse.jpg .\Media\Textures\Skull_Diffuse.ktx -format R8G8B8A8Unorm diff --git a/samples/rotatedCopy/04_Install_APK.bat b/samples/rotatedCopy/04_Install_APK.bat deleted file mode 100644 index 5fe94bd..0000000 --- a/samples/rotatedCopy/04_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\rotatedCopy\outputs\apk\debug\rotatedCopy-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\rotatedCopy\outputs\apk\debug\rotatedCopy-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/rotatedCopy/06_Adb_Logcat.bat b/samples/rotatedCopy/06_Adb_Logcat.bat deleted file mode 100644 index 6b89aff..0000000 --- a/samples/rotatedCopy/06_Adb_Logcat.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat... -call adb logcat -c -call adb logcat - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause \ No newline at end of file diff --git a/samples/rotatedCopy/CMakeLists.txt b/samples/rotatedCopy/CMakeLists.txt deleted file mode 100644 index 379988f..0000000 --- a/samples/rotatedCopy/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (rotatedCopy C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# - -set(CPP_SRC code/main/rotatedCopy.cpp - code/main/rotatedCopy.hpp -) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) - -# -# Copy required models to local folders -# -include(ModelPackager) - -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) - -# -# Convert and copy textures to local folders -# -include(TexturePackager) - -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file diff --git a/samples/rotatedCopy/README.md b/samples/rotatedCopy/README.md deleted file mode 100644 index 9845808..0000000 --- a/samples/rotatedCopy/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# VK_QCOM_rotated_copy_commands Extension Sample - -Sample to initialize and use the 'VK_QCOM_rotated_copy_commands' Vulkan extension. - -Extension may/will need enabling on older Qualcomm Vulkan drivers. Sample does nothing useful on non Qualcomm hardware. - -![Screenshot](img/screenshot.PNG) - -## Overview - -This extension extends adds an optional rotation transform to copy commands vkCmdBlitImage2KHR, vkCmdCopyImageToBuffer2KHR and vkCmdCopyBufferToImage2KHR. When copying between two resources, where one resource contains rotated content and the other does not, a rotated copy may be desired. - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - -Note: The sample assumes the existence of supporting assets under the **'Media'** folder. These assets are not currently distributed with the framework. -The framework team is working to build a centralized asset repository that should minimize these requirements in the near future. - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_Install_APK.bat -``` - -If desired, you can keep track of any logging by running one of the logcat batch files (which you can find on the current directory). - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. - - diff --git a/samples/rotatedCopy/build.gradle b/samples/rotatedCopy/build.gradle deleted file mode 100644 index bf65c61..0000000 --- a/samples/rotatedCopy/build.gradle +++ /dev/null @@ -1,94 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.rotatedcopy" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - assets.srcDirs = ['assets'] - - // Uncomment this to enable validation (may interfere with new Qualcomm extensions) -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.21.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/rotatedCopy/img/screenshot.PNG b/samples/rotatedCopy/img/screenshot.PNG deleted file mode 100644 index ea2b7c3..0000000 Binary files a/samples/rotatedCopy/img/screenshot.PNG and /dev/null differ diff --git a/samples/rotatedCopy/project/android/res/values/strings.xml b/samples/rotatedCopy/project/android/res/values/strings.xml deleted file mode 100644 index a42cb75..0000000 --- a/samples/rotatedCopy/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Rotated Copy - diff --git a/samples/rotatedCopy/project/android/res/values/styles.xml b/samples/rotatedCopy/project/android/res/values/styles.xml deleted file mode 100644 index f591923..0000000 --- a/samples/rotatedCopy/project/android/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/samples/rotatedCopy/shaders/Object.frag b/samples/rotatedCopy/shaders/Object.frag deleted file mode 100644 index c16d18f..0000000 --- a/samples/rotatedCopy/shaders/Object.frag +++ /dev/null @@ -1,63 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable -#extension GL_GOOGLE_include_directive : enable - -// Uniform buffer locations -#define SHADER_VERT_UBO_LOCATION 0 -#define SHADER_FRAG_UBO_LOCATION 1 - -// Texture Locations -#define SHADER_DIFFUSE_TEXTURE_LOC 2 -#define SHADER_NORMAL_TEXTURE_LOC 3 -#define SHADER_ENVIRONMENT_TEXTURE_LOC 4 -#define SHADER_IRRADIANCE_TEXTURE_LOC 5 -#define SHADER_REFLECT_TEXTURE_LOC 6 - -// Uniform Constant Buffer -layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff -{ -float tmp; -} FragCB; - -// Textures -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec3 v_WorldPos; -layout (location = 2) in vec3 v_WorldNorm; -layout (location = 3) in vec3 v_WorldTan; -layout (location = 4) in vec3 v_WorldBitan; -layout (location = 5) in vec4 v_VertColor; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Base Lighting - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); - - // Adjust by vertex color. - DiffuseColor.xyzw *= v_VertColor.xyzw; - - // Output - FragColor = vec4(DiffuseColor.xyz, 1.0); -} - diff --git a/samples/rotated_copy/CMakeLists.txt b/samples/rotated_copy/CMakeLists.txt new file mode 100644 index 0000000..7efa187 --- /dev/null +++ b/samples/rotated_copy/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required (VERSION 3.21) + +project (rotated_copy C CXX) +set(CMAKE_CXX_STANDARD 20) + +# +# Source files included in this application. +# + +set(CPP_SRC code/main/application.cpp + code/main/application.hpp +) + +# +# Setup the module path to include the 'project directory' (project/windows or project/android) +# +if(NOT DEFINED PROJECT_ROOT_DIR) + set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) + +# +# Do all the build steps for a Framework application. +# needs Framework_dir and project_name variables. +# +include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + +# +# Copy required models to local folders +# +include(ModelPackager) + +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) + +# +# Convert and copy textures to local folders +# +include(TexturePackager) + +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/rotated_copy/README.md b/samples/rotated_copy/README.md new file mode 100644 index 0000000..1b6a661 --- /dev/null +++ b/samples/rotated_copy/README.md @@ -0,0 +1,6 @@ +# VK_QCOM_rotated_copy_commands Extension Sample + +Sample to initialize and use the 'VK_QCOM_rotated_copy_commands' Vulkan extension. + +Extension may/will need enabling on older Qualcomm Vulkan drivers. Sample does nothing useful on non Qualcomm hardware. + diff --git a/samples/rotated_copy/code/main/VK_QCOM_rotated_copy_commands.h b/samples/rotated_copy/code/main/VK_QCOM_rotated_copy_commands.h new file mode 100644 index 0000000..8c3903e --- /dev/null +++ b/samples/rotated_copy/code/main/VK_QCOM_rotated_copy_commands.h @@ -0,0 +1,173 @@ +#pragma once +#ifndef _VULKAN_VK_QCOM_ROTATED_COPY_H_ +#define _VULKAN_VK_QCOM_ROTATED_COPY_H_ + +#include + +#if !defined(VK_QCOM_rotated_copy_commands) + +// Provided by VK_QCOM_rotated_copy_commands +constexpr VkStructureType VK_STRUCTURE_TYPE_COPY_COMMAND_TRANSFORM_INFO_QCOM = (VkStructureType)1000333000; + +#define VK_QCOM_rotated_copy_commands 1 +#define VK_QCOM_rotated_copy_commands_SPEC_VERSION 0 +#define VK_QCOM_rotated_copy_commands_EXTENSION_NAME "VK_QCOM_rotated_copy_commands" +typedef struct VkCopyCommandTransformInfoQCOM { + VkStructureType sType; + const void* pNext; + VkSurfaceTransformFlagBitsKHR transform; +} VkCopyCommandTransformInfoQCOM; + +#endif // !defined(VK_QCOM_rotated_copy_commands) + + + +#if !defined(VK_KHR_copy_commands2) + +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR = (VkStructureType)1000337000; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR = (VkStructureType)1000337001; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR = (VkStructureType)1000337002; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR = (VkStructureType)1000337003; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR = (VkStructureType)1000337004; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR = (VkStructureType)1000337005; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR = (VkStructureType)1000337006; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR = (VkStructureType)1000337007; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR = (VkStructureType)1000337008; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR = (VkStructureType)1000337009; +// Provided by VK_KHR_copy_commands2 +constexpr VkStructureType VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR = (VkStructureType)1000337010; + +#define VK_KHR_copy_commands2 1 +#define VK_KHR_COPY_COMMANDS_2_SPEC_VERSION 1 +#define VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME "VK_KHR_copy_commands2" +typedef struct VkBufferCopy2KHR { + VkStructureType sType; + const void* pNext; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy2KHR; + +typedef struct VkCopyBufferInfo2KHR { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferCopy2KHR* pRegions; +} VkCopyBufferInfo2KHR; + +typedef struct VkImageCopy2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy2KHR; + +typedef struct VkCopyImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageCopy2KHR* pRegions; +} VkCopyImageInfo2KHR; + +typedef struct VkBufferImageCopy2KHR { + VkStructureType sType; + const void* pNext; + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy2KHR; + +typedef struct VkCopyBufferToImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkBufferImageCopy2KHR* pRegions; +} VkCopyBufferToImageInfo2KHR; + +typedef struct VkCopyImageToBufferInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferImageCopy2KHR* pRegions; +} VkCopyImageToBufferInfo2KHR; + +typedef struct VkImageBlit2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit2KHR; + +typedef struct VkBlitImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageBlit2KHR* pRegions; + VkFilter filter; +} VkBlitImageInfo2KHR; + +typedef struct VkImageResolve2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve2KHR; + +typedef struct VkResolveImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageResolve2KHR* pRegions; +} VkResolveImageInfo2KHR; + +typedef void (VKAPI_PTR* PFN_vkCmdCopyBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR* pCopyBufferInfo); +typedef void (VKAPI_PTR* PFN_vkCmdCopyImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR* pCopyImageInfo); +typedef void (VKAPI_PTR* PFN_vkCmdCopyBufferToImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo); +typedef void (VKAPI_PTR* PFN_vkCmdCopyImageToBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo); +typedef void (VKAPI_PTR* PFN_vkCmdBlitImage2KHR)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR* pBlitImageInfo); +typedef void (VKAPI_PTR* PFN_vkCmdResolveImage2KHR)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR* pResolveImageInfo); + +#endif // !defined(VK_KHR_copy_commands2) + +#endif // _VULKAN_VK_QCOM_ROTATED_COPY_H_ diff --git a/samples/rotatedCopy/code/main/rotatedCopy.cpp b/samples/rotated_copy/code/main/application.cpp similarity index 75% rename from samples/rotatedCopy/code/main/rotatedCopy.cpp rename to samples/rotated_copy/code/main/application.cpp index 10b7aea..56f0dc6 100644 --- a/samples/rotatedCopy/code/main/rotatedCopy.cpp +++ b/samples/rotated_copy/code/main/application.cpp @@ -1,41 +1,44 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== /// /// Sample app for VK_QCOM_rotated_copy_commands extension /// -#include "rotatedCopy.hpp" +#include "application.hpp" #include "camera/cameraController.hpp" #include "camera/cameraControllerTouch.hpp" #include "gui/imguiVulkan.hpp" +#include "material/vulkan/descriptorSetLayout.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/material.hpp" +#include "material/vulkan/materialPass.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/pipeline.hpp" +#include "material/vulkan/shaderModule.hpp" +#include "material/shaderManagerT.hpp" #include "mesh/meshHelper.hpp" -#include "mesh/meshLoader.hpp" -#include "material/drawable.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" #include "system/math_common.hpp" -#include "texture/textureManager.hpp" -#include "imgui.h" +#include "texture/vulkan/textureManager.hpp" +#include "imgui/imgui.h" #include -#include +#include // Global Variables From Config File -VAR(glm::vec3, gCameraStartPos, glm::vec3(26.48f, 20.0f, -5.21f), kVariableNonpersistent); -VAR(glm::vec3, gCameraStartRot, glm::vec3(0.0f, 110.0f, 0.0f), kVariableNonpersistent); +VAR(char*, gSceneAssetModel, "SteamPunkSauna.gltf", kVariableNonpersistent); +VAR(glm::vec3, gCameraStartPos, glm::vec3(0.0f, 3.5f, 0.0f), kVariableNonpersistent); +VAR(glm::vec3, gCameraStartRot, glm::vec3(0.0f, 0.0f, 0.0f), kVariableNonpersistent); +VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); +VAR(float, gNearPlane, 0.1f, kVariableNonpersistent); +VAR(float, gFarPlane, 50.0f, kVariableNonpersistent); -VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); -VAR(float, gNearPlane, 0.1f, kVariableNonpersistent); -VAR(float, gFarPlane, 80.0f, kVariableNonpersistent); +VAR(bool, gRotatedFinalBlit, true, kVariableNonpersistent); -VAR(bool, gRotatedFinalBlit, true, kVariableNonpersistent); /// /// @brief Implementation of the Application entrypoint (called by the framework) @@ -96,11 +99,7 @@ bool Application::Initialize( uintptr_t windowHandle, uintptr_t hInstance ) auto* const pVulkan = GetVulkan(); // Get the pointer to the rotated blit function. - if (GetVulkan()->HasLoadedVulkanDeviceExtension("VK_QCOM_rotated_copy_commands")) - m_fpCmdBlitImage2KHR = (PFN_vkCmdBlitImage2KHR)vkGetInstanceProcAddr(pVulkan->GetVulkanInstance(), "vkCmdBlitImage2KHR"); - else - m_fpCmdBlitImage2KHR = nullptr; - if (m_fpCmdBlitImage2KHR == nullptr) + if (vkCmdBlitImage2KHR == nullptr) { LOGE("Unable to get function pointer from instance: vkCmdBlitImage2KHR"); // Disable rotated blit as we can't get the function pointer. @@ -114,9 +113,6 @@ bool Application::Initialize( uintptr_t windowHandle, uintptr_t hInstance ) CreateUniformBuffer( pVulkan, m_ObjectVertUniform ); CreateUniformBuffer( pVulkan, m_ObjectFragUniform ); - m_TexWhite = LoadKTXTexture(pVulkan, *m_AssetManager, "./Media/Textures/white_d.ktx", SamplerAddressMode::Repeat); - m_DefaultNormal = LoadKTXTexture(pVulkan, *m_AssetManager, "./Media/Textures/normal_default.ktx", SamplerAddressMode::Repeat); - InitGui(windowHandle); InitCommandBuffers(); @@ -141,11 +137,11 @@ bool Application::LoadShaders() typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "Object", "Media\\Shaders\\Object.json" }, - tIdAndFilename { "Tonemap", "Media\\Shaders\\Tonemap.json"} + { tIdAndFilename { "Object", "Object.json" }, + tIdAndFilename { "Tonemap", "Tonemap.json"} }) { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); } @@ -172,79 +168,82 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() const TextureFormat ScenePassColorFormats[] = { TextureFormat::A2B10G10R10_UNORM_PACK32 }; const TEXTURE_TYPE ScenePassTextureTypes[] = { TT_RENDER_TARGET }; - const VkSampleCountFlagBits ScenePassMsaa[] = { VK_SAMPLE_COUNT_1_BIT }; + const Msaa ScenePassMsaa[] = { Msaa::Samples1 }; const TextureFormat ScenePassDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); const TextureFormat TonemapPassColorFormats[] = { TextureFormat::A2B10G10R10_UNORM_PACK32 }; - const VkSampleCountFlagBits TonemapPassMsaa[] = { VK_SAMPLE_COUNT_1_BIT }; + const Msaa TonemapPassMsaa[] = { Msaa::Samples1 }; const TEXTURE_TYPE TonemapPassTextureTypes[] = { TT_RENDER_TARGET_TRANSFERSRC }; // // Scene render pass - VkRenderPass SceneRenderPass = VK_NULL_HANDLE; + RenderPass SceneRenderPass; if( !pVulkan->CreateRenderPass( ScenePassColorFormats, ScenePassDepthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, RenderPassInputUsage::Clear/*color*/, RenderPassOutputUsage::StoreReadOnly,/*color*/ true,/*clear depth*/ RenderPassOutputUsage::Discard,/*depth*/ - &SceneRenderPass ) ) + SceneRenderPass ) ) { return false; } pVulkan->SetDebugObjectName( SceneRenderPass, "SceneRenderPass" ); - // Create render target for 3d scene. - if (!m_SceneRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, ScenePassColorFormats, ScenePassDepthFormat, SceneRenderPass, (VkRenderPass)VK_NULL_HANDLE, ScenePassMsaa, "Scene RT", ScenePassTextureTypes)) + // + // Tonemap render pass + RenderPass TonemapRenderPass; + if (!pVulkan->CreateRenderPass(TonemapPassColorFormats, + TextureFormat::UNDEFINED, + Msaa::Samples1, + RenderPassInputUsage::DontCare/*color*/, + RenderPassOutputUsage::StoreTransferSrc,/*color*/ + false,/*dont clear depth*/ + RenderPassOutputUsage::Discard,/*depth*/ + TonemapRenderPass)) { - LOGE("Error initializing LinearColorRT"); return false; } + pVulkan->SetDebugObjectName(TonemapRenderPass, "TonemapRenderPass"); - // Load Scene (object) drawable - LoadSceneDrawables(SceneRenderPass, ScenePassMsaa); - - - // - // Tonemap render pass - VkRenderPass TonemapRenderPass = VK_NULL_HANDLE; - if( !pVulkan->CreateRenderPass( TonemapPassColorFormats, - TextureFormat::UNDEFINED, - VK_SAMPLE_COUNT_1_BIT, - RenderPassInputUsage::DontCare/*color*/, - RenderPassOutputUsage::StoreTransferSrc,/*color*/ - false,/*dont clear depth*/ - RenderPassOutputUsage::Discard,/*depth*/ - &TonemapRenderPass) ) + // Create render target for 3d scene. + if (!m_SceneRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, ScenePassColorFormats, ScenePassDepthFormat, "Scene RT", ScenePassTextureTypes, ScenePassMsaa) + || !m_SceneRT.InitializeFrameBuffer(pVulkan, SceneRenderPass)) { + LOGE("Error initializing LinearColorRT"); return false; } - pVulkan->SetDebugObjectName( TonemapRenderPass, "TonemapRenderPass" ); // Create the render target for the Tonemap/composite - if( !m_TonemapRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, TonemapPassColorFormats, TextureFormat::UNDEFINED, TonemapRenderPass/*takes ownership*/, (VkRenderPass) VK_NULL_HANDLE, TonemapPassMsaa, "Tonemap RT", TonemapPassTextureTypes ) ) + if( !m_TonemapRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, TonemapPassColorFormats, TextureFormat::UNDEFINED, "Tonemap RT", TonemapPassTextureTypes, TonemapPassMsaa) + || !m_TonemapRT.InitializeFrameBuffer(pVulkan, TonemapRenderPass)) { LOGE( "Error initializing LinearColorRT" ); return false; } + m_SceneRenderContext = RenderContext(std::move(SceneRenderPass), m_SceneRT.GetFrameBuffer(), "RP_OPAQUE"); + m_TonemapRenderContext = RenderContext(std::move(TonemapRenderPass), m_TonemapRT.GetFrameBuffer(), "Fullscreen"); + + // Load Scene (object) drawable + LoadSceneDrawables(m_SceneRenderContext, ScenePassMsaa[0]); + // Tonemap drawable - std::map tonemapTex = { {"Diffuse", &m_SceneRT[0].m_ColorAttachments.front() }, - {"Overlay", &m_GuiRT[0].m_ColorAttachments.front() } }; - m_TonemapDrawable = InitFullscreenDrawable( "Tonemap", tonemapTex, {}, m_TonemapRT.m_RenderPass, 0, TonemapPassMsaa ); + std::map tonemapTex = { {"Diffuse", &m_SceneRT.m_ColorAttachments.front() }, + {"Overlay", &m_GuiRT.m_ColorAttachments.front() } }; + m_TonemapDrawable = InitFullscreenDrawable( "Tonemap", tonemapTex, {}, m_TonemapRenderContext, 0, TonemapPassMsaa[0]); return true; } //----------------------------------------------------------------------------- -bool Application::LoadSceneDrawables( VkRenderPass renderPass, const std::span renderPassMultisamples ) +bool Application::LoadSceneDrawables( const RenderContext& renderContext, Msaa renderPassMultisamples ) //----------------------------------------------------------------------------- { - const char* pGLTFMeshFile = "./Media/Meshes/Museum.gltf"; - std::string texturesPath("Media\\"); + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); - LOGI( "Loading Scene Drawable %s", pGLTFMeshFile ); + LOGI( "Loading Scene Drawable %s", sceneAssetPath.c_str()); auto* const pVulkan = GetVulkan(); @@ -255,22 +254,29 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, const std::spanSetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); + m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory(TEXTURE_DESTINATION_PATH)); + + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); + if (!whiteTexture) + { + return false; + } // Lambda to load a texture for the given material (slot) - const auto& textureLoader = [&]( const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName ) -> const MaterialPass::tPerFrameTexInfo + const auto& textureLoader = [&]( const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName ) -> const MaterialManagerBase::tPerFrameTexInfo { - auto* texture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerEdgeClamp); - if (texture) - { - return { texture }; - } + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const PathManipulator_ChangeExtension changeTextureExt{ ".ktx" }; - return { &m_TexWhite }; + auto* texture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + + return { texture ? texture : whiteTexture }; }; // Lambda to associate (uniform) buffers with their shader binding/slot names. - const auto& bufferLoader = [&]( const std::string& bufferSlotName ) -> tPerFrameVkBuffer { + const auto& bufferLoader = [&]( const std::string& bufferSlotName ) -> PerFrameBuffer + { if( bufferSlotName == "Vert" ) { return { m_ObjectVertUniform.buf.GetVkBuffer() }; @@ -289,18 +295,15 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, const std::span std::optional { using namespace std::placeholders; - return m_MaterialManager->CreateMaterial( *pVulkan, *pObjectShader, NUM_VULKAN_BUFFERS, std::bind( textureLoader, std::cref( materialDef ), _1 ), bufferLoader ); + return m_MaterialManager->CreateMaterial( *pObjectShader, NUM_VULKAN_BUFFERS, std::bind( textureLoader, std::cref( materialDef ), _1 ), bufferLoader ); }; - static const char* opaquePassName = "RP_OPAQUE"; - const uint32_t renderPassSubpasses[] = { 0 }; - m_SceneObject.clear(); - if( !DrawableLoader::LoadDrawables( *pVulkan, *m_AssetManager, { &renderPass,1 }, &opaquePassName, pGLTFMeshFile, materialLoader, m_SceneObject, renderPassMultisamples, DrawableLoader::LoaderFlags::None, renderPassSubpasses ) ) + if (!DrawableLoader::LoadDrawables( *pVulkan, *m_AssetManager, renderContext, sceneAssetPath, materialLoader, m_SceneObject, 0 )) { - LOGE( "Error loading Object mesh: %s", pGLTFMeshFile ); + LOGE( "Error loading Object mesh: %s", sceneAssetPath.c_str()); return false; } @@ -309,7 +312,7 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, const std::span Application::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, const std::span renderPassMultisamples ) +std::unique_ptr Application::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, const RenderContext& renderContext, uint32_t subpassIdx, Msaa renderPassMultisamples ) //----------------------------------------------------------------------------- { auto* const pVulkan = GetVulkan(); @@ -324,20 +327,20 @@ std::unique_ptr Application::InitFullscreenDrawable( const char* pShad return nullptr; } - MeshObject mesh; + Mesh mesh; if (!MeshHelper::CreateMesh(GetVulkan()->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pShader->m_shaderDescription->m_vertexFormats, &mesh)) { LOGE("Error creating Fullscreen Mesh (for %s)", pShaderName); return nullptr; } - auto shaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pShader, NUM_VULKAN_BUFFERS, - [this, &ColorAttachmentsLookup](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { + auto shaderMaterial = m_MaterialManager->CreateMaterial(*pShader, NUM_VULKAN_BUFFERS, + [this, &ColorAttachmentsLookup](const std::string& texName) -> const MaterialManagerBase::tPerFrameTexInfo { return { ColorAttachmentsLookup.find(texName)->second }; assert(0); return {}; }, - []( const std::string& bufferName ) -> const tPerFrameVkBuffer { + []( const std::string& bufferName ) -> const PerFrameBuffer { assert(0); return {}; }, @@ -346,11 +349,8 @@ std::unique_ptr Application::InitFullscreenDrawable( const char* pShad } ); - static const char* passName = "Fullscreen"; - const uint32_t renderPassSubpasses[1] = { subpassIdx }; - auto drawable = std::make_unique( *pVulkan, std::move( shaderMaterial ) ); - if( !drawable->Init( renderPass, passName, std::move(mesh), std::nullopt, std::nullopt, &renderPassMultisamples[0], &renderPassSubpasses[0] ) ) + if( !drawable->Init(renderContext, std::move(mesh), std::nullopt, std::nullopt ) ) { LOGE("Error creating Blit Drawable"); return nullptr; @@ -370,7 +370,7 @@ bool Application::InitCommandBuffers() for( uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++ ) { sprintf( szName, "CommandBuffer (%d of %d)", WhichBuffer + 1, NUM_VULKAN_BUFFERS ); - if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY ) ) + if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, CommandList::Type::Primary ) ) { return false; } @@ -458,7 +458,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) auto& commandBuffer = m_CommandBuffer[bufferIdx]; - if( !commandBuffer.Begin( m_SceneRT[0].m_FrameBuffer, m_SceneRT.m_RenderPass ) ) + if( !commandBuffer.Begin() ) { return false; } @@ -487,12 +487,12 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) VkRect2D Scissor = {}; Scissor.offset.x = 0; Scissor.offset.y = 0; - Scissor.extent.width = m_SceneRT[0].m_Width; - Scissor.extent.height = m_SceneRT[0].m_Height; + Scissor.extent.width = m_SceneRT.m_Width; + Scissor.extent.height = m_SceneRT.m_Height; VkClearColorValue ClearColor[1] {0.1f, 0.0f, 0.0f, 0.0f}; - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, m_SceneRT[0].GetNumColorLayers(), true, m_SceneRT.m_RenderPass, false, m_SceneRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, m_SceneRT.GetNumColorLayers(), true, m_SceneRenderContext.GetRenderPass(), false, *m_SceneRenderContext.GetFramebuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -508,7 +508,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) if (m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_GuiRT.m_RenderPass, false, m_GuiRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_GuiContext.GetRenderPass(), false, *m_GuiContext.GetFramebuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -520,7 +520,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) // // Now add the tonemapping (and add UI overlay)... // - if (!commandBuffer.BeginRenderPass(Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_TonemapRT.m_RenderPass, false, m_TonemapRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE)) + if (!commandBuffer.BeginRenderPass(Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_TonemapRenderContext.GetRenderPass(), false, *m_TonemapRenderContext.GetFramebuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -532,12 +532,12 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) if (gRotatedFinalBlit) { // Rotated blit - AddRotatedSwapchainBlitToCmdBuffer(commandBuffer, m_TonemapRT[0], swapchainImage); + AddRotatedSwapchainBlitToCmdBuffer(commandBuffer, m_TonemapRT, swapchainImage); } else { // Non rotated blit - AddSwapchainBlitToCmdBuffer(commandBuffer, m_TonemapRT[0], swapchainImage); + AddSwapchainBlitToCmdBuffer(commandBuffer, m_TonemapRT, swapchainImage); } { @@ -566,7 +566,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) //----------------------------------------------------------------------------- -void Application::AddRotatedSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage) +void Application::AddRotatedSwapchainBlitToCmdBuffer(CommandListVulkan& commandBuffer, const RenderTarget& srcRT, VkImage swapchainImage) //----------------------------------------------------------------------------- { // @@ -608,13 +608,13 @@ void Application::AddRotatedSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& comma } // Add the BlitImage2 command to the command buffer. - if (m_fpCmdBlitImage2KHR) - m_fpCmdBlitImage2KHR(commandBuffer.m_VkCommandBuffer, &blitImageInfo); + if (vkCmdBlitImage2KHR) + vkCmdBlitImage2KHR(commandBuffer.m_VkCommandBuffer, &blitImageInfo); } //----------------------------------------------------------------------------- -void Application::AddSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage) +void Application::AddSwapchainBlitToCmdBuffer(CommandListVulkan& commandBuffer, const RenderTarget& srcRT, VkImage swapchainImage) //----------------------------------------------------------------------------- { VkImageBlit blitImageRegion{}; @@ -654,16 +654,37 @@ bool Application::InitGui(uintptr_t windowHandle) const TextureFormat GuiColorFormat[]{ TextureFormat::R8G8B8A8_UNORM }; const TEXTURE_TYPE GuiTextureTypes[]{ TT_RENDER_TARGET }; - if( !m_GuiRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, GuiColorFormat, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "Gui RT", GuiTextureTypes ) ) + // + // Gui render pass + RenderPass GuiRenderPass; + if (!pVulkan->CreateRenderPass(GuiColorFormat, + TextureFormat::UNDEFINED, + Msaa::Samples1, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store, + false, + RenderPassOutputUsage::Discard, + GuiRenderPass)) { return false; } + pVulkan->SetDebugObjectName(GuiRenderPass, "GuiRenderPass"); - m_Gui = std::make_unique>(*pVulkan, m_GuiRT.m_RenderPass); - if (!m_Gui->Initialize(windowHandle, m_GuiRT[0].m_Width, m_GuiRT[0].m_Height)) + if (!m_GuiRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, GuiColorFormat, TextureFormat::UNDEFINED, "Gui RT", GuiTextureTypes) + || !m_GuiRT.InitializeFrameBuffer(pVulkan, GuiRenderPass)) { + LOGE("Error initializing GuiRT"); return false; } + + m_GuiContext = RenderContext(std::move(GuiRenderPass), m_GuiRT.GetFrameBuffer(), "Gui RC"); + + m_Gui = std::make_unique(*pVulkan, m_GuiContext.GetRenderPass().Copy()); + if (!m_Gui->Initialize(windowHandle, m_GuiRT.m_pLayerFormats[0], m_GuiRT.m_Width, m_GuiRT.m_Height)) + { + return false; + } + return true; } @@ -684,7 +705,7 @@ void Application::UpdateGui() if (ImGui::Begin("Settings", &settingsOpen, (ImGuiWindowFlags)0)) { // Add our widgets - if (m_fpCmdBlitImage2KHR) + if (vkCmdBlitImage2KHR) { ImGui::Checkbox("Rotated backbuffer Blit", &m_RequestedRotatedFinalBlit); } @@ -714,8 +735,10 @@ void Application::Destroy() m_SceneObject.clear(); m_TonemapDrawable.reset(); - ReleaseTexture(*pVulkan, &m_TexWhite); - ReleaseTexture(*pVulkan, &m_DefaultNormal); + for (auto& texture : m_LoadedTextures) + { + ReleaseTexture(*pVulkan, &texture.second); + } ReleaseUniformBuffer( pVulkan, &m_ObjectVertUniform ); ReleaseUniformBuffer( pVulkan, &m_ObjectFragUniform ); diff --git a/samples/rotatedCopy/code/main/rotatedCopy.hpp b/samples/rotated_copy/code/main/application.hpp similarity index 66% rename from samples/rotatedCopy/code/main/rotatedCopy.hpp rename to samples/rotated_copy/code/main/application.hpp index 97b31cc..7c11827 100644 --- a/samples/rotatedCopy/code/main/rotatedCopy.hpp +++ b/samples/rotated_copy/code/main/application.hpp @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #pragma once /// @@ -18,15 +18,13 @@ #include "vulkan/renderTarget.hpp" #include "memory/vulkan/uniform.hpp" #include "vulkan/commandBuffer.hpp" - +#include "VK_QCOM_rotated_copy_commands.h" #include #include // Forward declarations -class Drawable; -class ShaderManager; -class MaterialManager; -struct ImageInfo; +class ShaderManagerBase; +class MaterialManagerBase; class Application : public ApplicationHelperBase @@ -48,8 +46,8 @@ class Application : public ApplicationHelperBase bool LoadShaders(); bool InitFramebuffersRenderPassesAndDrawables(); - bool LoadSceneDrawables( VkRenderPass renderPass, const std::span renderPassMultisamples ); - std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& inputLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, const std::span renderPassMultisamples); + bool LoadSceneDrawables( const RenderContext& renderContext, Msaa renderPassMultisamples ); + std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& inputLookup, const std::map& ImageAttachmentsLookup, const RenderContext& renderContext, uint32_t subpassIdx, Msaa renderPassMultisamples ); bool InitCommandBuffers(); bool InitCamera() override; @@ -67,11 +65,11 @@ class Application : public ApplicationHelperBase /// Adds a rotated blit command to the commandBuffer. /// Blits from the m_TonemapRT to the given swapchain image. - void AddRotatedSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage); + void AddRotatedSwapchainBlitToCmdBuffer(CommandListVulkan& commandBuffer, const RenderTarget& srcRT, VkImage swapchainImage); /// Adds a (non rotated) blit command to the commandBuffer. /// Blits from the m_TonemapRT to the given swapchain image. - void AddSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage); + void AddSwapchainBlitToCmdBuffer(CommandListVulkan& commandBuffer, const RenderTarget& srcRT, VkImage swapchainImage); void ChangeSwapchainMode(); @@ -83,10 +81,8 @@ class Application : public ApplicationHelperBase void Destroy() override; private: - // Textures - TextureVulkan m_TexWhite; - TextureVulkan m_DefaultNormal; + std::map m_LoadedTextures; // Materials @@ -110,20 +106,16 @@ class Application : public ApplicationHelperBase UniformT m_ObjectFragUniform; /// Render target for 3d Objects. - CRenderTargetArray<1> m_SceneRT; - - /// Render target for tonemap (also the composited ui) - CRenderTargetArray<1> m_TonemapRT; - - /// Render target for GUI. - CRenderTargetArray<1> m_GuiRT; + RenderTarget m_SceneRT; + RenderTarget m_TonemapRT; + RenderTarget m_GuiRT; + RenderContext m_SceneRenderContext; + RenderContext m_TonemapRenderContext; + RenderContext m_GuiContext; /// Single primary command buffer - Wrap_VkCommandBuffer m_CommandBuffer[NUM_VULKAN_BUFFERS]; + CommandListVulkan m_CommandBuffer[NUM_VULKAN_BUFFERS]; /// Modified by UI to request enable/disable of the final rotated blit bool m_RequestedRotatedFinalBlit = false; - - // function pointers - PFN_vkCmdBlitImage2KHR m_fpCmdBlitImage2KHR = nullptr; }; diff --git a/samples/rotated_copy/install_apk.bat b/samples/rotated_copy/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/rotated_copy/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/rotated_copy/install_config.bat b/samples/rotated_copy/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/rotated_copy/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/rayQueryShadows/project/android/AndroidManifest.xml b/samples/rotated_copy/project/android/AndroidManifest.xml similarity index 97% rename from samples/rayQueryShadows/project/android/AndroidManifest.xml rename to samples/rotated_copy/project/android/AndroidManifest.xml index bf314cf..bc9a085 100644 --- a/samples/rayQueryShadows/project/android/AndroidManifest.xml +++ b/samples/rotated_copy/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -21,7 +20,7 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="landscape" - android:theme="@style/Theme.NoTitleBar.NoActionBar.Fullscreen"> + android:theme="@style/Theme.NoTitleBar.NoActionBar.Fullscreen" android:exported="true"> - HDR Swapchain + SGS Rotated Copy diff --git a/samples/rayQueryShadows/project/android/res/values/styles.xml b/samples/rotated_copy/project/android/res/values/styles.xml similarity index 100% rename from samples/rayQueryShadows/project/android/res/values/styles.xml rename to samples/rotated_copy/project/android/res/values/styles.xml diff --git a/samples/rotatedCopy/shaders/Fullscreen.vert b/samples/rotated_copy/shaders/Fullscreen.vert similarity index 75% rename from samples/rotatedCopy/shaders/Fullscreen.vert rename to samples/rotated_copy/shaders/Fullscreen.vert index cdd4f93..6c36d02 100644 --- a/samples/rotatedCopy/shaders/Fullscreen.vert +++ b/samples/rotated_copy/shaders/Fullscreen.vert @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 #extension GL_ARB_separate_shader_objects : enable diff --git a/samples/shaderResolveTonemap/shaders/Object.frag b/samples/rotated_copy/shaders/Object.frag similarity index 86% rename from samples/shaderResolveTonemap/shaders/Object.frag rename to samples/rotated_copy/shaders/Object.frag index c16d18f..0340018 100644 --- a/samples/shaderResolveTonemap/shaders/Object.frag +++ b/samples/rotated_copy/shaders/Object.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 @@ -26,7 +26,7 @@ // Uniform Constant Buffer layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff { -float tmp; + float unused; } FragCB; // Textures diff --git a/samples/rotatedCopy/shaders/Object.json b/samples/rotated_copy/shaders/Object.json similarity index 100% rename from samples/rotatedCopy/shaders/Object.json rename to samples/rotated_copy/shaders/Object.json diff --git a/samples/rotatedCopy/shaders/Object.vert b/samples/rotated_copy/shaders/Object.vert similarity index 88% rename from samples/rotatedCopy/shaders/Object.vert rename to samples/rotated_copy/shaders/Object.vert index 790d13a..3d02f28 100644 --- a/samples/rotatedCopy/shaders/Object.vert +++ b/samples/rotated_copy/shaders/Object.vert @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== // Object.vert @@ -35,7 +35,6 @@ layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstant { mat4 MVPMatrix; mat4 ModelMatrix; - mat4 ShadowMatrix; } VertCB; // Varying's diff --git a/samples/rotatedCopy/shaders/Tonemap.frag b/samples/rotated_copy/shaders/Tonemap.frag similarity index 92% rename from samples/rotatedCopy/shaders/Tonemap.frag rename to samples/rotated_copy/shaders/Tonemap.frag index bf63ebf..ff280c6 100644 --- a/samples/rotatedCopy/shaders/Tonemap.frag +++ b/samples/rotated_copy/shaders/Tonemap.frag @@ -1,10 +1,10 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== #version 400 diff --git a/samples/rotatedCopy/shaders/Tonemap.json b/samples/rotated_copy/shaders/Tonemap.json similarity index 100% rename from samples/rotatedCopy/shaders/Tonemap.json rename to samples/rotated_copy/shaders/Tonemap.json diff --git a/samples/sdp-cli/CMakeLists.txt b/samples/sdp-cli/CMakeLists.txt deleted file mode 100644 index 025cc3b..0000000 --- a/samples/sdp-cli/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -cmake_minimum_required (VERSION 3.21) - -project (sdp_cli C CXX) -set(CMAKE_CXX_STANDARD 20) - -# -# Source files included in this application. -# -set(CPP_SRC code/main/application.cpp - code/main/application.hpp - - # Helpers - code/main/helpers/imgui_extensions.cpp - code/main/helpers/imgui_extensions.hpp - code/main/helpers/console_common.cpp - code/main/helpers/console_common.hpp - code/main/helpers/console_helper.cpp - code/main/helpers/console_helper.hpp - code/main/helpers/module_interface.cpp - code/main/helpers/module_interface.hpp - code/main/helpers/numerical_aggregator.cpp - code/main/helpers/numerical_aggregator.hpp - - # Module (SDP CLI) - code/main/modules/sdp_cli/sdp_cli.cpp - code/main/modules/sdp_cli/sdp_cli.hpp -) - -# -# Setup the module path to include the 'project directory' (project/windows or project/android) -# -if(NOT DEFINED PROJECT_ROOT_DIR) - set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) -endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - -# -# Do all the build steps for a Framework application. -# needs Framework_dir and project_name variables. -# -include(FrameworkApplicationHelper) diff --git a/samples/sdp-cli/README.md b/samples/sdp-cli/README.md deleted file mode 100644 index 1cf46bf..0000000 --- a/samples/sdp-cli/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# SDP CLI Sample - -![Screenshot](img/screenshot.png) - -## Overview - -SDP CLI (sdpcli) is a command line, on-device profiling tool for devices with Adreno™ GPUs. The tool provides some of the most often used [Snapdragon® Profiler](https://www.qualcomm.com/developer/software/snapdragon-profiler) functionality as a CLI application, that can easily be integrated into any project. SDP CLI is available at [Qualcomm® Software Center](https://softwarecenter.qualcomm.com/#/catalog/item/b71f3c61-9efe-11ee-ba71-026b10d3716b?type=Tool). - -This sample uses the CLI application to fetch device counters through the *realtime* metrics mode. It uses ImGui to create an UI where one can visualize the fetched data with graphs over long periods of time. - -Some implementation details of this sample: -- Drag and drop a graph into another to merge -- Right click a graph entry on the legend to unmerge -- Device selection is available through a drop-down widget -- Sampling period can be adjusted -- Each graph can be viewed as a histogram if not in a group - -## Building - -### Build - -Building this sample *.exe* is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -02_BuildWindows.bat -``` - -## Running - -After running the sample *.exe*, you will be asked for the *sdpcli* executable and the *trace* apk corresponding to your device (grab these at [Qualcomm® Software Center](https://softwarecenter.qualcomm.com/#/catalog/item/b71f3c61-9efe-11ee-ba71-026b10d3716b?type=Tool)). -After both are selected, press *inject* (sample will copy/install these 2 files into your device) and you should be directed to the sample main UI. \ No newline at end of file diff --git a/samples/sdp-cli/build.gradle b/samples/sdp-cli/build.gradle deleted file mode 100644 index e97164a..0000000 --- a/samples/sdp-cli/build.gradle +++ /dev/null @@ -1,73 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - defaultConfig { - applicationId "com.quic.framework_sdp_cli" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${project.rootDir}", "-DFRAMEWORK_DIR=${project.rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - assets.srcDirs = ['assets'] - - // Uncomment this to enable validation - //jniLibs { - // srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" - //} - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - -} diff --git a/samples/sdp-cli/code/main/application.cpp b/samples/sdp-cli/code/main/application.cpp deleted file mode 100644 index dde5a2e..0000000 --- a/samples/sdp-cli/code/main/application.cpp +++ /dev/null @@ -1,399 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "application.hpp" -#include "helpers/console_helper.hpp" -#include "helpers/console_common.hpp" -#include "camera/cameraController.hpp" -#include "camera/cameraControllerTouch.hpp" -#include "gui/imguiVulkan.hpp" -#include "mesh/meshHelper.hpp" -#include "mesh/meshLoader.hpp" -#include "material/drawable.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" -#include "system/math_common.hpp" -#include "texture/textureManager.hpp" -#include "imgui.h" -#include "implot/implot.h" -#include -#include -#include - -// Modules -#include "modules/sdp_cli/sdp_cli.hpp" - -/// -/// @brief Implementation of the Application entrypoint (called by the framework) -/// @return Pointer to Application (derived from @FrameworkApplicationBase). -/// Creates the Application class. Ownership is passed to the calling (framework) function. -/// -FrameworkApplicationBase* Application_ConstructApplication() -{ - return new Application(); -} - -Application::Application() : ApplicationHelperBase() -{ -} - -Application::~Application() -{ -} - -//----------------------------------------------------------------------------- -bool Application::Initialize( uintptr_t windowHandle, uintptr_t hInstance ) -//----------------------------------------------------------------------------- -{ - if( !ApplicationHelperBase::Initialize( windowHandle, hInstance ) ) - { - return false; - } - - auto* const pVulkan = GetVulkan(); - - InitGui(windowHandle); - - InitCommandBuffers(); - - // InitFramebuffersRenderPassesAndDrawables(); - - InitCamera(); - - UpdateDeviceList(); - - if (m_connected_devices.size() >= 1) - { - m_selected_device = m_connected_devices[0]; - InitializeModulesWithSelectedDevice(); - } - - return true; -} - -void Application::InitializeModulesWithSelectedDevice() -{ - m_modules.clear(); - - // Register each module that will be used: - m_modules.push_back(std::make_unique(m_selected_device)); - - // Initialize registered modules - for (int i = m_modules.size()-1; i>=0; i--) - { - if(!m_modules[i]->Initialize()) - { - m_modules.erase(m_modules.begin() + i); - } - } -} - -void Application::UpdateDeviceList() -{ - m_connected_devices = GetConnectedDeviceList(); -} - -/* -* # Main Window Requirements: -* - If multiple devices, selector for which device to use (if changed, restart whole app) -* - Root mode or disable other parts (thermal) -* - Restart and shutdown options -* - Deploy files (thermal, SDP CLI and others) -* - Switch to other tabs -* - Tabs should have different colors if active (if capture is active, if whatever they have active has overhead, etc) -* -* # Main Window: -* - Device info (Device name, Chipset, GPU, driver version, compiler version, Android version, DVT, is rooted) -* - CPU usage, frequency and thermal -* - Device overall thermal -* - Stalls PIE (SDP CLI) -* - Texture misses (SDP CLI) -* - FPS -* -* # Long Usage Capture (accumulated) -* - CPU usage, frequency and thermal -* - Device overall thermal -* - GPU Frequency -* - GPU Thermal -* - FPS -* - -*/ - -//----------------------------------------------------------------------------- -bool Application::InitCommandBuffers() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - char szName[256]; - for( uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++ ) - { - sprintf( szName, "CommandBuffer (%d of %d)", WhichBuffer + 1, NUM_VULKAN_BUFFERS ); - if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY ) ) - { - return false; - } - } - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitGui(uintptr_t windowHandle) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - const TextureFormat GuiColorFormat[]{ TextureFormat::R8G8B8A8_UNORM }; - const TEXTURE_TYPE GuiTextureTypes[]{ TT_RENDER_TARGET }; - - if( !m_GuiRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, GuiColorFormat, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "Gui RT", GuiTextureTypes ) ) - { - return false; - } - - m_Gui = std::make_unique>(*pVulkan, m_GuiRT.m_RenderPass); - if (!m_Gui->Initialize(windowHandle, m_GuiRT[0].m_Width, m_GuiRT[0].m_Height)) - { - return false; - } - - ImPlot::CreateContext(); - - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateGui(float time_elapsed) -//----------------------------------------------------------------------------- -{ - if(!m_Gui) - { - return; - } - - // Update Gui - m_Gui->Update(); - - ImGui::SetNextWindowSize(ImVec2((gRenderWidth * 3.0f) / 4.0f, 500.f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2((float)gRenderWidth / 8.0f, gRenderHeight / 2.0f), ImGuiCond_FirstUseEver); - if (ImGui::Begin("SDP CLI Interface")) - { - if (ImGui::BeginCombo("Connected Devices", m_selected_device.c_str())) - { - for (const auto& device : m_connected_devices) - { - bool is_selected = (device == m_selected_device); - if (ImGui::Selectable(device.c_str(), is_selected)) - { - m_selected_device = device; - InitializeModulesWithSelectedDevice(); - } - - if (is_selected) - { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - if (ImGui::BeginTabBar("Snapdragon Tabs")) - { - for (auto& registered_module : m_modules) - { - if (ImGui::BeginTabItem(registered_module->GetModuleName())) - { - registered_module->Draw(time_elapsed); - ImGui::EndTabItem(); - } - } - - if(ImGui::TabItemButton("Remove Tools", ImGuiTabItemFlags_Trailing)) - { - auto connected_devices = GetConnectedDeviceList(); - if (connected_devices.size() == 1) - { - for(auto& installed_module : m_modules) - { - installed_module->RemoveToolFromDevice(); - } - - InitializeModulesWithSelectedDevice(); - } - } - - ImGui::EndTabBar(); - } - } - ImGui::End(); - - // ImPlot::ShowDemoWindow(); -} - -//----------------------------------------------------------------------------- -void Application::AddSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage) -//----------------------------------------------------------------------------- -{ - VkImageBlit blitImageRegion{}; - blitImageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blitImageRegion.srcSubresource.layerCount = 1; - blitImageRegion.srcOffsets[0] = { 0,0,0 }; - blitImageRegion.srcOffsets[1] = { (int)srcRT.m_Width, (int)srcRT.m_Height, 1 }; - blitImageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blitImageRegion.dstSubresource.layerCount = 1; - blitImageRegion.dstOffsets[0] = { 0,0,0 }; - blitImageRegion.dstOffsets[1] = { (int)GetVulkan()->m_SurfaceWidth, (int)GetVulkan()->m_SurfaceHeight, 1 }; - vkCmdBlitImage(commandBuffer.m_VkCommandBuffer, srcRT.m_ColorAttachments.back().GetVkImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchainImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blitImageRegion, VK_FILTER_NEAREST); -} - -//----------------------------------------------------------------------------- -bool Application::UpdateCommandBuffer(uint32_t bufferIdx) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - auto& commandBuffer = m_CommandBuffer[bufferIdx]; - - if( !commandBuffer.Begin( m_GuiRT[0].m_FrameBuffer, m_GuiRT.m_RenderPass ) ) - { - return false; - } - - - VkRect2D Scissor = {}; - Scissor.offset.x = 0; - Scissor.offset.y = 0; - Scissor.extent.width = m_GuiRT[0].m_Width; - Scissor.extent.height = m_GuiRT[0].m_Height; - - VkClearColorValue ClearColor[1] {0.1f, 0.0f, 0.0f, 0.0f}; - - // - // Do the Gui - // - if (m_Gui) - { - // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_GuiRT.m_RenderPass, false, m_GuiRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) - { - return false; - } - GetGui()->Render(commandBuffer.m_VkCommandBuffer); - - commandBuffer.EndRenderPass(); - } - - VkImage swapchainImage = pVulkan->m_SwapchainBuffers[bufferIdx].image; - { - // Need to transition the swapchain before it can be blitted to (not needed if written by a render pass) - VkImageMemoryBarrier presentPreBlitTransitionBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - presentPreBlitTransitionBarrier.srcAccessMask = 0; - presentPreBlitTransitionBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - presentPreBlitTransitionBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - presentPreBlitTransitionBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - presentPreBlitTransitionBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentPreBlitTransitionBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentPreBlitTransitionBarrier.image = swapchainImage; - presentPreBlitTransitionBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - presentPreBlitTransitionBarrier.subresourceRange.baseMipLevel = 0; - presentPreBlitTransitionBarrier.subresourceRange.levelCount = 1; - presentPreBlitTransitionBarrier.subresourceRange.baseArrayLayer = 0; - presentPreBlitTransitionBarrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(commandBuffer.m_VkCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &presentPreBlitTransitionBarrier); - } - - AddSwapchainBlitToCmdBuffer(commandBuffer, m_GuiRT[0], swapchainImage); - - { - // Need to transition the swapchain before it can be presented - VkImageMemoryBarrier presentPostBlitTransitionBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - presentPostBlitTransitionBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - presentPostBlitTransitionBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - presentPostBlitTransitionBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - presentPostBlitTransitionBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - presentPostBlitTransitionBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentPostBlitTransitionBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - presentPostBlitTransitionBarrier.image = swapchainImage; - presentPostBlitTransitionBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - presentPostBlitTransitionBarrier.subresourceRange.baseMipLevel = 0; - presentPostBlitTransitionBarrier.subresourceRange.levelCount = 1; - presentPostBlitTransitionBarrier.subresourceRange.baseArrayLayer = 0; - presentPostBlitTransitionBarrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(commandBuffer.m_VkCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &presentPostBlitTransitionBarrier); - } - - commandBuffer.End(); - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::UpdateUniforms( uint32_t bufferIdx ) -//----------------------------------------------------------------------------- -{ -#if 0 - ObjectVertUniform data{ }; - data.MVPMatrix = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix(); - data.ModelMatrix = glm::identity(); - UpdateUniformBuffer( GetVulkan(), m_ObjectVertUniform, data ); -#endif - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateCamera(float elapsedTime) -//----------------------------------------------------------------------------- -{ -#if 0 - if (m_CameraController) - m_Camera.UpdateController(elapsedTime, *m_CameraController); - m_Camera.UpdateMatrices(); -#endif -} - -void Application::Render(float fltDiffTime) -{ - // Every x time, update device list - // Ideally we should listed through ADB in case a device is connected instead doing a query - // ever x seconds - m_update_device_list_time_elapsed += fltDiffTime; - if (m_update_device_list_time_elapsed > 2.5f) - { - m_update_device_list_time_elapsed = 0.0f; - UpdateDeviceList(); - } - - for (auto& registered_module : m_modules) - { - registered_module->Update(fltDiffTime); - } - - UpdateGui(fltDiffTime); - - UpdateCamera( fltDiffTime ); - - auto* const pVulkan = GetVulkan(); - - auto CurrentVulkanBuffer = pVulkan->SetNextBackBuffer(); - - UpdateUniforms( CurrentVulkanBuffer.idx ); - - UpdateCommandBuffer( CurrentVulkanBuffer.idx ); - - // ... submit the command buffer to the device queue - m_CommandBuffer[CurrentVulkanBuffer.idx].QueueSubmit( CurrentVulkanBuffer, pVulkan->m_RenderCompleteSemaphore ); - - // and Present - PresentQueue( pVulkan->m_RenderCompleteSemaphore, CurrentVulkanBuffer.swapchainPresentIdx ); - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); -} diff --git a/samples/sdp-cli/code/main/application.hpp b/samples/sdp-cli/code/main/application.hpp deleted file mode 100644 index d38b084..0000000 --- a/samples/sdp-cli/code/main/application.hpp +++ /dev/null @@ -1,80 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -/// -/// @file application.hpp -/// @brief Application implementation for 'empty' application. -/// -/// Most basic application that compiles and runs with the Vulkan Framework. -/// DOES NOT initialize Vulkan. -/// - -#include "main/applicationHelperBase.hpp" -#include "vulkan/renderTarget.hpp" -#include "memory/vulkan/uniform.hpp" -#include "vulkan/commandBuffer.hpp" -#include "helpers/module_interface.hpp" -#include - -class Application : public ApplicationHelperBase -{ -public: - Application(); - ~Application() override; - -/////////////////////////////////// -public: // ApplicationHelperBase // -/////////////////////////////////// - - bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; - - /// @brief Ticked every frame (by the Framework) - /// @param fltDiffTime time (in seconds) since the last call to Render. - void Render(float fltDiffTime) override; - -/////////////////////////////////// -public: /////////////////////////// -/////////////////////////////////// - - bool InitCommandBuffers(); - - /* - */ - bool InitGui(uintptr_t windowHandle); - - /* - */ - void UpdateGui(float time_elapsed); - - /* - */ - void InitializeModulesWithSelectedDevice(); - - /* - */ - void UpdateDeviceList(); - - void UpdateCamera(float elapsedTime); - bool UpdateUniforms(uint32_t bufferIdx); - bool UpdateCommandBuffer(uint32_t bufferIdx); - - void AddSwapchainBlitToCmdBuffer(Wrap_VkCommandBuffer& commandBuffer, const CRenderTarget& srcRT, VkImage swapchainImage); - -private: - - CRenderTargetArray<1> m_GuiRT; - - Wrap_VkCommandBuffer m_CommandBuffer[NUM_VULKAN_BUFFERS]; - - std::vector> m_modules; - - std::vector m_connected_devices; - std::string m_selected_device; - float m_update_device_list_time_elapsed = 0.0f; -}; diff --git a/samples/sdp-cli/code/main/helpers/console_common.cpp b/samples/sdp-cli/code/main/helpers/console_common.cpp deleted file mode 100644 index 5b3a5fc..0000000 --- a/samples/sdp-cli/code/main/helpers/console_common.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "console_common.hpp" -#include "console_helper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -std::vector GetConnectedDeviceList() -{ - ConsoleHelper console_helper; - auto console_output = console_helper.ExecuteSynchronousCommand("adb devices"); - - std::vector device_names; - std::istringstream iss(console_output); - std::string line; - - // Skip the first line (header) - std::getline(iss, line); - - // Read each subsequent line and extract the device name - while (std::getline(iss, line)) - { - std::istringstream line_stream(line); - std::string device_name; - line_stream >> device_name; // Assuming the device name is the first token - if (!device_name.empty()) - { - device_names.push_back(device_name); - } - } - - return device_names; -} - -std::string RunADBCommandForDevice(std::string_view device_name, std::string_view command) -{ - ConsoleHelper console_helper; - const std::string command_string = std::string("adb -s ").append(device_name).append(" ").append(command); - return console_helper.ExecuteSynchronousCommand(command_string); -} - -void EnterDeviceRootMode(std::string_view device_name, bool wait_for_device) -{ - ConsoleHelper console_helper; - - const std::string enter_root_command = std::string("adb -s ").append(device_name).append(" root"); - const std::string wait_device_command = std::string("adb -s ").append(device_name).append(" wait-for-device"); - - console_helper.ExecuteSynchronousCommand(enter_root_command); - if (wait_for_device) - { - console_helper.ExecuteSynchronousCommand(wait_device_command); - } -} - -bool DoesDeviceFileExist(std::string_view device_name, std::string_view file_path) -{ - ConsoleHelper console_helper; - - const std::string find_file_command = std::string("adb -s ").append(device_name).append(" shell ls ").append(file_path); - const auto console_output = console_helper.ExecuteSynchronousCommand(find_file_command); - - return console_output.find(file_path) != std::string::npos; -} - -std::string GetDeviceFileContents(std::string_view device_name, std::string_view file_path) -{ - if (!DoesDeviceFileExist(device_name, file_path)) - { - return std::string(); - } - - ConsoleHelper console_helper; - const std::string cat_file_command = std::string("adb -s ").append(device_name).append(" cat ").append(file_path); - const auto console_output = console_helper.ExecuteSynchronousCommand(cat_file_command); - - return console_output; -} \ No newline at end of file diff --git a/samples/sdp-cli/code/main/helpers/console_common.hpp b/samples/sdp-cli/code/main/helpers/console_common.hpp deleted file mode 100644 index 0da912b..0000000 --- a/samples/sdp-cli/code/main/helpers/console_common.hpp +++ /dev/null @@ -1,35 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include -#include -#include -#include - -/* -*/ -std::vector GetConnectedDeviceList(); - -/* -*/ -std::string RunADBCommandForDevice(std::string_view device_name, std::string_view command); - -/* -*/ -void EnterDeviceRootMode(std::string_view device_name, bool wait_for_device = true); - -/* -*/ -bool DoesDeviceFileExist(std::string_view device_name, std::string_view file_path); - -/* -*/ -std::string GetDeviceFileContents(std::string_view device_name, std::string_view file_path); \ No newline at end of file diff --git a/samples/sdp-cli/code/main/helpers/console_helper.cpp b/samples/sdp-cli/code/main/helpers/console_helper.cpp deleted file mode 100644 index 73d1993..0000000 --- a/samples/sdp-cli/code/main/helpers/console_helper.cpp +++ /dev/null @@ -1,235 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "console_helper.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Windows stuff -#include -#include - -#define COMMAND_LINE_BUFFER_SIZE 1024 - -ConsoleHelper::ConsoleHelper() -{ -} - -ConsoleHelper::~ConsoleHelper() -{ - if (m_is_asynchronous) - { - TerminateAsynchronousCommand(true); - } - - // Ensure all threads died (even the ones we didn't wait for) - for (auto& active_thread : m_non_waited_threads) - { - if (active_thread.joinable()) - active_thread.join(); - } -} - -std::string ConsoleHelper::ExecuteSynchronousCommand(std::string_view in_cmd) -{ - m_is_asynchronous = false; - - char buffer[COMMAND_LINE_BUFFER_SIZE]; - - FILE* pipe = _popen(in_cmd.data(), "r"); - if (!pipe) - { - return "ERROR"; - } - - auto process_handle = _get_osfhandle(_fileno(pipe)); - - if(!feof(pipe)) - { - while(fgets(buffer, 1024, pipe) != NULL) - { - m_command_result += buffer; - } - } - - if (pipe != nullptr) - { - TerminateProcess((HANDLE)(process_handle), 0); - _pclose(pipe); - } - - return m_command_result; -} - -void ConsoleHelper::ExecuteAsynchronousCommand( - std::string_view cmd, - std::optional command_line_callback, - bool ignore_first_line) -{ - m_is_asynchronous = true; - - if (command_line_callback) - { - m_command_line_callback = std::move(command_line_callback.value()); - } - - // Hold a hard copy since thread will manage its lifetime - std::string cmd_copy = cmd.data(); - - // Grab the current async version index - const int async_version_index = m_async_thread_version; - - m_async_command_thread = std::thread([cmd_copy, ignore_first_line, async_version_index, this]() -> void - { - char buffer[COMMAND_LINE_BUFFER_SIZE]; - bool should_ignore_next_line = ignore_first_line; - - FILE* pipe = _popen(cmd_copy.data(), "r"); - if (!pipe) - { - return; - } - - m_safety.lock(); - m_async_command_handle = _get_osfhandle(_fileno(pipe)); - m_safety.unlock(); - - while (!feof(pipe) && m_async_command_handle && async_version_index == m_async_thread_version) - { - if(fgets(buffer, 1024, pipe) != NULL && !should_ignore_next_line) - { - std::lock_guard l(m_safety); - - // Check it again here in case it changed before we acquired the lock - if (async_version_index != m_async_thread_version) - { - continue; - } - - m_command_result += buffer; - - if (m_command_line_callback) - { - m_command_line_callback(buffer); - } - } - - should_ignore_next_line = false; - } - - std::lock_guard l(m_safety); - - // Only close the pipe if the async thread index is still the same, otherwise it was already closed - // elsewhere - if (pipe != nullptr && async_version_index == m_async_thread_version) - { - _pclose(pipe); - TerminateProcess((HANDLE)(m_async_command_handle), 0); - m_async_command_handle = 0; - } - -#if 0 - if (pipe != nullptr) - { - _pclose(pipe); - - if (m_async_command_handle) - { - TerminateProcess((HANDLE)(m_async_command_handle), 0); - m_async_command_handle = 0; - } - } -#endif - }); -} - -std::string ConsoleHelper::GetAsynchronousCommandResult(bool terminate) -{ - m_safety.lock(); - auto result = m_command_result; - m_command_result.clear(); - m_safety.unlock(); - - if (terminate && m_is_asynchronous) - { - TerminateAsynchronousCommand(); - } - - return std::move(result); -} - -void ConsoleHelper::TerminateAsynchronousCommand(bool wait) -{ - m_safety.lock(); - if (m_async_command_handle) - { - TerminateProcess((HANDLE)(m_async_command_handle), 0); - m_async_command_handle = 0; - } - - m_async_thread_version++; - - m_safety.unlock(); - - if (wait) - { - if (m_async_command_thread.joinable()) - m_async_command_thread.join(); - } - else - { - m_non_waited_threads.push_back(std::move(m_async_command_thread)); - m_async_command_thread = std::thread(); - } -} - -#if 0 - - -std::string exec(const char* cmd, bool wait = true) -{ - while(!s_enable && wait) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - FILE* pipe = _popen(cmd, "r"); - if (!pipe) return "ERROR"; - char buffer[1024]; - std::string result = ""; - while(!feof(pipe)) - { - if(fgets(buffer, 1024, pipe) != NULL) - { - result += buffer; - _fclose_nolock(pipe); - pipe = nullptr; - break; - } - } - if (pipe != nullptr) - { - _pclose(pipe); - } - - if (!result.empty()) - { - m_val = result; - } - - return result; -} - -#endif \ No newline at end of file diff --git a/samples/sdp-cli/code/main/helpers/console_helper.hpp b/samples/sdp-cli/code/main/helpers/console_helper.hpp deleted file mode 100644 index a9dc1d7..0000000 --- a/samples/sdp-cli/code/main/helpers/console_helper.hpp +++ /dev/null @@ -1,61 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include -#include -#include -#include - -class ConsoleHelper -{ - using OnCommandLineOutput = std::function; - -public: - ConsoleHelper(); - ~ConsoleHelper(); - - /* - */ - std::string ExecuteSynchronousCommand(std::string_view in_cmd); - - /* - */ - void ExecuteAsynchronousCommand( - std::string_view cmd, - std::optional command_line_callback = std::nullopt, - bool ignore_first_line = false); - - /* - */ - std::string GetAsynchronousCommandResult(bool terminate = false); - - /* - */ - void TerminateAsynchronousCommand(bool wait = false); - - /* - */ - inline bool IsAsynchronousCommandActive() const - { - return m_async_command_handle != 0; - } - -private: - - std::thread m_async_command_thread; - int64_t m_async_command_handle = 0; - std::string m_command_result; - std::mutex m_safety; - bool m_is_asynchronous = false; - OnCommandLineOutput m_command_line_callback; - std::vector m_non_waited_threads; - std::atomic m_async_thread_version = 0; -}; diff --git a/samples/sdp-cli/code/main/helpers/imgui_extensions.cpp b/samples/sdp-cli/code/main/helpers/imgui_extensions.cpp deleted file mode 100644 index b2638c0..0000000 --- a/samples/sdp-cli/code/main/helpers/imgui_extensions.cpp +++ /dev/null @@ -1,245 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui.h" -#include "imgui_internal.h" - -namespace ImGui -{ - -// https://github.com/ocornut/imgui/issues/76 -// Taken from: https://github.com/wasikuss/imgui/commit/a50515ace6d9a62ebcd69817f1da927d31c39bb1 (MIT) - -extern float RoundScalarWithFormatFloat(const char* format, ImGuiDataType data_type, float v); - -extern float SliderCalcRatioFromValueFloat(ImGuiDataType data_type, float v, float v_min, float v_max, float power, float linear_zero_pos); - -// extern float RoundScalarWithFormatFloat(const char* format, ImGuiDataType data_type, float v); -float RoundScalarWithFormatFloat(const char* format, ImGuiDataType data_type, float v) -{ - return ImGui::RoundScalarWithFormatT(format, data_type, v); -} - -// extern float SliderCalcRatioFromValueFloat(ImGuiDataType data_type, float v, float v_min, float v_max, float power, float linear_zero_pos); -float SliderCalcRatioFromValueFloat(ImGuiDataType data_type, float v, float v_min, float v_max, float power, float linear_zero_pos) -{ - return ImGui::ScaleRatioFromValueT(data_type, v, v_min, v_max, false, power, linear_zero_pos); -} - -// ~80% common code with ImGui::SliderBehavior -bool RangeSliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v1, float* v2, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - const ImGuiStyle& style = g.Style; - - // Draw frame - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); - const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; - - const float grab_padding = 2.0f; - const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); - float grab_sz; - if (decimal_precision > 0) - grab_sz = ImMin(style.GrabMinSize, slider_sz); - else - grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit - const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f; - const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f; - - // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f - float linear_zero_pos = 0.0f; // 0.0->1.0f - if (v_min * v_max < 0.0f) - { - // Different sign - const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); - const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); - linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); - } - else - { - // Same sign - linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; - } - - // Process clicking on the slider - bool value_changed = false; - if (g.ActiveId == id) - { - if (g.IO.MouseDown[0]) - { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - float clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) - clicked_t = 1.0f - clicked_t; - - float new_value; - if (is_non_linear) - { - // Account for logarithmic scale on both sides of the zero - if (clicked_t < linear_zero_pos) - { - // Negative: rescale to the negative range before powering - float a = 1.0f - (clicked_t / linear_zero_pos); - a = powf(a, power); - new_value = ImLerp(ImMin(v_max,0.0f), v_min, a); - } - else - { - // Positive: rescale to the positive range before powering - float a; - if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f) - a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); - else - a = clicked_t; - a = powf(a, power); - new_value = ImLerp(ImMax(v_min,0.0f), v_max, a); - } - } - else - { - // Linear slider - new_value = ImLerp(v_min, v_max, clicked_t); - } - - char fmt[64]; - snprintf(fmt, 64, "%%.%df", decimal_precision); - - // Round past decimal precision - new_value = RoundScalarWithFormatFloat(fmt, ImGuiDataType_Float, new_value); - if (*v1 != new_value || *v2 != new_value) - { - if (fabsf(*v1 - new_value) < fabsf(*v2 - new_value)) - { - *v1 = new_value; - } - else - { - *v2 = new_value; - } - value_changed = true; - } - } - else - { - ClearActiveID(); - } - } - - // Calculate slider grab positioning - float grab_t = SliderCalcRatioFromValueFloat(ImGuiDataType_Float, *v1, v_min, v_max, power, linear_zero_pos); - - // Draw - if (!is_horizontal) - grab_t = 1.0f - grab_t; - float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - ImRect grab_bb1; - if (is_horizontal) - grab_bb1 = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding)); - else - grab_bb1 = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f)); - window->DrawList->AddRectFilled(grab_bb1.Min, grab_bb1.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Calculate slider grab positioning - grab_t = SliderCalcRatioFromValueFloat(ImGuiDataType_Float, *v2, v_min, v_max, power, linear_zero_pos); - - // Draw - if (!is_horizontal) - grab_t = 1.0f - grab_t; - grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - ImRect grab_bb2; - if (is_horizontal) - grab_bb2 = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding)); - else - grab_bb2 = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f)); - window->DrawList->AddRectFilled(grab_bb2.Min, grab_bb2.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - ImRect connector(grab_bb1.Min, grab_bb2.Max); - connector.Min.x += grab_sz; - connector.Min.y += grab_sz*0.3f; - connector.Max.x -= grab_sz; - connector.Max.y -= grab_sz*0.3f; - - window->DrawList->AddRectFilled(connector.Min, connector.Max, GetColorU32(ImGuiCol_SliderGrab), style.GrabRounding); - - return value_changed; -} - -// ~95% common code with ImGui::SliderFloat -bool RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, float v_max, const char* display_format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - - const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); - if (hovered) - SetHoveredID(id); - - if (!display_format) - display_format = "(%.3f, %.3f)"; - int decimal_precision = ImParseFormatPrecision(display_format, 3); - - // Tabbing or CTRL-clicking on Slider turns it into an input box - bool start_text_input = false; - if (hovered && g.IO.MouseClicked[0]) - { - SetActiveID(id, window); - FocusWindow(window); - - if (g.IO.KeyCtrl) - { - start_text_input = true; - g.TempInputId = 0; - } - } - - if (start_text_input || (g.ActiveId == id && g.TempInputId == id)) - { - char fmt[64]; - snprintf(fmt, 64, "%%.%df", decimal_precision); - return TempInputScalar(frame_bb, id, label, ImGuiDataType_Float, v1, fmt); - } - - ItemSize(total_bb, style.FramePadding.y); - - // Actual slider behavior + render grab - const bool value_changed = RangeSliderBehavior(frame_bb, id, v1, v2, v_min, v_max, power, decimal_precision, 0); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v1, *v2); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -} // namespace ImGui diff --git a/samples/sdp-cli/code/main/helpers/imgui_extensions.hpp b/samples/sdp-cli/code/main/helpers/imgui_extensions.hpp deleted file mode 100644 index 9b5664f..0000000 --- a/samples/sdp-cli/code/main/helpers/imgui_extensions.hpp +++ /dev/null @@ -1,104 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include "imgui.h" -#include "implot.h" -#include "implot_internal.h" -#include - -namespace ImGui -{ - IMGUI_API bool RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, float v_max, const char* display_format = "(%.3f, %.3f)", float power = 1.0f); - -} // namespace ImGui - -namespace ImPlot -{ - // double (*PlotTooltipGetter)(const PlotTooltipEntry& entry, int idx, void* user_data) - - template - int BinarySearch(const PlotTooltipEntry& entry, PlotTooltipGetter entry_value_getter, void* user_data, int l, int r, double x, double epsilon) - { - if (r >= l) { - int mid = l + (r - l) / 2; - if (std::fabs(entry_value_getter(entry, mid, user_data) - x) < epsilon) - return mid; - if (entry_value_getter(entry, mid, user_data) > x) - return BinarySearch(entry, entry_value_getter, user_data, l, mid - 1, x, epsilon); - return BinarySearch(entry, entry_value_getter, user_data, mid + 1, r, x, epsilon); - } - return -1; - } - - template - void PlotTooltip( - const char* label_id, - PlotTooltipEntryGroup entry_groups, - PlotTooltipGetter entry_value_getter, - int value_count, - double epsilon, - void* user_data = nullptr, - float width_percent = 0.25f) - { - if (entry_groups.empty()) - { - return; - } - - const auto& reference_entry = entry_groups.back(); - - // get ImGui window DrawList - ImDrawList* draw_list = ImPlot::GetPlotDrawList(); - - // calc real value width - double half_width = value_count > 1 ? (entry_value_getter(reference_entry, 1, user_data) - entry_value_getter(reference_entry, 0, user_data)) * width_percent : width_percent; - - // custom tool - if (ImPlot::IsPlotHovered()) - { - ImPlotPoint mouse = ImPlot::GetPlotMousePos(); - // mouse.x = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble(); - float tool_l = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x; - float tool_r = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x; - float tool_t = ImPlot::GetPlotPos().y; - float tool_b = tool_t + ImPlot::GetPlotSize().y; - ImPlot::PushPlotClipRect(); - draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64)); - ImPlot::PopPlotClipRect(); - // find mouse location index - - int idx = BinarySearch(reference_entry, entry_value_getter, user_data, 0, value_count - 1, mouse.x, epsilon); - // render tool tip (won't be affected by plot clip rect) - if (idx != -1) - { - ImGui::BeginTooltip(); - - for (auto& entry : entry_groups) - { - const auto value = entry_value_getter(entry, idx, user_data); - - ImGui::Text("Day: $%.2f", static_cast(value)); - } - -#if 0 - char buff[32]; - ImPlot::FormatDate(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotDateFmt_DayMoYr,ImPlot::GetStyle().UseISO8601); - ImGui::Text("Day: %s", buff); - ImGui::Text("Open: $%.2f", opens[idx]); - ImGui::Text("Close: $%.2f", closes[idx]); - ImGui::Text("Low: $%.2f", lows[idx]); - ImGui::Text("High: $%.2f", highs[idx]); -#endif - ImGui::EndTooltip(); - } - } - } - - -} // namespace ImPlot \ No newline at end of file diff --git a/samples/sdp-cli/code/main/helpers/module_interface.cpp b/samples/sdp-cli/code/main/helpers/module_interface.cpp deleted file mode 100644 index f631e7f..0000000 --- a/samples/sdp-cli/code/main/helpers/module_interface.cpp +++ /dev/null @@ -1,100 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "module_interface.hpp" -#include "portable-file-dialogs/portable-file-dialogs.h" -#include - -ModuleInterface::ModuleInterface(std::string device_name) - : m_device_name(std::move(device_name)) -{ -} - -void ModuleInterface::RemoveToolFromDevice() -{ -} - -void ModuleInterface::Reset() -{ - m_recording_time = 0.0; - m_active_time = 0.0; -} - -void ModuleInterface::Pause() -{ - m_previous_state = m_state; - m_state = ModuleState::PAUSED; -} - -void ModuleInterface::Stop() -{ - Reset(); - m_previous_state = m_state; - m_state = ModuleState::STOPPED; - m_recording_time = 0.0; - m_active_time = 0.0; -} - -void ModuleInterface::Record() -{ - Reset(); - m_previous_state = m_state; - m_state = ModuleState::RECORDING; - m_recording_time = 0.0; -} - -void ModuleInterface::Resume() -{ - const auto previous_state_local = m_previous_state; - m_previous_state = m_state; - - switch (previous_state_local) - { - case ModuleState::RECORDING: - m_state = ModuleState::RECORDING; - break; - - case ModuleState::ACTIVE: - m_state = ModuleState::ACTIVE; - break; - - default: - Reset(); - m_state = ModuleState::STOPPED; - break; - } -} - -void ModuleInterface::Update(float time_elapsed) -{ - if (m_state == ModuleState::RECORDING) - { - m_recording_time += time_elapsed; - m_active_time += time_elapsed; - } - else if (m_state == ModuleState::ACTIVE) - { - m_active_time += time_elapsed; - } -} - -void ModuleInterface::Draw(float time_elapsed) -{ -} - -std::string ModuleInterface::RequestFileSelection(std::string_view file_name) -{ - auto open_file_query = pfd::open_file(std::string(file_name), pfd::path::home()); - const auto selection = open_file_query.result(); - if (selection.empty()) - { - return std::string(); - } - - return selection[0]; -} \ No newline at end of file diff --git a/samples/sdp-cli/code/main/helpers/module_interface.hpp b/samples/sdp-cli/code/main/helpers/module_interface.hpp deleted file mode 100644 index 16c013c..0000000 --- a/samples/sdp-cli/code/main/helpers/module_interface.hpp +++ /dev/null @@ -1,100 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include "json/include/nlohmann/json_fwd.hpp" -#undef ERROR - -enum ModuleState -{ - ACTIVE, - RECORDING, // Also implies on ACTIVE - PAUSED, - STOPPED, - ERROR, - VIEW_ONLY, // Normally when loaded from disk, implies on STOPPED -}; - -class ModuleInterface -{ -public: - - ModuleInterface(std::string device_name); - virtual ~ModuleInterface() = default; - - /* - */ - virtual const char* GetModuleName() = 0; - - /* - * Initialize this module - */ - virtual bool Initialize() = 0; - - /* - * De-inject the tool, if any - * This module should be shutdown afterwards since there are no guarantees it can continue - * to support requests - */ - virtual void RemoveToolFromDevice(); - - /* - */ - virtual void Reset(); - - /* - * Pause this module - */ - virtual void Pause(); - - /* - * Stop this module - */ - virtual void Stop(); - - /* - * Begin recording process - */ - virtual void Record(); - - /* - * Goes back to recording or active, depending on the previous operation mode - */ - virtual void Resume(); - - /* - */ - virtual void Update(float time_elapsed); - - /* - */ - virtual void Draw(float time_elapsed); - - /* - */ - inline ModuleState GetState() const - { - return m_state; - } - -protected: - - /* - */ - std::string RequestFileSelection(std::string_view file_name); - -protected: - - std::string m_device_name; - ModuleState m_state = ModuleState::ACTIVE; - ModuleState m_previous_state = ModuleState::ACTIVE; - float m_recording_time = 0.0; - float m_active_time = 0.0; -}; diff --git a/samples/sdp-cli/code/main/helpers/numerical_aggregator.hpp b/samples/sdp-cli/code/main/helpers/numerical_aggregator.hpp deleted file mode 100644 index a6469e0..0000000 --- a/samples/sdp-cli/code/main/helpers/numerical_aggregator.hpp +++ /dev/null @@ -1,173 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include -#include -#include -#include - -enum NumericalAggregatorValueType -{ - NUMERICAL_AGGREGATOR_CURRENT, - NUMERICAL_AGGREGATOR_AVERAGE, - NUMERICAL_AGGREGATOR_MIN, - NUMERICAL_AGGREGATOR_MAX, -}; - -template -class NumericalAggregator -{ -public: - NumericalAggregator() = default; - ~NumericalAggregator() = default; - - void AddEntry(_Ty entry, bool drop_zero = false) - { - if(drop_zero && entry == _Ty(0)) return; - - m_data.push_back(entry); - m_sum += entry; - m_min_val = std::min(m_min_val, entry); - m_max_val = std::max(m_max_val, entry); - } - - NumericalAggregator& operator=(const _Ty& entry) - { - AddEntry(entry); - Reset(true); - return *this; - } - - _Ty GetValue(NumericalAggregatorValueType value_type) const - { - switch (value_type) - { - case NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_CURRENT: return GetValue(); - case NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_AVERAGE: return GetAverage(); - case NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_MIN: return GetMin(); - case NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_MAX: return GetMax(); - default: return _Ty(0); - } - } - - // Get the last value added - _Ty GetValue() const - { - if (m_data.empty()) - { - return _Ty(0); - } - - return m_data.back(); - } - - // Get the average of all entries - _Ty GetAverage() const - { - return static_cast<_Ty>(static_cast(m_sum) / std::max(_Ty(1), _Ty(m_data.size()))); - } - - // Get the lowest value - _Ty GetMin() const - { - return m_min_val; - } - - // Get the maximum value - _Ty GetMax() const - { - return m_max_val; - } - - std::span GetStoredValues() - { - return m_data; - } - - std::size_t GetStoredValueCount() const - { - return m_data.size(); - } - - void MatchSize(std::size_t size_to_match) - { - const auto old_size = m_data.size(); - m_data.resize(size_to_match); - if (old_size < size_to_match) - { - std::memset(&m_data[old_size], 0, sizeof(_Ty) * (m_data.size() - old_size)); - } - } - - void MatchSamplingFrequency(int previous_sampling_period_ms, int current_sampling_period_ms) - { - assert(previous_sampling_period_ms != 0 && previous_sampling_period_ms != 0); - if (previous_sampling_period_ms == current_sampling_period_ms) - { - return; - } - - const double entry_ratio = static_cast(previous_sampling_period_ms) / current_sampling_period_ms; - const std::size_t new_size = m_data.size() * entry_ratio; - - std::vector<_Ty> new_data(new_size); - -#if 0 - if (previous_sampling_period_ms > current_sampling_period_ms) - { - for (int i=0; i::max(); - m_max_val = std::numeric_limits<_Ty>::min(); - } - - } - -private: - - std::vector<_Ty> m_data; - _Ty m_sum = _Ty(0); - _Ty m_min_val = std::numeric_limits<_Ty>::max(); - _Ty m_max_val = std::numeric_limits<_Ty>::min(); -}; diff --git a/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.cpp b/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.cpp deleted file mode 100644 index 6e309d3..0000000 --- a/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.cpp +++ /dev/null @@ -1,1352 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#include "sdp_cli.hpp" -#include "../../helpers/console_common.hpp" -#include "../../helpers/imgui_extensions.hpp" -#include "imgui.h" -#include "implot/implot.h" -#include -#include -#include - -namespace -{ - constexpr const char* DRAG_DROP_SECTION_NAME = "DRAG_DROP_SECTION"; - constexpr const char* SDP_CLI_DEVICE_PATH = "/data/local/tmp/sdpcli/sdpcli"; - - uint64_t ParseNumber(const std::string& input) - { - try - { - size_t idx; - uint64_t result = std::stoul(input, &idx, 10); - - if (idx < input.size()) - { - char suffix = input[idx]; - switch (suffix) - { - case 'k': - result *= 1000; - break; - case 'M': - result *= 1000000; - break; - // Add more cases for other suffixes if needed - default: - throw std::invalid_argument("Invalid suffix: " + std::string(1, suffix)); - } - } - return result; - - } - catch (...) - { - return 0; - } - } - - template - _Ty ExtractValueFromThermalInfo(const std::string& input, size_t N) - { - // Find the position of the first occurrence of '|' - size_t start_pos = input.find('|'); - if (start_pos == std::string::npos) - { - return _Ty(0); - } - - // Extract the substring after the first '|' - std::string values_str = input.substr(start_pos + 1); - std::replace(values_str.begin(), values_str.end(), '|', ' '); - - // Create a stringstream to tokenize the values - std::istringstream iss(values_str); - std::string token; - std::vector values; - - // Tokenize the values using space as delimiter - while (std::getline(iss, token, ' ')) - { - // Convert the token to an integer (or 0 if NA is encountered) - if (token.find("NA") != std::string::npos) - { - values.push_back(0); - } - else if (token.empty()) - { - continue; - } - else - { - try - { - values.push_back(std::stof(token)); - } - catch (const std::invalid_argument&) - { - return _Ty(0); - } - } - } - - // Check if N is within valid range - if (N >= values.size()) - { - return _Ty(0); - } - - // Return the Nth value - return static_cast<_Ty>(values[N]); - } -} - -SDP::SDP(std::string device_name) - : ModuleInterface(std::move(device_name)) -{ - std::memset(m_sdp_cli_path.data(), 0, m_sdp_cli_path.size()); - std::memset(m_profilling_layer_path.data(), 0, m_profilling_layer_path.size()); - - // See if the CLI is already present on the device so we can skip up setup phase - m_has_sdp_cli = DoesDeviceFileExist(m_device_name, SDP_CLI_DEVICE_PATH); -} - -SDP::~SDP() -{ -} - -const char* SDP::GetModuleName() -{ - return "SDP CLI"; -} - -bool SDP::Initialize() -{ - RefreshAsyncUpdateThread(); - - return true; -} - -void SDP::RemoveToolFromDevice() -{ - RunADBCommandForDevice( - m_device_name, - std::string("shell rm ") - .append(SDP_CLI_DEVICE_PATH)); -} - -void SDP::Reset() -{ - ModuleInterface::Reset(); - - std::lock_guard l(m_safety); - - for (auto& [category, map] : m_realtime_options_by_category) - { - for (auto& [key, entry] : map) - { - entry.value.Reset(true); - } - } - - FixDisabledGroupEntries(); -} - -void SDP::Pause() -{ - ModuleInterface::Pause(); -} - -void SDP::Stop() -{ - ModuleInterface::Stop(); -} - -void SDP::Record() -{ - ModuleInterface::Record(); -} - -void SDP::Resume() -{ - ModuleInterface::Resume(); -} - -void SDP::Update(float time_elapsed) -{ - ModuleInterface::Update(time_elapsed); -} - -void SDP::Draw(float time_elapsed) -{ - ModuleInterface::Draw(time_elapsed); - - if (!m_has_sdp_cli) - { - DrawSetupSection(time_elapsed); - return; - } - - if (!m_did_setup_supported_options && m_has_sdp_cli) - { - SetupRuntimeOptions(); - RefreshAsyncUpdateThread(); - m_did_setup_supported_options = true; - } - - ImGui::SetNextItemWidth(120); - const auto previous_sampling_period_ms = m_sampling_period_ms; - if(ImGui::DragInt("Sampling Period", &m_sampling_period_ms, 100.0f, 100, 1000)) - { - RefreshAsyncUpdateThread(previous_sampling_period_ms); - } - - ImGui::SameLine(); - ImGui::Text("|"); - ImGui::SameLine(); - - if (ImGui::Button("Reset")) - { - Reset(); - } - - ImGui::Separator(); - - if (ImGui::BeginTabBar("SDP CLI Module")) - { -#if 0 - if (ImGui::BeginTabItem("General")) - { - DrawGeneralSection(time_elapsed); - ImGui::EndTabItem(); - } -#endif - - if (ImGui::BeginTabItem("Runtime")) - { - DrawPlaygroundSection(time_elapsed); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } -} - -void SDP::FixDisabledGroupEntries() -{ - /* - * Here we orphan entries that are part of a group (2+ entries) but aren't selected in - * the options - * We basically detect such entries, add into the oprhan vector below and re-add them - * as their own separated groups, as if a merge never happened - */ - - std::pmr::vector orphan_entries(&m_memory_resource); - - for (auto& ordered_group : m_realtime_group_options_ordered) - { - for (int i = ordered_group.size()-1; i>=0; i--) - { - auto ordered_key = ordered_group[i]; - - for (auto& [category, map] : m_realtime_options_by_category) - { - auto iter = map.find(ordered_key); - if (iter == map.end()) - { - continue; - } - - if (!iter->second.selected && ordered_group.size() > 1) - { - orphan_entries.push_back(ordered_key); - ordered_group.erase(ordered_group.begin() + i); - } - } - } - } - - for(auto orphan_entry_key : orphan_entries) - { - m_realtime_group_options_ordered.push_back({orphan_entry_key}); - } -} - -void SDP::SetupRuntimeOptions() -{ - auto command_result = RunADBCommandForDevice(m_device_name, - std::string("shell ") - .append(SDP_CLI_DEVICE_PATH) - .append(" r l")); - - std::vector option_lines; - size_t pos = 0; - size_t previous_pos = 0; - while ((pos = command_result.find('\n', pos)) != std::string::npos) - { - option_lines.push_back(command_result.substr(previous_pos, pos - previous_pos)); - pos++; - previous_pos = pos; - } - - // Erase the header - option_lines.erase(option_lines.begin(), option_lines.begin() + 5); - - std::unordered_map name_to_index_mapping; - - for (size_t i = 0; i < option_lines.size(); ++i) - { - std::string metric_index_str = option_lines[i].substr(0, option_lines[i].find('.')); - - std::string metric_name = option_lines[i].substr(option_lines[i].find('.') + 1); - metric_name.erase(0, metric_name.find_first_not_of(" \t")); - metric_name.erase(metric_name.find_last_not_of(" \t") + 1); - - const auto metric_index = std::stoi(metric_index_str); - name_to_index_mapping.emplace(std::move(metric_name), metric_index); - } - - auto& default_category = m_realtime_options_by_category.emplace("Default", std::map()).first->second; - auto& stages_category = m_realtime_options_by_category.emplace("Shader Stages", std::map()).first->second; - auto& stage_processing = m_realtime_options_by_category.emplace("Shader Processing", std::map()).first->second; - auto& stalls_category = m_realtime_options_by_category.emplace("Stalls", std::map()).first->second; - auto& primitives_category = m_realtime_options_by_category.emplace("Primitive Processing", std::map()).first->second; - auto& instructions_category = m_realtime_options_by_category.emplace("Instructions", std::map()).first->second; - auto& texture_category = m_realtime_options_by_category.emplace("Stage Textures", std::map()).first->second; - auto& memory_category = m_realtime_options_by_category.emplace("Memory", std::map()).first->second; - auto& unknown_category = m_realtime_options_by_category.emplace("Unknown", std::map()).first->second; - - auto AddRealtimeOption = [&]( - auto& category, - std::string_view name, - std::string_view legend, - bool always_available, - bool is_percentage, - double scale, - bool deleted_from_local_map = true) - { - const auto string_name = std::string(name); // I wish std::string_view hash operator was the same as std::string - if(auto iter = name_to_index_mapping.find(string_name); iter != name_to_index_mapping.end()) - { - auto entry_index = iter->second; - auto result = category.emplace(entry_index, RealtimeOption(name, legend, entry_index, always_available, is_percentage, scale)); - auto& entry = result.first->second; - m_realtime_options_by_name.emplace(name, std::ref(entry)); - m_realtime_options_by_index.emplace(entry_index, std::ref(entry)); - - if (deleted_from_local_map) - { - name_to_index_mapping.erase(string_name); - } - } - }; - - /* - * TODO: The scale sometimes is not properly applied: - * Values are returned as 000.000, if the value is higher than 1k, the returned value will be - * like 1.022k, which might cause a wrong value to be displayed since we are currently just - * removing the "." from the output string - * Maybe this is correct, IDK, but I should re-evaluate to make sure - */ - - // Setup realtime entries - AddRealtimeOption(default_category, "Clocks / Second", "Clocks", false, false, 1.0); - AddRealtimeOption(default_category, "GPU % Utilization", "Percentage", false, true, 0.001); - AddRealtimeOption(default_category, "GPU % Bus Busy", "Percentage", false, true, 0.001); - AddRealtimeOption(stalls_category, "% BVH Fetch Stall", "Percentage", true, true, 0.001); - AddRealtimeOption(stalls_category, "% Vertex Fetch Stall", "Percentage", true, true, 0.001); - AddRealtimeOption(stalls_category, "% Texture Fetch Stall", "Percentage", true, true, 0.001); - AddRealtimeOption(stalls_category, "L1 Texture Cache Miss Per Pixel", "Count", false, false, 1.0); - AddRealtimeOption(stalls_category, "% Texture L1 Miss", "Percentage", false, true, 0.001); - AddRealtimeOption(stalls_category, "% Texture L2 Miss", "Percentage", false, true, 0.001); - AddRealtimeOption(stalls_category, "% Stalled on System Memory", "Percentage", true, true, 0.001); - AddRealtimeOption(stalls_category, "% Instruction Cache Miss", "Percentage", false, true, 0.001); - AddRealtimeOption(primitives_category, "Pre-clipped Polygons/Second", "Count", false, false, 1.0); - AddRealtimeOption(primitives_category, "% Prims Trivially Rejected", "Percentage", false, true, 0.001); - AddRealtimeOption(primitives_category, "% Prims Clipped", "Percentage", false, true, 0.001); - AddRealtimeOption(primitives_category, "Average Vertices / Polygon", "Count", false, false, 1.0); - AddRealtimeOption(primitives_category, "Reused Vertices / Second", "Count", false, false, 1.0); - AddRealtimeOption(primitives_category, "Average Polygon Area", "Average Area", false, false, 0.001); - AddRealtimeOption(stages_category, "% RTU Busy", "Percentage", false, true, 0.001); - AddRealtimeOption(instructions_category, "RTU Ray Box Intersections Per Instruction", "Intersections", false, false, 1.0); - AddRealtimeOption(instructions_category, "RTU Ray Triangle Intersections Per Instruction", "Intersections", false, false, 1.0); - AddRealtimeOption(stalls_category, "Average BVH Fetch Latency Cycles", "Cycles", false, false, 1.0); - AddRealtimeOption(stalls_category, "% Shaders Busy", "Percentage", true, true, 0.001); - AddRealtimeOption(stalls_category, "% Shaders Stalled", "Percentage", true, true, 0.001); - AddRealtimeOption(stage_processing, "Vertices Shaded / Second", "Count", false, false, 1.0); - AddRealtimeOption(stage_processing, "Fragments Shaded / Second", "Count", false, false, 1.0); - AddRealtimeOption(instructions_category, "Vertex Instructions / Second", "Instructions", false, false, 1.0); - AddRealtimeOption(instructions_category, "Fragment Instructions / Second", "Instructions", false, false, 1.0); - AddRealtimeOption(instructions_category, "Fragment ALU Instructions / Sec (Full)", "Instructions", false, false, 1.0); - AddRealtimeOption(instructions_category, "Fragment ALU Instructions / Sec (Half)", "Instructions", false, false, 1.0); - AddRealtimeOption(instructions_category, "Fragment EFU Instructions / Second", "Instructions", false, false, 1.0); - AddRealtimeOption(texture_category, "Textures / Vertex", "Count", false, false, 0.001); - AddRealtimeOption(texture_category, "Textures / Fragment", "Count", false, false, 0.001); - AddRealtimeOption(stage_processing, "ALU / Vertex", "Count", false, false, 1.0); - AddRealtimeOption(stage_processing, "ALU / Fragment", "Count", false, false, 1.0); - AddRealtimeOption(stage_processing, "EFU / Fragment", "Count", false, false, 1.0); - AddRealtimeOption(stage_processing, "EFU / Vertex", "Count", false, false, 1.0); - AddRealtimeOption(stages_category, "% Time Shading Fragments", "Percentage", true, true, 0.001); - AddRealtimeOption(stages_category, "% Time Shading Vertices", "Percentage", true, true, 0.001); - AddRealtimeOption(stages_category, "% Time Compute", "Percentage", true, true, 0.001); - AddRealtimeOption(stage_processing, "% Shader ALU Capacity Utilized", "Percentage", false, true, 0.001); - AddRealtimeOption(stages_category, "% Time ALUs Working", "Percentage", false, true, 0.001); - AddRealtimeOption(stages_category, "% Time EFUs Working", "Percentage", false, true, 0.001); - AddRealtimeOption(stage_processing, "% Nearest Filtered", "Percentage", false, true, 0.001); - AddRealtimeOption(stage_processing, "% Linear Filtered", "Percentage", false, true, 0.001); - AddRealtimeOption(stage_processing, "% Anisotropic Filtered", "Percentage", false, true, 0.001); - AddRealtimeOption(texture_category, "% Non-Base Level Textures", "Percentage", false, true, 0.001); - AddRealtimeOption(stages_category, "% Texture Pipes Busy", "Percentage", false, true, 0.001); - AddRealtimeOption(stage_processing, "% Wave Context Occupancy", "Percentage", false, true, 0.001); - AddRealtimeOption(memory_category, "Read Total (Bytes/sec)", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "Write Total (Bytes/sec)", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "Texture Memory Read BW (Bytes/Second)", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "Vertex Memory Read (Bytes/Second)", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "SP Memory Read (Bytes/Second)", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "Avg Bytes / Fragment", "Bytes", false, false, 1.0); - AddRealtimeOption(memory_category, "Avg Bytes / Vertex", "Bytes", false, false, 1.0); - AddRealtimeOption(default_category, "Preemptions / second", "Count", false, false, 1.0); - AddRealtimeOption(default_category, "Avg Preemption Delay", "Delay", false, false, 1.0); - AddRealtimeOption(default_category, "GPU Frequency", "Frequency", false, false, 1.0); - - // For each uncategorized entry, add it to the unknow category - for (auto& [entry_name, _] : name_to_index_mapping) - { - AddRealtimeOption(unknown_category, entry_name, "Count", false, false, 1.0, false); - } - - // Prepare the ordered vector, which contains an index for each of the options above - // and can be modified by the user to change how plots are visualized - for (auto& [category, map] : m_realtime_options_by_category) - { - for (auto& [key, entry] : map) - { - m_realtime_group_options_ordered.push_back({key}); - } - } -} - -float SDP::GetActiveTimeBySampleCountAndFrequency() const -{ - int sample_count = 0; - for(auto& [_, map] : m_realtime_options_by_category) - { - for(auto& [_, option] : map) - { - if(option.selected || option.always_available) - { - sample_count = option.value.GetStoredValueCount(); - break; - } - } - - if (sample_count > 0) - { - break; - } - } - - return static_cast(sample_count) / (1000.0f / static_cast (m_sampling_period_ms)); -} - -void SDP::RefreshAsyncUpdateThread(int previous_sampling_period_ms) -{ - /* - * NOTE: Don't call reset here, we don't wanna clear all graph contents but only adjust the disabled - * ones and "correct" the just enabled ones - */ - - std::lock_guard l(m_safety); - - /* - * Size Matching - * - * In order to not need to reset existing values whenever the cmd line changes, go over each active option - * and fill out missing entries until the time/entries match - * - * Also ensure to match the sampling frequency first in case values needs to be expanded or contracted - */ - { - const size_t sample_count_total = m_active_time / (static_cast(m_sampling_period_ms) / 1000.0); - - for (auto& [category, map] : m_realtime_options_by_category) - { - for (auto& [key, entry] : map) - { - if(entry.selected || entry.always_available) - { - if (previous_sampling_period_ms != 0) - entry.value.MatchSamplingFrequency(previous_sampling_period_ms, m_sampling_period_ms); - - entry.value.MatchSize(sample_count_total); - } - } - } - } - - FixDisabledGroupEntries(); - - /* - * Build command line: - * - * Prepare runtime continuous sample request, with m_sampling_period_ms as the period - * Go over each active runtime option and include its index - */ - - std::string console_command = - std::string("adb -s ") - .append(m_device_name) - .append(" shell ") - .append(SDP_CLI_DEVICE_PATH) - .append(" r c -p ") - .append(std::to_string(m_sampling_period_ms)) - .append(" "); - - bool is_first_option = true; - for (const auto& [_, entry_ref] : m_realtime_options_by_index) - { - const auto& entry = entry_ref.get(); - if(entry.always_available || entry.selected) - { - if (!is_first_option) - { - console_command.append(","); - } - - console_command.append(std::to_string(entry.index)); - - is_first_option = false; - } - } - - // New command line = new header incomming, flag that we need to filter all initial - // arguments again - m_console_ignore_header = true; - - m_console_helper.TerminateAsynchronousCommand(); - - m_console_helper.ExecuteAsynchronousCommand( - console_command.data(), - [&](std::string_view cmd_output) - { - ParseConsoleOutput(cmd_output); - }, - true /*not really needed but we know we don't need the first line, this option will eventually be reworked*/); -} - -void SDP::ParseConsoleOutput(std::string_view console_output) -{ - /* - * Quick and dirty parse for CLI output - * Can be optimized for sure - * - * The #ifdef section below correspond to the type of output we need to handle here - */ - -#if 0 - sdpcli - - Running in Realtime Metrics mode... - - Requested metrics: - Metric ID 2 is invalid and will be ignored. - Metric ID 4: GPU % Bus Busy - Metric ID 5 is invalid and will be ignored. - Metric ID 7: % Vertex Fetch Stall - Metric ID 9: L1 Texture Cache Miss Per Pixel - Metric ID 12: % Stalled on System Memory - Metric ID 14 is invalid and will be ignored. - - Itr | 4 | 7 | 9 | 12 | 15 | 17 | - 1 | 0.024 | 0.009 | 0.000 | 0.000 | 27.534 | 57.143 | - 2 | 0.097 | 0.009 | 0.000 | 0.000 | 27.468 | 57.143 | - 3 | 0.132 | 0.009 | 0.000 | 0.000 | 27.565 | 57.143 | - 4 | 0.111 | 0.009 | 0.000 | 0.000 | 27.555 | 57.143 | -#endif - - // Search for the last line of the header - // When found, we cache the info to avoid future str comparissions - if (m_console_ignore_header && console_output.find("Itr") == std::string::npos) - { - m_console_ignore_header = false; - return; - } - - // Find the position of the first occurrence of '|' - size_t start_pos = console_output.find('|'); - if (start_pos == std::string::npos) - { - return; - } - - // Extract the substring after the first '|' - std::string values_str = std::string(console_output.substr(start_pos + 1)); - std::replace(values_str.begin(), values_str.end(), '|', ' '); - std::erase(values_str, '.'); - - // Create a stringstream to tokenize the values - std::istringstream iss(values_str); - std::string token; - - for (auto& [_, entry_ref] : m_realtime_options_by_index) - { - auto& entry = entry_ref.get(); - if(!entry.selected && !entry.always_available) - { - continue; - } - - bool did_find_value = false; // Just a guard in case cmdline changes between versions, ensuring - // we don't end up with undefined behavior - while (std::getline(iss, token, ' ')) - { - if (token.empty() || token == "\n") - { - continue; - } - - entry.value.AddEntry(static_cast(static_cast(ParseNumber(token)) * entry.scale)); - did_find_value = true; - break; - } - - assert(did_find_value); - } -} - -void SDP::DrawSetupSection(double time_elapsed) -{ - ImGui::Text("Select SDP CLI File:"); - ImGui::InputTextWithHint("##sdp_cli", "SDP CLI File", m_sdp_cli_path.data(), m_sdp_cli_path.size(), ImGuiInputTextFlags_ReadOnly); - - ImGui::SameLine(); - - if (ImGui::Button("Select File##cli")) - { - auto selected_file = RequestFileSelection("SDP CLI"); - - if (selected_file.size() < m_sdp_cli_path.size()) - { - std::copy(selected_file.begin(), selected_file.end(), m_sdp_cli_path.begin()); - m_sdp_cli_path[selected_file.size()] = '\0'; - } - } - - ImGui::Text("Select Profilling Layer File APK:"); - ImGui::InputTextWithHint("##sdp_pl", "Profilling Layer File APK", m_profilling_layer_path.data(), m_profilling_layer_path.size(), ImGuiInputTextFlags_ReadOnly); - - ImGui::SameLine(); - - if (ImGui::Button("Select File##pl")) - { - auto selected_file = RequestFileSelection("Profilling Layer APK"); - - if (selected_file.size() < m_profilling_layer_path.size()) - { - std::copy(selected_file.begin(), selected_file.end(), m_profilling_layer_path.begin()); - m_profilling_layer_path[selected_file.size()] = '\0'; - } - } - - ImGui::BeginDisabled(!std::filesystem::exists(m_sdp_cli_path.data()) || !std::filesystem::exists(m_profilling_layer_path.data())); - - /* - * These commands below are what you would normally need to run in case you were to manually run SDP CLI - * We push the SDP CLI file into the device, give it permission and install the trace layer APK (not - * currently/yet used by this application) - */ - if (ImGui::Button("Inject")) - { - const auto adjusted_sdp_cli_path = std::string("\"").append(m_sdp_cli_path.data()).append("\""); - const auto adjusted_pl_path = std::string("\"").append(m_profilling_layer_path.data()).append("\""); - - // Push files - RunADBCommandForDevice(m_device_name, std::string("push ").append(adjusted_sdp_cli_path).append(" ").append(SDP_CLI_DEVICE_PATH)); - RunADBCommandForDevice(m_device_name, std::string("install ").append(adjusted_pl_path)); - - // Fix access - RunADBCommandForDevice(m_device_name, std::string("shell chmod a+x ").append(SDP_CLI_DEVICE_PATH)); - m_has_sdp_cli = DoesDeviceFileExist(m_device_name, SDP_CLI_DEVICE_PATH); - if (m_has_sdp_cli) - { - RefreshAsyncUpdateThread(); - } - } - - ImGui::EndDisabled(); -} - -void SDP::DrawGeneralSection(double time_elapsed) -{ - if(ImGui::CollapsingHeader("Shader Stages", ImGuiTreeNodeFlags_DefaultOpen)) - { - std::pmr::vector labels(&m_memory_resource); - std::pmr::vector data(&m_memory_resource); - - if (GetRuntimeShaderStageValues(NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_CURRENT, labels, data)) - { - ImPlotPieChartFlags flags = 0; - - // CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize); - // CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden); - - if (ImPlot::BeginPlot("GPU Usage per Stage", ImVec2(320,320), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) - { - ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(labels.data(), data.data(), labels.size(), 0.5, 0.5, 0.4, "%.2f", 90, flags); - ImPlot::EndPlot(); - } - } - } - - if(ImGui::CollapsingHeader("Shader Stalls", ImGuiTreeNodeFlags_DefaultOpen)) - { - std::pmr::vector busy_vs_stall_labels(&m_memory_resource); - std::pmr::vector busy_vs_stall_data(&m_memory_resource); - std::pmr::vector stalls_labels(&m_memory_resource); - std::pmr::vector stalls_data(&m_memory_resource); - - if (GetRuntimeShaderStallValues( - NumericalAggregatorValueType::NUMERICAL_AGGREGATOR_CURRENT, - busy_vs_stall_labels, - busy_vs_stall_data, - stalls_labels, - stalls_data)) - { - ImPlotPieChartFlags flags = 0; - - // CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize); - // CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden); - - if (ImPlot::BeginPlot("Busy vs Stall", ImVec2(320,320), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) - { - ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(busy_vs_stall_labels.data(), busy_vs_stall_data.data(), busy_vs_stall_labels.size(), 0.5, 0.5, 0.4, "%.2f", 90, flags); - ImPlot::EndPlot(); - } - - ImGui::SameLine(); - - - if (ImPlot::BeginPlot("Stalls Category", ImVec2(320,320), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) - { - ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(stalls_labels.data(), stalls_data.data(), stalls_labels.size(), 0.5, 0.5, 0.4, "%.2f", 90, flags); - ImPlot::EndPlot(); - } - } - } -} - -void SDP::DrawPlaygroundSection(double time_elapsed) -{ - if (ImGui::Button(m_playground_selector_visible ? "<" : ">")) - { - m_playground_selector_visible = !m_playground_selector_visible; - } - - const float options_width = 360; - const float separator_width = 4; - const auto initial_cursor_screen_pos = ImGui::GetCursorScreenPos(); - - if (m_playground_selector_visible) - { - if (ImGui::BeginChild("##runtime_options", ImVec2(options_width, 0))) - { - DrawPlaygroundOptions(time_elapsed); - } - ImGui::EndChild(); - - ImGui::SetCursorScreenPos(ImVec2(initial_cursor_screen_pos.x + options_width + 1 * separator_width, initial_cursor_screen_pos.y)); - ImGui::Button("##runtime_section_button", ImVec2(separator_width, ImGui::GetContentRegionAvail().y)); - ImGui::SetCursorScreenPos(ImVec2(initial_cursor_screen_pos.x + options_width + 3 * separator_width, initial_cursor_screen_pos.y)); - } - - if (ImGui::BeginChild("##runtime_plots", ImVec2(0, 0))) - { - DrawPlaygroundPlots(time_elapsed); - } - ImGui::EndChild(); -} - -void SDP::DrawPlaygroundOptions(double time_elapsed) -{ - bool did_change_any_options = false; - - for (auto& [category, map] : m_realtime_options_by_category) - { - if (map.empty()) - { - continue; - } - - if (ImGui::CollapsingHeader(category.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - { - for (auto& [key, entry] : map) - { - bool is_selected = entry.selected; - ImGui::PushID(entry.index); - ImGui::Checkbox("##selected", &is_selected); - ImGui::PopID(); - - did_change_any_options |= is_selected != entry.selected; - - entry.selected = is_selected; - - ImGui::SameLine(); - - if (ImGui::TreeNodeEx(entry.name.c_str(), ImGuiTreeNodeFlags_Leaf)) - { - // Debug raw value tooltip - if (ImGui::IsItemHovered() && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) - { - ImGui::SetTooltip("Raw value: %lu", entry.value.GetValue()); - } - - ImGui::TreePop(); - } - } - } - } - - if (did_change_any_options) - { - RefreshAsyncUpdateThread(); - } -} - -void SDP::DrawPlaygroundPlots(double time_elapsed) -{ - const auto local_active_time = GetActiveTimeBySampleCountAndFrequency(); - - if (m_playground_auto_scroll) - { - m_playground_history_range_end = local_active_time; - } - - ImGui::RangeSliderFloat( - "History", - &m_playground_history_range_begin, - &m_playground_history_range_end, - 0.0f, - local_active_time); - - ImGui::SameLine(); - - ImGui::Checkbox("Auto Scroll", &m_playground_auto_scroll); - - struct SampleGetterData - { - int sample_count = 0; - double sample_index_offset = 0.0; - double sample_ratio = 1.0; - RealtimeOption* entry = nullptr; - }; - - auto base_getter = [](int idx, void* data) - { - const SampleGetterData& getter_data = *static_cast(data); - return ImPlotPoint((idx / getter_data.sample_ratio), 0); - }; - - auto target_getter = [](int idx, void* data) - { - const SampleGetterData& getter_data = *static_cast(data); - const auto index_offset = static_cast(static_cast(idx) * getter_data.sample_index_offset); - auto stored_values = getter_data.entry->value.GetStoredValues(); - - assert(index_offset < stored_values.size()); - - return ImPlotPoint((idx / getter_data.sample_ratio), stored_values[index_offset]); - }; - - auto CountAxisFormatter = [](double value, char* buff, int size, void* user_data) - { - const char* suffix = ""; - if (value >= 1000000.0) - { - value /= 1000000.0; - suffix = "M"; - } - else if (value >= 1000.0) - { - value /= 1000.0; - suffix = "k"; - } - - snprintf(buff, size, "%llu%s", static_cast(value), suffix); - - return static_cast(value); - }; - - auto PercentageAxisFormatter = [](double value, char* buff, int size, void* user_data) - { - snprintf(buff, size, "%llu%%", static_cast(value)); - return static_cast(value); - }; - - auto DrawRuntimeOptionPlot = [&]( - std::span> entries, - PlotSettings plot_settings, - bool histogram_active) -> std::optional - { - std::optional ungroup_value; - - assert(!entries.empty()); - - // Use the first entry for all the base references needed below - const auto& reference_entry = entries[0].get(); - - // Grab the sample count, which is the same for all entries since we match missing values whenever - // an option is added or removed - const auto sample_count = reference_entry.value.GetStoredValueCount(); - const auto sample_count_by_active_time_ratio = static_cast(sample_count) / std::max(1.0f, local_active_time); - - ImGui::PushID(reference_entry.name.c_str()); - if (ImPlot::BeginPlot("##runtime_plot_entry", ImVec2(-1,150))) - { - const double horizontal_limit = (std::max(3.0f, sample_count - 3.0f)); - - // Find out the Y axis limits and if they are all percentage - double plot_y_min = 0.0f; - double plot_y_max = 1.0f; - bool are_all_percentage = entries[0].get().is_percentage; - for (auto& entry : entries) - { - plot_y_min = std::min(plot_y_min, static_cast(entry.get().value.GetMin())); - plot_y_max = std::max(plot_y_max, static_cast(entry.get().value.GetMax())); - are_all_percentage &= entry.get().is_percentage; - } - - // If all values on this group are percentage values (0% - 100%), ensure the limits are properly set - plot_y_min = are_all_percentage ? 0 : plot_y_min; - plot_y_max = are_all_percentage ? 100 : plot_y_max; - const auto limit_padding = std::abs(plot_y_max - plot_y_min) * 0.1f; - - if (!histogram_active) - { - ImPlot::SetupAxes("Time", nullptr, 0, are_all_percentage ? 0 : ImPlotAxisFlags_AutoFit); - ImPlot::SetupAxisLimits( - ImAxis_X1, - m_playground_history_range_begin, - m_playground_history_range_end, - ImGuiCond_Always); - - /* - * Setup Y axis legend and name - */ - if (!are_all_percentage) - { - if (entries.size() == 1) - { - ImPlot::SetupAxis(ImAxis_Y1, reference_entry.legend.c_str()); - } - - ImPlot::SetupAxisFormat(ImAxis_Y1, CountAxisFormatter, nullptr); - } - else - { - ImPlot::SetupAxisFormat(ImAxis_Y1, PercentageAxisFormatter, nullptr); - } - - ImPlot::SetupAxisLimits(ImAxis_Y1, plot_y_min - limit_padding, plot_y_max + limit_padding, ImGuiCond_Always); - - for (int i = 0; i < entries.size(); i++) - { - auto& entry = entries[i]; - auto entry_values = entry.get().value.GetStoredValues(); - - SampleGetterData getter_data; - getter_data.sample_count = std::min(1000, static_cast(entry_values.size())); // Max of 1000 samples - getter_data.sample_index_offset = static_cast(entry_values.size()) / std::max(1, getter_data.sample_count); - getter_data.sample_ratio = sample_count_by_active_time_ratio; - getter_data.entry = &entry.get(); - - switch (entry.get().graph_type) - { - case GraphType::LINE: - ImPlot::PlotLineG(entry.get().name.c_str(), target_getter, &getter_data, getter_data.sample_count); - break; - - case GraphType::SHADE: - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotShadedG(entry.get().name.c_str(), base_getter, &getter_data, target_getter, &getter_data, getter_data.sample_count); - break; - } - - if (ImPlot::IsPlotHovered()) - { - const auto plot_mouse_values = ImPlot::GetPlotMousePos(); - - /* - * Do horizontal graph scrolling if the mouse wheel changed - */ - const float range_difference = m_playground_history_range_end - m_playground_history_range_begin; - float mouse_horizontal_range_factor = static_cast(plot_mouse_values.x) - m_playground_history_range_begin; - mouse_horizontal_range_factor = mouse_horizontal_range_factor / std::max(0.001f, range_difference); - const float range_begin_factor = mouse_horizontal_range_factor; - const float range_end_factor = 1.0f - mouse_horizontal_range_factor; - const float mouse_horizontal_range_scale = 0.05f; - const float range_begin_value = range_begin_factor * (range_difference * mouse_horizontal_range_scale); - const float range_end_value = range_end_factor * (range_difference * mouse_horizontal_range_scale); - - m_playground_history_range_begin -= ImGui::GetIO().MouseWheel * range_begin_value; - m_playground_history_range_end += m_playground_auto_scroll ? 0.0f : ImGui::GetIO().MouseWheel * range_end_value; - - m_playground_history_range_begin = std::max(m_playground_history_range_begin, 0.0f); - m_playground_history_range_end = std::min(m_playground_history_range_end, GetActiveTimeBySampleCountAndFrequency()); - } - - if (ImPlot::IsLegendEntryHovered(entry.get().name.c_str()) && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) - { - // Right click with CTRL = swap between graph types - // Right click without CTRL = ungroup - if (ImGui::IsKeyDown(ImGuiKey_RightCtrl) || ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) - entry.get().graph_type = entry.get().graph_type == GraphType::LINE ? GraphType::SHADE : GraphType::LINE; - else - ungroup_value = i; - } - } - - if (ImPlot::IsPlotHovered()) - { - /* - * Do tooltip draw at mouse screen pos - */ - - // double (*PlotTooltipGetter)(const PlotTooltipEntry& entry, int idx, void* user_data) - - // The problem is the binary search -> If I get the positional value from the mouse, I should be able - // to infer into the IDX directly without doing the bin search - - auto PlotTooltipGetter = [](const std::reference_wrapper& entry, int idx, void* user_data) -> double - { - auto entry_values = entry.get().value.GetStoredValues(); - assert(idx < entry_values.size()); - return static_cast(entry_values[idx]); - }; - - ImPlot::PlotTooltip( - "Test", - entries, - PlotTooltipGetter, - sample_count, - 0.1); - - // TODO - } - } - else - { - assert(!entries.empty()); - auto& entry = entries.back().get(); - auto entry_values = entry.value.GetStoredValues(); - - static const ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; - - ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotHistogram( - entry.name.c_str(), - entry_values.data(), - entry_values.size(), - ImPlotBin_Sturges, - 1.0, - ImPlotRange(entry.value.GetMin(), entry.value.GetMax()), - hist_flags); - } - - ImPlot::EndPlot(); - } - - ImGui::PopID(); - - return ungroup_value; - }; - - // Handle move requests from drag and drop - // int (1) - From - // int (2) - To - // bool - If true, means from should be merged with to, if false, from should be moved to to location - std::optional> move_request; - - for (int i = 0; i < m_realtime_group_options_ordered.size(); i++) - { - auto& ordered_group = m_realtime_group_options_ordered[i]; - - std::pmr::vector> entry_options_vector(&m_memory_resource); - std::pmr::vector entry_index_vector(&m_memory_resource); - - for (int j = 0; j < ordered_group.size(); j++) - { - auto ordered_key = ordered_group[j]; - for (auto& [category, map] : m_realtime_options_by_category) - { - auto iter = map.find(ordered_key); - if(iter == map.end()) - { - continue; - } - - if (iter->second.selected) - { - entry_options_vector.push_back(iter->second); - entry_index_vector.push_back(j); - } - } - } - - if (entry_options_vector.empty()) - { - continue; - } - - // Setup the section string - std::pmr::string section_string(&m_memory_resource); - - for (auto& entry : entry_options_vector) - { - section_string.append(section_string.empty() ? "" : " | "); - section_string.append(entry.get().name); - } - - // Returns true if the drop area is visible - auto SetupDragDropArea = [&move_request](int current_index, bool is_group) -> bool - { - if (ImGui::BeginDragDropSource()) - { - ImGui::SetDragDropPayload(DRAG_DROP_SECTION_NAME, ¤t_index, sizeof(int)); - ImGui::EndDragDropSource(); - } - - // Only render the drop target area if indexes are not the same - if(const ImGuiPayload* payload = ImGui::GetDragDropPayload(); payload && payload->IsDataType(DRAG_DROP_SECTION_NAME)) - { - if(auto from_index = *(const int*)payload->Data; from_index != current_index) - { - if (ImGui::BeginDragDropTarget()) - { - if(ImGui::AcceptDragDropPayload(DRAG_DROP_SECTION_NAME, 0)) - { - move_request = std::make_tuple(from_index, current_index, is_group); - } - - ImGui::EndDragDropTarget(); - - return true; - } - } - } - - return false; - }; - - if (ImGui::CollapsingHeader(section_string.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) - { - assert(!entry_options_vector.empty()); - const auto* reference_entry = &entry_options_vector.back().get(); - const auto local_id = ImGui::GetID(reference_entry); - ImGuiStorage* storage = ImGui::GetStateStorage(); - PlotSettings plot_settings = static_cast(storage->GetInt(local_id)); - const bool can_use_histogram = entry_options_vector.size() == 1; - - // Check if histogram mode is active, also adjust it in case it shouldn't - bool histogram_active = can_use_histogram ? (plot_settings & PLOT_HISTOGRAM) : false; - - ImGui::BeginDisabled(!can_use_histogram); - ImGui::PushStyleColor(ImGuiCol_Button, histogram_active ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); - ImGui::PushID(local_id); - if (ImGui::Button("histogram")) - { - histogram_active = !histogram_active; - } - ImGui::PopID(); - ImGui::PopStyleColor(); - ImGui::EndDisabled(); - - // Commit all changes back to the plot settings variable for saving - plot_settings = static_cast(plot_settings & ~PLOT_HISTOGRAM); - plot_settings = static_cast(plot_settings & ~PLOT_DEFAULT); - plot_settings = static_cast(histogram_active ? plot_settings | PLOT_HISTOGRAM : plot_settings | PLOT_DEFAULT); - - // Save back the group plot settings - storage->SetInt(local_id, static_cast(plot_settings)); - - // Draw plot for the entire group - // Only proceed with ungroup requests if there are multiple entries on this group - if (auto ungroup_index = DrawRuntimeOptionPlot(entry_options_vector, plot_settings, histogram_active); ungroup_index && ordered_group.size() > 1) - { - // Since we are processing in order and ordered_group is done processing, we can freely extract - // its entry and add right after the active one at m_realtime_group_options_ordered - auto extracted_entry = std::move(ordered_group[entry_index_vector[ungroup_index.value()]]); - ordered_group.erase(ordered_group.begin() + entry_index_vector[ungroup_index.value()]); - m_realtime_group_options_ordered.insert(m_realtime_group_options_ordered.begin() + i + 1, {std::move(extracted_entry)}); - } - - SetupDragDropArea(i, true); - } - - /* - * Draw an invisible button as a drop target, so plots can be re-ordered around - * While the drag/drop setup returns false, we return the cursor back to the area it was before the - * invisible button to not show any spaces between plots, if the mouse is hovering that area we - * keep the cursor where its - */ - const auto drag_area_begin = ImGui::GetCursorScreenPos(); - ImGui::InvisibleButton("##drag_area_after_group", ImVec2(ImGui::GetContentRegionAvail().x, 16)); - if (!SetupDragDropArea(i, false)) - { - ImGui::SetCursorScreenPos(drag_area_begin); - } - } - - if (move_request) - { - auto& [from_index, to_index, merge] = move_request.value(); - - if (merge) - { - m_realtime_group_options_ordered[to_index].insert( - m_realtime_group_options_ordered[to_index].end(), - m_realtime_group_options_ordered[from_index].begin(), - m_realtime_group_options_ordered[from_index].end() ); - m_realtime_group_options_ordered.erase(m_realtime_group_options_ordered.begin() + from_index); - } - else - { - // Might cause memory reallocation, we don't care, rarelly happens :) - auto entry = std::move(m_realtime_group_options_ordered[from_index]); - m_realtime_group_options_ordered.erase(m_realtime_group_options_ordered.begin() + from_index); - m_realtime_group_options_ordered.insert(m_realtime_group_options_ordered.begin() + to_index + 1, std::move(entry)); - } - } -} - -bool SDP::GetRuntimeShaderStallValues( - NumericalAggregatorValueType aggregator_visualizer_type, - std::pmr::vector& busy_vs_stall_labels, - std::pmr::vector& busy_vs_stall_data, - std::pmr::vector& stalls_labels, - std::pmr::vector& stalls_data) -{ - auto TryFindEntry = [&](auto& entry) -> std::optional - { - if (auto iter = m_realtime_options_by_name.find(entry); iter != m_realtime_options_by_name.end()) - { - const auto& entry = iter->second.get(); - return static_cast(entry.value.GetValue(aggregator_visualizer_type)); - } - - return std::nullopt; - }; - - auto EmplaceEntry = [&](auto& entry, auto& target_label_vector, auto& target_data_vector) - { - if (auto iter = m_realtime_options_by_name.find(entry); iter != m_realtime_options_by_name.end()) - { - const auto& entry = iter->second.get(); - target_label_vector.push_back(entry.name.c_str()); - target_data_vector.push_back(static_cast(entry.value.GetValue(aggregator_visualizer_type))); - } - }; - - /* - * Values are hardcoded and unlikely to change - * If SDP CLI values do start to change frequently between different versions, better to fetch require entries - * in another way - */ - - auto shaders_busy_value = TryFindEntry("% Shaders Busy"); - auto shaders_stalled_value = TryFindEntry("% Shaders Stalled"); - - EmplaceEntry("% Vertex Fetch Stall", stalls_labels, stalls_data); - EmplaceEntry("% Texture Fetch Stall", stalls_labels, stalls_data); - EmplaceEntry("% Stalled on System Memory", stalls_labels, stalls_data); - - if (!shaders_busy_value || !shaders_stalled_value) - { - return false; - } - - // We know stalls are a percentage of busy, so lets normalize it: - shaders_busy_value = std::max(0, shaders_busy_value.value() - shaders_stalled_value.value()); - - busy_vs_stall_labels.push_back("% Shaders Busy"); - busy_vs_stall_labels.push_back("% Shaders Stalled"); - busy_vs_stall_labels.push_back("% Shaders Idle"); - busy_vs_stall_data.push_back(shaders_busy_value.value()); - busy_vs_stall_data.push_back(shaders_stalled_value.value()); - busy_vs_stall_data.push_back(100 - std::min(100, (shaders_busy_value.value() + shaders_stalled_value.value()))); - - /* - * Since we want the ratio between all stall options, accumulate the value and - * then normalize between all options - * - * If no stall is detected, add the "unknow" category - */ - - float stall_total_measured = 0; - for (auto& value : stalls_data) - { - stall_total_measured += std::max(0.0f, static_cast(value)); - } - - const float stall_ratio = stall_total_measured / 100.0f; - for (auto& value : stalls_data) - { - value = static_cast(value / std::max(0.0001f, stall_ratio)); - } - - stalls_labels.push_back("Unknown"); // Static string ref -> safe - stalls_data.push_back(std::max(0, stall_total_measured == 0.0f ? 100 : 0)); - - return busy_vs_stall_data.size() >= 2 && !stalls_data.empty(); -} -bool SDP::GetRuntimeShaderStageValues( - NumericalAggregatorValueType aggregator_visualizer_type, - std::pmr::vector& labels, - std::pmr::vector& data) -{ - auto EmplaceEntry = [&](auto& entry) - { - if (auto iter = m_realtime_options_by_name.find(entry); iter != m_realtime_options_by_name.end()) - { - const auto& entry = iter->second.get(); - labels.push_back(entry.name.c_str()); - data.push_back(static_cast(entry.value.GetValue(aggregator_visualizer_type))); - } - }; - - /* - * Values are hardcoded and unlikely to change - * If SDP CLI values do start to change frequently between different versions, better to fetch require entries - * in another way - */ - EmplaceEntry("% Time Shading Fragments"); - EmplaceEntry("% Time Shading Vertices"); - EmplaceEntry("% Time Compute"); - - int total_measured = 0; - for (auto& value : data) - { - total_measured += value; - } - - // If GPU isn't fully busy, add "other" option - if (total_measured < 100) - { - labels.push_back("Other"); // Static string ref -> safe - data.push_back(std::max(0, 100 - total_measured)); - } - - return data.size() >= 3; -} \ No newline at end of file diff --git a/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.hpp b/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.hpp deleted file mode 100644 index 0a488f4..0000000 --- a/samples/sdp-cli/code/main/modules/sdp_cli/sdp_cli.hpp +++ /dev/null @@ -1,216 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../helpers/module_interface.hpp" -#include "../../helpers/console_helper.hpp" -#include "../../helpers/numerical_aggregator.hpp" - -class SDP : public ModuleInterface -{ - enum class GraphType - { - LINE, - SHADE, - }; - - enum PlotSettings - { - PLOT_DEFAULT = 1 << 0, // Individual graphs at the same plot, conflicts with HISTOGRAM - PLOT_HISTOGRAM = 1 << 1, // Only if there is a single graph on the plot, conflicts with DEFAULT - }; - - struct RealtimeOption - { - RealtimeOption( - std::string_view in_name, - std::string_view in_legend, - int in_index, - bool in_always_available, - bool in_is_percentage, - double in_scale) - : name(in_name) - , legend(in_legend) - , index(in_index) - , always_available(in_always_available) - , is_percentage(in_is_percentage) - , scale(in_scale) - { - } - ~RealtimeOption() = default; - - std::string name; - std::string legend; - int index = 0; - bool selected = false; - bool always_available = false; - bool is_percentage = false; - double scale = 1.0; - NumericalAggregator value; - GraphType graph_type = GraphType::LINE; - }; - -public: - SDP(std::string device_name); - virtual ~SDP() final; - -///////////////////////////// -public: // ModuleInterface // -///////////////////////////// - - /* - */ - virtual const char* GetModuleName() final; - - /* - * Initialize this module - */ - virtual bool Initialize() final; - - /* - * De-inject the tool, if any - * This module should be shutdown afterwards since there are no guarantees it can continue - * to support requests - */ - virtual void RemoveToolFromDevice() final; - - /* - */ - virtual void Reset() final; - - /* - * Pause this module - */ - virtual void Pause() final; - - /* - * Stop this module - */ - virtual void Stop() final; - - /* - * Begin recording process - */ - virtual void Record() final; - - /* - * Goes back to recording or active, depending on the previous operation mode - */ - virtual void Resume() final; - - /* - */ - virtual void Update(float time_elapsed) final; - - /* - */ - virtual void Draw(float time_elapsed) final; - -///////////////////////////// -public: ///////////////////// -///////////////////////////// - -private: - - /* - */ - float GetActiveTimeBySampleCountAndFrequency() const; - - /* - */ - void RefreshAsyncUpdateThread(int previous_sampling_period_ms = 0); - - /* - */ - void FixDisabledGroupEntries(); - - /* - */ - void SetupRuntimeOptions(); - - /* - */ - void ParseConsoleOutput(std::string_view console_output); - - /* - */ - void DrawSetupSection(double time_elapsed); - - /* - */ - void DrawGeneralSection(double time_elapsed); - - /* - */ - void DrawPlaygroundSection(double time_elapsed); - - /* - */ - void DrawPlaygroundOptions(double time_elapsed); - - /* - */ - void DrawPlaygroundPlots(double time_elapsed); - -//////////////////////////////////// -private: // SPECIALIZATION GROUPS // -//////////////////////////////////// - - /* - */ - bool GetRuntimeShaderStallValues( - NumericalAggregatorValueType aggregator_visualizer_type, - std::pmr::vector& busy_vs_stall_labels, - std::pmr::vector& busy_vs_stall_data, - std::pmr::vector& stalls_labels, - std::pmr::vector& stalls_data); - - /* - */ - bool GetRuntimeShaderStageValues( - NumericalAggregatorValueType aggregator_visualizer_type, - std::pmr::vector& labels, - std::pmr::vector& data); - -//////////////////////////////////// -private: /////////////////////////// -//////////////////////////////////// - - std::mutex m_safety; - ConsoleHelper m_console_helper; - bool m_console_ignore_header = true; - bool m_has_sdp_cli = false; - bool m_did_setup_supported_options = false; - std::array m_sdp_cli_path; - std::array m_profilling_layer_path; - int m_sampling_period_ms = 1000; - - // Playground stuff - bool m_playground_selector_visible = true; - bool m_playground_auto_scroll = true; - float m_playground_history_range_begin = 0.0f; // Seconds - float m_playground_history_range_end = 10.0f; // Seconds - - std::pmr::unsynchronized_pool_resource m_memory_resource; - - std::map> m_realtime_options_by_category; - std::unordered_map> m_realtime_options_by_name; - std::unordered_map> m_realtime_options_by_index; - std::vector> m_realtime_group_options_ordered; - - // NumericalAggregatorValueType m_aggregator_visualizer_type = NUMERICAL_AGGREGATOR_CURRENT; -}; diff --git a/samples/sdp-cli/img/screenshot.png b/samples/sdp-cli/img/screenshot.png deleted file mode 100644 index 5aa1ee4..0000000 Binary files a/samples/sdp-cli/img/screenshot.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/mipmap-hdpi/ic_launcher.png b/samples/sdp-cli/project/android/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a571e60..0000000 Binary files a/samples/sdp-cli/project/android/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/mipmap-mdpi/ic_launcher.png b/samples/sdp-cli/project/android/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c41dd28..0000000 Binary files a/samples/sdp-cli/project/android/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/mipmap-xhdpi/ic_launcher.png b/samples/sdp-cli/project/android/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 6dba46d..0000000 Binary files a/samples/sdp-cli/project/android/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/mipmap-xxhdpi/ic_launcher.png b/samples/sdp-cli/project/android/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 15ac681..0000000 Binary files a/samples/sdp-cli/project/android/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/mipmap-xxxhdpi/ic_launcher.png b/samples/sdp-cli/project/android/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index f25a419..0000000 Binary files a/samples/sdp-cli/project/android/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/sdp-cli/project/android/res/values/strings.xml b/samples/sdp-cli/project/android/res/values/strings.xml deleted file mode 100644 index c53250b..0000000 --- a/samples/sdp-cli/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Empty - diff --git a/samples/sgsr/01_CompileShaders.bat b/samples/sgsr/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/sgsr/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/sgsr/02_Install_APK.bat b/samples/sgsr/02_Install_APK.bat deleted file mode 100644 index f102c79..0000000 --- a/samples/sgsr/02_Install_APK.bat +++ /dev/null @@ -1,18 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -adb uninstall com.quic.sgsr - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\sgsr\outputs\apk\debug\sgsr-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\sgsr\outputs\apk\debug\sgsr-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/sgsr/03_InstallConfig.bat b/samples/sgsr/03_InstallConfig.bat deleted file mode 100644 index f666222..0000000 --- a/samples/sgsr/03_InstallConfig.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off - -adb push %~dp0/app_config.txt /sdcard/Android/data/com.quic.sgsr/files/app_config.txt - -pause diff --git a/samples/sgsr/CMakeLists.txt b/samples/sgsr/CMakeLists.txt index 97964f6..c5cb31f 100644 --- a/samples/sgsr/CMakeLists.txt +++ b/samples/sgsr/CMakeLists.txt @@ -10,6 +10,7 @@ set(CMAKE_CXX_STANDARD 20) set(CPP_SRC code/main/application.cpp code/main/application.hpp ) +set(FRAMEWORK_LIB framework_vulkan) # # Setup the module path to include the 'project directory' (project/windows or project/android) @@ -17,12 +18,7 @@ set(CPP_SRC code/main/application.cpp if(NOT DEFINED PROJECT_ROOT_DIR) set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) # # Do all the build steps for a Framework application. @@ -30,10 +26,27 @@ include(AddShadersDir) # include(FrameworkApplicationHelper) +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + # # Convert and copy textures to local folders # include(TexturePackager) # Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file +add_texture(textures/artifact.png UASTC) \ No newline at end of file diff --git a/samples/sgsr/build.gradle b/samples/sgsr/build.gradle deleted file mode 100644 index 00d5915..0000000 --- a/samples/sgsr/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.sgsr" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/sgsr/code/main/application.cpp b/samples/sgsr/code/main/application.cpp index 29d0892..b133588 100644 --- a/samples/sgsr/code/main/application.cpp +++ b/samples/sgsr/code/main/application.cpp @@ -17,11 +17,13 @@ #include "camera/cameraData.hpp" #include "camera/cameraGltfLoader.hpp" #include "gui/imguiVulkan.hpp" -#include "material/drawable.hpp" -#include "material/vulkan/shaderModule.hpp" -#include "material/shaderManagerT.hpp" -#include "material/materialManager.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/shaderManager.hpp" +#include "material/vulkan/materialManager.hpp" #include "material/vulkan/specializationConstantsLayout.hpp" +#include "memory/vulkan/drawIndirectBufferObject.hpp" +#include "memory/vulkan/indexBufferObject.hpp" +#include "memory/vulkan/vertexBufferObject.hpp" #include "mesh/meshHelper.hpp" #include "mesh/meshLoader.hpp" #include "system/math_common.hpp" @@ -32,12 +34,14 @@ #include #include +using namespace std::string_literals; + namespace { - static constexpr std::array sRenderPassNames = { "RP_SGSR", "RP_HUD", "RP_BLIT" }; + static const std::array sRenderPassNames = { "RP_SGSR", "RP_HUD", "RP_BLIT" }; - glm::vec3 gCameraStartPos = glm::vec3(26.48f, 20.0f, -5.21f); - glm::vec3 gCameraStartRot = glm::vec3(0.0f, 110.0f, 0.0f); + glm::vec3 gCameraStartPos = glm::vec3(0.0f, 3.5f, 0.0f); + glm::vec3 gCameraStartRot = glm::vec3(0.0f, 0.0f, 0.0f); float gFOV = PI_DIV_4; float gNearPlane = 1.0f; @@ -101,7 +105,7 @@ bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) return false; } - if (!InitAllRenderPasses()) + if (!CreateRenderPasses()) { return false; } @@ -121,16 +125,6 @@ bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) return false; } - if (!InitLocalSemaphores()) - { - return false; - } - - if (!BuildCmdBuffers()) - { - return false; - } - return true; } @@ -146,24 +140,7 @@ void Application::Destroy() // Cmd buffers for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { - for (auto& cmdBuffer : m_RenderPassData[whichPass].PassCmdBuffer) - { - cmdBuffer.Release(); - } - - for (auto& cmdBuffer : m_RenderPassData[whichPass].ObjectsCmdBuffer) - { - cmdBuffer.Release(); - } - - m_RenderPassData[whichPass].RenderTarget.Release(); - } - - // Render passes / Semaphores - for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].RenderPass, nullptr); - vkDestroySemaphore(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].PassCompleteSemaphore, nullptr); + m_RenderPassData[whichPass] = {}; } // Drawables @@ -236,10 +213,10 @@ bool Application::InitializeCamera() bool Application::LoadShaders() //----------------------------------------------------------------------------- { - m_ShaderManager = std::make_unique>(*GetVulkan()); + m_ShaderManager = std::make_unique(*GetVulkan()); m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); - m_MaterialManager = std::make_unique>(); + m_MaterialManager = std::make_unique(*GetVulkan()); LOGI("******************************"); LOGI("Loading Shaders..."); @@ -247,11 +224,11 @@ bool Application::LoadShaders() typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "SGSR", "Media\\Shaders\\sgsr_shader_mobile.json" } + { tIdAndFilename { "Blit"s, "Blit.json"s }, + tIdAndFilename { "SGSR"s, "sgsr_shader_mobile.json"s } }) { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); LOGI("Please verify if you have all required assets on the sample media folder"); @@ -269,21 +246,21 @@ bool Application::CreateRenderTargets() { Vulkan* const pVulkan = GetVulkan(); - LOGI("**************************"); - LOGI("Creating Render Targets..."); - LOGI("**************************"); - const TextureFormat MainColorType[] = { TextureFormat::B8G8R8A8_SRGB }; const TextureFormat HudColorType[] = { TextureFormat::B8G8R8A8_SRGB }; - if (!m_RenderPassData[RP_SGSR].RenderTarget.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "SGSR RT")) + LOGI("*************************************"); + LOGI("Creating Render Targets..."); + LOGI("*************************************"); + + if (!m_RenderPassData[RP_SGSR].RenderTarget.Initialize(pVulkan, gRenderWidth, gRenderHeight, MainColorType, TextureFormat::UNDEFINED, Msaa::Samples1, "SGSR RT")) { - LOGE("Unable to create scene render target"); + LOGE( "Unable to create scene render target" ); return false; } // Notice no depth on the HUD RT - if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) + if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, Msaa::Samples1, "HUD RT")) { LOGE("Unable to create hud render target"); return false; @@ -293,80 +270,82 @@ bool Application::CreateRenderTargets() } //----------------------------------------------------------------------------- -bool Application::InitUniforms() -//----------------------------------------------------------------------------- -{ - LOGI("******************************"); - LOGI("Initializing Uniforms..."); - LOGI("******************************"); - - Vulkan* const pVulkan = GetVulkan(); - - if (!CreateUniformBuffer(pVulkan, m_SGSRFragUniform)) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitAllRenderPasses() +bool Application::CreateRenderPasses() //----------------------------------------------------------------------------- { - Vulkan* const pVulkan = GetVulkan(); - - // ColorInputUsage | ClearDepthRenderPass | ColorOutputUsage | DepthOutputUsage | ClearColor - m_RenderPassData[RP_SGSR].PassSetup = { RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; - m_RenderPassData[RP_HUD].PassSetup = { RenderPassInputUsage::Clear, true, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; - m_RenderPassData[RP_BLIT].PassSetup = { RenderPassInputUsage::DontCare, true, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}}; - - TextureFormat surfaceFormat = pVulkan->m_SurfaceFormat; - auto swapChainColorFormat = std::span({ &surfaceFormat, 1 }); - auto swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; + auto& vulkan = *GetVulkan(); - LOGI("******************************"); - LOGI("Initializing Render Passes... "); - LOGI("******************************"); + // ColorInputUsage | ClearDepthRenderPass | ColorOutputUsage | DepthOutputUsage | ClearColor + m_RenderPassData[RP_SGSR].RenderPassSetup = { RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; + m_RenderPassData[RP_HUD].RenderPassSetup = { RenderPassInputUsage::Clear, false, RenderPassOutputUsage::StoreReadOnly, RenderPassOutputUsage::Discard, {}}; + m_RenderPassData[RP_BLIT].RenderPassSetup = { RenderPassInputUsage::DontCare, false, RenderPassOutputUsage::Present, RenderPassOutputUsage::Discard, {}}; - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + for (uint32_t whichPass = 0; whichPass < RP_BLIT; whichPass++) { - bool isSwapChainRenderPass = whichPass == RP_BLIT; - - std::span colorFormats = isSwapChainRenderPass ? swapChainColorFormat : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = isSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; + std::span colorFormats = m_RenderPassData[whichPass].RenderTarget.m_pLayerFormats; + TextureFormat depthFormat = m_RenderPassData[whichPass].RenderTarget.m_DepthFormat; - const auto& passSetup = m_RenderPassData[whichPass].PassSetup; + const auto& passSetup = m_RenderPassData[whichPass].RenderPassSetup; + auto& passData = m_RenderPassData[whichPass]; - if (!pVulkan->CreateRenderPass( + RenderPass renderPass; + if (!vulkan.CreateRenderPass( { colorFormats }, depthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, passSetup.ColorInputUsage, passSetup.ColorOutputUsage, passSetup.ClearDepthRenderPass, passSetup.DepthOutputUsage, - & m_RenderPassData[whichPass].RenderPass)) + renderPass)) { return false; } - + Framebuffer framebuffer; + framebuffer.Initialize( vulkan, + renderPass, + passData.RenderTarget.m_ColorAttachments, + &passData.RenderTarget.m_DepthAttachment, + sRenderPassNames[whichPass] ); + passData.RenderContext.push_back({std::move(renderPass), {}/*pipeline*/, std::move(framebuffer), sRenderPassNames[whichPass]}); + } + for (auto whichBuffer = 0; whichBuffer < vulkan.GetSwapchainBufferCount(); ++whichBuffer) + { + m_RenderPassData[RP_BLIT].RenderContext.push_back({ vulkan.m_SwapchainRenderPass.Copy(), {}, vulkan.GetSwapchainFramebuffer(whichBuffer), "RP_BLIT" }); } return true; } +//----------------------------------------------------------------------------- +bool Application::InitUniforms() +//----------------------------------------------------------------------------- +{ + LOGI("******************************"); + LOGI("Initializing Uniforms..."); + LOGI("******************************"); + + Vulkan* const pVulkan = GetVulkan(); + + if (!CreateUniformBuffer(pVulkan, m_SGSRFragUniform)) + { + return false; + } + + return true; +} + //----------------------------------------------------------------------------- bool Application::InitGui(uintptr_t windowHandle) //----------------------------------------------------------------------------- { const auto& hudRenderTarget = m_RenderPassData[RP_HUD].RenderTarget; - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPassData[RP_HUD].RenderPass); - if (!m_Gui->Initialize(windowHandle, hudRenderTarget[0].m_Width, hudRenderTarget[0].m_Height)) + m_Gui = std::make_unique(*GetVulkan(), m_RenderPassData[RP_HUD].RenderContext[0].GetRenderPass().Copy()); + if (!m_Gui->Initialize(windowHandle, TextureFormat::R8G8B8A8_UNORM, hudRenderTarget.m_Width, hudRenderTarget.m_Height)) { return false; } - + return true; } @@ -380,8 +359,8 @@ bool Application::LoadMeshObjects() LOGI("Initializing Shaders... "); LOGI("***********************"); - const auto* pSGSRQuadShader = m_ShaderManager->GetShader("SGSR"); - const auto* pBlitQuadShader = m_ShaderManager->GetShader("Blit"); + const auto* pSGSRQuadShader = m_ShaderManager->GetShader("SGSR"s); + const auto* pBlitQuadShader = m_ShaderManager->GetShader("Blit"s); if (!pSGSRQuadShader || !pBlitQuadShader) { return false; @@ -391,14 +370,12 @@ bool Application::LoadMeshObjects() LOGI("Loading and preparing assets..."); LOGI("*******************************"); - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - m_sgsr_sampler = CreateSampler(*pVulkan, SamplerAddressMode::ClampEdge, SamplerFilter::Nearest, SamplerBorderColor::TransparentBlackFloat, 0.0f/*mipBias*/); if (m_sgsr_sampler.IsEmpty()) return false; - auto* sceneTexture = apiCast(m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\artifact.ktx", m_sgsr_sampler)); - + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* sceneTexture = apiCast(m_TextureManager->GetOrLoadTexture("artifact.ktx"s, m_sgsr_sampler, prefixTextureDir)); if (!sceneTexture) { LOGE("Failed to load supporting textures"); @@ -412,12 +389,12 @@ bool Application::LoadMeshObjects() LOGI("Creating SGSR mesh..."); LOGI("*********************"); - MeshObject sgsrQuadMesh; - MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &sgsrQuadMesh); + Mesh sgsrQuadMesh; + MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pSGSRQuadShader->m_shaderDescription->m_vertexFormats, &sgsrQuadMesh ); // SGSR Material - auto sgsrQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pSGSRQuadShader, pVulkan->m_SwapchainImageCount, - [this, &sceneTexture](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto sgsrQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pSGSRQuadShader, pVulkan->m_SwapchainImageCount, + [this, &sceneTexture](const std::string& texName) -> const MaterialManagerBase::tPerFrameTexInfo { if (texName == "SceneColor") { @@ -425,7 +402,7 @@ bool Application::LoadMeshObjects() } return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer + [this](const std::string& bufferName) -> PerFrameBufferVulkan { if (bufferName == "SceneInfo") { @@ -437,7 +414,7 @@ bool Application::LoadMeshObjects() ); m_SGSRQuadDrawable = std::make_unique(*pVulkan, std::move(sgsrQuadShaderMaterial)); - if (!m_SGSRQuadDrawable->Init(m_RenderPassData[RP_SGSR].RenderPass, sRenderPassNames[RP_SGSR], std::move(sgsrQuadMesh))) + if (!m_SGSRQuadDrawable->Init( m_RenderPassData[RP_SGSR].RenderContext[0], std::move(sgsrQuadMesh))) { return false; } @@ -446,31 +423,30 @@ bool Application::LoadMeshObjects() LOGI("Creating Blit mesh..."); LOGI("*********************"); - MeshObject blitQuadMesh; - MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); + Mesh blitQuadMesh; + MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pBlitQuadShader->m_shaderDescription->m_vertexFormats, &blitQuadMesh); // Blit Material - auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pBlitQuadShader, pVulkan->m_SwapchainImageCount, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitQuadShader, pVulkan->m_SwapchainImageCount, + [this](const std::string& texName, MaterialManagerBase::tPerFrameTexInfo& texInfo) { if (texName == "Diffuse") { - return { &m_RenderPassData[RP_SGSR].RenderTarget[0].m_ColorAttachments[0] }; + texInfo = { &m_RenderPassData[RP_SGSR].RenderTarget.m_ColorAttachments[0] }; } else if (texName == "Overlay") { - return { &m_RenderPassData[RP_HUD].RenderTarget[0].m_ColorAttachments[0] }; + texInfo = { &m_RenderPassData[RP_HUD].RenderTarget.m_ColorAttachments[0] }; } - return {}; + return; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer + [this](const std::string& bufferName, PerFrameBufferBase& buffers) { - return {}; } ); m_BlitQuadDrawable = std::make_unique(*pVulkan, std::move(blitQuadShaderMaterial)); - if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderPass, sRenderPassNames[RP_BLIT], std::move(blitQuadMesh))) + if (!m_BlitQuadDrawable->Init( m_RenderPassData[RP_BLIT].RenderContext[0], std::move( blitQuadMesh ) )) { return false; } @@ -488,63 +464,12 @@ bool Application::InitCommandBuffers() Vulkan* const pVulkan = GetVulkan(); - auto GetPassName = [](uint32_t whichPass) - { - if (whichPass >= sRenderPassNames.size()) - { - LOGE("GetPassName() called with unknown pass (%d)!", whichPass); - return "RP_UNKNOWN"; - } - - return sRenderPassNames[whichPass]; - }; - - m_RenderPassData[RP_SGSR].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_SGSR].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_BLIT].PassCmdBuffer.resize(pVulkan->m_SwapchainImageCount); - m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.resize(pVulkan->m_SwapchainImageCount); - + // Per frame Command Buffer char szName[256]; - const VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + for (uint32_t whichBuffer = 0; whichBuffer < m_CommandLists.size(); whichBuffer++) { - for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[whichPass].PassCmdBuffer.size(); whichBuffer++) - { - // The Pass Command Buffer => Primary - sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) - { - return false; - } - - // Model => Secondary - sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CmdBuffLevel)) - { - return false; - } - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitLocalSemaphores() -//----------------------------------------------------------------------------- -{ - LOGI("********************************"); - LOGI("Initializing Local Semaphores..."); - LOGI("********************************"); - - const VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_RenderPassData[whichPass].PassCompleteSemaphore); - if (!CheckVkError("vkCreateSemaphore()", retVal)) + sprintf( szName, "Command Buffer (%d of %d)", whichBuffer + 1, NUM_VULKAN_BUFFERS ); + if (!m_CommandLists[whichBuffer].Initialize( pVulkan, szName, CommandList::Type::Primary )) { return false; } @@ -553,81 +478,6 @@ bool Application::InitLocalSemaphores() return true; } -//----------------------------------------------------------------------------- -bool Application::BuildCmdBuffers() -//----------------------------------------------------------------------------- -{ - LOGI("***************************"); - LOGI("Building Command Buffers..."); - LOGI("****************************"); - - Vulkan* const pVulkan = GetVulkan(); - - // Begin recording - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; - - for (uint32_t whichBuffer = 0; whichBuffer < renderPassData.ObjectsCmdBuffer.size(); whichBuffer++) - { - auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - - uint32_t targetWidth = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - uint32_t targetHeight = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)targetWidth; - viewport.height = (float)targetHeight; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = targetWidth; - scissor.extent.height = targetHeight; - - // Set up some values that change based on render pass - VkRenderPass whichRenderPass = renderPassData.RenderPass; - VkFramebuffer whichFramebuffer = bisSwapChainRenderPass ? pVulkan->m_SwapchainBuffers[whichBuffer].framebuffer : renderPassData.RenderTarget[0].m_FrameBuffer; - - // Objects (can render into any pass except Blit) - if (!cmdBufer.Begin(whichFramebuffer, whichRenderPass, bisSwapChainRenderPass)) - { - return false; - } - vkCmdSetViewport(cmdBufer.m_VkCommandBuffer, 0, 1, &viewport); - vkCmdSetScissor(cmdBufer.m_VkCommandBuffer, 0, 1, &scissor); - } - } - - // Blit quad drawable - AddDrawableToCmdBuffers(*m_BlitQuadDrawable.get(), m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.size())); - - // SGSR quad drawable - AddDrawableToCmdBuffers(*m_SGSRQuadDrawable.get(), m_RenderPassData[RP_SGSR].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_SGSR].ObjectsCmdBuffer.size())); - - // End recording - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - auto& renderPassData = m_RenderPassData[whichPass]; - - for (uint32_t whichBuffer = 0; whichBuffer < renderPassData.ObjectsCmdBuffer.size(); whichBuffer++) - { - auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - if (!cmdBufer.End()) - { - return false; - } - } - } - - return true; -} - //----------------------------------------------------------------------------- void Application::UpdateGui() //----------------------------------------------------------------------------- @@ -714,10 +564,6 @@ void Application::Render(float fltDiffTime) auto currentVulkanBuffer = pVulkan->SetNextBackBuffer(); uint32_t whichBuffer = currentVulkanBuffer.idx; - // ******************************** - // Application Draw() - Begin - // ******************************** - UpdateGui(); // Update camera @@ -727,20 +573,17 @@ void Application::Render(float fltDiffTime) // Update uniform buffers with latest data UpdateUniforms(whichBuffer); - // First time through, wait for the back buffer to be ready - std::span pWaitSemaphores = { ¤tVulkanBuffer.semaphore, 1 }; - - const VkPipelineStageFlags DefaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + // Open the command list for recording commands + auto& cmdBuffer = m_CommandLists[whichBuffer]; + cmdBuffer.Begin(); - // RP_SGSR + // RP_SCENE { - BeginRenderPass(whichBuffer, RP_SGSR, currentVulkanBuffer.swapchainPresentIdx); - AddPassCommandBuffer(whichBuffer, RP_SGSR); - EndRenderPass(whichBuffer, RP_SGSR); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_SGSR, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_SGSR].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_SGSR].PassCompleteSemaphore, 1 }; + const auto& renderContext = m_RenderPassData[RP_SGSR].RenderContext[0]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{ renderContext.GetRenderPassBeginInfo() }; + vkCmdBeginRenderPass(cmdBuffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + AddDrawableToCmdBuffers(*m_SGSRQuadDrawable.get(), &cmdBuffer, 1, 1, whichBuffer); + vkCmdEndRenderPass(cmdBuffer); } // RP_HUD @@ -748,124 +591,30 @@ void Application::Render(float fltDiffTime) if (m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget[0].m_FrameBuffer); + guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget.m_FrameBuffer); if (guiCommandBuffer != VK_NULL_HANDLE) { - BeginRenderPass(whichBuffer, RP_HUD, currentVulkanBuffer.swapchainPresentIdx); - vkCmdExecuteCommands(m_RenderPassData[RP_HUD].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &guiCommandBuffer); - EndRenderPass(whichBuffer, RP_HUD); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_HUD, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }; + const auto& renderContext = m_RenderPassData[RP_HUD].RenderContext[0]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{ renderContext.GetRenderPassBeginInfo() }; + vkCmdBeginRenderPass(cmdBuffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + vkCmdExecuteCommands(cmdBuffer, 1, &guiCommandBuffer); + vkCmdEndRenderPass(cmdBuffer); } } // Blit Results to the screen { - BeginRenderPass(whichBuffer, RP_BLIT, currentVulkanBuffer.swapchainPresentIdx); - AddPassCommandBuffer(whichBuffer, RP_BLIT); - EndRenderPass(whichBuffer, RP_BLIT); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_BLIT, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1 }, currentVulkanBuffer.fence); - pWaitSemaphores = { &m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1 }; - } - - // Queue is loaded up, tell the driver to start processing - pVulkan->PresentQueue(pWaitSemaphores, currentVulkanBuffer.swapchainPresentIdx); - - // ******************************** - // Application Draw() - End - // ******************************** -} - -//----------------------------------------------------------------------------- -void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, uint32_t WhichSwapchainImage) -//----------------------------------------------------------------------------- -{ - Vulkan* const pVulkan = GetVulkan(); - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; - - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Reset()) - { - LOGE("Pass (%d) command buffer Reset() failed !", whichPass); - } - - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Begin()) - { - LOGE("Pass (%d) command buffer Begin() failed !", whichPass); + const auto& renderContext = m_RenderPassData[RP_BLIT].RenderContext[0]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{ renderContext.GetRenderPassBeginInfo() }; + vkCmdBeginRenderPass(cmdBuffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + AddDrawableToCmdBuffers(*m_BlitQuadDrawable.get(), &cmdBuffer, 1, 1, whichBuffer); + vkCmdEndRenderPass(cmdBuffer); } - VkFramebuffer framebuffer = nullptr; - switch (whichPass) - { - case RP_SGSR: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_HUD: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_BLIT: - framebuffer = pVulkan->m_SwapchainBuffers[WhichSwapchainImage].framebuffer; - break; - default: - framebuffer = nullptr; - break; - } - - assert(framebuffer != nullptr); - - VkRect2D passArea = {}; - passArea.offset.x = 0; - passArea.offset.y = 0; - passArea.extent.width = bisSwapChainRenderPass ? pVulkan->m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - passArea.extent.height = bisSwapChainRenderPass ? pVulkan->m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - TextureFormat swapChainColorFormat = pVulkan->m_SurfaceFormat; - auto swapChainColorFormats = std::span({ &swapChainColorFormat, 1 }); - TextureFormat swapChainDepthFormat = pVulkan->m_SwapchainDepth.format; - std::span colorFormats = bisSwapChainRenderPass ? swapChainColorFormats : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = bisSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; - - VkClearColorValue clearColor = { renderPassData.PassSetup.ClearColor[0], renderPassData.PassSetup.ClearColor[1], renderPassData.PassSetup.ClearColor[2], renderPassData.PassSetup.ClearColor[3] }; - - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].BeginRenderPass( - passArea, - 0.0f, - 1.0f, - { &clearColor , 1 }, - (uint32_t)colorFormats.size(), - depthFormat != TextureFormat::UNDEFINED, - m_RenderPassData[whichPass].RenderPass, - bisSwapChainRenderPass, - framebuffer, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); -} - - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffer(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - if (m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_NumDrawCalls) - { - vkCmdExecuteCommands(m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_VkCommandBuffer); - } -} - -//----------------------------------------------------------------------------- -void Application::EndRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].EndRenderPass(); -} + // Close the command list + cmdBuffer.End(); -//----------------------------------------------------------------------------- -void Application::SubmitRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].End(); - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].QueueSubmit(WaitSemaphores, WaitDstStageMasks, SignalSemaphores, CompletionFence); + // Submit and present the queue + cmdBuffer.QueueSubmit( currentVulkanBuffer, pVulkan->m_RenderCompleteSemaphore ); + pVulkan->PresentQueue( pVulkan->m_RenderCompleteSemaphore, currentVulkanBuffer.swapchainPresentIdx ); } diff --git a/samples/sgsr/code/main/application.hpp b/samples/sgsr/code/main/application.hpp index f95e012..3751f05 100644 --- a/samples/sgsr/code/main/application.hpp +++ b/samples/sgsr/code/main/application.hpp @@ -16,12 +16,6 @@ #include "vulkan/commandBuffer.hpp" #include -#define NUM_SPOT_LIGHTS 4 - -class ShaderManager; -class MaterialManager; -class Drawable; - enum RENDER_PASS { RP_SGSR = 0, @@ -54,21 +48,12 @@ struct PassSetupInfo struct PassData { // Pass internal data - PassSetupInfo PassSetup; - VkRenderPass RenderPass = VK_NULL_HANDLE; - - // Recorded objects that are set to be drawn on this pass - std::vector< CommandListVulkan> ObjectsCmdBuffer; - - // Command buffer used to dispatch the render pass - std::vector< CommandListVulkan> PassCmdBuffer; - - // Indicates the completing of the underlying render pass - VkSemaphore PassCompleteSemaphore = VK_NULL_HANDLE; + PassSetupInfo RenderPassSetup; + std::vector> RenderContext; // context per framebuffer (some passes might all point to the same framebuffers) // Render targed used by the underlying render pass - // note: The blit pass uses the backbuffer directly instead this RT - CRenderTargetArray<1> RenderTarget; + // note: The blit pass uses the backbuffer targets directly instead this RT + RenderTarget RenderTarget; }; // ********************** @@ -92,13 +77,12 @@ class Application : public ApplicationHelperBase // Application - Initialization bool InitializeCamera(); bool LoadShaders(); + bool CreateRenderPasses(); bool CreateRenderTargets(); bool InitUniforms(); - bool InitAllRenderPasses(); bool InitGui(uintptr_t windowHandle); bool LoadMeshObjects(); bool InitCommandBuffers(); - bool InitLocalSemaphores(); bool BuildCmdBuffers(); private: @@ -107,7 +91,6 @@ class Application : public ApplicationHelperBase void BeginRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, uint32_t WhichSwapchainImage); void AddPassCommandBuffer(uint32_t WhichBuffer, RENDER_PASS WhichPass); void EndRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass); - void SubmitRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence = (VkFence)nullptr); void UpdateGui(); bool UpdateUniforms(uint32_t WhichBuffer); @@ -116,6 +99,9 @@ class Application : public ApplicationHelperBase // Render passes std::array< PassData, NUM_RENDER_PASSES> m_RenderPassData; + // Per Frame Command lists + std::array m_CommandLists; + // UBOs UniformT m_SGSRFragUniform; SGSRFragUB m_SGSRFragUniformData; @@ -125,7 +111,7 @@ class Application : public ApplicationHelperBase float m_sgsr_scale_factor = 1.0f; bool m_sgsr_active = false; bool m_sgsr_edge_dir_active = false; - SamplerT m_sgsr_sampler; + Sampler m_sgsr_sampler; // Drawables std::unique_ptr m_SGSRQuadDrawable; diff --git a/samples/sgsr/install_apk.bat b/samples/sgsr/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/sgsr/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sgsr/install_config.bat b/samples/sgsr/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/sgsr/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sgsr/project/android/AndroidManifest.xml b/samples/sgsr/project/android/AndroidManifest.xml index 0d06fc4..b2f75f5 100644 --- a/samples/sgsr/project/android/AndroidManifest.xml +++ b/samples/sgsr/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -23,7 +22,8 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> - Snapdragon GSR + SGS Snapdragon GSR v1 diff --git a/samples/sgsr/img/screenshot.png b/samples/sgsr/project/img/screenshot.png similarity index 100% rename from samples/sgsr/img/screenshot.png rename to samples/sgsr/project/img/screenshot.png diff --git a/samples/sgsr/shaders/Blit.frag b/samples/sgsr/shaders/Blit.frag index 839bc24..dbec96d 100644 --- a/samples/sgsr/shaders/Blit.frag +++ b/samples/sgsr/shaders/Blit.frag @@ -20,9 +20,8 @@ layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_Overla // Varying's layout (location = 0) in vec2 v_TexCoord; -layout (location = 1) in vec4 v_VertColor; -// Finally, the output color +// Output color layout (location = 0) out vec4 FragColor; @@ -38,9 +37,6 @@ void main() // Get base color from the color texture vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); - // Multiply by vertex color. - DiffuseColor.xyzw *= v_VertColor.xyzw; - // Apply darkening/lightening control // float lerp01 = min(1,FragCB.Diffuse); // float lerp12 = max(0,FragCB.Diffuse-1); diff --git a/samples/sgsr/shaders/Blit.json b/samples/sgsr/shaders/Blit.json index d7d32c6..b7d4975 100644 --- a/samples/sgsr/shaders/Blit.json +++ b/samples/sgsr/shaders/Blit.json @@ -30,7 +30,7 @@ ], "Vertex": [ { - "Span": 60, + "Span": 20, "Name": "VB0", "Elements": [ { @@ -38,25 +38,10 @@ "Offset": 0, "Type": "Vec3" }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, { "Name": "UV", - "Offset": 24, + "Offset": 12, "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" } ] } diff --git a/samples/sgsr/shaders/Blit.vert b/samples/sgsr/shaders/Blit.vert index d11750a..cdd4f93 100644 --- a/samples/sgsr/shaders/Blit.vert +++ b/samples/sgsr/shaders/Blit.vert @@ -11,20 +11,13 @@ #extension GL_ARB_shading_language_420pack : enable #define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 +#define SHADER_ATTRIB_LOC_TEXCOORD0 1 layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Varying's layout (location = 0) out vec2 v_TexCoord; -layout (location = 1) out vec4 v_VertColor; void main() { @@ -32,7 +25,4 @@ void main() vec4 TempPos = vec4(a_Position.xyz, 1.0); gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); v_TexCoord = vec2(a_TexCoord.xy); - - // Color is simple attribute color - v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); } diff --git a/samples/sgsr/shaders/sgsr_shader.vert b/samples/sgsr/shaders/sgsr_shader.vert index f134e60..10d9bed 100644 --- a/samples/sgsr/shaders/sgsr_shader.vert +++ b/samples/sgsr/shaders/sgsr_shader.vert @@ -11,16 +11,10 @@ #extension GL_ARB_shading_language_420pack : enable #define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_NORMAL 1 -#define SHADER_ATTRIB_LOC_TEXCOORD0 2 -#define SHADER_ATTRIB_LOC_COLOR 3 -#define SHADER_ATTRIB_LOC_TANGENT 4 +#define SHADER_ATTRIB_LOC_TEXCOORD0 1 layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; -layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Varying's layout (location = 0) out vec4 v_TexCoord; diff --git a/samples/sgsr/shaders/sgsr_shader_mobile.json b/samples/sgsr/shaders/sgsr_shader_mobile.json index 5b001d9..7f083be 100644 --- a/samples/sgsr/shaders/sgsr_shader_mobile.json +++ b/samples/sgsr/shaders/sgsr_shader_mobile.json @@ -29,7 +29,7 @@ ], "Vertex": [ { - "Span": 60, + "Span": 20, "Name": "VB0", "Elements": [ { @@ -37,25 +37,10 @@ "Offset": 0, "Type": "Vec3" }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, { "Name": "UV", - "Offset": 24, + "Offset": 12, "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" } ] } diff --git a/samples/sgsr2/01_Install_APK.bat b/samples/sgsr2/01_Install_APK.bat deleted file mode 100644 index 88a1631..0000000 --- a/samples/sgsr2/01_Install_APK.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\sgsr2\outputs\apk\debug\sgsr2-debug.apk -@echo **************************************** -call adb install -r -t ..\..\build\android\sgsr2\outputs\apk\debug\sgsr2-debug.apk -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/sgsr2/CMakeLists.txt b/samples/sgsr2/CMakeLists.txt index 3026c3f..9389c0b 100644 --- a/samples/sgsr2/CMakeLists.txt +++ b/samples/sgsr2/CMakeLists.txt @@ -14,6 +14,7 @@ set(CPP_SRC code/main/application.cpp code/main/sgsr2_context_frag.cpp code/main/sgsr2_context_frag.hpp ) +set(FRAMEWORK_LIB framework_vulkan) # # Setup the module path to include the 'project directory' (project/windows or project/android) @@ -21,12 +22,7 @@ set(CPP_SRC code/main/application.cpp if(NOT DEFINED PROJECT_ROOT_DIR) set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) # # Do all the build steps for a Framework application. @@ -34,19 +30,42 @@ include(AddShadersDir) # include(FrameworkApplicationHelper) +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + # # Copy required models to local folders # include(ModelPackager) -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) # # Convert and copy textures to local folders # include(TexturePackager) -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/sgsr2/build.gradle b/samples/sgsr2/build.gradle deleted file mode 100644 index f14490c..0000000 --- a/samples/sgsr2/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.sgsr2" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.19.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/sgsr2/code/main/application.cpp b/samples/sgsr2/code/main/application.cpp index 26a2ff3..6745ad5 100644 --- a/samples/sgsr2/code/main/application.cpp +++ b/samples/sgsr2/code/main/application.cpp @@ -17,15 +17,17 @@ #include "camera/cameraData.hpp" #include "camera/cameraGltfLoader.hpp" #include "gui/imguiVulkan.hpp" -#include "material/drawable.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/drawableLoader.hpp" +#include "material/vulkan/materialManager.hpp" #include "material/vulkan/shaderModule.hpp" -#include "material/shaderManagerT.hpp" -#include "material/materialManager.hpp" +#include "material/vulkan/shaderManager.hpp" #include "material/vulkan/specializationConstantsLayout.hpp" #include "mesh/meshHelper.hpp" #include "mesh/meshLoader.hpp" #include "system/math_common.hpp" -#include "texture/textureManager.hpp" +#include "texture/vulkan/textureManager.hpp" #include "imgui.h" #include "sgsr2_context.hpp" #include "sgsr2_context_frag.hpp" @@ -34,11 +36,10 @@ #include #include -VAR( char*, gSceneAssetPath, "Media\\Meshes\\Museum.gltf", kVariableNonpersistent ); -VAR( char*, gSceneTexturePath, "Media\\", kVariableNonpersistent ); +VAR( char*, gSceneAssetModel, "SteamPunkSauna.gltf", kVariableNonpersistent ); VAR( float, gSceneScale, 1.0f, kVariableNonpersistent ); -VAR( glm::vec3, gCameraStartPos, glm::vec3(26.48f, 20.0f, -5.21f), kVariableNonpersistent ); -VAR( glm::vec3, gCameraStartRot, glm::vec3(0.0f, 110.0f, 0.0f), kVariableNonpersistent ); // in degrees +VAR( glm::vec3, gCameraStartPos, glm::vec3(0.0f, 3.5f, 0.0f), kVariableNonpersistent ); +VAR( glm::vec3, gCameraStartRot, glm::vec3(0.0f, 0.0f, 0.0f), kVariableNonpersistent ); // in degrees VAR( float, gNearPlane, 0.3f, kVariableNonpersistent ); VAR( float, gFarPlane, 1800.0f, kVariableNonpersistent ); VAR( float, gFov, 45.0f, kVariableNonpersistent ); @@ -46,7 +47,7 @@ VAR( int, gUpscaleMode, 1, kVariableNonpersistent ); namespace { - static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_HUD", "RP_BLIT" }; + static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_HUD", "RP_BLIT" }; float gNormalAmount = 0.3f; float gNormalMirrorReflectAmount = 0.05f; @@ -145,12 +146,7 @@ bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) { return false; } - - if (!InitLocalSemaphores()) - { - return false; - } - + if (!BuildCmdBuffers()) { return false; @@ -177,11 +173,6 @@ void Application::Destroy() // Cmd buffers for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { - for (auto& cmdBuffer : m_RenderPassData[whichPass].PassCmdBuffer) - { - cmdBuffer.Release(); - } - for (auto& cmdBuffer : m_RenderPassData[whichPass].ObjectsCmdBuffer) { cmdBuffer.Release(); @@ -190,13 +181,6 @@ void Application::Destroy() m_RenderPassData[whichPass].RenderTarget.Release(); } - // Render passes / Semaphores - for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].RenderPass, nullptr); - vkDestroySemaphore(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].PassCompleteSemaphore, nullptr); - } - // Drawables m_SceneDrawables.clear(); m_BlitQuadDrawable.reset(); @@ -271,25 +255,26 @@ bool Application::InitializeCamera() bool Application::LoadShaders() //----------------------------------------------------------------------------- { - m_ShaderManager = std::make_unique>(*GetVulkan()); + m_ShaderManager = std::make_unique(*GetVulkan()); m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); - m_MaterialManager = std::make_unique>(); + m_MaterialManager = std::make_unique(*GetVulkan()); LOGI("******************************"); LOGI("Loading Shaders..."); LOGI("******************************"); - + typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" }, - tIdAndFilename { "SceneOpaque", "Media\\Shaders\\SceneOpaque.json" }, - tIdAndFilename { "SceneTransparent", "Media\\Shaders\\SceneTransparent.json" }, - tIdAndFilename { "sgsr2", "Media\\Shaders\\sgsr2.json" }, - tIdAndFilename { "sgsr2_frag", "Media\\Shaders\\sgsr2_frag.json" }, - }) - { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + { + tIdAndFilename { "Blit", "Blit.json" }, + tIdAndFilename { "SceneOpaque", "SceneOpaque.json" }, + tIdAndFilename { "SceneTransparent", "SceneTransparent.json" }, + tIdAndFilename { "sgsr2", "sgsr2.json" }, + tIdAndFilename { "sgsr2_frag", "sgsr2_frag.json" }, + }) + { + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); LOGI("Please verify if you have all required assets on the sample media folder"); @@ -321,14 +306,14 @@ bool Application::CreateRenderTargets() }; const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_SRGB }; - if (!m_RenderPassData[RP_SCENE].RenderTarget.Initialize(pVulkan, gRenderWidth, gRenderHeight, SceneColorTypes, desiredDepthFormat, VK_SAMPLE_COUNT_1_BIT, "Scene RT")) + if (!m_RenderPassData[RP_SCENE].RenderTarget.Initialize(pVulkan, gRenderWidth, gRenderHeight, SceneColorTypes, desiredDepthFormat, Msaa::Samples1, "Scene RT")) { LOGE("Unable to create scene render target"); return false; } // Notice no depth on the HUD RT - if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "HUD RT")) + if (!m_RenderPassData[RP_HUD].RenderTarget.Initialize(pVulkan, gSurfaceWidth, gSurfaceHeight, HudColorType, TextureFormat::UNDEFINED, Msaa::Samples1, "HUD RT")) { LOGE("Unable to create hud render target"); return false; @@ -379,28 +364,38 @@ bool Application::InitAllRenderPasses() LOGI("Initializing Render Passes... "); LOGI("******************************"); - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + for (uint32_t whichPass = 0; whichPass < RP_BLIT; whichPass++) { - bool isSwapChainRenderPass = whichPass == RP_BLIT; - - std::span colorFormats = isSwapChainRenderPass ? swapChainColorFormat : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = isSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; + std::span colorFormats = m_RenderPassData[whichPass].RenderTarget.m_pLayerFormats; + TextureFormat depthFormat = m_RenderPassData[whichPass].RenderTarget.m_DepthFormat; const auto& passSetup = m_RenderPassData[whichPass].RenderPassSetup; + auto& passData = m_RenderPassData[whichPass]; + RenderPass renderPass; if (!vulkan.CreateRenderPass( { colorFormats }, depthFormat, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, passSetup.ColorInputUsage, passSetup.ColorOutputUsage, passSetup.ClearDepthRenderPass, passSetup.DepthOutputUsage, - & m_RenderPassData[whichPass].RenderPass)) + renderPass)) { return false; } - + Framebuffer framebuffer; + framebuffer.Initialize( vulkan, + renderPass, + passData.RenderTarget.m_ColorAttachments, + &passData.RenderTarget.m_DepthAttachment, + sRenderPassNames[whichPass] ); + passData.RenderContext.push_back({std::move(renderPass), {}/*pipeline*/, std::move(framebuffer), sRenderPassNames[whichPass]}); + } + for (auto whichBuffer = 0; whichBuffer < vulkan.GetSwapchainBufferCount(); ++whichBuffer) + { + m_RenderPassData[RP_BLIT].RenderContext.push_back( {vulkan.m_SwapchainRenderPass.Copy(), {}, vulkan.GetSwapchainFramebuffer( whichBuffer ), "RP_BLIT"} ); } return true; @@ -411,8 +406,8 @@ bool Application::InitGui(uintptr_t windowHandle) //----------------------------------------------------------------------------- { const auto& hudRenderTarget = m_RenderPassData[RP_HUD].RenderTarget; - m_Gui = std::make_unique>(*GetVulkan(), m_RenderPassData[RP_HUD].RenderPass); - if (!m_Gui->Initialize(windowHandle, hudRenderTarget[0].m_Width, hudRenderTarget[0].m_Height)) + m_Gui = std::make_unique(*GetVulkan(), m_RenderPassData[RP_HUD].RenderContext[0].GetRenderPass().Copy()); + if (!m_Gui->Initialize(windowHandle, TextureFormat::R8G8B8A8_UNORM, hudRenderTarget.m_Width, hudRenderTarget.m_Height)) { return false; } @@ -434,10 +429,10 @@ bool Application::InitSGSR2Context() upscaler_config.display_size = glm::uvec2( gSurfaceWidth, gSurfaceHeight ); SGSR2::InputImages input_images; - input_images.color = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0]; - input_images.opaque_color = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0]; - input_images.velocity = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[1]; - input_images.depth = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_DepthAttachment; + input_images.color = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[0]; + input_images.opaque_color = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[0]; + input_images.velocity = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[1]; + input_images.depth = &m_RenderPassData[RP_SCENE].RenderTarget.m_DepthAttachment; if (!m_sgsr2_context->Initialize( vulkan, @@ -456,10 +451,10 @@ bool Application::InitSGSR2Context() upscaler_config.display_size = glm::uvec2( gSurfaceWidth, gSurfaceHeight ); SGSR2_Frag::InputImages input_images; - input_images.color = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0]; - input_images.opaque_color = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0]; - input_images.velocity = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[1]; - input_images.depth = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_DepthAttachment; + input_images.color = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[0]; + input_images.opaque_color = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[0]; + input_images.velocity = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[1]; + input_images.depth = &m_RenderPassData[RP_SCENE].RenderTarget.m_DepthAttachment; if (!m_sgsr2_context_frag->Initialize( vulkan, @@ -505,12 +500,13 @@ bool Application::LoadMeshObjects() LOGI("Loading and preparing the museum..."); LOGI("***********************************"); - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - - auto* whiteTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\white_d.ktx", m_SamplerRepeat); - auto* blackTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\black_d.ktx", m_SamplerRepeat); - auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures\\normal_default.ktx", m_SamplerRepeat); + m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory(TEXTURE_DESTINATION_PATH)); + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* blackTexture = m_TextureManager->GetOrLoadTexture("black_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture("normal_default.ktx", m_SamplerRepeat, prefixTextureDir); + if (!whiteTexture || !blackTexture || !normalDefaultTexture) { LOGE("Failed to load supporting textures"); @@ -536,17 +532,17 @@ bool Application::LoadMeshObjects() auto MaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef)->std::optional { - const PathManipulator_PrefixDirectory prefixTextureDir{gSceneTexturePath}; + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; const PathManipulator_ChangeExtension changeTextureExt{".ktx"}; - auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); - auto* normalTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.bumpFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); - auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.emissiveFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); - auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.specMapFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* normalTexture = m_TextureManager->GetOrLoadTexture(materialDef.bumpFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(materialDef.emissiveFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(materialDef.specMapFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); bool alphaCutout = materialDef.alphaCutout; bool transparent = materialDef.transparent; - const Shader* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; + const auto* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; ObjectMaterialParameters objectMaterial; objectMaterial.objectFragUniformData.Color.r = static_cast(materialDef.baseColorFactor[0]); @@ -561,8 +557,8 @@ bool Application::LoadMeshObjects() return std::nullopt; } - auto shaderMaterial = m_MaterialManager->CreateMaterial(vulkan, *targetShader, NUM_VULKAN_BUFFERS, - [&](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto shaderMaterial = m_MaterialManager->CreateMaterial(*targetShader, NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { @@ -583,11 +579,11 @@ bool Application::LoadMeshObjects() return {}; }, - [&](const std::string& bufferName) -> tPerFrameVkBuffer + [&](const std::string& bufferName) -> PerFrameBufferVulkan { if (bufferName == "Vert") { - return {m_ObjectVertUniform.vkBuffers.begin(), m_ObjectVertUniform.vkBuffers.end()}; + return {m_ObjectVertUniform.bufferHandles.begin(), m_ObjectVertUniform.bufferHandles.end()}; } else if (bufferName == "Frag") { @@ -595,7 +591,7 @@ bool Application::LoadMeshObjects() } else if (bufferName == "Light") { - return {m_LightUniform.vkBuffers.begin(), m_LightUniform.vkBuffers.end()}; + return {m_LightUniform.bufferHandles.begin(), m_LightUniform.bufferHandles.end()}; } return {}; @@ -606,27 +602,24 @@ bool Application::LoadMeshObjects() }; - const auto loaderFlags = 0; // No instancing + const uint32_t loaderFlags = 0; // No instancing const bool ignoreTransforms = (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0; - MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(gSceneAssetPath); - MeshObjectIntermediateGltfProcessor meshObjectProcessor(gSceneAssetPath, ignoreTransforms, glm::vec3(1.0f,1.0f,1.0f)); + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(sceneAssetPath); + MeshObjectIntermediateGltfProcessor meshObjectProcessor(sceneAssetPath, ignoreTransforms, glm::vec3(1.0f,1.0f,1.0f)); CameraGltfProcessor meshCameraProcessor{}; - if (!MeshLoader::LoadGltf(*m_AssetManager, gSceneAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || + if (!MeshLoader::LoadGltf(*m_AssetManager, sceneAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || !DrawableLoader::CreateDrawables(vulkan, - std::move(meshObjectProcessor.m_meshObjects), - { &m_RenderPassData[RP_SCENE].RenderPass, 1 }, - &sRenderPassNames[RP_SCENE], - MaterialLoader, - m_SceneDrawables, - {}, // RenderPassMultisample - loaderFlags, - {})) // RenderPassSubpasses + std::move(meshObjectProcessor.m_meshObjects), + m_RenderPassData[RP_SCENE].RenderContext, + MaterialLoader, + m_SceneDrawables, + loaderFlags)) { - LOGE("Error Loading the museum gltf file"); + LOGE("Error Loading the scene gltf file"); LOGI("Please verify if you have all required assets on the sample media folder"); - LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); return false; } @@ -640,15 +633,15 @@ bool Application::LoadMeshObjects() LOGI("Creating Quad mesh..."); LOGI("*********************"); - MeshObject blitQuadMesh; + Mesh blitQuadMesh; if (!MeshHelper::CreateMesh( vulkan.GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pBlitQuadShader->m_shaderDescription->m_vertexFormats, &blitQuadMesh )) { return false; } // Blit Material - auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(vulkan, *pBlitQuadShader, 2, - [this](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo + auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitQuadShader, 2, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo { if (texName == "Diffuse") { @@ -656,18 +649,18 @@ bool Application::LoadMeshObjects() } else if (texName == "Overlay") { - return { &m_RenderPassData[RP_HUD].RenderTarget[0].m_ColorAttachments[0] }; + return { &m_RenderPassData[RP_HUD].RenderTarget.m_ColorAttachments[0] }; } return {}; }, - [this](const std::string& bufferName) -> tPerFrameVkBuffer + [this](const std::string& bufferName) -> PerFrameBuffer { return {}; } ); m_BlitQuadDrawable = std::make_unique(vulkan, std::move(blitQuadShaderMaterial)); - if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderPass, sRenderPassNames[RP_BLIT], std::move(blitQuadMesh))) + if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].RenderContext[0], std::move(blitQuadMesh))) { return false; } @@ -696,60 +689,22 @@ bool Application::InitCommandBuffers() return sRenderPassNames[whichPass]; }; - m_RenderPassData[RP_SCENE].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].PassCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_HUD].ObjectsCmdBuffer.resize(NUM_VULKAN_BUFFERS); - m_RenderPassData[RP_BLIT].PassCmdBuffer.resize(pVulkan->m_SwapchainImageCount); - m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.resize(pVulkan->m_SwapchainImageCount); char szName[256]; - const VkCommandBufferLevel CmdBuffLevel = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.size(); whichBuffer++) { - for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[whichPass].PassCmdBuffer.size(); whichBuffer++) + // Model => Secondary + sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(RP_SCENE), whichBuffer + 1, NUM_VULKAN_BUFFERS); + if (!m_RenderPassData[RP_SCENE].ObjectsCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Secondary)) { - // The Pass Command Buffer => Primary - sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Initialize(pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) - { - return false; - } - - // Model => Secondary - sprintf(szName, "Model (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); - if (!m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CmdBuffLevel)) - { - return false; - } + return false; } } for (auto& command_buffer : m_UpscaleAndPostCommandList) { - command_buffer.Initialize( pVulkan, "SGSR2 and Post Process Command List", VK_COMMAND_BUFFER_LEVEL_PRIMARY ); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitLocalSemaphores() -//----------------------------------------------------------------------------- -{ - LOGI("********************************"); - LOGI("Initializing Local Semaphores..."); - LOGI("********************************"); - - const VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) - { - VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_RenderPassData[whichPass].PassCompleteSemaphore); - if (!CheckVkError("vkCreateSemaphore()", retVal)) - { - return false; - } + command_buffer.Initialize( pVulkan, "SGSR2 and Post Process Command List", CommandListBase::Type::Primary ); } return true; @@ -759,50 +714,44 @@ bool Application::InitLocalSemaphores() bool Application::BuildCmdBuffers() //----------------------------------------------------------------------------- { - LOGI("***************************"); - LOGI("Building Command Buffers..."); - LOGI("****************************"); + LOGI("*************************************"); + LOGI("Building Secondary Command Buffers..."); + LOGI("*************************************"); auto& vulkan = *GetVulkan(); // Begin recording - for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; + auto& renderPassData = m_RenderPassData[RP_SCENE]; + auto& renderContext = renderPassData.RenderContext[0]; - for (uint32_t whichBuffer = 0; whichBuffer < renderPassData.ObjectsCmdBuffer.size(); whichBuffer++) + for (auto& cmdBufer: renderPassData.ObjectsCmdBuffer) { - auto& cmdBufer = renderPassData.ObjectsCmdBuffer[whichBuffer]; - - uint32_t targetWidth = bisSwapChainRenderPass ? vulkan.m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - uint32_t targetHeight = bisSwapChainRenderPass ? vulkan.m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)targetWidth; - viewport.height = (float)targetHeight; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = targetWidth; - scissor.extent.height = targetHeight; + uint32_t targetWidth = renderPassData.RenderTarget.m_Width; + uint32_t targetHeight = renderPassData.RenderTarget.m_Height; + const VkViewport viewport { + .width = (float)targetWidth, + .height = (float)targetHeight, + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + + const VkRect2D scissor{ + .extent { + .width = targetWidth, + .height = targetHeight + }}; // Set up some values that change based on render pass - VkRenderPass whichRenderPass = renderPassData.RenderPass; - VkFramebuffer whichFramebuffer = bisSwapChainRenderPass ? vulkan.m_SwapchainBuffers[whichBuffer].framebuffer : renderPassData.RenderTarget[0].m_FrameBuffer; + const Framebuffer& framebuffer = *renderContext.GetFramebuffer(); - // Objects (can render into any pass except Blit) - if (!cmdBufer.Begin(whichFramebuffer, whichRenderPass, bisSwapChainRenderPass)) + // Objects (can render into any pass) + if (!cmdBufer.Begin(framebuffer, renderContext.GetRenderPass())) { return false; } - vkCmdSetViewport(cmdBufer.m_VkCommandBuffer, 0, 1, &viewport); - vkCmdSetScissor(cmdBufer.m_VkCommandBuffer, 0, 1, &scissor); + vkCmdSetViewport( cmdBufer, 0, 1, &viewport ); + vkCmdSetScissor ( cmdBufer, 0, 1, &scissor ); } } @@ -812,9 +761,6 @@ bool Application::BuildCmdBuffers() AddDrawableToCmdBuffers(sceneDrawable, m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_SCENE].ObjectsCmdBuffer.size())); } - // Blit quad drawable - AddDrawableToCmdBuffers(*m_BlitQuadDrawable.get(), m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.data(), 1, static_cast(m_RenderPassData[RP_BLIT].ObjectsCmdBuffer.size())); - // End recording for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) { @@ -961,7 +907,11 @@ void Application::Render(float fltDiffTime) m_Camera.SetJitter(m_sgsr2_context_frag->GetJitter() / glm::vec2(gRenderWidth, gRenderHeight)); break; } - m_Camera.UpdateController(fltDiffTime, *m_CameraController); + + static float cameraSpeedFactor = 1.0f; + cameraSpeedFactor = std::max(0.25f, cameraSpeedFactor + ImGui::GetIO().MouseWheel * 0.25f); + + m_Camera.UpdateController(fltDiffTime * cameraSpeedFactor, *m_CameraController); m_Camera.UpdateMatrices(); // Update uniform buffers with latest data @@ -972,21 +922,17 @@ void Application::Render(float fltDiffTime) // First time through, wait for the back buffer to be ready std::span pWaitSemaphores = { ¤tVulkanBuffer.semaphore, 1 }; - const VkPipelineStageFlags DefaultGfxWaitDstStageMasks[] = - { - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT - // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT - }; + + auto& command_buffer = m_UpscaleAndPostCommandList[whichBuffer]; + command_buffer.Begin(); // RP_SCENE { - BeginRenderPass(whichBuffer, RP_SCENE, currentVulkanBuffer.swapchainPresentIdx); - AddPassCommandBuffer(whichBuffer, RP_SCENE); - EndRenderPass(whichBuffer, RP_SCENE); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_SCENE, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_SCENE].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_SCENE].PassCompleteSemaphore, 1 }; + const auto& renderContext = m_RenderPassData[RP_SCENE].RenderContext[0]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{renderContext.GetRenderPassBeginInfo()}; + vkCmdBeginRenderPass( command_buffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS ); + vkCmdExecuteCommands( command_buffer, 1, &m_RenderPassData[RP_SCENE].ObjectsCmdBuffer[whichBuffer].m_VkCommandBuffer ); + vkCmdEndRenderPass( command_buffer ); } // RP_HUD @@ -994,25 +940,19 @@ void Application::Render(float fltDiffTime) if (m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget[0].m_FrameBuffer); + guiCommandBuffer = GetGui()->Render(whichBuffer, m_RenderPassData[RP_HUD].RenderTarget.m_FrameBuffer); if (guiCommandBuffer != VK_NULL_HANDLE) { - BeginRenderPass(whichBuffer, RP_HUD, currentVulkanBuffer.swapchainPresentIdx); - vkCmdExecuteCommands(m_RenderPassData[RP_HUD].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &guiCommandBuffer); - EndRenderPass(whichBuffer, RP_HUD); - - // Submit the commands to the queue. - SubmitRenderPass(whichBuffer, RP_HUD, pWaitSemaphores, DefaultGfxWaitDstStageMasks, { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }); - pWaitSemaphores = { &m_RenderPassData[RP_HUD].PassCompleteSemaphore,1 }; + const auto& renderContext = m_RenderPassData[RP_HUD].RenderContext[0]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{renderContext.GetRenderPassBeginInfo()}; + vkCmdBeginRenderPass( command_buffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS ); + vkCmdExecuteCommands( command_buffer, 1, &guiCommandBuffer ); + vkCmdEndRenderPass( command_buffer ); } } // SGSR 2.0 - { - auto& command_buffer = m_UpscaleAndPostCommandList[whichBuffer]; - command_buffer.Begin(); - // Upscale switch (gUpscaleMode) { case 0: @@ -1026,31 +966,19 @@ void Application::Render(float fltDiffTime) break; } - VkRect2D passArea = {}; - passArea.offset.x = 0; - passArea.offset.y = 0; - passArea.extent.width = vulkan.m_SurfaceWidth; - passArea.extent.height = vulkan.m_SurfaceHeight; - - VkClearColorValue clearColor{.int32 = {}}; - - command_buffer.BeginRenderPass( - passArea, - 0.0f, - 1.0f, - {&clearColor , 1}, - (uint32_t)1, - false/*has depth*/, - m_RenderPassData[RP_BLIT].RenderPass, - true/*swapchain*/, - vulkan.m_SwapchainBuffers[currentVulkanBuffer.swapchainPresentIdx].framebuffer, - VK_SUBPASS_CONTENTS_INLINE ); + const auto& renderContext = m_RenderPassData[RP_BLIT].RenderContext[currentVulkanBuffer.swapchainPresentIdx]; + const fvk::VkRenderPassBeginInfo RPBeginInfo{renderContext.GetRenderPassBeginInfo()}; + vkCmdBeginRenderPass( command_buffer, &RPBeginInfo, VK_SUBPASS_CONTENTS_INLINE ); + const auto scissor = renderContext.GetRenderPassClearData().scissor; + const auto viewport = renderContext.GetRenderPassClearData().viewport; + vkCmdSetScissor( command_buffer, 0, 1, &scissor); + vkCmdSetViewport(command_buffer, 0, 1, &viewport); const TextureVulkan* pUpscaledBuffer = nullptr; switch (gUpscaleMode) { case 0: default: - pUpscaledBuffer = &m_RenderPassData[RP_SCENE].RenderTarget[0].m_ColorAttachments[0]; + pUpscaledBuffer = &m_RenderPassData[RP_SCENE].RenderTarget.m_ColorAttachments[0]; break; case 1: pUpscaledBuffer = apiCast( m_sgsr2_context->GetSceneColorOutput() ); @@ -1065,109 +993,13 @@ void Application::Render(float fltDiffTime) m_BlitQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(outputBufferFlipFlop, "Diffuse", *pUpscaledBuffer); AddDrawableToCmdBuffers( *m_BlitQuadDrawable, &command_buffer, 1, 1, outputBufferFlipFlop ); - command_buffer.EndRenderPass(); - command_buffer.End(); - - // Submit the commands to the queue. - command_buffer.QueueSubmit( pWaitSemaphores, DefaultGfxWaitDstStageMasks, {&m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1}, currentVulkanBuffer.fence ); - pWaitSemaphores = { &m_RenderPassData[RP_BLIT].PassCompleteSemaphore,1 }; - } - - // Queue is loaded up, tell the driver to start processing - vulkan.PresentQueue(pWaitSemaphores, currentVulkanBuffer.swapchainPresentIdx); - vulkan.WaitUntilIdle(); - - // ******************************** - // Application Draw() - End - // ******************************** -} - -//----------------------------------------------------------------------------- -void Application::BeginRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, uint32_t WhichSwapchainImage) -//----------------------------------------------------------------------------- -{ - auto& vulkan = *GetVulkan(); - auto& renderPassData = m_RenderPassData[whichPass]; - bool bisSwapChainRenderPass = whichPass == RP_BLIT; - - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Reset()) - { - LOGE("Pass (%d) command buffer Reset() failed !", whichPass); + vkCmdEndRenderPass( command_buffer ); } + vkEndCommandBuffer( command_buffer ); - if (!m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].Begin()) - { - LOGE("Pass (%d) command buffer Begin() failed !", whichPass); - } + // Submit the commands to the queue. + command_buffer.QueueSubmit( currentVulkanBuffer.semaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vulkan.m_RenderCompleteSemaphore, currentVulkanBuffer.fence ); - VkFramebuffer framebuffer = nullptr; - switch (whichPass) - { - case RP_SCENE: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_HUD: - framebuffer = m_RenderPassData[whichPass].RenderTarget[0].m_FrameBuffer; - break; - case RP_BLIT: - framebuffer = vulkan.m_SwapchainBuffers[WhichSwapchainImage].framebuffer; - break; - default: - framebuffer = nullptr; - break; - } - - assert(framebuffer != nullptr); - - VkRect2D passArea = {}; - passArea.offset.x = 0; - passArea.offset.y = 0; - passArea.extent.width = bisSwapChainRenderPass ? vulkan.m_SurfaceWidth : renderPassData.RenderTarget[0].m_Width; - passArea.extent.height = bisSwapChainRenderPass ? vulkan.m_SurfaceHeight : renderPassData.RenderTarget[0].m_Height; - - TextureFormat swapChainColorFormat = vulkan.m_SurfaceFormat; - auto swapChainColorFormats = std::span({ &swapChainColorFormat, 1 }); - TextureFormat swapChainDepthFormat = vulkan.m_SwapchainDepth.format; - std::span colorFormats = bisSwapChainRenderPass ? swapChainColorFormats : m_RenderPassData[whichPass].RenderTarget[0].m_pLayerFormats; - TextureFormat depthFormat = bisSwapChainRenderPass ? swapChainDepthFormat : m_RenderPassData[whichPass].RenderTarget[0].m_DepthFormat; - - VkClearColorValue clearColor = { renderPassData.RenderPassSetup.ClearColor[0], renderPassData.RenderPassSetup.ClearColor[1], renderPassData.RenderPassSetup.ClearColor[2], renderPassData.RenderPassSetup.ClearColor[3] }; - - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].BeginRenderPass( - passArea, - 0.0f, - 1.0f, - { &clearColor , 1 }, - (uint32_t)colorFormats.size(), - depthFormat != TextureFormat::UNDEFINED, - m_RenderPassData[whichPass].RenderPass, - bisSwapChainRenderPass, - framebuffer, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); -} - - -//----------------------------------------------------------------------------- -void Application::AddPassCommandBuffer(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - if (m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_NumDrawCalls) - { - vkCmdExecuteCommands(m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].m_VkCommandBuffer, 1, &m_RenderPassData[whichPass].ObjectsCmdBuffer[whichBuffer].m_VkCommandBuffer); - } -} - -//----------------------------------------------------------------------------- -void Application::EndRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].EndRenderPass(); -} - -//----------------------------------------------------------------------------- -void Application::SubmitRenderPass(uint32_t whichBuffer, RENDER_PASS whichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence) -//----------------------------------------------------------------------------- -{ - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].End(); - m_RenderPassData[whichPass].PassCmdBuffer[whichBuffer].QueueSubmit(WaitSemaphores, WaitDstStageMasks, SignalSemaphores, CompletionFence); + // Queue is loaded up, tell the driver to start processing + vulkan.PresentQueue(vulkan.m_RenderCompleteSemaphore, currentVulkanBuffer.swapchainPresentIdx); } diff --git a/samples/sgsr2/code/main/application.hpp b/samples/sgsr2/code/main/application.hpp index 8e88228..031c50d 100644 --- a/samples/sgsr2/code/main/application.hpp +++ b/samples/sgsr2/code/main/application.hpp @@ -14,13 +14,11 @@ #include "main/applicationHelperBase.hpp" #include "memory/vulkan/uniform.hpp" #include "vulkan/commandBuffer.hpp" +#include "vulkan/renderPass.hpp" #include #define NUM_SPOT_LIGHTS 4 -class ShaderManager; -class MaterialManager; -class Drawable; namespace SGSR2 { class Context; @@ -99,21 +97,15 @@ struct RenderPassSetupInfo struct RenderPassData { // Pass internal data - RenderPassSetupInfo RenderPassSetup; - VkRenderPass RenderPass = VK_NULL_HANDLE; + RenderPassSetupInfo RenderPassSetup; + std::vector> RenderContext; // context per framebuffer (some passes might all point to the same framebuffers) - // Recorded objects that are set to be drawn on this pass - std::vector< CommandListVulkan> ObjectsCmdBuffer; - - // Command buffer used to dispatch the render pass - std::vector< CommandListVulkan> PassCmdBuffer; - - // Indicates the completing of the underlying render pass - VkSemaphore PassCompleteSemaphore = VK_NULL_HANDLE; + // Recorded objects that are set to be drawn on this pass (secondary) + std::vector ObjectsCmdBuffer; // Render targed used by the underlying render pass - // note: The blit pass uses the backbuffer directly instead this RT - CRenderTargetArray<1> RenderTarget; + // note: The blit pass uses the backbuffer targets directly instead this RT + RenderTarget RenderTarget; }; // ********************** @@ -172,7 +164,6 @@ class Application : public ApplicationHelperBase bool InitGui(uintptr_t windowHandle); bool LoadMeshObjects(); bool InitCommandBuffers(); - bool InitLocalSemaphores(); bool BuildCmdBuffers(); @@ -183,7 +174,7 @@ class Application : public ApplicationHelperBase void BeginRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, uint32_t WhichSwapchainImage); void AddPassCommandBuffer(uint32_t WhichBuffer, RENDER_PASS WhichPass); void EndRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass); - void SubmitRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, const std::span WaitSemaphores, const std::span WaitDstStageMasks, std::span SignalSemaphores, VkFence CompletionFence = (VkFence)nullptr); + void SubmitRenderPass(uint32_t WhichBuffer, RENDER_PASS WhichPass, VkSemaphore WaitSemaphores = VK_NULL_HANDLE, VkPipelineStageFlags WaitDstStageMasks = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VkSemaphore SignalSemaphores = VK_NULL_HANDLE, VkFence CompletionFence = (VkFence)nullptr); void UpdateGui(); bool UpdateUniforms(uint32_t WhichBuffer); @@ -193,6 +184,8 @@ class Application : public ApplicationHelperBase std::unique_ptr m_sgsr2_context_frag; // Render passes + RenderPass m_ObjectRenderPass; + std::array m_RenderPassData; // Command lists @@ -206,12 +199,6 @@ class Application : public ApplicationHelperBase std::unordered_map m_ObjectFragUniforms; // Drawables - std::vector m_SceneDrawables; - std::unique_ptr m_BlitQuadDrawable; - - // Shaders - std::unique_ptr m_ShaderManager; - - // Materials - std::unique_ptr m_MaterialManager; + std::vector m_SceneDrawables; + std::unique_ptr m_BlitQuadDrawable; }; diff --git a/samples/sgsr2/code/main/sgsr2_context.cpp b/samples/sgsr2/code/main/sgsr2_context.cpp index bba054e..3a340ec 100644 --- a/samples/sgsr2/code/main/sgsr2_context.cpp +++ b/samples/sgsr2/code/main/sgsr2_context.cpp @@ -7,9 +7,9 @@ //============================================================================================================ #include "sgsr2_context.hpp" -#include "material/computable.hpp" -#include "material/materialManager.hpp" -#include "material/shaderManager.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/shaderManager.hpp" #include "vulkan/commandBuffer.hpp" #include "memory/vulkan/uniform.hpp" #include @@ -50,8 +50,8 @@ SGSR2::Context::Context() bool SGSR2::Context::Initialize( GraphicsApiBase& gfxApi, - const ShaderManager& shaderManager, - const MaterialManager& materialManager, + const ShaderManager& shaderManager, + const MaterialManager& materialManager, const UpscalerConfiguration& upscaler_configuration, const InputImages& input_images ) { @@ -136,19 +136,13 @@ bool SGSR2::Context::Initialize( target_unique_ptr = std::make_unique( std::move(CreateTextureObject( vulkan, CreateTexObjectInfo{ - uiWidth, - uiHeight, - 1, - 1, - 1, - Format, - TEXTURE_TYPE::TT_COMPUTE_TARGET, - TEXTURE_FLAGS::None, - pName, - 1, - samplerFilter, - samplerAddressMode, - false, + .uiWidth = uiWidth, + .uiHeight = uiHeight, + .Format = Format, + .TexType = TEXTURE_TYPE::TT_COMPUTE_TARGET, + .pName = pName, + .FilterMode = samplerFilter, + .SamplerMode = samplerAddressMode }))); }; @@ -227,10 +221,9 @@ bool SGSR2::Context::Initialize( /////////////////////// auto material = materialManager.CreateMaterial( - gfxApi, *sgsr2_shader, vulkan.m_SwapchainImageCount * 2/*so we always have an even number of descriptors, which makes ping-ponging between history buffers work! */, - [&](const std::string& texName) -> MaterialPass::tPerFrameTexInfo + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo { // Inputs if (texName == "IN_OpaqueColor") @@ -270,11 +263,11 @@ bool SGSR2::Context::Initialize( assert(0); return {}; }, - [&](const std::string& bufferName) -> tPerFrameVkBuffer + [&](const std::string& bufferName) -> PerFrameBuffer { if (bufferName == "ShaderData") { - return {m_upscaler_uniform.vkBuffers.begin(), m_upscaler_uniform.vkBuffers.begin() + vulkan.m_SwapchainImageCount}; + return {m_upscaler_uniform.bufferHandles.begin(), m_upscaler_uniform.bufferHandles.begin() + vulkan.m_SwapchainImageCount}; } assert(0); return {}; @@ -285,7 +278,7 @@ bool SGSR2::Context::Initialize( //////////////////////// // Create the computable to execute the material - auto computable = std::make_unique(vulkan, std::move(material)); + auto computable = std::make_unique>(vulkan, std::move(material)); if (!computable->Init()) { LOGE("Error Creating SGSR computables..."); @@ -328,6 +321,11 @@ void SGSR2::Context::Release(GraphicsApiBase& gfxApi) ReleaseUniformBuffer(&static_cast(gfxApi), m_upscaler_uniform); } +const TextureBase* const SGSR2::Context::GetSceneColorOutput() const +{ + return m_scene_color_output.get(); +} + void SGSR2::Context::UpdateUniforms( Vulkan& vulkan, const Camera& camera, @@ -388,7 +386,7 @@ void SGSR2::Context::Dispatch( { for (const auto& computablePass : m_computable->GetPasses()) { - m_computable->DispatchPass( command_list.m_VkCommandBuffer, computablePass, m_buffer_index ); + m_computable->DispatchPass( command_list, computablePass, m_buffer_index ); } m_buffer_index = (m_buffer_index + 1) % (vulkan.m_SwapchainImageCount * 2); diff --git a/samples/sgsr2/code/main/sgsr2_context.hpp b/samples/sgsr2/code/main/sgsr2_context.hpp index 0bfe13a..7015a0d 100644 --- a/samples/sgsr2/code/main/sgsr2_context.hpp +++ b/samples/sgsr2/code/main/sgsr2_context.hpp @@ -18,13 +18,13 @@ #include #include +template class Computable; +template class MaterialManager; +template class ShaderManager; +class TextureBase; + class GraphicsApiBase; -class Computable; -class MaterialManager; -class ShaderManager; class Shadow; -class Texture; -class CommandList; namespace SGSR2 { @@ -72,16 +72,13 @@ class Context bool Initialize( GraphicsApiBase&, - const ShaderManager&, - const MaterialManager&, + const ShaderManager&, + const MaterialManager&, const UpscalerConfiguration&, const InputImages&); void Release(GraphicsApiBase&); - inline const Texture* const GetSceneColorOutput() const - { - return m_scene_color_output.get(); - } + const TextureBase* const GetSceneColorOutput() const; void UpdateUniforms( Vulkan& vulkan, @@ -96,7 +93,7 @@ class Context glm::vec2 GetJitter() const; protected: - std::unique_ptr m_computable; + std::unique_ptr> m_computable; UpscalerConfiguration m_configuration; @@ -127,4 +124,4 @@ class Context uint32_t m_camera_still_frame_count = 0; }; -}; \ No newline at end of file +}; diff --git a/samples/sgsr2/code/main/sgsr2_context_frag.cpp b/samples/sgsr2/code/main/sgsr2_context_frag.cpp index 4f9d813..64ad541 100644 --- a/samples/sgsr2/code/main/sgsr2_context_frag.cpp +++ b/samples/sgsr2/code/main/sgsr2_context_frag.cpp @@ -7,10 +7,9 @@ //============================================================================================================ #include "sgsr2_context_frag.hpp" -#include "material/drawable.hpp" -#include "material/materialManager.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/shaderManager.hpp" #include "mesh/meshHelper.hpp" #include "vulkan/commandBuffer.hpp" #include "memory/vulkan/uniform.hpp" @@ -51,9 +50,9 @@ SGSR2_Frag::Context::Context() } bool SGSR2_Frag::Context::Initialize( - GraphicsApiBase& gfxApi, - const ShaderManager& shaderManager, - const MaterialManager& materialManager, + GraphicsApiBase& gfxApi, + const ShaderManager& shaderManager, + const MaterialManager& materialManager, const UpscalerConfiguration& upscaler_configuration, const InputImages& input_images ) { @@ -101,16 +100,16 @@ bool SGSR2_Frag::Context::Initialize( auto CreateTextureWithSampler = [&]( auto& target_unique_ptr, - uint32_t uiWidth, - uint32_t uiHeight, - TextureFormat Format, - const char* pName, + uint32_t uiWidth, + uint32_t uiHeight, + TextureFormat Format, + const char* pName, SGSRSamplerType samplerType) - { - SamplerFilter samplerFilter = SamplerFilter::Undefined; - SamplerAddressMode samplerAddressMode = SamplerAddressMode::Undefined; - switch (samplerType) { + SamplerFilter samplerFilter = SamplerFilter::Undefined; + SamplerAddressMode samplerAddressMode = SamplerAddressMode::Undefined; + switch (samplerType) + { case SGSRSamplerType::NEAREST_CLAMP: { samplerFilter = SamplerFilter::Nearest; @@ -129,83 +128,100 @@ bool SGSR2_Frag::Context::Initialize( samplerAddressMode = SamplerAddressMode::ClampEdge; break; } - } + } - target_unique_ptr = std::make_unique( std::move(CreateTextureObject( - vulkan, - CreateTexObjectInfo{ - uiWidth, - uiHeight, - 1, - 1, - 1, - Format, - TEXTURE_TYPE::TT_COMPUTE_TARGET, - TEXTURE_FLAGS::None, - pName, - 1, - samplerFilter, - samplerAddressMode, - false, - }))); - }; + target_unique_ptr = std::make_unique( std::move(CreateTextureObject( + vulkan, + CreateTexObjectInfo{ + uiWidth, + uiHeight, + 1, + 1, + 1, + Format, + TEXTURE_TYPE::TT_COMPUTE_TARGET, + TEXTURE_FLAGS::None, + pName, + Msaa::Samples1, + samplerFilter, + samplerAddressMode, + false, + }))); + }; // // Render passes // - std::array renderPasses{}; + std::array< RenderPass, 2> renderPasses{}; std::array< TextureFormat, 1> convertPassOutputFormats{TextureFormat::R16G16B16A16_SFLOAT}; - if (!vulkan.CreateRenderPass( convertPassOutputFormats, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, RenderPassInputUsage::DontCare, RenderPassOutputUsage::StoreReadOnly, false, RenderPassOutputUsage::Discard, &renderPasses[0] )) + if (!vulkan.CreateRenderPass( convertPassOutputFormats, TextureFormat::UNDEFINED, Msaa::Samples1, RenderPassInputUsage::DontCare, RenderPassOutputUsage::StoreReadOnly, false, RenderPassOutputUsage::Discard, renderPasses[0] )) { return false; } std::array< TextureFormat, 1> upscalePassOutputFormats{TextureFormat::R16G16B16A16_SFLOAT}; - if (!vulkan.CreateRenderPass( upscalePassOutputFormats, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, RenderPassInputUsage::DontCare, RenderPassOutputUsage::StoreReadOnly, false, RenderPassOutputUsage::Discard, &renderPasses[1] )) + if (!vulkan.CreateRenderPass( upscalePassOutputFormats, TextureFormat::UNDEFINED, Msaa::Samples1, RenderPassInputUsage::DontCare, RenderPassOutputUsage::StoreReadOnly, false, RenderPassOutputUsage::Discard, renderPasses[1] )) { return false; } + // + // Image buffers + // if (!m_motion_depth_clip_alpha_buffer.Initialize( &vulkan, upscaler_configuration.render_size.x, upscaler_configuration.render_size.y, convertPassOutputFormats, TextureFormat::UNDEFINED, - renderPasses[0], - VK_NULL_HANDLE, - {}, + Msaa::Samples1, "motion_depth_clip_alpha_buffer" )) { return false; } - if (!m_scene_color_output.Initialize( &vulkan, - upscaler_configuration.display_size.x, - upscaler_configuration.display_size.y, - upscalePassOutputFormats, - TextureFormat::UNDEFINED, - renderPasses[1], - VK_NULL_HANDLE, - {}, - "scene_color_output" )) + if (!m_scene_color_output[0].Initialize( &vulkan, + upscaler_configuration.display_size.x, + upscaler_configuration.display_size.y, + upscalePassOutputFormats, + TextureFormat::UNDEFINED, + Msaa::Samples1, + "scene_color_output0" )) + { + return false; + } + if (!m_scene_color_output[1].Initialize( &vulkan, + upscaler_configuration.display_size.x, + upscaler_configuration.display_size.y, + upscalePassOutputFormats, + TextureFormat::UNDEFINED, + Msaa::Samples1, + "scene_color_output1" )) { return false; } + // + // Framebuffers + // + std::array< Framebuffer, 3> framebuffers{}; + //std::array quadMesh{}; - if (!MeshHelper::CreateMesh( vulkan.GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, sgsr2_shader->m_shaderDescription->m_vertexFormats, &quadMesh)) + if (!MeshHelper::CreateMesh( vulkan.GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, sgsr2_shader->m_shaderDescription->m_vertexFormats, &quadMesh )) { return false; } // Upscale material auto material = materialManager.CreateMaterial( - gfxApi, *sgsr2_shader, vulkan.m_SwapchainImageCount * 2/*so we always have an even number of descriptors, which makes ping-ponging between history buffers work! */, - [&](const std::string& texName) -> MaterialPass::tPerFrameTexInfo + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo { if (texName == "IN_Depth") return {m_input_depth_ref.get()}; @@ -215,36 +231,46 @@ bool SGSR2_Frag::Context::Initialize( return {m_input_color_ref.get()}; else if (texName == "VAR_MotionDepthClipAlphaBuffer") - return {&m_motion_depth_clip_alpha_buffer[0].m_ColorAttachments[0]}; + return {&m_motion_depth_clip_alpha_buffer.m_ColorAttachments[0]}; else if (texName == "VAR_PrevOutput") return {&m_scene_color_output[1].m_ColorAttachments[0], &m_scene_color_output[0].m_ColorAttachments[0]}; assert(0); return {}; - }, - [&](const std::string& bufferName) -> tPerFrameVkBuffer + }, + [&](const std::string& bufferName) -> PerFrameBuffer { if (bufferName == "ShaderData") { - return {m_upscaler_uniform.vkBuffers.begin(), m_upscaler_uniform.vkBuffers.begin() + vulkan.m_SwapchainImageCount}; + return {m_upscaler_uniform.bufferHandles.begin(), m_upscaler_uniform.bufferHandles.begin() + vulkan.m_SwapchainImageCount}; } assert(0); return {}; - }); + } ); // // Create the drawable to execute the fragment shader passes // - auto drawable = std::make_unique(vulkan, std::move(material)); + auto drawable = std::make_unique>(vulkan, std::move(material)); + + static const char* const passNames[]{"CONVERT", "UPSCALE"}; + RenderContext renderContexts[] { + RenderContext(renderPasses[0].Copy(), {}, framebuffers[0], passNames[0]), + RenderContext(renderPasses[1].Copy(), {}, framebuffers[1], passNames[1]), + RenderContext(renderPasses[1].Copy(), {}, framebuffers[2], passNames[1]) + }; - static const char* const passNames[] {"CONVERT", "UPSCALE"}; - if (!drawable->Init(renderPasses, passNames, 0x3, std::move(quadMesh))) + if (!drawable->Init(renderContexts, 0x3, std::move(quadMesh))) { - LOGE("Error Creating SGSR computables..."); + LOGE("Error Creating SGSR2 drawable..."); } m_drawable = std::move(drawable); + m_convertRenderContext = std::move(renderContexts[0]); + m_upscaleRenderContext[0] = std::move(renderContexts[1]); + m_upscaleRenderContext[1] = std::move(renderContexts[2]); + return true; } @@ -254,12 +280,18 @@ void SGSR2_Frag::Context::Release(GraphicsApiBase& gfxApi) m_drawable.reset(); - m_scene_color_output.Release(); + m_scene_color_output[0].Release(); + m_scene_color_output[1].Release(); m_motion_depth_clip_alpha_buffer.Release(); ReleaseUniformBuffer(&vulkan, m_upscaler_uniform); } +const TextureBase* const SGSR2_Frag::Context::GetSceneColorOutput() const +{ + return &m_scene_color_output[(m_buffer_index & 1) ^ 1].m_ColorAttachments[0]; +} + void SGSR2_Frag::Context::UpdateUniforms( Vulkan& vulkan, const Camera& camera, @@ -319,12 +351,12 @@ void SGSR2_Frag::Context::Dispatch( Vulkan& vulkan, CommandListVulkan& command_list) { - command_list.BeginRenderPass( m_motion_depth_clip_alpha_buffer[0], m_motion_depth_clip_alpha_buffer.m_RenderPass, VK_SUBPASS_CONTENTS_INLINE ); - m_drawable->DrawPass( command_list.m_VkCommandBuffer, m_drawable->GetDrawablePasses()[0], m_buffer_index ); + command_list.BeginRenderPass( m_convertRenderContext, VK_SUBPASS_CONTENTS_INLINE ); + m_drawable->DrawPass( command_list, m_drawable->GetDrawablePasses()[0], m_buffer_index ); command_list.EndRenderPass(); - command_list.BeginRenderPass( m_scene_color_output[m_buffer_index&1], m_scene_color_output.m_RenderPass, VK_SUBPASS_CONTENTS_INLINE ); - m_drawable->DrawPass( command_list.m_VkCommandBuffer, m_drawable->GetDrawablePasses()[1], m_buffer_index ); + command_list.BeginRenderPass( m_upscaleRenderContext[m_buffer_index&1], VK_SUBPASS_CONTENTS_INLINE ); + m_drawable->DrawPass( command_list, m_drawable->GetDrawablePasses()[1], m_buffer_index ); command_list.EndRenderPass(); m_buffer_index = (m_buffer_index + 1) % (vulkan.m_SwapchainImageCount * 2); diff --git a/samples/sgsr2/code/main/sgsr2_context_frag.hpp b/samples/sgsr2/code/main/sgsr2_context_frag.hpp index 705e7f1..e93f49e 100644 --- a/samples/sgsr2/code/main/sgsr2_context_frag.hpp +++ b/samples/sgsr2/code/main/sgsr2_context_frag.hpp @@ -10,6 +10,7 @@ #include "main/applicationHelperBase.hpp" #include "vulkan/vulkan.hpp" #include "vulkan/commandBuffer.hpp" +#include "vulkan/renderTarget.hpp" #include "material/computable.hpp" #include "memory/vulkan/uniform.hpp" #include @@ -19,12 +20,13 @@ #include class GraphicsApiBase; -class Drawable; -class MaterialManager; -class ShaderManager; +template class Drawable; +template class MaterialManager; +template class ShaderManager; class Shadow; -class Texture; -class CommandList; +class TextureBase; +template class Texture; +template class CommandList; namespace SGSR2_Frag { @@ -68,16 +70,13 @@ class Context bool Initialize( GraphicsApiBase&, - const ShaderManager&, - const MaterialManager&, + const ShaderManager&, + const MaterialManager&, const UpscalerConfiguration&, const InputImages&); void Release(GraphicsApiBase&); - inline const Texture* const GetSceneColorOutput() const - { - return &m_scene_color_output[(m_buffer_index & 1)^1].m_ColorAttachments[0]; - } + const TextureBase* const GetSceneColorOutput() const; void UpdateUniforms( Vulkan& vulkan, @@ -93,7 +92,7 @@ class Context protected: - std::unique_ptr m_drawable; + std::unique_ptr> m_drawable; UpscalerConfiguration m_configuration{}; UniformArrayT m_upscaler_uniform; @@ -105,13 +104,16 @@ class Context std::unique_ptr m_input_velocity_ref; // Convert output render target - CRenderTargetArray<1> m_motion_depth_clip_alpha_buffer; + RenderTarget m_motion_depth_clip_alpha_buffer; // Upscale Render target (and history color) - CRenderTargetArray<2> m_scene_color_output; + std::array,2> m_scene_color_output; + // RenderContext + RenderContext m_convertRenderContext; + RenderContext m_upscaleRenderContext[2]; int m_jitter_index = 0; int m_buffer_index = 0; uint32_t m_camera_still_frame_count = 0; }; -}; \ No newline at end of file +}; diff --git a/samples/sgsr2/install_apk.bat b/samples/sgsr2/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/sgsr2/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sgsr2/install_config.bat b/samples/sgsr2/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/sgsr2/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sgsr2/project/android/AndroidManifest.xml b/samples/sgsr2/project/android/AndroidManifest.xml index 35bbc15..b2f75f5 100644 --- a/samples/sgsr2/project/android/AndroidManifest.xml +++ b/samples/sgsr2/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -23,7 +22,8 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> - SGSR 2 + SGS Snapdragon GSR v2 diff --git a/samples/sgsr2/img/screenshot.png b/samples/sgsr2/project/img/screenshot.png similarity index 100% rename from samples/sgsr2/img/screenshot.png rename to samples/sgsr2/project/img/screenshot.png diff --git a/samples/shaderResolveTonemap/01_CompileShaders.bat b/samples/shaderResolveTonemap/01_CompileShaders.bat deleted file mode 100644 index fcb833a..0000000 --- a/samples/shaderResolveTonemap/01_CompileShaders.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -mkdir .\Media\Shaders - -@echo. -echo **************************************** -echo Compiling Shaders... -echo **************************************** -for %%i in (shaders\*.vert) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.frag) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) -for %%i in (shaders\*.comp) do ( - call :COMPILE %%i || GOTO COMPILE_FAILED -) - -@echo. -echo **************************************** -echo Copying .json -echo **************************************** -xcopy /y shaders\*.json .\Media\Shaders\. - -@echo. -echo **************************************** -echo Done -echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE -goto :EOF - -:COMPILE -glslangValidator.exe -V %1 -o .\Media\Shaders\%~nx1.spv -IF NOT ERRORLEVEL 1 echo. %1 -^> .\Media\Shaders\%~nx1.spv -goto :EOF - -:COMPILE_FAILED -echo COMPILE FAILED -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/shaderResolveTonemap/02_Install_APK.bat b/samples/shaderResolveTonemap/02_Install_APK.bat deleted file mode 100644 index 920c03e..0000000 --- a/samples/shaderResolveTonemap/02_Install_APK.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\shaderResolveTonemap\outputs\apk\debug\shaderResolveTonemap-debug.apk -@echo **************************************** -call adb install -r -t ..\..\build\android\shaderResolveTonemap\outputs\apk\debug\shaderResolveTonemap-debug.apk -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/shaderResolveTonemap/02_PrepareMedia.bat b/samples/shaderResolveTonemap/02_PrepareMedia.bat deleted file mode 100644 index 943b515..0000000 --- a/samples/shaderResolveTonemap/02_PrepareMedia.bat +++ /dev/null @@ -1,17 +0,0 @@ -@echo off -setlocal - -echo. -echo Copying from vkSampleFrameworkAssets/shared/Media (shared Assets submodule) -echo. -mkdir Media -rmdir /s /q Media\Objects -mkdir Media\Objects -rmdir /s /q Media\Textures -mkdir Media\Textures - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Objects\Skull_Separate.* Media\Objects\. - -xcopy /s /y /k /i ..\..\vkSampleFrameworkAssets\shared\Media\Textures\white_d.ktx Media\Textures\. - -..\..\project\tools\simpletextureconverter ..\..\vkSampleFrameworkAssets\shared\Media\Textures\Skull_Diffuse.jpg .\Media\Textures\Skull_Diffuse.ktx -format R8G8B8A8Unorm diff --git a/samples/shaderResolveTonemap/04_Install_APK.bat b/samples/shaderResolveTonemap/04_Install_APK.bat deleted file mode 100644 index 3c4b4a1..0000000 --- a/samples/shaderResolveTonemap/04_Install_APK.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -pushd . -cd /D "%~dp0" - -@echo. -@echo **************************************** -@echo Install ..\..\build\android\shaderResolveTonemap\outputs\apk\debug\shaderResolveTonemap-debug.apk -@echo **************************************** -adb install -r ..\..\build\android\shaderResolveTonemap\outputs\apk\debug\shaderResolveTonemap-debug.apk - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -popd -IF %0 EQU "%~dpnx0" PAUSE diff --git a/samples/shaderResolveTonemap/06_Adb_Logcat.bat b/samples/shaderResolveTonemap/06_Adb_Logcat.bat deleted file mode 100644 index 6b89aff..0000000 --- a/samples/shaderResolveTonemap/06_Adb_Logcat.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -@echo Logcat... -call adb logcat -c -call adb logcat - -@echo. -@echo **************************************** -@echo Done! -@echo **************************************** -pause \ No newline at end of file diff --git a/samples/shaderResolveTonemap/README.md b/samples/shaderResolveTonemap/README.md deleted file mode 100644 index 8a92c3d..0000000 --- a/samples/shaderResolveTonemap/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# shaderResolveTonemap Sample - -![Screenshot](img/screenshot_1.PNG) - -![Screenshot](img/screenshot_2.PNG) - -## Overview - -ShaderResolveTonemap Uses VK_QCOM_render_pass_shader_resolve to perform a filmic tonemapping operator (on a simple forward rendered scene) as part of the MSAA resolve. -Shader resolve allows creating complex, non-linear filtering of a multisample buffer in the last subpass of a subpass dependency chain. - -Optionally runs the tonemap/resolve as a subpass of the main scene pass. Has onscreen UI controls to modify MSAA sample counts and to enable/disable the shader resolve and use of subpasses (for measuring GPU subpass/shader-resolve efficiency). - -Bandwidth savings are meaused with Snapdragon Profiler. 20% of the bandwidth can be saved in this sample when enabling subpass. From SnapdragonProfiler, we can see that there are four surfaces when subpass is disabled and three surfaces otherwise. There is extra GMEM stores when there is no subpass. - - -![NoSubPassSurfaces](img/nosubpassstage.PNG) - -![SubPassSurfaces](img/subpassstage.PNG) - -Read total and write total have both been reduced when subpass is enable. - -![NoSubPassBandwidth](img/nosubpass.PNG) - -![SubPassBandwidth](img/subpass.PNG) - -## Building - -### Dependencies - -The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. - -* Android SDK -* Andorid NDK -* Gradle -* CMake -* Android Studio - -### Pre-Build - -Compile the underlying shaders to .spv by running the batch file below: - -``` -01_CompileShaders.bat -``` - -Note: The sample assumes the existence of supporting assets under the **'Media'** folder. These assets are not currently distributed with the framework. -The framework team is working to build a centralized asset repository that should minimize these requirements in the near future. - -### Build - -Once the dependencies are installed and shaders compiled, building this sample .apk/.exe is as simple as running any of the batch files from the framework root directory, accordingly to your target system: - -``` -01_BuildAndroid.bat -02_BuildWindows.bat -``` - -### Deploy (android-only) - -To deploy the media files and the .apk to a connected device, run the batch files below: - -``` -02_Install_APK.bat -``` - -If desired, you can keep track of any logging by running one of the logcat batch files (which you can find on the current directory). - -## Android Studio - -This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. - -To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. diff --git a/samples/shaderResolveTonemap/build.gradle b/samples/shaderResolveTonemap/build.gradle deleted file mode 100644 index 98a53e1..0000000 --- a/samples/shaderResolveTonemap/build.gradle +++ /dev/null @@ -1,94 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 30 - lintOptions { - abortOnError false - } - - String rootDir = "${project.rootDir}" - rootDir = rootDir.replace("\\", "/") - - defaultConfig { - applicationId "com.quic.shaderresolvetonemap" - minSdkVersion 26 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - ndkVersion "${project.ndkVersionDefault}" - ndk { - abiFilters 'arm64-v8a' - } - externalNativeBuild { - cmake { - arguments "-DPROJECT_ROOT_DIR=${rootDir}", "-DFRAMEWORK_DIR=${rootDir}/../../framework" - } - } - } - - signingConfigs{ - unsigned{ - storeFile file("${System.env.USERPROFILE}/.android/debug.keystore") - storePassword = "android" - keyAlias = "androiddebugkey" - keyPassword = "android" - v2SigningEnabled = false - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.unsigned - } - debug { - debuggable = true - jniDebuggable = true - } - } - - sourceSets { - main { - jni.srcDirs = [] - manifest.srcFile 'project/android/AndroidManifest.xml' - //java.srcDirs = ['src'] - res.srcDirs = ['project/android/res'] - assets.srcDirs = ['assets'] - - // Uncomment this to enable validation -// jniLibs { -// srcDir "${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs" -// } - } - main.assets { - srcDirs = ['assets'] - // Uncomment this to add Media folder to assets - srcDirs += ['assets_tmp'] - } - } - - dependencies { - } - - externalNativeBuild { - cmake { - version "3.21.0+" - path 'CMakeLists.txt' - } - } - - task copyTmpAssets(type: Copy) { - from "Media" - into "assets_tmp/Media" - } - task removeTmpAssets(type: Delete) { - delete "assets_tmp" - } - - afterEvaluate { - packageRelease.finalizedBy(removeTmpAssets) - } - - preBuild.dependsOn(copyTmpAssets) -} diff --git a/samples/shaderResolveTonemap/code/main/application.cpp b/samples/shaderResolveTonemap/code/main/application.cpp deleted file mode 100644 index 6be42f4..0000000 --- a/samples/shaderResolveTonemap/code/main/application.cpp +++ /dev/null @@ -1,977 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -/// -/// Sample app for Tonemapping in Shader Resolve (MSAA) -/// - -#include "application.hpp" -#include "camera/cameraController.hpp" -#include "camera/cameraControllerTouch.hpp" -#include "gui/imguiVulkan.hpp" -#include "material/drawable.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" -#include "mesh/meshHelper.hpp" -#include "system/math_common.hpp" -#include "texture/vulkan/textureManager.hpp" -#include "imgui.h" -#include - -// Global Variables From Config File -VAR(glm::vec3, gCameraStartPos, glm::vec3(-33.530f, 13.840f, 33.140f), kVariableNonpersistent); -VAR(glm::quat, gCameraStartRot, glm::quat(0.112f, 0.003f, -0.994f, 0.00727102f), kVariableNonpersistent); -VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); -VAR(float, gNearPlane, 0.1f, kVariableNonpersistent); -VAR(float, gFarPlane, 50.0f, kVariableNonpersistent); -VAR(bool, gShaderResolve, false, kVariableNonpersistent); -VAR(bool, gUseSubpasses, false, kVariableNonpersistent); -VAR(bool, gUseRenderPassTransform,false, kVariableNonpersistent); - -VkSampleCountFlagBits gMsaa = VK_SAMPLE_COUNT_2_BIT; - - -/// -/// @brief Implementation of the Application entrypoint (called by the framework) -/// @return Pointer to Application (derived from @ApplicationHelperBase ... derived from @FrameworkApplicationBase). -/// Creates the Application class. Ownership is passed to the calling (framework) function. -/// -FrameworkApplicationBase* Application_ConstructApplication() -{ - return new Application(); -} - -Application::Application() : ApplicationHelperBase() -{ -} - -Application::~Application() -{ -} - -//----------------------------------------------------------------------------- -int Application::PreInitializeSelectSurfaceFormat(std::span formats) -//----------------------------------------------------------------------------- -{ - // On Snapdragon if the surfaceflinger has to do the rotation to the display native orientation then it will do it at 8bit colordepth. - // To avoid this we need to enable the 'pre-rotation' of the display (and the use of VK_QCOM_render_pass_transform so we dont have to rotate our buffers/passes manually). - GetVulkan()->m_UseRenderPassTransform = gUseRenderPassTransform; - - // We want to select a SRGB output format (if one exists) - int index = 0; - for (const auto& format : formats) - { - if (format.format == TextureFormat::B8G8R8A8_SRGB) - return index; - ++index; - } - return -1; -} - -//----------------------------------------------------------------------------- -bool Application::Initialize( uintptr_t windowHandle, uintptr_t hInstance ) -//----------------------------------------------------------------------------- -{ - if( !ApplicationHelperBase::Initialize( windowHandle, hInstance ) ) - { - return false; - } - - auto* const pVulkan = GetVulkan(); - - // Disable shader resolve if it is not exposed by Vulkan. - gShaderResolve = gShaderResolve && pVulkan->GetExtRenderPassShaderResolveAvailable(); - // Disable render pass transform if it is not exposed by Vulkan. - gUseRenderPassTransform = gUseRenderPassTransform && GetVulkan()->GetExtRenderPassTransformAvailable(); - - m_RequestedShaderResolve = gShaderResolve; - m_RequestedUseSubpasses = gUseSubpasses; - m_RequestedUseRenderPassTransform = gUseRenderPassTransform; - m_RequestedMsaa = (VkSampleCountFlagBits) gMsaa; - - LoadShaders(); - - CreateUniformBuffer( pVulkan, m_ObjectVertUniform ); - CreateUniformBuffer( pVulkan, m_ObjectFragUniform ); - - InitGui(windowHandle); - - InitCommandBuffers(); - - InitFramebuffersRenderPassesAndDrawables(); - - InitHdr(); - - m_Camera.SetPosition(gCameraStartPos, gCameraStartRot); - m_Camera.SetAspect(float(gRenderWidth) / float(gRenderHeight)); - m_Camera.SetFov(gFOV); - m_Camera.SetClipPlanes(gNearPlane, gFarPlane); - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::LoadShaders() -//----------------------------------------------------------------------------- -{ - //m_ShaderManager->RegisterRenderPassNames( {sRenderPassNames} ); - - LOGI("Loading Shaders..."); - - typedef std::pair tIdAndFilename; - for (const tIdAndFilename& i : - { tIdAndFilename { "Object", "Media\\Shaders\\Object.json" }, - tIdAndFilename { "Tonemap", "Media\\Shaders\\Tonemap.json" }, - tIdAndFilename { "TonemapMsaa", "Media\\Shaders\\TonemapMsaa.json" }, - tIdAndFilename { "TonemapSubpassMsaa", "Media\\Shaders\\TonemapSubpassMsaa.json" }, - tIdAndFilename { "TonemapSubpassShaderResolve1x", "Media\\Shaders\\TonemapSubpassShaderResolve1x.json" }, - tIdAndFilename { "TonemapSubpassShaderResolve2x", "Media\\Shaders\\TonemapSubpassShaderResolve2x.json" }, - tIdAndFilename { "TonemapSubpassShaderResolve4x", "Media\\Shaders\\TonemapSubpassShaderResolve4x.json" }, - tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" } - }) - { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) - { - LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); - } - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::InitFramebuffersRenderPassesAndDrawables() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - // - // Clean up old render targets and render passes - m_LinearColorRT.Release(); - m_TonemapRT.Release(); - - if (m_BlitRenderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_BlitRenderPass, nullptr); - m_BlitRenderPass = VK_NULL_HANDLE; - } - - // - // Setup formats for the first pass - std::vector PassColorFormats = { TextureFormat::A2B10G10R10_UNORM_PACK32 };// TextureFormat::R8G8B8A8_UNORM };// { TextureFormat::R16G16B16A16_SFLOAT }; - std::vector PassColorMsaa = { gMsaa }; - std::vector PassTextureTypes; - - const TextureFormat DepthFormat = pVulkan->GetBestSurfaceDepthFormat(); - const bool NeedsResolve = gMsaa != VK_SAMPLE_COUNT_1_BIT; - - VkRenderPass ObjectRenderPass = VK_NULL_HANDLE; - if( gUseSubpasses ) - { - // - // Create a single RenderPass with subpasses for objects and then tonemapping - // - - PassTextureTypes = { TT_RENDER_TARGET_SUBPASS }; - if(NeedsResolve && !gShaderResolve) - { - // Intermediate MSAA target for output of tonemap - PassColorFormats.push_back( TextureFormat::A2B10G10R10_UNORM_PACK32 ); - PassColorMsaa.push_back( gMsaa ); - PassTextureTypes.push_back( TT_RENDER_TARGET_SUBPASS ); - } - // Resolved Output - PassColorFormats.push_back( TextureFormat::A2B10G10R10_UNORM_PACK32 ); - PassColorMsaa.push_back( VK_SAMPLE_COUNT_1_BIT ); - PassTextureTypes.push_back( TT_RENDER_TARGET ); - - // Create a compatible RenderPass - if (!gShaderResolve || !GetVulkan()->CreateSubpassShaderResolveRenderPass( { &PassColorFormats[0],1 }, { &PassColorFormats[1],1 }, DepthFormat, *PassColorMsaa.cbegin(), *PassColorMsaa.crbegin(), &ObjectRenderPass )) - { - // Drop to using Create2SubpassRenderPass (no shader resolve) - if (!Create2SubpassRenderPass( { &PassColorFormats[0],1 }, { &PassColorFormats[1],1 }, DepthFormat, *PassColorMsaa.cbegin(), *PassColorMsaa.crbegin(), &ObjectRenderPass )) - { - LOGE( "Unable to create render pass" ); - return false; - } - } - pVulkan->SetDebugObjectName( ObjectRenderPass, "CombinedRenderPass" ); - - m_TonemapSubPassIdx = 1; - } - else - { - // - // Non subpassed. - // Create two seperate passes - Object and Tonemap - // - - // Object render pass - PassTextureTypes = { TT_RENDER_TARGET }; - if( !pVulkan->CreateRenderPass( PassColorFormats, - DepthFormat, - PassColorMsaa.back(), - RenderPassInputUsage::Clear/*color*/, - RenderPassOutputUsage::StoreReadOnly,/*color*/ - true,/*clear depth*/ - RenderPassOutputUsage::Discard,/*depth*/ - &ObjectRenderPass ) ) - { - return false; - } - pVulkan->SetDebugObjectName( ObjectRenderPass, "ObjectRenderPass" ); - - // Tonemap render pass - VkRenderPass TonemapRenderPass = VK_NULL_HANDLE; - - std::vector TonemapColorAndResolveFormats{ TextureFormat::A2B10G10R10_UNORM_PACK32 }; - std::span TonemapResolveFormat{}; - std::vector TonemapColorAndResolveMsaa{ PassColorMsaa.back() }; - std::vector TonemapColorAndResolveTextureTypes{ TT_RENDER_TARGET }; - - if( NeedsResolve ) - { - // Setup to have an additional 1xMSAA buffer for the Resolve after the tonemap - TonemapColorAndResolveFormats.push_back( TextureFormat::A2B10G10R10_UNORM_PACK32 ); - TonemapResolveFormat = { &TonemapColorAndResolveFormats.back(), 1 }; - TonemapColorAndResolveMsaa.push_back( VK_SAMPLE_COUNT_1_BIT ); - TonemapColorAndResolveTextureTypes.push_back( TT_RENDER_TARGET ); - } - - std::span TonemapColorFormat{ &TonemapColorAndResolveFormats.front(),1 }; - if( !pVulkan->CreateRenderPass( TonemapColorFormat, - TextureFormat::UNDEFINED, - TonemapColorAndResolveMsaa.front(), - RenderPassInputUsage::DontCare/*color*/, - RenderPassOutputUsage::StoreReadOnly,/*color*/ - false,/*dont clear depth*/ - RenderPassOutputUsage::Discard,/*depth*/ - &TonemapRenderPass, - TonemapResolveFormat) ) - { - return false; - } - pVulkan->SetDebugObjectName( TonemapRenderPass, "TonemapRenderPass" ); - - // Create the render target for the Tonemap (only needed when passes are seperate - subpass variant has all the rendertarget buffers for both subpasses in m_LinearColorRT) - if( !m_TonemapRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, TonemapColorAndResolveFormats, TextureFormat::UNDEFINED, TonemapRenderPass/*takes ownership*/, (VkRenderPass) VK_NULL_HANDLE, TonemapColorAndResolveMsaa, "Tonemap RT", TonemapColorAndResolveTextureTypes ) ) - { - LOGE( "Error initializing LinearColorRT" ); - return false; - } - - m_TonemapSubPassIdx = 0; // not subpassed! - } - - // Create render target(s) for the scene render (sub) passes. - if (!m_LinearColorRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, PassColorFormats, DepthFormat, ObjectRenderPass, (VkRenderPass)VK_NULL_HANDLE, PassColorMsaa, "Main RT", PassTextureTypes)) - { - LOGE("Error initializing LinearColorRT"); - return false; - } - - // Blit render pass - if( !pVulkan->CreateRenderPass( { &pVulkan->m_SurfaceFormat, 1 }, - pVulkan->m_SwapchainDepth.format, - VK_SAMPLE_COUNT_1_BIT, - RenderPassInputUsage::DontCare/*color*/, - RenderPassOutputUsage::Present,/*color*/ - false,/*dont clear depth*/ - RenderPassOutputUsage::Discard,/*depth*/ - &m_BlitRenderPass ) ) - { - return false; - } - pVulkan->SetDebugObjectName( m_BlitRenderPass, "BlitRenderPass" ); - - // - // Setup the drawables using the correct render passes / subpasses - // - - // Scene (object) drawable - LoadSceneDrawables( ObjectRenderPass, 0, gMsaa ); - - if( gUseSubpasses ) - { - // - // Tonemap drawable - // - ImageInfo tonemapDiffuseImageInfo( m_LinearColorRT[0].m_ColorAttachments.front() ); - tonemapDiffuseImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - std::map tonemapImages = { {"Diffuse", std::move(tonemapDiffuseImageInfo) } }; - - const char* pShaderName = "TonemapSubpassMsaa"; - if( gShaderResolve ) - { - switch( PassColorMsaa.front() ) { - default: pShaderName = "TonemapSubpassShaderResolve1x"; break; - case VK_SAMPLE_COUNT_2_BIT: pShaderName = "TonemapSubpassShaderResolve2x"; break; - case VK_SAMPLE_COUNT_4_BIT: pShaderName = "TonemapSubpassShaderResolve4x"; break; - } - } - // Tonemapping runs in second sub-pass at the MSAA samplerate of its destination buffer (either the shader resolve output buffer @ 1xMSAA or the pre hardware resolve output buffer @ 4xMSAA etc) - m_TonemapDrawable = InitFullscreenDrawable( pShaderName, {}, tonemapImages, ObjectRenderPass, m_TonemapSubPassIdx, PassColorMsaa[1] ); - - // - // Blit drawable - // - std::map blitInputs = { {"Diffuse", &m_LinearColorRT[0].m_ColorAttachments.back() }, - {"Overlay", &m_GuiRT[0].m_ColorAttachments.front() }, - }; - - m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, VK_SAMPLE_COUNT_1_BIT ); - } - else - { - // - // Tonemap drawable - // - std::map tonemapTex = { {"Diffuse", &m_LinearColorRT[0].m_ColorAttachments.front() } }; - if( PassColorMsaa.front() == VK_SAMPLE_COUNT_1_BIT ) - { - m_TonemapDrawable = InitFullscreenDrawable( "Tonemap", tonemapTex, {}, m_TonemapRT.m_RenderPass, 0, PassColorMsaa.front() ); - } - else - { - m_TonemapDrawable = InitFullscreenDrawable( "TonemapMsaa", tonemapTex, {}, m_TonemapRT.m_RenderPass, 0, PassColorMsaa.front() ); - } - - // - // Blit drawable - // - std::map blitInputs = { {"Diffuse", &m_TonemapRT[0].m_ColorAttachments.back() }, - {"Overlay", &m_GuiRT[0].m_ColorAttachments.front() }, - }; - m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, VK_SAMPLE_COUNT_1_BIT ); - } - - return true; -} - -//----------------------------------------------------------------------------- -bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ) -//----------------------------------------------------------------------------- -{ - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); - - const char* pGLTFMeshFile = "./Media/Meshes/Museum.gltf"; - - LOGI( "Loading Scene Drawable %s", pGLTFMeshFile ); - - auto* const pVulkan = GetVulkan(); - - const auto* pObjectShader = m_ShaderManager->GetShader( "Object" ); - if( !pObjectShader ) - { - // Error (missing shader) - return false; - } - - auto* whiteTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, "Textures/white_d.ktx", m_SamplerEdgeClamp); - if (!whiteTexture) - { - return false; - } - - // Lambda to load a texture for the given material (slot) - const auto& textureLoader = [&]( const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName ) -> const MaterialPass::tPerFrameTexInfo - { - auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerEdgeClamp); - if (!diffuseTexture) - { - return { whiteTexture }; - } - else - { - return { diffuseTexture }; - } - }; - - // Lambda to associate (uniform) buffers with their shader binding/slot names. - const auto& bufferLoader = [&]( const std::string& bufferSlotName ) -> tPerFrameVkBuffer { - if( bufferSlotName == "Vert" ) - { - return { m_ObjectVertUniform.buf.GetVkBuffer() }; - } - else if( bufferSlotName == "Frag" ) - { - return { m_ObjectFragUniform.buf.GetVkBuffer() }; - } - else - { - assert( 0 ); - return {}; - } - }; - - const auto& materialLoader = [&]( const MeshObjectIntermediate::MaterialDef& materialDef ) -> std::optional - { - using namespace std::placeholders; - return std::move( m_MaterialManager->CreateMaterial( *pVulkan, *pObjectShader, NUM_VULKAN_BUFFERS, std::bind( textureLoader, std::cref( materialDef ), _1 ), bufferLoader ) ); - }; - - - static const char* opaquePassName = "RP_OPAQUE"; - const VkSampleCountFlagBits renderPassMultisamples[1] = { passMsaa }; - const uint32_t renderPassSubpasses[1] = { subpassIdx }; - - m_SceneObject.clear(); - - if( !DrawableLoader::LoadDrawables( *pVulkan, *m_AssetManager, { &renderPass,1 }, &opaquePassName, pGLTFMeshFile, materialLoader, m_SceneObject, renderPassMultisamples, DrawableLoader::LoaderFlags::None, renderPassSubpasses ) ) - { - LOGE( "Error loading Object mesh: %s", pGLTFMeshFile ); - return false; - } - - return true; -} - - -//----------------------------------------------------------------------------- -std::unique_ptr Application::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ) -//----------------------------------------------------------------------------- -{ - // - // Tonemap pass is a fullscreen quad, setup a 'drawable'. - // - - auto* const pVulkan = GetVulkan(); - - LOGI("Creating %s mesh...", pShaderName); - - const auto* pShader = m_ShaderManager->GetShader(pShaderName); - assert(pShader); - if( !pShader ) - { - LOGE("Error, %s shader is unknown (not loaded?)", pShaderName); - return nullptr; - } - - MeshObject mesh; - if (!MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pShader->m_shaderDescription->m_vertexFormats, &mesh)) - { - LOGE("Error creating Fullscreen Mesh (for %s)", pShaderName); - return nullptr; - } - - auto shaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pShader, NUM_VULKAN_BUFFERS, - [this, &ColorAttachmentsLookup](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { - return { ColorAttachmentsLookup.find(texName)->second }; - assert(0); - return {}; - }, - []( const std::string& bufferName ) -> const tPerFrameVkBuffer { - assert(0); - return {}; - }, - [this, &ImageAttachmentsLookup]( const std::string& imageName ) -> const ImageInfo { - return { ImageAttachmentsLookup.find(imageName)->second }; - } - ); - - static const char* passName = "Fullscreen"; - const VkSampleCountFlagBits renderPassMultisamples[1] = { passMsaa }; - const uint32_t renderPassSubpasses[1] = { subpassIdx }; - - auto drawable = std::make_unique( *pVulkan, std::move( shaderMaterial ) ); - if( !drawable->Init( renderPass, passName, std::move(mesh), std::nullopt, std::nullopt, renderPassMultisamples, renderPassSubpasses ) ) - { - LOGE("Error creating Blit Drawable"); - return nullptr; - } - - return std::move(drawable); -} - - -//----------------------------------------------------------------------------- -bool Application::InitCommandBuffers() -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - char szName[256]; - for( uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++ ) - { - sprintf( szName, "CommandBuffer (%d of %d)", WhichBuffer + 1, NUM_VULKAN_BUFFERS ); - if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY ) ) - { - return false; - } - } - return true; -} - - -//----------------------------------------------------------------------------- -void Application::Render(float fltDiffTime) -//----------------------------------------------------------------------------- -{ - UpdateGui(); - - UpdateCamera( fltDiffTime ); - - auto* const pVulkan = GetVulkan(); - - // See if we want to change the MSAA mode - if( gShaderResolve != m_RequestedShaderResolve || gUseSubpasses != m_RequestedUseSubpasses || gMsaa != m_RequestedMsaa || gUseRenderPassTransform != m_RequestedUseRenderPassTransform) - { - ChangeMsaaMode(); - } - - auto CurrentVulkanBuffer = pVulkan->SetNextBackBuffer(); - - UpdateUniforms( CurrentVulkanBuffer.idx ); - - UpdateCommandBuffer( CurrentVulkanBuffer.idx ); - - // ... submit the command buffer to the device queue - m_CommandBuffer[CurrentVulkanBuffer.idx].QueueSubmit( CurrentVulkanBuffer, pVulkan->m_RenderCompleteSemaphore ); - // and Present - PresentQueue( pVulkan->m_RenderCompleteSemaphore, CurrentVulkanBuffer.swapchainPresentIdx ); -} - - -//----------------------------------------------------------------------------- -void Application::UpdateCamera(float elapsedTime) -//----------------------------------------------------------------------------- -{ - if (m_CameraController) - m_Camera.UpdateController(elapsedTime, *m_CameraController); - m_Camera.UpdateMatrices(); -} - - -//----------------------------------------------------------------------------- -bool Application::UpdateUniforms( uint32_t bufferIdx ) -//----------------------------------------------------------------------------- -{ - ObjectVertUniform data{ }; - data.MVPMatrix = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix(); - data.ModelMatrix = glm::identity(); - UpdateUniformBuffer( GetVulkan(), m_ObjectVertUniform, data ); - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::UpdateCommandBuffer(uint32_t bufferIdx) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - auto& commandBuffer = m_CommandBuffer[bufferIdx]; - - if( !commandBuffer.Begin( m_LinearColorRT[0].m_FrameBuffer, m_LinearColorRT.m_RenderPass ) ) - { - return false; - } - - VkRect2D Scissor = {}; - Scissor.offset.x = 0; - Scissor.offset.y = 0; - Scissor.extent.width = m_LinearColorRT[0].m_Width; - Scissor.extent.height = m_LinearColorRT[0].m_Height; - - VkClearColorValue ClearColor[1]{{ 0.1f, 0.0f, 0.0f, 0.0f }}; - - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, m_LinearColorRT[0].GetNumColorLayers(), true, m_LinearColorRT.m_RenderPass, false, m_LinearColorRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) - { - return false; - } - - for(const auto& sceneObjectDrawable : m_SceneObject) - AddDrawableToCmdBuffers(sceneObjectDrawable, &commandBuffer, 1, 1); - - // - // Now add the tonemapping... - // - if( gUseSubpasses ) - { - // Tonemap is in (the next) subpass. - commandBuffer.NextSubpass( VK_SUBPASS_CONTENTS_INLINE ); - } - else - { - // Tonemap has its own render pass. - commandBuffer.EndRenderPass(); - - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_TonemapRT.m_RenderPass, false, m_TonemapRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) - { - return false; - } - } - - AddDrawableToCmdBuffers( *m_TonemapDrawable, &commandBuffer, 1, 1 ); - - commandBuffer.EndRenderPass(); - - // - // Do the Gui - // - if (m_Gui) - { - // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, true, m_GuiRT.m_RenderPass, false, m_GuiRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) - { - return false; - } - GetGui()->Render(commandBuffer.m_VkCommandBuffer); - - commandBuffer.EndRenderPass(); - } - - // - // Now add the final Blit to swapchain backbuffer... - // - - Scissor.extent.width = pVulkan->m_SurfaceWidth; - Scissor.extent.height = pVulkan->m_SurfaceHeight; - - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, ClearColor, 1, false, m_BlitRenderPass, true/*swapchain*/, pVulkan->m_SwapchainBuffers[bufferIdx].framebuffer, VK_SUBPASS_CONTENTS_INLINE ) ) - { - return false; - } - - AddDrawableToCmdBuffers( *m_BlitDrawable, &commandBuffer, 1, 1 ); - - commandBuffer.EndRenderPass(); - - commandBuffer.End(); - - return true; -} - - -//----------------------------------------------------------------------------- -bool Application::Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, VkSampleCountFlagBits InternalMsaa, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/ ) -//----------------------------------------------------------------------------- -{ - assert(pRenderPass && *pRenderPass == VK_NULL_HANDLE); // check not already allocated and that we have a location to place the renderpass handle - assert( !InternalColorFormats.empty() ); // not supporting a depth only pass - assert( !OutputColorFormats.empty() ); - - const bool NeedsResolve = InternalMsaa != OutputMsaa; - - // Color attachments and Depth attachment - std::vector PassAttachDescs; - PassAttachDescs.reserve(InternalColorFormats.size()+OutputColorFormats.size()+2); - - // Each subpass needs a reference to its attachments - std::array SubpassDesc { - VkSubpassDescription {0/*flags*/, VK_PIPELINE_BIND_POINT_GRAPHICS}, - VkSubpassDescription {0/*flags*/, VK_PIPELINE_BIND_POINT_GRAPHICS} }; - std::vector ColorReferencesPass0; - ColorReferencesPass0.reserve(InternalColorFormats.size()); - - std::vector InputReferencesPass1; - std::vector ColorReferencesPass1; - std::vector ResolveReferencesPass1; - InputReferencesPass1.reserve(InternalColorFormats.size()); - ColorReferencesPass1.reserve(OutputColorFormats.size()); - ResolveReferencesPass1.reserve(OutputColorFormats.size()); - - const bool HasDepth = InternalDepthFormat != TextureFormat::UNDEFINED; - const bool HasPass2ReadDepth = HasDepth && false; - - // - // First Pass Color Attachments (what is written by the first pass) - // Also setup as inputs to the second pass. - for(const auto& ColorFormat: InternalColorFormats) - { - // Pass0 color and depth buffers setup to clear on load, discard on end (of entire pass). - VkAttachmentDescription AttachmentDescPass0 = { 0/*flags*/, - TextureFormatToVk(ColorFormat)/*format*/, - InternalMsaa/*samples*/, - VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_UNDEFINED/*initialLayout*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/}; - PassAttachDescs.push_back( AttachmentDescPass0 ); - - ColorReferencesPass0.push_back({ (uint32_t) PassAttachDescs.size()-1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL/*output of first pass*/ }); - InputReferencesPass1.push_back({ (uint32_t) PassAttachDescs.size()-1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*input of second pass*/ }); - } - - // - // Second Pass Color Attachments (these are the outputs from the second pass) - for(const auto& ColorFormat: OutputColorFormats) - { - // Pass1 color buffers setup to, store on end (of entire pass). - VkAttachmentDescription AttachmentDescPass1 = { 0, - TextureFormatToVk(ColorFormat)/*format*/, - (gShaderResolve||!NeedsResolve) ? OutputMsaa : InternalMsaa/*samples*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, - (gShaderResolve||!NeedsResolve) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_UNDEFINED/*initialLayout*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/}; - PassAttachDescs.push_back( AttachmentDescPass1 ); - - ColorReferencesPass1.push_back({ (uint32_t) PassAttachDescs.size()-1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*output of second pass*/ }); - } - - if ( NeedsResolve && !gShaderResolve ) - { - // Shader Resolve not available, drop back to resolving at the end of the pass. - - // - // Second pass Resolve Attachments (output) - for( const auto& ColorFormat : OutputColorFormats ) - { - // Pass1 color buffers to resolve to at end of pass. - VkAttachmentDescription AttachmentDescResolvePass1 = { 0, - TextureFormatToVk(ColorFormat)/*format*/, - OutputMsaa/*samples*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, - VK_ATTACHMENT_STORE_OP_STORE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_UNDEFINED/*initialLayout*/, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/*finalLayout*/}; - - PassAttachDescs.push_back( AttachmentDescResolvePass1 ); - ResolveReferencesPass1.push_back( { (uint32_t) PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL } ); - } - - // - // Setup the resolve (second subpass) - SubpassDesc[1].pResolveAttachments = ResolveReferencesPass1.data(); - } - - // - // Depth Attachment (cleared at start of pass, written by first subpass, discarded after pass) - VkAttachmentReference DepthReference = {}; - VkAttachmentReference DepthReferencePass1 = {}; - if (HasDepth) - { - VkAttachmentDescription AttachmentDescDepthPass0 = { 0/*flags*/, - TextureFormatToVk(InternalDepthFormat)/*format*/, - InternalMsaa/*samples*/, - VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, - VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, - VK_ATTACHMENT_STORE_OP_DONT_CARE/*stencilStoreOp*/, - VK_IMAGE_LAYOUT_UNDEFINED/*initialLayout*/, - HasPass2ReadDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL/*finalLayout*/}; - - PassAttachDescs.push_back( AttachmentDescDepthPass0 ); - DepthReference = { (uint32_t) PassAttachDescs.size() - 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; - if (HasPass2ReadDepth) - InputReferencesPass1.push_back({ (uint32_t) PassAttachDescs.size()-1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL/*input of second pass*/ }); - } - - if( !NeedsResolve ) - { - // Doesnt need to do a resolve (shader or otherwise) - msaa is same for input and output from this pass. - } - else if( gShaderResolve ) - { - // Shader Resolve extension (VK_QCOM_render_pass_shader_resolve) is available, use it. - assert( OutputMsaa == VK_SAMPLE_COUNT_1_BIT ); - SubpassDesc[1].flags = 0x00000008/*VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM*/ | 0x00000004/*VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM*/; - } - - // - // Subpass dependencies - std::array PassDependencies = {}; - if (ColorReferencesPass0.size() > 0) - { - SubpassDesc[0].colorAttachmentCount = (uint32_t)ColorReferencesPass0.size(); - SubpassDesc[0].pColorAttachments = ColorReferencesPass0.data(); - SubpassDesc[1].inputAttachmentCount = (uint32_t)InputReferencesPass1.size(); - SubpassDesc[1].pInputAttachments = InputReferencesPass1.data(); - } - if (ColorReferencesPass1.size() > 0) - { - SubpassDesc[1].colorAttachmentCount = (uint32_t)ColorReferencesPass1.size(); - SubpassDesc[1].pColorAttachments = ColorReferencesPass1.data(); - } - - // Only first pass writes the Depth - if( HasDepth ) - { - SubpassDesc[0].pDepthStencilAttachment = &DepthReference; - } - - // Color subpass (takes output of first subpass as input to fragment shader) - PassDependencies[0].srcSubpass = 0; - PassDependencies[0].dstSubpass = 1; - PassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - PassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - PassDependencies[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - PassDependencies[0].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; - PassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - // Now ready to actually create the render pass - VkRenderPassCreateInfo RenderPassInfo {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; - RenderPassInfo.flags = 0; - RenderPassInfo.attachmentCount = (uint32_t) PassAttachDescs.size(); - RenderPassInfo.pAttachments = PassAttachDescs.data(); - RenderPassInfo.subpassCount = (uint32_t) SubpassDesc.size(); - RenderPassInfo.pSubpasses = SubpassDesc.data(); - RenderPassInfo.dependencyCount = (uint32_t) PassDependencies.size(); - RenderPassInfo.pDependencies = PassDependencies.data(); - - VkResult RetVal = vkCreateRenderPass(GetVulkan()->m_VulkanDevice, &RenderPassInfo, NULL, pRenderPass); - if (!CheckVkError("vkCreateRenderPass()", RetVal)) - { - return false; - } - - return true; -} - - -//----------------------------------------------------------------------------- -void Application::ChangeMsaaMode() -//----------------------------------------------------------------------------- -{ - gShaderResolve = m_RequestedShaderResolve; - gUseSubpasses = m_RequestedUseSubpasses; - gMsaa = m_RequestedMsaa; - gUseRenderPassTransform = m_RequestedUseRenderPassTransform; - - GetVulkan()->m_UseRenderPassTransform = gUseRenderPassTransform; - - GetVulkan()->RecreateSwapChain(); - - InitFramebuffersRenderPassesAndDrawables(); -} - - -//----------------------------------------------------------------------------- -bool Application::InitHdr() -//----------------------------------------------------------------------------- -{ - // Set the color profile - VkHdrMetadataEXT AuthoringProfile = {VK_STRUCTURE_TYPE_HDR_METADATA_EXT}; - AuthoringProfile.displayPrimaryRed.x = 0.680f; - AuthoringProfile.displayPrimaryRed.y = 0.320f; - AuthoringProfile.displayPrimaryGreen.x = 0.265f; - AuthoringProfile.displayPrimaryGreen.y = 0.690f; - AuthoringProfile.displayPrimaryBlue.x = 0.150f; - AuthoringProfile.displayPrimaryBlue.y = 0.060f; - AuthoringProfile.whitePoint.x = 0.3127f; - AuthoringProfile.whitePoint.y = 0.3290f; - AuthoringProfile.maxLuminance = 80.0f;// 1000.f; - AuthoringProfile.minLuminance = 0.001f; - AuthoringProfile.maxContentLightLevel = 2000.f; - AuthoringProfile.maxFrameAverageLightLevel = 1000.f; - return GetVulkan()->SetSwapchainHrdMetadata(AuthoringProfile); -} - - -//----------------------------------------------------------------------------- -bool Application::InitGui(uintptr_t windowHandle) -//----------------------------------------------------------------------------- -{ - auto* const pVulkan = GetVulkan(); - - const TextureFormat GuiColorFormat[]{ TextureFormat::R8G8B8A8_UNORM }; - const TEXTURE_TYPE GuiTextureTypes[]{ TT_RENDER_TARGET }; - - if( !m_GuiRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, GuiColorFormat, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "Gui RT", GuiTextureTypes ) ) - { - return false; - } - - m_Gui = std::make_unique>(*pVulkan, m_GuiRT.m_RenderPass); - if (!m_Gui->Initialize(windowHandle, m_GuiRT[0].m_Width, m_GuiRT[0].m_Height)) - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -void Application::UpdateGui() -//----------------------------------------------------------------------------- -{ - if (m_Gui) - { - // Update Gui - m_Gui->Update(); - - // Begin our window. - static bool settingsOpen = true; - ImGui::SetNextWindowSize(ImVec2((gRenderWidth * 3.0f) / 4.0f, 500.f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2((float)gRenderWidth / 8.0f, gRenderHeight / 2.0f), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Settings", &settingsOpen, (ImGuiWindowFlags)0)) - { - // Add our widgets - if( GetVulkan()->GetExtRenderPassShaderResolveAvailable() ) - { - ImGui::Checkbox( "Shader resolve", &m_RequestedShaderResolve ); - } - else - { - ImGui::Text( "Shader Resolve extension not available" ); - } - ImGui::Checkbox("Use Subpasses", &m_RequestedUseSubpasses); - if (GetVulkan()->GetExtRenderPassTransformAvailable()) - { - ImGui::Checkbox("Use Renderpass Transform", &m_RequestedUseRenderPassTransform); - } - else - { - ImGui::Text("Renderpass transform extension not available"); - } - - int iMsaa = (int) std::bitset<32>(m_RequestedMsaa - 1).count(); - ImGui::Combo( "MSAA", &iMsaa, "x1\0x2\0x4\0" ); - m_RequestedMsaa = (VkSampleCountFlagBits) (1<m_VulkanDevice, m_BlitRenderPass, nullptr ); - m_BlitRenderPass = VK_NULL_HANDLE; - } - - // Finally call into base class destroy - ApplicationHelperBase::Destroy(); -} diff --git a/samples/shaderResolveTonemap/code/main/application.hpp b/samples/shaderResolveTonemap/code/main/application.hpp deleted file mode 100644 index 4db4a8b..0000000 --- a/samples/shaderResolveTonemap/code/main/application.hpp +++ /dev/null @@ -1,118 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ -#pragma once - -/// -/// @file application.hpp -/// @brief Application to test gl_SampleMaskIn shader intrinsic. -/// -/// Very simple application that attempts to use gl_SampleMaskIn. -/// - -#include "main/applicationHelperBase.hpp" -#include -#include -#include "vulkan/commandBuffer.hpp" -#include "vulkan/renderTarget.hpp" -#include "memory/vulkan/uniform.hpp" -#include "vulkan/vulkan_support.hpp" - -// Forward declarations -class Drawable; -class ShaderManager; -class MaterialManager; -struct ImageInfo; - - -class Application : public ApplicationHelperBase -{ -public: - Application(); - ~Application() override; - - // - // Override ApplicationHelperBase - // - - /// Override surface format selection. - int PreInitializeSelectSurfaceFormat(std::span formats) override; - - /// Override Application entry point! - bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; - - bool LoadShaders(); - - bool InitFramebuffersRenderPassesAndDrawables(); - bool LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ); - std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& inputLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ); - bool InitCommandBuffers(); - bool Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, VkSampleCountFlagBits InternalMsaa, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/ ); - bool InitHdr(); - - /// @brief Ticked every frame (by the Framework) - /// @param fltDiffTime time (in seconds) since the last call to Render. - void Render( float fltDiffTime ) override; - - void UpdateCamera( float elapsedTime ); - bool UpdateUniforms( uint32_t bufferIdx ); - bool UpdateCommandBuffer( uint32_t bufferIdx ); - - void ChangeMsaaMode(); - - bool InitGui( uintptr_t windowHandle ); - void UpdateGui(); - - /// Shutdown function - void Destroy() override; - -private: - - // Materials - - // Drawable(s) for rendering the main scene object - std::vector m_SceneObject; - - // Drawable for tonemapping - std::unique_ptr m_TonemapDrawable; - - // Drawable for final blit to back buffer - std::unique_ptr m_BlitDrawable; - - // Uniform buffers - struct ObjectVertUniform - { - glm::mat4 MVPMatrix; - glm::mat4 ModelMatrix; - }; - UniformT m_ObjectVertUniform; - struct ObjectfragUniform - { - float tmp; - }; - UniformT m_ObjectFragUniform; - - // Render target for Objects. - CRenderTargetArray<1> m_LinearColorRT; - - // Render target for tonemap (when not running as part of a subpass chain) - CRenderTargetArray<1> m_TonemapRT; - - // Render target for GUI. - CRenderTargetArray<1> m_GuiRT; - - // Single command buffer - Wrap_VkCommandBuffer m_CommandBuffer[NUM_VULKAN_BUFFERS]; - - uint32_t m_TonemapSubPassIdx = 0; - VkRenderPass m_BlitRenderPass = VK_NULL_HANDLE; - - bool m_RequestedShaderResolve = false; - bool m_RequestedUseSubpasses = false; - bool m_RequestedUseRenderPassTransform = false; - VkSampleCountFlagBits m_RequestedMsaa = VK_SAMPLE_COUNT_1_BIT; -}; diff --git a/samples/shaderResolveTonemap/img/nosubpass.PNG b/samples/shaderResolveTonemap/img/nosubpass.PNG deleted file mode 100644 index ba8679c..0000000 Binary files a/samples/shaderResolveTonemap/img/nosubpass.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/img/nosubpssstage.PNG b/samples/shaderResolveTonemap/img/nosubpssstage.PNG deleted file mode 100644 index f079548..0000000 Binary files a/samples/shaderResolveTonemap/img/nosubpssstage.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/img/screenshot_1.PNG b/samples/shaderResolveTonemap/img/screenshot_1.PNG deleted file mode 100644 index 73e411e..0000000 Binary files a/samples/shaderResolveTonemap/img/screenshot_1.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/img/screenshot_2.PNG b/samples/shaderResolveTonemap/img/screenshot_2.PNG deleted file mode 100644 index 658b901..0000000 Binary files a/samples/shaderResolveTonemap/img/screenshot_2.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/img/subpass.PNG b/samples/shaderResolveTonemap/img/subpass.PNG deleted file mode 100644 index 2ce0527..0000000 Binary files a/samples/shaderResolveTonemap/img/subpass.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/img/subpassstage.PNG b/samples/shaderResolveTonemap/img/subpassstage.PNG deleted file mode 100644 index 959a987..0000000 Binary files a/samples/shaderResolveTonemap/img/subpassstage.PNG and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/AndroidManifest.xml b/samples/shaderResolveTonemap/project/android/AndroidManifest.xml deleted file mode 100644 index 60912c5..0000000 --- a/samples/shaderResolveTonemap/project/android/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/shaderResolveTonemap/project/android/res/mipmap-hdpi/ic_launcher.png b/samples/shaderResolveTonemap/project/android/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 1b58b37..0000000 Binary files a/samples/shaderResolveTonemap/project/android/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/res/mipmap-mdpi/ic_launcher.png b/samples/shaderResolveTonemap/project/android/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 11acf77..0000000 Binary files a/samples/shaderResolveTonemap/project/android/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/res/mipmap-xhdpi/ic_launcher.png b/samples/shaderResolveTonemap/project/android/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index b8016f2..0000000 Binary files a/samples/shaderResolveTonemap/project/android/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/res/mipmap-xxhdpi/ic_launcher.png b/samples/shaderResolveTonemap/project/android/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index c0b9e85..0000000 Binary files a/samples/shaderResolveTonemap/project/android/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/res/mipmap-xxxhdpi/ic_launcher.png b/samples/shaderResolveTonemap/project/android/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 7df4d52..0000000 Binary files a/samples/shaderResolveTonemap/project/android/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/samples/shaderResolveTonemap/project/android/res/values/strings.xml b/samples/shaderResolveTonemap/project/android/res/values/strings.xml deleted file mode 100644 index 3a95db5..0000000 --- a/samples/shaderResolveTonemap/project/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Shader Resolve Tonemap - diff --git a/samples/shaderResolveTonemap/project/android/res/values/styles.xml b/samples/shaderResolveTonemap/project/android/res/values/styles.xml deleted file mode 100644 index f591923..0000000 --- a/samples/shaderResolveTonemap/project/android/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/samples/shaderResolveTonemap/shaders/Blit.frag b/samples/shaderResolveTonemap/shaders/Blit.frag deleted file mode 100644 index a9d6d7e..0000000 --- a/samples/shaderResolveTonemap/shaders/Blit.frag +++ /dev/null @@ -1,40 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 -#define SHADER_OVERLAY_TEXTURE_LOC 1 - -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - FragColor = texture( u_DiffuseTex, LocalTexCoord ); - - // Overlay (GUI) - vec4 OverlayColor = texture( u_OverlayTex, LocalTexCoord ); - FragColor.rgb = FragColor.rgb*(1.0-OverlayColor.a) + OverlayColor.rgb; - FragColor.a = 1.0; -} - diff --git a/samples/shaderResolveTonemap/shaders/Blit.json b/samples/shaderResolveTonemap/shaders/Blit.json deleted file mode 100644 index 5fad1f3..0000000 --- a/samples/shaderResolveTonemap/shaders/Blit.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/Blit.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Overlay" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/Fullscreen.vert b/samples/shaderResolveTonemap/shaders/Fullscreen.vert deleted file mode 100644 index cdd4f93..0000000 --- a/samples/shaderResolveTonemap/shaders/Fullscreen.vert +++ /dev/null @@ -1,28 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -#define SHADER_ATTRIB_LOC_POSITION 0 -#define SHADER_ATTRIB_LOC_TEXCOORD0 1 - -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; -layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; - -// Varying's -layout (location = 0) out vec2 v_TexCoord; - -void main() -{ - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) - vec4 TempPos = vec4(a_Position.xyz, 1.0); - gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); - v_TexCoord = vec2(a_TexCoord.xy); -} diff --git a/samples/shaderResolveTonemap/shaders/Object.json b/samples/shaderResolveTonemap/shaders/Object.json deleted file mode 100644 index 6c9b9d5..0000000 --- a/samples/shaderResolveTonemap/shaders/Object.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "RP_OPAQUE", - "Shaders": { - "Vertex": "Media/Shaders/Object.vert.spv", - "Fragment": "Media/Shaders/Object.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Vertex" ], - "Names": [ "Vert" ] - }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Frag" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Normal" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ], - "FixedFunction": { - "CullBackFace": true, - "DepthTestEnable": true, - "DepthWriteEnable": true - } - } - ], - "Vertex": [ - { - "Span": 60, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "Normal", - "Offset": 12, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 24, - "Type": "Vec2" - }, - { - "Name": "Color", - "Offset": 32, - "Type": "Vec4" - }, - { - "Name": "Tangent", - "Offset": 48, - "Type": "Vec3" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/Tonemap.frag b/samples/shaderResolveTonemap/shaders/Tonemap.frag deleted file mode 100644 index c63437d..0000000 --- a/samples/shaderResolveTonemap/shaders/Tonemap.frag +++ /dev/null @@ -1,104 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -#define SHADER_DIFFUSE_SUBPASS_INPUT 0 - -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Get base color from the color texture - vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // ******************************** - // Alpha Blending - // ******************************** - FragColor.rgb = DiffuseColor.rgb; - FragColor.a = 1.0; - - // Color mapping - FragColor.rgb = ACESFilmic(FragColor.rgb); - - // ******************************** - // sRGB conversion (if speed was a factor we would probably hardcode and have 2 blit shaders) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/Tonemap.json b/samples/shaderResolveTonemap/shaders/Tonemap.json deleted file mode 100644 index c6b9fac..0000000 --- a/samples/shaderResolveTonemap/shaders/Tonemap.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/Tonemap.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/TonemapMsaa.frag b/samples/shaderResolveTonemap/shaders/TonemapMsaa.frag deleted file mode 100644 index 0f1219b..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapMsaa.frag +++ /dev/null @@ -1,100 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2DMS u_DiffuseTex; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.5; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - // ******************************** - // Texture Colors - // ******************************** - // Get base color from the multisampled color texture - vec4 DiffuseColor = texelFetch( u_DiffuseTex, ivec2(gl_FragCoord.xy), gl_SampleID ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // ******************************** - // Alpha Blending - // ******************************** - FragColor.rgb = DiffuseColor.rgb; - FragColor.a = 1.0; - - // Color mapping - FragColor.rgb = ACESFilmic(FragColor.rgb); - - // ******************************** - // sRGB conversion (if speed was a factor we would probably hardcode and have 2 blit shaders) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/TonemapMsaa.json b/samples/shaderResolveTonemap/shaders/TonemapMsaa.json deleted file mode 100644 index e1efc3c..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapMsaa.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/TonemapMsaa.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.frag b/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.frag deleted file mode 100644 index e500763..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.frag +++ /dev/null @@ -1,105 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -#define SHADER_DIFFUSE_SUBPASS_INPUT 0 - -layout (input_attachment_index = SHADER_DIFFUSE_SUBPASS_INPUT, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform subpassInputMS u_DiffuseInput; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Get base color from the color texture - //vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); - vec4 DiffuseColor = subpassLoad( u_DiffuseInput, gl_SampleID ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // ******************************** - // Alpha Blending - // ******************************** - FragColor.rgb = DiffuseColor.rgb; - FragColor.a = 1.0; - - // Color mapping - FragColor.rgb = ACESFilmic(FragColor.rgb); - - // ******************************** - // sRGB conversion (if speed was a factor we would probably hardcode and have 2 blit shaders) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.json b/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.json deleted file mode 100644 index b894930..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassMsaa.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/TonemapSubpassMsaa.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "InputAttachment", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.frag b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.frag deleted file mode 100644 index db4c63e..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.frag +++ /dev/null @@ -1,98 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -#define SHADER_DIFFUSE_SUBPASS_INPUT 0 - -layout (input_attachment_index = SHADER_DIFFUSE_SUBPASS_INPUT, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform subpassInput u_DiffuseInput; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Load subpass color input. - vec4 DiffuseColor0 = subpassLoad( u_DiffuseInput ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // Color mapping - FragColor.rgb = ACESFilmic(DiffuseColor0.rgb); - - // ******************************** - // sRGB conversion (if speed was a factor we would have sRGB flag as a shader specialization) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.json b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.json deleted file mode 100644 index b51edb0..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve1x.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/TonemapSubpassShaderResolve1x.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "InputAttachment", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.frag b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.frag deleted file mode 100644 index 40386f4..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.frag +++ /dev/null @@ -1,103 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -#define SHADER_DIFFUSE_SUBPASS_INPUT 0 - -layout (input_attachment_index = SHADER_DIFFUSE_SUBPASS_INPUT, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform subpassInputMS u_DiffuseInput; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Load the 2 MSAA samples - vec4 DiffuseColor0 = subpassLoad( u_DiffuseInput, 0 ); - vec4 DiffuseColor1 = subpassLoad( u_DiffuseInput, 1 ); - - // Apply darkening/lightening control - //float lerp01 = min(1,FragCB.Diffuse); - //float lerp12 = max(0,FragCB.Diffuse-1); - //DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; - - // Color mapping - FragColor.rgb = ACESFilmic(DiffuseColor0.rgb); - FragColor.rgb += ACESFilmic(DiffuseColor1.rgb); - - // Average for MSAA - FragColor.rgb *= 0.5f; - - // ******************************** - // sRGB conversion (if speed was a factor we would have sRGB flag as a shader specialization) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.json b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.json deleted file mode 100644 index 94c6d3a..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve2x.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/TonemapSubpassShaderResolve2x.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "InputAttachment", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.frag b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.frag deleted file mode 100644 index 5c7b573..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.frag +++ /dev/null @@ -1,100 +0,0 @@ -//============================================================================================================ -// -// -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause -// -//============================================================================================================ - -#version 400 - -#extension GL_ARB_separate_shader_objects : enable -#extension GL_ARB_shading_language_420pack : enable - -// Buffer binding locations -#define SHADER_DIFFUSE_TEXTURE_LOC 0 - -#define SHADER_DIFFUSE_SUBPASS_INPUT 0 - -layout (input_attachment_index = SHADER_DIFFUSE_SUBPASS_INPUT, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform subpassInputMS u_DiffuseInput; - -// Varying's -layout (location = 0) in vec2 v_TexCoord; - -// Finally, the output color -layout (location = 0) out vec4 FragColor; - - -// ACES Filmic Tone-mapping -vec3 ACESFilmic(vec3 x) -{ - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; -// return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); - return (x*(a*x+b))/(x*(c*x+d)+e); -} - -// ACES Filmic Tone-mapping for Rec2020 display. Input is linear with 1.0 being 80nit. Output maps to ~12.5 at 1000nits -vec3 ACESFilmicRec2020( vec3 x ) -{ - float a = 15.8f; - float b = 2.12f; - float c = 1.2f; - float d = 5.92f; - float e = 1.9f; - return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ); -} - -// Convert to scRGB - same as sRGB conversion (ie linear near zero and then approximation to gamma 2.2 curve) except negative values are also handled. -// If we wanted a sRGB only conversion we would clamp inputs to 0-1 and not have to handle negative cases. -// Could also allow input >1 which allows for HDR but does not give the wider color space of scRGB (enabled by negative rgb components). -float ToSCRGB(float x) -{ - float n = abs(x); - n = n > 0.0031308 ? (1.055 * pow(n, 1.0/2.4) - 0.055) : (n * 12.92); - return x > 0 ? n : -n; -} -float FromSCRGB(float x) -{ - float n = abs(x); - n = n > 0.04045 ? pow((n + 0.055)/1.055, 2.4) : (n / 12.92); - return x > 0 ? n : -n; -} - - -//----------------------------------------------------------------------------- -void main() -//----------------------------------------------------------------------------- -{ - vec2 LocalTexCoord = vec2(v_TexCoord.xy); - - // ******************************** - // Texture Colors - // ******************************** - // Load all 4 MSAA samples at once - vec4 DiffuseColor0 = subpassLoad( u_DiffuseInput, 0 ); - vec4 DiffuseColor1 = subpassLoad( u_DiffuseInput, 1 ); - FragColor.rgb = ACESFilmic(DiffuseColor0.rgb); - vec4 DiffuseColor2 = subpassLoad( u_DiffuseInput, 2 ); - FragColor.rgb += ACESFilmic(DiffuseColor1.rgb); - vec4 DiffuseColor3 = subpassLoad( u_DiffuseInput, 3 ); - FragColor.rgb += ACESFilmic(DiffuseColor2.rgb); - FragColor.rgb += ACESFilmic(DiffuseColor3.rgb); - - // Average for MSAA - FragColor.rgb *= 0.25f; - - // ******************************** - // sRGB conversion (if speed was a factor we would have sRGB flag as a shader specialization) - // ******************************** -// if (FragCB.sRGB == 1) - { -// FragColor.r = ToSCRGB(FragColor.r); -// FragColor.g = ToSCRGB(FragColor.g); -// FragColor.b = ToSCRGB(FragColor.b); - } -} - diff --git a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.json b/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.json deleted file mode 100644 index 89e0bc1..0000000 --- a/samples/shaderResolveTonemap/shaders/TonemapSubpassShaderResolve4x.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "../../../framework/schema/shaderSchema.json", - "Passes": [ - { - "Name": "Fullscreen", - "Shaders": { - "Vertex": "Media/Shaders/Fullscreen.vert.spv", - "Fragment": "Media/Shaders/TonemapSubpassShaderResolve4x.frag.spv" - }, - "DescriptorSets": [ - { - "Buffers": [ - { - "Type": "InputAttachment", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - } - ] - } - ], - "VertexBindings": [ "VB0" ] - } - ], - "Vertex": [ - { - "Span": 20, - "Name": "VB0", - "Elements": [ - { - "Name": "Position", - "Offset": 0, - "Type": "Vec3" - }, - { - "Name": "UV", - "Offset": 12, - "Type": "Vec2" - } - ] - } - ] -} diff --git a/samples/sub_pass/CMakeLists.txt b/samples/sub_pass/CMakeLists.txt new file mode 100644 index 0000000..5345797 --- /dev/null +++ b/samples/sub_pass/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required (VERSION 3.21) + +project (sub_pass C CXX) +set(CMAKE_CXX_STANDARD 20) + +# +# Source files included in this application. +# + +set(CPP_SRC code/main/application.cpp + code/main/application.hpp +) + +# +# Setup the module path to include the 'project directory' (project/windows or project/android) +# +if(NOT DEFINED PROJECT_ROOT_DIR) + set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) + +# +# Do all the build steps for a Framework application. +# needs Framework_dir and project_name variables. +# +include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + +# +# Copy required models to local folders +# +include(ModelPackager) + +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) + +# +# Convert and copy textures to local folders +# +include(TexturePackager) + +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/SubPass/README.md b/samples/sub_pass/README.md similarity index 100% rename from samples/SubPass/README.md rename to samples/sub_pass/README.md diff --git a/samples/SubPass/code/main/SubPass.cpp b/samples/sub_pass/code/main/application.cpp similarity index 75% rename from samples/SubPass/code/main/SubPass.cpp rename to samples/sub_pass/code/main/application.cpp index 28d198e..67509eb 100644 --- a/samples/SubPass/code/main/SubPass.cpp +++ b/samples/sub_pass/code/main/application.cpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -10,15 +10,13 @@ /// Sample app for Tonemapping in Shader Resolve (MSAA) /// -#include "SubPass.hpp" +#include "application.hpp" #include "camera/cameraController.hpp" #include "camera/cameraControllerTouch.hpp" #include "gui/imguiVulkan.hpp" -#include "material/drawable.hpp" -#include "material/materialManager.hpp" -#include "material/shader.hpp" -#include "material/shaderDescription.hpp" -#include "material/shaderManager.hpp" +#include "material/vulkan/drawable.hpp" +#include "material/vulkan/materialManager.hpp" +#include "material/vulkan/shaderManager.hpp" #include "mesh/meshHelper.hpp" #include "system/math_common.hpp" #include "texture/vulkan/textureManager.hpp" @@ -29,23 +27,19 @@ #include // Global Variables From Config File -//VAR(glm::vec3, gCameraStartPos, glm::vec3(-33.7f, 7.6f, 34.7f), kVariableNonpersistent); -//VAR(glm::vec3, gCameraStartRot, glm::vec3(-10.0f, 200.0f, 0.0f), kVariableNonpersistent); -VAR(glm::vec3, gCameraStartPos, glm::vec3(26.48f, 20.0f, -5.21f), kVariableNonpersistent); -VAR(glm::vec3, gCameraStartRot, glm::vec3(0.0f, 110.0f, 0.0f), kVariableNonpersistent); -VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); -VAR(float, gNearPlane, 0.1f, kVariableNonpersistent); -VAR(float, gFarPlane, 80.0f, kVariableNonpersistent); +VAR(glm::vec3, gCameraStartPos, glm::vec3(0.0f, 3.5f, 0.0f), kVariableNonpersistent); +VAR(glm::vec3, gCameraStartRot, glm::vec3(0.0f, 0.0f, 0.0f), kVariableNonpersistent); +VAR(float, gFOV, PI_DIV_4, kVariableNonpersistent); +VAR(float, gNearPlane, 0.1f, kVariableNonpersistent); +VAR(float, gFarPlane, 80.0f, kVariableNonpersistent); VAR(bool, gShaderResolve, true, kVariableNonpersistent); VAR(bool, gUseSubpasses, false, kVariableNonpersistent); VAR(bool, gUseRenderPassTransform,true, kVariableNonpersistent); namespace { - VkSampleCountFlagBits gMsaa = VK_SAMPLE_COUNT_4_BIT; - - const char* gMuseumAssetsPath = "Media\\Meshes"; - const char* gTextureFolder = "Media\\Textures\\"; + Msaa gMsaa = Msaa::Samples4; + const char* gSceneAssetModel = "SteamPunkSauna.gltf"; } /// @@ -108,8 +102,6 @@ bool Application::Initialize( uintptr_t windowHandle, uintptr_t instanceHandle ) CreateUniformBuffer( pVulkan, m_ObjectVertUniform ); CreateUniformBuffer( pVulkan, m_ObjectFragUniform ); - m_TexWhite = LoadKTXTexture(pVulkan, *m_AssetManager, "./Media/Textures/white_d.ktx", SamplerAddressMode::Repeat); - InitGui(windowHandle, instanceHandle); InitCommandBuffers(); @@ -139,16 +131,16 @@ bool Application::LoadShaders() typedef std::pair tIdAndFilename; for (const tIdAndFilename& i : - { tIdAndFilename { "Object", "Media\\Shaders\\Object.json" }, - tIdAndFilename { "Tonemap", "Media\\Shaders\\Tonemap.json" }, - tIdAndFilename { "TonemapMsaa", "Media\\Shaders\\TonemapMsaa.json" }, - tIdAndFilename { "TonemapSubpassMsaa", "Media\\Shaders\\TonemapSubpassMsaa.json" }, - tIdAndFilename { "TonemapSubpassShaderResolve1x", "Media\\Shaders\\TonemapSubpassShaderResolve1x.json" }, - tIdAndFilename { "TonemapSubpassShaderResolve4x", "Media\\Shaders\\TonemapSubpassShaderResolve4x.json" }, - tIdAndFilename { "Blit", "Media\\Shaders\\Blit.json" } + { tIdAndFilename { "Object", "Object.json" }, + tIdAndFilename { "Tonemap", "Tonemap.json" }, + tIdAndFilename { "TonemapMsaa", "TonemapMsaa.json" }, + tIdAndFilename { "TonemapSubpassMsaa", "TonemapSubpassMsaa.json" }, + tIdAndFilename { "TonemapSubpassShaderResolve1x", "TonemapSubpassShaderResolve1x.json" }, + tIdAndFilename { "TonemapSubpassShaderResolve4x", "TonemapSubpassShaderResolve4x.json" }, + tIdAndFilename { "Blit", "Blit.json" } }) { - if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second)) + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) { LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); } @@ -168,24 +160,19 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() // Clean up old render targets and render passes m_LinearColorRT.Release(); m_TonemapRT.Release(); - - if (m_BlitRenderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(pVulkan->m_VulkanDevice, m_BlitRenderPass, nullptr); - m_BlitRenderPass = VK_NULL_HANDLE; - } + m_BlitRenderPass = {}; // // Setup formats for the first pass std::vector PassColorFormats = { TextureFormat::A2B10G10R10_UNORM_PACK32 };// TextureFormat::R8G8B8A8_UNORM };// { TextureFormat::R16G16B16A16_SFLOAT }; // std::vector PassColorFormats = { TextureFormat::R8G8B8A8_UNORM }; - std::vector PassColorMsaa = { gMsaa }; + std::vector PassColorMsaa = { gMsaa }; std::vector PassTextureTypes; const TextureFormat DepthFormat = pVulkan->GetBestSurfaceDepthFormat(); const bool NeedsResolve = true; - VkRenderPass ObjectRenderPass = VK_NULL_HANDLE; + RenderPass ObjectRenderPass; if( gUseSubpasses ) { // @@ -202,18 +189,16 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() } // Resolved Output PassColorFormats.push_back( TextureFormat::A2B10G10R10_UNORM_PACK32 ); - PassColorMsaa.push_back( VK_SAMPLE_COUNT_1_BIT ); + PassColorMsaa.push_back( Msaa::Samples1 ); PassTextureTypes.push_back( TT_RENDER_TARGET ); // Create a compatible RenderPass - if( !Create2SubpassRenderPass( { &PassColorFormats[0],1 }, { &PassColorFormats[1],1 }, DepthFormat, *PassColorMsaa.cbegin(), *PassColorMsaa.crbegin(), &ObjectRenderPass ) ) + if( !Create2SubpassRenderPass( { &PassColorFormats[0],1 }, { &PassColorFormats[1],1 }, DepthFormat, *PassColorMsaa.cbegin(), *PassColorMsaa.crbegin(), ObjectRenderPass ) ) { LOGE( "Unable to create render pass" ); return false; } pVulkan->SetDebugObjectName( ObjectRenderPass, "CombinedRenderPass" ); - - m_TonemapSubPassIdx = 1; } else { @@ -231,19 +216,19 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() RenderPassOutputUsage::StoreReadOnly,/*color*/ true,/*clear depth*/ RenderPassOutputUsage::Discard,/*depth*/ - &ObjectRenderPass ) ) + ObjectRenderPass ) ) { return false; } pVulkan->SetDebugObjectName( ObjectRenderPass, "ObjectRenderPass" ); // Tonemap render pass - VkRenderPass TonemapRenderPass = VK_NULL_HANDLE; + RenderPass TonemapRenderPass; std::vector TonemapColorAndResolveFormats{ TextureFormat::A2B10G10R10_UNORM_PACK32 }; // std::vector TonemapColorAndResolveFormats{ TextureFormat::R8G8B8A8_UNORM }; std::span TonemapResolveFormat{}; - std::vector TonemapColorAndResolveMsaa{ PassColorMsaa.back() }; + std::vector TonemapColorAndResolveMsaa{ PassColorMsaa.back() }; std::vector TonemapColorAndResolveTextureTypes{ TT_RENDER_TARGET }; if( NeedsResolve ) @@ -251,7 +236,7 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() // Setup to have an additional 1xMSAA buffer for the Resolve after the tonemap TonemapColorAndResolveFormats.push_back( TextureFormat::A2B10G10R10_UNORM_PACK32 ); TonemapResolveFormat = { &TonemapColorAndResolveFormats.back(), 1 }; - TonemapColorAndResolveMsaa.push_back( VK_SAMPLE_COUNT_1_BIT ); + TonemapColorAndResolveMsaa.push_back( Msaa::Samples1 ); TonemapColorAndResolveTextureTypes.push_back( TT_RENDER_TARGET ); } @@ -263,57 +248,87 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() RenderPassOutputUsage::StoreReadOnly,/*color*/ false,/*dont clear depth*/ RenderPassOutputUsage::Discard,/*depth*/ - &TonemapRenderPass, + TonemapRenderPass, TonemapResolveFormat) ) { return false; } pVulkan->SetDebugObjectName( TonemapRenderPass, "TonemapRenderPass" ); - // Create the render target for the Tonemap (only needed when passes are seperate - subpass variant has all the rendertarget buffers for both subpasses in m_LinearColorRT) - if( !m_TonemapRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, TonemapColorAndResolveFormats, TextureFormat::UNDEFINED, TonemapRenderPass/*takes ownership*/, (VkRenderPass) VK_NULL_HANDLE, TonemapColorAndResolveMsaa, "Tonemap RT", TonemapColorAndResolveTextureTypes ) ) + // Tonemap RT { - LOGE( "Error initializing LinearColorRT" ); - return false; + const RenderTargetInitializeInfo info{ + .Width = gRenderWidth, + .Height = gRenderHeight, + .LayerFormats = TonemapColorAndResolveFormats, + .DepthFormat = TextureFormat::UNDEFINED, + .TextureTypes = TonemapColorAndResolveTextureTypes, + .Msaa = TonemapColorAndResolveMsaa, + .ResolveTextureFormats = TonemapResolveFormat, + .FilterModes = {} + }; + + // Create the render target for the Tonemap (only needed when passes are seperate - subpass variant has all the rendertarget buffers for both subpasses in m_LinearColorRT) + if (!m_TonemapRT.Initialize(pVulkan, info, "Tonemap RT", &TonemapRenderPass)) + { + LOGE("Error initializing LinearColorRT"); + return false; + } } - m_TonemapSubPassIdx = 0; // not subpassed! + m_TonemapRenderContext = RenderContext(std::move(TonemapRenderPass), m_TonemapRT.GetFrameBuffer(), "TonemapRenderContext"); } - // Create render target(s) for the scene render (sub) passes. - if (!m_LinearColorRT.Initialize(pVulkan, gRenderWidth, gRenderHeight, PassColorFormats, DepthFormat, ObjectRenderPass, (VkRenderPass)VK_NULL_HANDLE, PassColorMsaa, "Main RT", PassTextureTypes)) + // Linear Color RT { - LOGE("Error initializing LinearColorRT"); - return false; + const RenderTargetInitializeInfo info{ + .Width = gRenderWidth, + .Height = gRenderHeight, + .LayerFormats = PassColorFormats, + .DepthFormat = DepthFormat, + .TextureTypes = PassTextureTypes, + .Msaa = PassColorMsaa, + .ResolveTextureFormats = {}, + .FilterModes = {} + }; + + // Create render target(s) for the scene render (sub) passes. + if (!m_LinearColorRT.Initialize(pVulkan, info, "Main RT", &ObjectRenderPass)) + { + LOGE("Error initializing LinearColorRT"); + return false; + } } + m_ObjectRenderContext = RenderContext(std::move(ObjectRenderPass), m_LinearColorRT.GetFrameBuffer(), "ObjectRenderContext"); + // Blit render pass if( !pVulkan->CreateRenderPass( { &pVulkan->m_SurfaceFormat, 1 }, pVulkan->m_SwapchainDepth.format, - VK_SAMPLE_COUNT_1_BIT, + Msaa::Samples1, RenderPassInputUsage::DontCare/*color*/, RenderPassOutputUsage::Present,/*color*/ false,/*dont clear depth*/ RenderPassOutputUsage::Discard,/*depth*/ - &m_BlitRenderPass ) ) + m_BlitRenderPass) ) { return false; } - pVulkan->SetDebugObjectName( m_BlitRenderPass, "BlitRenderPass" ); + pVulkan->SetDebugObjectName(m_BlitRenderPass, "BlitRenderPass" ); // // Setup the drawables using the correct render passes / subpasses // // Scene (object) drawable - LoadSceneDrawables( ObjectRenderPass, 0, gMsaa ); + LoadSceneDrawables(m_ObjectRenderContext); if( gUseSubpasses ) { // // Tonemap drawable // - ImageInfo tonemapDiffuseImageInfo( m_LinearColorRT[0].m_ColorAttachments.front() ); + ImageInfo tonemapDiffuseImageInfo( m_LinearColorRT.GetColorAttachments().front() ); tonemapDiffuseImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; std::map tonemapImages = { {"Diffuse", std::move(tonemapDiffuseImageInfo) } }; @@ -321,57 +336,57 @@ bool Application::InitFramebuffersRenderPassesAndDrawables() if( gShaderResolve ) { switch( PassColorMsaa.front() ) { - default: pShaderName = "TonemapSubpassShaderResolve1x"; break; - case VK_SAMPLE_COUNT_2_BIT: pShaderName = "TonemapSubpassShaderResolve2x"; break; - case VK_SAMPLE_COUNT_4_BIT: pShaderName = "TonemapSubpassShaderResolve4x"; break; + default: pShaderName = "TonemapSubpassShaderResolve1x"; break; + case Msaa::Samples2: pShaderName = "TonemapSubpassShaderResolve2x"; break; + case Msaa::Samples4: pShaderName = "TonemapSubpassShaderResolve4x"; break; } } // Tonemapping runs in second sub-pass at the MSAA samplerate of its destination buffer (either the shader resolve output buffer @ 1xMSAA or the pre hardware resolve output buffer @ 4xMSAA etc) - m_TonemapDrawable = InitFullscreenDrawable( pShaderName, {}, tonemapImages, ObjectRenderPass, m_TonemapSubPassIdx, PassColorMsaa[1] ); + m_TonemapDrawable = InitFullscreenDrawable( pShaderName, {}, tonemapImages, m_ObjectRenderContext.GetRenderPass(), 1, PassColorMsaa[1]); // // Blit drawable // - std::map blitInputs = { {"Diffuse", &m_LinearColorRT[0].m_ColorAttachments.back() }, - {"Overlay", &m_GuiRT[0].m_ColorAttachments.front() }, + std::map blitInputs = { {"Diffuse", &m_LinearColorRT.GetColorAttachments().back() }, + {"Overlay", &m_GuiRT.GetColorAttachments().front() }, }; - m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, VK_SAMPLE_COUNT_1_BIT ); + m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, Msaa::Samples1 ); } else { // // Tonemap drawable // - std::map tonemapTex = { {"Diffuse", &m_LinearColorRT[0].m_ColorAttachments.front() } }; - if( PassColorMsaa.front() == VK_SAMPLE_COUNT_1_BIT ) + std::map tonemapTex = { {"Diffuse", &m_LinearColorRT.GetColorAttachments().front() } }; + if( PassColorMsaa.front() == Msaa::Samples1 ) { - m_TonemapDrawable = InitFullscreenDrawable( "Tonemap", tonemapTex, {}, m_TonemapRT.m_RenderPass, 0, PassColorMsaa.front() ); + m_TonemapDrawable = InitFullscreenDrawable( "Tonemap", tonemapTex, {}, m_TonemapRenderContext.GetRenderPass(), 0, PassColorMsaa.front()); } else { - m_TonemapDrawable = InitFullscreenDrawable( "TonemapMsaa", tonemapTex, {}, m_TonemapRT.m_RenderPass, 0, PassColorMsaa.front() ); + m_TonemapDrawable = InitFullscreenDrawable( "TonemapMsaa", tonemapTex, {}, m_TonemapRenderContext.GetRenderPass(), 0, PassColorMsaa.front()); } // // Blit drawable // - std::map blitInputs = { {"Diffuse", &m_TonemapRT[0].m_ColorAttachments.back() }, - {"Overlay", &m_GuiRT[0].m_ColorAttachments.front() }, + std::map blitInputs = { {"Diffuse", &m_TonemapRT.GetColorAttachments().back() }, + {"Overlay", &m_GuiRT.GetColorAttachments().front() }, }; - m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, VK_SAMPLE_COUNT_1_BIT ); + m_BlitDrawable = InitFullscreenDrawable( "Blit", blitInputs, {}, m_BlitRenderPass, 0, Msaa::Samples1 ); } return true; } //----------------------------------------------------------------------------- -bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ) +bool Application::LoadSceneDrawables( const RenderContext& renderContext) //----------------------------------------------------------------------------- { - const char* pGLTFMeshFile = "./Media/Meshes/Museum.gltf"; + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); - LOGI( "Loading Scene Drawable %s", pGLTFMeshFile ); + LOGI( "Loading Scene Drawable %s", sceneAssetPath.c_str()); Vulkan* pVulkan = GetVulkan(); @@ -382,15 +397,18 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassI return false; } - m_TextureManager->SetDefaultFilenameManipulators(PathManipulator_PrefixDirectory{ "Media\\" }, PathManipulator_ChangeExtension{ ".ktx" }); + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const auto whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); // Lambda to load a texture for the given material (slot) - const auto& textureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const MaterialPass::tPerFrameTexInfo + const auto& textureLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef, const std::string& textureSlotName) -> const PerFrameTexInfo { - auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(*m_AssetManager, materialDef.diffuseFilename, m_SamplerEdgeClamp); + const PathManipulator_ChangeExtension changeTextureExt{ ".ktx" }; + + auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerEdgeClamp, prefixTextureDir, changeTextureExt); if (!diffuseTexture) { - return { &m_TexWhite }; + return { whiteTexture }; } else { @@ -399,7 +417,7 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassI }; // Lambda to associate (uniform) buffers with their shader binding/slot names. - const auto& bufferLoader = [&]( const std::string& bufferSlotName ) -> tPerFrameVkBuffer { + const auto& bufferLoader = [&]( const std::string& bufferSlotName ) -> PerFrameBufferVulkan { if( bufferSlotName == "Vert" ) { return { m_ObjectVertUniform.buf.GetVkBuffer() }; @@ -419,19 +437,15 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassI { using namespace std::placeholders; - return std::move( m_MaterialManager->CreateMaterial( *pVulkan, *pObjectShader, NUM_VULKAN_BUFFERS, std::bind( textureLoader, std::cref( materialDef ), _1 ), bufferLoader ) ); + return std::move( m_MaterialManager->CreateMaterial(*pObjectShader, NUM_VULKAN_BUFFERS, std::bind( textureLoader, std::cref( materialDef ), _1 ), bufferLoader ) ); }; - static const char* opaquePassName = "RP_OPAQUE"; - const VkSampleCountFlagBits renderPassMultisamples[1] = { passMsaa }; - const uint32_t renderPassSubpasses[1] = { subpassIdx }; - m_SceneObject.clear(); - if( !DrawableLoader::LoadDrawables( *pVulkan, *m_AssetManager, { &renderPass,1 }, &opaquePassName, pGLTFMeshFile, materialLoader, m_SceneObject, renderPassMultisamples, DrawableLoader::LoaderFlags::None, renderPassSubpasses ) ) + if( !DrawableLoader::LoadDrawables( *pVulkan, *m_AssetManager, renderContext, sceneAssetPath, materialLoader, m_SceneObject, DrawableLoader::LoaderFlags::None)) { - LOGE( "Error loading Object mesh: %s", pGLTFMeshFile ); + LOGE( "Error loading Object mesh: %s", sceneAssetPath.c_str()); return false; } @@ -440,7 +454,7 @@ bool Application::LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassI //----------------------------------------------------------------------------- -std::unique_ptr Application::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ) +std::unique_ptr Application::InitFullscreenDrawable( const char* pShaderName, const std::map& ColorAttachmentsLookup, const std::map& ImageAttachmentsLookup, const RenderPass& renderPass, uint32_t subpassIdx, Msaa passMsaa ) //----------------------------------------------------------------------------- { // @@ -459,34 +473,37 @@ std::unique_ptr Application::InitFullscreenDrawable( const char* pShad return nullptr; } - MeshObject mesh; + Mesh mesh; if (!MeshHelper::CreateMesh(pVulkan->GetMemoryManager(), MeshObjectIntermediate::CreateScreenSpaceMesh(), 0, pShader->m_shaderDescription->m_vertexFormats, &mesh)) { LOGE("Error creating Fullscreen Mesh (for %s)", pShaderName); return nullptr; } - auto shaderMaterial = m_MaterialManager->CreateMaterial(*pVulkan, *pShader, NUM_VULKAN_BUFFERS, - [this, &ColorAttachmentsLookup](const std::string& texName) -> const MaterialPass::tPerFrameTexInfo { + auto shaderMaterial = m_MaterialManager->CreateMaterial(*pShader, NUM_VULKAN_BUFFERS, + [this, &ColorAttachmentsLookup](const std::string& texName) -> const PerFrameTexInfo { return { ColorAttachmentsLookup.find(texName)->second }; assert(0); return {}; }, - [](const std::string& bufferName) -> const tPerFrameVkBuffer { + [](const std::string& bufferName) -> const PerFrameBuffer { assert(0); return {}; }, - [this, &ImageAttachmentsLookup](const std::string& imageName) -> const ImageInfo { + [this, &ImageAttachmentsLookup](const std::string& imageName) -> const ImageInfo { return { ImageAttachmentsLookup.find(imageName)->second }; } ); static const char* passName = "Fullscreen"; - const VkSampleCountFlagBits renderPassMultisamples[1] = { passMsaa }; + const Msaa renderPassMultisamples[1] = { passMsaa }; const uint32_t renderPassSubpasses[1] = { subpassIdx }; + RenderContext renderContext = RenderContext(renderPass.Copy(), {}, passName); + renderContext.subPass = subpassIdx; + auto drawable = std::make_unique( *pVulkan, std::move( shaderMaterial ) ); - if( !drawable->Init( renderPass, passName, std::move(mesh), std::nullopt, std::nullopt, renderPassMultisamples, renderPassSubpasses ) ) + if (!drawable->Init(renderContext, std::move(mesh), std::nullopt, std::nullopt)) { LOGE("Error creating Blit Drawable"); return nullptr; @@ -506,7 +523,7 @@ bool Application::InitCommandBuffers() for( uint32_t WhichBuffer = 0; WhichBuffer < NUM_VULKAN_BUFFERS; WhichBuffer++ ) { sprintf( szName, "CommandBuffer (%d of %d)", WhichBuffer + 1, NUM_VULKAN_BUFFERS ); - if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, VK_COMMAND_BUFFER_LEVEL_PRIMARY, false ) ) + if( !m_CommandBuffer[WhichBuffer].Initialize( pVulkan, szName, CommandBuffer::Type::Primary ) ) { return false; } @@ -578,7 +595,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) auto& commandBuffer = m_CommandBuffer[bufferIdx]; - if( !commandBuffer.Begin( m_LinearColorRT[0].m_FrameBuffer, m_LinearColorRT.m_RenderPass ) ) + if( !commandBuffer.Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) { return false; } @@ -586,14 +603,14 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) VkRect2D Scissor = {}; Scissor.offset.x = 0; Scissor.offset.y = 0; - Scissor.extent.width = m_LinearColorRT[0].m_Width; - Scissor.extent.height = m_LinearColorRT[0].m_Height; + Scissor.extent.width = m_LinearColorRT.GetWidth(); + Scissor.extent.height = m_LinearColorRT.GetHeight(); VkClearColorValue ClearColor{}; std::fill( std::begin(ClearColor.float32), std::end(ClearColor.float32), 0.0f); ClearColor.float32[0] = 0.1f; - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, m_LinearColorRT[0].GetNumColorLayers(), true, m_LinearColorRT.m_RenderPass, false, m_LinearColorRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, m_LinearColorRT.GetNumColorLayers(), true, m_ObjectRenderContext.GetRenderPass(), false, m_LinearColorRT.GetFrameBuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -614,7 +631,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) // Tonemap has its own render pass. commandBuffer.EndRenderPass(); - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, true, m_TonemapRT.m_RenderPass, false, m_TonemapRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, true, m_TonemapRenderContext.GetRenderPass(), false, m_TonemapRT.GetFrameBuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -630,7 +647,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) if (m_Gui) { // Render gui (has its own command buffer, optionally returns vk_null_handle if not rendering anything) - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, true, m_GuiRT.m_RenderPass, false, m_GuiRT[0].m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, true, m_GuiRenderContext.GetRenderPass(), false, m_GuiRT.GetFrameBuffer(), VK_SUBPASS_CONTENTS_INLINE)) { return false; } @@ -646,7 +663,7 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) Scissor.extent.width = pVulkan->m_SurfaceWidth; Scissor.extent.height = pVulkan->m_SurfaceHeight; - if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, false, m_BlitRenderPass, true/*swapchain*/, pVulkan->m_SwapchainBuffers[bufferIdx].framebuffer, VK_SUBPASS_CONTENTS_INLINE ) ) + if( !commandBuffer.BeginRenderPass( Scissor, 0.0f, 1.0f, { &ClearColor , 1 }, 1, false, m_BlitRenderPass, true/*swapchain*/, pVulkan->m_SwapchainBuffers[bufferIdx].framebuffer.m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE ) ) { return false; } @@ -662,10 +679,9 @@ bool Application::UpdateCommandBuffer(uint32_t bufferIdx) //----------------------------------------------------------------------------- -bool Application::Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, VkSampleCountFlagBits InternalMsaa, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/ ) +bool Application::Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, Msaa InternalMsaa, Msaa OutputMsaa, RenderPass& rRenderPass/*out*/ ) //----------------------------------------------------------------------------- { - assert(pRenderPass && *pRenderPass == VK_NULL_HANDLE); // check not already allocated and that we have a location to place the renderpass handle assert( !InternalColorFormats.empty() ); // not supporting a depth only pass assert( !OutputColorFormats.empty() ); @@ -702,7 +718,7 @@ bool Application::Create2SubpassRenderPass( const std::span // Pass0 color and depth buffers setup to clear on load, discard on end (of entire pass). VkAttachmentDescription AttachmentDescPass0 = { 0/*flags*/, TextureFormatToVk(ColorFormat)/*format*/, - InternalMsaa/*samples*/, + EnumToVk(InternalMsaa)/*samples*/, VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -721,7 +737,7 @@ bool Application::Create2SubpassRenderPass( const std::span { // Pass1 color buffers setup to, store on end (of entire pass). VkAttachmentDescription AttachmentDescPass1 = { 0, TextureFormatToVk(ColorFormat)/*format*/, - (gShaderResolve||!NeedsResolve) ? OutputMsaa : InternalMsaa/*samples*/, + (gShaderResolve||!NeedsResolve) ? EnumToVk(OutputMsaa) : EnumToVk(InternalMsaa)/*samples*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, (gShaderResolve||!NeedsResolve) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -743,7 +759,7 @@ bool Application::Create2SubpassRenderPass( const std::span { // Pass1 color buffers to resolve to at end of pass. VkAttachmentDescription AttachmentDescResolvePass1 = { 0, TextureFormatToVk(ColorFormat)/*format*/, - OutputMsaa/*samples*/, + EnumToVk(OutputMsaa)/*samples*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*loadOp*/, VK_ATTACHMENT_STORE_OP_STORE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -768,7 +784,7 @@ bool Application::Create2SubpassRenderPass( const std::span { VkAttachmentDescription AttachmentDescDepthPass0 = { 0/*flags*/, TextureFormatToVk(InternalDepthFormat)/*format*/, - InternalMsaa/*samples*/, + EnumToVk(InternalMsaa)/*samples*/, VK_ATTACHMENT_LOAD_OP_CLEAR/*loadOp*/, VK_ATTACHMENT_STORE_OP_DONT_CARE/*storeOp*/, VK_ATTACHMENT_LOAD_OP_DONT_CARE/*stencilLoadOp*/, @@ -789,7 +805,7 @@ bool Application::Create2SubpassRenderPass( const std::span else if( gShaderResolve ) { // Shader Resolve extension (VK_QCOM_render_pass_shader_resolve) is available, use it. - assert( OutputMsaa == VK_SAMPLE_COUNT_1_BIT ); + assert( OutputMsaa == Msaa::Samples1 ); SubpassDesc[1].flags = 0x00000008/*VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM*/ | 0x00000004/*VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM*/; } @@ -825,20 +841,24 @@ bool Application::Create2SubpassRenderPass( const std::span PassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Now ready to actually create the render pass - VkRenderPassCreateInfo RenderPassInfo {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; - RenderPassInfo.flags = 0; - RenderPassInfo.attachmentCount = (uint32_t) PassAttachDescs.size(); - RenderPassInfo.pAttachments = PassAttachDescs.data(); - RenderPassInfo.subpassCount = (uint32_t) SubpassDesc.size(); - RenderPassInfo.pSubpasses = SubpassDesc.data(); - RenderPassInfo.dependencyCount = (uint32_t) PassDependencies.size(); - RenderPassInfo.pDependencies = PassDependencies.data(); - - VkResult RetVal = vkCreateRenderPass(pVulkan->m_VulkanDevice, &RenderPassInfo, NULL, pRenderPass); + const VkRenderPassCreateInfo RenderPassInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .flags = 0, + .attachmentCount = (uint32_t)PassAttachDescs.size(), + .pAttachments = PassAttachDescs.data(), + .subpassCount = (uint32_t)SubpassDesc.size(), + .pSubpasses = SubpassDesc.data(), + .dependencyCount = (uint32_t)PassDependencies.size(), + .pDependencies = PassDependencies.data() + }; + + VkRenderPass vkRenderPass = VK_NULL_HANDLE; + VkResult RetVal = vkCreateRenderPass(pVulkan->m_VulkanDevice, &RenderPassInfo, NULL, &vkRenderPass); if (!CheckVkError("vkCreateRenderPass()", RetVal)) { return false; } + rRenderPass.mRenderPass = {pVulkan->m_VulkanDevice, vkRenderPass}; return true; } @@ -891,13 +911,43 @@ bool Application::InitGui(uintptr_t windowHandle, uintptr_t instanceHandle) const TextureFormat GuiColorFormat[]{ TextureFormat::R8G8B8A8_UNORM }; const TEXTURE_TYPE GuiTextureTypes[]{ TT_RENDER_TARGET }; - if( !m_GuiRT.Initialize( pVulkan, gRenderWidth, gRenderHeight, GuiColorFormat, TextureFormat::UNDEFINED, VK_SAMPLE_COUNT_1_BIT, "Gui RT", GuiTextureTypes ) ) + RenderPass guiRenderPass; + if (!pVulkan->CreateRenderPass( + GuiColorFormat, + TextureFormat::UNDEFINED, + Msaa::Samples1, + RenderPassInputUsage::Clear/*color*/, + RenderPassOutputUsage::Store,/*color*/ + false,/*dont clear depth*/ + RenderPassOutputUsage::Discard,/*depth*/ + guiRenderPass)) { return false; } - m_Gui = std::make_unique>(*pVulkan, m_GuiRT.m_RenderPass); - if (!m_Gui->Initialize(windowHandle, m_GuiRT[0].m_Width, m_GuiRT[0].m_Height)) + // GUI RT + { + const RenderTargetInitializeInfo info{ + .Width = gRenderWidth, + .Height = gRenderHeight, + .LayerFormats = GuiColorFormat, + .DepthFormat = TextureFormat::UNDEFINED, + .TextureTypes = GuiTextureTypes, + .Msaa = {}, + .ResolveTextureFormats = {}, + .FilterModes = {} + }; + + if (!m_GuiRT.Initialize(pVulkan, info, "Gui RT", &guiRenderPass)) + { + return false; + } + } + + m_GuiRenderContext = {std::move(guiRenderPass), m_GuiRT .GetFrameBuffer(), "Gui Pass"}; + + m_Gui = std::make_unique(*pVulkan, m_GuiRenderContext.GetRenderPass()); + if (!m_Gui->Initialize(windowHandle, TextureFormat::R8G8B8A8_SRGB, m_GuiRT.GetWidth(), m_GuiRT.GetHeight())) { return false; } @@ -956,8 +1006,6 @@ void Application::Destroy() m_TonemapDrawable.reset(); m_BlitDrawable.reset(); - m_TexWhite.Release(pVulkan); - ReleaseUniformBuffer( pVulkan, &m_ObjectVertUniform ); ReleaseUniformBuffer( pVulkan, &m_ObjectFragUniform ); @@ -968,11 +1016,7 @@ void Application::Destroy() m_MaterialManager.reset(); m_ShaderManager.reset(); - if( m_BlitRenderPass != VK_NULL_HANDLE ) - { - vkDestroyRenderPass( pVulkan->m_VulkanDevice, m_BlitRenderPass, nullptr ); - m_BlitRenderPass = VK_NULL_HANDLE; - } + m_BlitRenderPass = {}; // Finally call into base class destroy ApplicationHelperBase::Destroy(); diff --git a/samples/SubPass/code/main/SubPass.hpp b/samples/sub_pass/code/main/application.hpp similarity index 70% rename from samples/SubPass/code/main/SubPass.hpp rename to samples/sub_pass/code/main/application.hpp index 9fae42b..f4c7a5f 100644 --- a/samples/SubPass/code/main/SubPass.hpp +++ b/samples/sub_pass/code/main/application.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ @@ -19,10 +19,6 @@ #include "vulkan/commandBuffer.hpp" // Forward declarations -class Drawable; -class ShaderManager; -class MaterialManager; -struct ImageInfo; class Application : public ApplicationHelperBase @@ -44,10 +40,10 @@ class Application : public ApplicationHelperBase bool LoadShaders(); bool InitFramebuffersRenderPassesAndDrawables(); - bool LoadSceneDrawables( VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ); - std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& inputLookup, const std::map& ImageAttachmentsLookup, VkRenderPass renderPass, uint32_t subpassIdx, VkSampleCountFlagBits passMsaa ); + bool LoadSceneDrawables( const RenderContext& renderContext); + std::unique_ptr InitFullscreenDrawable( const char* pShaderName, const std::map& inputLookup, const std::map& ImageAttachmentsLookup, const RenderPass& renderPass, uint32_t subpassIdx, Msaa passMsaa ); bool InitCommandBuffers(); - bool Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, VkSampleCountFlagBits InternalMsaa, VkSampleCountFlagBits OutputMsaa, VkRenderPass* pRenderPass/*out*/ ); + bool Create2SubpassRenderPass( const std::span InternalColorFormats, const std::span OutputColorFormats, TextureFormat InternalDepthFormat, Msaa InternalMsaa, Msaa OutputMsaa, RenderPass& rRenderPass ); bool InitHdr(); /// @brief Ticked every frame (by the Framework) @@ -68,8 +64,6 @@ class Application : public ApplicationHelperBase private: - TextureVulkan m_TexWhite; - // Drawable(s) for rendering the main scene object std::vector m_SceneObject; @@ -92,23 +86,21 @@ class Application : public ApplicationHelperBase }; UniformT m_ObjectFragUniform; - // Render target for Objects. - CRenderTargetArray<1> m_LinearColorRT; - - // Render target for tonemap (when not running as part of a subpass chain) - CRenderTargetArray<1> m_TonemapRT; + // Render targets + RenderTarget m_LinearColorRT; + RenderTarget m_TonemapRT; + RenderTarget m_GuiRT; - // Render target for GUI. - CRenderTargetArray<1> m_GuiRT; + RenderContext m_ObjectRenderContext; + RenderContext m_TonemapRenderContext; + RenderContext m_GuiRenderContext; - // Single command buffer - Wrap_VkCommandBuffer m_CommandBuffer[NUM_VULKAN_BUFFERS]; + RenderPass m_BlitRenderPass; - uint32_t m_TonemapSubPassIdx = 0; - VkRenderPass m_BlitRenderPass = VK_NULL_HANDLE; + CommandBuffer m_CommandBuffer[NUM_VULKAN_BUFFERS]; bool m_RequestedShaderResolve = false; bool m_RequestedUseSubpasses = false; bool m_RequestedUseRenderPassTransform = false; - VkSampleCountFlagBits m_RequestedMsaa = VK_SAMPLE_COUNT_1_BIT; + Msaa m_RequestedMsaa = Msaa::Samples1; }; diff --git a/samples/sub_pass/install_apk.bat b/samples/sub_pass/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/sub_pass/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sub_pass/install_config.bat b/samples/sub_pass/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/sub_pass/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/hello-gltf/project/android/AndroidManifest.xml b/samples/sub_pass/project/android/AndroidManifest.xml similarity index 98% rename from samples/hello-gltf/project/android/AndroidManifest.xml rename to samples/sub_pass/project/android/AndroidManifest.xml index 3f5db0e..b2f75f5 100644 --- a/samples/hello-gltf/project/android/AndroidManifest.xml +++ b/samples/sub_pass/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -23,7 +22,8 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> - Hello GLTF + SGS SubPass diff --git a/samples/SubPass/img/nosubpass.PNG b/samples/sub_pass/project/img/nosubpass.PNG similarity index 100% rename from samples/SubPass/img/nosubpass.PNG rename to samples/sub_pass/project/img/nosubpass.PNG diff --git a/samples/SubPass/img/nosubpassstage.PNG b/samples/sub_pass/project/img/nosubpassstage.PNG similarity index 100% rename from samples/SubPass/img/nosubpassstage.PNG rename to samples/sub_pass/project/img/nosubpassstage.PNG diff --git a/samples/SubPass/img/screenshot.PNG b/samples/sub_pass/project/img/screenshot.PNG similarity index 100% rename from samples/SubPass/img/screenshot.PNG rename to samples/sub_pass/project/img/screenshot.PNG diff --git a/samples/SubPass/img/subpass.PNG b/samples/sub_pass/project/img/subpass.PNG similarity index 100% rename from samples/SubPass/img/subpass.PNG rename to samples/sub_pass/project/img/subpass.PNG diff --git a/samples/SubPass/img/subpassstage.PNG b/samples/sub_pass/project/img/subpassstage.PNG similarity index 100% rename from samples/SubPass/img/subpassstage.PNG rename to samples/sub_pass/project/img/subpassstage.PNG diff --git a/samples/SubPass/shaders/Blit.frag b/samples/sub_pass/shaders/Blit.frag similarity index 100% rename from samples/SubPass/shaders/Blit.frag rename to samples/sub_pass/shaders/Blit.frag diff --git a/samples/SubPass/shaders/Blit.json b/samples/sub_pass/shaders/Blit.json similarity index 100% rename from samples/SubPass/shaders/Blit.json rename to samples/sub_pass/shaders/Blit.json diff --git a/samples/SubPass/shaders/Fullscreen.vert b/samples/sub_pass/shaders/Fullscreen.vert similarity index 100% rename from samples/SubPass/shaders/Fullscreen.vert rename to samples/sub_pass/shaders/Fullscreen.vert diff --git a/samples/SubPass/shaders/Object.frag b/samples/sub_pass/shaders/Object.frag similarity index 100% rename from samples/SubPass/shaders/Object.frag rename to samples/sub_pass/shaders/Object.frag diff --git a/samples/SubPass/shaders/Object.json b/samples/sub_pass/shaders/Object.json similarity index 100% rename from samples/SubPass/shaders/Object.json rename to samples/sub_pass/shaders/Object.json diff --git a/samples/SubPass/shaders/Object.vert b/samples/sub_pass/shaders/Object.vert similarity index 100% rename from samples/SubPass/shaders/Object.vert rename to samples/sub_pass/shaders/Object.vert diff --git a/samples/SubPass/shaders/Tonemap.frag b/samples/sub_pass/shaders/Tonemap.frag similarity index 100% rename from samples/SubPass/shaders/Tonemap.frag rename to samples/sub_pass/shaders/Tonemap.frag diff --git a/samples/SubPass/shaders/Tonemap.json b/samples/sub_pass/shaders/Tonemap.json similarity index 100% rename from samples/SubPass/shaders/Tonemap.json rename to samples/sub_pass/shaders/Tonemap.json diff --git a/samples/SubPass/shaders/TonemapMsaa.frag b/samples/sub_pass/shaders/TonemapMsaa.frag similarity index 100% rename from samples/SubPass/shaders/TonemapMsaa.frag rename to samples/sub_pass/shaders/TonemapMsaa.frag diff --git a/samples/SubPass/shaders/TonemapMsaa.json b/samples/sub_pass/shaders/TonemapMsaa.json similarity index 100% rename from samples/SubPass/shaders/TonemapMsaa.json rename to samples/sub_pass/shaders/TonemapMsaa.json diff --git a/samples/SubPass/shaders/TonemapSubpassMsaa.frag b/samples/sub_pass/shaders/TonemapSubpassMsaa.frag similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassMsaa.frag rename to samples/sub_pass/shaders/TonemapSubpassMsaa.frag diff --git a/samples/SubPass/shaders/TonemapSubpassMsaa.json b/samples/sub_pass/shaders/TonemapSubpassMsaa.json similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassMsaa.json rename to samples/sub_pass/shaders/TonemapSubpassMsaa.json diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve1x.frag b/samples/sub_pass/shaders/TonemapSubpassShaderResolve1x.frag similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve1x.frag rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve1x.frag diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve1x.json b/samples/sub_pass/shaders/TonemapSubpassShaderResolve1x.json similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve1x.json rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve1x.json diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve2x.frag b/samples/sub_pass/shaders/TonemapSubpassShaderResolve2x.frag similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve2x.frag rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve2x.frag diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve2x.json b/samples/sub_pass/shaders/TonemapSubpassShaderResolve2x.json similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve2x.json rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve2x.json diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve4x.frag b/samples/sub_pass/shaders/TonemapSubpassShaderResolve4x.frag similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve4x.frag rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve4x.frag diff --git a/samples/SubPass/shaders/TonemapSubpassShaderResolve4x.json b/samples/sub_pass/shaders/TonemapSubpassShaderResolve4x.json similarity index 100% rename from samples/SubPass/shaders/TonemapSubpassShaderResolve4x.json rename to samples/sub_pass/shaders/TonemapSubpassShaderResolve4x.json diff --git a/samples/SubPass/shaders/shaderSchema.json b/samples/sub_pass/shaders/shaderSchema.json similarity index 100% rename from samples/SubPass/shaders/shaderSchema.json rename to samples/sub_pass/shaders/shaderSchema.json diff --git a/samples/hello-gltf/CMakeLists.txt b/samples/tile_memory/CMakeLists.txt similarity index 51% rename from samples/hello-gltf/CMakeLists.txt rename to samples/tile_memory/CMakeLists.txt index 2704575..0bac8a0 100644 --- a/samples/hello-gltf/CMakeLists.txt +++ b/samples/tile_memory/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.21) -project (helloGltf C CXX) +project (tile_memory C CXX) set(CMAKE_CXX_STANDARD 20) # @@ -19,30 +19,48 @@ if(NOT DEFINED PROJECT_ROOT_DIR) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) - # # Do all the build steps for a Framework application. # needs Framework_dir and project_name variables. # include(FrameworkApplicationHelper) +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + # # Copy required models to local folders # include(ModelPackager) -# Museum GLTF -add_gltf(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Museum) +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) # # Convert and copy textures to local folders # include(TexturePackager) -# Textures -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/scenes/Museum/Textures) -add_textures_from_path(${CMAKE_CURRENT_SOURCE_DIR}/../../assets/textures) \ No newline at end of file +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/tile_memory/README.md b/samples/tile_memory/README.md new file mode 100644 index 0000000..a9b5069 --- /dev/null +++ b/samples/tile_memory/README.md @@ -0,0 +1,41 @@ +# Tile Memory Heap Sample + +![Screenshot](img/screenshot.png) + +## Overview + +TODO: pending overview + +## Building + +### Dependencies + +The following dependencies must be installed and the appropriate locations should be referenced in the `PATH` environment variable. + +* Android SDK +* Andorid NDK +* Gradle +* CMake +* Android Studio + +### Build + +First: +``` +03_BuildTools.bat +``` + +Then: +``` +01_BuildAndroid.bat +``` +Or: +``` +02_BuildWindows.bat +``` + +## Android Studio + +This sample can also be easily imported to Android Studio and be used within the Android Studio ecosystem including building, deploying, and native code debugging. + +To do this, open Android Studio and go to `File->New->Import Project...` and select the `project\android` folder as the source for the import. This will load up the gradle configuration and once finalized, the sample can be used within Android Studio. diff --git a/samples/tile_memory/code/main/application.cpp b/samples/tile_memory/code/main/application.cpp new file mode 100644 index 0000000..ce8db85 --- /dev/null +++ b/samples/tile_memory/code/main/application.cpp @@ -0,0 +1,1686 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "application.hpp" +#include "main/applicationEntrypoint.hpp" +#include "camera/cameraController.hpp" +#include "camera/cameraControllerTouch.hpp" +#include "camera/cameraData.hpp" +#include "camera/cameraGltfLoader.hpp" +#include "gui/imguiVulkan.hpp" +#include "material/drawable.hpp" +#include "material/vulkan/shaderModule.hpp" +#include "material/computable.hpp" +#include "material/materialManager.hpp" +#include "material/shaderManagerT.hpp" +#include "material/vulkan/specializationConstantsLayout.hpp" +#include "mesh/meshHelper.hpp" +#include "mesh/meshLoader.hpp" +#include "system/math_common.hpp" +#include "texture/textureManager.hpp" +#include "imgui.h" +#include "vulkan/extensionHelpers.hpp" +#include "material/drawableLoader.hpp" +#include "mesh/mesh.hpp" +#include "vulkan/extensionLib.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" + +#include +#include +#include + +/* +============================================================================================================ +* TILE MEMORY HEAP SAMPLE +============================================================================================================ +* +* This sample demonstrates a light clustering algorithm using Vulkan, with specific support for the +* VK_QCOM_tile_memory_heap extension. This extension allows the application to allocate and manage tile memory, +* which is used for efficient memory management within a command buffer submission batch. +* +* Reference points: +* +* 1. PreInitializeSetVulkanConfiguration +* - Sets up the Vulkan configuration, including required and optional extensions. +* - config.OptionalExtension(); +* - The application checks for the VK_QCOM_tile_memory_heap extension and sets up the Vulkan configuration accordingly. +* - Memory objects are created, including setting up the tile memory heap if supported. +* +* 2. CreateMemoryObjects +* - Creates memory objects, including tile memory if supported. +* +* 3. CreateRenderTargets +* - Render targets are created with the option to use tile memory. This involves setting the appropriate usage flags and binding the memory. +* +* 4. Render +* - The main rendering function binds the tile memory heap to the command buffer if tile memory is enabled. +* - The rendering process includes multiple passes: +* - GBuffer Pass: Outputs albedo, normal, and depth. +* - Clustering Pass: Divides point lights into clusters and performs light culling. +* - Lighting Pass: Applies the corresponding pixel light cluster to a screen quad. +* +* SPEC: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_memory_heap.html +============================================================================================================ +*/ + +namespace +{ + static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_DEFERRED_LIGHT", "RP_HUD", "RP_BLIT" }; + + glm::vec3 gCameraStartPos = glm::vec3(0.0f, 3.5f, 0.0f); + glm::vec3 gCameraStartRot = glm::vec3(0.0f, 0.0f, 0.0f); + + float gFOV = PI_DIV_4; + float gNearPlane = 1.0f; + float gFarPlane = 1800.0f; + float gNormalAmount = 0.3f; + float gNormalMirrorReflectAmount = 0.05f; + + const char* gSceneAssetModel = "SteamPunkSauna.gltf"; + + const TextureFormat SceneColorType[] = { TextureFormat::R8G8B8A8_SRGB, TextureFormat::R8G8B8A8_SRGB }; + const TextureFormat DeferredLightColorType[] = { TextureFormat::R8G8B8A8_SRGB}; + const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_SRGB }; + const TextureFormat FinalColorType[] = { TextureFormat::R8G8B8A8_SRGB }; +} + +//----------------------------------------------------------------------------- +// Patch the ExtensionLib::Ext_VK_QCOM_tile_memory_heap extension so we can intercept the extension +// being registered with VkDeviceCreate and (potential) request tile memory. +struct TileMemoryHeapExtension : public ExtensionLib::Ext_VK_QCOM_tile_memory_heap +{ + TileMemoryHeapExtension(VulkanExtensionStatus status, Vulkan& vulkan) + : ExtensionLib::Ext_VK_QCOM_tile_memory_heap(status) + , m_Vulkan( vulkan ) + {} + void PostLoad() override + { + assert( m_QcomTileMemoryTypeIndex == -1 ); + + // Memory manager not available yet so can't use MemoryManager::GetHeapWithHeapFlags + // Go directly to Vulkan api for memory heap data + VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties{}; + vkGetPhysicalDeviceMemoryProperties(m_Vulkan.m_VulkanGpu, &physicalDeviceMemoryProperties ); + for (uint32_t typeIndex = 0; typeIndex < physicalDeviceMemoryProperties.memoryTypeCount; ++typeIndex) + { + auto heapIndex = physicalDeviceMemoryProperties.memoryTypes[typeIndex].heapIndex; + if ((physicalDeviceMemoryProperties.memoryHeaps[heapIndex].flags & VkMemoryHeapFlagBits( VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM )) != 0) + { + m_tileMemoryHeapSize = physicalDeviceMemoryProperties.memoryHeaps[heapIndex].size; + m_QcomTileMemoryTypeIndex = typeIndex; + LOGI( "Qcom tile memory typeIndex: %d heap index: %d Heap size: %zu", m_QcomTileMemoryTypeIndex, heapIndex, m_tileMemoryHeapSize ); + break; + } + } + if (m_QcomTileMemoryTypeIndex == -1) + { + LOGI( "Qcom tile memory heap NOT FOUND (despite VK_QCOM_tile_memory_heap extension being loaded)" ); + } + } + + MemoryPool CreateTileMemoryPool() const + { + if (m_QcomTileMemoryTypeIndex < 0) + return {}; + return m_Vulkan.GetMemoryManager().CreateCustomPool( m_QcomTileMemoryTypeIndex, m_tileMemoryHeapSize, 1, VK_BUFFER_USAGE_TILE_MEMORY_BIT_QCOM, VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM ); + } + + Vulkan& m_Vulkan; + int m_QcomTileMemoryTypeIndex = -1; + size_t m_tileMemoryHeapSize = 0; +}; + + +/// +/// @brief Implementation of the Application entrypoint (called by the framework) +/// @return Pointer to Application (derived from @FrameworkApplicationBase). +/// Creates the Application class. Ownership is passed to the calling (framework) function. +/// +FrameworkApplicationBase* Application_ConstructApplication() +{ + return new Application(); +} + +Application::Application() : ApplicationHelperBase() +{ +} + +Application::~Application() +{ +} + +//----------------------------------------------------------------------------- +void Application::PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) +//----------------------------------------------------------------------------- +{ + ApplicationHelperBase::PreInitializeSetVulkanConfiguration(config); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.OptionalExtension(); + + m_TileMemoryHeapExtension = config.OptionalExtension(*GetVulkan()); +} + +//----------------------------------------------------------------------------- +bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) +//----------------------------------------------------------------------------- +{ + if (!ApplicationHelperBase::Initialize( windowHandle, hInstance )) + { + return false; + } + + m_IsTileMemoryHeapSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME); + + if (m_IsTileMemoryHeapSupported && m_TileMemoryHeapExtension) + { + m_TileMemoryPool = m_TileMemoryHeapExtension->CreateTileMemoryPool(); + } + + if (!InitializeCamera()) + { + return false; + } + + if (!InitializeLights()) + { + return false; + } + + if (!LoadShaders()) + { + return false; + } + + if (!InitBuffers()) + { + return false; + } + + if (!CreateRenderTargets()) + { + return false; + } + + if (!SetupRenderContext()) + { + return false; + } + + if (!InitGui(windowHandle)) + { + return false; + } + + if (!LoadMeshObjects()) + { + return false; + } + + if (!CreateComputables()) + { + return false; + } + + if (!InitCommandBuffers()) + { + return false; + } + + if (!InitLocalSemaphores()) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void Application::Destroy() +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Uniform Buffers + ReleaseUniformBuffer(pVulkan, &m_ObjectVertUniform); + ReleaseUniformBuffer(pVulkan, &m_LightUniform); + + for (auto& [hash, objectUniform] : m_ObjectFragUniforms) + { + ReleaseUniformBuffer(pVulkan, &objectUniform.objectFragUniform); + } + + // Cmd buffers + for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + m_CmdBuffer[whichPass].Release(); + + for(auto& colorTexture : m_RenderTargets[whichPass].color) + { + colorTexture.Release(pVulkan); + } + + m_RenderTargets[whichPass].depth.Release(pVulkan); + } + + for(auto& swapchainCopyCmdBuffer : m_SwapchainCopyCmdBuffer) + { + swapchainCopyCmdBuffer.Release(); + } + + // Semaphores + vkDestroySemaphore(pVulkan->m_VulkanDevice, m_Semaphore, nullptr); + + // Drawables + m_SceneDrawables.clear(); + m_BlitQuadDrawable.reset(); + + // Internal + m_ShaderManager.reset(); + m_MaterialManager.reset(); + m_CameraController.reset(); + m_AssetManager.reset(); + + ApplicationHelperBase::Destroy(); +} + +//----------------------------------------------------------------------------- +bool Application::InitializeLights() +//----------------------------------------------------------------------------- +{ + // NOTE: Hardcoded values for the Museum asset + const float sceneRadius = 67.0f; + const float sceneMinHeight = 3.0f; + const float sceneMaxHeight = 25.0f; + + const glm::vec2 lightScaleRange = glm::vec2(3.5f, 5.0f); + + std::random_device rd; + std::mt19937 gen(rd()); + + // Distributions for position, scale, and color + std::uniform_real_distribution sceneRadiusDist(-sceneRadius, sceneRadius); + std::uniform_real_distribution sceneHeightDist(sceneMinHeight, sceneMaxHeight); + std::uniform_real_distribution lightRadiusDist(lightScaleRange.x, lightScaleRange.y); + std::uniform_real_distribution lightColorDist(0.0f, 1.0f); + + for(auto& lightData : m_LightUniformData) + { + const glm::vec2 direction = glm::normalize(glm::vec2(sceneRadiusDist(gen), sceneRadiusDist(gen))); + const glm::vec2 planePosition = direction * sceneRadiusDist(gen); + const glm::vec3 lightPosition = glm::vec3(planePosition.x, sceneHeightDist(gen), planePosition.y); + + lightData.lightPosAndRadius = glm::vec4(lightPosition, lightRadiusDist(gen)); + lightData.lightColor = glm::vec4(lightColorDist(gen), lightColorDist(gen), lightColorDist(gen), 1.0f); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitializeCamera() +//----------------------------------------------------------------------------- +{ + LOGI("******************************"); + LOGI("Initializing Camera..."); + LOGI("******************************"); + + m_Camera.SetPosition(gCameraStartPos, glm::quat(gCameraStartRot * TO_RADIANS)); + m_Camera.SetAspect(float(gRenderWidth) / float(gRenderHeight)); + m_Camera.SetFov(gFOV); + m_Camera.SetClipPlanes(gNearPlane, gFarPlane); + + // Camera Controller // + +#if defined(OS_ANDROID) + typedef CameraControllerTouch tCameraController; +#else + typedef CameraController tCameraController; +#endif + + auto cameraController = std::make_unique(); + if (!cameraController->Initialize(gRenderWidth, gRenderHeight)) + { + return false; + } + + m_CameraController = std::move(cameraController); + m_CameraController->SetMoveSpeed(10.0f); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::LoadShaders() +//----------------------------------------------------------------------------- +{ + m_ShaderManager = std::make_unique(*GetVulkan()); + m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); + + m_MaterialManager = std::make_unique(*GetVulkan()); + + LOGI("******************************"); + LOGI("Loading Shaders..."); + LOGI("******************************"); + + typedef std::pair tIdAndFilename; + for (const tIdAndFilename& i : + { tIdAndFilename { "Blit", "Blit.json" }, + tIdAndFilename { "SceneOpaque", "SceneOpaque.json" }, + tIdAndFilename { "SceneTransparent", "SceneTransparent.json" }, + tIdAndFilename { "LightCulling", "LightCulling.json" }, + tIdAndFilename { "DeferredLight", "DeferredLight.json" } + }) + { + if (!m_ShaderManager->AddShader(*m_AssetManager, i.first, i.second, SHADER_DESTINATION_PATH)) + { + LOGE("Error Loading shader %s from %s", i.first.c_str(), i.second.c_str()); + LOGI("Please verify if you have all required assets on the sample media folder"); + LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::CreateRenderTargets() +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + LOGI("**************************"); + LOGI("Creating Render Targets..."); + LOGI("**************************"); + + const TextureFormat desiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); + + auto CreateRenderTarget = [&]( + auto& renderTargetGroup, + std::string_view name, + bool tileMemory, + uint32_t width, + uint32_t height, + const std::span colorFormats, + std::optional< TextureFormat> depthFormat = std::nullopt) + { + auto textureName = std::string(name); + + for (const auto& colorFormat : colorFormats) + { + CreateTexObjectInfo createInfo{ + .uiWidth = width, + .uiHeight = height, + .Format = colorFormat, + .TexType = textureName == "BLIT" ? TEXTURE_TYPE::TT_RENDER_TARGET_TRANSFERSRC : TEXTURE_TYPE::TT_RENDER_TARGET, + .pName = textureName.c_str(), + }; + renderTargetGroup.color.push_back(CreateTextureObject(*pVulkan, createInfo)); + + if (tileMemory && m_TileMemoryPool) + { + textureName.append(" TILEMEM"); + createInfo.pName = textureName.c_str(); + if (auto newTexture = CreateTextureObject(*pVulkan, createInfo, &m_TileMemoryPool); !newTexture.IsEmpty()) + { + renderTargetGroup.colorTileMemory.push_back(std::move(newTexture)); + continue; + } + } + + renderTargetGroup.colorTileMemory.push_back(CreateTextureObject(*pVulkan, createInfo)); + } + + if (depthFormat) + { + CreateTexObjectInfo createInfo{ + .uiWidth = width, + .uiHeight = height, + .Format = depthFormat.value(), + .TexType = textureName == "BLIT" ? TEXTURE_TYPE::TT_RENDER_TARGET_TRANSFERSRC : TEXTURE_TYPE::TT_DEPTH_TARGET, + .pName = textureName.c_str(), + }; + renderTargetGroup.depth = CreateTextureObject(*pVulkan, createInfo); + + if (tileMemory && m_TileMemoryPool) + { + textureName.append(" TILEMEM"); + createInfo.pName = textureName.c_str(); + + if (auto newTexture = CreateTextureObject(*pVulkan, createInfo, &m_TileMemoryPool); !newTexture.IsEmpty()) + { + renderTargetGroup.depthTileMemory = std::move(newTexture); + return; + } + } + + renderTargetGroup.depthTileMemory = CreateTextureObject(*pVulkan, createInfo); + } + }; + + CreateRenderTarget(m_RenderTargets[RP_SCENE], "SCENE", true, gRenderWidth, gRenderHeight, SceneColorType, desiredDepthFormat); + CreateRenderTarget(m_RenderTargets[RP_DEFERRED_LIGHT], "DEFERRED LIGHT", false, gRenderWidth, gRenderHeight, DeferredLightColorType); + CreateRenderTarget(m_RenderTargets[RP_HUD], "HUD", false, gSurfaceWidth, gSurfaceHeight, HudColorType); + CreateRenderTarget(m_RenderTargets[RP_BLIT], "BLIT", false, gSurfaceWidth, gSurfaceHeight, FinalColorType); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::SetupRenderContext() +//----------------------------------------------------------------------------- +{ + auto& vulkan = *GetVulkan(); + + LOGI("******************************"); + LOGI("Initializing Render Passes... "); + LOGI("******************************"); + + for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + std::vector< TextureFormat> colorFormats; + for (auto& colorAttachment : m_RenderTargets[whichPass].color) + { + colorFormats.push_back(colorAttachment.Format); + } + + m_RenderPassData[whichPass].renderContext = + { + colorFormats, + m_RenderTargets[whichPass].depth.Format, + TextureFormat::UNDEFINED, + sRenderPassNames[whichPass] + }; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitBuffers() +//----------------------------------------------------------------------------- +{ + LOGI("******************************"); + LOGI("Initializing Buffers..."); + LOGI("******************************"); + + Vulkan* const pVulkan = GetVulkan(); + + if (!CreateUniformBuffer(pVulkan, m_ObjectVertUniform)) + { + return false; + } + + if (!CreateUniformBuffer(pVulkan, &m_LightUniform, sizeof(LightUB) * m_LightUniformData.size(), m_LightUniformData.data())) + { + return false; + } + + if (!CreateUniformBuffer(pVulkan, m_SceneInfoUniform)) + { + return false; + } + + // Worst case = every light is present on every tile + // NOTE: int32_t can totally be swapped by a smaller type + const size_t lightIndicesBufferSize = sizeof(int32_t) * MAX_CLUSTER_MATRIX_SIZE * MAX_LIGHT_COUNT; + + if (!m_LightCountsBuffer.Initialize( + &pVulkan->GetMemoryManager(), + sizeof(int32_t) * MAX_CLUSTER_MATRIX_SIZE, + BufferUsageFlags::Storage, + MemoryUsage::GpuExclusive)) + { + return false; + } + + if (!m_LightIndicesBuffer.Initialize( + &pVulkan->GetMemoryManager(), + lightIndicesBufferSize, + BufferUsageFlags::Storage, + MemoryUsage::GpuExclusive)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitGui(uintptr_t windowHandle) +//----------------------------------------------------------------------------- +{ + const auto& hudRenderTarget = m_RenderTargets[RP_HUD]; + m_Gui = std::make_unique(*GetVulkan()); + if (!m_Gui->Initialize( + windowHandle, + hudRenderTarget.color[0].Format, + hudRenderTarget.color[0].Width, + hudRenderTarget.color[0].Height)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::LoadMeshObjects() +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + LOGI("***********************"); + LOGI("Initializing Meshes... "); + LOGI("***********************"); + + const auto* pSceneOpaqueShader = m_ShaderManager->GetShader("SceneOpaque"); + const auto* pSceneTransparentShader = m_ShaderManager->GetShader("SceneTransparent"); + const auto* deferredLightShader = m_ShaderManager->GetShader("DeferredLight"); + const auto* pBlitQuadShader = m_ShaderManager->GetShader("Blit"); + if (!pSceneOpaqueShader || !pSceneTransparentShader || !pBlitQuadShader || !deferredLightShader) + { + return false; + } + + LOGI("***********************************"); + LOGI("Loading and preparing the museum..."); + LOGI("***********************************"); + + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* blackTexture = m_TextureManager->GetOrLoadTexture("black_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture("normal_default.ktx", m_SamplerRepeat, prefixTextureDir); + + if (!whiteTexture || !blackTexture || !normalDefaultTexture) + { + LOGE("Failed to load supporting textures"); + return false; + } + + auto UniformBufferLoader = [&](const ObjectMaterialParameters& objectMaterialParameters) -> ObjectMaterialParameters& + { + auto hash = objectMaterialParameters.GetHash(); + + auto iter = m_ObjectFragUniforms.try_emplace(hash, ObjectMaterialParameters()); + if (iter.second) + { + iter.first->second.objectFragUniformData = objectMaterialParameters.objectFragUniformData; + if (!CreateUniformBuffer(pVulkan, iter.first->second.objectFragUniform)) + { + LOGE("Failed to create object uniform buffer"); + } + } + + return iter.first->second; + }; + + auto MaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef)->std::optional + { + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const PathManipulator_ChangeExtension changeTextureExt{ ".ktx" }; + + auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* normalTexture = m_TextureManager->GetOrLoadTexture(materialDef.bumpFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(materialDef.emissiveFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(materialDef.specMapFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + bool alphaCutout = materialDef.alphaCutout; + bool transparent = materialDef.transparent; + + const auto* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; + + ObjectMaterialParameters objectMaterial; + objectMaterial.objectFragUniformData.Color.r = static_cast(materialDef.baseColorFactor[0]); + objectMaterial.objectFragUniformData.Color.g = static_cast(materialDef.baseColorFactor[1]); + objectMaterial.objectFragUniformData.Color.b = static_cast(materialDef.baseColorFactor[2]); + objectMaterial.objectFragUniformData.Color.a = static_cast(materialDef.baseColorFactor[3]); + objectMaterial.objectFragUniformData.ORM.b = static_cast(materialDef.metallicFactor); + objectMaterial.objectFragUniformData.ORM.g = static_cast(materialDef.roughnessFactor); + + if (diffuseTexture == nullptr || normalTexture == nullptr) + { + return std::nullopt; + } + + auto shaderMaterial = m_MaterialManager->CreateMaterial(*targetShader, NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "Diffuse") + { + return { diffuseTexture ? diffuseTexture : whiteTexture }; + } + if (texName == "Normal") + { + return { normalTexture ? normalTexture : normalDefaultTexture }; + } + if (texName == "Emissive") + { + return { emissiveTexture ? emissiveTexture : blackTexture }; + } + if (texName == "MetallicRoughness") + { + return { metallicRoughnessTexture ? metallicRoughnessTexture : blackTexture }; + } + + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "Vert") + { + return { m_ObjectVertUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "Frag") + { + return { UniformBufferLoader(objectMaterial).objectFragUniform.buf.GetVkBuffer() }; + } + + return {}; + } + ); + + return shaderMaterial; + }; + + + const uint32_t loaderFlags = 0; // No instancing + const bool ignoreTransforms = (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0; + + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(sceneAssetPath); + MeshObjectIntermediateGltfProcessor meshObjectProcessor(sceneAssetPath, ignoreTransforms, glm::vec3(1.0f,1.0f,1.0f)); + CameraGltfProcessor meshCameraProcessor{}; + + if (!MeshLoader::LoadGltf(*m_AssetManager, sceneAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || + !DrawableLoader::CreateDrawables(*pVulkan, + std::move(meshObjectProcessor.m_meshObjects), + m_RenderPassData[RP_SCENE].renderContext, + MaterialLoader, + m_SceneDrawables, + loaderFlags)) + { + LOGE("Error Loading the museum gltf file"); + LOGI("Please verify if you have all required assets on the sample media folder"); + LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); + return false; + } + + if (!meshCameraProcessor.m_cameras.empty()) + { + const auto& camera = meshCameraProcessor.m_cameras[0]; + m_Camera.SetPosition(camera.Position, camera.Orientation); + } + + LOGI("*********************"); + LOGI("Creating Quad mesh..."); + LOGI("*********************"); + { + Mesh blitQuadMesh; + MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); + + // Blit Material + auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitQuadShader, pVulkan->m_SwapchainImageCount, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "Scene") + { + return { &m_RenderTargets[RP_DEFERRED_LIGHT].color[0] }; + } + else if (texName == "Overlay") + { + return { &m_RenderTargets[RP_HUD].color[0] }; + } + return {}; + }, + [this](const std::string& bufferName) -> PerFrameBufferVulkan + { + return {}; + } + ); + + m_BlitQuadDrawable = std::make_unique(*pVulkan, std::move(blitQuadShaderMaterial)); + if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].renderContext, std::move(blitQuadMesh))) + { + return false; + } + } + + LOGI("************************************"); + LOGI("Creating Deferred Light Quad mesh..."); + LOGI("************************************"); + { + Mesh blitQuadMesh; + MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); + + auto deferredLightQuadShaderMaterial = m_MaterialManager->CreateMaterial(*deferredLightShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneColor") + { + return { &m_RenderTargets[RP_SCENE].color[0] }; + } + else if (texName == "SceneNormal") + { + return { &m_RenderTargets[RP_SCENE].color[1] }; + } + else if (texName == "SceneDepth") + { + return { &m_RenderTargets[RP_SCENE].depth }; + } + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + } + ); + + m_DeferredLightQuadDrawable = std::make_unique(*pVulkan, std::move(deferredLightQuadShaderMaterial)); + if (!m_DeferredLightQuadDrawable->Init(m_RenderPassData[RP_DEFERRED_LIGHT].renderContext, std::move(blitQuadMesh))) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::CreateComputables() +//----------------------------------------------------------------------------- +{ + auto pVulkan = GetVulkan(); + + const auto* lightCullingShader = m_ShaderManager->GetShader("LightCulling"); + if (!lightCullingShader) + { + return false; + } + + auto material = m_MaterialManager->CreateMaterial( + *lightCullingShader, + NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneDepth") + { + return { &m_RenderTargets[RP_SCENE].depth }; + } + assert(0); + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + }); + + m_ComputePassData[CP_LIGHT_CULLING_LIST].passComputable = std::make_unique(*pVulkan, std::move(material)); + if (!m_ComputePassData[CP_LIGHT_CULLING_LIST].passComputable->Init()) + { + LOGE("Error Creating SGSR computables..."); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitCommandBuffers() +//----------------------------------------------------------------------------- +{ + LOGI("*******************************"); + LOGI("Initializing Command Buffers..."); + LOGI("*******************************"); + + Vulkan* const pVulkan = GetVulkan(); + + auto GetPassName = [](uint32_t whichPass) + { + if (whichPass >= sRenderPassNames.size()) + { + LOGE("GetPassName() called with unknown pass (%d)!", whichPass); + return "RP_UNKNOWN"; + } + + return sRenderPassNames[whichPass]; + }; + + char szName[256]; + for (uint32_t whichBuffer = 0; whichBuffer < m_CmdBuffer.size(); whichBuffer++) + { + // The Pass Command Buffer => Primary + sprintf(szName, "Primary (Buffer %d of %d)", whichBuffer + 1, NUM_VULKAN_BUFFERS); + if (!m_CmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + + for (int i=0; i< m_SwapchainCopyCmdBuffer.size(); i++) + { + auto& swapchainCopyCmdBuffer = m_SwapchainCopyCmdBuffer[i]; + + // The Pass Command Buffer => Primary + sprintf(szName, "Swapchain Copy CMD Buffer (Buffer %d of %d)", i + 1, NUM_VULKAN_BUFFERS); + if (!swapchainCopyCmdBuffer.Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitLocalSemaphores() +//----------------------------------------------------------------------------- +{ + LOGI("********************************"); + LOGI("Initializing Local Semaphores..."); + LOGI("********************************"); + + const VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + + VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_Semaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + + retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_SwapchhainCopySemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void Application::UpdateGui() +//----------------------------------------------------------------------------- +{ + if (m_Gui) + { + m_Gui->Update(); + ImGuiIO& io = ImGui::GetIO(); + + ImGui::SetNextWindowPos(ImVec2(200, 100), ImGuiCond_Once); + if (ImGui::Begin("FPS", (bool*)nullptr, ImGuiWindowFlags_NoTitleBar)) + { + if(ImGui::CollapsingHeader("Scene Details")) + { + ImGui::Text("FPS: %.1f", m_CurrentFPS); + ImGui::Text("Camera [%f, %f, %f]", m_Camera.Position().x, m_Camera.Position().y, m_Camera.Position().z); + } + + // Sun light part isn't really doing anything right now +#if 0 + if(ImGui::CollapsingHeader("Sun Light")) + { + ImGui::DragFloat3("Sun Dir", &m_SceneInfoUniformData.skyLightDirection.x, 0.01f, -1.0f, 1.0f); + ImGui::DragFloat3("Sun Color", &m_SceneInfoUniformData.skyLightColor.x, 0.01f, 0.0f, 1.0f); + ImGui::DragFloat("Sun Intensity", &m_SceneInfoUniformData.skyLightColor.w, 0.1f, 0.0f, 100.0f); + ImGui::DragFloat3("Ambient Color", &m_SceneInfoUniformData.ambientColor.x, 0.01f, 0.0f, 1.0f); + } +#endif + + if(ImGui::CollapsingHeader("Clustered Deferred Light", ImGuiTreeNodeFlags_DefaultOpen)) + { + bool ignoreLightTiles = static_cast(m_SceneInfoUniformData.ignoreLightTiles); + ImGui::Checkbox("Deactivate Clustering", &ignoreLightTiles); + m_SceneInfoUniformData.ignoreLightTiles = static_cast(ignoreLightTiles); + + bool debugShaders = static_cast(m_SceneInfoUniformData.debugShaders); + ImGui::Checkbox("Visualize Clusters", &debugShaders); + m_SceneInfoUniformData.debugShaders = static_cast(debugShaders); + + ImGui::Separator(); + + ImGui::DragInt("Light Count", &m_SceneInfoUniformData.lightCount, 1.0f, 0, MAX_LIGHT_COUNT); + + ImGui::Separator(); + + std::array tileCount + { + static_cast(m_SceneInfoUniformData.clusterCountX) , + static_cast(m_SceneInfoUniformData.clusterCountY) + }; + + ImGui::DragInt2("Cluster Count", tileCount.data(), 1.0f, 1, MAX_CLUSTER_MATRIX_DIMENSION_SIZE); + m_SceneInfoUniformData.clusterCountX = std::min(static_cast(MAX_CLUSTER_MATRIX_DIMENSION_SIZE), static_cast(tileCount[0])); + m_SceneInfoUniformData.clusterCountY = std::min(static_cast(MAX_CLUSTER_MATRIX_DIMENSION_SIZE), static_cast(tileCount[1])); + + ImGui::Text( + "Total Clusters: [%d, %d] -> %d", + m_SceneInfoUniformData.clusterCountX, + m_SceneInfoUniformData.clusterCountY, + m_SceneInfoUniformData.clusterCountX * m_SceneInfoUniformData.clusterCountY); + } + + if (ImGui::CollapsingHeader("Tile Memory", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::BeginDisabled(!m_IsTileMemoryHeapSupported); + ImGui::Checkbox("Tile Memory: Render Targets", &m_RenderTargetUsesTileMemory); + ImGui::EndDisabled(); + + m_SceneInfoUniformData.tileMemoryEnabled = static_cast(m_RenderTargetUsesTileMemory); + } + + glm::vec3 LightDirNotNormalized = m_SceneInfoUniformData.skyLightDirection; + LightDirNotNormalized = glm::normalize(LightDirNotNormalized); + m_SceneInfoUniformData.skyLightDirection = glm::vec4(LightDirNotNormalized, 0.0f); + } + ImGui::End(); + + return; + } +} + +//----------------------------------------------------------------------------- +bool Application::UpdateUniforms(uint32_t whichBuffer) +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Vert data + { + glm::mat4 LocalModel = glm::mat4(1.0f); + LocalModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); + LocalModel = glm::scale(LocalModel, glm::vec3(1.0f)); + glm::mat4 LocalMVP = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix() * LocalModel; + + m_ObjectVertUniformData.MVPMatrix = LocalMVP; + m_ObjectVertUniformData.ModelMatrix = LocalModel; + UpdateUniformBuffer(pVulkan, m_ObjectVertUniform, m_ObjectVertUniformData); + } + + // Frag data + for (auto& [hash, objectUniform] : m_ObjectFragUniforms) + { + UpdateUniformBuffer(pVulkan, objectUniform.objectFragUniform, objectUniform.objectFragUniformData); + } + + // Scene data + { + glm::mat4 CameraViewInv = glm::inverse(m_Camera.ViewMatrix()); + glm::mat4 CameraProjection = m_Camera.ProjectionMatrix(); + glm::mat4 CameraProjectionInv = glm::inverse(CameraProjection); + + m_SceneInfoUniformData.projectionInv = CameraProjectionInv; + m_SceneInfoUniformData.viewInv = CameraViewInv; + m_SceneInfoUniformData.view = m_Camera.ViewMatrix(); + m_SceneInfoUniformData.viewInv[3][0] *= 0.5f; + m_SceneInfoUniformData.viewInv[3][1] *= 0.5f; + m_SceneInfoUniformData.viewInv[3][2] *= 0.5f; + m_SceneInfoUniformData.viewProjection = CameraProjection * m_Camera.ViewMatrix(); + m_SceneInfoUniformData.viewProjectionInv = glm::inverse(m_SceneInfoUniformData.viewProjection); + m_SceneInfoUniformData.projectionInvW = glm::vec4(CameraProjectionInv[0].w, CameraProjectionInv[1].w, CameraProjectionInv[2].w, CameraProjectionInv[3].w); + m_SceneInfoUniformData.cameraPos = glm::vec4(m_Camera.Position(), 0.0f); + + m_SceneInfoUniformData.viewportWidth = gRenderWidth; + m_SceneInfoUniformData.viewportHeight = gRenderHeight; + + UpdateUniformBuffer(pVulkan, m_SceneInfoUniform, m_SceneInfoUniformData); + } + + // Light data (this doesn't change, no need to update every frame) + { + // UpdateUniformBuffer(pVulkan, m_LightUniform, m_LightUniformData); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::UpdateDescriptors(uint32_t whichBuffer) +//----------------------------------------------------------------------------- +{ + // NOTE: Commented sections are because transient buffers depend on the tile memory extension + + bool success = true; + + for(auto& computablePasses : m_ComputePassData[CP_LIGHT_CULLING_LIST].passComputable->GetPasses()) + { + auto& vkComputablePass = apiCast(computablePasses); + auto& computableMaterialPass = vkComputablePass.GetMaterialPass(); + + success &= computableMaterialPass.UpdateDescriptorSetBinding(whichBuffer, "SceneDepth", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].depthTileMemory : m_RenderTargets[RP_SCENE].depth); + } + + success &= m_BlitQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "Scene", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_DEFERRED_LIGHT].colorTileMemory[0] : m_RenderTargets[RP_DEFERRED_LIGHT].color[0]); + success &= m_BlitQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "Overlay", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_HUD].colorTileMemory[0] : m_RenderTargets[RP_HUD].color[0]); + + success &= m_DeferredLightQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "SceneColor", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[0] : m_RenderTargets[RP_SCENE].color[0]); + success &= m_DeferredLightQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "SceneNormal", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[1] : m_RenderTargets[RP_SCENE].color[1]); + success &= m_DeferredLightQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "SceneDepth", + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].depthTileMemory : m_RenderTargets[RP_SCENE].depth); + + return success; +} + +//----------------------------------------------------------------------------- +void Application::Render(float fltDiffTime) +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Obtain the next swap chain image for the next frame. + auto currentVulkanBuffer = pVulkan->SetNextBackBuffer(); + uint32_t whichBuffer = currentVulkanBuffer.idx; + + // ******************************** + // Application Draw() - Begin + // ******************************** + + UpdateGui(); + + // Update camera + m_Camera.UpdateController(fltDiffTime, *m_CameraController); + m_Camera.UpdateMatrices(); + + // Update uniform buffers with latest data + if (!UpdateUniforms(whichBuffer)) + { + assert(false); + return; + } + + // Update descriptors + if(!UpdateDescriptors(whichBuffer)) + { + assert(false); + return; + } + + // const VkPipelineStageFlags defaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + const VkPipelineStageFlags defaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT }; + + // First time through, wait for the back buffer to be ready + std::span pWaitSemaphores = { ¤tVulkanBuffer.semaphore, 1 }; + + auto RecordPassData = [this, whichBuffer, defaultGfxWaitDstStageMasks, &pWaitSemaphores]( + std::optional> renderingInfo, + PassRecordData passRecordInfo, + VkFence completionFence = VK_NULL_HANDLE) + { + auto& [passData, passDataProcessor] = passRecordInfo; + + if (passDataProcessor.preDrawCallback) + { + passDataProcessor.preDrawCallback(whichBuffer, m_CmdBuffer[whichBuffer]); + } + + if (renderingInfo) + { + const auto& [renderPassIndex, renderingAttachmentGroup] = renderingInfo.value(); + auto& renderPassInfo = m_RenderPassData[renderPassIndex].renderContext.GetRenderingInfo(renderingAttachmentGroup).get(); + vkCmdBeginRenderingKHR(m_CmdBuffer[whichBuffer].m_VkCommandBuffer, &renderPassInfo); + + VkViewport viewport; + viewport.x = renderPassInfo.renderArea.offset.x; + viewport.y = renderPassInfo.renderArea.offset.y; + viewport.width = renderPassInfo.renderArea.extent.width; + viewport.height = renderPassInfo.renderArea.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(m_CmdBuffer[whichBuffer].m_VkCommandBuffer, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = viewport.x; + scissor.offset.y = viewport.y; + scissor.extent.width = viewport.width; + scissor.extent.height = viewport.height; + vkCmdSetScissor(m_CmdBuffer[whichBuffer].m_VkCommandBuffer, 0, 1, &scissor); + } + + if (passDataProcessor.drawCallback) + { + passDataProcessor.drawCallback(whichBuffer, m_CmdBuffer[whichBuffer]); + } + + if (renderingInfo) + { + vkCmdEndRenderingKHR(m_CmdBuffer[whichBuffer].m_VkCommandBuffer); + } + + if (passDataProcessor.postDrawCallback) + { + passDataProcessor.postDrawCallback(whichBuffer, m_CmdBuffer[whichBuffer]); + } + }; + + auto SimpleBufferBarrier = [this]( + CommandListVulkan& commandList, + VkBuffer targetBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask) + { + VkBufferMemoryBarrier memoryBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER }; + memoryBarrier.srcAccessMask = srcAccessMask; + memoryBarrier.dstAccessMask = dstAccessMask; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.buffer = targetBuffer; + memoryBarrier.offset = 0; + memoryBarrier.size = VK_WHOLE_SIZE; + + vkCmdPipelineBarrier( + commandList.m_VkCommandBuffer, + srcStageMask, + dstStageMask, + 0, + 0, + nullptr, + 1, + &memoryBarrier, + 0, + nullptr); + }; + + auto SimpleImageBarrier = [this]( + CommandListVulkan& commandList, + VkImage targetImage, + VkImageAspectFlags aspectFlags, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkImageLayout fromLayout, + std::optional toLayout = std::nullopt) + { + VkImageMemoryBarrier memoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + memoryBarrier.oldLayout = fromLayout; + memoryBarrier.newLayout = toLayout ? toLayout.value() : fromLayout; + memoryBarrier.image = targetImage; + memoryBarrier.subresourceRange.aspectMask = aspectFlags; + memoryBarrier.subresourceRange.baseMipLevel = 0; + memoryBarrier.subresourceRange.levelCount = 1; + memoryBarrier.subresourceRange.baseArrayLayer = 0; + memoryBarrier.subresourceRange.layerCount = 1; + memoryBarrier.srcAccessMask = srcAccessMask; + memoryBarrier.dstAccessMask = dstAccessMask; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + commandList.m_VkCommandBuffer, + srcStageMask, + dstStageMask, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &memoryBarrier); + }; + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_SCENE // + /////////////////////////////////////////////////////////////////////////////////////// + auto sceneDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_SCENE].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[1].GetVkImage() : m_RenderTargets[RP_SCENE].color[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].depthTileMemory.GetVkImage() : m_RenderTargets[RP_SCENE].depth.GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + // Scene drawables + for (const auto& sceneDrawable : m_SceneDrawables) + { + AddDrawableToCmdBuffer(sceneDrawable, commandList, whichBuffer); + } + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_SCENE].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory[1].GetVkImage() : m_RenderTargets[RP_SCENE].color[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].depthTileMemory.GetVkImage() : m_RenderTargets[RP_SCENE].depth.GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); // VK_IMAGE_LAYOUT_GENERAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: CP_LIGHT_CULLING_LIST // + /////////////////////////////////////////////////////////////////////////////////////// + auto lightCullingListDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + auto& computePassData = m_ComputePassData[CP_LIGHT_CULLING_LIST]; + + for (auto& computablePass : computePassData.passComputable->GetPasses()) + { + computePassData.passComputable->SetDispatchGroupCount(0, + { + // Dispatch all tiles at once + m_SceneInfoUniformData.clusterCountX, + m_SceneInfoUniformData.clusterCountY, + 1 + }); + + computePassData.passComputable->DispatchPass( + commandList, + computablePass, + whichBuffer); + VkPipelineStageFlags2; + } + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT); + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].depthTileMemory.GetVkImage() : m_RenderTargets[RP_SCENE].depth.GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, // VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_DEFERRED_LIGHT // + /////////////////////////////////////////////////////////////////////////////////////// + auto deferredLightDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_DEFERRED_LIGHT].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_DEFERRED_LIGHT].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + AddDrawableToCmdBuffer(*m_DeferredLightQuadDrawable, commandList, whichBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_DEFERRED_LIGHT].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_DEFERRED_LIGHT].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_HUD // + /////////////////////////////////////////////////////////////////////////////////////// + auto hudDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_HUD].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_HUD].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + GetGui()->Render(commandList.m_VkCommandBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_HUD].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_HUD].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_BLIT // + /////////////////////////////////////////////////////////////////////////////////////// + auto blitDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_BLIT].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_BLIT].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + AddDrawableToCmdBuffer(*m_BlitQuadDrawable.get(), commandList, whichBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_BLIT].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_BLIT].color[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // RECORD COMMANDS // + /////////////////////////////////////////////////////////////////////////////////////// + + if (!m_CmdBuffer[whichBuffer].Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) + { + return; + } + + /* + * Here we bind the tile memory heap object, ensuring it will be used for any subsequent command + * Since all tile memory comes from a single pool, and we are not micro-managing this pool (we allocate + * attachments until nothing else fits), we can grab the memory allocation from any of the attachments, + * here we choose the scene color one since it's the first we attempt to allocate on tile memory (and + * consequentially, the one that will always successfully be placed there) + */ + if (m_IsTileMemoryHeapSupported && m_RenderTargetUsesTileMemory && !m_RenderTargets[RP_SCENE].colorTileMemory.empty()) + { + const auto& imageAllocation = m_RenderTargets[RP_SCENE].colorTileMemory[0].Image.GetMemoryAllocation(); + VkTileMemoryBindInfoQCOM tileMemoryBindInfo{ VK_STRUCTURE_TYPE_TILE_MEMORY_BIND_INFO_QCOM }; + tileMemoryBindInfo.memory = pVulkan->GetMemoryManager().GetVkDeviceMemory(imageAllocation); + + vkCmdBindTileMemoryQCOM(m_CmdBuffer[whichBuffer].m_VkCommandBuffer, &tileMemoryBindInfo); + } + + RecordPassData( + std::optional>({ RP_SCENE , RenderingAttachmentInfoGroup( + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_SCENE].colorTileMemory : m_RenderTargets[RP_SCENE].color, + m_RenderTargetUsesTileMemory ? &m_RenderTargets[RP_SCENE].depthTileMemory : &m_RenderTargets[RP_SCENE].depth, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store, + true, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_SCENE], std::move(sceneDataProcessor))); + + RecordPassData( + std::nullopt, + PassRecordData(m_ComputePassData[CP_LIGHT_CULLING_LIST], std::move(lightCullingListDataProcessor))); + + RecordPassData( + std::optional>({ RP_DEFERRED_LIGHT , RenderingAttachmentInfoGroup( + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_DEFERRED_LIGHT].colorTileMemory : m_RenderTargets[RP_DEFERRED_LIGHT].color, + nullptr, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_DEFERRED_LIGHT], std::move(deferredLightDataProcessor))); + + RecordPassData( + std::optional>({ RP_HUD , RenderingAttachmentInfoGroup( + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_HUD].colorTileMemory : m_RenderTargets[RP_HUD].color, + nullptr, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_HUD], std::move(hudDataProcessor))); + + RecordPassData( + std::optional>({ RP_BLIT , RenderingAttachmentInfoGroup( + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_BLIT].colorTileMemory : m_RenderTargets[RP_BLIT].color, + nullptr, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_BLIT], std::move(blitDataProcessor))); + + m_CmdBuffer[whichBuffer].End(); + + m_CmdBuffer[whichBuffer].QueueSubmit( + pWaitSemaphores, + defaultGfxWaitDstStageMasks, + { &m_Semaphore,1 }); + + pWaitSemaphores = { &m_Semaphore,1 }; + + /////////////////////////////////////////////////////////////////////////////////////// + // SWAPCHAIN COPY // + /////////////////////////////////////////////////////////////////////////////////////// + { + const auto& sourceImage = m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_BLIT].colorTileMemory[0] : m_RenderTargets[RP_BLIT].color[0]; + const auto swapchainImage = pVulkan->GetSwapchainImage(currentVulkanBuffer.swapchainPresentIdx); + + m_SwapchainCopyCmdBuffer[whichBuffer].Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + //////////////////////////////////// + // PREPARE SWAPCHAIN FOR TRANSFER // + //////////////////////////////////// + { + SimpleImageBarrier( + m_SwapchainCopyCmdBuffer[whichBuffer], + swapchainImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } + + ///////////////////////// + // BLIT INTO SWAPCHAIN // + ///////////////////////// + + VkImageBlit blitRegion{}; + blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.srcSubresource.baseArrayLayer = 0; + blitRegion.srcSubresource.layerCount = 1; + blitRegion.srcSubresource.mipLevel = 0; + blitRegion.srcOffsets[1].x = sourceImage.Width; + blitRegion.srcOffsets[1].y = sourceImage.Height; + blitRegion.srcOffsets[1].z = 1; + blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.dstSubresource.baseArrayLayer = 0; + blitRegion.dstSubresource.layerCount = 1; + blitRegion.dstSubresource.mipLevel = 0; + blitRegion.dstOffsets[1].x = pVulkan->m_SurfaceWidth; + blitRegion.dstOffsets[1].y = pVulkan->m_SurfaceHeight; + blitRegion.dstOffsets[1].z = 1; + + vkCmdBlitImage( + m_SwapchainCopyCmdBuffer[whichBuffer].m_VkCommandBuffer, + m_RenderTargetUsesTileMemory ? m_RenderTargets[RP_BLIT].colorTileMemory[0].GetVkImage() : m_RenderTargets[RP_BLIT].color[0].GetVkImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + swapchainImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &blitRegion, + VK_FILTER_NEAREST); + + /////////////////////////////////// + // PREPARE SWAPCHAIN FOR PRESENT // + /////////////////////////////////// + { + SimpleImageBarrier( + m_SwapchainCopyCmdBuffer[whichBuffer], + swapchainImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + } + + m_SwapchainCopyCmdBuffer[whichBuffer].End(); + + /////////////////////////// + // SUBMIT SWAPCHAIN COPY // + /////////////////////////// + + m_SwapchainCopyCmdBuffer[whichBuffer].QueueSubmit( + pWaitSemaphores, + defaultGfxWaitDstStageMasks, + { &m_SwapchhainCopySemaphore,1 }, + currentVulkanBuffer.fence); + + pWaitSemaphores = { &m_SwapchhainCopySemaphore,1 }; + } + + // Queue is loaded up, tell the driver to start processing + pVulkan->PresentQueue(pWaitSemaphores, currentVulkanBuffer.swapchainPresentIdx); +} \ No newline at end of file diff --git a/samples/tile_memory/code/main/application.hpp b/samples/tile_memory/code/main/application.hpp new file mode 100644 index 0000000..e5d202d --- /dev/null +++ b/samples/tile_memory/code/main/application.hpp @@ -0,0 +1,310 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "main/applicationHelperBase.hpp" +#include "memory/vulkan/uniform.hpp" +#include "memory/buffer.hpp" +#include "memory/vulkan/bufferObject.hpp" +#include "memory/vulkan/indexBufferObject.hpp" +#include "memory/vulkan/vertexBufferObject.hpp" +#include "memory/vulkan/drawIndirectBufferObject.hpp" +#include "vulkan/commandBuffer.hpp" +#include "vulkan/renderPass.hpp" +#include + +#define MAX_LIGHT_COUNT 2048 +#define MAX_CLUSTER_MATRIX_DIMENSION_SIZE 8 +#define MAX_CLUSTER_MATRIX_SIZE (MAX_CLUSTER_MATRIX_DIMENSION_SIZE * MAX_CLUSTER_MATRIX_DIMENSION_SIZE) + +// Forward declarations +class MaterialManagerBase; +class ShaderManagerBase; +struct TileMemoryHeapExtension; + +enum RENDER_PASS +{ + RP_SCENE = 0, + RP_DEFERRED_LIGHT, + RP_HUD, + RP_BLIT, + NUM_RENDER_PASSES +}; + +enum COMPUTE_PASS +{ + CP_LIGHT_CULLING_LIST = 0, + NUM_COMPUTE_PASSES +}; + +// ********************** +// Uniform Buffers +// ********************** +struct ObjectVertUB +{ + glm::mat4 MVPMatrix; + glm::mat4 ModelMatrix; + glm::mat4 ShadowMatrix; +}; + +struct ObjectFragUB +{ + glm::vec4 Color; + glm::vec4 ORM; +}; + +struct LightUB +{ + glm::vec4 lightPosAndRadius; // xyz = pos | w = radius + glm::vec4 lightColor; +}; + +struct alignas(16) SceneInfoUB +{ + glm::mat4 projectionInv; + glm::mat4 view; + glm::mat4 viewInv; + glm::mat4 viewProjection; + glm::mat4 viewProjectionInv; // ViewInv * ProjectionInv + glm::vec4 projectionInvW; // w components of ProjectionInv + glm::vec4 cameraPos; + + glm::vec4 skyLightDirection = glm::vec4(-0.564000f, 0.826000f, 0.000000f, 0.0f); + glm::vec4 skyLightColor = glm::vec4(1.000000f, 1.000000f, 1.000000f, 1.000000); + + glm::vec4 ambientColor = glm::vec4(0.340000f, 0.340000f, 0.340000f, 0.0f); + + uint32_t viewportWidth; + uint32_t viewportHeight; + uint32_t clusterCountX = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; // How many clusters on the x coordinate, inside each bin + uint32_t clusterCountY = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; // How many clusters on the y coordinate, inside each bin + int32_t lightCount = MAX_LIGHT_COUNT / 4; + int32_t debugShaders = false; + int32_t ignoreLightTiles = false; + int32_t tileMemoryEnabled = false; +}; + +struct BasePassData +{ +}; + +struct RenderPassData : public BasePassData +{ + ::RenderContext renderContext; +}; + +struct ComputePassData : public BasePassData +{ + std::unique_ptr< Computable> passComputable; +}; + +struct RenderTargetGroup +{ + std::vector< Texture> color; + std::vector< Texture> colorTileMemory; + Texture depth; + Texture depthTileMemory; +}; + +struct PassAttachmentInfo +{ + PassAttachmentInfo() = default; + PassAttachmentInfo( + const RenderTargetGroup& inRenderTarget, + RenderPassInputUsage inInputUsage, + RenderPassOutputUsage inOutputUsage, + std::vector inRenderTargetLocalReadColorAttachments = {}, + bool inRenderTargetLocalReadDepthStencilAttachment = false) + : renderTarget(&inRenderTarget) + , colorInputUsage(inInputUsage) + , depthInputUsage(inInputUsage) + , colorOutputUsage(inOutputUsage) + , depthOutputUsage(inOutputUsage) + , renderTargetLocalReadColorAttachments(std::move(inRenderTargetLocalReadColorAttachments)) + , renderTargetLocalReadDepthStencilAttachment(inRenderTargetLocalReadDepthStencilAttachment) + { + } + + PassAttachmentInfo( + const RenderTargetGroup& inRenderTarget, + RenderPassInputUsage inColorInputUsage, + RenderPassOutputUsage inColorOutputUsage, + RenderPassInputUsage inDepthInputUsage, + RenderPassOutputUsage inDepthOutputUsage, + std::vector inRenderTargetLocalReadColorAttachments = {}, + bool inRenderTargetLocalReadDepthStencilAttachment = false) + : renderTarget(&inRenderTarget) + , colorInputUsage(inColorInputUsage) + , colorOutputUsage(inColorOutputUsage) + , depthInputUsage(inDepthInputUsage) + , depthOutputUsage(inDepthOutputUsage) + , renderTargetLocalReadColorAttachments(std::move(inRenderTargetLocalReadColorAttachments)) + , renderTargetLocalReadDepthStencilAttachment(inRenderTargetLocalReadDepthStencilAttachment) + { + } + + const RenderTargetGroup* renderTarget; + std::vector renderTargetLocalReadColorAttachments; + bool renderTargetLocalReadDepthStencilAttachment; + RenderPassInputUsage colorInputUsage; + RenderPassInputUsage depthInputUsage; + RenderPassOutputUsage colorOutputUsage; + RenderPassOutputUsage depthOutputUsage; +}; + +struct PassDataProcessor +{ + PassDataProcessor() = default; + PassDataProcessor(std::function inDrawCallback) + : drawCallback(inDrawCallback) + { + } + + PassDataProcessor( + std::function inPreDrawCallback, + std::function inDrawCallback, + std::function inPostDrawCallback) + : preDrawCallback(inPreDrawCallback) + , drawCallback(inDrawCallback) + , postDrawCallback(inPostDrawCallback) + { + } + + std::function preDrawCallback; + std::function drawCallback; + std::function postDrawCallback; +}; + +using PassRecordData = std::pair, PassDataProcessor>; + +struct TileMemoryHeapInfo +{ + VkDeviceMemory deviceMemoryBlob; + VkDeviceSize allocationSize; + VkDeviceSize usedSize; + uint32_t heapIndex; + uint32_t memoryType; + VkMemoryHeap heap; + + VkDeviceSize GetAvailableSize() const + { + return allocationSize - usedSize; + } +}; + +// ********************** +// Application +// ********************** +class Application : public ApplicationHelperBase +{ + struct ObjectMaterialParameters + { + UniformT objectFragUniform; + ObjectFragUB objectFragUniformData; + + std::size_t GetHash() const + { + auto hash_combine = [](std::size_t seed, const float& v) -> std::size_t + { + std::hash hasher; + seed ^= hasher(v) + 0x9e3228b9 + (seed << 6) + (seed >> 2); + return seed; + }; + + std::size_t result = 0; + result = hash_combine(result, objectFragUniformData.Color.x); + result = hash_combine(result, objectFragUniformData.Color.y); + result = hash_combine(result, objectFragUniformData.Color.z); + result = hash_combine(result, objectFragUniformData.Color.w); + result = hash_combine(result, objectFragUniformData.ORM.r); + result = hash_combine(result, objectFragUniformData.ORM.g); + result = hash_combine(result, objectFragUniformData.ORM.b); + result = hash_combine(result, objectFragUniformData.ORM.a); + + return result; + }; + }; + +public: + Application(); + ~Application() override; + + // ApplicationHelperBase + virtual void PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) override; + virtual bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; + virtual void Destroy() override; + virtual void Render(float fltDiffTime) override; + +private: + + // Application - Initialization + bool InitializeLights(); + bool InitializeCamera(); + bool LoadShaders(); + bool CreateRenderTargets(); + bool InitBuffers(); + bool InitGui(uintptr_t windowHandle); + bool LoadMeshObjects(); + bool CreateComputables(); + bool InitCommandBuffers(); + bool InitLocalSemaphores(); + bool SetupRenderContext(); + +private: + + void UpdateGui(); + bool UpdateUniforms(uint32_t WhichBuffer); + bool UpdateDescriptors(uint32_t WhichBuffer); + +private: + + // Passes + std::array m_RenderPassData; + std::array m_ComputePassData; + + std::array m_RenderTargets; + + std::array< CommandListVulkan, NUM_VULKAN_BUFFERS> m_SwapchainCopyCmdBuffer; + VkSemaphore m_SwapchhainCopySemaphore; + + std::array< CommandListVulkan, NUM_VULKAN_BUFFERS> m_CmdBuffer; + VkSemaphore m_Semaphore; + + // UBOs + UniformT m_ObjectVertUniform; + ObjectVertUB m_ObjectVertUniformData; + Uniform m_LightUniform; + std::array m_LightUniformData; + UniformT m_SceneInfoUniform; + SceneInfoUB m_SceneInfoUniformData; + + BufferVulkan m_LightIndicesBuffer; + BufferVulkan m_LightCountsBuffer; + + std::unordered_map m_ObjectFragUniforms; + + // Drawables + std::vector m_SceneDrawables; + std::unique_ptr m_DeferredLightQuadDrawable; + std::unique_ptr m_BlitQuadDrawable; + + // Shaders + std::unique_ptr m_ShaderManager; + + // Materials + std::unique_ptr m_MaterialManager; + + // Tile memory heap specific + bool m_IsTileMemoryHeapSupported = true; + bool m_RenderTargetUsesTileMemory = false; + // std::optional m_tileMemoryHeapInfo; + + // Tile memory extension + const TileMemoryHeapExtension* m_TileMemoryHeapExtension = nullptr; + MemoryPool m_TileMemoryPool; +}; diff --git a/samples/tile_memory/install_apk.bat b/samples/tile_memory/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/tile_memory/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/tile_memory/install_config.bat b/samples/tile_memory/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/tile_memory/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/BloomImageProcessing/project/android/AndroidManifest.xml b/samples/tile_memory/project/android/AndroidManifest.xml similarity index 94% rename from samples/BloomImageProcessing/project/android/AndroidManifest.xml rename to samples/tile_memory/project/android/AndroidManifest.xml index 5d91737..bbd0b27 100644 --- a/samples/BloomImageProcessing/project/android/AndroidManifest.xml +++ b/samples/tile_memory/project/android/AndroidManifest.xml @@ -1,13 +1,13 @@ + android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> - Empty + SGS Tile Memory diff --git a/samples/tile_memory/project/img/screenshot.png b/samples/tile_memory/project/img/screenshot.png new file mode 100644 index 0000000..d65670a Binary files /dev/null and b/samples/tile_memory/project/img/screenshot.png differ diff --git a/samples/hello-gltf/shaders/Blit.frag b/samples/tile_memory/shaders/Blit.frag similarity index 74% rename from samples/hello-gltf/shaders/Blit.frag rename to samples/tile_memory/shaders/Blit.frag index 839bc24..e3e694d 100644 --- a/samples/hello-gltf/shaders/Blit.frag +++ b/samples/tile_memory/shaders/Blit.frag @@ -15,8 +15,8 @@ #define SHADER_DIFFUSE_TEXTURE_LOC 0 #define SHADER_OVERLAY_TEXTURE_LOC 1 -layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; -layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_SceneTex; +layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; // Varying's layout (location = 0) in vec2 v_TexCoord; @@ -36,15 +36,15 @@ void main() // Texture Colors // ******************************** // Get base color from the color texture - vec4 DiffuseColor = texture( u_DiffuseTex, LocalTexCoord.xy ); + vec4 SceneColor = texture( u_SceneTex, LocalTexCoord.xy ); // Multiply by vertex color. - DiffuseColor.xyzw *= v_VertColor.xyzw; + SceneColor.xyzw *= v_VertColor.xyzw; // Apply darkening/lightening control // float lerp01 = min(1,FragCB.Diffuse); // float lerp12 = max(0,FragCB.Diffuse-1); - // DiffuseColor = DiffuseColor * lerp01 + lerp12 - lerp12 * DiffuseColor; + // SceneColor = SceneColor * lerp01 + lerp12 - lerp12 * SceneColor; // Get the Overlay value vec4 OverlayColor = texture( u_OverlayTex, LocalTexCoord.xy ); @@ -52,12 +52,12 @@ void main() // ******************************** // Alpha Blending // ******************************** - FragColor.rgb = DiffuseColor.rgb *(1.0-OverlayColor.a) + OverlayColor.rgb; + FragColor.rgb = SceneColor.rgb *(1.0-OverlayColor.a) + OverlayColor.rgb; FragColor.a = 1.0; // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! - // FragColor.xyz = (1.0 - OverlayColor.a) * DiffuseColor.xyz + OverlayColor.a * OverlayColor.xyz; - // FragColor = vec4(DiffuseColor.xyz, DiffuseColor.a); + // FragColor.xyz = (1.0 - OverlayColor.a) * SceneColor.xyz + OverlayColor.a * OverlayColor.xyz; + // FragColor = vec4(SceneColor.xyz, SceneColor.a); // FragColor = vec4(OverlayColor.xyz, OverlayColor.a); // FragColor.a = 1.0; // FragColor = vec4(0.8, 0.2, 0.8, 1.0); diff --git a/samples/hello-gltf/shaders/Blit.json b/samples/tile_memory/shaders/Blit.json similarity index 97% rename from samples/hello-gltf/shaders/Blit.json rename to samples/tile_memory/shaders/Blit.json index d7d32c6..60aa80a 100644 --- a/samples/hello-gltf/shaders/Blit.json +++ b/samples/tile_memory/shaders/Blit.json @@ -14,7 +14,7 @@ "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, - "Names": [ "Diffuse" ] + "Names": [ "Scene" ] }, { "Type": "ImageSampler", diff --git a/samples/hello-gltf/shaders/Blit.vert b/samples/tile_memory/shaders/Blit.vert similarity index 100% rename from samples/hello-gltf/shaders/Blit.vert rename to samples/tile_memory/shaders/Blit.vert diff --git a/samples/tile_memory/shaders/DeferredLight.frag b/samples/tile_memory/shaders/DeferredLight.frag new file mode 100644 index 0000000..a4a17fb --- /dev/null +++ b/samples/tile_memory/shaders/DeferredLight.frag @@ -0,0 +1,215 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 +#define MAX_TILE_MATRIX_DIMENSION_SIZE 8 +#define MAX_TILE_MATRIX_SIZE (MAX_TILE_MATRIX_DIMENSION_SIZE * MAX_TILE_MATRIX_DIMENSION_SIZE) + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +precision highp float; +precision highp int; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint clusterCountX; + uint clusterCountY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; +}; + +layout(set = 0, binding = 0) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = 1) uniform sampler2D u_NormalTex; +layout(set = 0, binding = 2) uniform highp sampler2D u_DepthTex; + +layout(std140, set = 0, binding = 3) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 4) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 5) buffer TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 6) buffer TileLightCounts +{ + uint lightCounts[]; +}; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec4 v_VertColor; + +// Finally, the output color +layout (location = 0) out vec4 FragColor; + + + +//----------------------------------------------------------------------------- +vec3 ApplyLight(LightUB light, highp vec3 fragWorldSpacePos, highp vec3 fragNormal) +//----------------------------------------------------------------------------- +{ + highp vec3 lightPosition = light.lightPosAndRadius.xyz; + highp float lightRadius = light.lightPosAndRadius.w; + + highp vec3 lightDir = normalize(lightPosition - fragWorldSpacePos); + highp float distance = length(lightPosition - fragWorldSpacePos); + + if (distance > lightRadius * 2.0) + { + return vec3(0.0); // Skip lights that do not affect this fragment + } + + // float attenuation = 1.0 / (distance * distance); + float attenuation = 1.0 / (distance * distance / (lightRadius * lightRadius)); + float intensity = max(dot(fragNormal, lightDir), 0.0) * light.lightColor.a; + + return light.lightColor.rgb * attenuation * intensity; +} + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + // + // NON-SHARED SECTION BEGIN + // + + uint globalClusterCountX = sceneInfo.clusterCountX; + uint globalClusterCountY = sceneInfo.clusterCountY; + + vec2 screenSize = vec2(sceneInfo.viewportWidth, sceneInfo.viewportHeight); + + float clusterSizeX = screenSize.x / float(globalClusterCountX); + float clusterSizeY = screenSize.y / float(globalClusterCountY); + + // + // NON-SHARED SECTION END + // + // (everything below this is common code between the 2 fragment shader versions) + // (with the exception of the texture sampling/subpassLoad functions below) + // + + uint currentClusterIndexX = uint(floor((v_TexCoord.x * screenSize.x) / clusterSizeX)); + uint currentClusterIndexY = uint(floor((v_TexCoord.y * screenSize.y) / clusterSizeY)); + + uint clusterIndex = currentClusterIndexY * globalClusterCountX + currentClusterIndexX; + + vec3 diffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ).xyz; + vec3 normal = texture( u_NormalTex, v_TexCoord.xy ).xyz; + float depth = texture( u_DepthTex, v_TexCoord.xy ).r * 2.0 - 1.0; + + highp vec4 clipSpacePos = vec4( + v_TexCoord.x * 2.0 - 1.0, + (1.0 - v_TexCoord.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + highp vec4 fragWorldSpacePos = sceneInfo.viewInv * fragViewSpacePos; + + uint lightCount = lightCounts[clusterIndex]; + highp vec3 accumulatedColor = vec3(0.0); + + if(sceneInfo.ignoreLightTiles != 0) + { + for (int i = 0; i < sceneInfo.lightCount; ++i) + { + LightUB light = lights[i]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + else + { + for (uint i = 0u; i < lightCount; ++i) + { + uint lightIndex = lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + i]; + LightUB light = lights[lightIndex]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + + FragColor = vec4(accumulatedColor * diffuseColor, 1.0); + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// + + if(sceneInfo.debugShaders != 0) + { + ivec2 clusterSize = ivec2(clusterSizeX, clusterSizeY); + + // Compute the local coordinate within the tile + ivec2 pixelCoord = ivec2(v_TexCoord.xy * screenSize); + ivec2 localCoord = pixelCoord % clusterSize; + + // Draw grid lines along tile boundaries using green + float lineThickness = 1.0; + bool drawGrid = (float(localCoord.x) < lineThickness || + float(localCoord.y) < lineThickness || + float(localCoord.x) > float(clusterSize.x) - lineThickness || + float(localCoord.y) > float(clusterSize.y) - lineThickness); + if(drawGrid) + { + FragColor = mix(FragColor, vec4(0.0, 0.7, 1.0, 1.0), 0.7); + } + + // Draw debug bar: within each tile, reserve a small horizontal region + // and subdivide it into MAX_LIGHT_COUNT segments. Fill segments (from left) up to tile_light_count. + float debugBarHeight = 6.0; // height in pixels of the debug bar + if(float(localCoord.y) < debugBarHeight) + { + float factor = float(lightCount) / float(sceneInfo.lightCount); + float segmentWidth = float(clusterSize.x) / float(sceneInfo.lightCount); + int segIndex = int(float(localCoord.x) / segmentWidth); + if(segIndex < int(lightCount)) + { + FragColor = mix(FragColor, vec4(factor, 1.0 - factor, 0.0, 1.0), 0.7); + } + } + } + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// +} \ No newline at end of file diff --git a/samples/tile_memory/shaders/DeferredLight.json b/samples/tile_memory/shaders/DeferredLight.json new file mode 100644 index 0000000..f691477 --- /dev/null +++ b/samples/tile_memory/shaders/DeferredLight.json @@ -0,0 +1,94 @@ +{ + "$schema": "../../../framework/schema/shaderSchema.json", + "Passes": [ + { + "Name": "RP_DEFERRED_LIGHT", + "Shaders": { + "Vertex": "Media/Shaders/DeferredLight.vert.spv", + "Fragment": "Media/Shaders/DeferredLight.frag.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneColor" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneNormal" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneDepth" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneInfo" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightCounts" ] + } + ] + } + ], + "VertexBindings": [ "VB0" ] + } + ], + "Vertex": [ + { + "Span": 60, + "Name": "VB0", + "Elements": [ + { + "Name": "Position", + "Offset": 0, + "Type": "Vec3" + }, + { + "Name": "Normal", + "Offset": 12, + "Type": "Vec3" + }, + { + "Name": "UV", + "Offset": 24, + "Type": "Vec2" + }, + { + "Name": "Color", + "Offset": 32, + "Type": "Vec4" + }, + { + "Name": "Tangent", + "Offset": 48, + "Type": "Vec3" + } + ] + } + ] +} diff --git a/samples/rayReflections/shaders/Blit.vert b/samples/tile_memory/shaders/DeferredLight.vert similarity index 100% rename from samples/rayReflections/shaders/Blit.vert rename to samples/tile_memory/shaders/DeferredLight.vert diff --git a/samples/tile_memory/shaders/LightCulling.comp b/samples/tile_memory/shaders/LightCulling.comp new file mode 100644 index 0000000..c2ba95d --- /dev/null +++ b/samples/tile_memory/shaders/LightCulling.comp @@ -0,0 +1,164 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 +#define MAX_TILE_MATRIX_DIMENSION_SIZE 8 +#define MAX_TILE_MATRIX_SIZE (MAX_TILE_MATRIX_DIMENSION_SIZE * MAX_TILE_MATRIX_DIMENSION_SIZE) + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint clusterCountX; + uint clusterCountY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; +}; + +layout(set = 0, binding = 0, rgba32f) uniform highp readonly image2D u_DepthTex; + +layout(std140, set = 0, binding = 1) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 2) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 3) buffer writeonly TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 4) buffer writeonly TileLightCounts +{ + uint lightCounts[]; +}; + +vec3 reconstructWorldPosition(highp vec2 uv, highp float depth) +{ + highp vec4 clipSpacePos = vec4( + uv.x * 2.0 - 1.0, + (1.0 - uv.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + return (sceneInfo.viewInv * fragViewSpacePos).xyz; +} + +void main() +{ + // + // NON-SHARED SECTION BEGIN + // + + // How many clusters exist on the screen + uint globalClusterCountX = sceneInfo.clusterCountX; + uint globalClusterCountY = sceneInfo.clusterCountY; + + // Individual cluster size + float clusterSizeX = float(sceneInfo.viewportWidth) / float(globalClusterCountX); + float clusterSizeY = float(sceneInfo.viewportHeight) / float(globalClusterCountY); + + // Current cluster being processed now + uint currentClusterIndexX = gl_GlobalInvocationID.x; + uint currentClusterIndexY = gl_GlobalInvocationID.y; + + // + // NON-SHARED SECTION END + // + // (everything below this is common code between the 2 compute shader versions) + // + + uint clusterIndex = currentClusterIndexY * globalClusterCountX + currentClusterIndexX; + + // Tile bounds in pixels + uint clusterMinX = uint(float(currentClusterIndexX) * clusterSizeX); + uint clusterMinY = uint(float(currentClusterIndexY) * clusterSizeY); + uint clusterMaxX = uint(float(clusterMinX) + clusterSizeX); + uint clusterMaxY = uint(float(clusterMinY) + clusterSizeY); + + // Early exit if fully out of bounds + if (clusterMinX >= sceneInfo.viewportWidth || clusterMinY >= sceneInfo.viewportHeight) + { + return; + } + + // Reconstruct tile frustum + highp vec3 clusterMin = vec3(1e8); + highp vec3 clusterMax = vec3(-1e8); + + for (uint y = clusterMinY; y < clusterMaxY; ++y) + { + for (uint x = clusterMinX; x < clusterMaxX; ++x) + { + vec2 uv = vec2(float(x) / float(sceneInfo.viewportWidth), + float(y) / float(sceneInfo.viewportHeight)); + if(uv.x > 1.0 || uv.y > 1.0) + { + continue; + } + + ivec2 pixelLocation = ivec2(uv * vec2(sceneInfo.viewportWidth, sceneInfo.viewportHeight)); + + highp float depth = imageLoad(u_DepthTex, pixelLocation).r * 2.0 - 1.0; + vec3 worldPos = reconstructWorldPosition(uv, depth); + + clusterMin = min(clusterMin, worldPos); + clusterMax = max(clusterMax, worldPos); + } + } + + uint tileLightCount = 0u; + for (int lightId = 0; lightId < int(sceneInfo.lightCount); ++lightId) + { + vec3 lightPosition = lights[lightId].lightPosAndRadius.xyz; + float lightRadius = lights[lightId].lightPosAndRadius.w; + + // Test light against tile bounds (AABB vs Sphere) + vec3 closestPoint = clamp(lightPosition, clusterMin, clusterMax); + float distance = length(closestPoint - lightPosition); + + if (distance < lightRadius * 2.0) + { + lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + tileLightCount] = uint(lightId); + tileLightCount++; + } + } + + lightCounts[clusterIndex] = tileLightCount; +} \ No newline at end of file diff --git a/samples/rayQueryShadows/shaders/ShadowRT.json b/samples/tile_memory/shaders/LightCulling.json similarity index 58% rename from samples/rayQueryShadows/shaders/ShadowRT.json rename to samples/tile_memory/shaders/LightCulling.json index 303cefd..c899c51 100644 --- a/samples/rayQueryShadows/shaders/ShadowRT.json +++ b/samples/tile_memory/shaders/LightCulling.json @@ -2,42 +2,42 @@ "$schema": "../../../framework/schema/shaderSchema.json", "Passes": [ { - "Name": "ShadowRayQuery", + "Name": "CP_LIGHT_CULLING_LIST", "Shaders": { - "Compute": "Media/Shaders/ShadowRT.comp.spv" + "Compute": "Media/Shaders/LightCulling.comp.spv" }, "DescriptorSets": [ { "Buffers": [ { - "Type": "UniformBuffer", + "Type": "ImageSampled", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "Uniform" ] + "Names": [ "SceneDepth" ] }, { - "Type": "ImageSampler", + "Type": "UniformBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "Depth" ] + "Names": [ "SceneInfo" ] }, { - "Type": "ImageSampler", + "Type": "UniformBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "Normal" ] + "Names": [ "LightInfo" ] }, { - "Type": "AccelerationStructure", + "Type": "StorageBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "AccelerationStructure" ] + "Names": [ "LightIndices" ] }, { - "Type": "ImageStorage", + "Type": "StorageBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "Output" ] + "Names": [ "LightCounts" ] } ] } diff --git a/samples/hello-gltf/shaders/Scene.vert b/samples/tile_memory/shaders/Scene.vert similarity index 100% rename from samples/hello-gltf/shaders/Scene.vert rename to samples/tile_memory/shaders/Scene.vert diff --git a/samples/tile_memory/shaders/SceneOpaque.frag b/samples/tile_memory/shaders/SceneOpaque.frag new file mode 100644 index 0000000..00c6f0e --- /dev/null +++ b/samples/tile_memory/shaders/SceneOpaque.frag @@ -0,0 +1,93 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +// Uniform buffer locations +#define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 + +// Texture Locations +#define SHADER_DIFFUSE_TEXTURE_LOC 2 +#define SHADER_NORMAL_TEXTURE_LOC 3 +#define SHADER_EMISSIVE_TEXTURE_LOC 4 +#define SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC 5 + +#define NUM_SPOT_LIGHTS (4) + +// Uniform Constant Buffer +layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff +{ + vec4 Color; + vec4 ORM; + +} FragCB; + +#ifndef PI +#define PI (3.14159265359) +#endif + +// Textures +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; +layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; +layout(set = 0, binding = SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC) uniform sampler2D u_MetallicRoughnessTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec3 v_WorldPos; +layout (location = 2) in vec3 v_WorldNorm; +layout (location = 3) in vec3 v_WorldTan; +layout (location = 4) in vec3 v_WorldBitan; +layout (location = 5) in vec4 v_ShadowCoord; +layout (location = 6) in vec4 v_VertColor; + +// Output color +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 NormalColor; + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 LocalTexCoord = vec2(v_TexCoord.xy); + + // ******************************** + // Base (albedo) color + // ******************************** + // Get color from the color texture + + vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); + DiffuseColor.xyzw *= FragCB.Color.xyzw; + + if(DiffuseColor.a < 0.5) + { + discard; + } + + // Adjust by vertex color. + DiffuseColor.xyzw *= v_VertColor.xyzw; + + vec4 Emissive = texture( u_EmissiveTex, v_TexCoord.xy ); + vec4 MetallicRoughness = texture( u_MetallicRoughnessTex, v_TexCoord.xy ); + + // Get base normal from the bump texture + vec3 Normal = texture( u_NormalTex, v_TexCoord.xy ).rgb; + Normal = Normal * 2.0 - 1.0; + + mat3 TBN = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); + Normal = normalize(TBN * Normal); + + NormalColor = vec4(Normal, 0.0); + FragColor.rgb = DiffuseColor.rgb; + FragColor.a = DiffuseColor.a; +} \ No newline at end of file diff --git a/samples/hello-gltf/shaders/SceneOpaque.json b/samples/tile_memory/shaders/SceneOpaque.json similarity index 94% rename from samples/hello-gltf/shaders/SceneOpaque.json rename to samples/tile_memory/shaders/SceneOpaque.json index b4f7683..12c3cb6 100644 --- a/samples/hello-gltf/shaders/SceneOpaque.json +++ b/samples/tile_memory/shaders/SceneOpaque.json @@ -20,11 +20,6 @@ "Stages": [ "Fragment" ], "Names": [ "Frag" ] }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Light" ] - }, { "Type": "ImageSampler", "Stages": [ "Fragment" ], @@ -60,6 +55,7 @@ "DepthCompareOp": "LessEqual" }, "Outputs": [ + { "BlendEnable": false }, { "BlendEnable": false } ] } diff --git a/samples/tile_memory/shaders/SceneTransparent.frag b/samples/tile_memory/shaders/SceneTransparent.frag new file mode 100644 index 0000000..b62e0fa --- /dev/null +++ b/samples/tile_memory/shaders/SceneTransparent.frag @@ -0,0 +1,93 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +// Uniform buffer locations +#define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 + +// Texture Locations +#define SHADER_DIFFUSE_TEXTURE_LOC 2 +#define SHADER_NORMAL_TEXTURE_LOC 3 +#define SHADER_EMISSIVE_TEXTURE_LOC 4 +#define SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC 5 + +#define NUM_SPOT_LIGHTS (4) + +// Uniform Constant Buffer +layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff +{ + vec4 Color; + vec4 ORM; + +} FragCB; + +#ifndef PI +#define PI (3.14159265359) +#endif + +// Textures +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; +layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; +layout(set = 0, binding = SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC) uniform sampler2D u_MetallicRoughnessTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec3 v_WorldPos; +layout (location = 2) in vec3 v_WorldNorm; +layout (location = 3) in vec3 v_WorldTan; +layout (location = 4) in vec3 v_WorldBitan; +layout (location = 5) in vec4 v_ShadowCoord; +layout (location = 6) in vec4 v_VertColor; + +// Output color +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 NormalColor; + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 LocalTexCoord = vec2(v_TexCoord.xy); + + // ******************************** + // Base (albedo) color + // ******************************** + // Get color from the color texture + + vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); + DiffuseColor.xyzw *= FragCB.Color.xyzw; + + if(DiffuseColor.a < 0.5) + { + discard; + } + + // Adjust by vertex color. + DiffuseColor.xyzw *= v_VertColor.xyzw; + + vec4 Emissive = texture( u_EmissiveTex, v_TexCoord.xy ); + vec4 MetallicRoughness = texture( u_MetallicRoughnessTex, v_TexCoord.xy ); + + // Get base normal from the bump texture + vec3 Normal = texture( u_NormalTex, v_TexCoord.xy ).rgb; + Normal = Normal * 2.0 - 1.0; + + mat3 TBN = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); + Normal = normalize(TBN * Normal); + + // NormalColor = vec4(Normal, 0.0); // Don't write normal on transparency pass + FragColor.rgb = DiffuseColor.rgb; + FragColor.a = DiffuseColor.a; +} \ No newline at end of file diff --git a/samples/hello-gltf/shaders/SceneTransparent.json b/samples/tile_memory/shaders/SceneTransparent.json similarity index 94% rename from samples/hello-gltf/shaders/SceneTransparent.json rename to samples/tile_memory/shaders/SceneTransparent.json index 39169fb..c4b6dd1 100644 --- a/samples/hello-gltf/shaders/SceneTransparent.json +++ b/samples/tile_memory/shaders/SceneTransparent.json @@ -20,11 +20,6 @@ "Stages": [ "Fragment" ], "Names": [ "Frag" ] }, - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Names": [ "Light" ] - }, { "Type": "ImageSampler", "Stages": [ "Fragment" ], @@ -59,6 +54,7 @@ "DepthWriteEnable": false }, "Outputs": [ + { "BlendEnable": true }, { "BlendEnable": true } ] } diff --git a/samples/tile_shading/CMakeLists.txt b/samples/tile_shading/CMakeLists.txt new file mode 100644 index 0000000..5c883ea --- /dev/null +++ b/samples/tile_shading/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required (VERSION 3.21) + +project (tile_shading C CXX) +set(CMAKE_CXX_STANDARD 20) + +# +# Source files included in this application. +# + +set(CPP_SRC code/main/application.cpp + code/main/application.hpp +) + +# +# Setup the module path to include the 'project directory' (project/windows or project/android) +# +if(NOT DEFINED PROJECT_ROOT_DIR) + set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) + +# +# Do all the build steps for a Framework application. +# needs Framework_dir and project_name variables. +# +include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + +# +# Copy required models to local folders +# +include(ModelPackager) + +# Scene GLTF +add_gltf(scenes/SteamPunkSauna/SteamPunkSauna.gltf) + +# +# Convert and copy textures to local folders +# +include(TexturePackager) + +# Scene Textures +add_textures_from_path(scenes/SteamPunkSauna UASTC) + +# Supporting Textures +add_textures_from_path(textures) \ No newline at end of file diff --git a/samples/tile_shading/README.md b/samples/tile_shading/README.md new file mode 100644 index 0000000..de86360 --- /dev/null +++ b/samples/tile_shading/README.md @@ -0,0 +1,37 @@ +/* +============================================================================================================ +* TILE SHADING SAMPLE +============================================================================================================ +* +* This sample demonstrates a light clustering algorithm using Vulkan, with specific support for the +* VK_QCOM_tile_shading extension. This extension allows the application to leverage tile-based deferred rendering +* (TBDR) for efficient memory management and rendering performance. +* +* Reference points: +* +* 1. PreInitializeSetVulkanConfiguration +*    - Sets up the Vulkan configuration, including required and optional extensions. +*    - config.OptionalExtension(); +*    - The application checks for the VK_QCOM_tile_shading extension and sets up the Vulkan configuration accordingly. +*    - If supported, tile shading is enabled at startup. +* +* 2. Initialize +*    - Initializes various components of the application, including camera, lights, shaders, buffers, render targets, GUI, and mesh objects. +*    - m_IsTileShadingSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_QCOM_TILE_SHADING_EXTENSION_NAME); +*    - m_IsTileShadingActive = m_IsTileShadingSupported; +* +* 3. CreateComputables +*    - Creates computable objects for light culling, including tile shading variants. +*    - m_TileShadingPassData.passComputable->DispatchPass(...); +* +* 4. Render +*    - The main rendering function binds the tile shading extension to the command buffer if tile shading is enabled. +*    - The rendering process includes multiple passes: +*      - Scene Pass: Outputs albedo, normal, and depth. +*      - Light Culling Pass: Divides point lights into clusters and performs light culling. +*      - Deferred Light Pass: Applies the corresponding pixel light cluster to a screen quad. +*    - vkCmdBeginPerTileExecutionQCOM and vkCmdEndPerTileExecutionQCOM are used for the compute dispatch. +* +* SPEC: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_shading.html +============================================================================================================ +*/ \ No newline at end of file diff --git a/samples/tile_shading/code/main/application.cpp b/samples/tile_shading/code/main/application.cpp new file mode 100644 index 0000000..d02c336 --- /dev/null +++ b/samples/tile_shading/code/main/application.cpp @@ -0,0 +1,2167 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#include "application.hpp" +#include "main/applicationEntrypoint.hpp" +#include "camera/cameraController.hpp" +#include "camera/cameraControllerTouch.hpp" +#include "camera/cameraData.hpp" +#include "camera/cameraGltfLoader.hpp" +#include "gui/imguiVulkan.hpp" +#include "material/drawable.hpp" +#include "material/vulkan/shaderModule.hpp" +#include "material/computable.hpp" +#include "material/materialManager.hpp" +#include "material/shaderManagerT.hpp" +#include "material/vulkan/specializationConstantsLayout.hpp" +#include "mesh/meshHelper.hpp" +#include "mesh/meshLoader.hpp" +#include "system/math_common.hpp" +#include "texture/textureManager.hpp" +#include "imgui.h" +#include "vulkan/extensionHelpers.hpp" +#include "material/drawableLoader.hpp" +#include "mesh/mesh.hpp" +#include "vulkan/extensionLib.hpp" +#include "material/vulkan/computable.hpp" +#include "material/vulkan/drawable.hpp" + +#include +#include +#include + +/* +============================================================================================================ +* TILE SHADING SAMPLE +============================================================================================================ +* +* This sample demonstrates a light clustering algorithm using Vulkan, with specific support for the +* VK_QCOM_tile_shading extension. This extension allows the application to leverage tile-based deferred rendering +* (TBDR) for efficient memory management and rendering performance. +* +* Reference points: +* +* 1. PreInitializeSetVulkanConfiguration +*    - Sets up the Vulkan configuration, including required and optional extensions. +*    - config.OptionalExtension(); +*    - The application checks for the VK_QCOM_tile_shading extension and sets up the Vulkan configuration accordingly. +*    - If supported, tile shading is enabled at startup. +* +* 2. Initialize +*    - Initializes various components of the application, including camera, lights, shaders, buffers, render targets, GUI, and mesh objects. +*    - m_IsTileShadingSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_QCOM_TILE_SHADING_EXTENSION_NAME); +*    - m_IsTileShadingActive = m_IsTileShadingSupported; +* +* 3. CreateComputables +*    - Creates computable objects for light culling, including tile shading variants. +*    - m_TileShadingPassData.passComputable->DispatchPass(...); +* +* 4. Render +*    - The main rendering function binds the tile shading extension to the command buffer if tile shading is enabled. +*    - The rendering process includes multiple passes: +*      - Scene Pass: Outputs albedo, normal, and depth. +*      - Light Culling Pass: Divides point lights into clusters and performs light culling. +*      - Deferred Light Pass: Applies the corresponding pixel light cluster to a screen quad. +*    - vkCmdBeginPerTileExecutionQCOM and vkCmdEndPerTileExecutionQCOM are used for the compute dispatch. +* +* SPEC: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_shading.html +============================================================================================================ +*/ + +namespace +{ + static constexpr std::array sRenderPassNames = { "RP_SCENE", "RP_DEFERRED_LIGHT", "RP_HUD", "RP_BLIT" }; + + glm::vec3 gCameraStartPos = glm::vec3(0.0f, 3.5f, 0.0f); + glm::vec3 gCameraStartRot = glm::vec3(0.0f, 0.0f, 0.0f); + + float gFOV = PI_DIV_4; + float gNearPlane = 1.0f; + float gFarPlane = 1800.0f; + float gNormalAmount = 0.3f; + float gNormalMirrorReflectAmount = 0.05f; + + const char* gSceneAssetModel = "SteamPunkSauna.gltf"; +} + +/// +/// @brief Implementation of the Application entrypoint (called by the framework) +/// @return Pointer to Application (derived from @FrameworkApplicationBase). +/// Creates the Application class. Ownership is passed to the calling (framework) function. +/// +FrameworkApplicationBase* Application_ConstructApplication() +{ + return new Application(); +} + +Application::Application() : ApplicationHelperBase() +{ +} + +Application::~Application() +{ +} + +//----------------------------------------------------------------------------- +void Application::PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) +//----------------------------------------------------------------------------- +{ + ApplicationHelperBase::PreInitializeSetVulkanConfiguration(config); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.RequiredExtension(); + config.OptionalExtension(); + config.OptionalExtension(); +} + +//----------------------------------------------------------------------------- +bool Application::Initialize(uintptr_t windowHandle, uintptr_t hInstance) +//----------------------------------------------------------------------------- +{ + if (!ApplicationHelperBase::Initialize( windowHandle, hInstance )) + { + return false; + } + + m_IsTilePropertiesSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME); + m_IsTileShadingSupported &= GetVulkan()->HasLoadedVulkanDeviceExtension(VK_QCOM_TILE_SHADING_EXTENSION_NAME); + + // Begin with tile shading active (so we can gather all the necessary tile info at startup), if supported + m_IsTileShadingActive = m_IsTileShadingSupported; + + if (!InitializeCamera()) + { + return false; + } + + if (!InitializeLights()) + { + return false; + } + + if (!LoadShaders()) + { + return false; + } + + if (!InitBuffers()) + { + return false; + } + + if (!CreateRenderTargets()) + { + return false; + } + + if (!SetupRenderContext()) + { + return false; + } + + if (!InitGui(windowHandle)) + { + return false; + } + + if (!LoadMeshObjects()) + { + return false; + } + + if (!CreateComputables()) + { + return false; + } + + if (!InitCommandBuffers()) + { + return false; + } + + if (!InitLocalSemaphores()) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void Application::Destroy() +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Uniform Buffers + ReleaseUniformBuffer(pVulkan, &m_ObjectVertUniform); + ReleaseUniformBuffer(pVulkan, &m_LightUniform); + + for (auto& [hash, objectUniform] : m_ObjectFragUniforms) + { + ReleaseUniformBuffer(pVulkan, &objectUniform.objectFragUniform); + } + + // Cmd buffers + for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + for (auto& cmdBuffer : m_RenderPassData[whichPass].passCmdBuffer) + { + cmdBuffer.Release(); + } + + for (auto& cmdBuffer : m_TileShadingPassData.passCmdBuffer) + { + cmdBuffer.Release(); + } + + m_DefaultRenderTargets[whichPass].Release(); + } + m_TileShadingSceneRenderTarget.Release(); + + for(auto& swapchainCopyCmdBuffer : m_SwapchainCopyCmdBuffer) + { + swapchainCopyCmdBuffer.Release(); + } + + // Render passes / Semaphores + for (int whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + vkDestroySemaphore(pVulkan->m_VulkanDevice, m_RenderPassData[whichPass].passSemaphore, nullptr); + } + vkDestroySemaphore(pVulkan->m_VulkanDevice, m_TileShadingPassData.passSemaphore, nullptr); + + // Drawables + m_SceneDrawables.clear(); + m_BlitQuadDrawable.reset(); + + // Internal + m_ShaderManager.reset(); + m_MaterialManager.reset(); + m_CameraController.reset(); + m_AssetManager.reset(); + + ApplicationHelperBase::Destroy(); +} + +//----------------------------------------------------------------------------- +bool Application::InitializeLights() +//----------------------------------------------------------------------------- +{ + // NOTE: Hardcoded values for the Museum asset + const float sceneRadius = 67.0f; + const float sceneMinHeight = 3.0f; + const float sceneMaxHeight = 25.0f; + + const glm::vec2 lightScaleRange = glm::vec2(3.5f, 5.0f); + + std::random_device rd; + std::mt19937 gen(rd()); + + // Distributions for position, scale, and color + std::uniform_real_distribution sceneRadiusDist(-sceneRadius, sceneRadius); + std::uniform_real_distribution sceneHeightDist(sceneMinHeight, sceneMaxHeight); + std::uniform_real_distribution lightRadiusDist(lightScaleRange.x, lightScaleRange.y); + std::uniform_real_distribution lightColorDist(0.0f, 1.0f); + + for(int i=0; i< m_LightUniformData.size(); i++) + { + auto& lightData = m_LightUniformData[i]; + const float lightIndexFactor = static_cast(i) / static_cast(m_LightUniformData.size()) * 0.8f + 0.2f; + + const glm::vec2 direction = glm::normalize(glm::vec2(sceneRadiusDist(gen), sceneRadiusDist(gen))); + const glm::vec2 planePosition = direction * sceneRadiusDist(gen) * lightIndexFactor; + const glm::vec3 lightPosition = glm::vec3(planePosition.x, sceneHeightDist(gen), planePosition.y); + + lightData.lightPosAndRadius = glm::vec4(lightPosition, lightRadiusDist(gen)); + lightData.lightColor = glm::vec4(lightColorDist(gen), lightColorDist(gen), lightColorDist(gen), 1.0f); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitializeCamera() +//----------------------------------------------------------------------------- +{ + LOGI("******************************"); + LOGI("Initializing Camera..."); + LOGI("******************************"); + + m_Camera.SetPosition(gCameraStartPos, glm::quat(gCameraStartRot * TO_RADIANS)); + m_Camera.SetAspect(float(gRenderWidth) / float(gRenderHeight)); + m_Camera.SetFov(gFOV); + m_Camera.SetClipPlanes(gNearPlane, gFarPlane); + + // Camera Controller // + +#if defined(OS_ANDROID) + typedef CameraControllerTouch tCameraController; +#else + typedef CameraController tCameraController; +#endif + + auto cameraController = std::make_unique(); + if (!cameraController->Initialize(gRenderWidth, gRenderHeight)) + { + return false; + } + + m_CameraController = std::move(cameraController); + m_CameraController->SetMoveSpeed(10.0f); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::LoadShaders() +//----------------------------------------------------------------------------- +{ + m_ShaderManager = std::make_unique(*GetVulkan()); + m_ShaderManager->RegisterRenderPassNames(sRenderPassNames); + + m_MaterialManager = std::make_unique(*GetVulkan()); + + LOGI("******************************"); + LOGI("Loading Shaders..."); + LOGI("******************************"); + + auto LoadShader = [&](const std::string& id, const std::string& filename) -> bool + { + if (!m_ShaderManager->AddShader(*m_AssetManager, id, filename, SHADER_DESTINATION_PATH)) + { + LOGE("Error Loading shader %s from %s", id.c_str(), filename.c_str()); + LOGI("Please verify if you have all required assets on the sample media folder"); + LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); + return false; + } + + return true; + }; + + typedef std::pair tIdAndFilename; + for (const tIdAndFilename& i : + { tIdAndFilename { "Blit", "Blit.json" }, + tIdAndFilename { "SceneOpaque", "SceneOpaque.json" }, + tIdAndFilename { "SceneTransparent", "SceneTransparent.json" }, + tIdAndFilename { "LightCulling", "LightCulling.json" }, + tIdAndFilename { "DeferredLight", "DeferredLight.json" } + }) + { + if (!LoadShader(i.first, i.second)) + { + return false; + } + } + + if (m_IsTileShadingSupported) + { + if (!LoadShader("LightCullingTileShading", "LightCullingTileShading.json") + || !LoadShader("LightCullingTileShadingAreaDispatch", "LightCullingTileShadingAreaDispatch.json") + || !LoadShader("DeferredLightTileShading", "DeferredLightTileShading.json")) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::CreateRenderTargets() +//----------------------------------------------------------------------------- +{ + auto pVulkan = GetVulkan(); + + LOGI("**************************"); + LOGI("Creating Render Targets..."); + LOGI("**************************"); + + TextureFormat vkDesiredDepthFormat = pVulkan->GetBestSurfaceDepthFormat(); + TextureFormat desiredDepthFormat = vkDesiredDepthFormat; + + const TextureFormat SceneColorType[] = { TextureFormat::R8G8B8A8_SRGB, TextureFormat::R8G8B8A8_SRGB }; + const TextureFormat DeferredLightColorType[] = { TextureFormat::R8G8B8A8_SRGB}; + const TextureFormat HudColorType[] = { TextureFormat::R8G8B8A8_SRGB }; + const TextureFormat FinalColorType[] = { TextureFormat::R8G8B8A8_SRGB }; + const TextureFormat TileShadingSceneColorType[] = + { + TextureFormat::R8G8B8A8_SRGB, // Diffuse + TextureFormat::R8G8B8A8_SRGB, // Normal + TextureFormat::R8G8B8A8_SRGB, // Scene + }; + + if (!m_DefaultRenderTargets[RP_SCENE].Initialize( + pVulkan, + RenderTargetInitializeInfo + { + gRenderWidth, + gRenderHeight, + SceneColorType, + desiredDepthFormat, + {}, // std::span + }, + "Scene RT")) + { + LOGE("Unable to create scene render target"); + return false; + } + + if (!m_DefaultRenderTargets[RP_DEFERRED_LIGHT].Initialize( + pVulkan, + RenderTargetInitializeInfo + { + gRenderWidth, + gRenderHeight, + DeferredLightColorType, + TextureFormat::UNDEFINED, + {}, // std::span + }, + "Particles RT")) + { + LOGE("Unable to create the deferred light render target"); + return false; + } + + if (!m_DefaultRenderTargets[RP_HUD].Initialize( + pVulkan, + RenderTargetInitializeInfo + { + gSurfaceWidth, + gSurfaceHeight, + HudColorType, + TextureFormat::UNDEFINED, + {}, // std::span + }, + "HUD RT")) + { + LOGE("Unable to create hud render target"); + return false; + } + + std::array blitColorTypes; + blitColorTypes[0] = TT_RENDER_TARGET_TRANSFERSRC; + + if (!m_DefaultRenderTargets[RP_BLIT].Initialize( + pVulkan, + RenderTargetInitializeInfo + { + gSurfaceWidth, + gSurfaceHeight, + FinalColorType, + TextureFormat::UNDEFINED, + blitColorTypes, + }, + "BLIT RT")) + { + LOGE("Unable to create blit render target"); + return false; + } + + if (m_IsTileShadingSupported) + { + std::array tileShadingSceneColorTypes{ TT_RENDER_TARGET_LOCAL_READ_TRANSIENT , TT_RENDER_TARGET_LOCAL_READ_TRANSIENT , TT_RENDER_TARGET }; + if (!m_TileShadingSceneRenderTarget.Initialize( + pVulkan, + RenderTargetInitializeInfo + { + gRenderWidth, + gRenderHeight, + TileShadingSceneColorType, + desiredDepthFormat, + tileShadingSceneColorTypes, + TT_DEPTH_TARGET_LOCAL_READ, + }, + "Tile Shading Scene RT")) + { + LOGE("Unable to create tile shading scene render target"); + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::SetupRenderContext() +//----------------------------------------------------------------------------- +{ + auto& vulkan = *GetVulkan(); + + LOGI("******************************"); + LOGI("Initializing Render Passes... "); + LOGI("******************************"); + + for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + std::vector< TextureFormat> colorFormats; + for (auto& colorAttachment : m_DefaultRenderTargets[whichPass].GetColorAttachments()) + { + colorFormats.push_back(colorAttachment.Format); + } + + m_RenderPassData[whichPass].renderContext = + { + colorFormats, + m_DefaultRenderTargets[whichPass].GetDepthFormat(), + TextureFormat::UNDEFINED, + sRenderPassNames[whichPass] + }; + } + + { + std::vector< TextureFormat> colorFormats; + for (auto& colorAttachment : m_TileShadingSceneRenderTarget.GetColorAttachments()) + { + colorFormats.push_back(colorAttachment.Format); + } + + m_TileShadingPassData.renderContext = + { + colorFormats, + m_TileShadingSceneRenderTarget.GetDepthFormat(), + TextureFormat::UNDEFINED, + "RP_SCENE" + }; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitBuffers() +//----------------------------------------------------------------------------- +{ + LOGI("******************************"); + LOGI("Initializing Buffers..."); + LOGI("******************************"); + + Vulkan* const pVulkan = GetVulkan(); + + if (!CreateUniformBuffer(pVulkan, m_ObjectVertUniform)) + { + return false; + } + + if (!CreateUniformBuffer(pVulkan, &m_LightUniform, sizeof(LightUB) * m_LightUniformData.size(), m_LightUniformData.data())) + { + return false; + } + + if (!CreateUniformBuffer(pVulkan, m_SceneInfoUniform)) + { + return false; + } + + // Worst case = every light is present on every tile + // NOTE: int32_t can totally be swapped by a smaller type + const size_t lightIndicesBufferSize = sizeof(int32_t) * MAX_CLUSTER_MATRIX_SIZE * MAX_LIGHT_COUNT; + + if (!m_LightCountsBuffer.Initialize( + &pVulkan->GetMemoryManager(), + sizeof(int32_t) * MAX_CLUSTER_MATRIX_SIZE, + BufferUsageFlags::Storage, + MemoryUsage::GpuExclusive)) + { + return false; + } + + if (!m_LightIndicesBuffer.Initialize( + &pVulkan->GetMemoryManager(), + lightIndicesBufferSize, + BufferUsageFlags::Storage, + MemoryUsage::GpuExclusive)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitGui(uintptr_t windowHandle) +//----------------------------------------------------------------------------- +{ + const auto& hudRenderTarget = m_DefaultRenderTargets[RP_HUD]; + m_Gui = std::make_unique(*GetVulkan()); + if (!m_Gui->Initialize( + windowHandle, + hudRenderTarget.m_ColorAttachments[0].Format, + hudRenderTarget.m_ColorAttachments[0].Width, + hudRenderTarget.m_ColorAttachments[0].Height)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::LoadMeshObjects() +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + LOGI("***********************"); + LOGI("Initializing Meshes... "); + LOGI("***********************"); + + const auto* pSceneOpaqueShader = m_ShaderManager->GetShader("SceneOpaque"); + const auto* pSceneTransparentShader = m_ShaderManager->GetShader("SceneTransparent"); + const auto* deferredLightShader = m_ShaderManager->GetShader("DeferredLight"); + const auto* deferredLightTileShadingShader = m_ShaderManager->GetShader("DeferredLightTileShading"); + const auto* pBlitQuadShader = m_ShaderManager->GetShader("Blit"); + if (!pSceneOpaqueShader || !pSceneTransparentShader || !pBlitQuadShader || !deferredLightShader || (m_IsTileShadingSupported && !deferredLightTileShadingShader)) + { + return false; + } + + LOGI("***********************************"); + LOGI("Loading and preparing the museum..."); + LOGI("***********************************"); + + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + auto* whiteTexture = m_TextureManager->GetOrLoadTexture("white_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* blackTexture = m_TextureManager->GetOrLoadTexture("black_d.ktx", m_SamplerRepeat, prefixTextureDir); + auto* normalDefaultTexture = m_TextureManager->GetOrLoadTexture("normal_default.ktx", m_SamplerRepeat, prefixTextureDir); + + if (!whiteTexture || !blackTexture || !normalDefaultTexture) + { + LOGE("Failed to load supporting textures"); + return false; + } + + auto UniformBufferLoader = [&](const ObjectMaterialParameters& objectMaterialParameters) -> ObjectMaterialParameters& + { + auto hash = objectMaterialParameters.GetHash(); + + auto iter = m_ObjectFragUniforms.try_emplace(hash, ObjectMaterialParameters()); + if (iter.second) + { + iter.first->second.objectFragUniformData = objectMaterialParameters.objectFragUniformData; + if (!CreateUniformBuffer(pVulkan, iter.first->second.objectFragUniform)) + { + LOGE("Failed to create object uniform buffer"); + } + } + + return iter.first->second; + }; + + auto MaterialLoader = [&](const MeshObjectIntermediate::MaterialDef& materialDef)->std::optional + { + const PathManipulator_PrefixDirectory prefixTextureDir{ TEXTURE_DESTINATION_PATH }; + const PathManipulator_ChangeExtension changeTextureExt{ ".ktx" }; + + auto* diffuseTexture = m_TextureManager->GetOrLoadTexture(materialDef.diffuseFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* normalTexture = m_TextureManager->GetOrLoadTexture(materialDef.bumpFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* emissiveTexture = m_TextureManager->GetOrLoadTexture(materialDef.emissiveFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + auto* metallicRoughnessTexture = m_TextureManager->GetOrLoadTexture(materialDef.specMapFilename, m_SamplerRepeat, prefixTextureDir, changeTextureExt); + bool alphaCutout = materialDef.alphaCutout; + bool transparent = materialDef.transparent; + + const auto* targetShader = transparent ? pSceneTransparentShader : pSceneOpaqueShader; + + ObjectMaterialParameters objectMaterial; + objectMaterial.objectFragUniformData.Color.r = static_cast(materialDef.baseColorFactor[0]); + objectMaterial.objectFragUniformData.Color.g = static_cast(materialDef.baseColorFactor[1]); + objectMaterial.objectFragUniformData.Color.b = static_cast(materialDef.baseColorFactor[2]); + objectMaterial.objectFragUniformData.Color.a = static_cast(materialDef.baseColorFactor[3]); + objectMaterial.objectFragUniformData.ORM.b = static_cast(materialDef.metallicFactor); + objectMaterial.objectFragUniformData.ORM.g = static_cast(materialDef.roughnessFactor); + + if (diffuseTexture == nullptr || normalTexture == nullptr) + { + return std::nullopt; + } + + auto shaderMaterial = m_MaterialManager->CreateMaterial(*targetShader, NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "Diffuse") + { + return { diffuseTexture ? diffuseTexture : whiteTexture }; + } + if (texName == "Normal") + { + return { normalTexture ? normalTexture : normalDefaultTexture }; + } + if (texName == "Emissive") + { + return { emissiveTexture ? emissiveTexture : blackTexture }; + } + if (texName == "MetallicRoughness") + { + return { metallicRoughnessTexture ? metallicRoughnessTexture : blackTexture }; + } + + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "Vert") + { + return { m_ObjectVertUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "Frag") + { + return { UniformBufferLoader(objectMaterial).objectFragUniform.buf.GetVkBuffer() }; + } + + return {}; + } + ); + + return shaderMaterial; + }; + + + const auto loaderFlags = 0; // No instancing + const bool ignoreTransforms = (loaderFlags & DrawableLoader::LoaderFlags::IgnoreHierarchy) != 0; + + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + MeshLoaderModelSceneSanityCheck meshSanityCheckProcessor(sceneAssetPath); + MeshObjectIntermediateGltfProcessor meshObjectProcessor(sceneAssetPath, ignoreTransforms, glm::vec3(1.0f, 1.0f, 1.0f)); + CameraGltfProcessor meshCameraProcessor{}; + + if (!MeshLoader::LoadGltf(*m_AssetManager, sceneAssetPath, meshSanityCheckProcessor, meshObjectProcessor, meshCameraProcessor) || + !DrawableLoader::CreateDrawables(*pVulkan, + std::move(meshObjectProcessor.m_meshObjects), + m_RenderPassData[RP_SCENE].renderContext, + MaterialLoader, + m_SceneDrawables, + loaderFlags)) + { + LOGE("Error Loading the museum gltf file"); + LOGI("Please verify if you have all required assets on the sample media folder"); + LOGI("If you are running on Android, don't forget to run the `02_CopyMediaToDevice.bat` script to copy all media files into the device memory"); + return false; + } + + if (!meshCameraProcessor.m_cameras.empty()) + { + const auto& camera = meshCameraProcessor.m_cameras[0]; + m_Camera.SetPosition(camera.Position, camera.Orientation); + } + + LOGI("*********************"); + LOGI("Creating Quad mesh..."); + LOGI("*********************"); + { + Mesh blitQuadMesh; + MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); + + // Blit Material + auto blitQuadShaderMaterial = m_MaterialManager->CreateMaterial(*pBlitQuadShader, pVulkan->m_SwapchainImageCount, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "Scene") + { + return { &m_DefaultRenderTargets[RP_DEFERRED_LIGHT].GetColorAttachments()[0] }; + } + else if (texName == "Overlay") + { + return { &m_DefaultRenderTargets[RP_HUD].GetColorAttachments()[0] }; + } + return {}; + }, + [this](const std::string& bufferName) -> PerFrameBufferVulkan + { + return {}; + } + ); + + m_BlitQuadDrawable = std::make_unique(*pVulkan, std::move(blitQuadShaderMaterial)); + if (!m_BlitQuadDrawable->Init(m_RenderPassData[RP_BLIT].renderContext, std::move(blitQuadMesh))) + { + return false; + } + } + + LOGI("************************************"); + LOGI("Creating Deferred Light Quad mesh..."); + LOGI("************************************"); + { + Mesh blitQuadMesh; + MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &blitQuadMesh); + + auto deferredLightQuadShaderMaterial = m_MaterialManager->CreateMaterial(*deferredLightShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneColor") + { + return { &m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[0] }; + } + else if (texName == "SceneNormal") + { + return { &m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[1] }; + } + else if (texName == "SceneDepth") + { + return { &m_DefaultRenderTargets[RP_SCENE].GetDepthAttachment() }; + } + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + } + ); + + m_DeferredLightQuadDrawable = std::make_unique(*pVulkan, std::move(deferredLightQuadShaderMaterial)); + if (!m_DeferredLightQuadDrawable->Init(m_RenderPassData[RP_DEFERRED_LIGHT].renderContext, std::move(blitQuadMesh))) + { + return false; + } + + if (m_IsTileShadingSupported) + { + auto deferredLightTileShadingQuadShaderMaterial = m_MaterialManager->CreateMaterial(*deferredLightTileShadingShader, NUM_VULKAN_BUFFERS, + [this](const std::string& texName) -> const MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneColor") + { + return { &m_TileShadingSceneRenderTarget.GetColorAttachments()[0] }; + } + else if (texName == "SceneNormal") + { + return { &m_TileShadingSceneRenderTarget.GetColorAttachments()[1] }; + } + else if (texName == "SceneDepth") + { + return { &m_TileShadingSceneRenderTarget.GetDepthAttachment() }; + } + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + } + ); + + Mesh tileShadingBlitQuadMesh; + MeshHelper::CreateScreenSpaceMesh(pVulkan->GetMemoryManager(), 0, &tileShadingBlitQuadMesh); + + m_DeferredLightTileShadingQuadDrawable = std::make_unique(*pVulkan, std::move(deferredLightTileShadingQuadShaderMaterial)); + if (!m_DeferredLightTileShadingQuadDrawable->Init(m_TileShadingPassData.renderContext, std::move(tileShadingBlitQuadMesh))) + { + return false; + } + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::CreateComputables() +//----------------------------------------------------------------------------- +{ + auto pVulkan = GetVulkan(); + + const auto lightCullingShader = m_ShaderManager->GetShader("LightCulling"); + const auto lightCullingTileShadingShader = m_ShaderManager->GetShader("LightCullingTileShading"); + const auto lightCullingTileShadingShaderAreaDispatch = m_ShaderManager->GetShader("LightCullingTileShadingAreaDispatch"); + if (!lightCullingShader || (m_IsTileShadingSupported && (!lightCullingTileShadingShader || !lightCullingTileShadingShaderAreaDispatch))) + { + return false; + } + + auto material = m_MaterialManager->CreateMaterial( + *lightCullingShader, + NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneDepth") + { + return { &m_DefaultRenderTargets[RP_SCENE].GetDepthAttachment() }; + } + assert(0); + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + }); + + m_ComputePassData[CP_LIGHT_CULLING_LIST].passComputable = std::make_unique(*pVulkan, std::move(material)); + if (!m_ComputePassData[CP_LIGHT_CULLING_LIST].passComputable->Init()) + { + LOGE("Error Creating SGSR computables..."); + } + + if (m_IsTileShadingSupported) + { + auto materialTileShading = m_MaterialManager->CreateMaterial( + *lightCullingTileShadingShader, + NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneDepth") + { + return { &m_TileShadingSceneRenderTarget.GetDepthAttachment() }; + } + assert(0); + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + }); + + m_TileShadingPassData.passComputable = std::make_unique(*pVulkan, std::move(materialTileShading)); + if (!m_TileShadingPassData.passComputable->Init()) + { + LOGE("Error Creating SGSR computables..."); + } + + auto materialTileShadingAreaDispatch = m_MaterialManager->CreateMaterial( + *lightCullingTileShadingShaderAreaDispatch, + NUM_VULKAN_BUFFERS, + [&](const std::string& texName) -> MaterialManager::tPerFrameTexInfo + { + if (texName == "SceneDepth") + { + return { &m_TileShadingSceneRenderTarget.GetDepthAttachment() }; + } + assert(0); + return {}; + }, + [&](const std::string& bufferName) -> PerFrameBufferVulkan + { + if (bufferName == "SceneInfo") + { + return { m_SceneInfoUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightInfo") + { + return { m_LightUniform.buf.GetVkBuffer() }; + } + else if (bufferName == "LightIndices") + { + return { m_LightIndicesBuffer.GetVkBuffer() }; + } + else if (bufferName == "LightCounts") + { + return { m_LightCountsBuffer.GetVkBuffer() }; + } + assert(0); + return {}; + }); + + m_TileShadingPassData.passComputableAreaDispatch = std::make_unique(*pVulkan, std::move(materialTileShadingAreaDispatch)); + if (!m_TileShadingPassData.passComputableAreaDispatch->Init()) + { + LOGE("Error Creating SGSR computables..."); + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitCommandBuffers() +//----------------------------------------------------------------------------- +{ + LOGI("*******************************"); + LOGI("Initializing Command Buffers..."); + LOGI("*******************************"); + + Vulkan* const pVulkan = GetVulkan(); + + auto GetPassName = [](uint32_t whichPass) + { + if (whichPass >= sRenderPassNames.size()) + { + LOGE("GetPassName() called with unknown pass (%d)!", whichPass); + return "RP_UNKNOWN"; + } + + return sRenderPassNames[whichPass]; + }; + + char szName[256]; + for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + for (uint32_t whichBuffer = 0; whichBuffer < m_RenderPassData[whichPass].passCmdBuffer.size(); whichBuffer++) + { + // The Pass Command Buffer => Primary + sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); + if (!m_RenderPassData[whichPass].passCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + } + + for (uint32_t whichBuffer = 0; whichBuffer < m_TileShadingPassData.passCmdBuffer.size(); whichBuffer++) + { + // The Pass Command Buffer => Primary + sprintf(szName, "Primary (Buffer %d of %d)", whichBuffer + 1, NUM_VULKAN_BUFFERS); + if (!m_TileShadingPassData.passCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + + for (uint32_t whichPass = 0; whichPass < NUM_COMPUTE_PASSES; whichPass++) + { + for (uint32_t whichBuffer = 0; whichBuffer < m_ComputePassData[whichPass].passCmdBuffer.size(); whichBuffer++) + { + // The Pass Command Buffer => Primary + sprintf(szName, "Primary (%s; Buffer %d of %d)", GetPassName(whichPass), whichBuffer + 1, NUM_VULKAN_BUFFERS); + if (!m_ComputePassData[whichPass].passCmdBuffer[whichBuffer].Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + } + + for (int i=0; i< m_SwapchainCopyCmdBuffer.size(); i++) + { + auto& swapchainCopyCmdBuffer = m_SwapchainCopyCmdBuffer[i]; + + // The Pass Command Buffer => Primary + sprintf(szName, "Swapchain Copy CMD Buffer (Buffer %d of %d)", i + 1, NUM_VULKAN_BUFFERS); + if (!swapchainCopyCmdBuffer.Initialize(pVulkan, szName, CommandListBase::Type::Primary)) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitLocalSemaphores() +//----------------------------------------------------------------------------- +{ + LOGI("********************************"); + LOGI("Initializing Local Semaphores..."); + LOGI("********************************"); + + const VkSemaphoreCreateInfo SemaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + + for (uint32_t whichPass = 0; whichPass < NUM_RENDER_PASSES; whichPass++) + { + VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_RenderPassData[whichPass].passSemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + } + + { + VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_TileShadingPassData.passSemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + } + + for (uint32_t whichPass = 0; whichPass < NUM_COMPUTE_PASSES; whichPass++) + { + VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_ComputePassData[whichPass].passSemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + } + + VkResult retVal = vkCreateSemaphore(GetVulkan()->m_VulkanDevice, &SemaphoreInfo, NULL, &m_SwapchhainCopySemaphore); + if (!CheckVkError("vkCreateSemaphore()", retVal)) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void Application::UpdateGui() +//----------------------------------------------------------------------------- +{ + if (m_Gui) + { + m_Gui->Update(); + ImGuiIO& io = ImGui::GetIO(); + + if (ImGui::Begin("FPS", (bool*)nullptr, ImGuiWindowFlags_NoTitleBar)) + { + if(ImGui::CollapsingHeader("Scene Details")) + { + ImGui::Text("FPS: %.1f", m_CurrentFPS); + ImGui::Text("Camera [%f, %f, %f]", m_Camera.Position().x, m_Camera.Position().y, m_Camera.Position().z); + ImGui::Checkbox("Limit Values to Adreno Values", &m_CapMaxValues); + } + + if(ImGui::CollapsingHeader("Clustered Deferred Light", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::BeginDisabled(!m_IsTileShadingSupported); + ImGui::Checkbox("Tile Shading Enabled", &m_IsTileShadingActive); + ImGui::EndDisabled(); + + ImGui::BeginDisabled(!m_IsTileShadingSupported || !m_IsTileShadingActive || true); + ImGui::Checkbox("Area Dispatch", &m_IsAreaDispatchActive); + ImGui::EndDisabled(); + + ImGui::Separator(); + + bool ignoreLightTiles = static_cast(m_SceneInfoUniformData.ignoreLightTiles); + ImGui::Checkbox("Deactivate Clustering", &ignoreLightTiles); + m_SceneInfoUniformData.ignoreLightTiles = static_cast(ignoreLightTiles); + + bool debugShaders = static_cast(m_SceneInfoUniformData.debugShaders); + ImGui::Checkbox("Visualize Clusters", &debugShaders); + m_SceneInfoUniformData.debugShaders = static_cast(debugShaders); + + ImGui::Separator(); + + ImGui::DragInt("Light Count", &m_SceneInfoUniformData.lightCount, 1.0f, 0, m_CapMaxValues ? 64 : MAX_LIGHT_COUNT); + m_SceneInfoUniformData.lightCount = std::min(m_SceneInfoUniformData.lightCount, m_CapMaxValues ? 64 : MAX_LIGHT_COUNT); + + ImGui::Separator(); + + std::array clusterSize + { + static_cast(m_SceneInfoUniformData.clusterSizeX) + }; + + ImGui::DragInt("Cluster Size", clusterSize.data(), 8.0f, 32, m_CapMaxValues ? 96 : 192); + m_SceneInfoUniformData.clusterSizeX = static_cast(clusterSize[0]); + m_SceneInfoUniformData.clusterSizeY = static_cast(clusterSize[0]); + + // Calculate the cluster count + if(m_IsTileShadingActive) + { + m_SceneInfoUniformData.binClusterCountX = static_cast(std::ceil((static_cast(m_BinSize.x) / static_cast(m_SceneInfoUniformData.clusterSizeX)))); + m_SceneInfoUniformData.binClusterCountY = static_cast(std::ceil((static_cast(m_BinSize.y) / static_cast(m_SceneInfoUniformData.clusterSizeY)))); + m_SceneInfoUniformData.globalClusterCountX = m_SceneInfoUniformData.binClusterCountX * m_SceneInfoUniformData.binCountX; + m_SceneInfoUniformData.globalClusterCountY = m_SceneInfoUniformData.binClusterCountY * m_SceneInfoUniformData.binCountY; + } + else + { + if (m_IsTileShadingSupported) + { + /* + * Use the same amount of clusters as the tile shading variant, so comparissions are fully compatible (aples to aples), otherwise + * the non-tile path could potentially trigger less dispatches than the tile shading one, resulting in better performance. The + * reason this is done is: Using the cluster size as the base variable hinders our ability of fitting clusters perfectly according + * to the binning/screen area, ideally we should use bin count (which is then used to determine the optimal cluster size). This is + * a limitation on how the sample was prepared and not related to the extension itself. + */ + m_SceneInfoUniformData.globalClusterCountX = static_cast(std::ceil((static_cast(m_BinSize.x) / static_cast(m_SceneInfoUniformData.clusterSizeX)))) * m_SceneInfoUniformData.binCountX; + m_SceneInfoUniformData.globalClusterCountY = static_cast(std::ceil((static_cast(m_BinSize.y) / static_cast(m_SceneInfoUniformData.clusterSizeY)))) * m_SceneInfoUniformData.binCountY; + } + else + { + m_SceneInfoUniformData.globalClusterCountX = (m_SceneInfoUniformData.viewportWidth + (m_SceneInfoUniformData.clusterSizeX - 1)) / m_SceneInfoUniformData.clusterSizeX; + m_SceneInfoUniformData.globalClusterCountY = (m_SceneInfoUniformData.viewportHeight + (m_SceneInfoUniformData.clusterSizeY - 1)) / m_SceneInfoUniformData.clusterSizeY; + } + + m_SceneInfoUniformData.binClusterCountX = m_SceneInfoUniformData.globalClusterCountX; + m_SceneInfoUniformData.binClusterCountY = m_SceneInfoUniformData.globalClusterCountY; + } + + // Disable tile memory if number of bins is too high and tile shading isn't active + // If tile shading is active, we don't really care about the number of bins, because each time one + // executed via tile shading dispatch, it will fill the same memory buffer area, because of that + // we only need a buffer that fits the light cluster total, which is pretty much a guarantee on + // any device that supports tile memory + if (m_SceneInfoUniformData.binCountX * m_SceneInfoUniformData.binCountY > MAX_TILE_MEMORY_SUPPORTED_TILES && !m_IsTileShadingActive) + { + m_SceneInfoUniformData.tileMemoryEnabled = false; + } + + ImGui::Text("Bin Count: [%d, %d] -> T %d", + m_IsTileShadingActive ? m_SceneInfoUniformData.binCountX : 1, + m_IsTileShadingActive ? m_SceneInfoUniformData.binCountY : 1, + m_IsTileShadingActive ? m_SceneInfoUniformData.binCountX * m_SceneInfoUniformData.binCountY : 1); + + ImGui::Text( + "Total Clusters Per Bin: [%d, %d] -> T %d", + m_SceneInfoUniformData.binClusterCountX, + m_SceneInfoUniformData.binClusterCountY, + m_SceneInfoUniformData.binClusterCountX * m_SceneInfoUniformData.binClusterCountY); + + ImGui::Text( + "Total Clusters: [%d, %d] -> T %d", + m_SceneInfoUniformData.globalClusterCountX, + m_SceneInfoUniformData.globalClusterCountY, + m_SceneInfoUniformData.globalClusterCountX * m_SceneInfoUniformData.globalClusterCountY); + } + + glm::vec3 LightDirNotNormalized = m_SceneInfoUniformData.skyLightDirection; + LightDirNotNormalized = glm::normalize(LightDirNotNormalized); + m_SceneInfoUniformData.skyLightDirection = glm::vec4(LightDirNotNormalized, 0.0f); + } + ImGui::End(); + + return; + } +} + +//----------------------------------------------------------------------------- +bool Application::UpdateUniforms(uint32_t whichBuffer) +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Vert data + { + glm::mat4 LocalModel = glm::mat4(1.0f); + LocalModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); + LocalModel = glm::scale(LocalModel, glm::vec3(1.0f)); + glm::mat4 LocalMVP = m_Camera.ProjectionMatrix() * m_Camera.ViewMatrix() * LocalModel; + + m_ObjectVertUniformData.MVPMatrix = LocalMVP; + m_ObjectVertUniformData.ModelMatrix = LocalModel; + UpdateUniformBuffer(pVulkan, m_ObjectVertUniform, m_ObjectVertUniformData); + } + + // Frag data + for (auto& [hash, objectUniform] : m_ObjectFragUniforms) + { + UpdateUniformBuffer(pVulkan, objectUniform.objectFragUniform, objectUniform.objectFragUniformData); + } + + // Scene data + { + glm::mat4 CameraViewInv = glm::inverse(m_Camera.ViewMatrix()); + glm::mat4 CameraProjection = m_Camera.ProjectionMatrix(); + glm::mat4 CameraProjectionInv = glm::inverse(CameraProjection); + + m_SceneInfoUniformData.projectionInv = CameraProjectionInv; + m_SceneInfoUniformData.viewInv = CameraViewInv; + m_SceneInfoUniformData.view = m_Camera.ViewMatrix(); + m_SceneInfoUniformData.viewInv[3][0] *= 0.5f; + m_SceneInfoUniformData.viewInv[3][1] *= 0.5f; + m_SceneInfoUniformData.viewInv[3][2] *= 0.5f; + m_SceneInfoUniformData.viewProjection = CameraProjection * m_Camera.ViewMatrix(); + m_SceneInfoUniformData.viewProjectionInv = glm::inverse(m_SceneInfoUniformData.viewProjection); + m_SceneInfoUniformData.projectionInvW = glm::vec4(CameraProjectionInv[0].w, CameraProjectionInv[1].w, CameraProjectionInv[2].w, CameraProjectionInv[3].w); + m_SceneInfoUniformData.cameraPos = glm::vec4(m_Camera.Position(), 0.0f); + + UpdateUniformBuffer(pVulkan, m_SceneInfoUniform, m_SceneInfoUniformData); + } + + // Light data (this doesn't change, no need to update every frame) + { + // UpdateUniformBuffer(pVulkan, m_LightUniform, m_LightUniformData); + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::UpdateDescriptors(uint32_t whichBuffer) +//----------------------------------------------------------------------------- +{ + bool success = true; + + // Select which render target should be used for the screen blit operation depending if tile shading + // is or isn't active + success &= m_BlitQuadDrawable->GetMaterial().UpdateDescriptorSetBinding(whichBuffer, "Scene", + m_IsTileShadingActive + ? m_TileShadingSceneRenderTarget.GetColorAttachments()[2] + : m_DefaultRenderTargets[RP_DEFERRED_LIGHT].GetColorAttachments()[0]); + + return success; +} + +//----------------------------------------------------------------------------- +void Application::Render(float fltDiffTime) +//----------------------------------------------------------------------------- +{ + Vulkan* const pVulkan = GetVulkan(); + + // Obtain the next swap chain image for the next frame. + auto currentVulkanBuffer = pVulkan->SetNextBackBuffer(); + uint32_t whichBuffer = currentVulkanBuffer.idx; + + // ******************************** + // Application Draw() - Begin + // ******************************** + + m_SceneInfoUniformData.viewportWidth = gRenderWidth; + m_SceneInfoUniformData.viewportHeight = gRenderHeight; + + UpdateGui(); + + // Update camera + m_Camera.UpdateController(fltDiffTime, *m_CameraController); + m_Camera.UpdateMatrices(); + + // Update uniform buffers with latest data + if (!UpdateUniforms(whichBuffer)) + { + assert(false); + return; + } + + // Update descriptors + if(!UpdateDescriptors(whichBuffer)) + { + assert(false); + return; + } + + // const VkPipelineStageFlags defaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + const VkPipelineStageFlags defaultGfxWaitDstStageMasks[] = { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT }; + + // First time through, wait for the back buffer to be ready + std::span pWaitSemaphores = { ¤tVulkanBuffer.semaphore, 1 }; + + auto RefreshBinInformation = [&](const VkRenderingInfo* renderingInfo) + { + if (m_IsTilePropertiesSupported) + { + VkTilePropertiesQCOM tileProperties{ VK_STRUCTURE_TYPE_TILE_PROPERTIES_QCOM }; + if (vkGetDynamicRenderingTilePropertiesQCOM(GetVulkan()->m_VulkanDevice, renderingInfo, &tileProperties) == VK_SUCCESS) + { + auto CalculateTileCount = [&tileProperties](VkExtent2D renderArea) -> std::pair + { + uint32_t tilesX = static_cast(std::ceil(static_cast(renderArea.width) / tileProperties.tileSize.width)); + uint32_t tilesY = static_cast(std::ceil(static_cast(renderArea.height) / tileProperties.tileSize.height)); + + return std::make_pair(tilesX, tilesY); + }; + + const auto [binCountX, binCountY] = CalculateTileCount(renderingInfo->renderArea.extent); + + m_SceneInfoUniformData.binCountX = binCountX; + m_SceneInfoUniformData.binCountY = binCountY; + m_BinSize.x = tileProperties.tileSize.width; + m_BinSize.y = tileProperties.tileSize.height; + } + } + }; + + using RecordRenderingInfo = std::optional*, RenderingAttachmentInfoGroup>>; + auto RecordPassData = [this, whichBuffer, defaultGfxWaitDstStageMasks, &pWaitSemaphores, &RefreshBinInformation]( + RecordRenderingInfo renderingInfo, + PassRecordData passRecordInfo, + bool isTileShadingPass = false, + VkFence completionFence = VK_NULL_HANDLE) + { + auto& [passData, passDataProcessor] = passRecordInfo; + auto& commandBuffer = passData.get().passCmdBuffer[whichBuffer]; + + if (!commandBuffer.Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) + { + return; + } + + if (passDataProcessor.preDrawCallback) + { + passDataProcessor.preDrawCallback(whichBuffer, commandBuffer); + } + + if (renderingInfo) + { + const auto& [renderContext, renderingAttachmentGroup] = renderingInfo.value(); + const auto renderPassInfoRef = renderContext->GetRenderingInfo(renderingAttachmentGroup); + VkRenderingInfo renderPassInfo = renderPassInfoRef.get(); + const auto renderArea = renderPassInfo.renderArea; + + VkRenderPassTileShadingCreateInfoQCOM tileShadingCreateInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_TILE_SHADING_CREATE_INFO_QCOM }; + tileShadingCreateInfo.flags = VK_TILE_SHADING_RENDER_PASS_ENABLE_BIT_QCOM /* | VK_TILE_SHADING_RENDER_PASS_PER_TILE_EXECUTION_BIT_QCOM*/; + tileShadingCreateInfo.tileApronSize = VkExtent2D{0, 0}; + + if (isTileShadingPass) + { + renderPassInfo.pNext = &tileShadingCreateInfo; + } + + vkCmdBeginRenderingKHR(commandBuffer, &renderPassInfo); + + // For simplicity, since refresh the bin information after beginning a tile-shading rendering context + // (ideally this should be done before starting the rendering context, as the information will lag + // one frame, but for a sample it's good enough) + if (isTileShadingPass) + { + RefreshBinInformation(&renderPassInfo); + } + + VkViewport viewport; + viewport.x = renderPassInfo.renderArea.offset.x; + viewport.y = renderPassInfo.renderArea.offset.y; + viewport.width = renderPassInfo.renderArea.extent.width; + viewport.height = renderPassInfo.renderArea.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = viewport.x; + scissor.offset.y = viewport.y; + scissor.extent.width = viewport.width; + scissor.extent.height = viewport.height; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + } + + if (passDataProcessor.drawCallback) + { + passDataProcessor.drawCallback(whichBuffer, commandBuffer); + } + + if (renderingInfo) + { + vkCmdEndRenderingKHR(commandBuffer); + } + + if (passDataProcessor.postDrawCallback) + { + passDataProcessor.postDrawCallback(whichBuffer, commandBuffer); + } + + commandBuffer.End(); + + commandBuffer.QueueSubmit( + pWaitSemaphores, + defaultGfxWaitDstStageMasks, + { &passData.get().passSemaphore,1 }, + completionFence); + + pWaitSemaphores = { &passData.get().passSemaphore,1 }; + }; + + auto SimpleBufferBarrier = [this]( + CommandListVulkan& commandList, + VkBuffer targetBuffer, + VkPipelineStageFlags2 srcStageMask, + VkPipelineStageFlags2 dstStageMask, + VkAccessFlags2 srcAccessMask, + VkAccessFlags2 dstAccessMask) + { + VkBufferMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2 }; + memoryBarrier.srcAccessMask = srcAccessMask; + memoryBarrier.dstAccessMask = dstAccessMask; + memoryBarrier.srcStageMask = srcStageMask; + memoryBarrier.dstStageMask = dstStageMask; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.buffer = targetBuffer; + memoryBarrier.offset = 0; + memoryBarrier.size = VK_WHOLE_SIZE; + + VkDependencyInfo dependencyInfo{ VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + dependencyInfo.bufferMemoryBarrierCount = 1; + dependencyInfo.pBufferMemoryBarriers = &memoryBarrier; + + vkCmdPipelineBarrier2KHR(commandList.m_VkCommandBuffer, &dependencyInfo); + }; + + auto SimpleImageBarrier = [this]( + CommandListVulkan& commandList, + VkImage targetImage, + VkImageAspectFlags aspectFlags, + VkPipelineStageFlags2 srcStageMask, + VkPipelineStageFlags2 dstStageMask, + VkAccessFlags2 srcAccessMask, + VkAccessFlags2 dstAccessMask, + VkImageLayout fromLayout, + std::optional toLayout = std::nullopt) + { + VkImageMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 }; + memoryBarrier.oldLayout = fromLayout; + memoryBarrier.newLayout = toLayout ? toLayout.value() : fromLayout; + memoryBarrier.image = targetImage; + memoryBarrier.subresourceRange.aspectMask = aspectFlags; + memoryBarrier.subresourceRange.baseMipLevel = 0; + memoryBarrier.subresourceRange.levelCount = 1; + memoryBarrier.subresourceRange.baseArrayLayer = 0; + memoryBarrier.subresourceRange.layerCount = 1; + memoryBarrier.srcAccessMask = srcAccessMask; + memoryBarrier.dstAccessMask = dstAccessMask; + memoryBarrier.srcStageMask = srcStageMask; + memoryBarrier.dstStageMask = dstStageMask; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + VkDependencyInfo dependencyInfo{ VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + dependencyInfo.imageMemoryBarrierCount = 1; + dependencyInfo.pImageMemoryBarriers = &memoryBarrier; + + vkCmdPipelineBarrier2KHR(commandList.m_VkCommandBuffer, &dependencyInfo); + }; + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_SCENE // + /////////////////////////////////////////////////////////////////////////////////////// + auto sceneDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + // Scene drawables + for (const auto& sceneDrawable : m_SceneDrawables) + { + AddDrawableToCmdBuffer(sceneDrawable, commandList, whichBuffer); + } + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetColorAttachments()[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); // VK_IMAGE_LAYOUT_GENERAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: CP_LIGHT_CULLING_LIST // + /////////////////////////////////////////////////////////////////////////////////////// + auto lightCullingListDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + auto& computePassData = m_ComputePassData[CP_LIGHT_CULLING_LIST]; + + for (auto& computablePass : computePassData.passComputable->GetPasses()) + { + computePassData.passComputable->SetDispatchGroupCount(0, + { + // Dispatch all tiles at once + m_SceneInfoUniformData.globalClusterCountX, + m_SceneInfoUniformData.globalClusterCountY, + 1 + }); + + computePassData.passComputable->DispatchPass( + commandList, + computablePass, + whichBuffer); + } + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT); + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_SCENE].GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, // VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_DEFERRED_LIGHT // + /////////////////////////////////////////////////////////////////////////////////////// + auto deferredLightDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_DEFERRED_LIGHT].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + AddDrawableToCmdBuffer(*m_DeferredLightQuadDrawable, commandList, whichBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_DEFERRED_LIGHT].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_HUD // + /////////////////////////////////////////////////////////////////////////////////////// + auto hudDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_HUD].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + GetGui()->Render(commandList.m_VkCommandBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_HUD].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: RP_BLIT // + /////////////////////////////////////////////////////////////////////////////////////// + auto blitDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_BLIT].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + AddDrawableToCmdBuffer(*m_BlitQuadDrawable.get(), commandList, whichBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_DefaultRenderTargets[RP_BLIT].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + }); + + /////////////////////////////////////////////////////////////////////////////////////// + // DATA PROCESSOR: TILE SHADING SCENE // + /////////////////////////////////////////////////////////////////////////////////////// + auto tileShadingSceneDataProcessor = PassDataProcessor( + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[2].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + // Scene drawables + for (const auto& sceneDrawable : m_SceneDrawables) + { + AddDrawableToCmdBuffer(sceneDrawable, commandList, whichBuffer); + } + + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[1].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_2_SHADER_TILE_ATTACHMENT_READ_BIT_QCOM, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR); // Works with storage image, otherwise it would need to be VK_IMAGE_LAYOUT_GENERAL + + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT); + + VkPerTileBeginInfoQCOM per_tile_beging_info{ VK_STRUCTURE_TYPE_PER_TILE_BEGIN_INFO_QCOM }; + vkCmdBeginPerTileExecutionQCOM(commandList.m_VkCommandBuffer, &per_tile_beging_info); + + if (m_IsAreaDispatchActive) + { + auto& passes = m_TileShadingPassData.passComputableAreaDispatch->GetPasses(); + for (int i=0; i< passes.size(); ++i) + { + m_TileShadingPassData.passComputableAreaDispatch->DispatchPass( + commandList, + passes[i], + whichBuffer); + } + } + else + { + auto& passes = m_TileShadingPassData.passComputable->GetPasses(); + for (int i = 0; i < passes.size(); ++i) + { + m_TileShadingPassData.passComputable->SetDispatchGroupCount(i, + { + m_SceneInfoUniformData.binClusterCountX, + m_SceneInfoUniformData.binClusterCountY, + 1 + }); + + m_TileShadingPassData.passComputable->DispatchPass( + commandList, + passes[i], + whichBuffer); + } + } + + VkPerTileEndInfoQCOM per_tile_end_info{ VK_STRUCTURE_TYPE_PER_TILE_END_INFO_QCOM }; + vkCmdEndPerTileExecutionQCOM(commandList.m_VkCommandBuffer, &per_tile_end_info); + + SimpleBufferBarrier( + commandList, + m_LightIndicesBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT); + SimpleBufferBarrier( + commandList, + m_LightCountsBuffer.GetVkBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT); + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetDepthAttachment().GetVkImage(), + VK_IMAGE_ASPECT_DEPTH_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_SHADER_TILE_ATTACHMENT_READ_BIT_QCOM, // VK_ACCESS_2_MEMORY_READ_BIT + VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT, // VK_ACCESS_2_MEMORY_READ_BIT + VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR, + VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR); + + std::array inputColorAttachmentIndices = { 0, 1 }; + uint32_t inputDepthAttachmentIndex = 2; + std::array colorAttachmentLocations = { 2 }; + + VkRenderingInputAttachmentIndexInfo renderingInputAttachmentIndexInfo{ VK_STRUCTURE_TYPE_RENDERING_INPUT_ATTACHMENT_INDEX_INFO }; + renderingInputAttachmentIndexInfo.colorAttachmentCount = inputColorAttachmentIndices.size(); + renderingInputAttachmentIndexInfo.pColorAttachmentInputIndices = inputColorAttachmentIndices.data(); + renderingInputAttachmentIndexInfo.pDepthInputAttachmentIndex = &inputDepthAttachmentIndex; + + VkRenderingAttachmentLocationInfo renderingAttachmentLocationInfo{ VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_LOCATION_INFO }; + renderingAttachmentLocationInfo.colorAttachmentCount = colorAttachmentLocations.size(); + renderingAttachmentLocationInfo.pColorAttachmentLocations = colorAttachmentLocations.data(); + + vkCmdSetRenderingInputAttachmentIndices(commandList, &renderingInputAttachmentIndexInfo); + vkCmdSetRenderingAttachmentLocations(commandList, &renderingAttachmentLocationInfo); + + AddDrawableToCmdBuffer(*m_DeferredLightTileShadingQuadDrawable, commandList, whichBuffer); + }, + [&](uint32_t whichBuffer, CommandListVulkan& commandList) + { + SimpleImageBarrier( + commandList, + m_TileShadingSceneRenderTarget.GetColorAttachments()[2].GetVkImage(), + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, // VK_ACCESS_2_MEMORY_WRITE_BIT + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + + /////////////////////////////////////////////////////////////////////////////////////// + // RECORD COMMANDS // + /////////////////////////////////////////////////////////////////////////////////////// + if (m_IsTileShadingActive) + { + RecordPassData( + RecordRenderingInfo({ &m_TileShadingPassData.renderContext , RenderingAttachmentInfoGroup( + { + RenderingAttachmentInfo::Color(m_TileShadingSceneRenderTarget.GetColorAttachments()[0], RenderPassInputUsage::Clear, RenderPassOutputUsage::Store), // RenderPassOutputUsage::Discard + RenderingAttachmentInfo::Color(m_TileShadingSceneRenderTarget.GetColorAttachments()[1], RenderPassInputUsage::Clear, RenderPassOutputUsage::Store), // RenderPassOutputUsage::Discard + RenderingAttachmentInfo::Color(m_TileShadingSceneRenderTarget.GetColorAttachments()[2], RenderPassInputUsage::Clear, RenderPassOutputUsage::Store), + }, + RenderingAttachmentInfo::Depth(m_TileShadingSceneRenderTarget.GetDepthAttachment(), true, RenderPassOutputUsage::Store) // RenderPassOutputUsage::Discard + )}), + PassRecordData(m_TileShadingPassData, std::move(tileShadingSceneDataProcessor)), + true); + RecordPassData( + RecordRenderingInfo({ &m_TileShadingPassData.renderContext , RenderingAttachmentInfoGroup( + m_TileShadingSceneRenderTarget, + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store, + true, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_TileShadingPassData, std::move(tileShadingSceneDataProcessor)), + true); + } + else + { + RecordPassData( + RecordRenderingInfo({ &m_RenderPassData[RP_SCENE].renderContext , RenderingAttachmentInfoGroup( + m_DefaultRenderTargets[RP_SCENE], + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store, + true, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_SCENE], std::move(sceneDataProcessor))); + + RecordPassData( + std::nullopt, + PassRecordData(m_ComputePassData[CP_LIGHT_CULLING_LIST], std::move(lightCullingListDataProcessor))); + + RecordPassData( + RecordRenderingInfo({ &m_RenderPassData[RP_DEFERRED_LIGHT].renderContext , RenderingAttachmentInfoGroup( + m_DefaultRenderTargets[RP_DEFERRED_LIGHT], + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_DEFERRED_LIGHT], std::move(deferredLightDataProcessor))); + } + + RecordPassData( + RecordRenderingInfo({ &m_RenderPassData[RP_HUD].renderContext , RenderingAttachmentInfoGroup( + m_DefaultRenderTargets[RP_HUD], + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_HUD], std::move(hudDataProcessor))); + + RecordPassData( + RecordRenderingInfo({ &m_RenderPassData[RP_BLIT].renderContext , RenderingAttachmentInfoGroup( + m_DefaultRenderTargets[RP_BLIT], + RenderPassInputUsage::Clear, + RenderPassOutputUsage::Store) + }), + PassRecordData(m_RenderPassData[RP_BLIT], std::move(blitDataProcessor))); + + /////////////////////////////////////////////////////////////////////////////////////// + // SWAPCHAIN COPY // + /////////////////////////////////////////////////////////////////////////////////////// + { + const auto& sourceImage = m_DefaultRenderTargets[RP_BLIT].GetColorAttachments()[0]; + const auto swapchainImage = pVulkan->GetSwapchainImage(currentVulkanBuffer.swapchainPresentIdx); + + m_SwapchainCopyCmdBuffer[whichBuffer].Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + //////////////////////////////////// + // PREPARE SWAPCHAIN FOR TRANSFER // + //////////////////////////////////// + { + SimpleImageBarrier( + m_SwapchainCopyCmdBuffer[whichBuffer], + swapchainImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_ACCESS_2_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } + + ///////////////////////// + // BLIT INTO SWAPCHAIN // + ///////////////////////// + + VkImageBlit blitRegion{}; + blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.srcSubresource.baseArrayLayer = 0; + blitRegion.srcSubresource.layerCount = 1; + blitRegion.srcSubresource.mipLevel = 0; + blitRegion.srcOffsets[1].x = sourceImage.Width; + blitRegion.srcOffsets[1].y = sourceImage.Height; + blitRegion.srcOffsets[1].z = 1; + blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.dstSubresource.baseArrayLayer = 0; + blitRegion.dstSubresource.layerCount = 1; + blitRegion.dstSubresource.mipLevel = 0; + blitRegion.dstOffsets[1].x = pVulkan->m_SurfaceWidth; + blitRegion.dstOffsets[1].y = pVulkan->m_SurfaceHeight; + blitRegion.dstOffsets[1].z = 1; + + vkCmdBlitImage( + m_SwapchainCopyCmdBuffer[whichBuffer].m_VkCommandBuffer, + m_DefaultRenderTargets[RP_BLIT].GetColorAttachments()[0].GetVkImage(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + swapchainImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &blitRegion, + VK_FILTER_NEAREST); + + /////////////////////////////////// + // PREPARE SWAPCHAIN FOR PRESENT // + /////////////////////////////////// + { + SimpleImageBarrier( + m_SwapchainCopyCmdBuffer[whichBuffer], + swapchainImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_ACCESS_2_TRANSFER_WRITE_BIT, + VK_ACCESS_2_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + } + + m_SwapchainCopyCmdBuffer[whichBuffer].End(); + + /////////////////////////// + // SUBMIT SWAPCHAIN COPY // + /////////////////////////// + + m_SwapchainCopyCmdBuffer[whichBuffer].QueueSubmit( + pWaitSemaphores, + defaultGfxWaitDstStageMasks, + { &m_SwapchhainCopySemaphore,1 }, + currentVulkanBuffer.fence); + + pWaitSemaphores = { &m_SwapchhainCopySemaphore,1 }; + } + + // Queue is loaded up, tell the driver to start processing + pVulkan->PresentQueue(pWaitSemaphores, currentVulkanBuffer.swapchainPresentIdx); +} \ No newline at end of file diff --git a/samples/tile_shading/code/main/application.hpp b/samples/tile_shading/code/main/application.hpp new file mode 100644 index 0000000..2d1b283 --- /dev/null +++ b/samples/tile_shading/code/main/application.hpp @@ -0,0 +1,275 @@ +//============================================================================================================ +// +// +// Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ +#pragma once + +#include "main/applicationHelperBase.hpp" +#include "memory/vulkan/uniform.hpp" +#include "memory/vulkan/bufferObject.hpp" +#include "vulkan/commandBuffer.hpp" +#include "vulkan/renderTarget.hpp" +#include +#include + +#define MAX_LIGHT_COUNT 2048 +#define MAX_CLUSTER_MATRIX_DIMENSION_SIZE 64 +#define MAX_CLUSTER_MATRIX_SIZE (MAX_CLUSTER_MATRIX_DIMENSION_SIZE * MAX_CLUSTER_MATRIX_DIMENSION_SIZE) + +// TODO: This should be adjusted to reflect the spec, it must reflect the max shading rate on a vkCmdTileDispatchQCOM +#define MAX_CLUSTERS_PER_TILE_DIMENSION_SIZE 8 +#define MAX_CLUSTERS_PER_TILE_MATRIX_SIZE (MAX_CLUSTERS_PER_TILE_DIMENSION_SIZE * MAX_CLUSTERS_PER_TILE_DIMENSION_SIZE) + +// Define the maximum number of tiles/bins we support with tile memory, this is merely to control the size required for +// tile memory allocations, as it's a limited resource (no particular reason for this number, but it cannot make the tile +// memory buffer allocations higher than what the device supports) +#define MAX_TILE_MEMORY_SUPPORTED_TILES 10 + +class MaterialManagerBase; +class ShaderManagerBase; +struct TileMemoryHeapExtension; + +enum RENDER_PASS +{ + RP_SCENE = 0, + RP_DEFERRED_LIGHT, + RP_HUD, + RP_BLIT, + NUM_RENDER_PASSES +}; + +enum COMPUTE_PASS +{ + CP_LIGHT_CULLING_LIST = 0, + NUM_COMPUTE_PASSES +}; + +// ********************** +// Uniform Buffers +// ********************** +struct ObjectVertUB +{ + glm::mat4 MVPMatrix; + glm::mat4 ModelMatrix; + glm::mat4 ShadowMatrix; +}; + +struct ObjectFragUB +{ + glm::vec4 Color; + glm::vec4 ORM; +}; + +struct LightUB +{ + glm::vec4 lightPosAndRadius; // xyz = pos | w = radius + glm::vec4 lightColor; +}; + +struct alignas(16) SceneInfoUB +{ + glm::mat4 projectionInv; + glm::mat4 view; + glm::mat4 viewInv; + glm::mat4 viewProjection; + glm::mat4 viewProjectionInv; // ViewInv * ProjectionInv + glm::vec4 projectionInvW; // w components of ProjectionInv + glm::vec4 cameraPos; + + glm::vec4 skyLightDirection = glm::vec4(-0.564000f, 0.826000f, 0.000000f, 0.0f); + glm::vec4 skyLightColor = glm::vec4(1.000000f, 1.000000f, 1.000000f, 1.000000); + + glm::vec4 ambientColor = glm::vec4(0.340000f, 0.340000f, 0.340000f, 0.0f); + + uint32_t viewportWidth; + uint32_t viewportHeight; + uint32_t binCountX = 1; // Assume full screen bin if we cannot retrieve bin info (VK_QCOM_tile_properties) + uint32_t binCountY = 1; // Assume full screen bin if we cannot retrieve bin info (VK_QCOM_tile_properties) + uint32_t globalClusterCountX = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; + uint32_t globalClusterCountY = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; + uint32_t binClusterCountX = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; // How many clusters on the x coordinate, inside each bin + uint32_t binClusterCountY = MAX_CLUSTER_MATRIX_DIMENSION_SIZE; // How many clusters on the y coordinate, inside each bin + uint32_t clusterSizeX = 96; + uint32_t clusterSizeY = 96; + int32_t lightCount = 64; + int32_t debugShaders = false; + int32_t ignoreLightTiles = false; + int32_t tileMemoryEnabled = false; + int32_t padding1; + int32_t padding2; +}; + +// ********************** +// Render Context +// ********************** + +struct BasePassData +{ + std::array< CommandListVulkan, NUM_VULKAN_BUFFERS> passCmdBuffer; + VkSemaphore passSemaphore; +}; + +struct RenderPassData : public BasePassData +{ + ::RenderContext renderContext; +}; + +struct ComputePassData : public BasePassData +{ + std::unique_ptr< Computable> passComputable; +}; + +struct HybridPassData : public BasePassData +{ + ::RenderContext renderContext; + std::unique_ptr< Computable> passComputable; + std::unique_ptr< Computable> passComputableAreaDispatch; +}; + +enum class PassAttachmentType +{ + COLOR, + DEPTH, + STENCIL, + DEPTH_STENCIL, +}; + +struct PassDataProcessor +{ + PassDataProcessor() = default; + PassDataProcessor(std::function inDrawCallback) + : drawCallback(inDrawCallback) + { + } + + PassDataProcessor( + std::function inPreDrawCallback, + std::function inDrawCallback, + std::function inPostDrawCallback) + : preDrawCallback(inPreDrawCallback) + , drawCallback(inDrawCallback) + , postDrawCallback(inPostDrawCallback) + { + } + + std::function preDrawCallback; + std::function drawCallback; + std::function postDrawCallback; +}; + +using PassRecordData = std::pair, PassDataProcessor>; + +// ********************** +// Application +// ********************** +class Application : public ApplicationHelperBase +{ + struct ObjectMaterialParameters + { + UniformT objectFragUniform; + ObjectFragUB objectFragUniformData; + + std::size_t GetHash() const + { + auto hash_combine = [](std::size_t seed, const float& v) -> std::size_t + { + std::hash hasher; + seed ^= hasher(v) + 0x9e3228b9 + (seed << 6) + (seed >> 2); + return seed; + }; + + std::size_t result = 0; + result = hash_combine(result, objectFragUniformData.Color.x); + result = hash_combine(result, objectFragUniformData.Color.y); + result = hash_combine(result, objectFragUniformData.Color.z); + result = hash_combine(result, objectFragUniformData.Color.w); + result = hash_combine(result, objectFragUniformData.ORM.r); + result = hash_combine(result, objectFragUniformData.ORM.g); + result = hash_combine(result, objectFragUniformData.ORM.b); + result = hash_combine(result, objectFragUniformData.ORM.a); + + return result; + }; + }; + +public: + Application(); + ~Application() override; + + // ApplicationHelperBase + virtual void PreInitializeSetVulkanConfiguration(Vulkan::AppConfiguration& config) override; + virtual bool Initialize(uintptr_t windowHandle, uintptr_t hInstance) override; + virtual void Destroy() override; + virtual void Render(float fltDiffTime) override; + +private: + + // Application - Initialization + bool InitializeLights(); + bool InitializeCamera(); + bool LoadShaders(); + bool CreateRenderTargets(); + bool InitBuffers(); + bool InitGui(uintptr_t windowHandle); + bool LoadMeshObjects(); + bool CreateComputables(); + bool InitCommandBuffers(); + bool InitLocalSemaphores(); + bool SetupRenderContext(); + +private: + + void UpdateGui(); + bool UpdateUniforms(uint32_t WhichBuffer); + bool UpdateDescriptors(uint32_t WhichBuffer); + +private: + + // Passes + std::array m_RenderPassData; + std::array m_ComputePassData; + HybridPassData m_TileShadingPassData; + + std::array m_DefaultRenderTargets; + RenderTarget m_TileShadingSceneRenderTarget; + + std::array< CommandListVulkan, NUM_VULKAN_BUFFERS> m_SwapchainCopyCmdBuffer; + VkSemaphore m_SwapchhainCopySemaphore; + + // UBOs + UniformT m_ObjectVertUniform; + ObjectVertUB m_ObjectVertUniformData; + Uniform m_LightUniform; + std::array m_LightUniformData; + UniformT m_SceneInfoUniform; + SceneInfoUB m_SceneInfoUniformData; + + BufferVulkan m_LightIndicesBuffer; + BufferVulkan m_LightCountsBuffer; + + std::unordered_map m_ObjectFragUniforms; + + // Drawables + std::vector m_SceneDrawables; + std::unique_ptr m_DeferredLightQuadDrawable; + std::unique_ptr m_DeferredLightTileShadingQuadDrawable; + std::unique_ptr m_DeferredLightTileShadingAreaDispatchQuadDrawable; + std::unique_ptr m_BlitQuadDrawable; + + // Shaders + std::unique_ptr m_ShaderManager; + + // Materials + std::unique_ptr m_MaterialManager; + + // Tile shading specific + bool m_IsTilePropertiesSupported = true; + bool m_IsTileShadingSupported = true; + bool m_IsTileShadingActive = false; + bool m_IsAreaDispatchActive = false; + bool m_CapMaxValues = true; + glm::uvec2 m_BinSize = glm::uvec2(100, 100); +}; diff --git a/samples/tile_shading/install_apk.bat b/samples/tile_shading/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/samples/tile_shading/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/tile_shading/install_config.bat b/samples/tile_shading/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/samples/tile_shading/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/hdrSwapchain/project/android/AndroidManifest.xml b/samples/tile_shading/project/android/AndroidManifest.xml similarity index 91% rename from samples/hdrSwapchain/project/android/AndroidManifest.xml rename to samples/tile_shading/project/android/AndroidManifest.xml index 6cdb47f..49b853f 100644 --- a/samples/hdrSwapchain/project/android/AndroidManifest.xml +++ b/samples/tile_shading/project/android/AndroidManifest.xml @@ -1,13 +1,14 @@ + + android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> + + SGS Tile Shading + diff --git a/samples/tile_shading/project/img/screenshot.png b/samples/tile_shading/project/img/screenshot.png new file mode 100644 index 0000000..d65670a Binary files /dev/null and b/samples/tile_shading/project/img/screenshot.png differ diff --git a/samples/tile_shading/shaders/Blit.frag b/samples/tile_shading/shaders/Blit.frag new file mode 100644 index 0000000..e3e694d --- /dev/null +++ b/samples/tile_shading/shaders/Blit.frag @@ -0,0 +1,65 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +// Buffer binding locations +#define SHADER_DIFFUSE_TEXTURE_LOC 0 +#define SHADER_OVERLAY_TEXTURE_LOC 1 + +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_SceneTex; +layout(set = 0, binding = SHADER_OVERLAY_TEXTURE_LOC) uniform sampler2D u_OverlayTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec4 v_VertColor; + +// Finally, the output color +layout (location = 0) out vec4 FragColor; + + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 LocalTexCoord = vec2(v_TexCoord.xy); + + // ******************************** + // Texture Colors + // ******************************** + // Get base color from the color texture + vec4 SceneColor = texture( u_SceneTex, LocalTexCoord.xy ); + + // Multiply by vertex color. + SceneColor.xyzw *= v_VertColor.xyzw; + + // Apply darkening/lightening control + // float lerp01 = min(1,FragCB.Diffuse); + // float lerp12 = max(0,FragCB.Diffuse-1); + // SceneColor = SceneColor * lerp01 + lerp12 - lerp12 * SceneColor; + + // Get the Overlay value + vec4 OverlayColor = texture( u_OverlayTex, LocalTexCoord.xy ); + + // ******************************** + // Alpha Blending + // ******************************** + FragColor.rgb = SceneColor.rgb *(1.0-OverlayColor.a) + OverlayColor.rgb; + FragColor.a = 1.0; + + // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! + // FragColor.xyz = (1.0 - OverlayColor.a) * SceneColor.xyz + OverlayColor.a * OverlayColor.xyz; + // FragColor = vec4(SceneColor.xyz, SceneColor.a); + // FragColor = vec4(OverlayColor.xyz, OverlayColor.a); + // FragColor.a = 1.0; + // FragColor = vec4(0.8, 0.2, 0.8, 1.0); +} + diff --git a/samples/rayReflections/shaders/Blit.json b/samples/tile_shading/shaders/Blit.json similarity index 77% rename from samples/rayReflections/shaders/Blit.json rename to samples/tile_shading/shaders/Blit.json index 3ef5c0b..60aa80a 100644 --- a/samples/rayReflections/shaders/Blit.json +++ b/samples/tile_shading/shaders/Blit.json @@ -10,23 +10,11 @@ "DescriptorSets": [ { "Buffers": [ - { - "Type": "UniformBuffer", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "BlitFragCB" ] - }, - { - "Type": "ImageSampler", - "Stages": [ "Fragment" ], - "Count": 1, - "Names": [ "Diffuse" ] - }, { "Type": "ImageSampler", "Stages": [ "Fragment" ], "Count": 1, - "Names": [ "Bloom" ] + "Names": [ "Scene" ] }, { "Type": "ImageSampler", diff --git a/samples/hdrSwapchain/shaders/Blit.vert b/samples/tile_shading/shaders/Blit.vert similarity index 99% rename from samples/hdrSwapchain/shaders/Blit.vert rename to samples/tile_shading/shaders/Blit.vert index 23d3fc3..d11750a 100644 --- a/samples/hdrSwapchain/shaders/Blit.vert +++ b/samples/tile_shading/shaders/Blit.vert @@ -6,8 +6,6 @@ // //============================================================================================================ -// Blit.vert - #version 400 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable diff --git a/samples/tile_shading/shaders/DeferredLight.frag b/samples/tile_shading/shaders/DeferredLight.frag new file mode 100644 index 0000000..f8acee0 --- /dev/null +++ b/samples/tile_shading/shaders/DeferredLight.frag @@ -0,0 +1,202 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +precision highp float; +precision highp int; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint binCountX; + uint binCountY; + uint globalClusterCountX; + uint globalClusterCountY; + uint binClusterCountX; + uint binClusterCountY; + uint clusterSizeX; + uint clusterSizeY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; + int padding1; + int padding2; +}; + +layout(set = 0, binding = 0) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = 1) uniform sampler2D u_NormalTex; +layout(set = 0, binding = 2) uniform highp sampler2D u_DepthTex; + +layout(std140, set = 0, binding = 3) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 4) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 5) buffer TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 6) buffer TileLightCounts +{ + uint lightCounts[]; +}; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec4 v_VertColor; + +// Finally, the output color +layout (location = 0) out vec4 FragColor; + +//----------------------------------------------------------------------------- +vec3 ApplyLight(LightUB light, highp vec3 fragWorldSpacePos, highp vec3 fragNormal) +//----------------------------------------------------------------------------- +{ + highp vec3 lightPosition = light.lightPosAndRadius.xyz; + highp float lightRadius = light.lightPosAndRadius.w; + + highp vec3 lightDir = normalize(lightPosition - fragWorldSpacePos); + highp float distance = length(lightPosition - fragWorldSpacePos); + + if (distance > lightRadius * 2.0) + { + return vec3(0.0); // Skip lights that do not affect this fragment + } + + // float attenuation = 1.0 / (distance * distance); + float attenuation = 1.0 / (distance * distance / (lightRadius * lightRadius)); + float intensity = max(dot(fragNormal, lightDir), 0.0) * light.lightColor.a; + + return light.lightColor.rgb * attenuation * intensity; +} + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 screenSize = vec2(sceneInfo.viewportWidth, sceneInfo.viewportHeight); + + uint currentClusterIndexX = uint(floor((v_TexCoord.x * screenSize.x) / float(sceneInfo.clusterSizeX))); + uint currentClusterIndexY = uint(floor((v_TexCoord.y * screenSize.y) / float(sceneInfo.clusterSizeY))); + + uint clusterIndex = currentClusterIndexY * sceneInfo.globalClusterCountX + currentClusterIndexX; + + vec3 diffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ).xyz; + vec3 normal = texture( u_NormalTex, v_TexCoord.xy ).xyz; + float depth = texture( u_DepthTex, v_TexCoord.xy ).r * 2.0 - 1.0; + + highp vec4 clipSpacePos = vec4( + v_TexCoord.x * 2.0 - 1.0, + (1.0 - v_TexCoord.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + highp vec4 fragWorldSpacePos = sceneInfo.viewInv * fragViewSpacePos; + + uint lightCount = lightCounts[clusterIndex]; + highp vec3 accumulatedColor = vec3(0.0); + + if(sceneInfo.ignoreLightTiles != 0) + { + for (int i = 0; i < sceneInfo.lightCount; ++i) + { + LightUB light = lights[i]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + else + { + for (uint i = 0u; i < lightCount; ++i) + { + uint lightIndex = lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + i]; + LightUB light = lights[lightIndex]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + + FragColor = vec4(accumulatedColor * diffuseColor, 1.0); + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// + + if(sceneInfo.debugShaders != 0) + { + ivec2 clusterSize = ivec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + + // Compute the local coordinate within the tile + ivec2 pixelCoord = ivec2(v_TexCoord.xy * screenSize); + ivec2 localCoord = pixelCoord % clusterSize; + + // Draw grid lines along tile boundaries using green + float lineThickness = 1.0; + bool drawGrid = (float(localCoord.x) < lineThickness || + float(localCoord.y) < lineThickness || + float(localCoord.x) > float(clusterSize.x) - lineThickness || + float(localCoord.y) > float(clusterSize.y) - lineThickness); + if(drawGrid) + { + FragColor = mix(FragColor, vec4(0.0, 0.7, 1.0, 1.0), 0.7); + } + + // Draw debug bar: within each tile, reserve a small horizontal region + // and subdivide it into MAX_LIGHT_COUNT segments. Fill segments (from left) up to tile_light_count. + float debugBarHeight = 6.0; // height in pixels of the debug bar + if(float(localCoord.y) < debugBarHeight) + { + float factor = float(lightCount) / float(sceneInfo.lightCount); + float segmentWidth = float(clusterSize.x) / float(sceneInfo.lightCount); + int segIndex = int(float(localCoord.x) / segmentWidth); + if(segIndex < int(lightCount)) + { + FragColor = mix(FragColor, vec4(factor, 1.0 - factor, 0.0, 1.0), 0.7); + } + } + } + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/DeferredLight.json b/samples/tile_shading/shaders/DeferredLight.json new file mode 100644 index 0000000..f691477 --- /dev/null +++ b/samples/tile_shading/shaders/DeferredLight.json @@ -0,0 +1,94 @@ +{ + "$schema": "../../../framework/schema/shaderSchema.json", + "Passes": [ + { + "Name": "RP_DEFERRED_LIGHT", + "Shaders": { + "Vertex": "Media/Shaders/DeferredLight.vert.spv", + "Fragment": "Media/Shaders/DeferredLight.frag.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneColor" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneNormal" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneDepth" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneInfo" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightCounts" ] + } + ] + } + ], + "VertexBindings": [ "VB0" ] + } + ], + "Vertex": [ + { + "Span": 60, + "Name": "VB0", + "Elements": [ + { + "Name": "Position", + "Offset": 0, + "Type": "Vec3" + }, + { + "Name": "Normal", + "Offset": 12, + "Type": "Vec3" + }, + { + "Name": "UV", + "Offset": 24, + "Type": "Vec2" + }, + { + "Name": "Color", + "Offset": 32, + "Type": "Vec4" + }, + { + "Name": "Tangent", + "Offset": 48, + "Type": "Vec3" + } + ] + } + ] +} diff --git a/samples/hdrSwapchain/shaders/Light.vert b/samples/tile_shading/shaders/DeferredLight.vert similarity index 91% rename from samples/hdrSwapchain/shaders/Light.vert rename to samples/tile_shading/shaders/DeferredLight.vert index 04353db..d11750a 100644 --- a/samples/hdrSwapchain/shaders/Light.vert +++ b/samples/tile_shading/shaders/DeferredLight.vert @@ -6,8 +6,6 @@ // //============================================================================================================ -// Blit.vert - #version 400 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable @@ -26,6 +24,7 @@ layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Varying's layout (location = 0) out vec2 v_TexCoord; +layout (location = 1) out vec4 v_VertColor; void main() { @@ -33,4 +32,7 @@ void main() vec4 TempPos = vec4(a_Position.xyz, 1.0); gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); v_TexCoord = vec2(a_TexCoord.xy); + + // Color is simple attribute color + v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); } diff --git a/samples/tile_shading/shaders/DeferredLightTileShading.frag b/samples/tile_shading/shaders/DeferredLightTileShading.frag new file mode 100644 index 0000000..ac9325f --- /dev/null +++ b/samples/tile_shading/shaders/DeferredLightTileShading.frag @@ -0,0 +1,202 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_QCOM_tile_shading : enable + +precision highp float; +precision highp int; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint binCountX; + uint binCountY; + uint globalClusterCountX; + uint globalClusterCountY; + uint binClusterCountX; + uint binClusterCountY; + uint clusterSizeX; + uint clusterSizeY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; + int padding1; + int padding2; +}; + +layout (input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput u_DiffuseTex; +layout (input_attachment_index = 1, set = 0, binding = 1) uniform highp subpassInput u_NormalTex; +layout (input_attachment_index = 2, set = 0, binding = 2) uniform highp subpassInput u_DepthTex; + +layout(std140, set = 0, binding = 3) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 4) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 5) buffer TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 6) buffer TileLightCounts +{ + uint lightCounts[]; +}; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec4 v_VertColor; + +// Finally, the output color +layout (location = 2) out vec4 FragColor; + +//----------------------------------------------------------------------------- +vec3 ApplyLight(LightUB light, highp vec3 fragWorldSpacePos, highp vec3 fragNormal) +//----------------------------------------------------------------------------- +{ + highp vec3 lightPosition = light.lightPosAndRadius.xyz; + highp float lightRadius = light.lightPosAndRadius.w; + + highp vec3 lightDir = normalize(lightPosition - fragWorldSpacePos); + highp float distance = length(lightPosition - fragWorldSpacePos); + + if (distance > lightRadius * 2.0) + { + return vec3(0.0); // Skip lights that do not affect this fragment + } + + // float attenuation = 1.0 / (distance * distance); + float attenuation = 1.0 / (distance * distance / (lightRadius * lightRadius)); + float intensity = max(dot(fragNormal, lightDir), 0.0) * light.lightColor.a; + + return light.lightColor.rgb * attenuation * intensity; +} + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 screenSize = vec2(sceneInfo.viewportWidth, sceneInfo.viewportHeight); + + uvec2 clusterSize = uvec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + uvec2 clusterKey = uvec2(gl_FragCoord.xy) / clusterSize; + uint clusterIndex = clusterKey.y * sceneInfo.globalClusterCountX + clusterKey.x; + + vec3 diffuseColor = subpassLoad( u_DiffuseTex).xyz; + vec3 normal = subpassLoad( u_NormalTex).xyz; + float depth = subpassLoad( u_DepthTex).r * 2.0 - 1.0; + + highp vec4 clipSpacePos = vec4( + v_TexCoord.x * 2.0 - 1.0, + (1.0 - v_TexCoord.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + highp vec4 fragWorldSpacePos = sceneInfo.viewInv * fragViewSpacePos; + + uint lightCount = lightCounts[clusterIndex]; + highp vec3 accumulatedColor = vec3(0.0); + + if(sceneInfo.ignoreLightTiles != 0) + { + for (int i = 0; i < sceneInfo.lightCount; ++i) + { + LightUB light = lights[i]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + else + { + for (uint i = 0u; i < lightCount; ++i) + { + uint lightIndex = lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + i]; + LightUB light = lights[lightIndex]; + + accumulatedColor += ApplyLight(light, fragWorldSpacePos.xyz, normal); + } + } + + FragColor = vec4(accumulatedColor * diffuseColor, 1.0); + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// + + if(sceneInfo.debugShaders != 0) + { + ivec2 clusterSize = ivec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + + // Compute the local coordinate within the tile + ivec2 pixelCoord = ivec2(v_TexCoord.xy * screenSize); + ivec2 localCoord = pixelCoord % clusterSize; + + // Draw grid lines along tile boundaries using green + float lineThickness = 1.0; + bool drawGrid = (float(localCoord.x) < lineThickness || + float(localCoord.y) < lineThickness || + float(localCoord.x) > float(clusterSize.x) - lineThickness || + float(localCoord.y) > float(clusterSize.y) - lineThickness); + if(drawGrid) + { + FragColor = mix(FragColor, vec4(0.0, 0.7, 1.0, 1.0), 0.7); + } + + // Draw debug bar: within each tile, reserve a small horizontal region + // and subdivide it into MAX_LIGHT_COUNT segments. Fill segments (from left) up to tile_light_count. + float debugBarHeight = 6.0; // height in pixels of the debug bar + if(float(localCoord.y) < debugBarHeight) + { + float factor = float(lightCount) / float(sceneInfo.lightCount); + float segmentWidth = float(clusterSize.x) / float(sceneInfo.lightCount); + int segIndex = int(float(localCoord.x) / segmentWidth); + if(segIndex < int(lightCount)) + { + FragColor = mix(FragColor, vec4(factor, 1.0 - factor, 0.0, 1.0), 0.7); + } + } + } + +////////////////////////////////////////////////////////////////////////////////////// +// DEBUG // +////////////////////////////////////////////////////////////////////////////////////// +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/DeferredLightTileShading.json b/samples/tile_shading/shaders/DeferredLightTileShading.json new file mode 100644 index 0000000..fef6f17 --- /dev/null +++ b/samples/tile_shading/shaders/DeferredLightTileShading.json @@ -0,0 +1,94 @@ +{ + "$schema": "../../../framework/schema/shaderSchema.json", + "Passes": [ + { + "Name": "RP_SCENE", + "Shaders": { + "Vertex": "Media/Shaders/DeferredLight.vert.spv", + "Fragment": "Media/Shaders/DeferredLightTileShading.frag.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "InputAttachment", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneColor" ] + }, + { + "Type": "InputAttachment", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneNormal" ] + }, + { + "Type": "InputAttachment", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneDepth" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "SceneInfo" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "LightCounts" ] + } + ] + } + ], + "VertexBindings": [ "VB0" ] + } + ], + "Vertex": [ + { + "Span": 60, + "Name": "VB0", + "Elements": [ + { + "Name": "Position", + "Offset": 0, + "Type": "Vec3" + }, + { + "Name": "Normal", + "Offset": 12, + "Type": "Vec3" + }, + { + "Name": "UV", + "Offset": 24, + "Type": "Vec2" + }, + { + "Name": "Color", + "Offset": 32, + "Type": "Vec4" + }, + { + "Name": "Tangent", + "Offset": 48, + "Type": "Vec3" + } + ] + } + ] +} diff --git a/samples/tile_shading/shaders/LightCulling.comp b/samples/tile_shading/shaders/LightCulling.comp new file mode 100644 index 0000000..7a5e09c --- /dev/null +++ b/samples/tile_shading/shaders/LightCulling.comp @@ -0,0 +1,151 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 +#define LOCAL_SIZE 8 + +#extension GL_KHR_shader_subgroup_arithmetic : enable + +layout(local_size_x = LOCAL_SIZE, local_size_y = LOCAL_SIZE, local_size_z = 1) in; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint binCountX; + uint binCountY; + uint globalClusterCountX; + uint globalClusterCountY; + uint binClusterCountX; + uint binClusterCountY; + uint clusterSizeX; + uint clusterSizeY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; + int padding1; + int padding2; +}; + +layout(set = 0, binding = 0, rgba32f) uniform highp readonly image2D u_DepthTex; + +layout(std140, set = 0, binding = 1) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 2) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 3) buffer writeonly TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 4) buffer writeonly TileLightCounts +{ + uint lightCounts[]; +}; + +vec3 reconstructWorldPosition(highp vec2 uv, highp float depth) +{ + highp vec4 clipSpacePos = vec4( + uv.x * 2.0 - 1.0, + (1.0 - uv.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + return (sceneInfo.viewInv * fragViewSpacePos).xyz; +} + +void main() +{ + uvec2 clusterSize = uvec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + uvec2 clusterKey = gl_WorkGroupID.xy; + uint clusterIndex = clusterKey.y * sceneInfo.globalClusterCountX + clusterKey.x; + + // Tile bounds in pixels + uint clusterMinX = uint(float(clusterKey.x) * clusterSize.x); + uint clusterMinY = uint(float(clusterKey.y) * clusterSize.y); + + // Reconstruct tile frustum + highp vec3 clusterMin = vec3(1e8); + highp vec3 clusterMax = vec3(-1e8); + + for (uint y = 0; y < clusterSize.y / LOCAL_SIZE; ++y) + { + for (uint x = 0; x < clusterSize.x / LOCAL_SIZE; ++x) + { + ivec2 pixelLocation = ivec2(ivec2(clusterMinX, clusterMinY) + ivec2(clusterSize / LOCAL_SIZE) * gl_LocalInvocationID.xy) + ivec2(x, y); + + vec2 uv = vec2(float(pixelLocation.x) / float(sceneInfo.viewportWidth), + float(pixelLocation.y) / float(sceneInfo.viewportHeight)); + if(uv.x > 1.0 || uv.y > 1.0) + { + continue; + } + + highp float depth = imageLoad(u_DepthTex, pixelLocation).r * 2.0 - 1.0; + vec3 worldPos = reconstructWorldPosition(uv, depth); + + clusterMin = min(clusterMin, worldPos); + clusterMax = max(clusterMax, worldPos); + } + } + + clusterMax = subgroupMax(clusterMax); + clusterMin = subgroupMin(clusterMin); + + if ((gl_LocalInvocationID.x == 0) && (gl_LocalInvocationID.y == 0)) + { + uint tileLightCount = 0u; + for (int lightId = 0; lightId < int(sceneInfo.lightCount); ++lightId) + { + vec3 lightPosition = lights[lightId].lightPosAndRadius.xyz; + float lightRadius = lights[lightId].lightPosAndRadius.w; + + // Test light against tile bounds (AABB vs Sphere) + vec3 closestPoint = clamp(lightPosition, clusterMin, clusterMax); + float distance = length(closestPoint - lightPosition); + + if (distance < lightRadius * 2.0) + { + lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + tileLightCount] = uint(lightId); + tileLightCount++; + } + } + + lightCounts[clusterIndex] = tileLightCount; + } +} \ No newline at end of file diff --git a/samples/rayQueryShadows/shaders/AnimateBuffer.json b/samples/tile_shading/shaders/LightCulling.json similarity index 50% rename from samples/rayQueryShadows/shaders/AnimateBuffer.json rename to samples/tile_shading/shaders/LightCulling.json index 22c5206..c899c51 100644 --- a/samples/rayQueryShadows/shaders/AnimateBuffer.json +++ b/samples/tile_shading/shaders/LightCulling.json @@ -2,36 +2,46 @@ "$schema": "../../../framework/schema/shaderSchema.json", "Passes": [ { - "Name": "AnimateBuffer", + "Name": "CP_LIGHT_CULLING_LIST", "Shaders": { - "Compute": "Media/Shaders/AnimateBuffer.comp.spv" + "Compute": "Media/Shaders/LightCulling.comp.spv" }, "DescriptorSets": [ { "Buffers": [ { - "Type": "StorageBuffer", + "Type": "ImageSampled", "Stages": [ "Compute" ], "Count": 1, - "ReadOnly": true, - "Names": [ "InputVertexData" ] + "Names": [ "SceneDepth" ] }, { - "Type": "StorageBuffer", + "Type": "UniformBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": [ "OutputVertexData" ] + "Names": [ "SceneInfo" ] }, { "Type": "UniformBuffer", "Stages": [ "Compute" ], "Count": 1, - "Names": ["Uniform"] + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightCounts" ] } ] } - ], - "WorkGroup": { "LocalSize": [ 128, 1, 1 ] } + ] } ] } diff --git a/samples/tile_shading/shaders/LightCullingTileShading.comp b/samples/tile_shading/shaders/LightCullingTileShading.comp new file mode 100644 index 0000000..92c6348 --- /dev/null +++ b/samples/tile_shading/shaders/LightCullingTileShading.comp @@ -0,0 +1,153 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 +#define LOCAL_SIZE 8 + +#extension GL_QCOM_tile_shading : enable +#extension GL_KHR_shader_subgroup_arithmetic : enable + +layout(local_size_x = LOCAL_SIZE, local_size_y = LOCAL_SIZE, local_size_z = 1) in; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint binCountX; + uint binCountY; + uint globalClusterCountX; + uint globalClusterCountY; + uint binClusterCountX; + uint binClusterCountY; + uint clusterSizeX; + uint clusterSizeY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; + int padding1; + int padding2; +}; + +layout(set = 0, binding = 0, tile_attachmentQCOM, rgba32f) uniform highp readonly image2D u_DepthTex; + +layout(std140, set = 0, binding = 1) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 2) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 3) buffer writeonly TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 4) buffer writeonly TileLightCounts +{ + uint lightCounts[]; +}; + +vec3 reconstructWorldPosition(highp vec2 uv, highp float depth) +{ + highp vec4 clipSpacePos = vec4( + uv.x * 2.0 - 1.0, + (1.0 - uv.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + return (sceneInfo.viewInv * fragViewSpacePos).xyz; +} + +void main() +{ + uvec2 clusterSize = uvec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + uvec2 clusterOffset = gl_TileOffsetQCOM.xy / clusterSize; + uvec2 clusterKey = clusterOffset + gl_WorkGroupID.xy; + uint clusterIndex = clusterKey.y * sceneInfo.globalClusterCountX + clusterKey.x; + + // Tile bounds in pixels + uint clusterMinX = uint(float(clusterKey.x) * clusterSize.x); + uint clusterMinY = uint(float(clusterKey.y) * clusterSize.y); + + // Reconstruct tile frustum + highp vec3 clusterMin = vec3(1e8); + highp vec3 clusterMax = vec3(-1e8); + + for (uint y = 0; y < clusterSize.y / LOCAL_SIZE; ++y) + { + for (uint x = 0; x < clusterSize.x / LOCAL_SIZE; ++x) + { + ivec2 pixelLocation = ivec2(ivec2(clusterMinX, clusterMinY) + ivec2(clusterSize / LOCAL_SIZE) * gl_LocalInvocationID.xy) + ivec2(x, y); + + vec2 uv = vec2(float(pixelLocation.x) / float(sceneInfo.viewportWidth), + float(pixelLocation.y) / float(sceneInfo.viewportHeight)); + if(uv.x > 1.0 || uv.y > 1.0) + { + continue; + } + + highp float depth = imageLoad(u_DepthTex, pixelLocation).r * 2.0 - 1.0; + vec3 worldPos = reconstructWorldPosition(uv, depth); + + clusterMin = min(clusterMin, worldPos); + clusterMax = max(clusterMax, worldPos); + } + } + + clusterMax = subgroupMax(clusterMax); + clusterMin = subgroupMin(clusterMin); + + if ((gl_LocalInvocationID.x == 0) && (gl_LocalInvocationID.y == 0)) + { + uint tileLightCount = 0u; + for (int lightId = 0; lightId < int(sceneInfo.lightCount); ++lightId) + { + vec3 lightPosition = lights[lightId].lightPosAndRadius.xyz; + float lightRadius = lights[lightId].lightPosAndRadius.w; + + // Test light against tile bounds (AABB vs Sphere) + vec3 closestPoint = clamp(lightPosition, clusterMin, clusterMax); + float distance = length(closestPoint - lightPosition); + + if (distance < lightRadius * 2.0) + { + lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + tileLightCount] = uint(lightId); + tileLightCount++; + } + } + + lightCounts[clusterIndex] = tileLightCount; + } +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/LightCullingTileShading.json b/samples/tile_shading/shaders/LightCullingTileShading.json new file mode 100644 index 0000000..7dcc03e --- /dev/null +++ b/samples/tile_shading/shaders/LightCullingTileShading.json @@ -0,0 +1,47 @@ +{ + "$schema": "../../../framework/schema/shaderSchema.json", + "Passes": [ + { + "Name": "CP_LIGHT_CULLING_LIST", + "Shaders": { + "Compute": "Media/Shaders/LightCullingTileShading.comp.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "ImageSampled", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "SceneDepth" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "SceneInfo" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightCounts" ] + } + ] + } + ] + } + ] +} diff --git a/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.comp b/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.comp new file mode 100644 index 0000000..ca4180c --- /dev/null +++ b/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.comp @@ -0,0 +1,174 @@ +#version 460 +//============================================================================================================ +// +// +// Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#define MAX_LIGHT_COUNT 2048 +#define TILE_SHADING_RATE 8 // up to 8 is guaranteed on Andreno HW + +#extension GL_QCOM_tile_shading : enable + +layout(shading_rate_xQCOM = TILE_SHADING_RATE, shading_rate_yQCOM = TILE_SHADING_RATE, shading_rate_zQCOM = 1) in; + +struct LightUB +{ + vec4 lightPosAndRadius; // xyz = pos | w = radius + vec4 lightColor; +}; + +struct SceneInfoUB +{ + mat4 projectionInv; + mat4 view; + mat4 viewInv; + mat4 viewProjection; + mat4 viewProjectionInv; // ViewInv * ProjectionInv + vec4 projectionInvW; // w components of ProjectionInv + vec4 cameraPos; + + vec4 skyLightDirection; + vec4 skyLightColor; + + vec4 ambientColor; + + uint viewportWidth; + uint viewportHeight; + uint binCountX; + uint binCountY; + uint globalClusterCountX; + uint globalClusterCountY; + uint binClusterCountX; + uint binClusterCountY; + uint clusterSizeX; + uint clusterSizeY; + int lightCount; + int debugShaders; + int ignoreLightTiles; + int tileMemoryEnabled; + int padding1; + int padding2; +}; + +layout(set = 0, binding = 0, tile_attachmentQCOM, rgba32f) uniform highp readonly image2D u_DepthTex; + +layout(std140, set = 0, binding = 1) uniform SceneInfo +{ + SceneInfoUB sceneInfo; +}; + +layout(std140, set = 0, binding = 2) uniform Lights +{ + LightUB lights[MAX_LIGHT_COUNT]; +}; + +layout(std430, set = 0, binding = 3) buffer writeonly TileLightIndices +{ + uint lightIndices[]; +}; + +layout(std430, set = 0, binding = 4) buffer writeonly TileLightCounts +{ + uint lightCounts[]; +}; + +vec3 reconstructWorldPosition(highp vec2 uv, highp float depth) +{ + highp vec4 clipSpacePos = vec4( + uv.x * 2.0 - 1.0, + (1.0 - uv.y) * 2.0 - 1.0, + depth, + 1.0 + ); + + highp vec4 fragViewSpacePos = sceneInfo.projectionInv * clipSpacePos; + fragViewSpacePos /= fragViewSpacePos.w; + return (sceneInfo.viewInv * fragViewSpacePos).xyz; +} + +ivec2 getBinIndex() +{ + return ivec2(ceil(vec2(gl_TileOffsetQCOM) / vec2(gl_TileDimensionQCOM))); +} + +void main() +{ + uvec2 clusterSize = uvec2(sceneInfo.clusterSizeX, sceneInfo.clusterSizeY); + + uvec2 totalTileArea = gl_TileDimensionQCOM.xy/* * uvec2(sceneInfo.binCountX, sceneInfo.binCountY)*/; + uvec2 totalInvocations = totalTileArea / uvec2(TILE_SHADING_RATE, TILE_SHADING_RATE); + uvec2 currentInvocation = gl_GlobalInvocationID.xy; + + uvec2 invocationClusterCount = max(uvec2(1, 1), totalInvocations / clusterSize); + + uvec2 tileOffset = gl_TileOffsetQCOM.xy; + + for (uint x = 0; x < invocationClusterCount.x; ++x) + { + for (uint y = 0; y < invocationClusterCount.y; ++y) + { + uvec2 clusterKey = tileOffset / clusterSize + uvec2(x, y); + uint clusterIndex = clusterKey.y * sceneInfo.globalClusterCountX + clusterKey.x; + + // Tile bounds in pixels + uint clusterMinX = uint(float(clusterKey.x) * clusterSize.x); + uint clusterMinY = uint(float(clusterKey.y) * clusterSize.y); + + // Early exit if fully out of bounds + if (clusterMinX >= sceneInfo.viewportWidth || clusterMinY >= sceneInfo.viewportHeight) + { + continue; + } + + // Reconstruct tile frustum + highp vec3 clusterMin = vec3(1e8); + highp vec3 clusterMax = vec3(-1e8); + + for (uint y = 0; y < clusterSize.y; ++y) + { + for (uint x = 0; x < clusterSize.x; ++x) + { + ivec2 pixelLocation = ivec2(clusterMinX, clusterMinY) + ivec2(x, y); + + vec2 uv = vec2(float(pixelLocation.x) / float(sceneInfo.viewportWidth), + float(pixelLocation.y) / float(sceneInfo.viewportHeight)); + if(uv.x > 1.0 || uv.y > 1.0) + { + continue; + } + + highp float depth = imageLoad(u_DepthTex, pixelLocation).r * 2.0 - 1.0; + vec3 worldPos = reconstructWorldPosition(uv, depth); + + clusterMin = min(clusterMin, worldPos); + clusterMax = max(clusterMax, worldPos); + } + } + + if ((gl_LocalInvocationID.x == 0) && (gl_LocalInvocationID.y == 0)) + { + uint tileLightCount = 0u; + for (int lightId = 0; lightId < int(sceneInfo.lightCount); ++lightId) + { + vec3 lightPosition = lights[lightId].lightPosAndRadius.xyz; + float lightRadius = lights[lightId].lightPosAndRadius.w; + + // Test light against tile bounds (AABB vs Sphere) + vec3 closestPoint = clamp(lightPosition, clusterMin, clusterMax); + float distance = length(closestPoint - lightPosition); + + if (distance < lightRadius * 2.0) + { + lightIndices[clusterIndex * uint(MAX_LIGHT_COUNT) + tileLightCount] = uint(lightId); + tileLightCount++; + } + } + + lightCounts[clusterIndex] = tileLightCount; + } + } + } +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.json b/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.json new file mode 100644 index 0000000..d400adc --- /dev/null +++ b/samples/tile_shading/shaders/LightCullingTileShadingAreaDispatch.json @@ -0,0 +1,47 @@ +{ + "$schema": "../../../framework/schema/shaderSchema.json", + "Passes": [ + { + "Name": "CP_LIGHT_CULLING_LIST", + "Shaders": { + "Compute": "Media/Shaders/LightCullingTileShadingAreaDispatch.comp.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "ImageSampled", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "SceneDepth" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "SceneInfo" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightInfo" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightIndices" ] + }, + { + "Type": "StorageBuffer", + "Stages": [ "Compute" ], + "Count": 1, + "Names": [ "LightCounts" ] + } + ] + } + ] + } + ] +} diff --git a/samples/shaderResolveTonemap/shaders/Object.vert b/samples/tile_shading/shaders/Scene.vert similarity index 73% rename from samples/shaderResolveTonemap/shaders/Object.vert rename to samples/tile_shading/shaders/Scene.vert index 790d13a..d1a33d9 100644 --- a/samples/shaderResolveTonemap/shaders/Object.vert +++ b/samples/tile_shading/shaders/Scene.vert @@ -6,8 +6,6 @@ // //============================================================================================================ -// Object.vert - #version 400 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable @@ -23,12 +21,13 @@ // These start back over at 0! #define SHADER_VERT_UBO_LOCATION 0 #define SHADER_FRAG_UBO_LOCATION 1 +#define SHADER_LIGHT_UBO_LOCATION 2 -layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; +layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec4 a_Position; layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; +layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Uniform Constant Buffer layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff @@ -38,18 +37,24 @@ layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstant mat4 ShadowMatrix; } VertCB; +const mat4 biasMat = mat4( + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0 ); + // Varying's layout (location = 0) out vec2 v_TexCoord; layout (location = 1) out vec3 v_WorldPos; layout (location = 2) out vec3 v_WorldNorm; layout (location = 3) out vec3 v_WorldTan; layout (location = 4) out vec3 v_WorldBitan; -layout (location = 5) out vec4 v_VertColor; +layout (location = 5) out vec4 v_ShadowCoord; +layout (location = 6) out vec4 v_VertColor; void main() { - // Position and text coord are simple (Except Y in inverted on screen compared to OpenGL) vec4 TempPos = VertCB.MVPMatrix * vec4(a_Position.xyz, 1.0); gl_Position = vec4(TempPos.x, -TempPos.y, TempPos.z, TempPos.w); v_TexCoord = vec2(a_TexCoord.x, a_TexCoord.y); @@ -57,11 +62,16 @@ void main() // Need Position in world space v_WorldPos = (VertCB.ModelMatrix * vec4(a_Position.xyz, 1.0)).xyz; + // Get shadow texture coordinate while we have world position + // Expanded out since trying to handle shadows in reflection + v_ShadowCoord = biasMat * VertCB.ShadowMatrix * vec4(v_WorldPos.x, v_WorldPos.y, v_WorldPos.z, 1.0); + // Need Normal, Tangent, and Bitangent in world space - v_WorldNorm = (VertCB.ModelMatrix * vec4(a_Normal.xyz, 0.0)).xyz; - v_WorldTan = (VertCB.ModelMatrix * vec4(a_Tangent.xyz, 0.0)).xyz; - v_WorldBitan = cross(v_WorldNorm, v_WorldTan); + v_WorldNorm = normalize((VertCB.ModelMatrix * vec4(a_Normal.xyz, 0.0)).xyz); + v_WorldTan = normalize((VertCB.ModelMatrix * vec4(a_Tangent.xyz, 0.0)).xyz); + v_WorldBitan = normalize(cross(v_WorldNorm, v_WorldTan)); // Color is simple attribute color v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); + } diff --git a/samples/tile_shading/shaders/SceneOpaque.frag b/samples/tile_shading/shaders/SceneOpaque.frag new file mode 100644 index 0000000..00c6f0e --- /dev/null +++ b/samples/tile_shading/shaders/SceneOpaque.frag @@ -0,0 +1,93 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +// Uniform buffer locations +#define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 + +// Texture Locations +#define SHADER_DIFFUSE_TEXTURE_LOC 2 +#define SHADER_NORMAL_TEXTURE_LOC 3 +#define SHADER_EMISSIVE_TEXTURE_LOC 4 +#define SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC 5 + +#define NUM_SPOT_LIGHTS (4) + +// Uniform Constant Buffer +layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff +{ + vec4 Color; + vec4 ORM; + +} FragCB; + +#ifndef PI +#define PI (3.14159265359) +#endif + +// Textures +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; +layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; +layout(set = 0, binding = SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC) uniform sampler2D u_MetallicRoughnessTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec3 v_WorldPos; +layout (location = 2) in vec3 v_WorldNorm; +layout (location = 3) in vec3 v_WorldTan; +layout (location = 4) in vec3 v_WorldBitan; +layout (location = 5) in vec4 v_ShadowCoord; +layout (location = 6) in vec4 v_VertColor; + +// Output color +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 NormalColor; + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 LocalTexCoord = vec2(v_TexCoord.xy); + + // ******************************** + // Base (albedo) color + // ******************************** + // Get color from the color texture + + vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); + DiffuseColor.xyzw *= FragCB.Color.xyzw; + + if(DiffuseColor.a < 0.5) + { + discard; + } + + // Adjust by vertex color. + DiffuseColor.xyzw *= v_VertColor.xyzw; + + vec4 Emissive = texture( u_EmissiveTex, v_TexCoord.xy ); + vec4 MetallicRoughness = texture( u_MetallicRoughnessTex, v_TexCoord.xy ); + + // Get base normal from the bump texture + vec3 Normal = texture( u_NormalTex, v_TexCoord.xy ).rgb; + Normal = Normal * 2.0 - 1.0; + + mat3 TBN = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); + Normal = normalize(TBN * Normal); + + NormalColor = vec4(Normal, 0.0); + FragColor.rgb = DiffuseColor.rgb; + FragColor.a = DiffuseColor.a; +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/SceneOpaque.json b/samples/tile_shading/shaders/SceneOpaque.json new file mode 100644 index 0000000..12c3cb6 --- /dev/null +++ b/samples/tile_shading/shaders/SceneOpaque.json @@ -0,0 +1,102 @@ +{ + "$schema": "shaderSchema.json", + "Passes": [ + { + "Name": "RP_SCENE", + "Shaders": { + "Vertex": "Media/Shaders/Scene.vert.spv", + "Fragment": "Media/Shaders/SceneOpaque.frag.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "UniformBuffer", + "Stages": [ "Vertex" ], + "Names": [ "Vert" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Names": [ "Frag" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Diffuse" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Normal" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Emissive" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "MetallicRoughness" ] + } + ] + } + ], + "VertexBindings": [ "Position", "Attributes" ], + "FixedFunction": { + "CullBackFace": true, + "DepthTestEnable": true, + "DepthWriteEnable": true, + "DepthCompareOp": "LessEqual" + }, + "Outputs": [ + { "BlendEnable": false }, + { "BlendEnable": false } + ] + } + ], + "Vertex": [ + { + "Span": 12, + "Name": "Position", + "Elements": [ + { + "Name": "Position", + "Offset": 0, + "Type": "Vec3" + } + ] + }, + { + "Span": 48, + "Name": "Attributes", + "Elements": [ + { + "Name": "Normal", + "Offset": 0, + "Type": "Vec3" + }, + { + "Name": "UV", + "Offset": 12, + "Type": "Vec2" + }, + { + "Name": "Color", + "Offset": 20, + "Type": "Vec4" + }, + { + "Name": "Tangent", + "Offset": 36, + "Type": "Vec3" + } + ] + } + ] +} diff --git a/samples/tile_shading/shaders/SceneTransparent.frag b/samples/tile_shading/shaders/SceneTransparent.frag new file mode 100644 index 0000000..b62e0fa --- /dev/null +++ b/samples/tile_shading/shaders/SceneTransparent.frag @@ -0,0 +1,93 @@ +//============================================================================================================ +// +// +// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// +//============================================================================================================ + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +// Uniform buffer locations +#define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 + +// Texture Locations +#define SHADER_DIFFUSE_TEXTURE_LOC 2 +#define SHADER_NORMAL_TEXTURE_LOC 3 +#define SHADER_EMISSIVE_TEXTURE_LOC 4 +#define SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC 5 + +#define NUM_SPOT_LIGHTS (4) + +// Uniform Constant Buffer +layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff +{ + vec4 Color; + vec4 ORM; + +} FragCB; + +#ifndef PI +#define PI (3.14159265359) +#endif + +// Textures +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; +layout(set = 0, binding = SHADER_NORMAL_TEXTURE_LOC) uniform sampler2D u_NormalTex; +layout(set = 0, binding = SHADER_EMISSIVE_TEXTURE_LOC) uniform sampler2D u_EmissiveTex; +layout(set = 0, binding = SHADER_METALLIC_ROUGHNESS_TEXTURE_LOC) uniform sampler2D u_MetallicRoughnessTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec3 v_WorldPos; +layout (location = 2) in vec3 v_WorldNorm; +layout (location = 3) in vec3 v_WorldTan; +layout (location = 4) in vec3 v_WorldBitan; +layout (location = 5) in vec4 v_ShadowCoord; +layout (location = 6) in vec4 v_VertColor; + +// Output color +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 NormalColor; + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + vec2 LocalTexCoord = vec2(v_TexCoord.xy); + + // ******************************** + // Base (albedo) color + // ******************************** + // Get color from the color texture + + vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); + DiffuseColor.xyzw *= FragCB.Color.xyzw; + + if(DiffuseColor.a < 0.5) + { + discard; + } + + // Adjust by vertex color. + DiffuseColor.xyzw *= v_VertColor.xyzw; + + vec4 Emissive = texture( u_EmissiveTex, v_TexCoord.xy ); + vec4 MetallicRoughness = texture( u_MetallicRoughnessTex, v_TexCoord.xy ); + + // Get base normal from the bump texture + vec3 Normal = texture( u_NormalTex, v_TexCoord.xy ).rgb; + Normal = Normal * 2.0 - 1.0; + + mat3 TBN = mat3(normalize(v_WorldTan), normalize(v_WorldBitan), normalize(v_WorldNorm)); + Normal = normalize(TBN * Normal); + + // NormalColor = vec4(Normal, 0.0); // Don't write normal on transparency pass + FragColor.rgb = DiffuseColor.rgb; + FragColor.a = DiffuseColor.a; +} \ No newline at end of file diff --git a/samples/tile_shading/shaders/SceneTransparent.json b/samples/tile_shading/shaders/SceneTransparent.json new file mode 100644 index 0000000..c4b6dd1 --- /dev/null +++ b/samples/tile_shading/shaders/SceneTransparent.json @@ -0,0 +1,101 @@ +{ + "$schema": "shaderSchema.json", + "Passes": [ + { + "Name": "RP_SCENE", + "Shaders": { + "Vertex": "Media/Shaders/Scene.vert.spv", + "Fragment": "Media/Shaders/SceneTransparent.frag.spv" + }, + "DescriptorSets": [ + { + "Buffers": [ + { + "Type": "UniformBuffer", + "Stages": [ "Vertex" ], + "Names": [ "Vert" ] + }, + { + "Type": "UniformBuffer", + "Stages": [ "Fragment" ], + "Names": [ "Frag" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Diffuse" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Normal" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "Emissive" ] + }, + { + "Type": "ImageSampler", + "Stages": [ "Fragment" ], + "Count": 1, + "Names": [ "MetallicRoughness" ] + } + ] + } + ], + "VertexBindings": [ "Position", "Attributes" ], + "FixedFunction": { + "CullBackFace": true, + "DepthTestEnable": true, + "DepthWriteEnable": false + }, + "Outputs": [ + { "BlendEnable": true }, + { "BlendEnable": true } + ] + } + ], + "Vertex": [ + { + "Span": 12, + "Name": "Position", + "Elements": [ + { + "Name": "Position", + "Offset": 0, + "Type": "Vec3" + } + ] + }, + { + "Span": 48, + "Name": "Attributes", + "Elements": [ + { + "Name": "Normal", + "Offset": 0, + "Type": "Vec3" + }, + { + "Name": "UV", + "Offset": 12, + "Type": "Vec2" + }, + { + "Name": "Color", + "Offset": 20, + "Type": "Vec4" + }, + { + "Name": "Tangent", + "Offset": 36, + "Type": "Vec3" + } + ] + } + ] +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..3e432c9 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +project (samples C CXX) + +set(CMAKE_CXX_STANDARD 20) + +# From the list of projects determine which are enabled (by ConfigLocal.cmake - which sets a variable per sample) +# if a variable is not found for a sample it is assumed to be enabled. +function(add_sample_subdirectory _SAMPLE_FOLDER _SAMPLE_NAME) + if(FRAMEWORK_tests AND (NOT DEFINED FRAMEWORK_tests_${_SAMPLE_NAME} OR FRAMEWORK_tests_${_SAMPLE_NAME})) + if(NOT DEFINED FRAMEWORK_tests_${_SAMPLE_NAME}) + message(STATUS "Skipping test: " ${_SAMPLE_NAME} " (config flag not found - please add to Config.txt and re-run configuration script)") + return() + else() + message(STATUS "Adding test: " ${_SAMPLE_NAME}) + endif() + add_subdirectory(${_SAMPLE_FOLDER} ${_SAMPLE_NAME}) + else() + message(STATUS "Skipping test: " ${_SAMPLE_NAME}) + endif() +endfunction() + +# Automatically discover test projects +file(GLOB SAMPLE_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */) + +foreach(SAMPLE_DIR ${SAMPLE_DIRS}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SAMPLE_DIR}/CMakeLists.txt" OR + EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SAMPLE_DIR}/cmakelists.txt") + add_sample_subdirectory(${SAMPLE_DIR} ${SAMPLE_DIR}) + endif() +endforeach() \ No newline at end of file diff --git a/samples/empty/CMakeLists.txt b/tests/empty/CMakeLists.txt similarity index 52% rename from samples/empty/CMakeLists.txt rename to tests/empty/CMakeLists.txt index f8d630f..9dc7c04 100644 --- a/samples/empty/CMakeLists.txt +++ b/tests/empty/CMakeLists.txt @@ -10,8 +10,7 @@ set(CMAKE_CXX_STANDARD 20) set(CPP_SRC code/main/application.cpp code/main/application.hpp ) -set(FRAMEWORK_LIB framework) - +set(FRAMEWORK_LIB framework_base) # # Setup the module path to include the 'project directory' (project/windows or project/android) @@ -19,15 +18,31 @@ set(FRAMEWORK_LIB framework) if(NOT DEFINED PROJECT_ROOT_DIR) set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) endif() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake) - -# -# Add in the contents of 'shaders' directory -# -include(AddShadersDir) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) # # Do all the build steps for a Framework application. # needs Framework_dir and project_name variables. # include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() \ No newline at end of file diff --git a/samples/empty/code/main/application.cpp b/tests/empty/code/main/application.cpp similarity index 89% rename from samples/empty/code/main/application.cpp rename to tests/empty/code/main/application.cpp index d4cd3f7..54a6b91 100644 --- a/samples/empty/code/main/application.cpp +++ b/tests/empty/code/main/application.cpp @@ -1,13 +1,14 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ #include "application.hpp" #include "main/applicationEntrypoint.hpp" +#include "graphicsApi/graphicsApiBase.hpp" /// /// @brief Implementation of the Application entrypoint (called by the framework) diff --git a/samples/empty/code/main/application.hpp b/tests/empty/code/main/application.hpp similarity index 93% rename from samples/empty/code/main/application.hpp rename to tests/empty/code/main/application.hpp index 3ad31bd..c5594ef 100644 --- a/samples/empty/code/main/application.hpp +++ b/tests/empty/code/main/application.hpp @@ -1,7 +1,7 @@ //============================================================================================================ // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. +// Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. // SPDX-License-Identifier: BSD-3-Clause // //============================================================================================================ diff --git a/samples/empty/project/android/AndroidManifest.xml b/tests/empty/project/android/AndroidManifest.xml similarity index 97% rename from samples/empty/project/android/AndroidManifest.xml rename to tests/empty/project/android/AndroidManifest.xml index d445bd7..be94abe 100644 --- a/samples/empty/project/android/AndroidManifest.xml +++ b/tests/empty/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -21,7 +20,8 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> - SubPass + SGS Empty diff --git a/tests/framework_test_vulkan/CMakeLists.txt b/tests/framework_test_vulkan/CMakeLists.txt new file mode 100644 index 0000000..c7f39b9 --- /dev/null +++ b/tests/framework_test_vulkan/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required (VERSION 3.21) + +project (framework_test_vulkan C CXX) +set(CMAKE_CXX_STANDARD 20) + +# +# Source files included in this application. +# + +set(CPP_SRC code/main/application.cpp + code/main/application.hpp +) +set(FRAMEWORK_LIB framework_vulkan) + +# +# Setup the module path to include the 'project directory' (project/windows or project/android) +# +if(NOT DEFINED PROJECT_ROOT_DIR) + set(PROJECT_ROOT_DIR ${CMAKE_SOURCE_DIR}) # Windows can use CMAKE_SOURCE_DIR, Android needs build.gradle needs "-DPROJECT_ROOT_DIR=${project.rootDir}" in call to cmake set since there is not a 'top' cmakefile (gradle is top level) +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_ROOT_DIR}/cmake ${FRAMEWORK_DIR}/cmake) + +# +# Do all the build steps for a Framework application. +# needs Framework_dir and project_name variables. +# +include(FrameworkApplicationHelper) + +# +# Setup asset source and target folders +# + +# cmake will use our GameSampleAssets (default for no parameter) as root directory for any asset request (see FrameworkApplicationHelper.cmake for more info) +inject_root_asset_path() + +# Register local variables for asset request, while also defining them in the C++ code for easy access +# Here we use the default destionation paths, all defined at FrameworkApplicationHelper.cmake +register_local_asset_path(SHADER_DESTINATION "${DEFAULT_LOCAL_SHADER_DESTINATION}") +register_local_asset_path(MESH_DESTINATION "${DEFAULT_LOCAL_MESH_DESTINATION}") +register_local_asset_path(TEXTURE_DESTINATION "${DEFAULT_LOCAL_TEXTURE_DESTINATION}") + +# +# Add in the contents of 'shaders' directory +# +include(AddShadersDir) + +# Search and include all project shaders +scan_for_shaders() + +# +# Copy required models to local folders +# +include(ModelPackager) + +# Scene GLTF +add_gltf(scenes/General/UVSphere_Separate.gltf) + +# +# Convert and copy textures to local folders +# +include(TexturePackager) + +# Loose Textures +add_texture(textures/surf_d.ktx UASTC) diff --git a/tests/framework_test_vulkan/README.md b/tests/framework_test_vulkan/README.md new file mode 100644 index 0000000..5f43269 --- /dev/null +++ b/tests/framework_test_vulkan/README.md @@ -0,0 +1,15 @@ +# Framework Test Vulkan sample + +Loads a simple gltf model and texture and draws it on the screen using Vulkan API. + +## Running + +- If you haven't already, setup the framework and build the code [instructions here](../../README.md) +- Copy the relevant mesh and textures to this project's Media folder by running `02_PrepareMedia.bat` + +Windows: +- From this directory (tests\frameworkTestVulkan) execute `..\..\project\windows\solution\tests\frameworkTestVulkan\Debug\FrameworkTest.exe` +- Or run from the Microsoft Visual Studio solution `..\..\project\windows\solution\vkSampleFramework.sln` + +Android: +- Once the apk is built [instructions here](../../README.md) the script `04_Install_APK.bat` will install the apk on your connected Android device. diff --git a/tests/framework_test_vulkan/app_config.txt b/tests/framework_test_vulkan/app_config.txt new file mode 100644 index 0000000..3a41ceb --- /dev/null +++ b/tests/framework_test_vulkan/app_config.txt @@ -0,0 +1,9 @@ +// ************************************ +// General Settings +// ************************************ +gSurfaceWidth = 1280 +gSurfaceHeight = 720 + +gRenderWidth = 1280 +gRenderHeight = 720 +gMsaaSamples = 1 diff --git a/tests/framework_test_vulkan/code/main/Materials.h b/tests/framework_test_vulkan/code/main/Materials.h new file mode 100644 index 0000000..c7c53fc --- /dev/null +++ b/tests/framework_test_vulkan/code/main/Materials.h @@ -0,0 +1,87 @@ +#pragma once + +#include "vulkan/vulkan.hpp" + +#include "system/os_common.h" + +// GLM Include Files +#define GLmFORCE_CXX03 +#define GLmDEPTH_ZERO_TO_ONE +#include +#include +#include + +class ShaderInfo; +template class TextureT; + +//============================================================================= +// Uniform Buffers +//============================================================================= +// ************************************ +// Test +// ************************************ +typedef struct _TestVertUB +{ + glm::mat4 MVPMatrix; + glm::mat4 ModelMatrix; +} TestVertUB; + +typedef struct _TestFragUB +{ + glm::vec4 Color; + glm::vec4 EyePos; + glm::vec4 LightDir; + glm::vec4 LightColor; +} TestFragUB; + + + +//============================================================================= +// MaterialVk Description +//============================================================================= + +typedef struct _MaterialVk +{ + // Depth Test/Write + VkBool32 DepthTestEnable; + VkBool32 DepthWriteEnable; + + // Depth Bias + VkBool32 DepthBiasEnable; + float DepthBiasConstant; + float DepthBiasClamp; + float DepthBiasSlope; + + // Culling + VkCullModeFlagBits CullMode; + + // The Shader (Use references since we are not cleaning anything up) + ShaderInfo* pShader; + + // Textures (Use references since we are not cleaning anything up) + Texture* pColorTexture; + Texture* pNormalTexture; + Texture* pShadowDepthTexture; + Texture* pShadowColorTexture; + Texture* pEnvironmentCubeMap; + Texture* pIrradianceCubeMap; + Texture* pReflectTexture; + + // Constant Buffers + uint32_t VertUniformOffset; + uint32_t VertUniformLength; + Uniform* pVertUniform; + + uint32_t FragUniformOffset; + uint32_t FragUniformLength; + Uniform* pFragUniform; + + // Vulkan Objects + VkDescriptorPool DescPool; + VkDescriptorSet DescSet; + + VkDescriptorSetLayout DescLayout; + VkPipelineLayout PipelineLayout; + + VkPipeline Pipeline; +} MaterialVk; diff --git a/tests/framework_test_vulkan/code/main/application.cpp b/tests/framework_test_vulkan/code/main/application.cpp new file mode 100644 index 0000000..1201cf5 --- /dev/null +++ b/tests/framework_test_vulkan/code/main/application.cpp @@ -0,0 +1,1007 @@ +//============================================================================= +// +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +#include "application.hpp" +#include "main/applicationEntrypoint.hpp" +#include "material/materialProps.h" +#include "system/math_common.hpp" +#include "system/os_common.h" +#include "texture/vulkan/loaderKtx.hpp" +#include "vulkan/vulkan_support.hpp" +#include + +glm::vec4 gClearColor = glm::vec4(0.3f, 0.3f, 0.3f, 1.0f); + +VAR( char*, gSceneAssetModel, "UVSphere_Separate.gltf", kVariableNonpersistent ); + +// The vertex buffer bind id, used as a constant in various places in the sample +#define VERTEX_BUFFER_BIND_ID 0 + +// These are local defines that need to match what is in the shader +#define SHADER_DIFFUSE_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 0) +#define SHADER_NORMAL_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 1) +#define SHADER_SHADOW_DEPTH_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 2) +#define SHADER_SHADOW_COLOR_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 3) +#define SHADER_ENVIRONMENT_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 4) +#define SHADER_IRRADIANCE_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 5) +#define SHADER_REFLECT_TEXTURE_LOC (SHADER_BASE_TEXTURE_LOC + 6) +#define NUM_SHADER_TEXTURE_LOCATIONS (7) + +// +// Implementation of the Application entrypoint (called by the framework) +// Construct the Application class +// +//----------------------------------------------------------------------------- +FrameworkApplicationBase* Application_ConstructApplication() +//----------------------------------------------------------------------------- +{ + return new Application(); +} + +//----------------------------------------------------------------------------- +Application::Application() : ApplicationHelperBase(), m_TestTexture{} +//----------------------------------------------------------------------------- +{ + // Object rotation + m_TotalRotation = 0.0f; + m_RotationSpeed = 0.5f; // Radians per second + + // Camera + m_CurrentCameraPos = glm::vec3(0.0f, 2.0f, 5.5f); + m_CurrentCameraLook = glm::vec3(0.0f, 1.0f, 0.0f); + + // The Test Object + m_ObjectScale = 1.0f; + m_ObjectWorldPos = glm::vec3(0.0f, 1.25f, 0.0f); +} + +//----------------------------------------------------------------------------- +Application::~Application() +//----------------------------------------------------------------------------- +{ +} + +//----------------------------------------------------------------------------- +bool Application::Initialize( uintptr_t windowHandle, uintptr_t instanceHandle) +//----------------------------------------------------------------------------- +{ + if (!ApplicationHelperBase::Initialize(windowHandle, instanceHandle)) + { + return false; + } + + if (!LoadMeshObjects()) + return false; + + if (!LoadTextures()) + return false; + + if (!LoadShaders()) + return false; + + if (!InitUniforms()) + return false; + + if (!InitMaterials()) + return false; + + if (!InitRenderPass()) + return false; + + if (!InitPipelines()) + return false; + + if (!InitCommandBuffers()) + return false; + + if (!BuildCmdBuffers()) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +void Application::Destroy() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + // Command Buffers + for(auto& CommandBuffer: m_CommandBuffers) + CommandBuffer.Release(); + + // Meshes + m_TestMesh.Destroy(); + + // Textures + ReleaseTexture(*pVulkan, &m_TestTexture); + + // Shaders + ReleaseShader(pVulkan, &m_TestShader); + + // Uniform Buffers + ReleaseUniformBuffer(pVulkan, &m_TestVertUniform); + ReleaseUniformBuffer(pVulkan, &m_TestFragUniform); + + // Finally call into base class destroy + ApplicationHelperBase::Destroy(); +} + +//----------------------------------------------------------------------------- +void Application::Render(float fltDiffTime) +//----------------------------------------------------------------------------- +{ + // LOGI("Render() Entered..."); + + // Grab the vulkan wrapper + Vulkan* pVulkan = GetVulkan(); + + // Obtain the next swap chain image for the next frame. + auto currentBuffer = pVulkan->SetNextBackBuffer(); + + // ******************************** + // Application Draw() - Begin + // ******************************** + + // Update uniform buffers with latest data + UpdateUniforms(fltDiffTime); + + m_CommandBuffers[currentBuffer.idx].QueueSubmit(currentBuffer.semaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, pVulkan->m_RenderCompleteSemaphore, currentBuffer.fence); + + // ******************************** + // Application Draw() - End + // ******************************** + + pVulkan->PresentQueue( pVulkan->m_RenderCompleteSemaphore, currentBuffer.swapchainPresentIdx ); +} + +//----------------------------------------------------------------------------- +void Application::TouchDownEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + LOGI("TouchDownEvent(%0.2f, %0.2f) Entered...", xPos, yPos); +} + +//----------------------------------------------------------------------------- +void Application::TouchMoveEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + // LOGI("TouchMoveEvent(%0.2f, %0.2f) Entered...", xPos, yPos); +} + +//----------------------------------------------------------------------------- +void Application::TouchUpEvent(int iPointerID, float xPos, float yPos) +//----------------------------------------------------------------------------- +{ + LOGI("TouchUpEvent(%0.2f, %0.2f) Entered...", xPos, yPos); +} + +//----------------------------------------------------------------------------- +bool Application::LoadMeshObjects() +//----------------------------------------------------------------------------- +{ + const auto sceneAssetPath = std::filesystem::path(MESH_DESTINATION_PATH).append(gSceneAssetModel).string(); + LOGI("Loading glTF mesh: %s...", sceneAssetPath.c_str()); + if (!LoadGLTF(sceneAssetPath, VERTEX_BUFFER_BIND_ID, &m_TestMesh)) + { + LOGE("Error loading Object mesh: %s", sceneAssetPath.c_str()); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::LoadTextures() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + TextureKtx textureLoader { *pVulkan }; + if (!textureLoader.Initialize()) + return false; + + const auto textureAssetPath = std::filesystem::path(TEXTURE_DESTINATION_PATH).append("surf_d.ktx").string(); + m_TestTexture = textureLoader.LoadKtx(*pVulkan, *m_AssetManager, textureAssetPath.c_str(), m_SamplerRepeat.Copy()); + return !m_TestTexture.IsEmpty(); +} + +//----------------------------------------------------------------------------- +bool Application::LoadShaders() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + LOGI("Loading Test shader..."); + auto debugVertFilename = std::filesystem::path(SHADER_DESTINATION_PATH).append( "Debug.vert.spv").string(); + auto debugFragFilename = std::filesystem::path(SHADER_DESTINATION_PATH).append( "Debug.frag.spv").string(); + LoadShader(pVulkan, *m_AssetManager, &m_TestShader, debugVertFilename.c_str(), debugFragFilename.c_str()); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::InitUniforms() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + // These are only created here, they are not set to initial values + LOGI("Creating uniform buffers..."); + CreateUniformBuffer(pVulkan, &m_TestVertUniform, sizeof(m_TestVertUniformData), NULL); + CreateUniformBuffer(pVulkan, &m_TestFragUniform, sizeof(m_TestFragUniformData), NULL); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::UpdateUniforms(float fltDiffTime) +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + // Projection Matrix + float aspect = (float)gRenderWidth / (float)gRenderHeight; + + // These are labeled as global because they should go to config file + float gFOV = PI_DIV_4; + float gNearPlane = 1.0f; + float gFarPlane = 100.0f; + + m_ProjectionMatrix = glm::perspectiveRH(gFOV, aspect, gNearPlane, gFarPlane); + + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + m_ViewMatrix = glm::lookAtRH(m_CurrentCameraPos, m_CurrentCameraLook, up); + + // Handle object rotation + m_TotalRotation += m_RotationSpeed * fltDiffTime; + if (m_TotalRotation > PI_MUL_2) + m_TotalRotation -= PI_MUL_2; + + + glm::mat4 OneModel = glm::mat4(1.0f); + + // ******************************** + // Test Vert Uniform + // ******************************** + OneModel = glm::translate(glm::mat4(1.0f), m_ObjectWorldPos); + OneModel = glm::scale(OneModel, glm::vec3(m_ObjectScale, m_ObjectScale, m_ObjectScale)); + OneModel = glm::rotate(OneModel, m_TotalRotation, glm::vec3(0.0f, 1.0f, 0.0f)); + + m_TestVertUniformData.MVPMatrix = m_ProjectionMatrix * m_ViewMatrix * OneModel; + m_TestVertUniformData.ModelMatrix = OneModel; + UpdateUniformBuffer(pVulkan, &m_TestVertUniform, sizeof(m_TestVertUniformData), &m_TestVertUniformData); + + // ******************************** + // Test Frag Uniform + // ******************************** + // These are labeled as global because they should go to config file + + float gSpecularExponent = 256.0f; + glm::vec4 gLightDirection = glm::vec4(-0.5f, -1.0f, -1.0f, 0.0); + + m_TestFragUniformData.Color = glm::vec4(0.9f, 0.9f, 0.9f, 1.0f); // White by default + m_TestFragUniformData.EyePos = glm::vec4(m_CurrentCameraPos.x, m_CurrentCameraPos.y, m_CurrentCameraPos.z, 1.0f); + m_TestFragUniformData.LightDir = normalize(gLightDirection); + m_TestFragUniformData.LightColor = glm::vec4(1.0f, 1.0f, 1.0f, gSpecularExponent); // White by default; + UpdateUniformBuffer(pVulkan, &m_TestFragUniform, sizeof(m_TestFragUniformData), &m_TestFragUniformData); + + return true; +} + +//----------------------------------------------------------------------------- +bool Application::BuildCmdBuffers() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + LOGI("Creating %d command buffers...", pVulkan->m_SwapchainImageCount); + + for (uint32_t WhichImage = 0; WhichImage < pVulkan->m_SwapchainImageCount; WhichImage++) + { + auto& CmdBuffer = m_CommandBuffers[WhichImage]; + + // Begin should reset the command buffer, but Reset can be called + // to make it more explicit. + if (!CmdBuffer.Reset()) + { + return false; + } + + // By calling Begin, CmdBuffer is put into the recording state. + if (!CmdBuffer.Begin()) + { + return false; + } + + // When starting the render pass, we can set clear values. + VkClearColorValue ClearColor[1] = {}; + ClearColor[0].float32[0] = gClearColor.x; + ClearColor[0].float32[1] = gClearColor.y; + ClearColor[0].float32[2] = gClearColor.z; + ClearColor[0].float32[3] = gClearColor.w; + + VkViewport Viewport = {}; + Viewport.width = (float)pVulkan->m_SurfaceWidth; + Viewport.height = (float)pVulkan->m_SurfaceHeight; + Viewport.minDepth = 0.0f; + Viewport.maxDepth = 1.0f; + VkRect2D Scissor = {}; + Scissor.offset.x = 0; + Scissor.offset.y = 0; + Scissor.extent.width = pVulkan->m_SurfaceWidth; + Scissor.extent.height = pVulkan->m_SurfaceHeight; + + CmdBuffer.BeginRenderPass(Scissor, 0.0f, 1.0f, ClearColor, 1, true, pVulkan->m_SwapchainRenderPass, true/*swapchain*/, pVulkan->GetSwapchainFramebuffer(WhichImage).m_FrameBuffer, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdSetViewport(CmdBuffer, 0, 1, &Viewport); + vkCmdSetScissor(CmdBuffer, 0, 1, &Scissor); + + // TODO: Add other mesh renders to the command buffer + AddMeshRenderToCmdBuffer(CmdBuffer, &m_TestMesh, &m_TestMaterial); + + // Now our render pass has ended. + CmdBuffer.EndRenderPass(); + + // By ending the command buffer, it is put out of record mode. + if (!CmdBuffer.End()) + { + return false; + } + } // WhichImage + + return true; +} + +//----------------------------------------------------------------------------- +void Application::AddMeshRenderToCmdBuffer(CommandListVulkan& cmdBuffer, Mesh* pMesh, MaterialVk* pMaterial) +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + LOGI("AddMeshRenderToCmdBuffer() Entered..."); + + // Bind the pipeline for this material + vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pMaterial->Pipeline); + + // Bind everything the shader needs + vkCmdBindDescriptorSets(cmdBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pMaterial->PipelineLayout, + 0, + 1, + &pMaterial->DescSet, + 0, + NULL); + + // Bind our mesh vertex buffer + VkDeviceSize offsets[1] = { 0 }; + assert(pMesh->m_VertexBuffers.size() == 1); + const VkBuffer vertexBuffer[1]{ pMesh->m_VertexBuffers[0].GetVkBuffer() }; + vkCmdBindVertexBuffers(cmdBuffer, + VERTEX_BUFFER_BIND_ID, + 1, + vertexBuffer, + offsets); + + // Everything is set up, draw the mesh + LOGI(" pMesh->GetNumVertices() = %d", (int)pMesh->m_NumVertices); + vkCmdDraw(cmdBuffer, pMesh->m_NumVertices, 1, 0, 0); + +} + +//----------------------------------------------------------------------------- +bool Application::InitMaterials() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + // ******************************** + // Test MaterialBase + // ******************************** + LOGI("Setting up Test material..."); + memset(&m_TestMaterial, 0, sizeof(m_TestMaterial)); + + // Depth Test/Write + m_TestMaterial.DepthTestEnable = VK_TRUE; + m_TestMaterial.DepthWriteEnable = VK_TRUE; + + // Depth Bias + m_TestMaterial.DepthBiasEnable = VK_FALSE; + + // Culling + m_TestMaterial.CullMode = VK_CULL_MODE_BACK_BIT; + + // The Shader (Use references since we are not cleaning anything up) + m_TestMaterial.pShader = &m_TestShader; + + // Textures (Use references since we are not cleaning anything up) + m_TestMaterial.pColorTexture = &m_TestTexture; + m_TestMaterial.pNormalTexture = NULL; + m_TestMaterial.pShadowDepthTexture = NULL; + m_TestMaterial.pShadowColorTexture = NULL; + m_TestMaterial.pEnvironmentCubeMap = NULL; + m_TestMaterial.pIrradianceCubeMap = NULL; + m_TestMaterial.pReflectTexture = NULL; + + // Constant Buffers + m_TestMaterial.VertUniformOffset = 0; + m_TestMaterial.VertUniformLength = sizeof(m_TestVertUniformData); + m_TestMaterial.pVertUniform = &m_TestVertUniform; + + m_TestMaterial.FragUniformOffset = 0; + m_TestMaterial.FragUniformLength = sizeof(m_TestFragUniformData); + m_TestMaterial.pFragUniform = &m_TestFragUniform; + + // Initialze Vulkan objects for this material + InitOneLayout(&m_TestMaterial); + InitOneDescriptorSet(&m_TestMaterial); + // Pipeline for this material will be created later (After renderpass is created) + + + return true; +} + +//----------------------------------------------------------------------------- +void Application::InitOneLayout(MaterialVk* pMaterial) +//----------------------------------------------------------------------------- +{ + VkResult RetVal = VK_SUCCESS; + Vulkan* pVulkan = GetVulkan(); + + // This layout needs to match one set for vkUpdateDescriptorSets + + // Used as scratch for setting them up below. Just make large enough to play with + uint32_t WhichLayout = 0; + std::array BindingLayouts = {}; + + // The only thing that changes on this structure is the number of valid bindings + VkDescriptorSetLayoutCreateInfo LayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + LayoutInfo.pNext = NULL; + LayoutInfo.flags = 0; + LayoutInfo.bindingCount = 0; // Changed by each instance below + LayoutInfo.pBindings = BindingLayouts.data(); + + // The only thing that changes on this structure is the pointer to the layout + VkPipelineLayoutCreateInfo PipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + PipelineLayoutInfo.pNext = NULL; + PipelineLayoutInfo.setLayoutCount = 1; // Should never change from 1 + PipelineLayoutInfo.pSetLayouts = NULL; // Changed by each instance below + + // Always a Vert Buffer + BindingLayouts[WhichLayout].binding = SHADER_VERT_UBO_LOCATION; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + WhichLayout++; + + // Always a Frag Buffer + BindingLayouts[WhichLayout].binding = SHADER_FRAG_UBO_LOCATION; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + + // Color Texture + if (pMaterial->pColorTexture) + { + BindingLayouts[WhichLayout].binding = SHADER_DIFFUSE_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Normal Texture + if (pMaterial->pNormalTexture) + { + BindingLayouts[WhichLayout].binding = SHADER_NORMAL_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Shadow Depth Texture + if (pMaterial->pShadowDepthTexture) + { + BindingLayouts[WhichLayout].binding = SHADER_SHADOW_DEPTH_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Shadow Color Texture + if (pMaterial->pShadowColorTexture) + { + BindingLayouts[WhichLayout].binding = SHADER_SHADOW_COLOR_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Environment Cubemap Texture + if (pMaterial->pEnvironmentCubeMap) + { + BindingLayouts[WhichLayout].binding = SHADER_ENVIRONMENT_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Irradiance Cubemap Texture + if (pMaterial->pIrradianceCubeMap) + { + BindingLayouts[WhichLayout].binding = SHADER_IRRADIANCE_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // Reflection Texture + if (pMaterial->pReflectTexture) + { + BindingLayouts[WhichLayout].binding = SHADER_REFLECT_TEXTURE_LOC; + BindingLayouts[WhichLayout].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + BindingLayouts[WhichLayout].descriptorCount = 1; + BindingLayouts[WhichLayout].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + WhichLayout++; + } + + // How many layouts did we end up with? + LayoutInfo.bindingCount = WhichLayout; + + // Update the layout info structure and create the layout + RetVal = vkCreateDescriptorSetLayout(pVulkan->m_VulkanDevice, &LayoutInfo, NULL, &pMaterial->DescLayout); + if (!CheckVkError("vkCreateDescriptorSetLayout()", RetVal)) + { + return; + } + + // Update the other layout structure and create the pipeline layout + PipelineLayoutInfo.pSetLayouts = &pMaterial->DescLayout; + RetVal = vkCreatePipelineLayout(pVulkan->m_VulkanDevice, &PipelineLayoutInfo, NULL, &pMaterial->PipelineLayout); + if (!CheckVkError("vkCreatePipelineLayout()", RetVal)) + { + return; + } +} + +//----------------------------------------------------------------------------- +void Application::InitOneDescriptorSet(MaterialVk* pMaterial) +//----------------------------------------------------------------------------- +{ + // This layout must match what is in vkCreateDescriptorSetLayout + + VkResult RetVal = VK_SUCCESS; + Vulkan* pVulkan = GetVulkan(); + + // Used as scratch for setting them up below. Just make large enough to play with + uint32_t PoolCount = 0; + std::array PoolSize = {}; + + // Need uniform buffers for Vert and Frag shaders + PoolSize[PoolCount].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + PoolSize[PoolCount].descriptorCount = NUM_VULKAN_BUFFERS * 2; // Need for each buffer (Vert + Frag) * NUM_VULKAN_BUFFERS + PoolCount++; + + // Need SSBO for vert and frag + PoolSize[PoolCount].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + PoolSize[PoolCount].descriptorCount = NUM_VULKAN_BUFFERS * 2; // Need for each buffer (Vert + Frag) * NUM_VULKAN_BUFFERS + PoolCount++; + + // Need combined image samplers for each possible texture + PoolSize[PoolCount].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + PoolSize[PoolCount].descriptorCount = NUM_VULKAN_BUFFERS * NUM_SHADER_TEXTURE_LOCATIONS; // Need for each buffer + PoolCount++; + + VkDescriptorPoolCreateInfo PoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + PoolInfo.pNext = NULL; + PoolInfo.flags = 0; // VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT to allow them to be returned + PoolInfo.maxSets = NUM_VULKAN_BUFFERS; // Since descriptor sets come out of this pool we need more than one + PoolInfo.poolSizeCount = PoolCount; + PoolInfo.pPoolSizes = PoolSize.data(); + + RetVal = vkCreateDescriptorPool(pVulkan->m_VulkanDevice, &PoolInfo, NULL, &pMaterial->DescPool); + if (!CheckVkError("vkCreateDescriptorPool()", RetVal)) + { + return; + } + + // **************************************************** + // Allocate the descriptor set + // **************************************************** + VkDescriptorSetAllocateInfo DescSetInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + DescSetInfo.pNext = NULL; + DescSetInfo.descriptorPool = pMaterial->DescPool; + DescSetInfo.descriptorSetCount = 1; + DescSetInfo.pSetLayouts = &pMaterial->DescLayout; + + RetVal = vkAllocateDescriptorSets(pVulkan->m_VulkanDevice, &DescSetInfo, &pMaterial->DescSet); + if (!CheckVkError("vkAllocateDescriptorSets()", RetVal)) + { + return; + } + + UpdateOneDescriptorSet(pMaterial); +} + +//----------------------------------------------------------------------------- +void Application::UpdateOneDescriptorSet(MaterialVk* pMaterial) +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + uint32_t DescCount = 0; + + LOGI("Updating Descriptor Set:"); + + std::array DescImageInfo = {}; + + std::array WriteInfo = {}; + + // Reset structures used to update descriptor sets + DescCount = 0; + + // Always a Vert Buffer + LOGI(" Contains Vertex Buffer"); + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_VERT_UBO_LOCATION; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + WriteInfo[DescCount].pImageInfo = NULL; + WriteInfo[DescCount].pBufferInfo = &pMaterial->pVertUniform->bufferInfo; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + + // Always a Frag Buffer + LOGI(" Contains Fragment Buffer"); + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_FRAG_UBO_LOCATION; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + WriteInfo[DescCount].pImageInfo = NULL; + WriteInfo[DescCount].pBufferInfo = &pMaterial->pFragUniform->bufferInfo; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + + // Color Texture + if (pMaterial->pColorTexture) + { + LOGI(" Contains Diffuse Texture"); + DescImageInfo[DescCount] = pMaterial->pColorTexture->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_DIFFUSE_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Normal Texture + if (pMaterial->pNormalTexture) + { + LOGI(" Contains Normal Texture"); + DescImageInfo[DescCount] = pMaterial->pNormalTexture->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_NORMAL_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Shadow Depth Texture + if (pMaterial->pShadowDepthTexture) + { + LOGI(" Contains Shadow Depth Texture"); + DescImageInfo[DescCount] = pMaterial->pShadowDepthTexture->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_SHADOW_DEPTH_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Shadow Color Texture + if (pMaterial->pShadowColorTexture) + { + LOGI(" Contains Shadow Color Texture"); + DescImageInfo[DescCount] = pMaterial->pShadowColorTexture->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_SHADOW_COLOR_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Environment Cubemap Texture + if (pMaterial->pEnvironmentCubeMap) + { + LOGI(" Contains Environment Texture"); + DescImageInfo[DescCount] = pMaterial->pEnvironmentCubeMap->GetVkDescriptorImageInfo(); + + // DescImageInfo[DescCount].sampler = mTexEnvironment.GetSampler(); + // DescImageInfo[DescCount].imageView = mTexEnvironment.GetView(); + // DescImageInfo[DescCount].imageLayout = mTexEnvironment.GetLayout(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_ENVIRONMENT_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Irradiance Cubemap Texture + if (pMaterial->pIrradianceCubeMap) + { + LOGI(" Contains Irradiance Texture"); + DescImageInfo[DescCount] = pMaterial->pIrradianceCubeMap->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_IRRADIANCE_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + // Reflection Texture + if (pMaterial->pReflectTexture) + { + LOGI(" Contains Reflect Texture"); + DescImageInfo[DescCount] = pMaterial->pReflectTexture->GetVkDescriptorImageInfo(); + + WriteInfo[DescCount].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteInfo[DescCount].pNext = NULL; + WriteInfo[DescCount].dstSet = pMaterial->DescSet; + WriteInfo[DescCount].dstBinding = SHADER_REFLECT_TEXTURE_LOC; + WriteInfo[DescCount].dstArrayElement = 0; + WriteInfo[DescCount].descriptorCount = 1; + WriteInfo[DescCount].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + WriteInfo[DescCount].pImageInfo = &DescImageInfo[DescCount]; + WriteInfo[DescCount].pBufferInfo = NULL; + WriteInfo[DescCount].pTexelBufferView = NULL; + DescCount++; + } + + LOGI("Updating Descriptor Set with %d objects", DescCount); + vkUpdateDescriptorSets(pVulkan->m_VulkanDevice, DescCount, WriteInfo.data(), 0, NULL); + + LOGI("Descriptor Set Updated!"); +} + +//----------------------------------------------------------------------------- +bool Application::InitRenderPass() +//----------------------------------------------------------------------------- +{ + VkResult RetVal = VK_SUCCESS; + Vulkan* pVulkan = GetVulkan(); + + // The renderpass defines the attachments to the framebuffer object that gets + // used in the pipelines. We have two attachments, the colour buffer, and the + // depth buffer. The operations and layouts are set to defaults for this type + // of attachment. + VkAttachmentDescription attachmentDescriptions[2] = {}; + attachmentDescriptions[0].flags = 0; + attachmentDescriptions[0].format = TextureFormatToVk( pVulkan->m_SurfaceFormat ); + attachmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + attachmentDescriptions[1].flags = 0; + attachmentDescriptions[1].format = TextureFormatToVk( pVulkan->m_SwapchainDepth.format ); + attachmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // We have references to the attachment offsets, stating the layout type. + VkAttachmentReference colorReference = {}; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 1; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // There can be multiple subpasses in a renderpass, but this example has only one. + // We set the color and depth references at the grahics bind point in the pipeline. + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.flags = 0; + subpassDescription.inputAttachmentCount = 0; + subpassDescription.pInputAttachments = nullptr; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + subpassDescription.pResolveAttachments = nullptr; + subpassDescription.pDepthStencilAttachment = &depthReference; + subpassDescription.preserveAttachmentCount = 0; + subpassDescription.pPreserveAttachments = nullptr; + + // Dependencies + VkSubpassDependency dependencies[2]; + // dependencies[0] is an acquire dependency + // dependencies[1] is a present dependency + // We use subpass dependencies to define the color image layout transitions rather than + // explicitly do them in the command buffer, as it is more efficient to do it this way. + + // Before we can use the back buffer from the swapchain, we must change the + // image layout from the PRESENT mode to the COLOR_ATTACHMENT mode. + dependencies[0] = {}; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = 0; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + // After writing to the back buffer of the swapchain, we need to change the + // image layout from the COLOR_ATTACHMENT mode to the PRESENT mode which + // is optimal for sending to the screen for users to see the completed rendering. + dependencies[1] = {}; + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = 0; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + // The renderpass itself is created with the number of subpasses, and the + // list of attachments which those subpasses can reference. + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.pNext = nullptr; + renderPassCreateInfo.attachmentCount = 2; + renderPassCreateInfo.pAttachments = attachmentDescriptions; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpassDescription; + renderPassCreateInfo.dependencyCount = 2; + renderPassCreateInfo.pDependencies = dependencies; + + RetVal = vkCreateRenderPass(pVulkan->m_VulkanDevice, &renderPassCreateInfo, nullptr, &m_RenderPass); + if (!CheckVkError("vkCreateRenderPass()", RetVal)) + { + return false; + } + + return true; + +} + +//----------------------------------------------------------------------------- +bool Application::InitPipelines() +//----------------------------------------------------------------------------- +{ + Vulkan* pVulkan = GetVulkan(); + + VkPipelineVertexInputStateCreateInfo visci = {}; + + // Common to all pipelines + // State for rasterization, such as polygon fill mode is defined. + VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_BACK_BIT; + rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rs.depthClampEnable = VK_FALSE; + rs.rasterizerDiscardEnable = VK_FALSE; + rs.depthBiasEnable = VK_FALSE; + rs.lineWidth = 1.0f; + + // For the cube, we don't write or check depth + VkPipelineDepthStencilStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + ds.depthTestEnable = VK_TRUE; + ds.depthWriteEnable = VK_TRUE; + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + ds.depthBoundsTestEnable = VK_FALSE; + ds.back.failOp = VK_STENCIL_OP_KEEP; + ds.back.passOp = VK_STENCIL_OP_KEEP; + ds.back.compareOp = VK_COMPARE_OP_ALWAYS; + ds.stencilTestEnable = VK_FALSE; + ds.front = ds.back; + + + // ******************************** + // Test Object + // ******************************** + rs.cullMode = m_TestMaterial.CullMode; + rs.depthBiasEnable = m_TestMaterial.DepthBiasEnable; + ds.depthTestEnable = m_TestMaterial.DepthTestEnable; + ds.depthWriteEnable = m_TestMaterial.DepthWriteEnable; + + LOGI("Creating Test Object PipelineState and Pipeline..."); + visci = m_TestMesh.m_VertexBuffers[0].CreatePipelineState(); + return GetVulkan()->CreatePipeline(VK_NULL_HANDLE, + &visci, + m_TestMaterial.PipelineLayout, + Vulkan::RenderContext(RenderPass(pVulkan->m_VulkanDevice, m_RenderPass), Framebuffer(), ""), + &rs, + &ds, + nullptr, + nullptr, + nullptr, + {}, + nullptr, + nullptr, + VK_NULL_HANDLE,//task shader + VK_NULL_HANDLE,//mesh shader + m_TestShader.VertShaderModule.GetVkShaderModule(), + m_TestShader.FragShaderModule.GetVkShaderModule(), + nullptr, + false, + VK_NULL_HANDLE, + &m_TestMaterial.Pipeline); +} + +//----------------------------------------------------------------------------- +bool Application::InitCommandBuffers() +//----------------------------------------------------------------------------- +{ + for (auto& CommandBuffer : m_CommandBuffers) + { + if (!CommandBuffer.Initialize(GetVulkan())) + return false; + } + return true; +} diff --git a/tests/framework_test_vulkan/code/main/application.hpp b/tests/framework_test_vulkan/code/main/application.hpp new file mode 100644 index 0000000..ebd4c5c --- /dev/null +++ b/tests/framework_test_vulkan/code/main/application.hpp @@ -0,0 +1,102 @@ +//============================================================================= +// +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== +#pragma once + +/// +/// @file FrameTestApp.hpp +/// @brief Application demonstrating use of Framework to load and render a simple object. +/// + +#include "main/applicationHelperBase.hpp" + +#include "memory/vulkan/indexBufferObject.hpp" +#include "memory/vulkan/uniform.hpp" +#include "memory/vulkan/vertexBufferObject.hpp" +#include "mesh/mesh.hpp" +#include "texture/vulkan/texture.hpp" +#include "vulkan/vulkan.hpp" +#include "vulkan/commandBuffer.hpp" +#include "vulkan/vulkan_support.hpp" + +// MaterialBase Descriptions +#include "Materials.h" + + +class Application : public ApplicationHelperBase +{ +public: + Application(); + ~Application() override; + + // Override FrameworkApplicationBase + bool Initialize(uintptr_t windowHandle, uintptr_t instanceHandle) override; + void Destroy() override; + + void Render(float fltDiffTime) override; + + void TouchDownEvent(int iPointerID, float xPos, float yPos) override; + void TouchMoveEvent(int iPointerID, float xPos, float yPos) override; + void TouchUpEvent(int iPointerID, float xPos, float yPos) override; + + bool LoadMeshObjects(); + bool LoadTextures(); + bool LoadShaders(); + bool InitUniforms(); + bool UpdateUniforms(float fltDiffTime); + bool InitMaterials(); + void InitOneLayout(MaterialVk* pMaterial); + void InitOneDescriptorSet(MaterialVk* pMaterial); + void UpdateOneDescriptorSet(MaterialVk* pMaterial); + bool InitRenderPass(); + bool InitPipelines(); + bool InitCommandBuffers(); + + bool BuildCmdBuffers(); + void AddMeshRenderToCmdBuffer(CommandListVulkan& cmdBuffer, Mesh* pMesh, MaterialVk* pMaterial); + + +private: + // Meshes + Mesh m_TestMesh; + + // Textures + TextureVulkan m_TestTexture; + + // Shaders + ShaderInfo m_TestShader; + + // Uniforms + UniformVulkan m_TestVertUniform; + TestVertUB m_TestVertUniformData; + UniformVulkan m_TestFragUniform; + TestFragUB m_TestFragUniformData; + + MaterialVk m_TestMaterial; + + // Object rotation + float m_TotalRotation; + float m_RotationSpeed; + + // Camera + glm::vec3 m_CurrentCameraPos; + glm::vec3 m_CurrentCameraLook; + + // Matrices + glm::mat4 m_ProjectionMatrix; + glm::mat4 m_ViewMatrix; + + // The Test Object + float m_ObjectScale; + glm::vec3 m_ObjectWorldPos; + + // Render Pass + VkRenderPass m_RenderPass; + + // Command Buffers + std::array m_CommandBuffers; +}; diff --git a/tests/framework_test_vulkan/install_apk.bat b/tests/framework_test_vulkan/install_apk.bat new file mode 100644 index 0000000..62ab3c4 --- /dev/null +++ b/tests/framework_test_vulkan/install_apk.bat @@ -0,0 +1,21 @@ +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +@echo. +@echo **************************************** +@echo Installing APK for project: %project_name% +@echo **************************************** + +set "apk_path=..\..\build\android\%project_name%\outputs\apk\debug\%project_name%-debug.apk" + +call adb install -r -t "%apk_path%" + +@echo. +@echo **************************************** +@echo Done! +@echo **************************************** + +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/tests/framework_test_vulkan/install_config.bat b/tests/framework_test_vulkan/install_config.bat new file mode 100644 index 0000000..c3d20e8 --- /dev/null +++ b/tests/framework_test_vulkan/install_config.bat @@ -0,0 +1,31 @@ + +@echo off +cd /D "%~dp0" + +:: Get the name of the current folder (assumed to be the project name) +for %%I in ("%~dp0.") do set "project_name=%%~nxI" + +:: Check if app_config.txt exists +if exist "app_config.txt" ( + @echo. + @echo **************************************** + @echo Pushing app_config.txt to: /sdcard/Android/data/com.quic.%project_name%/files/ + @echo **************************************** + adb push ./app_config.txt /sdcard/Android/data/com.quic.%project_name%/files/app_config.txt + + @echo. + @echo **************************************** + @echo Done! + @echo **************************************** +) else ( + @echo. + @echo **************************************** + @echo No app_config.txt was found. + @echo It's not necessary for the app, but it can be used to override application settings. + @echo If such functionality is desired, please create the file and override the global variables + @echo according to how they are defined in the project. + @echo **************************************** +) + +:: Pause only if run directly +IF "%~dpnx0"=="%0" PAUSE \ No newline at end of file diff --git a/samples/sdp-cli/project/android/AndroidManifest.xml b/tests/framework_test_vulkan/project/android/AndroidManifest.xml similarity index 97% rename from samples/sdp-cli/project/android/AndroidManifest.xml rename to tests/framework_test_vulkan/project/android/AndroidManifest.xml index 8ebd924..be94abe 100644 --- a/samples/sdp-cli/project/android/AndroidManifest.xml +++ b/tests/framework_test_vulkan/project/android/AndroidManifest.xml @@ -1,7 +1,6 @@ @@ -21,7 +20,8 @@ android:versionName="1.0"> android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="reverseLandscape" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:exported="true"> + + SGS VK Framework Test + diff --git a/tests/framework_test_vulkan/shaders/Debug.frag b/tests/framework_test_vulkan/shaders/Debug.frag new file mode 100644 index 0000000..7f9cf60 --- /dev/null +++ b/tests/framework_test_vulkan/shaders/Debug.frag @@ -0,0 +1,95 @@ +//============================================================================= +// +// +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. +// +//============================================================================== + +// Debug.frag + +#version 400 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +// Uniform buffer locations +#define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 + +// Texture Locations +#define SHADER_DIFFUSE_TEXTURE_LOC 2 +#define SHADER_NORMAL_TEXTURE_LOC 3 +#define SHADER_SHADOW_DEPTH_TEXTURE_LOC 4 +#define SHADER_SHADOW_COLOR_TEXTURE_LOC 5 +#define SHADER_ENVIRONMENT_TEXTURE_LOC 6 +#define SHADER_IRRADIANCE_TEXTURE_LOC 7 +#define SHADER_REFLECT_TEXTURE_LOC 8 + +// Uniform Constant Buffer +layout(std140, set = 0, binding = SHADER_FRAG_UBO_LOCATION) uniform FragConstantsBuff +{ + vec4 Color; + vec4 EyePos; + vec4 LightDir; + vec4 LightColor; +} FragCB; + +// Textures +layout(set = 0, binding = SHADER_DIFFUSE_TEXTURE_LOC) uniform sampler2D u_DiffuseTex; + +// Varying's +layout (location = 0) in vec2 v_TexCoord; +layout (location = 1) in vec3 v_WorldPos; +layout (location = 2) in vec3 v_WorldNorm; +layout (location = 6) in vec4 v_VertColor; + +// Finally, the output color +layout (location = 0) out vec4 FragColor; + +//----------------------------------------------------------------------------- +void main() +//----------------------------------------------------------------------------- +{ + // ******************************** + // Base Lighting + // ******************************** + // Get base color from the color texture + vec4 DiffuseColor = texture( u_DiffuseTex, v_TexCoord.xy ); + // vec4 DiffuseColor = vec4(0.8, 0.8, 0.8, 1.0); + DiffuseColor.xyzw *= FragCB.Color.xyzw; + + // Adjust by vertex color. + DiffuseColor.xyzw *= v_VertColor.xyzw; + + // Need the normal + vec3 BumpNormal = normalize(v_WorldNorm.xyz); + + // Light direction is from world position to light position + // vec3 LightDir = normalize(FragCB.LightPos.xyz - v_WorldPos.xyz); + + // Can now figure out the diffuse amount + float DiffuseAmount = max(0.25, dot(BumpNormal.xyz, -FragCB.LightDir.xyz)); + + // For specular, we need Half vector + vec3 EyeDir = normalize(FragCB.EyePos.xyz - v_WorldPos.xyz); + vec3 Half = normalize(EyeDir - FragCB.LightDir.xyz); + float Specular = max(0.0, dot(BumpNormal.xyz, Half)); + Specular = pow(Specular, FragCB.LightColor.w); + + // ******************************** + // Start adding in the colors + // ******************************** + + vec3 LightTotal = DiffuseAmount * DiffuseColor.xyz * FragCB.LightColor.xyz; + + // TODO: This equation may no longer be correct since removed many parts + vec3 FinalColor = LightTotal + Specular * FragCB.LightColor.xyz; + + // Write out the color and put depth value in the alpha channel + FragColor = vec4(FinalColor.rgb, FragCB.Color.a); + + // DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! DEBUG! + FragColor = vec4(DiffuseColor.xyz, 1.0); +} + diff --git a/samples/rayReflections/shaders/Skybox.vert b/tests/framework_test_vulkan/shaders/Debug.vert similarity index 70% rename from samples/rayReflections/shaders/Skybox.vert rename to tests/framework_test_vulkan/shaders/Debug.vert index f68539d..98e75ea 100644 --- a/samples/rayReflections/shaders/Skybox.vert +++ b/tests/framework_test_vulkan/shaders/Debug.vert @@ -1,10 +1,12 @@ -//============================================================================================================ +//============================================================================= // // -// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. -// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020 QUALCOMM Technologies Inc. +// All Rights Reserved. // -//============================================================================================================ +//============================================================================== + +// Debug.vert #version 400 #extension GL_ARB_separate_shader_objects : enable @@ -15,24 +17,23 @@ #define SHADER_ATTRIB_LOC_TEXCOORD0 2 #define SHADER_ATTRIB_LOC_COLOR 3 #define SHADER_ATTRIB_LOC_TANGENT 4 -// #define SHADER_ATTRIB_LOC_BITANGENT 5 +#define SHADER_ATTRIB_LOC_BITANGENT 5 #define NUM_SHADER_ATTRIB_LOCATIONS 6 // These start back over at 0! #define SHADER_VERT_UBO_LOCATION 0 +#define SHADER_FRAG_UBO_LOCATION 1 layout (location = SHADER_ATTRIB_LOC_POSITION ) in vec3 a_Position; layout (location = SHADER_ATTRIB_LOC_NORMAL ) in vec3 a_Normal; layout (location = SHADER_ATTRIB_LOC_TEXCOORD0) in vec2 a_TexCoord; layout (location = SHADER_ATTRIB_LOC_COLOR ) in vec4 a_Color; -layout (location = SHADER_ATTRIB_LOC_TANGENT ) in vec4 a_Tangent; // Uniform Constant Buffer layout(std140, set = 0, binding = SHADER_VERT_UBO_LOCATION) uniform VertConstantsBuff { mat4 MVPMatrix; mat4 ModelMatrix; - vec4 Color; } VertCB; // Varying's @@ -52,9 +53,9 @@ void main() // Need Position in world space v_WorldPos = (VertCB.ModelMatrix * vec4(a_Position.xyz, 1.0)).xyz; - // Need Normal in world space (Normals point inward on current model) - v_WorldNorm = -(VertCB.ModelMatrix * vec4(a_Normal.xyz, 0.0)).xyz; + // Need Normal in world space + v_WorldNorm = (VertCB.ModelMatrix * vec4(a_Normal.xyz, 0.0)).xyz; - // Color is simple attribute color multiplied by constant color - v_VertColor.xyzw = vec4(a_Color.xyz * VertCB.Color.xyz, VertCB.Color.w); + // Color is simple attribute color + v_VertColor.xyzw = vec4(a_Color.xyz, 1.0); }