mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
v0.9.0 (pre-release)
This commit is contained in:
commit
7c0c29c1e7
165 changed files with 13442 additions and 38 deletions
13
.clang-tidy
13
.clang-tidy
|
|
@ -8,13 +8,13 @@ Checks:
|
|||
- "readability-*"
|
||||
- "bugprone-*"
|
||||
- "misc-*"
|
||||
- "-misc-include-cleaner"
|
||||
- "-readability-braces-around-statements"
|
||||
- "-readability-function-cognitive-complexity"
|
||||
- "-readability-identifier-length"
|
||||
- "-readability-implicit-bool-conversion"
|
||||
- "-readability-magic-numbers"
|
||||
- "-readability-math-missing-parentheses"
|
||||
- "-readability-named-parameter"
|
||||
- "-bugprone-easily-swappable-parameters"
|
||||
# configure modernization
|
||||
- "modernize-*"
|
||||
|
|
@ -24,3 +24,14 @@ Checks:
|
|||
- "-cppcoreguidelines-avoid-magic-numbers"
|
||||
- "-cppcoreguidelines-pro-type-reinterpret-cast" # allows reinterpret_cast
|
||||
- "-cppcoreguidelines-avoid-non-const-global-variables"
|
||||
- "-cppcoreguidelines-pro-type-union-access"
|
||||
# disable slow and pointless checks
|
||||
- "-modernize-use-std-numbers"
|
||||
- "-modernize-type-traits"
|
||||
- "-cppcoreguidelines-owning-memory"
|
||||
- "-cppcoreguidelines-macro-to-enum"
|
||||
- "-readability-container-contains"
|
||||
- "-bugprone-reserved-identifier"
|
||||
- "-bugprone-stringview-nullptr"
|
||||
- "-bugprone-standalone-empty"
|
||||
- "-misc-unused-using-decls"
|
||||
|
|
|
|||
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1,4 +1,3 @@
|
|||
*.cpp diff=cpp eol=lf
|
||||
*.hpp diff=cpp eol=lf
|
||||
*.md diff=markdown eol=lf
|
||||
*.cs binary
|
||||
|
|
|
|||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
github: [PancakeTAS]
|
||||
ko_fi: pancaketas
|
||||
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Report a bug (if lsfg-vk does work, but not as expected)
|
||||
title: "[BUG] Explain your bug"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what's happening is and, if not immediately obvious, what's supposed to happen instead.
|
||||
|
||||
Before reporting, make sure you have read through these two wiki pages and tried all the options:
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/Quirks
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/Known-incompatibilities
|
||||
|
||||
Make sure this bug hasn't already been reported. Comment your findings on an existing issue if you find one!
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Open '...'
|
||||
2. Do '...'
|
||||
3. Notice '...'
|
||||
|
||||
**Screenshots/Videos**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**System information**
|
||||
What Linux distro are you on? What driver version are you using? What's in your machine?
|
||||
Anything that could be relevant.
|
||||
|
||||
**Verbose log messages**
|
||||
Follow this wiki page to get log message. You may skip this step if you think it isn't relevant:
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/How-to-ask-for-help
|
||||
34
.github/ISSUE_TEMPLATE/compatibility_report.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/compatibility_report.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: Compatibility report
|
||||
about: Report a bug (if lsfg-vk does not work, or poorly works on a certain game or app)
|
||||
title: "[COMPATIBILITY] Explain your bug"
|
||||
labels: compatibility
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what's happening is and, if not immediately obvious, what's supposed to happen instead.
|
||||
|
||||
Before reporting, make sure you have read through these two wiki pages and tried all the options:
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/Quirks
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/Known-incompatibilities
|
||||
|
||||
Make sure this bug hasn't already been reported. Comment your findings on an existing issue if you find one!
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Open '...'
|
||||
2. Do '...'
|
||||
3. Notice '...'
|
||||
|
||||
**Screenshots/Videos**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**System information**
|
||||
What Linux distro are you on? What driver version are you using? What's in your machine?
|
||||
Anything that could be relevant.
|
||||
|
||||
**Verbose log messages**
|
||||
Follow this wiki page to get log message. You may skip this step if you think it isn't relevant:
|
||||
https://github.com/PancakeTAS/lsfg-vk/wiki/How-to-ask-for-help
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Request a new feature (DO NOT REQUEST ANYTHING UNRELATED TO FRAME GENERATION)
|
||||
title: "[COMPATIBILITY] Explain your bug"
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the feature**
|
||||
A short and fitting description of what you want to see in the project.
|
||||
|
||||
If someone already reported a variation of this feature, comment on it. If the issue has been closed, do not resubmit the feature request.
|
||||
|
||||
**Example usecase/scenario**
|
||||
When would users want this feature?
|
||||
69
.github/workflows/build.yml
vendored
Normal file
69
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
name: Build lsfg-vk
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["release"]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# prepare system
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install build dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: git wget xvfb
|
||||
clang clang-tools llvm rustup
|
||||
cmake ninja-build pkg-config
|
||||
libvulkan-dev
|
||||
libgtk-4-dev libadwaita-1-dev
|
||||
version: 1.0
|
||||
execute_install_scripts: true
|
||||
- name: Install rust dependency
|
||||
run: |
|
||||
rustup default stable
|
||||
# build the project
|
||||
- name: Configure with CMake and Ninja
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=./build-release \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On
|
||||
- name: Build with Ninja
|
||||
run: |
|
||||
ninja -C build
|
||||
- name: Install with CMake
|
||||
run: |
|
||||
cmake --install build --strip
|
||||
- name: Build lsfg-vk-ui with appimage.sh
|
||||
run: |
|
||||
pushd ui
|
||||
chmod +x ../scripts/build/appimage.sh
|
||||
../scripts/build/appimage.sh
|
||||
popd
|
||||
- name: Install lsfg-vk-ui
|
||||
run: |
|
||||
mkdir -p build-release/{bin,share/applications,share/icons/hicolor/256x256/apps}
|
||||
mv ui/lsfg-vk-ui.AppImage build-release/bin/lsfg-vk-ui
|
||||
cp ui/rsc/gay.pancake.lsfg-vk-ui.desktop build-release/share/applications/lsfg-vk-ui.desktop
|
||||
cp ui/rsc/icon.png build-release/share/icons/hicolor/256x256/apps/gay.pancake.lsfg-vk-ui.png
|
||||
# upload all files
|
||||
- name: Upload lsfg-vk artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lsfg-vk_TEST
|
||||
path: |
|
||||
build-release/share/vulkan/implicit_layer.d/VkLayer_LS_frame_generation.json
|
||||
build-release/share/applications/lsfg-vk-ui.desktop
|
||||
build-release/share/icons/hicolor/256x256/apps/gay.pancake.lsfg-vk-ui.png
|
||||
build-release/lib/liblsfg-vk.so
|
||||
build-release/bin/lsfg-vk-ui
|
||||
24
.github/workflows/flatpak.yml
vendored
Normal file
24
.github/workflows/flatpak.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: Build lsfg-vk Flatpak extensions
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["release"]
|
||||
|
||||
jobs:
|
||||
flatpak:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: ["23.08", "24.08"]
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-${{ matrix.version }}
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Flatpak extension (${{ matrix.version }})
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: "org.freedesktop.Platform.VulkanLayer.lsfg_vk_TEST_${{ matrix.version }}.flatpak"
|
||||
manifest-path: "scripts/flatpak/org.freedesktop.Platform.VulkanLayer.lsfgvk_${{ matrix.version }}.yml"
|
||||
verbose: true
|
||||
47
.github/workflows/package.yml
vendored
Normal file
47
.github/workflows/package.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
name: Package lsfg-vk
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build lsfg-vk"]
|
||||
types:
|
||||
- completed
|
||||
branches: ["release"]
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
# prepare system
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Download lsfg-vk artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: lsfg-vk_TEST
|
||||
path: .
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
- name: Package lsfg-vk for various distros
|
||||
run: |
|
||||
export VERSION=$(grep -oP ' VERSION\s+\K[\d.]+' CMakeLists.txt)
|
||||
chmod +x scripts/package/package.sh
|
||||
bash ./scripts/package/package.sh
|
||||
- name: Upload lsfg-vk for dpkg
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lsfg-vk.dpkg_TEST
|
||||
path: |
|
||||
*.deb
|
||||
- name: Upload lsfg-vk for rpm
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lsfg-vk.rpm_TEST
|
||||
path: |
|
||||
*.rpm
|
||||
- name: Upload lsfg-vk for alpm
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lsfg-vk.alpm_TEST
|
||||
path: |
|
||||
*.zst
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,7 +1,11 @@
|
|||
# cmake files
|
||||
/build
|
||||
|
||||
# cargo files
|
||||
/ui/target
|
||||
|
||||
# ide/lsp files
|
||||
/.zed
|
||||
/.vscode
|
||||
/.clangd
|
||||
/.cache
|
||||
|
|
|
|||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[submodule "thirdparty/pe-parse"]
|
||||
path = thirdparty/pe-parse
|
||||
url = https://github.com/trailofbits/pe-parse
|
||||
[submodule "thirdparty/dxbc"]
|
||||
path = thirdparty/dxbc
|
||||
url = https://github.com/PancakeTAS/dxbc.git
|
||||
[submodule "thirdparty/toml11"]
|
||||
path = thirdparty/toml11
|
||||
url = https://github.com/ToruNiina/toml11
|
||||
[submodule "thirdparty/volk"]
|
||||
path = thirdparty/volk
|
||||
url = https://github.com/zeux/volk
|
||||
103
CMakeLists.txt
103
CMakeLists.txt
|
|
@ -1,48 +1,79 @@
|
|||
cmake_minimum_required(VERSION 3.29)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(lsfg-vk-base-base
|
||||
VERSION 0.0.1
|
||||
DESCRIPTION "lsfg-vk-base: LSFG on Linux through Vulkan"
|
||||
LANGUAGES CXX)
|
||||
set(CMAKE_SKIP_RPATH ON)
|
||||
|
||||
# cmake options
|
||||
# subprojects
|
||||
add_compile_options(-fPIC
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-unused-template)
|
||||
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_CLANG_TIDY clang-tidy)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
add_subdirectory(thirdparty/dxbc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(thirdparty/toml11 EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(thirdparty/volk EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(framegen)
|
||||
|
||||
# main project
|
||||
project(lsfg-vk
|
||||
VERSION 0.9.0
|
||||
DESCRIPTION "Lossless Scaling Frame Generation on Linux"
|
||||
LANGUAGES CXX)
|
||||
|
||||
file(GLOB SOURCES
|
||||
"src/core/*.cpp"
|
||||
"src/config/*.cpp"
|
||||
"src/extract/*.cpp"
|
||||
"src/mini/*.cpp"
|
||||
"src/utils/*.cpp"
|
||||
"src/*.cpp"
|
||||
)
|
||||
|
||||
add_executable(lsfg-vk-base ${SOURCES})
|
||||
add_library(lsfg-vk SHARED ${SOURCES})
|
||||
|
||||
target_include_directories(lsfg-vk-base
|
||||
PUBLIC include)
|
||||
target_link_libraries(lsfg-vk-base
|
||||
PUBLIC vulkan)
|
||||
target_compile_options(lsfg-vk-base PRIVATE
|
||||
-Weverything
|
||||
# disable compat c++ flags
|
||||
-Wno-pre-c++20-compat-pedantic
|
||||
-Wno-pre-c++17-compat
|
||||
-Wno-c++98-compat-pedantic
|
||||
-Wno-c++98-compat
|
||||
# disable other flags
|
||||
-Wno-missing-designated-field-initializers
|
||||
-Wno-shadow # allow shadowing
|
||||
-Wno-switch-enum # ignore missing cases
|
||||
-Wno-switch-default # ignore missing default
|
||||
-Wno-padded # ignore automatic padding
|
||||
-Wno-exit-time-destructors # allow globals
|
||||
-Wno-global-constructors
|
||||
# required for vulkan
|
||||
-Wno-cast-function-type-strict
|
||||
)
|
||||
# target
|
||||
set_target_properties(lsfg-vk PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
target_include_directories(lsfg-vk
|
||||
PRIVATE include)
|
||||
target_link_libraries(lsfg-vk PRIVATE
|
||||
pe-parse dxbc toml11
|
||||
lsfg-vk-framegen)
|
||||
|
||||
get_target_property(TOML11_INCLUDE_DIRS toml11 INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(lsfg-vk SYSTEM PRIVATE ${TOML11_INCLUDE_DIRS})
|
||||
|
||||
# diagnostics
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set_target_properties(lsfg-vk PROPERTIES
|
||||
EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
|
||||
if(LSFGVK_EXCESS_DEBUG)
|
||||
message(STATUS "LSFGVK_EXCESS_DEBUG is only compatible with clang")
|
||||
target_compile_options(lsfg-vk PRIVATE
|
||||
-Weverything
|
||||
# disable compat c++ flags
|
||||
-Wno-pre-c++20-compat-pedantic
|
||||
-Wno-pre-c++17-compat
|
||||
-Wno-c++98-compat-pedantic
|
||||
-Wno-c++98-compat
|
||||
# disable other flags
|
||||
-Wno-missing-designated-field-initializers
|
||||
-Wno-shadow # allow shadowing
|
||||
-Wno-switch-enum # ignore missing cases
|
||||
-Wno-switch-default # ignore missing default
|
||||
-Wno-padded # ignore automatic padding
|
||||
-Wno-exit-time-destructors # allow globals
|
||||
-Wno-global-constructors # allow globals
|
||||
-Wno-cast-function-type-strict # for vulkan
|
||||
)
|
||||
|
||||
set_target_properties(lsfg-vk PROPERTIES
|
||||
CXX_CLANG_TIDY clang-tidy)
|
||||
endif()
|
||||
|
||||
# install
|
||||
install(FILES "${CMAKE_BINARY_DIR}/liblsfg-vk.so"
|
||||
DESTINATION lib)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/VkLayer_LS_frame_generation.json"
|
||||
DESTINATION share/vulkan/implicit_layer.d)
|
||||
|
|
|
|||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## MIT License
|
||||
|
||||
Copyright (c) 2025 lsfg-vk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
26
README.md
Normal file
26
README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# lsfg-vk
|
||||
Lossless Scaling is a Windows-exclusive app with the goal of bringing frame generation (among other features) to every single game or app.
|
||||
|
||||
lsfg-vk brings this frame generation to Linux users by acting as a Vulkan layer inbetween your game and your graphics card.
|
||||
|
||||
>[!TIP]
|
||||
> **This is a pre-release**. We are still ironing out the last few issues before finally releasing a first version, so beware of any issues you encounter on the way and report them via GitHub issues or the Discord.
|
||||
|
||||
## Installation
|
||||
|
||||
lsfg-vk can run on a variety of Linux distributions:
|
||||
- Click [here](https://github.com/PancakeTAS/lsfg-vk/releases) to download lsfg-vk for your distribution
|
||||
- Follow [this guide](https://github.com/PancakeTAS/lsfg-vk/wiki/Installation-Guide) if you any more help.
|
||||
|
||||
Once installed, open up the lsfg-vk Configuration Window which should hopefully appear in your application menu.
|
||||
|
||||
Please see the [Wiki](https://github.com/PancakeTAS/lsfg-vk/wiki) for more information and join the [Discord](https://discord.gg/losslessscaling) for help (needs Steam verification).
|
||||
|
||||
## Credits
|
||||
Most of the project has still only been written by me, PancakeTAS, but I couldn't have done it without the help of these people:
|
||||
- [0xNULLderef](https://github.com/0xNULLderef): Teaching me how to reverse engineer software.
|
||||
- [Caliel666](https://github.com/Caliel666): Writing the initial draft of the user interface.
|
||||
- [Samueru-sama](https://github.com/Samueru-sama): Helping with various things XDG as well as app images and testing.
|
||||
- Other contributors: Thank you for your contribution!
|
||||
|
||||
I'd also like to thank every single person sponsoring this project. Thanks to you I'll be able to invest more time into this and hopefully bring some cool new features to everyone.
|
||||
18
VkLayer_LS_frame_generation.json
Normal file
18
VkLayer_LS_frame_generation.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"file_format_version": "1.0.0",
|
||||
"layer": {
|
||||
"name": "VK_LAYER_LS_frame_generation",
|
||||
"type": "GLOBAL",
|
||||
"api_version": "1.4.313",
|
||||
"library_path": "liblsfg-vk.so",
|
||||
"implementation_version": "1",
|
||||
"description": "Lossless Scaling frame generation layer",
|
||||
"functions": {
|
||||
"vkGetInstanceProcAddr": "layer_vkGetInstanceProcAddr",
|
||||
"vkGetDeviceProcAddr": "layer_vkGetDeviceProcAddr"
|
||||
},
|
||||
"disable_environment": {
|
||||
"DISABLE_LSFG": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
37
framegen/.clang-tidy
Normal file
37
framegen/.clang-tidy
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
Checks:
|
||||
# enable basic checks
|
||||
- "clang-analyzer-*"
|
||||
# configure performance checks
|
||||
- "performance-*"
|
||||
- "-performance-enum-size"
|
||||
# configure readability and bugprone checks
|
||||
- "readability-*"
|
||||
- "bugprone-*"
|
||||
- "misc-*"
|
||||
- "-readability-braces-around-statements"
|
||||
- "-readability-function-cognitive-complexity"
|
||||
- "-readability-identifier-length"
|
||||
- "-readability-implicit-bool-conversion"
|
||||
- "-readability-magic-numbers"
|
||||
- "-readability-math-missing-parentheses"
|
||||
- "-readability-named-parameter"
|
||||
- "-bugprone-easily-swappable-parameters"
|
||||
# configure modernization
|
||||
- "modernize-*"
|
||||
- "-modernize-use-trailing-return-type"
|
||||
# configure cppcoreguidelines
|
||||
- "cppcoreguidelines-*"
|
||||
- "-cppcoreguidelines-avoid-magic-numbers"
|
||||
- "-cppcoreguidelines-pro-type-reinterpret-cast" # allows reinterpret_cast
|
||||
- "-cppcoreguidelines-avoid-non-const-global-variables"
|
||||
- "-cppcoreguidelines-pro-type-union-access"
|
||||
# disable slow and pointless checks
|
||||
- "-modernize-use-std-numbers"
|
||||
- "-modernize-type-traits"
|
||||
- "-cppcoreguidelines-owning-memory"
|
||||
- "-cppcoreguidelines-macro-to-enum"
|
||||
- "-readability-container-contains"
|
||||
- "-bugprone-reserved-identifier"
|
||||
- "-bugprone-stringview-nullptr"
|
||||
- "-bugprone-standalone-empty"
|
||||
- "-misc-unused-using-decls"
|
||||
3
framegen/.gitattributes
vendored
Normal file
3
framegen/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*.cpp diff=cpp eol=lf
|
||||
*.hpp diff=cpp eol=lf
|
||||
*.md diff=markdown eol=lf
|
||||
9
framegen/.gitignore
vendored
Normal file
9
framegen/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# cmake files
|
||||
/build
|
||||
|
||||
# ide/lsp files
|
||||
/.zed
|
||||
/.vscode
|
||||
/.clangd
|
||||
/.cache
|
||||
/.ccls
|
||||
66
framegen/CMakeLists.txt
Normal file
66
framegen/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(lsfg-vk-framegen
|
||||
DESCRIPTION "Lossless Scaling Frame Generation Backend"
|
||||
LANGUAGES CXX)
|
||||
|
||||
file(GLOB SOURCES
|
||||
"src/common/*.cpp"
|
||||
"src/config/*.cpp"
|
||||
"src/core/*.cpp"
|
||||
"src/pool/*.cpp"
|
||||
"src/*.cpp"
|
||||
"v3.1_src/core/*.cpp"
|
||||
"v3.1_src/pool/*.cpp"
|
||||
"v3.1_src/shaders/*.cpp"
|
||||
"v3.1_src/utils/*.cpp"
|
||||
"v3.1_src/*.cpp"
|
||||
"v3.1p_src/core/*.cpp"
|
||||
"v3.1p_src/pool/*.cpp"
|
||||
"v3.1p_src/shaders/*.cpp"
|
||||
"v3.1p_src/utils/*.cpp"
|
||||
"v3.1p_src/*.cpp"
|
||||
)
|
||||
|
||||
add_library(lsfg-vk-framegen STATIC ${SOURCES})
|
||||
|
||||
# target
|
||||
set_target_properties(lsfg-vk-framegen PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
target_include_directories(lsfg-vk-framegen
|
||||
PUBLIC include
|
||||
PUBLIC public
|
||||
PRIVATE v3.1_include
|
||||
PRIVATE v3.1p_include)
|
||||
target_link_libraries(lsfg-vk-framegen
|
||||
PUBLIC volk)
|
||||
|
||||
# diagnostics
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set_target_properties(lsfg-vk-framegen PROPERTIES
|
||||
EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
|
||||
if(LSFGVK_EXCESS_DEBUG)
|
||||
target_compile_options(lsfg-vk-framegen PRIVATE
|
||||
-Weverything
|
||||
# disable compat c++ flags
|
||||
-Wno-pre-c++20-compat-pedantic
|
||||
-Wno-pre-c++17-compat
|
||||
-Wno-c++98-compat-pedantic
|
||||
-Wno-c++98-compat
|
||||
# disable other flags
|
||||
-Wno-missing-designated-field-initializers
|
||||
-Wno-shadow # allow shadowing
|
||||
-Wno-switch-enum # ignore missing cases
|
||||
-Wno-switch-default # ignore missing default
|
||||
-Wno-padded # ignore automatic padding
|
||||
-Wno-exit-time-destructors # allow globals
|
||||
-Wno-global-constructors # allow globals
|
||||
-Wno-cast-function-type-strict # for vulkan
|
||||
)
|
||||
|
||||
set_target_properties(lsfg-vk-framegen PROPERTIES
|
||||
CXX_CLANG_TIDY clang-tidy)
|
||||
endif()
|
||||
21
framegen/LICENSE.md
Normal file
21
framegen/LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## MIT License
|
||||
|
||||
Copyright (c) 2025 lsfg-vk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
14
framegen/README.md
Normal file
14
framegen/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
## lsfg-vk-framegen
|
||||
Lossless Scaling Frame Generation
|
||||
|
||||
This is a subproject of lsfg-vk and contains the dedicated Vulkan logic for generating frames.
|
||||
|
||||
The project is intentionally structured as a fully external project, such that it can be integrated into other applications.
|
||||
|
||||
### Interface
|
||||
|
||||
Interfacing with lsfg-vk-framegen is done via `lsfg_x_x.hpp` header. The internal Vulkan instance is created using `LSFG_X_X::initialize()` and requires a specific deviceUUID, as well as parts of the lsfg-vk configuration, including a function loading SPIR-V shaders by name. Cleanup is done via `LSFG_X_X::finalize()` after which `LSFG_X_X::initialize()` may be called again. Please note that the initialization process is expensive and may take a while. It is recommended to call this function once during the applications lifetime.
|
||||
|
||||
Once the format and extent of the requested images is determined, `LSFG_X_X::createContext()` should be called to initialize a frame generation context. The Vulkan images are created from backing memory, which is passed through the file descriptor arguments. A context can be destroyed using `LSFG_X_X::deleteContext()`.
|
||||
|
||||
Presenting the context can be done via `LSFG_X_X::presentContext()`. Before calling the function a second time, make sure the outgoing semaphores have been signaled.
|
||||
62
framegen/include/common/exception.hpp
Normal file
62
framegen/include/common/exception.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace LSFG {
|
||||
|
||||
/// Simple exception class for Vulkan errors.
|
||||
class vulkan_error : public std::runtime_error {
|
||||
public:
|
||||
///
|
||||
/// Construct a vulkan_error with a message and a Vulkan result code.
|
||||
///
|
||||
/// @param result The Vulkan result code associated with the error.
|
||||
/// @param message The error message.
|
||||
///
|
||||
explicit vulkan_error(VkResult result, const std::string& message);
|
||||
|
||||
/// Get the Vulkan result code associated with this error.
|
||||
[[nodiscard]] VkResult error() const { return this->result; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
vulkan_error(const vulkan_error&) = default;
|
||||
vulkan_error(vulkan_error&&) = default;
|
||||
vulkan_error& operator=(const vulkan_error&) = default;
|
||||
vulkan_error& operator=(vulkan_error&&) = default;
|
||||
~vulkan_error() noexcept override;
|
||||
private:
|
||||
VkResult result;
|
||||
};
|
||||
|
||||
/// Simple exception class for stacking errors.
|
||||
class rethrowable_error : public std::runtime_error {
|
||||
public:
|
||||
///
|
||||
/// Construct a new rethrowable_error with a message.
|
||||
///
|
||||
/// @param message The error message.
|
||||
/// @param exe The original exception to rethrow.
|
||||
///
|
||||
explicit rethrowable_error(const std::string& message,
|
||||
const std::exception& exe);
|
||||
|
||||
/// Get the exception as a string.
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return message.c_str();
|
||||
}
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
rethrowable_error(const rethrowable_error&) = default;
|
||||
rethrowable_error(rethrowable_error&&) = default;
|
||||
rethrowable_error& operator=(const rethrowable_error&) = default;
|
||||
rethrowable_error& operator=(rethrowable_error&&) = default;
|
||||
~rethrowable_error() noexcept override;
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
}
|
||||
108
framegen/include/common/utils.hpp
Normal file
108
framegen/include/common/utils.hpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "pool/resourcepool.hpp"
|
||||
#include "pool/shaderpool.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <optional>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG::Utils {
|
||||
|
||||
///
|
||||
/// Insert memory barriers for images in a command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
||||
///
|
||||
class BarrierBuilder {
|
||||
public:
|
||||
/// Create a barrier builder.
|
||||
BarrierBuilder(const Core::CommandBuffer& buffer)
|
||||
: commandBuffer(&buffer) {
|
||||
this->barriers.reserve(16); // this is performance critical
|
||||
}
|
||||
|
||||
// Add a resource to the barrier builder.
|
||||
BarrierBuilder& addR2W(Core::Image& image);
|
||||
BarrierBuilder& addW2R(Core::Image& image);
|
||||
|
||||
// Add an optional resource to the barrier builder.
|
||||
BarrierBuilder& addR2W(std::optional<Core::Image>& image) {
|
||||
if (image.has_value()) this->addR2W(*image); return *this; }
|
||||
BarrierBuilder& addW2R(std::optional<Core::Image>& image) {
|
||||
if (image.has_value()) this->addW2R(*image); return *this; }
|
||||
|
||||
/// Add a list of resources to the barrier builder.
|
||||
BarrierBuilder& addR2W(std::vector<Core::Image>& images) {
|
||||
for (auto& image : images) this->addR2W(image); return *this; }
|
||||
BarrierBuilder& addW2R(std::vector<Core::Image>& images) {
|
||||
for (auto& image : images) this->addW2R(image); return *this; }
|
||||
|
||||
/// Add an array of resources to the barrier builder.
|
||||
template<std::size_t N>
|
||||
BarrierBuilder& addR2W(std::array<Core::Image, N>& images) {
|
||||
for (auto& image : images) this->addR2W(image); return *this; }
|
||||
template<std::size_t N>
|
||||
BarrierBuilder& addW2R(std::array<Core::Image, N>& images) {
|
||||
for (auto& image : images) this->addW2R(image); return *this; }
|
||||
|
||||
/// Finish building the barrier
|
||||
void build() const;
|
||||
private:
|
||||
const Core::CommandBuffer* commandBuffer;
|
||||
|
||||
std::vector<VkImageMemoryBarrier2> barriers;
|
||||
};
|
||||
|
||||
///
|
||||
/// Upload a DDS file to a Vulkan image.
|
||||
///
|
||||
/// @param device The Vulkan device
|
||||
/// @param commandPool The command pool
|
||||
/// @param image The Vulkan image to upload to
|
||||
/// @param path The path to the DDS file.
|
||||
///
|
||||
/// @throws std::system_error If the file cannot be opened or read.
|
||||
/// @throws ls:vulkan_error If the Vulkan image cannot be created or updated.
|
||||
///
|
||||
void uploadImage(const Core::Device& device,
|
||||
const Core::CommandPool& commandPool,
|
||||
Core::Image& image, const std::string& path);
|
||||
|
||||
///
|
||||
/// Clear a texture to white during setup.
|
||||
///
|
||||
/// @param device The Vulkan device.
|
||||
/// @param image The image to clear.
|
||||
/// @param white If true, the image will be cleared to white, otherwise to black.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error If the Vulkan image cannot be cleared.
|
||||
///
|
||||
void clearImage(const Core::Device& device, Core::Image& image, bool white = false);
|
||||
|
||||
}
|
||||
|
||||
namespace LSFG {
|
||||
struct Vulkan {
|
||||
Core::Device device;
|
||||
Core::CommandPool commandPool;
|
||||
Core::DescriptorPool descriptorPool;
|
||||
|
||||
uint64_t generationCount;
|
||||
float flowScale;
|
||||
bool isHdr;
|
||||
|
||||
Pool::ShaderPool shaders;
|
||||
Pool::ResourcePool resources;
|
||||
};
|
||||
}
|
||||
71
framegen/include/core/buffer.hpp
Normal file
71
framegen/include/core/buffer.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan buffer.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan buffer.
|
||||
///
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param data Initial data for the buffer, also specifies the size of the buffer.
|
||||
/// @param usage Usage flags for the buffer
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
template<typename T>
|
||||
Buffer(const Core::Device& device, const T& data, VkBufferUsageFlags usage)
|
||||
: size(sizeof(T)) {
|
||||
construct(device, reinterpret_cast<const void*>(&data), usage);
|
||||
}
|
||||
|
||||
///
|
||||
/// Create the buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param data Initial data for the buffer
|
||||
/// @param size Size of the buffer in bytes
|
||||
/// @param usage Usage flags for the buffer
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Buffer(const Core::Device& device, const void* data, size_t size, VkBufferUsageFlags usage)
|
||||
: size(size) {
|
||||
construct(device, data, usage);
|
||||
}
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->buffer; }
|
||||
/// Get the size of the buffer.
|
||||
[[nodiscard]] size_t getSize() const { return this->size; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Buffer(const Buffer&) noexcept = default;
|
||||
Buffer& operator=(const Buffer&) noexcept = default;
|
||||
Buffer(Buffer&&) noexcept = default;
|
||||
Buffer& operator=(Buffer&&) noexcept = default;
|
||||
~Buffer() = default;
|
||||
private:
|
||||
void construct(const Core::Device& device, const void* data, VkBufferUsageFlags usage);
|
||||
|
||||
std::shared_ptr<VkBuffer> buffer;
|
||||
std::shared_ptr<VkDeviceMemory> memory;
|
||||
|
||||
size_t size{};
|
||||
};
|
||||
|
||||
}
|
||||
112
framegen/include/core/commandbuffer.hpp
Normal file
112
framegen/include/core/commandbuffer.hpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/fence.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
/// State of the command buffer.
|
||||
enum class CommandBufferState {
|
||||
/// Command buffer is not initialized or has been destroyed.
|
||||
Invalid,
|
||||
/// Command buffer has been created.
|
||||
Empty,
|
||||
/// Command buffer recording has started.
|
||||
Recording,
|
||||
/// Command buffer recording has ended.
|
||||
Full,
|
||||
/// Command buffer has been submitted to a queue.
|
||||
Submitted
|
||||
};
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command buffer.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command buffer.
|
||||
///
|
||||
class CommandBuffer {
|
||||
public:
|
||||
CommandBuffer() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the command buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param pool Vulkan command pool
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandBuffer(const Core::Device& device, const CommandPool& pool);
|
||||
|
||||
///
|
||||
/// Begin recording commands in the command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is in Empty state
|
||||
/// @throws LSFG::vulkan_error if beginning the command buffer fails.
|
||||
///
|
||||
void begin();
|
||||
|
||||
///
|
||||
/// Dispatch a compute command.
|
||||
///
|
||||
/// @param x Number of groups in the X dimension
|
||||
/// @param y Number of groups in the Y dimension
|
||||
/// @param z Number of groups in the Z dimension
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
||||
///
|
||||
void dispatch(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
|
||||
///
|
||||
/// End recording commands in the command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
||||
/// @throws LSFG::vulkan_error if ending the command buffer fails.
|
||||
///
|
||||
void end();
|
||||
|
||||
///
|
||||
/// Submit the command buffer to a queue.
|
||||
///
|
||||
/// @param queue Vulkan queue to submit to
|
||||
/// @param fence Optional fence to signal when the command buffer has finished executing
|
||||
/// @param waitSemaphores Semaphores to wait on before executing the command buffer
|
||||
/// @param waitSemaphoreValues Values for the semaphores to wait on
|
||||
/// @param signalSemaphores Semaphores to signal after executing the command buffer
|
||||
/// @param signalSemaphoreValues Values for the semaphores to signal
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Full state.
|
||||
/// @throws LSFG::vulkan_error if submission fails.
|
||||
///
|
||||
void submit(VkQueue queue, std::optional<Fence> fence,
|
||||
const std::vector<Semaphore>& waitSemaphores = {},
|
||||
std::optional<std::vector<uint64_t>> waitSemaphoreValues = std::nullopt,
|
||||
const std::vector<Semaphore>& signalSemaphores = {},
|
||||
std::optional<std::vector<uint64_t>> signalSemaphoreValues = std::nullopt);
|
||||
|
||||
/// Get the state of the command buffer.
|
||||
[[nodiscard]] CommandBufferState getState() const { return *this->state; }
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandBuffer; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandBuffer(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer& operator=(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer(CommandBuffer&&) noexcept = default;
|
||||
CommandBuffer& operator=(CommandBuffer&&) noexcept = default;
|
||||
~CommandBuffer() = default;
|
||||
private:
|
||||
std::shared_ptr<CommandBufferState> state;
|
||||
std::shared_ptr<VkCommandBuffer> commandBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
42
framegen/include/core/commandpool.hpp
Normal file
42
framegen/include/core/commandpool.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command pool.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command pool.
|
||||
///
|
||||
class CommandPool {
|
||||
public:
|
||||
CommandPool() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the command pool.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandPool(const Core::Device& device);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandPool; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandPool(const CommandPool&) noexcept = default;
|
||||
CommandPool& operator=(const CommandPool&) noexcept = default;
|
||||
CommandPool(CommandPool&&) noexcept = default;
|
||||
CommandPool& operator=(CommandPool&&) noexcept = default;
|
||||
~CommandPool() = default;
|
||||
private:
|
||||
std::shared_ptr<VkCommandPool> commandPool;
|
||||
};
|
||||
|
||||
}
|
||||
42
framegen/include/core/descriptorpool.hpp
Normal file
42
framegen/include/core/descriptorpool.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan descriptor pool.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan descriptor pool.
|
||||
///
|
||||
class DescriptorPool {
|
||||
public:
|
||||
DescriptorPool() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the descriptor pool.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
DescriptorPool(const Core::Device& device);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->descriptorPool; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
DescriptorPool(const DescriptorPool&) noexcept = default;
|
||||
DescriptorPool& operator=(const DescriptorPool&) noexcept = default;
|
||||
DescriptorPool(DescriptorPool&&) noexcept = default;
|
||||
DescriptorPool& operator=(DescriptorPool&&) noexcept = default;
|
||||
~DescriptorPool() = default;
|
||||
private:
|
||||
std::shared_ptr<VkDescriptorPool> descriptorPool;
|
||||
};
|
||||
|
||||
}
|
||||
124
framegen/include/core/descriptorset.hpp
Normal file
124
framegen/include/core/descriptorset.hpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
class DescriptorSetUpdateBuilder;
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan descriptor set.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan descriptor set.
|
||||
///
|
||||
class DescriptorSet {
|
||||
public:
|
||||
DescriptorSet() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the descriptor set.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param pool Descriptor pool to allocate from
|
||||
/// @param shaderModule Shader module to use for the descriptor set
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
DescriptorSet(const Core::Device& device,
|
||||
const DescriptorPool& pool, const ShaderModule& shaderModule);
|
||||
|
||||
///
|
||||
/// Update the descriptor set with resources.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
[[nodiscard]] DescriptorSetUpdateBuilder update(const Core::Device& device) const;
|
||||
|
||||
///
|
||||
/// Bind a descriptor set to a command buffer.
|
||||
///
|
||||
/// @param commandBuffer Command buffer to bind the descriptor set to.
|
||||
/// @param pipeline Pipeline to bind the descriptor set to.
|
||||
///
|
||||
void bind(const CommandBuffer& commandBuffer, const Pipeline& pipeline) const;
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->descriptorSet; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
DescriptorSet(const DescriptorSet&) noexcept = default;
|
||||
DescriptorSet& operator=(const DescriptorSet&) noexcept = default;
|
||||
DescriptorSet(DescriptorSet&&) noexcept = default;
|
||||
DescriptorSet& operator=(DescriptorSet&&) noexcept = default;
|
||||
~DescriptorSet() = default;
|
||||
private:
|
||||
std::shared_ptr<VkDescriptorSet> descriptorSet;
|
||||
};
|
||||
|
||||
///
|
||||
/// Builder class for updating a descriptor set.
|
||||
///
|
||||
class DescriptorSetUpdateBuilder {
|
||||
friend class DescriptorSet;
|
||||
public:
|
||||
/// Add a resource to the descriptor set update.
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const Image& image);
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const Sampler& sampler);
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const Buffer& buffer);
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type); // empty entry
|
||||
|
||||
/// Add a list of resources to the descriptor set update.
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::vector<Image>& images) {
|
||||
for (const auto& image : images) this->add(type, image); return *this; }
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::vector<Sampler>& samplers) {
|
||||
for (const auto& sampler : samplers) this->add(type, sampler); return *this; }
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::vector<Buffer>& buffers) {
|
||||
for (const auto& buffer : buffers) this->add(type, buffer); return *this; }
|
||||
|
||||
/// Add an array of resources
|
||||
template<std::size_t N>
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::array<Image, N>& images) {
|
||||
for (const auto& image : images) this->add(type, image); return *this; }
|
||||
template<std::size_t N>
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::array<Sampler, N>& samplers) {
|
||||
for (const auto& sampler : samplers) this->add(type, sampler); return *this; }
|
||||
template<std::size_t N>
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::array<Buffer, N>& buffers) {
|
||||
for (const auto& buffer : buffers) this->add(type, buffer); return *this; }
|
||||
|
||||
/// Add an optional resource to the descriptor set update.
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::optional<Image>& image) {
|
||||
if (image.has_value()) this->add(type, *image); else this->add(type); return *this; }
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::optional<Sampler>& sampler) {
|
||||
if (sampler.has_value()) this->add(type, *sampler); else this->add(type); return *this; }
|
||||
DescriptorSetUpdateBuilder& add(VkDescriptorType type, const std::optional<Buffer>& buffer) {
|
||||
if (buffer.has_value()) this->add(type, *buffer); else this->add(type); return *this; }
|
||||
|
||||
/// Finish building the descriptor set update.
|
||||
void build();
|
||||
private:
|
||||
const DescriptorSet* descriptorSet;
|
||||
const Core::Device* device;
|
||||
|
||||
DescriptorSetUpdateBuilder(const DescriptorSet& descriptorSet, const Core::Device& device)
|
||||
: descriptorSet(&descriptorSet), device(&device) {}
|
||||
|
||||
std::vector<VkWriteDescriptorSet> entries;
|
||||
};
|
||||
|
||||
}
|
||||
53
framegen/include/core/device.hpp
Normal file
53
framegen/include/core/device.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/instance.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan device.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan device.
|
||||
///
|
||||
class Device {
|
||||
public:
|
||||
///
|
||||
/// Create the device.
|
||||
///
|
||||
/// @param instance Vulkan instance
|
||||
/// @param deviceUUID The UUID of the Vulkan device to use.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Device(const Instance& instance, uint64_t deviceUUID);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->device; }
|
||||
/// Get the physical device associated with this logical device.
|
||||
[[nodiscard]] VkPhysicalDevice getPhysicalDevice() const { return this->physicalDevice; }
|
||||
/// Get the compute queue family index.
|
||||
[[nodiscard]] uint32_t getComputeFamilyIdx() const { return this->computeFamilyIdx; }
|
||||
/// Get the compute queue.
|
||||
[[nodiscard]] VkQueue getComputeQueue() const { return this->computeQueue; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Device(const Core::Device&) noexcept = default;
|
||||
Device& operator=(const Core::Device&) noexcept = default;
|
||||
Device(Device&&) noexcept = default;
|
||||
Device& operator=(Device&&) noexcept = default;
|
||||
~Device() = default;
|
||||
private:
|
||||
std::shared_ptr<VkDevice> device;
|
||||
VkPhysicalDevice physicalDevice{};
|
||||
|
||||
uint32_t computeFamilyIdx{0};
|
||||
|
||||
VkQueue computeQueue{};
|
||||
};
|
||||
|
||||
}
|
||||
63
framegen/include/core/fence.hpp
Normal file
63
framegen/include/core/fence.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan fence.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan fence.
|
||||
///
|
||||
class Fence {
|
||||
public:
|
||||
Fence() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the fence.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Fence(const Core::Device& device);
|
||||
|
||||
///
|
||||
/// Reset the fence to an unsignaled state.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resetting fails.
|
||||
///
|
||||
void reset(const Core::Device& device) const;
|
||||
|
||||
///
|
||||
/// Wait for the fence
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
|
||||
/// @returns true if the fence signaled, false if it timed out.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if waiting fails.
|
||||
///
|
||||
[[nodiscard]] bool wait(const Core::Device& device, uint64_t timeout = UINT64_MAX) const;
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->fence; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Fence(const Fence&) noexcept = default;
|
||||
Fence& operator=(const Fence&) noexcept = default;
|
||||
Fence(Fence&&) noexcept = default;
|
||||
Fence& operator=(Fence&&) noexcept = default;
|
||||
~Fence() = default;
|
||||
private:
|
||||
std::shared_ptr<VkFence> fence;
|
||||
};
|
||||
|
||||
}
|
||||
87
framegen/include/core/image.hpp
Normal file
87
framegen/include/core/image.hpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan image.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan image.
|
||||
///
|
||||
class Image {
|
||||
public:
|
||||
Image() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the image.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param extent Extent of the image in pixels.
|
||||
/// @param format Vulkan format of the image
|
||||
/// @param usage Usage flags for the image
|
||||
/// @param aspectFlags Aspect flags for the image view
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Image(const Core::Device& device, VkExtent2D extent,
|
||||
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
|
||||
///
|
||||
/// Create the image with shared backing memory.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param extent Extent of the image in pixels.
|
||||
/// @param format Vulkan format of the image
|
||||
/// @param usage Usage flags for the image
|
||||
/// @param aspectFlags Aspect flags for the image view
|
||||
/// @param fd File descriptor for shared memory.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->image; }
|
||||
/// Get the Vulkan device memory handle.
|
||||
[[nodiscard]] auto getMemory() const { return *this->memory; }
|
||||
/// Get the Vulkan image view handle.
|
||||
[[nodiscard]] auto getView() const { return *this->view; }
|
||||
/// Get the extent of the image.
|
||||
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
|
||||
/// Get the format of the image.
|
||||
[[nodiscard]] VkFormat getFormat() const { return this->format; }
|
||||
/// Get the aspect flags of the image.
|
||||
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
|
||||
|
||||
/// Set the layout of the image.
|
||||
void setLayout(VkImageLayout layout) { *this->layout = layout; }
|
||||
/// Get the current layout of the image.
|
||||
[[nodiscard]] VkImageLayout getLayout() const { return *this->layout; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Image(const Image&) noexcept = default;
|
||||
Image& operator=(const Image&) noexcept = default;
|
||||
Image(Image&&) noexcept = default;
|
||||
Image& operator=(Image&&) noexcept = default;
|
||||
~Image() = default;
|
||||
private:
|
||||
std::shared_ptr<VkImage> image;
|
||||
std::shared_ptr<VkDeviceMemory> memory;
|
||||
std::shared_ptr<VkImageView> view;
|
||||
|
||||
std::shared_ptr<VkImageLayout> layout;
|
||||
|
||||
VkExtent2D extent{};
|
||||
VkFormat format{};
|
||||
VkImageAspectFlags aspectFlags{};
|
||||
};
|
||||
|
||||
}
|
||||
36
framegen/include/core/instance.hpp
Normal file
36
framegen/include/core/instance.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan instance.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan instance.
|
||||
///
|
||||
class Instance {
|
||||
public:
|
||||
///
|
||||
/// Create the instance.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Instance();
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return this->instance ? *this->instance : VK_NULL_HANDLE; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Instance(const Instance&) noexcept = default;
|
||||
Instance& operator=(const Instance&) noexcept = default;
|
||||
Instance(Instance&&) noexcept = default;
|
||||
Instance& operator=(Instance&&) noexcept = default;
|
||||
~Instance() = default;
|
||||
private:
|
||||
std::shared_ptr<VkInstance> instance;
|
||||
};
|
||||
|
||||
}
|
||||
55
framegen/include/core/pipeline.hpp
Normal file
55
framegen/include/core/pipeline.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan pipeline.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan pipeline.
|
||||
///
|
||||
class Pipeline {
|
||||
public:
|
||||
Pipeline() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create a compute pipeline.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param shader Shader module to use for the pipeline.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Pipeline(const Core::Device& device, const ShaderModule& shader);
|
||||
|
||||
///
|
||||
/// Bind the pipeline to a command buffer.
|
||||
///
|
||||
/// @param commandBuffer Command buffer to bind the pipeline to.
|
||||
///
|
||||
void bind(const CommandBuffer& commandBuffer) const;
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->pipeline; }
|
||||
/// Get the pipeline layout.
|
||||
[[nodiscard]] auto getLayout() const { return *this->layout; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Pipeline(const Pipeline&) noexcept = default;
|
||||
Pipeline& operator=(const Pipeline&) noexcept = default;
|
||||
Pipeline(Pipeline&&) noexcept = default;
|
||||
Pipeline& operator=(Pipeline&&) noexcept = default;
|
||||
~Pipeline() = default;
|
||||
private:
|
||||
std::shared_ptr<VkPipeline> pipeline;
|
||||
std::shared_ptr<VkPipelineLayout> layout;
|
||||
};
|
||||
|
||||
}
|
||||
48
framegen/include/core/sampler.hpp
Normal file
48
framegen/include/core/sampler.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan sampler.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan sampler.
|
||||
///
|
||||
class Sampler {
|
||||
public:
|
||||
Sampler() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the sampler.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param mode Address mode for the sampler.
|
||||
/// @param compare Compare operation for the sampler.
|
||||
/// @param isWhite Whether the border color is white.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Sampler(const Core::Device& device,
|
||||
VkSamplerAddressMode mode,
|
||||
VkCompareOp compare,
|
||||
bool isWhite);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->sampler; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Sampler(const Sampler&) noexcept = default;
|
||||
Sampler& operator=(const Sampler&) noexcept = default;
|
||||
Sampler(Sampler&&) noexcept = default;
|
||||
Sampler& operator=(Sampler&&) noexcept = default;
|
||||
~Sampler() = default;
|
||||
private:
|
||||
std::shared_ptr<VkSampler> sampler;
|
||||
};
|
||||
|
||||
}
|
||||
80
framegen/include/core/semaphore.hpp
Normal file
80
framegen/include/core/semaphore.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan semaphore.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan semaphore.
|
||||
///
|
||||
class Semaphore {
|
||||
public:
|
||||
Semaphore() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the semaphore.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param initial Optional initial value for creating a timeline semaphore.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(const Core::Device& device, std::optional<uint32_t> initial = std::nullopt);
|
||||
|
||||
///
|
||||
/// Import a semaphore.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param fd File descriptor to import the semaphore from.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(const Core::Device& device, int fd);
|
||||
|
||||
///
|
||||
/// Signal the semaphore to a specific value.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param value The value to signal the semaphore to.
|
||||
///
|
||||
/// @throws std::logic_error if the semaphore is not a timeline semaphore.
|
||||
/// @throws LSFG::vulkan_error if signaling fails.
|
||||
///
|
||||
void signal(const Core::Device& device, uint64_t value) const;
|
||||
|
||||
///
|
||||
/// Wait for the semaphore to reach a specific value.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param value The value to wait for.
|
||||
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
|
||||
/// @returns true if the semaphore reached the value, false if it timed out.
|
||||
///
|
||||
/// @throws std::logic_error if the semaphore is not a timeline semaphore.
|
||||
/// @throws LSFG::vulkan_error if waiting fails.
|
||||
///
|
||||
[[nodiscard]] bool wait(const Core::Device& device, uint64_t value, uint64_t timeout = UINT64_MAX) const;
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->semaphore; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Semaphore(const Semaphore&) noexcept = default;
|
||||
Semaphore& operator=(const Semaphore&) noexcept = default;
|
||||
Semaphore(Semaphore&&) noexcept = default;
|
||||
Semaphore& operator=(Semaphore&&) noexcept = default;
|
||||
~Semaphore() = default;
|
||||
private:
|
||||
std::shared_ptr<VkSemaphore> semaphore;
|
||||
bool isTimeline{};
|
||||
};
|
||||
|
||||
}
|
||||
52
framegen/include/core/shadermodule.hpp
Normal file
52
framegen/include/core/shadermodule.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace LSFG::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan shader module.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan shader module.
|
||||
///
|
||||
class ShaderModule {
|
||||
public:
|
||||
ShaderModule() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the shader module.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param code SPIR-V bytecode for the shader.
|
||||
/// @param descriptorTypes Descriptor types used in the shader.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
ShaderModule(const Core::Device& device, const std::vector<uint8_t>& code,
|
||||
const std::vector<std::pair<size_t, VkDescriptorType>>& descriptorTypes);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->shaderModule; }
|
||||
/// Get the descriptor set layout.
|
||||
[[nodiscard]] auto getLayout() const { return *this->descriptorSetLayout; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
ShaderModule(const ShaderModule&) noexcept = default;
|
||||
ShaderModule& operator=(const ShaderModule&) noexcept = default;
|
||||
ShaderModule(ShaderModule&&) noexcept = default;
|
||||
ShaderModule& operator=(ShaderModule&&) noexcept = default;
|
||||
~ShaderModule() = default;
|
||||
private:
|
||||
std::shared_ptr<VkShaderModule> shaderModule;
|
||||
std::shared_ptr<VkDescriptorSetLayout> descriptorSetLayout;
|
||||
};
|
||||
|
||||
}
|
||||
69
framegen/include/pool/resourcepool.hpp
Normal file
69
framegen/include/pool/resourcepool.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace LSFG::Pool {
|
||||
|
||||
///
|
||||
/// Resource pool for each Vulkan device.
|
||||
///
|
||||
class ResourcePool {
|
||||
public:
|
||||
ResourcePool() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the resource pool.
|
||||
///
|
||||
/// @param isHdr HDR support stored in buffers.
|
||||
/// @param flowScale Scale factor stored in buffers.
|
||||
///
|
||||
/// @throws std::runtime_error if the resource pool cannot be created.
|
||||
///
|
||||
ResourcePool(bool isHdr, float flowScale)
|
||||
: isHdr(isHdr), flowScale(flowScale) {}
|
||||
|
||||
///
|
||||
/// Retrieve a buffer with given parameters or create it.
|
||||
///
|
||||
/// @param timestamp Timestamp stored in buffer
|
||||
/// @param firstIter First iteration stored in buffer
|
||||
/// @param firstIterS First special iteration stored in buffer
|
||||
/// @return Created or cached buffer
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the buffer cannot be created.
|
||||
///
|
||||
Core::Buffer getBuffer(
|
||||
const Core::Device& device,
|
||||
float timestamp = 0.0F, bool firstIter = false, bool firstIterS = false);
|
||||
|
||||
///
|
||||
/// Retrieve a sampler by type or create it.
|
||||
///
|
||||
/// @param type Type of the sampler
|
||||
/// @param compare Compare operation for the sampler
|
||||
/// @param isWhite Whether the sampler is white
|
||||
/// @return Created or cached sampler
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the sampler cannot be created.
|
||||
///
|
||||
Core::Sampler getSampler(
|
||||
const Core::Device& device,
|
||||
VkSamplerAddressMode type = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
VkCompareOp compare = VK_COMPARE_OP_NEVER,
|
||||
bool isWhite = false);
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, Core::Buffer> buffers;
|
||||
std::unordered_map<uint64_t, Core::Sampler> samplers;
|
||||
bool isHdr{};
|
||||
float flowScale{};
|
||||
};
|
||||
|
||||
}
|
||||
65
framegen/include/pool/shaderpool.hpp
Normal file
65
framegen/include/pool/shaderpool.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/device.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG::Pool {
|
||||
|
||||
///
|
||||
/// Shader pool for each Vulkan device.
|
||||
///
|
||||
class ShaderPool {
|
||||
public:
|
||||
ShaderPool() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the shader pool.
|
||||
///
|
||||
/// @param source Function to retrieve shader source code by name.
|
||||
///
|
||||
/// @throws std::runtime_error if the shader pool cannot be created.
|
||||
///
|
||||
ShaderPool(const std::function<std::vector<uint8_t>(const std::string&)>& source)
|
||||
: source(source) {}
|
||||
|
||||
///
|
||||
/// Retrieve a shader module by name or create it.
|
||||
///
|
||||
/// @param name Name of the shader module
|
||||
/// @param types Descriptor types for the shader module
|
||||
/// @return Shader module
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the shader module cannot be created.
|
||||
///
|
||||
Core::ShaderModule getShader(
|
||||
const Core::Device& device, const std::string& name,
|
||||
const std::vector<std::pair<size_t, VkDescriptorType>>& types);
|
||||
|
||||
///
|
||||
/// Retrieve a pipeline shader module by name or create it.
|
||||
///
|
||||
/// @param name Name of the shader module
|
||||
/// @return Pipeline shader module or empty
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the shader module cannot be created.
|
||||
///
|
||||
Core::Pipeline getPipeline(
|
||||
const Core::Device& device, const std::string& name);
|
||||
private:
|
||||
std::function<std::vector<uint8_t>(const std::string&)> source;
|
||||
std::unordered_map<std::string, Core::ShaderModule> shaders;
|
||||
std::unordered_map<std::string, Core::Pipeline> pipelines;
|
||||
};
|
||||
|
||||
}
|
||||
66
framegen/public/lsfg_3_1.hpp
Normal file
66
framegen/public/lsfg_3_1.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1 {
|
||||
|
||||
///
|
||||
/// Initialize the LSFG library.
|
||||
///
|
||||
/// @param deviceUUID The UUID of the Vulkan device to use.
|
||||
/// @param isHdr Whether the images are in HDR format.
|
||||
/// @param flowScale Internal flow scale factor.
|
||||
/// @param generationCount Number of frames to generate.
|
||||
/// @param loader Function to load shader source code by name.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if Vulkan objects fail to initialize.
|
||||
///
|
||||
void initialize(uint64_t deviceUUID,
|
||||
bool isHdr, float flowScale, uint64_t generationCount,
|
||||
const std::function<std::vector<uint8_t>(const std::string&)>& loader);
|
||||
|
||||
///
|
||||
/// Create a new LSFG context on a swapchain.
|
||||
///
|
||||
/// @param in0 File descriptor for the first input image.
|
||||
/// @param in1 File descriptor for the second input image.
|
||||
/// @param outN File descriptor for each output image. This defines the LSFG level.
|
||||
/// @param extent The size of the images
|
||||
/// @param format The format of the images.
|
||||
/// @return A unique identifier for the created context.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context cannot be created.
|
||||
///
|
||||
int32_t createContext(
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format);
|
||||
|
||||
///
|
||||
/// Present a context.
|
||||
///
|
||||
/// @param id Unique identifier of the context to present.
|
||||
/// @param inSem Semaphore to wait on before starting the generation.
|
||||
/// @param outSem Semaphores to signal once each output image is ready.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context cannot be presented.
|
||||
///
|
||||
void presentContext(int32_t id, int inSem, const std::vector<int>& outSem);
|
||||
|
||||
///
|
||||
/// Delete an LSFG context.
|
||||
///
|
||||
/// @param id Unique identifier of the context to delete.
|
||||
///
|
||||
void deleteContext(int32_t id);
|
||||
|
||||
///
|
||||
/// Deinitialize the LSFG library.
|
||||
///
|
||||
void finalize();
|
||||
|
||||
}
|
||||
66
framegen/public/lsfg_3_1p.hpp
Normal file
66
framegen/public/lsfg_3_1p.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1P {
|
||||
|
||||
///
|
||||
/// Initialize the LSFG library.
|
||||
///
|
||||
/// @param deviceUUID The UUID of the Vulkan device to use.
|
||||
/// @param isHdr Whether the images are in HDR format.
|
||||
/// @param flowScale Internal flow scale factor.
|
||||
/// @param generationCount Number of frames to generate.
|
||||
/// @param loader Function to load shader source code by name.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if Vulkan objects fail to initialize.
|
||||
///
|
||||
void initialize(uint64_t deviceUUID,
|
||||
bool isHdr, float flowScale, uint64_t generationCount,
|
||||
const std::function<std::vector<uint8_t>(const std::string&)>& loader);
|
||||
|
||||
///
|
||||
/// Create a new LSFG context on a swapchain.
|
||||
///
|
||||
/// @param in0 File descriptor for the first input image.
|
||||
/// @param in1 File descriptor for the second input image.
|
||||
/// @param outN File descriptor for each output image. This defines the LSFG level.
|
||||
/// @param extent The size of the images
|
||||
/// @param format The format of the images.
|
||||
/// @return A unique identifier for the created context.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context cannot be created.
|
||||
///
|
||||
int32_t createContext(
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format);
|
||||
|
||||
///
|
||||
/// Present a context.
|
||||
///
|
||||
/// @param id Unique identifier of the context to present.
|
||||
/// @param inSem Semaphore to wait on before starting the generation.
|
||||
/// @param outSem Semaphores to signal once each output image is ready.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context cannot be presented.
|
||||
///
|
||||
void presentContext(int32_t id, int inSem, const std::vector<int>& outSem);
|
||||
|
||||
///
|
||||
/// Delete an LSFG context.
|
||||
///
|
||||
/// @param id Unique identifier of the context to delete.
|
||||
///
|
||||
void deleteContext(int32_t id);
|
||||
|
||||
///
|
||||
/// Deinitialize the LSFG library.
|
||||
///
|
||||
void finalize();
|
||||
|
||||
}
|
||||
24
framegen/src/common/exception.cpp
Normal file
24
framegen/src/common/exception.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#include "common/exception.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
vulkan_error::vulkan_error(VkResult result, const std::string& message)
|
||||
: std::runtime_error(std::format("{} (error {})", message, static_cast<int32_t>(result))),
|
||||
result(result) {}
|
||||
|
||||
vulkan_error::~vulkan_error() noexcept = default;
|
||||
|
||||
rethrowable_error::rethrowable_error(const std::string& message, const std::exception& exe)
|
||||
: std::runtime_error(message) {
|
||||
this->message = std::format("{}\n- {}", message, exe.what());
|
||||
}
|
||||
|
||||
rethrowable_error::~rethrowable_error() noexcept = default;
|
||||
191
framegen/src/common/utils.cpp
Normal file
191
framegen/src/common/utils.cpp
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "common/utils.hpp"
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/fence.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <ios>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG::Utils;
|
||||
|
||||
BarrierBuilder& BarrierBuilder::addR2W(Core::Image& image) {
|
||||
this->barriers.emplace_back(VkImageMemoryBarrier2 {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
.oldLayout = image.getLayout(),
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.image = image.handle(),
|
||||
.subresourceRange = {
|
||||
.aspectMask = image.getAspectFlags(),
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
});
|
||||
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BarrierBuilder& BarrierBuilder::addW2R(Core::Image& image) {
|
||||
this->barriers.emplace_back(VkImageMemoryBarrier2 {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
|
||||
.oldLayout = image.getLayout(),
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.image = image.handle(),
|
||||
.subresourceRange = {
|
||||
.aspectMask = image.getAspectFlags(),
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
});
|
||||
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BarrierBuilder::build() const {
|
||||
const VkDependencyInfo dependencyInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = static_cast<uint32_t>(this->barriers.size()),
|
||||
.pImageMemoryBarriers = this->barriers.data()
|
||||
};
|
||||
vkCmdPipelineBarrier2(this->commandBuffer->handle(), &dependencyInfo);
|
||||
}
|
||||
|
||||
void Utils::uploadImage(const Core::Device& device, const Core::CommandPool& commandPool,
|
||||
Core::Image& image, const std::string& path) {
|
||||
// read image bytecode
|
||||
std::ifstream file(path.data(), std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to open image: " + path);
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
size -= 124 + 4; // dds header and magic bytes
|
||||
std::vector<char> code(static_cast<size_t>(size));
|
||||
|
||||
file.seekg(124 + 4, std::ios::beg);
|
||||
if (!file.read(code.data(), size))
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to read image: " + path);
|
||||
|
||||
file.close();
|
||||
|
||||
// copy data to buffer
|
||||
const Core::Buffer stagingBuffer(
|
||||
device, code.data(), static_cast<uint32_t>(code.size()),
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
);
|
||||
|
||||
// perform the upload
|
||||
Core::CommandBuffer commandBuffer(device, commandPool);
|
||||
commandBuffer.begin();
|
||||
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_NONE,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = image.getLayout(),
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.image = image.handle(),
|
||||
.subresourceRange = {
|
||||
.aspectMask = image.getAspectFlags(),
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdPipelineBarrier(
|
||||
commandBuffer.handle(),
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &barrier
|
||||
);
|
||||
|
||||
auto extent = image.getExtent();
|
||||
const VkBufferImageCopy region{
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource = {
|
||||
.aspectMask = image.getAspectFlags(),
|
||||
.layerCount = 1
|
||||
},
|
||||
.imageExtent = { extent.width, extent.height, 1 }
|
||||
};
|
||||
vkCmdCopyBufferToImage(
|
||||
commandBuffer.handle(),
|
||||
stagingBuffer.handle(), image.handle(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion
|
||||
);
|
||||
|
||||
commandBuffer.end();
|
||||
|
||||
Core::Fence fence(device);
|
||||
commandBuffer.submit(device.getComputeQueue(), fence);
|
||||
|
||||
// wait for the upload to complete
|
||||
if (!fence.wait(device))
|
||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Upload operation timed out");
|
||||
}
|
||||
|
||||
void Utils::clearImage(const Core::Device& device, Core::Image& image, bool white) {
|
||||
Core::Fence fence(device);
|
||||
const Core::CommandPool cmdPool(device);
|
||||
Core::CommandBuffer cmdBuf(device, cmdPool);
|
||||
cmdBuf.begin();
|
||||
|
||||
const VkImageMemoryBarrier2 barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = image.getLayout(),
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.image = image.handle(),
|
||||
.subresourceRange = {
|
||||
.aspectMask = image.getAspectFlags(),
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
const VkDependencyInfo dependencyInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &barrier
|
||||
};
|
||||
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdPipelineBarrier2(cmdBuf.handle(), &dependencyInfo);
|
||||
|
||||
const float clearValue = white ? 1.0F : 0.0F;
|
||||
const VkClearColorValue clearColor = {{ clearValue, clearValue, clearValue, clearValue }};
|
||||
const VkImageSubresourceRange subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
};
|
||||
vkCmdClearColorImage(cmdBuf.handle(),
|
||||
image.handle(), image.getLayout(),
|
||||
&clearColor,
|
||||
1, &subresourceRange);
|
||||
|
||||
cmdBuf.end();
|
||||
|
||||
cmdBuf.submit(device.getComputeQueue(), fence);
|
||||
if (!fence.wait(device))
|
||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Failed to wait for clearing fence.");
|
||||
}
|
||||
86
framegen/src/core/buffer.cpp
Normal file
86
framegen/src/core/buffer.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
void Buffer::construct(const Core::Device& device, const void* data, VkBufferUsageFlags usage) {
|
||||
// create buffer
|
||||
const VkBufferCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = this->size,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VkBuffer bufferHandle{};
|
||||
auto res = vkCreateBuffer(device.handle(), &desc, nullptr, &bufferHandle);
|
||||
if (res != VK_SUCCESS || bufferHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan buffer");
|
||||
|
||||
// find memory type
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetBufferMemoryRequirements(device.handle(), bufferHandle, &memReqs);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
std::optional<uint32_t> memType{};
|
||||
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
|
||||
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
|
||||
(memProps.memoryTypes[i].propertyFlags &
|
||||
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))) {
|
||||
memType.emplace(i);
|
||||
break;
|
||||
} // NOLINTEND
|
||||
}
|
||||
if (!memType.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// allocate and bind memory
|
||||
const VkMemoryAllocateInfo allocInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memType.value()
|
||||
};
|
||||
VkDeviceMemory memoryHandle{};
|
||||
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
|
||||
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan buffer");
|
||||
|
||||
res = vkBindBufferMemory(device.handle(), bufferHandle, memoryHandle, 0);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan buffer");
|
||||
|
||||
// upload data to buffer
|
||||
uint8_t* buf{};
|
||||
res = vkMapMemory(device.handle(), memoryHandle, 0, this->size, 0, reinterpret_cast<void**>(&buf));
|
||||
if (res != VK_SUCCESS || buf == nullptr)
|
||||
throw LSFG::vulkan_error(res, "Failed to map memory for Vulkan buffer");
|
||||
std::copy_n(reinterpret_cast<const uint8_t*>(data), this->size, buf);
|
||||
vkUnmapMemory(device.handle(), memoryHandle);
|
||||
|
||||
// store buffer and memory in shared ptr
|
||||
this->buffer = std::shared_ptr<VkBuffer>(
|
||||
new VkBuffer(bufferHandle),
|
||||
[dev = device.handle()](VkBuffer* img) {
|
||||
vkDestroyBuffer(dev, *img, nullptr);
|
||||
}
|
||||
);
|
||||
this->memory = std::shared_ptr<VkDeviceMemory>(
|
||||
new VkDeviceMemory(memoryHandle),
|
||||
[dev = device.handle()](VkDeviceMemory* mem) {
|
||||
vkFreeMemory(dev, *mem, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
125
framegen/src/core/commandbuffer.cpp
Normal file
125
framegen/src/core/commandbuffer.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/fence.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
CommandBuffer::CommandBuffer(const Core::Device& device, const CommandPool& pool) {
|
||||
// create command buffer
|
||||
const VkCommandBufferAllocateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = pool.handle(),
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1
|
||||
};
|
||||
VkCommandBuffer commandBufferHandle{};
|
||||
auto res = vkAllocateCommandBuffers(device.handle(), &desc, &commandBufferHandle);
|
||||
if (res != VK_SUCCESS || commandBufferHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to allocate command buffer");
|
||||
|
||||
// store command buffer in shared ptr
|
||||
this->state = std::make_shared<CommandBufferState>(CommandBufferState::Empty);
|
||||
this->commandBuffer = std::shared_ptr<VkCommandBuffer>(
|
||||
new VkCommandBuffer(commandBufferHandle),
|
||||
[dev = device.handle(), pool = pool.handle()](VkCommandBuffer* cmdBuffer) {
|
||||
vkFreeCommandBuffers(dev, pool, 1, cmdBuffer);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void CommandBuffer::begin() {
|
||||
if (*this->state != CommandBufferState::Empty)
|
||||
throw std::logic_error("Command buffer is not in Empty state");
|
||||
|
||||
const VkCommandBufferBeginInfo beginInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||
};
|
||||
auto res = vkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to begin command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Recording;
|
||||
}
|
||||
|
||||
void CommandBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z) const {
|
||||
if (*this->state != CommandBufferState::Recording)
|
||||
throw std::logic_error("Command buffer is not in Recording state");
|
||||
|
||||
vkCmdDispatch(*this->commandBuffer, x, y, z);
|
||||
}
|
||||
|
||||
void CommandBuffer::end() {
|
||||
if (*this->state != CommandBufferState::Recording)
|
||||
throw std::logic_error("Command buffer is not in Recording state");
|
||||
|
||||
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to end command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Full;
|
||||
}
|
||||
|
||||
void CommandBuffer::submit(VkQueue queue, std::optional<Fence> fence,
|
||||
const std::vector<Semaphore>& waitSemaphores,
|
||||
std::optional<std::vector<uint64_t>> waitSemaphoreValues,
|
||||
const std::vector<Semaphore>& signalSemaphores,
|
||||
std::optional<std::vector<uint64_t>> signalSemaphoreValues) {
|
||||
if (*this->state != CommandBufferState::Full)
|
||||
throw std::logic_error("Command buffer is not in Full state");
|
||||
|
||||
const std::vector<VkPipelineStageFlags> waitStages(waitSemaphores.size(),
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
|
||||
VkTimelineSemaphoreSubmitInfo timelineInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||
};
|
||||
if (waitSemaphoreValues.has_value()) {
|
||||
timelineInfo.waitSemaphoreValueCount =
|
||||
static_cast<uint32_t>(waitSemaphoreValues->size());
|
||||
timelineInfo.pWaitSemaphoreValues = waitSemaphoreValues->data();
|
||||
}
|
||||
if (signalSemaphoreValues.has_value()) {
|
||||
timelineInfo.signalSemaphoreValueCount =
|
||||
static_cast<uint32_t>(signalSemaphoreValues->size());
|
||||
timelineInfo.pSignalSemaphoreValues = signalSemaphoreValues->data();
|
||||
}
|
||||
|
||||
std::vector<VkSemaphore> waitSemaphoresHandles;
|
||||
waitSemaphoresHandles.reserve(waitSemaphores.size());
|
||||
for (const auto& semaphore : waitSemaphores)
|
||||
waitSemaphoresHandles.push_back(semaphore.handle());
|
||||
std::vector<VkSemaphore> signalSemaphoresHandles;
|
||||
signalSemaphoresHandles.reserve(signalSemaphores.size());
|
||||
for (const auto& semaphore : signalSemaphores)
|
||||
signalSemaphoresHandles.push_back(semaphore.handle());
|
||||
|
||||
const VkSubmitInfo submitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = (waitSemaphoreValues.has_value() || signalSemaphoreValues.has_value())
|
||||
? &timelineInfo : nullptr,
|
||||
.waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size()),
|
||||
.pWaitSemaphores = waitSemaphoresHandles.data(),
|
||||
.pWaitDstStageMask = waitStages.data(),
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &(*this->commandBuffer),
|
||||
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
||||
.pSignalSemaphores = signalSemaphoresHandles.data()
|
||||
};
|
||||
auto res = vkQueueSubmit(queue, 1, &submitInfo, fence ? fence->handle() : VK_NULL_HANDLE);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to submit command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Submitted;
|
||||
}
|
||||
30
framegen/src/core/commandpool.cpp
Normal file
30
framegen/src/core/commandpool.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
CommandPool::CommandPool(const Core::Device& device) {
|
||||
// create command pool
|
||||
const VkCommandPoolCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.queueFamilyIndex = device.getComputeFamilyIdx()
|
||||
};
|
||||
VkCommandPool commandPoolHandle{};
|
||||
auto res = vkCreateCommandPool(device.handle(), &desc, nullptr, &commandPoolHandle);
|
||||
if (res != VK_SUCCESS || commandPoolHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create command pool");
|
||||
|
||||
// store command pool in shared ptr
|
||||
this->commandPool = std::shared_ptr<VkCommandPool>(
|
||||
new VkCommandPool(commandPoolHandle),
|
||||
[dev = device.handle()](VkCommandPool* commandPoolHandle) {
|
||||
vkDestroyCommandPool(dev, *commandPoolHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
41
framegen/src/core/descriptorpool.cpp
Normal file
41
framegen/src/core/descriptorpool.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
DescriptorPool::DescriptorPool(const Core::Device& device) {
|
||||
// create descriptor pool
|
||||
const std::array<VkDescriptorPoolSize, 4> pools{{ // arbitrary limits
|
||||
{ .type = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 4096 },
|
||||
{ .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 4096 },
|
||||
{ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .descriptorCount = 4096 },
|
||||
{ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 4096 }
|
||||
}};
|
||||
const VkDescriptorPoolCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = 16384,
|
||||
.poolSizeCount = static_cast<uint32_t>(pools.size()),
|
||||
.pPoolSizes = pools.data()
|
||||
};
|
||||
VkDescriptorPool poolHandle{};
|
||||
auto res = vkCreateDescriptorPool(device.handle(), &desc, nullptr, &poolHandle);
|
||||
if (res != VK_SUCCESS || poolHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create descriptor pool");
|
||||
|
||||
// store pool in shared ptr
|
||||
this->descriptorPool = std::shared_ptr<VkDescriptorPool>(
|
||||
new VkDescriptorPool(poolHandle),
|
||||
[dev = device.handle()](VkDescriptorPool* poolHandle) {
|
||||
vkDestroyDescriptorPool(dev, *poolHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
129
framegen/src/core/descriptorset.cpp
Normal file
129
framegen/src/core/descriptorset.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/buffer.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
DescriptorSet::DescriptorSet(const Core::Device& device,
|
||||
const DescriptorPool& pool, const ShaderModule& shaderModule) {
|
||||
// create descriptor set
|
||||
VkDescriptorSetLayout layout = shaderModule.getLayout();
|
||||
const VkDescriptorSetAllocateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = pool.handle(),
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &layout
|
||||
};
|
||||
VkDescriptorSet descriptorSetHandle{};
|
||||
auto res = vkAllocateDescriptorSets(device.handle(), &desc, &descriptorSetHandle);
|
||||
if (res != VK_SUCCESS || descriptorSetHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to allocate descriptor set");
|
||||
|
||||
/// store set in shared ptr
|
||||
this->descriptorSet = std::shared_ptr<VkDescriptorSet>(
|
||||
new VkDescriptorSet(descriptorSetHandle),
|
||||
[dev = device.handle(), pool = pool](VkDescriptorSet* setHandle) {
|
||||
vkFreeDescriptorSets(dev, pool.handle(), 1, setHandle);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
DescriptorSetUpdateBuilder DescriptorSet::update(const Core::Device& device) const {
|
||||
return { *this, device };
|
||||
}
|
||||
|
||||
void DescriptorSet::bind(const CommandBuffer& commandBuffer, const Pipeline& pipeline) const {
|
||||
VkDescriptorSet descriptorSetHandle = this->handle();
|
||||
vkCmdBindDescriptorSets(commandBuffer.handle(),
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.getLayout(),
|
||||
0, 1, &descriptorSetHandle, 0, nullptr);
|
||||
}
|
||||
|
||||
// updater class
|
||||
|
||||
DescriptorSetUpdateBuilder& DescriptorSetUpdateBuilder::add(VkDescriptorType type, const Image& image) {
|
||||
this->entries.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = this->descriptorSet->handle(),
|
||||
.dstBinding = static_cast<uint32_t>(this->entries.size()),
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pImageInfo = new VkDescriptorImageInfo {
|
||||
.imageView = image.getView(),
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
|
||||
},
|
||||
.pBufferInfo = nullptr
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
DescriptorSetUpdateBuilder& DescriptorSetUpdateBuilder::add(VkDescriptorType type, const Sampler& sampler) {
|
||||
this->entries.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = this->descriptorSet->handle(),
|
||||
.dstBinding = static_cast<uint32_t>(this->entries.size()),
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pImageInfo = new VkDescriptorImageInfo {
|
||||
.sampler = sampler.handle(),
|
||||
},
|
||||
.pBufferInfo = nullptr
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
DescriptorSetUpdateBuilder& DescriptorSetUpdateBuilder::add(VkDescriptorType type, const Buffer& buffer) {
|
||||
this->entries.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = this->descriptorSet->handle(),
|
||||
.dstBinding = static_cast<uint32_t>(this->entries.size()),
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pImageInfo = nullptr,
|
||||
.pBufferInfo = new VkDescriptorBufferInfo {
|
||||
.buffer = buffer.handle(),
|
||||
.range = buffer.getSize()
|
||||
}
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
DescriptorSetUpdateBuilder& DescriptorSetUpdateBuilder::add(VkDescriptorType type) {
|
||||
this->entries.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = this->descriptorSet->handle(),
|
||||
.dstBinding = static_cast<uint32_t>(this->entries.size()),
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pImageInfo = new VkDescriptorImageInfo {
|
||||
},
|
||||
.pBufferInfo = nullptr
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::build() {
|
||||
vkUpdateDescriptorSets(this->device->handle(),
|
||||
static_cast<uint32_t>(this->entries.size()),
|
||||
this->entries.data(), 0, nullptr);
|
||||
|
||||
// NOLINTBEGIN
|
||||
for (const auto& entry : this->entries) {
|
||||
delete entry.pImageInfo;
|
||||
delete entry.pBufferInfo;
|
||||
}
|
||||
// NOLINTEND
|
||||
}
|
||||
117
framegen/src/core/device.cpp
Normal file
117
framegen/src/core/device.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/device.hpp"
|
||||
#include "core/instance.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
const std::vector<const char*> requiredExtensions = {
|
||||
"VK_KHR_external_memory_fd",
|
||||
"VK_KHR_external_semaphore_fd",
|
||||
"VK_EXT_robustness2",
|
||||
};
|
||||
|
||||
Device::Device(const Instance& instance, uint64_t deviceUUID) {
|
||||
// get all physical devices
|
||||
uint32_t deviceCount{};
|
||||
auto res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, nullptr);
|
||||
if (res != VK_SUCCESS || deviceCount == 0)
|
||||
throw LSFG::vulkan_error(res, "Failed to enumerate physical devices");
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, devices.data());
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to get physical devices");
|
||||
|
||||
// get device by uuid
|
||||
std::optional<VkPhysicalDevice> physicalDevice;
|
||||
for (const auto& device : devices) {
|
||||
VkPhysicalDeviceProperties properties;
|
||||
vkGetPhysicalDeviceProperties(device, &properties);
|
||||
|
||||
const uint64_t uuid =
|
||||
static_cast<uint64_t>(properties.vendorID) << 32 | properties.deviceID;
|
||||
if (deviceUUID == uuid || deviceUUID == 0x1463ABAC) {
|
||||
physicalDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!physicalDevice)
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED,
|
||||
"Could not find physical device with UUID");
|
||||
|
||||
// find queue family indices
|
||||
uint32_t familyCount{};
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, nullptr);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(familyCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, queueFamilies.data());
|
||||
|
||||
std::optional<uint32_t> computeFamilyIdx;
|
||||
for (uint32_t i = 0; i < familyCount; ++i) {
|
||||
if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
|
||||
computeFamilyIdx = i;
|
||||
}
|
||||
if (!computeFamilyIdx)
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No compute queue family found");
|
||||
|
||||
// create logical device
|
||||
const float queuePriority{1.0F}; // highest priority
|
||||
VkPhysicalDeviceRobustness2FeaturesEXT robustness2{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
|
||||
.nullDescriptor = VK_TRUE,
|
||||
};
|
||||
VkPhysicalDeviceVulkan13Features features13{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
|
||||
.pNext = &robustness2,
|
||||
.synchronization2 = VK_TRUE
|
||||
};
|
||||
const VkPhysicalDeviceVulkan12Features features12{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &features13,
|
||||
.timelineSemaphore = VK_TRUE,
|
||||
.vulkanMemoryModel = VK_TRUE
|
||||
};
|
||||
const VkDeviceQueueCreateInfo computeQueueDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = *computeFamilyIdx,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority
|
||||
};
|
||||
const VkDeviceCreateInfo deviceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = &features12,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &computeQueueDesc,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()),
|
||||
.ppEnabledExtensionNames = requiredExtensions.data()
|
||||
};
|
||||
VkDevice deviceHandle{};
|
||||
res = vkCreateDevice(*physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);
|
||||
if (res != VK_SUCCESS | deviceHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create logical device");
|
||||
|
||||
volkLoadDevice(deviceHandle);
|
||||
|
||||
// get compute queue
|
||||
VkQueue queueHandle{};
|
||||
vkGetDeviceQueue(deviceHandle, *computeFamilyIdx, 0, &queueHandle);
|
||||
|
||||
// store in shared ptr
|
||||
this->computeQueue = queueHandle;
|
||||
this->computeFamilyIdx = *computeFamilyIdx;
|
||||
this->physicalDevice = *physicalDevice;
|
||||
this->device = std::shared_ptr<VkDevice>(
|
||||
new VkDevice(deviceHandle),
|
||||
[](VkDevice* device) {
|
||||
vkDestroyDevice(*device, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
46
framegen/src/core/fence.cpp
Normal file
46
framegen/src/core/fence.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/fence.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
Fence::Fence(const Core::Device& device) {
|
||||
// create fence
|
||||
const VkFenceCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
|
||||
};
|
||||
VkFence fenceHandle{};
|
||||
auto res = vkCreateFence(device.handle(), &desc, nullptr, &fenceHandle);
|
||||
if (res != VK_SUCCESS || fenceHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create fence");
|
||||
|
||||
// store fence in shared ptr
|
||||
this->fence = std::shared_ptr<VkFence>(
|
||||
new VkFence(fenceHandle),
|
||||
[dev = device.handle()](VkFence* fenceHandle) {
|
||||
vkDestroyFence(dev, *fenceHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Fence::reset(const Core::Device& device) const {
|
||||
VkFence fenceHandle = this->handle();
|
||||
auto res = vkResetFences(device.handle(), 1, &fenceHandle);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to reset fence");
|
||||
}
|
||||
|
||||
bool Fence::wait(const Core::Device& device, uint64_t timeout) const {
|
||||
VkFence fenceHandle = this->handle();
|
||||
auto res = vkWaitForFences(device.handle(), 1, &fenceHandle, VK_TRUE, timeout);
|
||||
if (res != VK_SUCCESS && res != VK_TIMEOUT)
|
||||
throw LSFG::vulkan_error(res, "Unable to wait for fence");
|
||||
|
||||
return res == VK_SUCCESS;
|
||||
}
|
||||
242
framegen/src/core/image.cpp
Normal file
242
framegen/src/core/image.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/image.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags)
|
||||
: extent(extent), format(format), aspectFlags(aspectFlags) {
|
||||
// create image
|
||||
const VkImageCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = {
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = 1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VkImage imageHandle{};
|
||||
auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle);
|
||||
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
|
||||
|
||||
// find memory type
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
std::optional<uint32_t> memType{};
|
||||
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
|
||||
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
|
||||
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
|
||||
memType.emplace(i);
|
||||
break;
|
||||
} // NOLINTEND
|
||||
}
|
||||
if (!memType.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// allocate and bind memory
|
||||
const VkMemoryAllocateInfo allocInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memType.value()
|
||||
};
|
||||
VkDeviceMemory memoryHandle{};
|
||||
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
|
||||
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
|
||||
|
||||
res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
|
||||
|
||||
// create image view
|
||||
const VkImageViewCreateInfo viewDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = imageHandle,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = aspectFlags,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
|
||||
VkImageView viewHandle{};
|
||||
res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle);
|
||||
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create image view");
|
||||
|
||||
// store objects in shared ptr
|
||||
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
this->image = std::shared_ptr<VkImage>(
|
||||
new VkImage(imageHandle),
|
||||
[dev = device.handle()](VkImage* img) {
|
||||
vkDestroyImage(dev, *img, nullptr);
|
||||
}
|
||||
);
|
||||
this->memory = std::shared_ptr<VkDeviceMemory>(
|
||||
new VkDeviceMemory(memoryHandle),
|
||||
[dev = device.handle()](VkDeviceMemory* mem) {
|
||||
vkFreeMemory(dev, *mem, nullptr);
|
||||
}
|
||||
);
|
||||
this->view = std::shared_ptr<VkImageView>(
|
||||
new VkImageView(viewHandle),
|
||||
[dev = device.handle()](VkImageView* imgView) {
|
||||
vkDestroyImageView(dev, *imgView, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// shared memory constructor
|
||||
|
||||
Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd)
|
||||
: extent(extent), format(format), aspectFlags(aspectFlags) {
|
||||
// create image
|
||||
const VkExternalMemoryImageCreateInfo externalInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
|
||||
};
|
||||
const VkImageCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &externalInfo,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = {
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = 1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VkImage imageHandle{};
|
||||
auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle);
|
||||
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
|
||||
|
||||
// find memory type
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
std::optional<uint32_t> memType{};
|
||||
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
|
||||
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
|
||||
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
|
||||
memType.emplace(i);
|
||||
break;
|
||||
} // NOLINTEND
|
||||
}
|
||||
if (!memType.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// ~~allocate~~ and bind memory
|
||||
const VkMemoryDedicatedAllocateInfoKHR dedicatedInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
|
||||
.image = imageHandle,
|
||||
};
|
||||
const VkImportMemoryFdInfoKHR importInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
|
||||
.pNext = &dedicatedInfo2,
|
||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
|
||||
.fd = fd // closes the fd
|
||||
};
|
||||
const VkMemoryAllocateInfo allocInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = fd == -1 ? nullptr : &importInfo,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memType.value()
|
||||
};
|
||||
VkDeviceMemory memoryHandle{};
|
||||
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
|
||||
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
|
||||
|
||||
res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
|
||||
|
||||
// create image view
|
||||
const VkImageViewCreateInfo viewDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = imageHandle,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = aspectFlags,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
|
||||
VkImageView viewHandle{};
|
||||
res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle);
|
||||
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create image view");
|
||||
|
||||
// store objects in shared ptr
|
||||
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
this->image = std::shared_ptr<VkImage>(
|
||||
new VkImage(imageHandle),
|
||||
[dev = device.handle()](VkImage* img) {
|
||||
vkDestroyImage(dev, *img, nullptr);
|
||||
}
|
||||
);
|
||||
this->memory = std::shared_ptr<VkDeviceMemory>(
|
||||
new VkDeviceMemory(memoryHandle),
|
||||
[dev = device.handle()](VkDeviceMemory* mem) {
|
||||
vkFreeMemory(dev, *mem, nullptr);
|
||||
}
|
||||
);
|
||||
this->view = std::shared_ptr<VkImageView>(
|
||||
new VkImageView(viewHandle),
|
||||
[dev = device.handle()](VkImageView* imgView) {
|
||||
vkDestroyImageView(dev, *imgView, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
49
framegen/src/core/instance.cpp
Normal file
49
framegen/src/core/instance.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/instance.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
const std::vector<const char*> requiredExtensions = {
|
||||
|
||||
};
|
||||
|
||||
Instance::Instance() {
|
||||
volkInitialize();
|
||||
|
||||
// create Vulkan instance
|
||||
const VkApplicationInfo appInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "lsfg-vk-base",
|
||||
.applicationVersion = VK_MAKE_VERSION(0, 0, 1),
|
||||
.pEngineName = "lsfg-vk-base",
|
||||
.engineVersion = VK_MAKE_VERSION(0, 0, 1),
|
||||
.apiVersion = VK_API_VERSION_1_3
|
||||
};
|
||||
const VkInstanceCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &appInfo,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()),
|
||||
.ppEnabledExtensionNames = requiredExtensions.data()
|
||||
};
|
||||
VkInstance instanceHandle{};
|
||||
auto res = vkCreateInstance(&createInfo, nullptr, &instanceHandle);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan instance");
|
||||
|
||||
volkLoadInstance(instanceHandle);
|
||||
|
||||
// store in shared ptr
|
||||
this->instance = std::shared_ptr<VkInstance>(
|
||||
new VkInstance(instanceHandle),
|
||||
[](VkInstance* instance) {
|
||||
vkDestroyInstance(*instance, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
62
framegen/src/core/pipeline.cpp
Normal file
62
framegen/src/core/pipeline.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
Pipeline::Pipeline(const Core::Device& device, const ShaderModule& shader) {
|
||||
// create pipeline layout
|
||||
VkDescriptorSetLayout shaderLayout = shader.getLayout();
|
||||
const VkPipelineLayoutCreateInfo layoutDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &shaderLayout,
|
||||
};
|
||||
VkPipelineLayout layoutHandle{};
|
||||
auto res = vkCreatePipelineLayout(device.handle(), &layoutDesc, nullptr, &layoutHandle);
|
||||
if (res != VK_SUCCESS || !layoutHandle)
|
||||
throw LSFG::vulkan_error(res, "Failed to create pipeline layout");
|
||||
|
||||
// create pipeline
|
||||
const VkPipelineShaderStageCreateInfo shaderStageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.module = shader.handle(),
|
||||
.pName = "main",
|
||||
};
|
||||
const VkComputePipelineCreateInfo pipelineDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.stage = shaderStageInfo,
|
||||
.layout = layoutHandle,
|
||||
};
|
||||
VkPipeline pipelineHandle{};
|
||||
res = vkCreateComputePipelines(device.handle(),
|
||||
VK_NULL_HANDLE, 1, &pipelineDesc, nullptr, &pipelineHandle);
|
||||
if (res != VK_SUCCESS || !pipelineHandle)
|
||||
throw LSFG::vulkan_error(res, "Failed to create compute pipeline");
|
||||
|
||||
// store layout and pipeline in shared ptr
|
||||
this->layout = std::shared_ptr<VkPipelineLayout>(
|
||||
new VkPipelineLayout(layoutHandle),
|
||||
[dev = device.handle()](VkPipelineLayout* layout) {
|
||||
vkDestroyPipelineLayout(dev, *layout, nullptr);
|
||||
}
|
||||
);
|
||||
this->pipeline = std::shared_ptr<VkPipeline>(
|
||||
new VkPipeline(pipelineHandle),
|
||||
[dev = device.handle()](VkPipeline* pipeline) {
|
||||
vkDestroyPipeline(dev, *pipeline, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Pipeline::bind(const CommandBuffer& commandBuffer) const {
|
||||
vkCmdBindPipeline(commandBuffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, *this->pipeline);
|
||||
}
|
||||
43
framegen/src/core/sampler.cpp
Normal file
43
framegen/src/core/sampler.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
Sampler::Sampler(const Core::Device& device,
|
||||
VkSamplerAddressMode mode,
|
||||
VkCompareOp compare,
|
||||
bool isWhite) {
|
||||
// create sampler
|
||||
const VkSamplerCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
.minFilter = VK_FILTER_LINEAR,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||
.addressModeU = mode,
|
||||
.addressModeV = mode,
|
||||
.addressModeW = mode,
|
||||
.compareOp = compare,
|
||||
.maxLod = VK_LOD_CLAMP_NONE,
|
||||
.borderColor =
|
||||
isWhite ? VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
|
||||
: VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK
|
||||
};
|
||||
VkSampler samplerHandle{};
|
||||
auto res = vkCreateSampler(device.handle(), &desc, nullptr, &samplerHandle);
|
||||
if (res != VK_SUCCESS || samplerHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create sampler");
|
||||
|
||||
// store sampler in shared ptr
|
||||
this->sampler = std::shared_ptr<VkSampler>(
|
||||
new VkSampler(samplerHandle),
|
||||
[dev = device.handle()](VkSampler* samplerHandle) {
|
||||
vkDestroySampler(dev, *samplerHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
110
framegen/src/core/semaphore.cpp
Normal file
110
framegen/src/core/semaphore.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/semaphore.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
Semaphore::Semaphore(const Core::Device& device, std::optional<uint32_t> initial) {
|
||||
// create semaphore
|
||||
const VkSemaphoreTypeCreateInfo typeInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
|
||||
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
|
||||
.initialValue = initial.value_or(0)
|
||||
};
|
||||
const VkSemaphoreCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.pNext = initial.has_value() ? &typeInfo : nullptr,
|
||||
};
|
||||
VkSemaphore semaphoreHandle{};
|
||||
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
|
||||
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
||||
|
||||
// store semaphore in shared ptr
|
||||
this->isTimeline = initial.has_value();
|
||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||
new VkSemaphore(semaphoreHandle),
|
||||
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
|
||||
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(const Core::Device& device, int fd) {
|
||||
// create semaphore
|
||||
const VkExportSemaphoreCreateInfo exportInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
|
||||
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
|
||||
};
|
||||
const VkSemaphoreCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.pNext = &exportInfo
|
||||
};
|
||||
VkSemaphore semaphoreHandle{};
|
||||
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
|
||||
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
||||
|
||||
// import semaphore from fd
|
||||
auto vkImportSemaphoreFdKHR = reinterpret_cast<PFN_vkImportSemaphoreFdKHR>(
|
||||
vkGetDeviceProcAddr(device.handle(), "vkImportSemaphoreFdKHR"));
|
||||
|
||||
const VkImportSemaphoreFdInfoKHR importInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
|
||||
.semaphore = semaphoreHandle,
|
||||
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
|
||||
.fd = fd // closes the fd
|
||||
};
|
||||
res = vkImportSemaphoreFdKHR(device.handle(), &importInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to import semaphore from fd");
|
||||
|
||||
// store semaphore in shared ptr
|
||||
this->isTimeline = false;
|
||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||
new VkSemaphore(semaphoreHandle),
|
||||
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
|
||||
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Semaphore::signal(const Core::Device& device, uint64_t value) const {
|
||||
if (!this->isTimeline)
|
||||
throw std::logic_error("Invalid timeline semaphore");
|
||||
|
||||
const VkSemaphoreSignalInfo signalInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
|
||||
.semaphore = this->handle(),
|
||||
.value = value
|
||||
};
|
||||
auto res = vkSignalSemaphore(device.handle(), &signalInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to signal semaphore");
|
||||
}
|
||||
|
||||
bool Semaphore::wait(const Core::Device& device, uint64_t value, uint64_t timeout) const {
|
||||
if (!this->isTimeline)
|
||||
throw std::logic_error("Invalid timeline semaphore");
|
||||
|
||||
VkSemaphore semaphore = this->handle();
|
||||
const VkSemaphoreWaitInfo waitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
|
||||
.semaphoreCount = 1,
|
||||
.pSemaphores = &semaphore,
|
||||
.pValues = &value
|
||||
};
|
||||
auto res = vkWaitSemaphores(device.handle(), &waitInfo, timeout);
|
||||
if (res != VK_SUCCESS && res != VK_TIMEOUT)
|
||||
throw LSFG::vulkan_error(res, "Unable to wait for semaphore");
|
||||
|
||||
return res == VK_SUCCESS;
|
||||
}
|
||||
65
framegen/src/core/shadermodule.cpp
Normal file
65
framegen/src/core/shadermodule.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
using namespace LSFG::Core;
|
||||
|
||||
ShaderModule::ShaderModule(const Core::Device& device, const std::vector<uint8_t>& code,
|
||||
const std::vector<std::pair<size_t, VkDescriptorType>>& descriptorTypes) {
|
||||
// create shader module
|
||||
const uint8_t* data_ptr = code.data();
|
||||
const VkShaderModuleCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = code.size(),
|
||||
.pCode = reinterpret_cast<const uint32_t*>(data_ptr)
|
||||
};
|
||||
VkShaderModule shaderModuleHandle{};
|
||||
auto res = vkCreateShaderModule(device.handle(), &createInfo, nullptr, &shaderModuleHandle);
|
||||
if (res != VK_SUCCESS || !shaderModuleHandle)
|
||||
throw LSFG::vulkan_error(res, "Failed to create shader module");
|
||||
|
||||
// create descriptor set layout
|
||||
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
|
||||
size_t bindIdx = 0;
|
||||
for (const auto &[count, type] : descriptorTypes)
|
||||
for (size_t i = 0; i < count; i++, bindIdx++)
|
||||
layoutBindings.emplace_back(VkDescriptorSetLayoutBinding {
|
||||
.binding = static_cast<uint32_t>(bindIdx),
|
||||
.descriptorType = type,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
|
||||
});
|
||||
|
||||
const VkDescriptorSetLayoutCreateInfo layoutDesc{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = static_cast<uint32_t>(layoutBindings.size()),
|
||||
.pBindings = layoutBindings.data()
|
||||
};
|
||||
VkDescriptorSetLayout descriptorSetLayout{};
|
||||
res = vkCreateDescriptorSetLayout(device.handle(), &layoutDesc, nullptr, &descriptorSetLayout);
|
||||
if (res != VK_SUCCESS || !descriptorSetLayout)
|
||||
throw LSFG::vulkan_error(res, "Failed to create descriptor set layout");
|
||||
|
||||
// store module and layout in shared ptr
|
||||
this->shaderModule = std::shared_ptr<VkShaderModule>(
|
||||
new VkShaderModule(shaderModuleHandle),
|
||||
[dev = device.handle()](VkShaderModule* shaderModuleHandle) {
|
||||
vkDestroyShaderModule(dev, *shaderModuleHandle, nullptr);
|
||||
}
|
||||
);
|
||||
this->descriptorSetLayout = std::shared_ptr<VkDescriptorSetLayout>(
|
||||
new VkDescriptorSetLayout(descriptorSetLayout),
|
||||
[dev = device.handle()](VkDescriptorSetLayout* layout) {
|
||||
vkDestroyDescriptorSetLayout(dev, *layout, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
72
framegen/src/pool/resourcepool.cpp
Normal file
72
framegen/src/pool/resourcepool.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include "pool/resourcepool.hpp"
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG::Pool;
|
||||
|
||||
struct ConstantBuffer {
|
||||
std::array<uint32_t, 2> inputOffset;
|
||||
uint32_t firstIter;
|
||||
uint32_t firstIterS;
|
||||
uint32_t advancedColorKind;
|
||||
uint32_t hdrSupport;
|
||||
float resolutionInvScale;
|
||||
float timestamp;
|
||||
float uiThreshold;
|
||||
std::array<uint32_t, 3> pad;
|
||||
};
|
||||
|
||||
Core::Buffer ResourcePool::getBuffer(
|
||||
const Core::Device& device,
|
||||
float timestamp, bool firstIter, bool firstIterS) {
|
||||
uint64_t hash = 0;
|
||||
const union { float f; uint32_t i; } u{
|
||||
.f = timestamp };
|
||||
hash |= u.i;
|
||||
hash |= static_cast<uint64_t>(firstIter) << 32;
|
||||
hash |= static_cast<uint64_t>(firstIterS) << 33;
|
||||
|
||||
auto it = buffers.find(hash);
|
||||
if (it != buffers.end())
|
||||
return it->second;
|
||||
|
||||
// create the buffer
|
||||
const ConstantBuffer data{
|
||||
.inputOffset = { 0, 0 },
|
||||
.advancedColorKind = this->isHdr ? 2U : 0U,
|
||||
.hdrSupport = this->isHdr,
|
||||
.resolutionInvScale = this->flowScale,
|
||||
.timestamp = timestamp,
|
||||
.uiThreshold = 0.5F,
|
||||
};
|
||||
Core::Buffer buffer(device, data, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
||||
buffers[hash] = buffer;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Core::Sampler ResourcePool::getSampler(
|
||||
const Core::Device& device,
|
||||
VkSamplerAddressMode type,
|
||||
VkCompareOp compare,
|
||||
bool isWhite) {
|
||||
uint64_t hash = 0;
|
||||
hash |= static_cast<uint64_t>(type) << 0;
|
||||
hash |= static_cast<uint64_t>(compare) << 8;
|
||||
hash |= static_cast<uint64_t>(isWhite) << 16;
|
||||
|
||||
auto it = samplers.find(hash);
|
||||
if (it != samplers.end())
|
||||
return it->second;
|
||||
|
||||
// create the sampler
|
||||
Core::Sampler sampler(device, type, compare, isWhite);
|
||||
samplers[hash] = sampler;
|
||||
return sampler;
|
||||
}
|
||||
48
framegen/src/pool/shaderpool.cpp
Normal file
48
framegen/src/pool/shaderpool.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#include "pool/shaderpool.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "core/device.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG::Pool;
|
||||
|
||||
Core::ShaderModule ShaderPool::getShader(
|
||||
const Core::Device& device, const std::string& name,
|
||||
const std::vector<std::pair<size_t, VkDescriptorType>>& types) {
|
||||
auto it = shaders.find(name);
|
||||
if (it != shaders.end())
|
||||
return it->second;
|
||||
|
||||
// grab the shader
|
||||
auto bytecode = this->source(name);
|
||||
if (bytecode.empty())
|
||||
throw std::runtime_error("Shader code is empty: " + name);
|
||||
|
||||
// create the shader module
|
||||
Core::ShaderModule shader(device, bytecode, types);
|
||||
shaders[name] = shader;
|
||||
return shader;
|
||||
}
|
||||
|
||||
Core::Pipeline ShaderPool::getPipeline(
|
||||
const Core::Device& device, const std::string& name) {
|
||||
auto it = pipelines.find(name);
|
||||
if (it != pipelines.end())
|
||||
return it->second;
|
||||
|
||||
// grab the shader module
|
||||
auto shader = this->getShader(device, name, {});
|
||||
|
||||
// create the pipeline
|
||||
Core::Pipeline pipeline(device, shader);
|
||||
pipelines[name] = pipeline;
|
||||
return pipeline;
|
||||
}
|
||||
85
framegen/v3.1_include/v3_1/context.hpp
Normal file
85
framegen/v3.1_include/v3_1/context.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/image.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "core/fence.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "shaders/alpha.hpp"
|
||||
#include "shaders/beta.hpp"
|
||||
#include "shaders/delta.hpp"
|
||||
#include "shaders/gamma.hpp"
|
||||
#include "shaders/generate.hpp"
|
||||
#include "shaders/mipmaps.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
namespace LSFG_3_1 {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
class Context {
|
||||
public:
|
||||
///
|
||||
/// Create a context
|
||||
///
|
||||
/// @param vk The Vulkan instance to use.
|
||||
/// @param in0 File descriptor for the first input image.
|
||||
/// @param in1 File descriptor for the second input image.
|
||||
/// @param outN File descriptors for the output images.
|
||||
/// @param extent The size of the images.
|
||||
/// @param format The format of the images.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context fails to initialize.
|
||||
///
|
||||
Context(Vulkan& vk,
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format);
|
||||
|
||||
///
|
||||
/// Present on the context.
|
||||
///
|
||||
/// @param inSem Semaphore to wait on before starting the generation.
|
||||
/// @param outSem Semaphores to signal after each generation is done.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context fails to present.
|
||||
///
|
||||
void present(Vulkan& vk,
|
||||
int inSem, const std::vector<int>& outSem);
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Context(const Context&) = default;
|
||||
Context& operator=(const Context&) = default;
|
||||
Context(Context&&) = default;
|
||||
Context& operator=(Context&&) = default;
|
||||
~Context() = default;
|
||||
private:
|
||||
Core::Image inImg_0, inImg_1; // inImg_0 is next when fc % 2 == 0
|
||||
uint64_t frameIdx{0};
|
||||
|
||||
struct RenderData {
|
||||
Core::Semaphore inSemaphore; // signaled when input is ready
|
||||
std::vector<Core::Semaphore> internalSemaphores; // signaled when first step is done
|
||||
std::vector<Core::Semaphore> outSemaphores; // signaled when each pass is done
|
||||
std::vector<Core::Fence> completionFences; // fence for completion of each pass
|
||||
|
||||
Core::CommandBuffer cmdBuffer1;
|
||||
std::vector<Core::CommandBuffer> cmdBuffers2; // command buffers for second step
|
||||
|
||||
bool shouldWait{false};
|
||||
};
|
||||
std::array<RenderData, 8> data;
|
||||
|
||||
Shaders::Mipmaps mipmaps;
|
||||
std::array<Shaders::Alpha, 7> alpha;
|
||||
Shaders::Beta beta;
|
||||
std::array<Shaders::Gamma, 7> gamma;
|
||||
std::array<Shaders::Delta, 3> delta;
|
||||
Shaders::Generate generate;
|
||||
};
|
||||
|
||||
}
|
||||
62
framegen/v3.1_include/v3_1/shaders/alpha.hpp
Normal file
62
framegen/v3.1_include/v3_1/shaders/alpha.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Alpha shader.
|
||||
///
|
||||
class Alpha {
|
||||
public:
|
||||
Alpha() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg One mipmap level
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Alpha(Vulkan& vk, Core::Image inImg);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Alpha(const Alpha&) noexcept = default;
|
||||
Alpha& operator=(const Alpha&) noexcept = default;
|
||||
Alpha(Alpha&&) noexcept = default;
|
||||
Alpha& operator=(Alpha&&) noexcept = default;
|
||||
~Alpha() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 4> shaderModules;
|
||||
std::array<Core::Pipeline, 4> pipelines;
|
||||
Core::Sampler sampler;
|
||||
std::array<Core::DescriptorSet, 3> descriptorSets;
|
||||
std::array<Core::DescriptorSet, 3> lastDescriptorSet;
|
||||
|
||||
Core::Image inImg;
|
||||
std::array<Core::Image, 2> tempImgs1;
|
||||
std::array<Core::Image, 2> tempImgs2;
|
||||
std::array<Core::Image, 4> tempImgs3;
|
||||
std::array<std::array<Core::Image, 4>, 3> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
63
framegen/v3.1_include/v3_1/shaders/beta.hpp
Normal file
63
framegen/v3.1_include/v3_1/shaders/beta.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Beta shader.
|
||||
///
|
||||
class Beta {
|
||||
public:
|
||||
Beta() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs Three sets of four RGBA images, corresponding to a frame count % 3.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Beta(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Beta(const Beta&) noexcept = default;
|
||||
Beta& operator=(const Beta&) noexcept = default;
|
||||
Beta(Beta&&) noexcept = default;
|
||||
Beta& operator=(Beta&&) noexcept = default;
|
||||
~Beta() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 5> shaderModules;
|
||||
std::array<Core::Pipeline, 5> pipelines;
|
||||
std::array<Core::Sampler, 2> samplers;
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 4> descriptorSets;
|
||||
|
||||
std::array<std::array<Core::Image, 4>, 3> inImgs;
|
||||
std::array<Core::Image, 2> tempImgs1;
|
||||
std::array<Core::Image, 2> tempImgs2;
|
||||
std::array<Core::Image, 6> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
81
framegen/v3.1_include/v3_1/shaders/delta.hpp
Normal file
81
framegen/v3.1_include/v3_1/shaders/delta.hpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Delta shader.
|
||||
///
|
||||
class Delta {
|
||||
public:
|
||||
Delta() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs1 Three sets of four RGBA images, corresponding to a frame count % 3.
|
||||
/// @param inImg2 Second Input image
|
||||
/// @param optImg1 Optional image for non-first passes.
|
||||
/// @param optImg2 Second optional image for non-first passes.
|
||||
/// @param optImg3 Third optional image for non-first passes.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Delta(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg1,
|
||||
std::optional<Core::Image> optImg2,
|
||||
std::optional<Core::Image> optImg3);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx);
|
||||
|
||||
/// Get the first output image
|
||||
[[nodiscard]] const auto& getOutImage1() const { return this->outImg1; }
|
||||
/// Get the second output image
|
||||
[[nodiscard]] const auto& getOutImage2() const { return this->outImg2; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Delta(const Delta&) noexcept = default;
|
||||
Delta& operator=(const Delta&) noexcept = default;
|
||||
Delta(Delta&&) noexcept = default;
|
||||
Delta& operator=(Delta&&) noexcept = default;
|
||||
~Delta() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 10> shaderModules;
|
||||
std::array<Core::Pipeline, 10> pipelines;
|
||||
std::array<Core::Sampler, 3> samplers;
|
||||
struct DeltaPass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 8> descriptorSets;
|
||||
std::array<Core::DescriptorSet, 3> sixthDescriptorSet;
|
||||
};
|
||||
std::vector<DeltaPass> passes;
|
||||
|
||||
std::array<std::array<Core::Image, 4>, 3> inImgs1;
|
||||
Core::Image inImg2;
|
||||
std::optional<Core::Image> optImg1, optImg2, optImg3;
|
||||
std::array<Core::Image, 4> tempImgs1;
|
||||
std::array<Core::Image, 4> tempImgs2;
|
||||
Core::Image outImg1, outImg2;
|
||||
};
|
||||
|
||||
}
|
||||
73
framegen/v3.1_include/v3_1/shaders/gamma.hpp
Normal file
73
framegen/v3.1_include/v3_1/shaders/gamma.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Gamma shader.
|
||||
///
|
||||
class Gamma {
|
||||
public:
|
||||
Gamma() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs1 Three sets of four RGBA images, corresponding to a frame count % 3.
|
||||
/// @param inImg2 Second Input image
|
||||
/// @param optImg Optional image for non-first passes.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Gamma(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs1,
|
||||
Core::Image inImg2, std::optional<Core::Image> optImg);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx);
|
||||
|
||||
/// Get the output image
|
||||
[[nodiscard]] const auto& getOutImage() const { return this->outImg; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Gamma(const Gamma&) noexcept = default;
|
||||
Gamma& operator=(const Gamma&) noexcept = default;
|
||||
Gamma(Gamma&&) noexcept = default;
|
||||
Gamma& operator=(Gamma&&) noexcept = default;
|
||||
~Gamma() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 5> shaderModules;
|
||||
std::array<Core::Pipeline, 5> pipelines;
|
||||
std::array<Core::Sampler, 3> samplers;
|
||||
struct GammaPass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 4> descriptorSets;
|
||||
};
|
||||
std::vector<GammaPass> passes;
|
||||
|
||||
std::array<std::array<Core::Image, 4>, 3> inImgs1;
|
||||
Core::Image inImg2;
|
||||
std::optional<Core::Image> optImg;
|
||||
std::array<Core::Image, 4> tempImgs1;
|
||||
std::array<Core::Image, 4> tempImgs2;
|
||||
Core::Image outImg;
|
||||
};
|
||||
|
||||
}
|
||||
72
framegen/v3.1_include/v3_1/shaders/generate.hpp
Normal file
72
framegen/v3.1_include/v3_1/shaders/generate.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Generate shader.
|
||||
///
|
||||
class Generate {
|
||||
public:
|
||||
Generate() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg1 Input image 1.
|
||||
/// @param inImg2 Input image 2.
|
||||
/// @param inImg3 Input image 3.
|
||||
/// @param inImg4 Input image 4.
|
||||
/// @param inImg5 Input image 5.
|
||||
/// @param fds File descriptors for the output images.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Generate(Vulkan& vk,
|
||||
Core::Image inImg1, Core::Image inImg2,
|
||||
Core::Image inImg3, Core::Image inImg4, Core::Image inImg5,
|
||||
const std::vector<int>& fds, VkFormat format);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx);
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Generate(const Generate&) noexcept = default;
|
||||
Generate& operator=(const Generate&) noexcept = default;
|
||||
Generate(Generate&&) noexcept = default;
|
||||
Generate& operator=(Generate&&) noexcept = default;
|
||||
~Generate() = default;
|
||||
private:
|
||||
Core::ShaderModule shaderModule;
|
||||
Core::Pipeline pipeline;
|
||||
std::array<Core::Sampler, 2> samplers;
|
||||
struct GeneratePass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 2> descriptorSet;
|
||||
};
|
||||
std::vector<GeneratePass> passes;
|
||||
|
||||
Core::Image inImg1, inImg2;
|
||||
Core::Image inImg3, inImg4, inImg5;
|
||||
std::vector<Core::Image> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
61
framegen/v3.1_include/v3_1/shaders/mipmaps.hpp
Normal file
61
framegen/v3.1_include/v3_1/shaders/mipmaps.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Mipmaps shader.
|
||||
///
|
||||
class Mipmaps {
|
||||
public:
|
||||
Mipmaps() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg_0 The next frame (when fc % 2 == 0)
|
||||
/// @param inImg_1 The next frame (when fc % 2 == 1)
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Mipmaps(Vulkan& vk, Core::Image inImg_0, Core::Image inImg_1);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images.
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Mipmaps(const Mipmaps&) noexcept = default;
|
||||
Mipmaps& operator=(const Mipmaps&) noexcept = default;
|
||||
Mipmaps(Mipmaps&&) noexcept = default;
|
||||
Mipmaps& operator=(Mipmaps&&) noexcept = default;
|
||||
~Mipmaps() = default;
|
||||
private:
|
||||
Core::ShaderModule shaderModule;
|
||||
Core::Pipeline pipeline;
|
||||
Core::Buffer buffer;
|
||||
Core::Sampler sampler;
|
||||
std::array<Core::DescriptorSet, 2> descriptorSets;
|
||||
|
||||
Core::Image inImg_0, inImg_1;
|
||||
std::array<Core::Image, 7> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
122
framegen/v3.1_src/context.cpp
Normal file
122
framegen/v3.1_src/context.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/context.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1;
|
||||
|
||||
Context::Context(Vulkan& vk,
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format) {
|
||||
// import input images
|
||||
this->inImg_0 = Core::Image(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, in0);
|
||||
this->inImg_1 = Core::Image(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, in1);
|
||||
|
||||
// prepare render data
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
auto& data = this->data.at(i);
|
||||
data.internalSemaphores.resize(vk.generationCount);
|
||||
data.outSemaphores.resize(vk.generationCount);
|
||||
data.completionFences.resize(vk.generationCount);
|
||||
data.cmdBuffers2.resize(vk.generationCount);
|
||||
}
|
||||
|
||||
// create shader chains
|
||||
this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1);
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i));
|
||||
this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages());
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
this->gamma.at(i) = Shaders::Gamma(vk,
|
||||
this->alpha.at(6 - i).getOutImages(),
|
||||
this->beta.getOutImages().at(std::min<size_t>(6 - i, 5)),
|
||||
(i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()));
|
||||
if (i < 4) continue;
|
||||
|
||||
this->delta.at(i - 4) = Shaders::Delta(vk,
|
||||
this->alpha.at(6 - i).getOutImages(),
|
||||
this->beta.getOutImages().at(6 - i),
|
||||
(i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()),
|
||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()),
|
||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage2()));
|
||||
}
|
||||
this->generate = Shaders::Generate(vk,
|
||||
this->inImg_0, this->inImg_1,
|
||||
this->gamma.at(6).getOutImage(),
|
||||
this->delta.at(2).getOutImage1(),
|
||||
this->delta.at(2).getOutImage2(),
|
||||
outN, format);
|
||||
}
|
||||
|
||||
void Context::present(Vulkan& vk,
|
||||
int inSem, const std::vector<int>& outSem) {
|
||||
auto& data = this->data.at(this->frameIdx % 8);
|
||||
|
||||
// 3. wait for completion of previous frame in this slot
|
||||
if (data.shouldWait)
|
||||
for (auto& fence : data.completionFences)
|
||||
if (!fence.wait(vk.device, UINT64_MAX))
|
||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out");
|
||||
data.shouldWait = true;
|
||||
|
||||
// 1. create mipmaps and process input image
|
||||
if (inSem >= 0) data.inSemaphore = Core::Semaphore(vk.device, inSem);
|
||||
for (size_t i = 0; i < vk.generationCount; i++)
|
||||
data.internalSemaphores.at(i) = Core::Semaphore(vk.device);
|
||||
|
||||
data.cmdBuffer1 = Core::CommandBuffer(vk.device, vk.commandPool);
|
||||
data.cmdBuffer1.begin();
|
||||
|
||||
this->mipmaps.Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->alpha.at(6 - i).Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
this->beta.Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
|
||||
data.cmdBuffer1.end();
|
||||
std::vector<Core::Semaphore> waits = { data.inSemaphore };
|
||||
if (inSem < 0) waits.clear();
|
||||
data.cmdBuffer1.submit(vk.device.getComputeQueue(), std::nullopt,
|
||||
waits, std::nullopt,
|
||||
data.internalSemaphores, std::nullopt);
|
||||
|
||||
// 2. generate intermediary frames
|
||||
for (size_t pass = 0; pass < vk.generationCount; pass++) {
|
||||
auto& internalSemaphore = data.internalSemaphores.at(pass);
|
||||
auto& outSemaphore = data.outSemaphores.at(pass);
|
||||
if (inSem >= 0) outSemaphore = Core::Semaphore(vk.device, outSem.empty() ? -1 : outSem.at(pass));
|
||||
auto& completionFence = data.completionFences.at(pass);
|
||||
completionFence = Core::Fence(vk.device);
|
||||
|
||||
auto& buf2 = data.cmdBuffers2.at(pass);
|
||||
buf2 = Core::CommandBuffer(vk.device, vk.commandPool);
|
||||
buf2.begin();
|
||||
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
this->gamma.at(i).Dispatch(buf2, this->frameIdx, pass);
|
||||
if (i >= 4)
|
||||
this->delta.at(i - 4).Dispatch(buf2, this->frameIdx, pass);
|
||||
}
|
||||
this->generate.Dispatch(buf2, this->frameIdx, pass);
|
||||
|
||||
buf2.end();
|
||||
std::vector<Core::Semaphore> signals = { outSemaphore };
|
||||
if (inSem < 0) signals.clear();
|
||||
buf2.submit(vk.device.getComputeQueue(), completionFence,
|
||||
{ internalSemaphore }, std::nullopt,
|
||||
signals, std::nullopt);
|
||||
}
|
||||
|
||||
this->frameIdx++;
|
||||
}
|
||||
97
framegen/v3.1_src/lsfg.cpp
Normal file
97
framegen/v3.1_src/lsfg.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "lsfg_3_1.hpp"
|
||||
#include "v3_1/context.hpp"
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/instance.hpp"
|
||||
#include "pool/shaderpool.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG_3_1;
|
||||
|
||||
namespace {
|
||||
std::optional<Core::Instance> instance;
|
||||
std::optional<Vulkan> device;
|
||||
std::unordered_map<int32_t, Context> contexts;
|
||||
}
|
||||
|
||||
void LSFG_3_1::initialize(uint64_t deviceUUID,
|
||||
bool isHdr, float flowScale, uint64_t generationCount,
|
||||
const std::function<std::vector<uint8_t>(const std::string&)>& loader) {
|
||||
if (instance.has_value() || device.has_value())
|
||||
return;
|
||||
|
||||
instance.emplace();
|
||||
device.emplace(Vulkan {
|
||||
.device{*instance, deviceUUID},
|
||||
.generationCount = generationCount,
|
||||
.flowScale = flowScale,
|
||||
.isHdr = isHdr
|
||||
});
|
||||
contexts = std::unordered_map<int32_t, Context>();
|
||||
|
||||
device->commandPool = Core::CommandPool(device->device);
|
||||
device->descriptorPool = Core::DescriptorPool(device->device);
|
||||
|
||||
device->resources = Pool::ResourcePool(device->isHdr, device->flowScale);
|
||||
device->shaders = Pool::ShaderPool(loader);
|
||||
|
||||
std::srand(static_cast<uint32_t>(std::time(nullptr)));
|
||||
}
|
||||
|
||||
int32_t LSFG_3_1::createContext(
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
const int32_t id = std::rand();
|
||||
contexts.emplace(id, Context(*device, in0, in1, outN, extent, format));
|
||||
return id;
|
||||
}
|
||||
|
||||
void LSFG_3_1::presentContext(int32_t id, int inSem, const std::vector<int>& outSem) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
auto it = contexts.find(id);
|
||||
if (it == contexts.end())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Context not found");
|
||||
|
||||
it->second.present(*device, inSem, outSem);
|
||||
}
|
||||
|
||||
void LSFG_3_1::deleteContext(int32_t id) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
auto it = contexts.find(id);
|
||||
if (it == contexts.end())
|
||||
throw LSFG::vulkan_error(VK_ERROR_DEVICE_LOST, "No such context");
|
||||
|
||||
vkDeviceWaitIdle(device->device.handle());
|
||||
contexts.erase(it);
|
||||
}
|
||||
|
||||
void LSFG_3_1::finalize() {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
return;
|
||||
|
||||
vkDeviceWaitIdle(device->device.handle());
|
||||
contexts.clear();
|
||||
device.reset();
|
||||
instance.reset();
|
||||
}
|
||||
140
framegen/v3.1_src/shaders/alpha.cpp
Normal file
140
framegen/v3.1_src/shaders/alpha.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/alpha.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Alpha::Alpha(Vulkan& vk, Core::Image inImg) : inImg(std::move(inImg)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "alpha[0]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "alpha[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "alpha[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "alpha[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "alpha[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "alpha[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "alpha[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "alpha[3]")
|
||||
}};
|
||||
this->sampler = vk.resources.getSampler(vk.device);
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(i));
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->lastDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(3));
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImg.getExtent();
|
||||
const VkExtent2D halfExtent = {
|
||||
.width = (extent.width + 1) >> 1,
|
||||
.height = (extent.height + 1) >> 1
|
||||
};
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, halfExtent);
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, halfExtent);
|
||||
}
|
||||
|
||||
const VkExtent2D quarterExtent = {
|
||||
.width = (halfExtent.width + 1) >> 1,
|
||||
.height = (halfExtent.height + 1) >> 1
|
||||
};
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
this->tempImgs3.at(i) = Core::Image(vk.device, quarterExtent);
|
||||
for (size_t j = 0; j < 3; j++)
|
||||
this->outImgs.at(j).at(i) = Core::Image(vk.device, quarterExtent);
|
||||
}
|
||||
|
||||
// hook up shaders
|
||||
this->descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
this->descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
this->descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs3)
|
||||
.build();
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->lastDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs3)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs.at(i))
|
||||
.build();
|
||||
}
|
||||
|
||||
void Alpha::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto halfExtent = this->tempImgs1.at(0).getExtent();
|
||||
uint32_t threadsX = (halfExtent.width + 7) >> 3;
|
||||
uint32_t threadsY = (halfExtent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImg)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
this->descriptorSets.at(0).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
this->descriptorSets.at(1).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third pass
|
||||
const auto quarterExtent = this->tempImgs3.at(0).getExtent();
|
||||
threadsX = (quarterExtent.width + 7) >> 3;
|
||||
threadsY = (quarterExtent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs3)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
this->descriptorSets.at(2).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs3)
|
||||
.addR2W(this->outImgs.at(frameCount % 3))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
this->lastDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
162
framegen/v3.1_src/shaders/beta.cpp
Normal file
162
framegen/v3.1_src/shaders/beta.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/beta.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Beta::Beta(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs)
|
||||
: inImgs(std::move(inImgs)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "beta[0]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 12, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "beta[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "beta[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "beta[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "beta[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "beta[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "beta[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "beta[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "beta[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "beta[4]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(0));
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(i + 1));
|
||||
this->buffer = vk.resources.getBuffer(vk.device, 0.5F);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
this->outImgs.at(i) = Core::Image(vk.device,
|
||||
{ extent.width >> i, extent.height >> i },
|
||||
VK_FORMAT_R8_UNORM);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
this->firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at((i + 1) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
}
|
||||
this->descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
this->descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
this->descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
this->descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, this->buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs)
|
||||
.build();
|
||||
}
|
||||
|
||||
void Beta::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs.at(0))
|
||||
.addW2R(this->inImgs.at(1))
|
||||
.addW2R(this->inImgs.at(2))
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
this->firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
this->descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
this->descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
this->descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth pass
|
||||
threadsX = (extent.width + 31) >> 5;
|
||||
threadsY = (extent.height + 31) >> 5;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->outImgs)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
this->descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
341
framegen/v3.1_src/shaders/delta.cpp
Normal file
341
framegen/v3.1_src/shaders/delta.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/delta.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Delta::Delta(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg1,
|
||||
std::optional<Core::Image> optImg2,
|
||||
std::optional<Core::Image> optImg3)
|
||||
: inImgs1(std::move(inImgs1)), inImg2(std::move(inImg2)),
|
||||
optImg1(std::move(optImg1)), optImg2(std::move(optImg2)),
|
||||
optImg3(std::move(optImg3)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "delta[0]",
|
||||
{ { 1 , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 9, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[5]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 10, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[6]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[7]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[8]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "delta[9]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "delta[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[4]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[5]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[6]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[7]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[8]"),
|
||||
vk.shaders.getPipeline(vk.device, "delta[9]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
this->samplers.at(2) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs1.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
}
|
||||
|
||||
this->outImg1 = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
this->outImg2 = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t pass_idx = 0; pass_idx < vk.generationCount; pass_idx++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(pass_idx + 1) / static_cast<float>(vk.generationCount + 1),
|
||||
false, !this->optImg1.has_value());
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(0));
|
||||
pass.firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(2))
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(0) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(1));
|
||||
pass.descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(1) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(2));
|
||||
pass.descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
pass.descriptorSets.at(2) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(3));
|
||||
pass.descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(3) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(4));
|
||||
pass.descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg1)
|
||||
.build();
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.sixthDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(5));
|
||||
pass.sixthDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(1))
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(4) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(6));
|
||||
pass.descriptorSets.at(4).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.build();
|
||||
pass.descriptorSets.at(5) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(7));
|
||||
pass.descriptorSets.at(5).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(1))
|
||||
.build();
|
||||
pass.descriptorSets.at(6) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(8));
|
||||
pass.descriptorSets.at(6).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.build();
|
||||
pass.descriptorSets.at(7) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(9));
|
||||
pass.descriptorSets.at(7).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg3)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg2)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
void Delta::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first shader
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
const uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
const uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg1)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.addR2W(this->tempImgs1.at(2))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
pass.firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addW2R(this->tempImgs1.at(2))
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
pass.descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
pass.descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
pass.descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addW2R(this->optImg1)
|
||||
.addW2R(this->inImg2)
|
||||
.addR2W(this->outImg1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
pass.descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// sixth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg1)
|
||||
.addW2R(this->optImg2)
|
||||
.addR2W(this->tempImgs2.at(0))
|
||||
.addR2W(this->tempImgs2.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(5).bind(buf);
|
||||
pass.sixthDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(5));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// seventh shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2.at(0))
|
||||
.addW2R(this->tempImgs2.at(1))
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(6).bind(buf);
|
||||
pass.descriptorSets.at(4).bind(buf, this->pipelines.at(6));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// eighth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addR2W(this->tempImgs2.at(0))
|
||||
.addR2W(this->tempImgs2.at(1))
|
||||
.build();
|
||||
this->pipelines.at(7).bind(buf);
|
||||
pass.descriptorSets.at(5).bind(buf, this->pipelines.at(7));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// ninth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2.at(0))
|
||||
.addW2R(this->tempImgs2.at(1))
|
||||
.addW2R(this->optImg3)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(8).bind(buf);
|
||||
pass.descriptorSets.at(6).bind(buf, this->pipelines.at(8));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// tenth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addW2R(this->optImg3)
|
||||
.addR2W(this->outImg2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(9).bind(buf);
|
||||
pass.descriptorSets.at(7).bind(buf, this->pipelines.at(9));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
193
framegen/v3.1_src/shaders/gamma.cpp
Normal file
193
framegen/v3.1_src/shaders/gamma.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/gamma.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Gamma::Gamma(Vulkan& vk, std::array<std::array<Core::Image, 4>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg)
|
||||
: inImgs1(std::move(inImgs1)), inImg2(std::move(inImg2)),
|
||||
optImg(std::move(optImg)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "gamma[0]",
|
||||
{ { 1 , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 9, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "gamma[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "gamma[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "gamma[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "gamma[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "gamma[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "gamma[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "gamma[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "gamma[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "gamma[4]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
this->samplers.at(2) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs1.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
}
|
||||
|
||||
this->outImg = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t pass_idx = 0; pass_idx < vk.generationCount; pass_idx++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(pass_idx + 1) / static_cast<float>(vk.generationCount + 1),
|
||||
!this->optImg.has_value());
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(0));
|
||||
pass.firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(2))
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(0) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(1));
|
||||
pass.descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(1) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(2));
|
||||
pass.descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
pass.descriptorSets.at(2) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(3));
|
||||
pass.descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(3) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(4));
|
||||
pass.descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
void Gamma::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first shader
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
const uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
const uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.addR2W(this->tempImgs1.at(2))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
pass.firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addW2R(this->tempImgs1.at(2))
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
pass.descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
pass.descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
pass.descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addW2R(this->optImg)
|
||||
.addW2R(this->inImg2)
|
||||
.addR2W(this->outImg)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
pass.descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
83
framegen/v3.1_src/shaders/generate.cpp
Normal file
83
framegen/v3.1_src/shaders/generate.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/generate.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Generate::Generate(Vulkan& vk,
|
||||
Core::Image inImg1, Core::Image inImg2,
|
||||
Core::Image inImg3, Core::Image inImg4, Core::Image inImg5,
|
||||
const std::vector<int>& fds, VkFormat format)
|
||||
: inImg1(std::move(inImg1)), inImg2(std::move(inImg2)),
|
||||
inImg3(std::move(inImg3)), inImg4(std::move(inImg4)),
|
||||
inImg5(std::move(inImg5)) {
|
||||
// create resources
|
||||
this->shaderModule = vk.shaders.getShader(vk.device, "generate",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } });
|
||||
this->pipeline = vk.shaders.getPipeline(vk.device, "generate");
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImg1.getExtent();
|
||||
for (size_t i = 0; i < vk.generationCount; i++)
|
||||
this->outImgs.emplace_back(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, fds.empty() ? -1 : fds.at(i));
|
||||
|
||||
// hook up shaders
|
||||
for (size_t i = 0; i < vk.generationCount; i++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(i + 1) / static_cast<float>(vk.generationCount + 1));
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
pass.descriptorSet.at(j) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModule);
|
||||
pass.descriptorSet.at(j).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, j == 0 ? this->inImg2 : this->inImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, j == 0 ? this->inImg1 : this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg3)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg4)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg5)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs.at(i))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Generate::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first pass
|
||||
const auto extent = this->inImg1.getExtent();
|
||||
const uint32_t threadsX = (extent.width + 15) >> 4;
|
||||
const uint32_t threadsY = (extent.height + 15) >> 4;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImg1)
|
||||
.addW2R(this->inImg2)
|
||||
.addW2R(this->inImg3)
|
||||
.addW2R(this->inImg4)
|
||||
.addW2R(this->inImg5)
|
||||
.addR2W(this->outImgs.at(pass_idx))
|
||||
.build();
|
||||
|
||||
this->pipeline.bind(buf);
|
||||
pass.descriptorSet.at(frameCount % 2).bind(buf, this->pipeline);
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
66
framegen/v3.1_src/shaders/mipmaps.cpp
Normal file
66
framegen/v3.1_src/shaders/mipmaps.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1/shaders/mipmaps.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1::Shaders;
|
||||
|
||||
Mipmaps::Mipmaps(Vulkan& vk,
|
||||
Core::Image inImg_0, Core::Image inImg_1)
|
||||
: inImg_0(std::move(inImg_0)), inImg_1(std::move(inImg_1)) {
|
||||
// create resources
|
||||
this->shaderModule = vk.shaders.getShader(vk.device, "mipmaps",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } });
|
||||
this->pipeline = vk.shaders.getPipeline(vk.device, "mipmaps");
|
||||
this->buffer = vk.resources.getBuffer(vk.device);
|
||||
this->sampler = vk.resources.getSampler(vk.device);
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModule);
|
||||
|
||||
// create outputs
|
||||
const VkExtent2D flowExtent{
|
||||
.width = static_cast<uint32_t>(
|
||||
static_cast<float>(this->inImg_0.getExtent().width) / vk.flowScale),
|
||||
.height = static_cast<uint32_t>(
|
||||
static_cast<float>(this->inImg_0.getExtent().height) / vk.flowScale)
|
||||
};
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->outImgs.at(i) = Core::Image(vk.device,
|
||||
{ flowExtent.width >> i, flowExtent.height >> i },
|
||||
VK_FORMAT_R8_UNORM);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t fc = 0; fc < 2; fc++)
|
||||
this->descriptorSets.at(fc).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, this->buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, (fc % 2 == 0) ? this->inImg_0 : this->inImg_1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs)
|
||||
.build();
|
||||
}
|
||||
|
||||
void Mipmaps::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto flowExtent = this->outImgs.at(0).getExtent();
|
||||
const uint32_t threadsX = (flowExtent.width + 63) >> 6;
|
||||
const uint32_t threadsY = (flowExtent.height + 63) >> 6;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R((frameCount % 2 == 0) ? this->inImg_0 : this->inImg_1)
|
||||
.addR2W(this->outImgs)
|
||||
.build();
|
||||
|
||||
this->pipeline.bind(buf);
|
||||
this->descriptorSets.at(frameCount % 2).bind(buf, this->pipeline);
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
85
framegen/v3.1p_include/v3_1p/context.hpp
Normal file
85
framegen/v3.1p_include/v3_1p/context.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/image.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "core/fence.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "shaders/alpha.hpp"
|
||||
#include "shaders/beta.hpp"
|
||||
#include "shaders/delta.hpp"
|
||||
#include "shaders/gamma.hpp"
|
||||
#include "shaders/generate.hpp"
|
||||
#include "shaders/mipmaps.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
namespace LSFG_3_1P {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
class Context {
|
||||
public:
|
||||
///
|
||||
/// Create a context
|
||||
///
|
||||
/// @param vk The Vulkan instance to use.
|
||||
/// @param in0 File descriptor for the first input image.
|
||||
/// @param in1 File descriptor for the second input image.
|
||||
/// @param outN File descriptors for the output images.
|
||||
/// @param extent The size of the images.
|
||||
/// @param format The format of the images.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context fails to initialize.
|
||||
///
|
||||
Context(Vulkan& vk,
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format);
|
||||
|
||||
///
|
||||
/// Present on the context.
|
||||
///
|
||||
/// @param inSem Semaphore to wait on before starting the generation.
|
||||
/// @param outSem Semaphores to signal after each generation is done.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if the context fails to present.
|
||||
///
|
||||
void present(Vulkan& vk,
|
||||
int inSem, const std::vector<int>& outSem);
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Context(const Context&) = default;
|
||||
Context& operator=(const Context&) = default;
|
||||
Context(Context&&) = default;
|
||||
Context& operator=(Context&&) = default;
|
||||
~Context() = default;
|
||||
private:
|
||||
Core::Image inImg_0, inImg_1; // inImg_0 is next when fc % 2 == 0
|
||||
uint64_t frameIdx{0};
|
||||
|
||||
struct RenderData {
|
||||
Core::Semaphore inSemaphore; // signaled when input is ready
|
||||
std::vector<Core::Semaphore> internalSemaphores; // signaled when first step is done
|
||||
std::vector<Core::Semaphore> outSemaphores; // signaled when each pass is done
|
||||
std::vector<Core::Fence> completionFences; // fence for completion of each pass
|
||||
|
||||
Core::CommandBuffer cmdBuffer1;
|
||||
std::vector<Core::CommandBuffer> cmdBuffers2; // command buffers for second step
|
||||
|
||||
bool shouldWait{false};
|
||||
};
|
||||
std::array<RenderData, 8> data;
|
||||
|
||||
Shaders::Mipmaps mipmaps;
|
||||
std::array<Shaders::Alpha, 7> alpha;
|
||||
Shaders::Beta beta;
|
||||
std::array<Shaders::Gamma, 7> gamma;
|
||||
std::array<Shaders::Delta, 3> delta;
|
||||
Shaders::Generate generate;
|
||||
};
|
||||
|
||||
}
|
||||
62
framegen/v3.1p_include/v3_1p/shaders/alpha.hpp
Normal file
62
framegen/v3.1p_include/v3_1p/shaders/alpha.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Alpha shader.
|
||||
///
|
||||
class Alpha {
|
||||
public:
|
||||
Alpha() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg One mipmap level
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Alpha(Vulkan& vk, Core::Image inImg);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Alpha(const Alpha&) noexcept = default;
|
||||
Alpha& operator=(const Alpha&) noexcept = default;
|
||||
Alpha(Alpha&&) noexcept = default;
|
||||
Alpha& operator=(Alpha&&) noexcept = default;
|
||||
~Alpha() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 4> shaderModules;
|
||||
std::array<Core::Pipeline, 4> pipelines;
|
||||
Core::Sampler sampler;
|
||||
std::array<Core::DescriptorSet, 3> descriptorSets;
|
||||
std::array<Core::DescriptorSet, 3> lastDescriptorSet;
|
||||
|
||||
Core::Image inImg;
|
||||
Core::Image tempImg1;
|
||||
Core::Image tempImg2;
|
||||
std::array<Core::Image, 2> tempImgs3;
|
||||
std::array<std::array<Core::Image, 2>, 3> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
63
framegen/v3.1p_include/v3_1p/shaders/beta.hpp
Normal file
63
framegen/v3.1p_include/v3_1p/shaders/beta.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Beta shader.
|
||||
///
|
||||
class Beta {
|
||||
public:
|
||||
Beta() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs Three sets of two RGBA images, corresponding to a frame count % 3.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Beta(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Beta(const Beta&) noexcept = default;
|
||||
Beta& operator=(const Beta&) noexcept = default;
|
||||
Beta(Beta&&) noexcept = default;
|
||||
Beta& operator=(Beta&&) noexcept = default;
|
||||
~Beta() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 5> shaderModules;
|
||||
std::array<Core::Pipeline, 5> pipelines;
|
||||
std::array<Core::Sampler, 2> samplers;
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 4> descriptorSets;
|
||||
|
||||
std::array<std::array<Core::Image, 2>, 3> inImgs;
|
||||
std::array<Core::Image, 2> tempImgs1;
|
||||
std::array<Core::Image, 2> tempImgs2;
|
||||
std::array<Core::Image, 6> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
80
framegen/v3.1p_include/v3_1p/shaders/delta.hpp
Normal file
80
framegen/v3.1p_include/v3_1p/shaders/delta.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Delta shader.
|
||||
///
|
||||
class Delta {
|
||||
public:
|
||||
Delta() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs1 Three sets of two RGBA images, corresponding to a frame count % 3.
|
||||
/// @param inImg2 Second Input image
|
||||
/// @param optImg1 Optional image for non-first passes.
|
||||
/// @param optImg2 Second optional image for non-first passes.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Delta(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg1,
|
||||
std::optional<Core::Image> optImg2);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx,
|
||||
bool last);
|
||||
|
||||
/// Get the first output image
|
||||
[[nodiscard]] const auto& getOutImage1() const { return this->outImg1; }
|
||||
/// Get the second output image
|
||||
[[nodiscard]] const auto& getOutImage2() const { return this->outImg2; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Delta(const Delta&) noexcept = default;
|
||||
Delta& operator=(const Delta&) noexcept = default;
|
||||
Delta(Delta&&) noexcept = default;
|
||||
Delta& operator=(Delta&&) noexcept = default;
|
||||
~Delta() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 10> shaderModules;
|
||||
std::array<Core::Pipeline, 10> pipelines;
|
||||
std::array<Core::Sampler, 3> samplers;
|
||||
struct DeltaPass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 8> descriptorSets;
|
||||
std::array<Core::DescriptorSet, 3> sixthDescriptorSet;
|
||||
};
|
||||
std::vector<DeltaPass> passes;
|
||||
|
||||
std::array<std::array<Core::Image, 2>, 3> inImgs1;
|
||||
Core::Image inImg2;
|
||||
std::optional<Core::Image> optImg1, optImg2;
|
||||
std::array<Core::Image, 3> tempImgs1;
|
||||
std::array<Core::Image, 2> tempImgs2;
|
||||
Core::Image outImg1, outImg2;
|
||||
};
|
||||
|
||||
}
|
||||
73
framegen/v3.1p_include/v3_1p/shaders/gamma.hpp
Normal file
73
framegen/v3.1p_include/v3_1p/shaders/gamma.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Gamma shader.
|
||||
///
|
||||
class Gamma {
|
||||
public:
|
||||
Gamma() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImgs1 Three sets of two RGBA images, corresponding to a frame count % 3.
|
||||
/// @param inImg2 Second Input image
|
||||
/// @param optImg Optional image for non-first passes.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Gamma(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs1,
|
||||
Core::Image inImg2, std::optional<Core::Image> optImg);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx);
|
||||
|
||||
/// Get the output image
|
||||
[[nodiscard]] const auto& getOutImage() const { return this->outImg; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Gamma(const Gamma&) noexcept = default;
|
||||
Gamma& operator=(const Gamma&) noexcept = default;
|
||||
Gamma(Gamma&&) noexcept = default;
|
||||
Gamma& operator=(Gamma&&) noexcept = default;
|
||||
~Gamma() = default;
|
||||
private:
|
||||
std::array<Core::ShaderModule, 5> shaderModules;
|
||||
std::array<Core::Pipeline, 5> pipelines;
|
||||
std::array<Core::Sampler, 3> samplers;
|
||||
struct GammaPass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 3> firstDescriptorSet;
|
||||
std::array<Core::DescriptorSet, 4> descriptorSets;
|
||||
};
|
||||
std::vector<GammaPass> passes;
|
||||
|
||||
std::array<std::array<Core::Image, 2>, 3> inImgs1;
|
||||
Core::Image inImg2;
|
||||
std::optional<Core::Image> optImg;
|
||||
std::array<Core::Image, 3> tempImgs1;
|
||||
std::array<Core::Image, 2> tempImgs2;
|
||||
Core::Image outImg;
|
||||
};
|
||||
|
||||
}
|
||||
72
framegen/v3.1p_include/v3_1p/shaders/generate.hpp
Normal file
72
framegen/v3.1p_include/v3_1p/shaders/generate.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Generate shader.
|
||||
///
|
||||
class Generate {
|
||||
public:
|
||||
Generate() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg1 Input image 1.
|
||||
/// @param inImg2 Input image 2.
|
||||
/// @param inImg3 Input image 3.
|
||||
/// @param inImg4 Input image 4.
|
||||
/// @param inImg5 Input image 5.
|
||||
/// @param fds File descriptors for the output images.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Generate(Vulkan& vk,
|
||||
Core::Image inImg1, Core::Image inImg2,
|
||||
Core::Image inImg3, Core::Image inImg4, Core::Image inImg5,
|
||||
const std::vector<int>& fds, VkFormat format);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx);
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Generate(const Generate&) noexcept = default;
|
||||
Generate& operator=(const Generate&) noexcept = default;
|
||||
Generate(Generate&&) noexcept = default;
|
||||
Generate& operator=(Generate&&) noexcept = default;
|
||||
~Generate() = default;
|
||||
private:
|
||||
Core::ShaderModule shaderModule;
|
||||
Core::Pipeline pipeline;
|
||||
std::array<Core::Sampler, 2> samplers;
|
||||
struct GeneratePass {
|
||||
Core::Buffer buffer;
|
||||
std::array<Core::DescriptorSet, 2> descriptorSet;
|
||||
};
|
||||
std::vector<GeneratePass> passes;
|
||||
|
||||
Core::Image inImg1, inImg2;
|
||||
Core::Image inImg3, inImg4, inImg5;
|
||||
std::vector<Core::Image> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
61
framegen/v3.1p_include/v3_1p/shaders/mipmaps.hpp
Normal file
61
framegen/v3.1p_include/v3_1p/shaders/mipmaps.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/buffer.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/descriptorset.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/pipeline.hpp"
|
||||
#include "core/sampler.hpp"
|
||||
#include "core/shadermodule.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LSFG_3_1P::Shaders {
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
///
|
||||
/// Mipmaps shader.
|
||||
///
|
||||
class Mipmaps {
|
||||
public:
|
||||
Mipmaps() = default;
|
||||
|
||||
///
|
||||
/// Initialize the shaderchain.
|
||||
///
|
||||
/// @param inImg_0 The next frame (when fc % 2 == 0)
|
||||
/// @param inImg_1 The next frame (when fc % 2 == 1)
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if resource creation fails.
|
||||
///
|
||||
Mipmaps(Vulkan& vk, Core::Image inImg_0, Core::Image inImg_1);
|
||||
|
||||
///
|
||||
/// Dispatch the shaderchain.
|
||||
///
|
||||
void Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount);
|
||||
|
||||
/// Get the output images.
|
||||
[[nodiscard]] const auto& getOutImages() const { return this->outImgs; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Mipmaps(const Mipmaps&) noexcept = default;
|
||||
Mipmaps& operator=(const Mipmaps&) noexcept = default;
|
||||
Mipmaps(Mipmaps&&) noexcept = default;
|
||||
Mipmaps& operator=(Mipmaps&&) noexcept = default;
|
||||
~Mipmaps() = default;
|
||||
private:
|
||||
Core::ShaderModule shaderModule;
|
||||
Core::Pipeline pipeline;
|
||||
Core::Buffer buffer;
|
||||
Core::Sampler sampler;
|
||||
std::array<Core::DescriptorSet, 2> descriptorSets;
|
||||
|
||||
Core::Image inImg_0, inImg_1;
|
||||
std::array<Core::Image, 7> outImgs;
|
||||
};
|
||||
|
||||
}
|
||||
122
framegen/v3.1p_src/context.cpp
Normal file
122
framegen/v3.1p_src/context.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/context.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "common/exception.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG_3_1P;
|
||||
|
||||
Context::Context(Vulkan& vk,
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format) {
|
||||
// import input images
|
||||
this->inImg_0 = Core::Image(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, in0);
|
||||
this->inImg_1 = Core::Image(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, in1);
|
||||
|
||||
// prepare render data
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
auto& data = this->data.at(i);
|
||||
data.internalSemaphores.resize(vk.generationCount);
|
||||
data.outSemaphores.resize(vk.generationCount);
|
||||
data.completionFences.resize(vk.generationCount);
|
||||
data.cmdBuffers2.resize(vk.generationCount);
|
||||
}
|
||||
|
||||
// create shader chains
|
||||
this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1);
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i));
|
||||
this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages());
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
this->gamma.at(i) = Shaders::Gamma(vk,
|
||||
this->alpha.at(6 - i).getOutImages(),
|
||||
this->beta.getOutImages().at(std::min<size_t>(6 - i, 5)),
|
||||
(i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()));
|
||||
if (i < 4) continue;
|
||||
|
||||
this->delta.at(i - 4) = Shaders::Delta(vk,
|
||||
this->alpha.at(6 - i).getOutImages(),
|
||||
this->beta.getOutImages().at(6 - i),
|
||||
(i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()),
|
||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()));
|
||||
}
|
||||
this->generate = Shaders::Generate(vk,
|
||||
this->inImg_0, this->inImg_1,
|
||||
this->gamma.at(6).getOutImage(),
|
||||
this->delta.at(2).getOutImage1(),
|
||||
this->delta.at(2).getOutImage2(),
|
||||
outN, format);
|
||||
}
|
||||
|
||||
void Context::present(Vulkan& vk,
|
||||
int inSem, const std::vector<int>& outSem) {
|
||||
auto& data = this->data.at(this->frameIdx % 8);
|
||||
|
||||
// 3. wait for completion of previous frame in this slot
|
||||
if (data.shouldWait)
|
||||
for (auto& fence : data.completionFences)
|
||||
if (!fence.wait(vk.device, UINT64_MAX))
|
||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out");
|
||||
data.shouldWait = true;
|
||||
|
||||
// 1. create mipmaps and process input image
|
||||
if (inSem >= 0) data.inSemaphore = Core::Semaphore(vk.device, inSem);
|
||||
for (size_t i = 0; i < vk.generationCount; i++)
|
||||
data.internalSemaphores.at(i) = Core::Semaphore(vk.device);
|
||||
|
||||
data.cmdBuffer1 = Core::CommandBuffer(vk.device, vk.commandPool);
|
||||
data.cmdBuffer1.begin();
|
||||
|
||||
this->mipmaps.Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->alpha.at(6 - i).Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
this->beta.Dispatch(data.cmdBuffer1, this->frameIdx);
|
||||
|
||||
data.cmdBuffer1.end();
|
||||
std::vector<Core::Semaphore> waits = { data.inSemaphore };
|
||||
if (inSem < 0) waits.clear();
|
||||
data.cmdBuffer1.submit(vk.device.getComputeQueue(), std::nullopt,
|
||||
waits, std::nullopt,
|
||||
data.internalSemaphores, std::nullopt);
|
||||
|
||||
// 2. generate intermediary frames
|
||||
for (size_t pass = 0; pass < vk.generationCount; pass++) {
|
||||
auto& internalSemaphore = data.internalSemaphores.at(pass);
|
||||
auto& outSemaphore = data.outSemaphores.at(pass);
|
||||
if (inSem >= 0) outSemaphore = Core::Semaphore(vk.device, outSem.empty() ? -1 : outSem.at(pass));
|
||||
auto& completionFence = data.completionFences.at(pass);
|
||||
completionFence = Core::Fence(vk.device);
|
||||
|
||||
auto& buf2 = data.cmdBuffers2.at(pass);
|
||||
buf2 = Core::CommandBuffer(vk.device, vk.commandPool);
|
||||
buf2.begin();
|
||||
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
this->gamma.at(i).Dispatch(buf2, this->frameIdx, pass);
|
||||
if (i >= 4)
|
||||
this->delta.at(i - 4).Dispatch(buf2, this->frameIdx, pass, i == 6);
|
||||
}
|
||||
this->generate.Dispatch(buf2, this->frameIdx, pass);
|
||||
|
||||
buf2.end();
|
||||
std::vector<Core::Semaphore> signals = { outSemaphore };
|
||||
if (inSem < 0) signals.clear();
|
||||
buf2.submit(vk.device.getComputeQueue(), completionFence,
|
||||
{ internalSemaphore }, std::nullopt,
|
||||
signals, std::nullopt);
|
||||
}
|
||||
|
||||
this->frameIdx++;
|
||||
}
|
||||
97
framegen/v3.1p_src/lsfg.cpp
Normal file
97
framegen/v3.1p_src/lsfg.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "lsfg_3_1p.hpp"
|
||||
#include "v3_1p/context.hpp"
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/descriptorpool.hpp"
|
||||
#include "core/instance.hpp"
|
||||
#include "pool/shaderpool.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace LSFG;
|
||||
using namespace LSFG_3_1P;
|
||||
|
||||
namespace {
|
||||
std::optional<Core::Instance> instance;
|
||||
std::optional<Vulkan> device;
|
||||
std::unordered_map<int32_t, Context> contexts;
|
||||
}
|
||||
|
||||
void LSFG_3_1P::initialize(uint64_t deviceUUID,
|
||||
bool isHdr, float flowScale, uint64_t generationCount,
|
||||
const std::function<std::vector<uint8_t>(const std::string&)>& loader) {
|
||||
if (instance.has_value() || device.has_value())
|
||||
return;
|
||||
|
||||
instance.emplace();
|
||||
device.emplace(Vulkan {
|
||||
.device{*instance, deviceUUID},
|
||||
.generationCount = generationCount,
|
||||
.flowScale = flowScale,
|
||||
.isHdr = isHdr
|
||||
});
|
||||
contexts = std::unordered_map<int32_t, Context>();
|
||||
|
||||
device->commandPool = Core::CommandPool(device->device);
|
||||
device->descriptorPool = Core::DescriptorPool(device->device);
|
||||
|
||||
device->resources = Pool::ResourcePool(device->isHdr, device->flowScale);
|
||||
device->shaders = Pool::ShaderPool(loader);
|
||||
|
||||
std::srand(static_cast<uint32_t>(std::time(nullptr)));
|
||||
}
|
||||
|
||||
int32_t LSFG_3_1P::createContext(
|
||||
int in0, int in1, const std::vector<int>& outN,
|
||||
VkExtent2D extent, VkFormat format) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
const int32_t id = std::rand();
|
||||
contexts.emplace(id, Context(*device, in0, in1, outN, extent, format));
|
||||
return id;
|
||||
}
|
||||
|
||||
void LSFG_3_1P::presentContext(int32_t id, int inSem, const std::vector<int>& outSem) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
auto it = contexts.find(id);
|
||||
if (it == contexts.end())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Context not found");
|
||||
|
||||
it->second.present(*device, inSem, outSem);
|
||||
}
|
||||
|
||||
void LSFG_3_1P::deleteContext(int32_t id) {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
||||
|
||||
auto it = contexts.find(id);
|
||||
if (it == contexts.end())
|
||||
throw LSFG::vulkan_error(VK_ERROR_DEVICE_LOST, "No such context");
|
||||
|
||||
vkDeviceWaitIdle(device->device.handle());
|
||||
contexts.erase(it);
|
||||
}
|
||||
|
||||
void LSFG_3_1P::finalize() {
|
||||
if (!instance.has_value() || !device.has_value())
|
||||
return;
|
||||
|
||||
vkDeviceWaitIdle(device->device.handle());
|
||||
contexts.clear();
|
||||
device.reset();
|
||||
instance.reset();
|
||||
}
|
||||
138
framegen/v3.1p_src/shaders/alpha.cpp
Normal file
138
framegen/v3.1p_src/shaders/alpha.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/alpha.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Alpha::Alpha(Vulkan& vk, Core::Image inImg) : inImg(std::move(inImg)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "p_alpha[0]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_alpha[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_alpha[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_alpha[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "p_alpha[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_alpha[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_alpha[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_alpha[3]")
|
||||
}};
|
||||
this->sampler = vk.resources.getSampler(vk.device);
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(i));
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->lastDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(3));
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImg.getExtent();
|
||||
const VkExtent2D halfExtent = {
|
||||
.width = (extent.width + 1) >> 1,
|
||||
.height = (extent.height + 1) >> 1
|
||||
};
|
||||
this->tempImg1 = Core::Image(vk.device, halfExtent);
|
||||
this->tempImg2 = Core::Image(vk.device, halfExtent);
|
||||
|
||||
const VkExtent2D quarterExtent = {
|
||||
.width = (halfExtent.width + 1) >> 1,
|
||||
.height = (halfExtent.height + 1) >> 1
|
||||
};
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
this->tempImgs3.at(i) = Core::Image(vk.device, quarterExtent);
|
||||
for (size_t j = 0; j < 3; j++)
|
||||
this->outImgs.at(j).at(i) = Core::Image(vk.device, quarterExtent);
|
||||
}
|
||||
|
||||
// hook up shaders
|
||||
this->descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImg1)
|
||||
.build();
|
||||
this->descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImg2)
|
||||
.build();
|
||||
this->descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs3)
|
||||
.build();
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->lastDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs3)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs.at(i))
|
||||
.build();
|
||||
}
|
||||
|
||||
void Alpha::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto halfExtent = this->tempImg1.getExtent();
|
||||
uint32_t threadsX = (halfExtent.width + 7) >> 3;
|
||||
uint32_t threadsY = (halfExtent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImg)
|
||||
.addR2W(this->tempImg1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
this->descriptorSets.at(0).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImg1)
|
||||
.addR2W(this->tempImg2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
this->descriptorSets.at(1).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third pass
|
||||
const auto quarterExtent = this->tempImgs3.at(0).getExtent();
|
||||
threadsX = (quarterExtent.width + 7) >> 3;
|
||||
threadsY = (quarterExtent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImg2)
|
||||
.addR2W(this->tempImgs3)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
this->descriptorSets.at(2).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs3)
|
||||
.addR2W(this->outImgs.at(frameCount % 3))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
this->lastDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
162
framegen/v3.1p_src/shaders/beta.cpp
Normal file
162
framegen/v3.1p_src/shaders/beta.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/beta.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Beta::Beta(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs)
|
||||
: inImgs(std::move(inImgs)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "p_beta[0]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_beta[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_beta[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_beta[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_beta[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "p_beta[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_beta[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_beta[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_beta[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_beta[4]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(0));
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModules.at(i + 1));
|
||||
this->buffer = vk.resources.getBuffer(vk.device, 0.5F);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
this->outImgs.at(i) = Core::Image(vk.device,
|
||||
{ extent.width >> i, extent.height >> i },
|
||||
VK_FORMAT_R8_UNORM);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
this->firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at((i + 1) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
}
|
||||
this->descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
this->descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
this->descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
this->descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, this->buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs)
|
||||
.build();
|
||||
}
|
||||
|
||||
void Beta::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs.at(0))
|
||||
.addW2R(this->inImgs.at(1))
|
||||
.addW2R(this->inImgs.at(2))
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
this->firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
this->descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
this->descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth pass
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
this->descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth pass
|
||||
threadsX = (extent.width + 31) >> 5;
|
||||
threadsY = (extent.height + 31) >> 5;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->outImgs)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
this->descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
323
framegen/v3.1p_src/shaders/delta.cpp
Normal file
323
framegen/v3.1p_src/shaders/delta.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/delta.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Delta::Delta(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg1,
|
||||
std::optional<Core::Image> optImg2)
|
||||
: inImgs1(std::move(inImgs1)), inImg2(std::move(inImg2)),
|
||||
optImg1(std::move(optImg1)), optImg2(std::move(optImg2)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "p_delta[0]",
|
||||
{ { 1 , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[5]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[6]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[7]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[8]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_delta[9]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[4]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[5]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[6]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[7]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[8]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_delta[9]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
this->samplers.at(2) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs1.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
|
||||
this->outImg1 = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
this->outImg2 = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t pass_idx = 0; pass_idx < vk.generationCount; pass_idx++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(pass_idx + 1) / static_cast<float>(vk.generationCount + 1),
|
||||
false, !this->optImg1.has_value());
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(0));
|
||||
pass.firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(0) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(1));
|
||||
pass.descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(1) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(2));
|
||||
pass.descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.build();
|
||||
pass.descriptorSets.at(2) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(3));
|
||||
pass.descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(3) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(4));
|
||||
pass.descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg1)
|
||||
.build();
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.sixthDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(5));
|
||||
pass.sixthDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(0))
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(4) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(6));
|
||||
pass.descriptorSets.at(4).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.build();
|
||||
pass.descriptorSets.at(5) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(7));
|
||||
pass.descriptorSets.at(5).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2.at(0))
|
||||
.build();
|
||||
pass.descriptorSets.at(6) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(8));
|
||||
pass.descriptorSets.at(6).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.build();
|
||||
pass.descriptorSets.at(7) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(9));
|
||||
pass.descriptorSets.at(7).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg2)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
void Delta::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx,
|
||||
bool last) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first shader
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
const uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
const uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg1)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
pass.firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
pass.descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
pass.descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
pass.descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addW2R(this->optImg1)
|
||||
.addW2R(this->inImg2)
|
||||
.addR2W(this->outImg1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
pass.descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// sixth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg1)
|
||||
.addW2R(this->optImg2)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(5).bind(buf);
|
||||
pass.sixthDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(5));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
if (!last)
|
||||
return;
|
||||
|
||||
// seventh shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(6).bind(buf);
|
||||
pass.descriptorSets.at(4).bind(buf, this->pipelines.at(6));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// eighth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
this->pipelines.at(7).bind(buf);
|
||||
pass.descriptorSets.at(5).bind(buf, this->pipelines.at(7));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// ninth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(8).bind(buf);
|
||||
pass.descriptorSets.at(6).bind(buf, this->pipelines.at(8));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// tenth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addR2W(this->outImg2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(9).bind(buf);
|
||||
pass.descriptorSets.at(7).bind(buf, this->pipelines.at(9));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
189
framegen/v3.1p_src/shaders/gamma.cpp
Normal file
189
framegen/v3.1p_src/shaders/gamma.cpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/gamma.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Gamma::Gamma(Vulkan& vk, std::array<std::array<Core::Image, 2>, 3> inImgs1,
|
||||
Core::Image inImg2,
|
||||
std::optional<Core::Image> optImg)
|
||||
: inImgs1(std::move(inImgs1)), inImg2(std::move(inImg2)),
|
||||
optImg(std::move(optImg)) {
|
||||
// create resources
|
||||
this->shaderModules = {{
|
||||
vk.shaders.getShader(vk.device, "p_gamma[0]",
|
||||
{ { 1 , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_gamma[1]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_gamma[2]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_gamma[3]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } }),
|
||||
vk.shaders.getShader(vk.device, "p_gamma[4]",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } })
|
||||
}};
|
||||
this->pipelines = {{
|
||||
vk.shaders.getPipeline(vk.device, "p_gamma[0]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_gamma[1]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_gamma[2]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_gamma[3]"),
|
||||
vk.shaders.getPipeline(vk.device, "p_gamma[4]")
|
||||
}};
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true);
|
||||
this->samplers.at(2) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImgs1.at(0).at(0).getExtent();
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
this->tempImgs1.at(i) = Core::Image(vk.device, extent);
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
this->tempImgs2.at(i) = Core::Image(vk.device, extent);
|
||||
|
||||
this->outImg = Core::Image(vk.device,
|
||||
{ extent.width, extent.height },
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t pass_idx = 0; pass_idx < vk.generationCount; pass_idx++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(pass_idx + 1) / static_cast<float>(vk.generationCount + 1),
|
||||
!this->optImg.has_value());
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
pass.firstDescriptorSet.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(0));
|
||||
pass.firstDescriptorSet.at(i).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at((i + 2) % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImgs1.at(i % 3))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1)
|
||||
.build();
|
||||
}
|
||||
pass.descriptorSets.at(0) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(1));
|
||||
pass.descriptorSets.at(0).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(1) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(2));
|
||||
pass.descriptorSets.at(1).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs1.at(1))
|
||||
.build();
|
||||
pass.descriptorSets.at(2) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(3));
|
||||
pass.descriptorSets.at(2).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs1.at(1))
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->tempImgs2)
|
||||
.build();
|
||||
pass.descriptorSets.at(3) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModules.at(4));
|
||||
pass.descriptorSets.at(3).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(0))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers.at(2))
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->tempImgs2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->optImg)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImg)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
void Gamma::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first shader
|
||||
const auto extent = this->tempImgs1.at(0).getExtent();
|
||||
const uint32_t threadsX = (extent.width + 7) >> 3;
|
||||
const uint32_t threadsY = (extent.height + 7) >> 3;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImgs1.at((frameCount + 2) % 3))
|
||||
.addW2R(this->inImgs1.at(frameCount % 3))
|
||||
.addW2R(this->optImg)
|
||||
.addR2W(this->tempImgs1)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(0).bind(buf);
|
||||
pass.firstDescriptorSet.at(frameCount % 3).bind(buf, this->pipelines.at(0));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// second shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1)
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(1).bind(buf);
|
||||
pass.descriptorSets.at(0).bind(buf, this->pipelines.at(1));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// third shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addR2W(this->tempImgs1.at(0))
|
||||
.addR2W(this->tempImgs1.at(1))
|
||||
.build();
|
||||
|
||||
this->pipelines.at(2).bind(buf);
|
||||
pass.descriptorSets.at(1).bind(buf, this->pipelines.at(2));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fourth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs1.at(0))
|
||||
.addW2R(this->tempImgs1.at(1))
|
||||
.addR2W(this->tempImgs2)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(3).bind(buf);
|
||||
pass.descriptorSets.at(2).bind(buf, this->pipelines.at(3));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
|
||||
// fifth shader
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->tempImgs2)
|
||||
.addW2R(this->optImg)
|
||||
.addW2R(this->inImg2)
|
||||
.addR2W(this->outImg)
|
||||
.build();
|
||||
|
||||
this->pipelines.at(4).bind(buf);
|
||||
pass.descriptorSets.at(3).bind(buf, this->pipelines.at(4));
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
83
framegen/v3.1p_src/shaders/generate.cpp
Normal file
83
framegen/v3.1p_src/shaders/generate.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/generate.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
#include "core/image.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Generate::Generate(Vulkan& vk,
|
||||
Core::Image inImg1, Core::Image inImg2,
|
||||
Core::Image inImg3, Core::Image inImg4, Core::Image inImg5,
|
||||
const std::vector<int>& fds, VkFormat format)
|
||||
: inImg1(std::move(inImg1)), inImg2(std::move(inImg2)),
|
||||
inImg3(std::move(inImg3)), inImg4(std::move(inImg4)),
|
||||
inImg5(std::move(inImg5)) {
|
||||
// create resources
|
||||
this->shaderModule = vk.shaders.getShader(vk.device, "p_generate",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } });
|
||||
this->pipeline = vk.shaders.getPipeline(vk.device, "p_generate");
|
||||
this->samplers.at(0) = vk.resources.getSampler(vk.device);
|
||||
this->samplers.at(1) = vk.resources.getSampler(vk.device,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS);
|
||||
|
||||
// create internal images/outputs
|
||||
const VkExtent2D extent = this->inImg1.getExtent();
|
||||
for (size_t i = 0; i < vk.generationCount; i++)
|
||||
this->outImgs.emplace_back(vk.device, extent, format,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, fds.empty() ? -1 : fds.at(i));
|
||||
|
||||
// hook up shaders
|
||||
for (size_t i = 0; i < vk.generationCount; i++) {
|
||||
auto& pass = this->passes.emplace_back();
|
||||
pass.buffer = vk.resources.getBuffer(vk.device,
|
||||
static_cast<float>(i + 1) / static_cast<float>(vk.generationCount + 1));
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
pass.descriptorSet.at(j) = Core::DescriptorSet(vk.device, vk.descriptorPool,
|
||||
this->shaderModule);
|
||||
pass.descriptorSet.at(j).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pass.buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->samplers)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, j == 0 ? this->inImg2 : this->inImg1)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, j == 0 ? this->inImg1 : this->inImg2)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg3)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg4)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, this->inImg5)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs.at(i))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Generate::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount, uint64_t pass_idx) {
|
||||
auto& pass = this->passes.at(pass_idx);
|
||||
|
||||
// first pass
|
||||
const auto extent = this->inImg1.getExtent();
|
||||
const uint32_t threadsX = (extent.width + 15) >> 4;
|
||||
const uint32_t threadsY = (extent.height + 15) >> 4;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R(this->inImg1)
|
||||
.addW2R(this->inImg2)
|
||||
.addW2R(this->inImg3)
|
||||
.addW2R(this->inImg4)
|
||||
.addW2R(this->inImg5)
|
||||
.addR2W(this->outImgs.at(pass_idx))
|
||||
.build();
|
||||
|
||||
this->pipeline.bind(buf);
|
||||
pass.descriptorSet.at(frameCount % 2).bind(buf, this->pipeline);
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
66
framegen/v3.1p_src/shaders/mipmaps.cpp
Normal file
66
framegen/v3.1p_src/shaders/mipmaps.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include <volk.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "v3_1p/shaders/mipmaps.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "core/image.hpp"
|
||||
#include "core/commandbuffer.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LSFG_3_1P::Shaders;
|
||||
|
||||
Mipmaps::Mipmaps(Vulkan& vk,
|
||||
Core::Image inImg_0, Core::Image inImg_1)
|
||||
: inImg_0(std::move(inImg_0)), inImg_1(std::move(inImg_1)) {
|
||||
// create resources
|
||||
this->shaderModule = vk.shaders.getShader(vk.device, "p_mipmaps",
|
||||
{ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
|
||||
{ 7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE } });
|
||||
this->pipeline = vk.shaders.getPipeline(vk.device, "p_mipmaps");
|
||||
this->buffer = vk.resources.getBuffer(vk.device);
|
||||
this->sampler = vk.resources.getSampler(vk.device);
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
this->descriptorSets.at(i) = Core::DescriptorSet(vk.device, vk.descriptorPool, this->shaderModule);
|
||||
|
||||
// create outputs
|
||||
const VkExtent2D flowExtent{
|
||||
.width = static_cast<uint32_t>(
|
||||
static_cast<float>(this->inImg_0.getExtent().width) / vk.flowScale),
|
||||
.height = static_cast<uint32_t>(
|
||||
static_cast<float>(this->inImg_0.getExtent().height) / vk.flowScale)
|
||||
};
|
||||
for (size_t i = 0; i < 7; i++)
|
||||
this->outImgs.at(i) = Core::Image(vk.device,
|
||||
{ flowExtent.width >> i, flowExtent.height >> i },
|
||||
VK_FORMAT_R8_UNORM);
|
||||
|
||||
// hook up shaders
|
||||
for (size_t fc = 0; fc < 2; fc++)
|
||||
this->descriptorSets.at(fc).update(vk.device)
|
||||
.add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, this->buffer)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLER, this->sampler)
|
||||
.add(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, (fc % 2 == 0) ? this->inImg_0 : this->inImg_1)
|
||||
.add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, this->outImgs)
|
||||
.build();
|
||||
}
|
||||
|
||||
void Mipmaps::Dispatch(const Core::CommandBuffer& buf, uint64_t frameCount) {
|
||||
// first pass
|
||||
const auto flowExtent = this->outImgs.at(0).getExtent();
|
||||
const uint32_t threadsX = (flowExtent.width + 63) >> 6;
|
||||
const uint32_t threadsY = (flowExtent.height + 63) >> 6;
|
||||
|
||||
Utils::BarrierBuilder(buf)
|
||||
.addW2R((frameCount % 2 == 0) ? this->inImg_0 : this->inImg_1)
|
||||
.addR2W(this->outImgs)
|
||||
.build();
|
||||
|
||||
this->pipeline.bind(buf);
|
||||
this->descriptorSets.at(frameCount % 2).bind(buf, this->pipeline);
|
||||
buf.dispatch(threadsX, threadsY, 1);
|
||||
}
|
||||
60
include/config/config.hpp
Normal file
60
include/config/config.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace Config {
|
||||
|
||||
/// lsfg-vk configuration
|
||||
struct Configuration {
|
||||
/// Whether lsfg-vk should be loaded in the first place.
|
||||
bool enable{false};
|
||||
/// Path to Lossless.dll.
|
||||
std::string dll;
|
||||
|
||||
/// The frame generation muliplier
|
||||
size_t multiplier{2};
|
||||
/// The internal flow scale factor
|
||||
float flowScale{1.0F};
|
||||
/// Whether performance mode is enabled
|
||||
bool performance{false};
|
||||
/// Whether HDR is enabled
|
||||
bool hdr{false};
|
||||
|
||||
/// Experimental flag for overriding the synchronization method.
|
||||
VkPresentModeKHR e_present;
|
||||
|
||||
/// Path to the configuration file.
|
||||
std::filesystem::path config_file;
|
||||
/// File timestamp of the configuration file
|
||||
std::chrono::time_point<std::chrono::file_clock> timestamp;
|
||||
};
|
||||
|
||||
/// Active configuration. Must be set in main.cpp.
|
||||
extern Configuration activeConf;
|
||||
|
||||
///
|
||||
/// Read the configuration file while preserving the previous configuration
|
||||
/// in case of an error.
|
||||
///
|
||||
/// @param file The path to the configuration file.
|
||||
///
|
||||
/// @throws std::runtime_error if an error occurs while loading the configuration file.
|
||||
///
|
||||
void updateConfig(const std::string& file);
|
||||
|
||||
///
|
||||
/// Get the configuration for a game.
|
||||
///
|
||||
/// @param name The name of the executable to fetch.
|
||||
/// @return The configuration for the game or global configuration.
|
||||
///
|
||||
/// @throws std::runtime_error if the configuration is invalid.
|
||||
///
|
||||
Configuration getConfig(const std::pair<std::string, std::string>& name);
|
||||
|
||||
}
|
||||
36
include/config/default_conf.hpp
Normal file
36
include/config/default_conf.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
const std::string DEFAULT_CONFIG = R"(version = 1
|
||||
[global]
|
||||
# override the location of Lossless Scaling
|
||||
# dll = "/games/Lossless Scaling/Lossless.dll"
|
||||
|
||||
# [[game]] # example entry
|
||||
# exe = "Game.exe"
|
||||
#
|
||||
# multiplier = 3
|
||||
# flow_scale = 0.7
|
||||
# performance_mode = true
|
||||
# hdr_mode = false
|
||||
#
|
||||
# experimental_present_mode = "fifo"
|
||||
|
||||
[[game]] # default vkcube entry
|
||||
exe = "vkcube"
|
||||
|
||||
multiplier = 4
|
||||
performance_mode = true
|
||||
|
||||
[[game]] # default benchmark entry
|
||||
exe = "benchmark"
|
||||
|
||||
multiplier = 4
|
||||
performance_mode = false
|
||||
|
||||
[[game]] # override Genshin Impact
|
||||
exe = "Genshin"
|
||||
|
||||
multiplier = 3
|
||||
)";
|
||||
80
include/context.hpp
Normal file
80
include/context.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include "hooks.hpp"
|
||||
#include "mini/commandbuffer.hpp"
|
||||
#include "mini/commandpool.hpp"
|
||||
#include "mini/image.hpp"
|
||||
#include "mini/semaphore.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
///
|
||||
/// This class is the frame generation context. There should be one instance per swapchain.
|
||||
///
|
||||
class LsContext {
|
||||
public:
|
||||
///
|
||||
/// Create the swapchain context.
|
||||
///
|
||||
/// @param info The device information to use.
|
||||
/// @param swapchain The Vulkan swapchain to use.
|
||||
/// @param extent The extent of the swapchain images.
|
||||
/// @param swapchainImages The swapchain images to use.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if any Vulkan call fails.
|
||||
///
|
||||
LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
||||
VkExtent2D extent, const std::vector<VkImage>& swapchainImages);
|
||||
|
||||
///
|
||||
/// Custom present logic.
|
||||
///
|
||||
/// @param info The device information to use.
|
||||
/// @param pNext Unknown pointer set in the present info structure.
|
||||
/// @param queue The Vulkan queue to present the frame on.
|
||||
/// @param gameRenderSemaphores The semaphores to wait on before presenting.
|
||||
/// @param presentIdx The index of the swapchain image to present.
|
||||
/// @return The result of the Vulkan present operation, which can be VK_SUCCESS or VK_SUBOPTIMAL_KHR.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if any Vulkan call fails.
|
||||
///
|
||||
VkResult present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue,
|
||||
const std::vector<VkSemaphore>& gameRenderSemaphores, uint32_t presentIdx);
|
||||
|
||||
// Non-copyable, trivially moveable and destructible
|
||||
LsContext(const LsContext&) = delete;
|
||||
LsContext& operator=(const LsContext&) = delete;
|
||||
LsContext(LsContext&&) = default;
|
||||
LsContext& operator=(LsContext&&) = default;
|
||||
~LsContext() = default;
|
||||
private:
|
||||
VkSwapchainKHR swapchain;
|
||||
std::vector<VkImage> swapchainImages;
|
||||
VkExtent2D extent;
|
||||
|
||||
std::shared_ptr<int32_t> lsfgCtxId; // lsfg context id
|
||||
Mini::Image frame_0, frame_1; // frames shared with lsfg. write to frame_0 when fc % 2 == 0
|
||||
std::vector<Mini::Image> out_n; // output images shared with lsfg, indexed by framegen id
|
||||
|
||||
Mini::CommandPool cmdPool;
|
||||
uint64_t frameIdx{0};
|
||||
|
||||
struct RenderPassInfo {
|
||||
Mini::CommandBuffer preCopyBuf; // copy from swapchain image to frame_0/frame_1
|
||||
std::array<Mini::Semaphore, 2> preCopySemaphores; // signal when preCopyBuf is done
|
||||
|
||||
std::vector<Mini::Semaphore> renderSemaphores; // signal when lsfg is done with frame n
|
||||
|
||||
std::vector<Mini::Semaphore> acquireSemaphores; // signal for swapchain image n
|
||||
|
||||
std::vector<Mini::CommandBuffer> postCopyBufs; // copy from out_n to swapchain image
|
||||
std::vector<Mini::Semaphore> postCopySemaphores; // signal when postCopyBuf is done
|
||||
std::vector<Mini::Semaphore> prevPostCopySemaphores; // signal for previous postCopyBuf
|
||||
}; // data for a single render pass
|
||||
std::array<RenderPassInfo, 8> passInfos; // allocate 8 because why not
|
||||
};
|
||||
26
include/extract/extract.hpp
Normal file
26
include/extract/extract.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Extract {
|
||||
|
||||
///
|
||||
/// Extract all known shaders.
|
||||
///
|
||||
/// @throws std::runtime_error if shader extraction fails.
|
||||
///
|
||||
void extractShaders();
|
||||
|
||||
///
|
||||
/// Get a shader by name.
|
||||
///
|
||||
/// @param name The name of the shader to get.
|
||||
/// @return The shader bytecode.
|
||||
///
|
||||
/// @throws std::runtime_error if the shader is not found.
|
||||
///
|
||||
std::vector<uint8_t> getShader(const std::string& name);
|
||||
|
||||
}
|
||||
16
include/extract/trans.hpp
Normal file
16
include/extract/trans.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace Extract {
|
||||
|
||||
///
|
||||
/// Translate DXBC bytecode to SPIR-V bytecode.
|
||||
///
|
||||
/// @param bytecode The DXBC bytecode to translate.
|
||||
/// @return The translated SPIR-V bytecode.
|
||||
///
|
||||
std::vector<uint8_t> translateShader(std::vector<uint8_t> bytecode);
|
||||
|
||||
}
|
||||
22
include/hooks.hpp
Normal file
22
include/hooks.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
|
||||
namespace Hooks {
|
||||
|
||||
/// Vulkan device information structure.
|
||||
struct DeviceInfo {
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
std::pair<uint32_t, VkQueue> queue; // graphics family
|
||||
};
|
||||
|
||||
/// Map of hooked Vulkan functions.
|
||||
extern std::unordered_map<std::string, PFN_vkVoidFunction> hooks;
|
||||
|
||||
}
|
||||
224
include/layer.hpp
Normal file
224
include/layer.hpp
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Layer {
|
||||
/// Call to the original vkCreateInstance function.
|
||||
VkResult ovkCreateInstance(
|
||||
const VkInstanceCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkInstance* pInstance);
|
||||
/// Call to the original vkDestroyInstance function.
|
||||
void ovkDestroyInstance(
|
||||
VkInstance instance,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkCreateDevice function.
|
||||
VkResult ovkCreateDevice(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
const VkDeviceCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDevice* pDevice);
|
||||
/// Call to the original vkDestroyDevice function.
|
||||
void ovkDestroyDevice(
|
||||
VkDevice device,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkSetDeviceLoaderData function.
|
||||
VkResult ovkSetDeviceLoaderData(
|
||||
VkDevice device,
|
||||
void* object);
|
||||
|
||||
/// Call to the original vkGetInstanceProcAddr function.
|
||||
PFN_vkVoidFunction ovkGetInstanceProcAddr(
|
||||
VkInstance instance,
|
||||
const char* pName);
|
||||
/// Call to the original vkGetDeviceProcAddr function.
|
||||
PFN_vkVoidFunction ovkGetDeviceProcAddr(
|
||||
VkDevice device,
|
||||
const char* pName);
|
||||
|
||||
/// Call to the original vkGetPhysicalDeviceQueueFamilyProperties function.
|
||||
void ovkGetPhysicalDeviceQueueFamilyProperties(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
uint32_t* pQueueFamilyPropertyCount,
|
||||
VkQueueFamilyProperties* pQueueFamilyProperties);
|
||||
/// Call to the original vkGetPhysicalDeviceMemoryProperties function.
|
||||
void ovkGetPhysicalDeviceMemoryProperties(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkPhysicalDeviceMemoryProperties* pMemoryProperties);
|
||||
/// Call to the original vkGetPhysicalDeviceProperties function.
|
||||
void ovkGetPhysicalDeviceProperties(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkPhysicalDeviceProperties* pProperties);
|
||||
/// Call to the original vkGetPhysicalDeviceSurfaceCapabilitiesKHR function.
|
||||
VkResult ovkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkSurfaceKHR surface,
|
||||
VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
|
||||
|
||||
/// Call to the original vkCreateSwapchainKHR function.
|
||||
VkResult ovkCreateSwapchainKHR(
|
||||
VkDevice device,
|
||||
const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkSwapchainKHR* pSwapchain);
|
||||
/// Call to the original vkQueuePresentKHR function.
|
||||
VkResult ovkQueuePresentKHR(
|
||||
VkQueue queue,
|
||||
const VkPresentInfoKHR* pPresentInfo);
|
||||
/// Call to the original vkDestroySwapchainKHR function.
|
||||
void ovkDestroySwapchainKHR(
|
||||
VkDevice device,
|
||||
VkSwapchainKHR swapchain,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkGetSwapchainImagesKHR function.
|
||||
VkResult ovkGetSwapchainImagesKHR(
|
||||
VkDevice device,
|
||||
VkSwapchainKHR swapchain,
|
||||
uint32_t* pSwapchainImageCount,
|
||||
VkImage* pSwapchainImages);
|
||||
|
||||
/// Call to the original vkAllocateCommandBuffers function.
|
||||
VkResult ovkAllocateCommandBuffers(
|
||||
VkDevice device,
|
||||
const VkCommandBufferAllocateInfo* pAllocateInfo,
|
||||
VkCommandBuffer* pCommandBuffers);
|
||||
/// Call to the original vkFreeCommandBuffers function.
|
||||
void ovkFreeCommandBuffers(
|
||||
VkDevice device,
|
||||
VkCommandPool commandPool,
|
||||
uint32_t commandBufferCount,
|
||||
const VkCommandBuffer* pCommandBuffers);
|
||||
|
||||
/// Call to the original vkBeginCommandBuffer function.
|
||||
VkResult ovkBeginCommandBuffer(
|
||||
VkCommandBuffer commandBuffer,
|
||||
const VkCommandBufferBeginInfo* pBeginInfo);
|
||||
/// Call to the original vkEndCommandBuffer function.
|
||||
VkResult ovkEndCommandBuffer(
|
||||
VkCommandBuffer commandBuffer);
|
||||
|
||||
/// Call to the original vkCreateCommandPool function.
|
||||
VkResult ovkCreateCommandPool(
|
||||
VkDevice device,
|
||||
const VkCommandPoolCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkCommandPool* pCommandPool);
|
||||
/// Call to the original vkDestroyCommandPool function.
|
||||
void ovkDestroyCommandPool(
|
||||
VkDevice device,
|
||||
VkCommandPool commandPool,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkCreateImage function.
|
||||
VkResult ovkCreateImage(
|
||||
VkDevice device,
|
||||
const VkImageCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkImage* pImage);
|
||||
/// Call to the original vkDestroyImage function.
|
||||
void ovkDestroyImage(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkGetImageMemoryRequirements function.
|
||||
void ovkGetImageMemoryRequirements(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
VkMemoryRequirements* pMemoryRequirements);
|
||||
/// Call to the original vkBindImageMemory function.
|
||||
VkResult ovkBindImageMemory(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
VkDeviceMemory memory,
|
||||
VkDeviceSize memoryOffset);
|
||||
/// Call to the original vkAllocateMemory function.
|
||||
VkResult ovkAllocateMemory(
|
||||
VkDevice device,
|
||||
const VkMemoryAllocateInfo* pAllocateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDeviceMemory* pMemory);
|
||||
/// Call to the original vkFreeMemory function.
|
||||
void ovkFreeMemory(
|
||||
VkDevice device,
|
||||
VkDeviceMemory memory,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkCreateSemaphore function.
|
||||
VkResult ovkCreateSemaphore(
|
||||
VkDevice device,
|
||||
const VkSemaphoreCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkSemaphore* pSemaphore);
|
||||
/// Call to the original vkDestroySemaphore function.
|
||||
void ovkDestroySemaphore(
|
||||
VkDevice device,
|
||||
VkSemaphore semaphore,
|
||||
const VkAllocationCallbacks* pAllocator);
|
||||
|
||||
/// Call to the original vkGetMemoryFdKHR function.
|
||||
VkResult ovkGetMemoryFdKHR(
|
||||
VkDevice device,
|
||||
const VkMemoryGetFdInfoKHR* pGetFdInfo,
|
||||
int* pFd);
|
||||
/// Call to the original vkGetSemaphoreFdKHR function.
|
||||
VkResult ovkGetSemaphoreFdKHR(
|
||||
VkDevice device,
|
||||
const VkSemaphoreGetFdInfoKHR* pGetFdInfo,
|
||||
int* pFd);
|
||||
|
||||
/// Call to the original vkGetDeviceQueue function.
|
||||
void ovkGetDeviceQueue(
|
||||
VkDevice device,
|
||||
uint32_t queueFamilyIndex,
|
||||
uint32_t queueIndex,
|
||||
VkQueue* pQueue);
|
||||
/// Call to the original vkQueueSubmit function.
|
||||
VkResult ovkQueueSubmit(
|
||||
VkQueue queue,
|
||||
uint32_t submitCount,
|
||||
const VkSubmitInfo* pSubmits,
|
||||
VkFence fence);
|
||||
|
||||
/// Call to the original vkCmdPipelineBarrier function.
|
||||
void ovkCmdPipelineBarrier(
|
||||
VkCommandBuffer commandBuffer,
|
||||
VkPipelineStageFlags srcStageMask,
|
||||
VkPipelineStageFlags dstStageMask,
|
||||
VkDependencyFlags dependencyFlags,
|
||||
uint32_t memoryBarrierCount,
|
||||
const VkMemoryBarrier* pMemoryBarriers,
|
||||
uint32_t bufferMemoryBarrierCount,
|
||||
const VkBufferMemoryBarrier* pBufferMemoryBarriers,
|
||||
uint32_t imageMemoryBarrierCount,
|
||||
const VkImageMemoryBarrier* pImageMemoryBarriers);
|
||||
/// Call to the original vkCmdBlitImage function.
|
||||
void ovkCmdBlitImage(
|
||||
VkCommandBuffer commandBuffer,
|
||||
VkImage srcImage,
|
||||
VkImageLayout srcImageLayout,
|
||||
VkImage dstImage,
|
||||
VkImageLayout dstImageLayout,
|
||||
uint32_t regionCount,
|
||||
const VkImageBlit* pRegions,
|
||||
VkFilter filter);
|
||||
|
||||
/// Call to the original vkAcquireNextImageKHR function.
|
||||
VkResult ovkAcquireNextImageKHR(
|
||||
VkDevice device,
|
||||
VkSwapchainKHR swapchain,
|
||||
uint64_t timeout,
|
||||
VkSemaphore semaphore,
|
||||
VkFence fence,
|
||||
uint32_t* pImageIndex);
|
||||
}
|
||||
|
||||
/// Symbol definition for Vulkan instance layer.
|
||||
extern "C" PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char* pName);
|
||||
/// Symbol definition for Vulkan device layer.
|
||||
extern "C" PFN_vkVoidFunction layer_vkGetDeviceProcAddr(VkDevice device, const char* pName);
|
||||
91
include/mini/commandbuffer.hpp
Normal file
91
include/mini/commandbuffer.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
|
||||
#include "mini/commandpool.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
/// State of the command buffer.
|
||||
enum class CommandBufferState {
|
||||
/// Command buffer is not initialized or has been destroyed.
|
||||
Invalid,
|
||||
/// Command buffer has been created.
|
||||
Empty,
|
||||
/// Command buffer recording has started.
|
||||
Recording,
|
||||
/// Command buffer recording has ended.
|
||||
Full,
|
||||
/// Command buffer has been submitted to a queue.
|
||||
Submitted
|
||||
};
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command buffer.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command buffer.
|
||||
///
|
||||
class CommandBuffer {
|
||||
public:
|
||||
CommandBuffer() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the command buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param pool Vulkan command pool
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandBuffer(VkDevice device, const CommandPool& pool);
|
||||
|
||||
///
|
||||
/// Begin recording commands in the command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is in Empty state
|
||||
/// @throws LSFG::vulkan_error if beginning the command buffer fails.
|
||||
///
|
||||
void begin();
|
||||
|
||||
///
|
||||
/// End recording commands in the command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
||||
/// @throws LSFG::vulkan_error if ending the command buffer fails.
|
||||
///
|
||||
void end();
|
||||
|
||||
///
|
||||
/// Submit the command buffer to a queue.
|
||||
///
|
||||
/// @param queue Vulkan queue to submit to
|
||||
/// @param waitSemaphores Semaphores to wait on before executing the command buffer
|
||||
/// @param signalSemaphores Semaphores to signal after executing the command buffer
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Full state.
|
||||
/// @throws LSFG::vulkan_error if submission fails.
|
||||
///
|
||||
void submit(VkQueue queue,
|
||||
const std::vector<VkSemaphore>& waitSemaphores = {},
|
||||
const std::vector<VkSemaphore>& signalSemaphores = {});
|
||||
|
||||
/// Get the state of the command buffer.
|
||||
[[nodiscard]] CommandBufferState getState() const { return *this->state; }
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandBuffer; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandBuffer(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer& operator=(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer(CommandBuffer&&) noexcept = default;
|
||||
CommandBuffer& operator=(CommandBuffer&&) noexcept = default;
|
||||
~CommandBuffer() = default;
|
||||
private:
|
||||
std::shared_ptr<CommandBufferState> state;
|
||||
std::shared_ptr<VkCommandBuffer> commandBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
42
include/mini/commandpool.hpp
Normal file
42
include/mini/commandpool.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command pool.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command pool.
|
||||
///
|
||||
class CommandPool {
|
||||
public:
|
||||
CommandPool() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the command pool.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param graphicsFamilyIdx Index of the graphics queue family
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandPool(VkDevice device, uint32_t graphicsFamilyIdx);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandPool; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandPool(const CommandPool&) noexcept = default;
|
||||
CommandPool& operator=(const CommandPool&) noexcept = default;
|
||||
CommandPool(CommandPool&&) noexcept = default;
|
||||
CommandPool& operator=(CommandPool&&) noexcept = default;
|
||||
~CommandPool() = default;
|
||||
private:
|
||||
std::shared_ptr<VkCommandPool> commandPool;
|
||||
};
|
||||
|
||||
}
|
||||
60
include/mini/image.hpp
Normal file
60
include/mini/image.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan image.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan image.
|
||||
///
|
||||
class Image {
|
||||
public:
|
||||
Image() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the image and export the backing fd
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param physicalDevice Vulkan physical device
|
||||
/// @param extent Extent of the image in pixels.
|
||||
/// @param format Vulkan format of the image
|
||||
/// @param usage Usage flags for the image
|
||||
/// @param aspectFlags Aspect flags for the image view
|
||||
/// @param fd Pointer to an integer where the file descriptor will be stored.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Image(VkDevice device, VkPhysicalDevice physicalDevice, VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int* fd);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->image; }
|
||||
/// Get the Vulkan device memory handle.
|
||||
[[nodiscard]] auto getMemory() const { return *this->memory; }
|
||||
/// Get the extent of the image.
|
||||
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
|
||||
/// Get the format of the image.
|
||||
[[nodiscard]] VkFormat getFormat() const { return this->format; }
|
||||
/// Get the aspect flags of the image.
|
||||
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Image(const Image&) noexcept = default;
|
||||
Image& operator=(const Image&) noexcept = default;
|
||||
Image(Image&&) noexcept = default;
|
||||
Image& operator=(Image&&) noexcept = default;
|
||||
~Image() = default;
|
||||
private:
|
||||
std::shared_ptr<VkImage> image;
|
||||
std::shared_ptr<VkDeviceMemory> memory;
|
||||
|
||||
VkExtent2D extent{};
|
||||
VkFormat format{};
|
||||
VkImageAspectFlags aspectFlags{};
|
||||
};
|
||||
|
||||
}
|
||||
50
include/mini/semaphore.hpp
Normal file
50
include/mini/semaphore.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan semaphore.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan semaphore.
|
||||
///
|
||||
class Semaphore {
|
||||
public:
|
||||
Semaphore() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the semaphore.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(VkDevice device);
|
||||
|
||||
///
|
||||
/// Import a semaphore.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param fd File descriptor to import the semaphore from.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(VkDevice device, int* fd);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->semaphore; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Semaphore(const Semaphore&) noexcept = default;
|
||||
Semaphore& operator=(const Semaphore&) noexcept = default;
|
||||
Semaphore(Semaphore&&) noexcept = default;
|
||||
Semaphore& operator=(Semaphore&&) noexcept = default;
|
||||
~Semaphore() = default;
|
||||
private:
|
||||
std::shared_ptr<VkSemaphore> semaphore;
|
||||
};
|
||||
|
||||
}
|
||||
15
include/utils/benchmark.hpp
Normal file
15
include/utils/benchmark.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Benchmark {
|
||||
|
||||
///
|
||||
/// Run the benchmark.
|
||||
///
|
||||
/// @param width The width of the benchmark.
|
||||
/// @param height The height of the benchmark.
|
||||
///
|
||||
[[noreturn]] void run(uint32_t width, uint32_t height);
|
||||
|
||||
}
|
||||
102
include/utils/utils.hpp
Normal file
102
include/utils/utils.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
///
|
||||
/// Find a queue in the physical device that supports the given queue flags.
|
||||
///
|
||||
/// @param device The Vulkan device to use for queue retrieval.
|
||||
/// @param physicalDevice The physical device to search in.
|
||||
/// @param desc The device creation info, used to determine enabled queue families.
|
||||
/// @param flags The queue flags to search for (e.g., VK_QUEUE_GRAPHICS_BIT).
|
||||
/// @return Pair of queue family index and queue handle.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if no suitable queue is found.
|
||||
///
|
||||
std::pair<uint32_t, VkQueue> findQueue(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkDeviceCreateInfo* desc, VkQueueFlags flags);
|
||||
|
||||
///
|
||||
/// Get the UUID of the physical device.
|
||||
///
|
||||
/// @param physicalDevice The physical device to get the UUID from.
|
||||
/// @return The UUID of the physical device.
|
||||
///
|
||||
uint64_t getDeviceUUID(VkPhysicalDevice physicalDevice);
|
||||
|
||||
///
|
||||
/// Get the max image count for a swapchain.
|
||||
///
|
||||
/// @param physicalDevice The physical device to query.
|
||||
/// @param surface The surface to query the capabilities for.
|
||||
/// @return The maximum image count for the swapchain.
|
||||
///
|
||||
uint32_t getMaxImageCount(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
|
||||
|
||||
///
|
||||
/// Ensure a list of extensions is present in the given array.
|
||||
///
|
||||
/// @param extensions The array of extensions to check.
|
||||
/// @param requiredExtensions The list of required extensions to ensure are present.
|
||||
///
|
||||
std::vector<const char*> addExtensions(const char* const* extensions, size_t count,
|
||||
const std::vector<const char*>& requiredExtensions);
|
||||
|
||||
///
|
||||
/// Copy an image from source to destination in a command buffer.
|
||||
///
|
||||
/// @param buf The command buffer to record the copy operation into.
|
||||
/// @param src The source image to copy from.
|
||||
/// @param dst The destination image to copy to.
|
||||
/// @param width The width of the image to copy.
|
||||
/// @param height The height of the image to copy.
|
||||
/// @param pre The pipeline stage to wait on.
|
||||
/// @param post The pipeline stage to provide after the copy.
|
||||
/// @param makeSrcPresentable If true, the source image will be made presentable after the copy.
|
||||
/// @param makeDstPresentable If true, the destination image will be made presentable after the copy.
|
||||
///
|
||||
void copyImage(VkCommandBuffer buf,
|
||||
VkImage src, VkImage dst,
|
||||
uint32_t width, uint32_t height,
|
||||
VkPipelineStageFlags pre, VkPipelineStageFlags post,
|
||||
bool makeSrcPresentable, bool makeDstPresentable);
|
||||
|
||||
///
|
||||
/// Log a message at most n times.
|
||||
///
|
||||
/// @param id The identifier for the log message.
|
||||
/// @param n The maximum number of times to log the message.
|
||||
/// @param message The message to log.
|
||||
///
|
||||
void logLimitN(const std::string& id, size_t n, const std::string& message);
|
||||
|
||||
///
|
||||
/// Reset the log limit for a given identifier.
|
||||
///
|
||||
/// @param id The identifier for the log message.
|
||||
///
|
||||
void resetLimitN(const std::string& id) noexcept;
|
||||
|
||||
///
|
||||
/// Get the process name of the current executable.
|
||||
///
|
||||
/// @return The name of the process.
|
||||
///
|
||||
std::pair<std::string, std::string> getProcessName();
|
||||
|
||||
///
|
||||
/// Get the configuration file path.
|
||||
///
|
||||
/// @return The path to the configuration file.
|
||||
///
|
||||
std::string getConfigFile();
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue