Inital commit
This commit is contained in:
133
.clang-format
Normal file
133
.clang-format
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: AlignAfterOperator
|
||||
AlignTrailingComments: false
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BitFieldColonSpacing: None
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterExternBlock: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeWhile: true
|
||||
BeforeLambdaBody: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyNamespace: true
|
||||
SplitEmptyRecord: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeConceptDeclarations: Allowed
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 120
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
EmptyLineAfterAccessModifier: Always
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
FixNamespaceComments: true
|
||||
IncludeBlocks: Regroup
|
||||
DerivePointerAlignment: false
|
||||
IncludeCategories:
|
||||
- Regex: '^<moc_.*>'
|
||||
Priority: 1
|
||||
SortPriority: 1
|
||||
CaseSensitive: false
|
||||
- Regex: '^<Q.*>'
|
||||
Priority: 2
|
||||
SortPriority: 2
|
||||
CaseSensitive: true
|
||||
- Regex: '^<.*/.*>'
|
||||
Priority: 4
|
||||
SortPriority: 4
|
||||
- Regex: '^<.*>'
|
||||
Priority: 5
|
||||
SortPriority: 5
|
||||
- Regex: '^".*"'
|
||||
Priority: 9
|
||||
SortPriority: 9
|
||||
- Regex: '^".*/.*"'
|
||||
Priority: 10
|
||||
SortPriority: 10
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: true
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: AlignWithSpaces
|
||||
IndentWrappedFunctionNames: true
|
||||
InsertBraces: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
PPIndentWidth: 2
|
||||
PackConstructorInitializers: CurrentLine
|
||||
PenaltyBreakOpenParenthesis: 999
|
||||
PenaltyReturnTypeOnItsOwnLine: 999
|
||||
PointerAlignment: Left
|
||||
QualifierAlignment: Leave
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: false
|
||||
RequiresClausePosition: OwnLine
|
||||
SeparateDefinitionBlocks: Always
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAroundPointerQualifiers: After
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Always
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: true
|
||||
SpacesInParentheses: true
|
||||
SpacesInSquareBrackets: true
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros: [ emit ]
|
||||
StatementMacros: [ Q_UNUSED ]
|
||||
...
|
||||
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# docs
|
||||
/docs/html/
|
||||
/docs/latex/
|
||||
|
||||
# dot hidden
|
||||
.*
|
||||
.*/
|
||||
|
||||
# explicit
|
||||
*.ignore
|
||||
|
||||
# Build dirs
|
||||
/build/
|
||||
/cmake-build-*/
|
||||
/src/shaders/
|
||||
|
||||
# Build helpers
|
||||
build.bat
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# IDE specific
|
||||
# # clion
|
||||
/venv/
|
||||
# # Visual Studio
|
||||
/out/build/x64-Debug
|
||||
/CMakeFiles
|
||||
/CMakeCache.txt
|
||||
*.user
|
||||
*.autosave
|
||||
# #Visual Studio Indexes
|
||||
*.vsidx
|
||||
read.lock
|
||||
# # Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
36
.gitmodules
vendored
Normal file
36
.gitmodules
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
[submodule "docs/doxygen-awesome-css"]
|
||||
path = docs/doxygen-awesome-css
|
||||
url = https://github.com/jothepro/doxygen-awesome-css.git
|
||||
[submodule "3rdparty/qtimageformats"]
|
||||
path = 3rdparty/qtimageformats
|
||||
url = https://github.com/qt/qtimageformats.git
|
||||
[submodule "dependencies/fmt"]
|
||||
path = dependencies/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
[submodule "dependencies/spdlog"]
|
||||
path = dependencies/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "dependencies/sqlite_modern_cpp"]
|
||||
path = dependencies/sqlite_modern_cpp
|
||||
url = https://github.com/SqliteModernCpp/sqlite_modern_cpp.git
|
||||
[submodule "dependencies/tracy"]
|
||||
path = dependencies/tracy
|
||||
url = https://github.com/wolfpld/tracy.git
|
||||
[submodule "dependencies/catch2"]
|
||||
path = dependencies/catch2
|
||||
url = https://github.com/catchorg/Catch2.git
|
||||
[submodule "dependencies/lz4"]
|
||||
path = dependencies/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "dependencies/blurhash"]
|
||||
path = dependencies/blurhash
|
||||
url = https://github.com/KJNeko/blurhash-cxx.git
|
||||
[submodule "dependencies/vma"]
|
||||
path = dependencies/vma
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "dependencies/glm"]
|
||||
path = dependencies/glm
|
||||
url = https://github.com/g-truc/glm.git
|
||||
[submodule "dependencies/glfw3"]
|
||||
path = dependencies/glfw3
|
||||
url = https://github.com/glfw/glfw.git
|
||||
74
CMakeLists.txt
Normal file
74
CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
# /CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.26.4)
|
||||
project(Game LANGUAGES CXX C)
|
||||
set(APP_ICON_RESOURCE_WINDOWS "${CMAKE_CURRENT_SOURCE_DIR}/appicon.rc")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL DEBUG CACHE STRING "CMake messaging level")
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/data)
|
||||
|
||||
#Enable cmake_modules
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
|
||||
include(common)
|
||||
|
||||
message("-- CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
|
||||
|
||||
PlatformPreSetup()
|
||||
CompilerPreSetup()
|
||||
message("-- FGL_FLAGS: ${FGL_FLAGS}")
|
||||
|
||||
add_subdirectory(dependencies/vma)
|
||||
include(dependencies/glfw)
|
||||
include(dependencies/glm)
|
||||
include(dependencies/tracy)
|
||||
include(dependencies/vulkan)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
file(GLOB FRAG_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/shaders/*.frag")
|
||||
file(GLOB VERT_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/shaders/*.vert")
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/bin/shaders")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/bin/models")
|
||||
|
||||
file(GLOB MODELS "${CMAKE_CURRENT_SOURCE_DIR}/models/*.obj")
|
||||
|
||||
foreach (MODEL IN LISTS MODELS)
|
||||
get_filename_component(FILENAME ${MODEL} NAME)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/models/${FILENAME}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${MODEL} ${CMAKE_BINARY_DIR}/bin/models/${FILENAME}
|
||||
COMMENT "Copying ${MODEL}")
|
||||
list(APPEND OBJ_MODELS ${CMAKE_BINARY_DIR}/bin/models/${FILENAME})
|
||||
endforeach ()
|
||||
|
||||
foreach (SHADER IN LISTS FRAG_SHADERS)
|
||||
get_filename_component(FILENAME ${SHADER} NAME)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv
|
||||
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
|
||||
COMMENT " Compiling ${SHADER} ")
|
||||
list(APPEND SPV_SHADERS ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv)
|
||||
endforeach ()
|
||||
|
||||
foreach (SHADER IN LISTS VERT_SHADERS)
|
||||
get_filename_component(FILENAME ${SHADER} NAME)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv
|
||||
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
|
||||
COMMENT " Compiling ${SHADER} ")
|
||||
list(APPEND SPV_SHADERS ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv)
|
||||
endforeach ()
|
||||
|
||||
add_custom_target(shaders ALL DEPENDS ${SPV_SHADERS})
|
||||
add_custom_target(models ALL DEPENDS ${OBJ_MODELS})
|
||||
|
||||
add_dependencies(${PROJECT_NAME} shaders)
|
||||
add_dependencies(${PROJECT_NAME} models)
|
||||
|
||||
SetVersionInfo()
|
||||
CompilerPostSetup()
|
||||
98
GUIDELINES.md
Normal file
98
GUIDELINES.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# ATLAS Programming guidelines
|
||||
|
||||
---
|
||||
|
||||
## Last updated: `Tue 1 Aug. 07:20`
|
||||
|
||||
---
|
||||
|
||||
# Table of contents
|
||||
|
||||
1. [Compiler specification](#compiler-specification)
|
||||
1. [Warning flags](#i-warning-flags)
|
||||
2. [codegen-flags](#ii-codegen-flags)
|
||||
2. [Style](#style)
|
||||
1. [Naming style](#naming-style)
|
||||
3. [Programing Rules](#programming-rules)
|
||||
1. [General notes](#general-notes)
|
||||
2. [Init style](#init-style)
|
||||
|
||||
---
|
||||
|
||||
# Compiler specification
|
||||
|
||||
This is written under the assumption that gcc is used. Other compilers (while not supported) can be used with a mirrored warning setup (Try to mirror all warnings that are shown here)
|
||||
|
||||
---
|
||||
|
||||
## i. Warning flags
|
||||
|
||||
``-Wall -Wextra -Wundef -Wnull-dereference -Wpedantic -pedantic-errors -Weffc++ -Wnoexcept -Wuninitialized -Wunused -Wunused-parameter -Winit-self -Wconversion -Wuseless-cast -Wextra-semi -Wsuggest-final-types -Wsuggest-final-methods -Wsuggest-override -Wformat-signedness -Wno-format-zero-length -Wmissing-include-dirs -Wshift-overflow=2 -Walloc-zero -Walloca -Wsign-promo -Wconversion -Wduplicated-branches -Wduplicated-cond -Wfloat-equal -Wshadow -Wshadow=local -Wmultiple-inheritance -Wvirtual-inheritance -Wno-virtual-move-assign -Wunsafe-loop-optimizations -Wnormalized -Wpacked -Wredundant-decls -Wmismatched-tags -Wredundant-tags -Wctor-dtor-privacy -Wdeprecated-copy-dtor -Wstrict-null-sentinel -Wold-style-cast -Woverloaded-virtual -Wzero-as-null-pointer-constant -Wconditionally-supported -Werror=pedantic -Wwrite-strings -Wmultiple-inheritance -Wunused-const-variable=2 -Wdouble-promotion -Wpointer-arith -Wcast-align=strict -Wcast-qual -Wconversion -Wsign-conversion -Wimplicit-fallthrough=1 -Wmisleading-indentation -Wdangling-else -Wdate-time -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation=2 -Wswitch-default -Wswitch-enum -Wstrict-overflow=5 -Wstringop-overflow=4 -Warray-bounds=2 -Wattribute-alias=2 -Wcatch-value=2 -Wplacement-new=2 -Wtrampolines -Winvalid-imported-macros -Winvalid-imported-macros``
|
||||
> Note: These flags should be used within reason. If unable to use due to weird constraints then they can be SELECTIVELY disabled. Reasoning must be given and verified for the reason of being disabled.
|
||||
|
||||
---
|
||||
|
||||
## ii. Codegen flags
|
||||
|
||||
## Debug
|
||||
|
||||
``-Og -g -fstrict-aliasing -fno-omit-frame-pointer -fstack-check -ftrapv -fwrapv -fverbose-asm -femit-class-debug-always``
|
||||
> * Recomended `-fanalyzer` should be used where possible
|
||||
|
||||
## System Release
|
||||
|
||||
``-DNDEBUG -Ofast -march=native -fgcse-las -fgcse-sm -fdeclone-ctor-dtor -fdevirtualize-speculatively -fdevirtualize-at-ltrans -ftree-loop-im -fivopts -ftree-loop-ivcanon -fira-hoist-pressure -fsched-pressure -fsched-spec-load -fipa-pta -flto=auto -s -ffat-lto-objects -fno-enforce-eh-specs -fstrict-enums``
|
||||
> * Only should be run on the same system used for compilation
|
||||
|
||||
## Release
|
||||
|
||||
``-DNDEBUG -Ofast -fdeclone-ctor-dtor -flto=auto -s``
|
||||
> * Default unless running on the same system as compilation (See System Release*)
|
||||
|
||||
## RelWithDebug
|
||||
|
||||
``-Ofast -march=native -g -fstrict-aliasing -fno-omit-frame-pointer -fstack-check -ftrapv -fwrapv -fverbose-asm -femit-class-debug-always``
|
||||
|
||||
---
|
||||
|
||||
## iii. Configuration
|
||||
|
||||
`-fmax-errors=3`: Whatever value you want set
|
||||
|
||||
`-Wpadded`: Disable if impossible to fix
|
||||
|
||||
`-fconcepts-diagnostics-depth=4`: If you do crazy template stuff and are debugging it you'll want this higher possibly.
|
||||
|
||||
# Style
|
||||
|
||||
## Naming Style
|
||||
|
||||
| | case type | extra notes |
|
||||
|-------------------|------------|-------------|
|
||||
| functions/methods | cammelCase | |
|
||||
| class/struct | PascalCase | |
|
||||
| variables | snake_case | |
|
||||
| lambdas | cammelCase | |
|
||||
|
||||
## Variable names
|
||||
|
||||
Variables in a class should be prefixed with `m_`. Variables outside of a class are exempt from this. Variables within a `struct` with no methods are exempt from this. (POD)
|
||||
|
||||
# Programming rules
|
||||
|
||||
## General notes
|
||||
|
||||
- Avoid C style code at all costs (C style memory allocation and C style casts). Prefer using things like `static_cast`
|
||||
- Avoid raw pointers at all cost. Use proper wrappers like `std::unique_ptr` and `std::shared_ptr`.
|
||||
- Follow the Core CPP guidelines where possible.
|
||||
- Const where possible. If the value isn't changed then it should be const.
|
||||
|
||||
## Init style
|
||||
|
||||
An init style like the following should be used `int my_int { 4 };` This is to assist with readability. As it can safely be guaranteed that `my_int` is created here in ANY situation at a quick glance.
|
||||
|
||||
---
|
||||
|
||||
# Nobleese Oblige
|
||||
|
||||
#### Thank you for your continued service.
|
||||
11
cmake_modules/apple.cmake
Normal file
11
cmake_modules/apple.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
# /cmake_modules/apple.cmake
|
||||
|
||||
if (APPLE)
|
||||
set(which_program "which")
|
||||
set(os_path_separator "/")
|
||||
function(PlatformPreSetup)
|
||||
endfunction()
|
||||
|
||||
function(PlatformPostSetup)
|
||||
endfunction()
|
||||
endif ()
|
||||
23
cmake_modules/clang.cmake
Normal file
23
cmake_modules/clang.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
function(CompilerPreSetup)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
#These two flags added with older gcc versions and Qt causes a compiler segfault -Wmismatched-tags -Wredundant-tags
|
||||
# STL has some warnings that prevent -Werror level compilation "-Wnull-dereference"
|
||||
set(FGL_WARNINGS "-Wall -Wundef -Wextra -Wpessimizing-move -Wpedantic -Weffc++ -pedantic-errors -Wuninitialized -Wunused -Wunused-parameter -Winit-self -Wconversion -Wextra-semi -Wsuggest-override -Wno-format-zero-length -Wmissing-include-dirs -Wshift-overflow -Walloca -Wsign-promo -Wconversion -Wpacked -Wredundant-decls -Wctor-dtor-privacy -Wdeprecated-copy-dtor -Wold-style-cast -Woverloaded-virtual -Wzero-as-null-pointer-constant -Wwrite-strings -Wunused-const-variable -Wdouble-promotion -Wpointer-arith -Wcast-qual -Wconversion -Wsign-conversion -Wimplicit-fallthrough -Wmisleading-indentation -Wdangling-else -Wdate-time -Wformat=2 -Wswitch-default")
|
||||
|
||||
set(FGL_CONFIG "-std=c++20")
|
||||
set(FGL_DEBUG "-Og -g -fstrict-aliasing -fno-omit-frame-pointer -fstack-check -ftrapv -fverbose-asm")
|
||||
#Generates system specific stuff (IE requires AVX)
|
||||
set(FGL_SYSTEM_SPECIFIC "-march=native -fgcse -fgcse-las -fgcse-sm -fdeclone-ctor-dtor -fdevirtualize-speculatively -ftree-loop-im -fivopts -ftree-loop-ivcanon -fira-hoist-pressure -fsched-pressure -fsched-spec-load -fipa-pta -flto=auto -s -ffat-lto-objects -fno-enforce-eh-specs -fstrict-enums -funroll-loops")
|
||||
#Generates safe optimization flags
|
||||
set(FGL_SYSTEM_SAFE "-O3 -fdevirtualize-at-ltrans -s")
|
||||
set(FGL_FLAGS_DEBUG "${FGL_WARNINGS} ${FGL_CONFIG} ${FGL_DEBUG}")
|
||||
set(FGL_FLAGS_SYSTEM "${FLG_CONFIG} -DNDEBUG ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
set(FGL_FLAGS_RELEASE "${FGL_CONFIG} -DNDEBUG -s ${FGL_SYSTEM_SAFE} ${FGL_WARNINGS} -Werror")
|
||||
set(FGL_FLAGS_RELWITHDEBINFO "${FGL_CONFIG} -DNDEBUG -g ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
set(FGL_FLAGS "${FGL_FLAGS_${UPPER_BUILD_TYPE}}" PARENT_SCOPE) # PARENT_SCOPE sends the variable up one level.
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
function(CompilerPostSetup)
|
||||
|
||||
endfunction()
|
||||
36
cmake_modules/common.cmake
Normal file
36
cmake_modules/common.cmake
Normal file
@@ -0,0 +1,36 @@
|
||||
# /cmake_modules/common.cmake
|
||||
|
||||
message(DEBUG "Entering ${CMAKE_CURRENT_LIST_FILE}")
|
||||
message(DEBUG "Platform: ${CMAKE_CXX_PLATFORM_ID}")
|
||||
message(DEBUG "Compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
message(DEBUG "Compiler: ${CMAKE_CXX_COMPILER}")
|
||||
set(BINARY_FOLDER "${CMAKE_BINARY_DIR}/bin")
|
||||
include(utils)
|
||||
include(qt)
|
||||
|
||||
if ((${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") OR (${CMAKE_CXX_PLATFORM_ID} STREQUAL "MinGW"))
|
||||
include(gcc)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
include(clang)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
|
||||
message()
|
||||
include(msvc)
|
||||
endif ()
|
||||
|
||||
if ((WIN32))
|
||||
message(DEBUG "Compiling for Windows")
|
||||
include(win32)
|
||||
elseif (APPLE)
|
||||
message(DEBUG "Compiling for Apple")
|
||||
elseif (UNIX)
|
||||
message(DEBUG "Compiling for Unix")
|
||||
include(linux)
|
||||
else ()
|
||||
message(DEBUG "Unknown Platform")
|
||||
endif ()
|
||||
|
||||
include(feature_checks)
|
||||
include(versioninfo)
|
||||
include(profiling)
|
||||
include(docs)
|
||||
message(DEBUG "Leaving ${CMAKE_CURRENT_LIST_FILE}")
|
||||
9
cmake_modules/dependencies.cmake
Normal file
9
cmake_modules/dependencies.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
|
||||
include(dependencies/tracy)
|
||||
include(dependencies/qt)
|
||||
include(dependencies/fmt)
|
||||
include(dependencies/spdlog)
|
||||
include(dependencies/lz4)
|
||||
include(dependencies/blurhash)
|
||||
2
cmake_modules/dependencies/blurhash.cmake
Normal file
2
cmake_modules/dependencies/blurhash.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/blurhash)
|
||||
set_target_properties(BlurhashCXX PROPERTIES COMPILE_FLAGS ${FGL_FLAGS})
|
||||
9
cmake_modules/dependencies/fmt.cmake
Normal file
9
cmake_modules/dependencies/fmt.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
if (NOT HAS_STD_FORMAT AND NOT ATLAS_IGNORE_STD_FORMAT EQUAL 1)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/fmt)
|
||||
set(ATLAS_LINK_FMT 1)
|
||||
option(SPDLOG_FMT_EXTERNAL "" ON)
|
||||
else ()
|
||||
option(SPDLOG_FMT_EXTERNAL "" OFF)
|
||||
#dummy lib to link to
|
||||
set(ATLAS_LINK_FMT 0)
|
||||
endif ()
|
||||
13
cmake_modules/dependencies/glfw.cmake
Normal file
13
cmake_modules/dependencies/glfw.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
if (WIN32)
|
||||
if (DEFINED ENV{GLFW_PATH})
|
||||
message("-- GLFW_PATH defined as: $ENV{GLFW_PATH}.")
|
||||
list(APPEND CMAKE_PREFIX_PATH $ENV{GLFW_PATH})
|
||||
find_package(glfw3 REQUIRED)
|
||||
else ()
|
||||
message("-- GLFW_PATH not defined. Using submodule instead")
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/glfw3)
|
||||
endif ()
|
||||
else ()
|
||||
find_package(glfw3 REQUIRED)
|
||||
endif ()
|
||||
5
cmake_modules/dependencies/glm.cmake
Normal file
5
cmake_modules/dependencies/glm.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
if (WIN32)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/glm)
|
||||
else ()
|
||||
find_package(glm REQUIRED)
|
||||
endif ()
|
||||
11
cmake_modules/dependencies/lz4.cmake
Normal file
11
cmake_modules/dependencies/lz4.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
set(LZ4_DIR ${CMAKE_SOURCE_DIR}/dependencies/lz4/lib)
|
||||
|
||||
file(GLOB_RECURSE LZ4_SOURCES ${LZ4_DIR}/*.c)
|
||||
|
||||
add_library(lz4 STATIC ${LZ4_SOURCES})
|
||||
target_include_directories(lz4 PUBLIC ${LZ4_DIR})
|
||||
|
||||
if (WIN32)
|
||||
#target_compile_definitions(${CMAKE_SOURCE_DIR}/dependencies/lz4 PRIVATE UNICODE=1)
|
||||
target_compile_definitions(lz4 PRIVATE LZ4_DEBUG=0)
|
||||
endif ()
|
||||
13
cmake_modules/dependencies/qt.cmake
Normal file
13
cmake_modules/dependencies/qt.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
#Verify after setting QT_PATH
|
||||
if (DEFINED QT_PATH)
|
||||
message("-- QT_PATH defined as ${QT_PATH}.")
|
||||
list(APPEND CMAKE_PREFIX_PATH ${QT_PATH})
|
||||
else ()
|
||||
message("-- QT_PATH not defined.")
|
||||
endif ()
|
||||
|
||||
find_package(Qt6 COMPONENTS Widgets Core Concurrent Network Test Charts REQUIRED)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
1
cmake_modules/dependencies/spdlog.cmake
Normal file
1
cmake_modules/dependencies/spdlog.cmake
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/spdlog)
|
||||
5
cmake_modules/dependencies/tracy.cmake
Normal file
5
cmake_modules/dependencies/tracy.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
#if (WIN32)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/tracy)
|
||||
#else ()
|
||||
#find_package(tracy REQUIRED)
|
||||
#endif ()
|
||||
1
cmake_modules/dependencies/vulkan.cmake
Normal file
1
cmake_modules/dependencies/vulkan.cmake
Normal file
@@ -0,0 +1 @@
|
||||
find_package(Vulkan REQUIRED)
|
||||
22
cmake_modules/docs.cmake
Normal file
22
cmake_modules/docs.cmake
Normal file
@@ -0,0 +1,22 @@
|
||||
option(BUILD_DOC "Build documentation" ON)
|
||||
|
||||
if (DEFINED BUILD_DOCS AND BUILD_DOCS)
|
||||
|
||||
find_package(Doxygen)
|
||||
if (DOXYGEN_FOUND)
|
||||
set(DOXYGEN_IN ${CMAKE_SOURCE_DIR}/Doxyfile)
|
||||
set(DOXYGEN_OUT ${CMAKE_BINARY_DIR}/Doxyfile)
|
||||
|
||||
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
|
||||
|
||||
add_custom_target(doc_doxygen ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM
|
||||
)
|
||||
else ()
|
||||
message("Doxygen need to be installed to generate the doxygen documentation")
|
||||
endif ()
|
||||
|
||||
endif ()
|
||||
6
cmake_modules/feature_checks.cmake
Normal file
6
cmake_modules/feature_checks.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
include(feature_checks/check_format)
|
||||
include(feature_checks/check_backtrace)
|
||||
6
cmake_modules/feature_checks/check_backtrace.cmake
Normal file
6
cmake_modules/feature_checks/check_backtrace.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <backtrace>\nint main(void){ std::basic_stacktrace thing { std::basic_stacktrace::current() }; return 0;}"
|
||||
HAVE_STD_BACKTRACE
|
||||
)
|
||||
8
cmake_modules/feature_checks/check_format.cmake
Normal file
8
cmake_modules/feature_checks/check_format.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <format>\nint main(void){ std::format(\"{}\", 1);return 0;}"
|
||||
HAVE_STD_FORMAT
|
||||
)
|
||||
|
||||
|
||||
136
cmake_modules/gcc.cmake
Normal file
136
cmake_modules/gcc.cmake
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
|
||||
function(AppendFlag FLAG_TEXT)
|
||||
set(FGL_WARNINGS "${FGL_WARNINGS} ${FLAG_TEXT}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(CompilerPreSetup)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
#These two flags added with older gcc versions and Qt causes a compiler segfault -Wmismatched-tags -Wredundant-tags
|
||||
# STL has some warnings that prevent -Werror level compilation "-Wnull-dereference"
|
||||
|
||||
# Generic warnings.
|
||||
set(FGL_WARNINGS "-Wall -Wundef -Wextra -Wpedantic")
|
||||
|
||||
|
||||
#AppendFlag("-Wno-changes-meaning") # Prevents accidently changing the type of things. Cannot define 1 things as another later
|
||||
AppendFlag("-Wdouble-promotion") #Prevents issue where you can do math as a double which might not be intended.
|
||||
AppendFlag("-Wnonnull") #Prevents passing null as an argument marked as nonnull attribute
|
||||
AppendFlag("-Wnull-dereference") #Warns about a possible null dereference. Compiler checks all possible paths
|
||||
#AppendFlag("-Wnrvo") #Compiler checks for return value optimization invalidations
|
||||
# Disabled because of older GCC compilers being unhappy with it
|
||||
#AppendFlag("-Winfinite-recursion") #Warns about infinite recursive calls
|
||||
AppendFlag("-Winit-self") #Yells at you if you init something with itself
|
||||
AppendFlag("-Wimplicit-fallthrough=4") # Warns about switch statements having a implicit fallthrough. must be marked with [[fallthrough]]
|
||||
AppendFlag("-Wignored-qualifiers") #Warns if qualifiers are used in return types. Which are ignored.
|
||||
AppendFlag("-Wno-ignored-attributes") #Warns if the compiler ignored an attribute and is unknown to the compiler
|
||||
AppendFlag("-Wmain") #Warns if the main function looks odd. (Wrong return type, ect)
|
||||
AppendFlag("-Wmisleading-indentation")#Warns if the indentation around an if/conditional could be misleading
|
||||
AppendFlag("-Wmissing-attributes") #Warns about missing attributes that are defined with a related function (attributes in the prototype but missing in the definition) (-Wall enabled)
|
||||
AppendFlag("-Wmissing-braces") #Warns when initalizing and missing braces during a aggregate or union initalization. (-Wall enabled)
|
||||
AppendFlag("-Wmissing-include-dirs") #Warns when missing a include dir that was supplied to the compiler
|
||||
AppendFlag("-Wmultistatement-macros") #Warns about odd behaviours with macros being used with conditionals that appear guarded by them
|
||||
AppendFlag("-Wparentheses") #Warns about unnecessary parentheses or other weird cases. (Also warns of a case x<=y<=z being seen as (<=y ? 1 : 0) <= z
|
||||
#AppendFlag("-Wno-self-move") # Prevents moving a value into itself. Which has no effect
|
||||
AppendFlag("-Wsequence-point") # Prevents some really weird shit like a = a++. Since due to the order of operations this results in undefined behaviour
|
||||
AppendFlag("-Wreturn-type") #Warns when a return type defaults to int.
|
||||
AppendFlag("-Wno-shift-count-negative") #Warns when a bitshift count is negative
|
||||
AppendFlag("-Wno-shift-count-overflow") #Warns when bitshifting will overflow the width
|
||||
AppendFlag("-Wswitch") #Warns when a switch lacks a case for it's given type.
|
||||
AppendFlag("-Wswitch-enum") #Warn when a switch misses an enum type in it's case list
|
||||
AppendFlag("-Wno-switch-outside-range") #Prevents a case outside of a switch's range.
|
||||
AppendFlag("-Wno-switch-unreachable") #Warns when a case value can possible not be reached
|
||||
AppendFlag("-Wunused") #Enables a bunch of warnings that relate to things stated but never used.
|
||||
AppendFlag("-Wuninitialized") #Warns about values being uninitalized. Where accessing them might be UB in some situations
|
||||
AppendFlag("-Wmaybe-uninitialized") #Warns when a value MIGHT not be initalized upon access.
|
||||
AppendFlag("-Wunknown-pragmas") #Self explanitory
|
||||
AppendFlag("-Wno-prio-ctor-dtor") #Yells about the developer setting priority to compiler reserved values for ctor/dtors
|
||||
AppendFlag("-Wstrict-aliasing=3") #Included in -Wall. Prevents aliasing rule breaking
|
||||
AppendFlag("-Wstrict-overflow=2") #Trys to hint at using values that won't overflow or will have the smallest chance of overflowing. Example. x+2 > y -> x+1 >= y
|
||||
AppendFlag("-Wbool-operation") #Warn against weird operations on the boolean type. Such as bitwise operations ON the bool
|
||||
AppendFlag("-Wduplicated-branches") #Warns about branches that appear to do the same thing
|
||||
AppendFlag("-Wduplicated-cond") #Warns about a conditional branch having a matching condition for both sides
|
||||
AppendFlag("-Wtautological-compare") #Warns when comparing something to itself
|
||||
AppendFlag("-Wshadow") #Warns about shadowing any variables
|
||||
AppendFlag("-Wfree-nonheap-object") #Warns about freeing a object not on the heap.
|
||||
AppendFlag("-Wpointer-arith") #Warns about missuse of 'sizeof' on types with no size. (Such as void)
|
||||
AppendFlag("-Wtype-limits") #Warn about comparisons that might be always true/false due to the limitations of a types' ability to display large or small values
|
||||
AppendFlag("-Wundef") #Warns about undefined behaviour when evaluating undefined defines
|
||||
AppendFlag("-Wcast-qual") #Warns when a cast removes a const attribute from a pointer.
|
||||
AppendFlag("-Wcast-align") #Warns when casting can shift a byte boundary for the pointer
|
||||
AppendFlag("-Wcast-function-type") #Warns when a function pointer is cast to a incompatable function pointer.
|
||||
AppendFlag("-Wwrite-strings") #Warns about string literals to char* conversions
|
||||
AppendFlag("-Wclobbered") #Warns about variables that are changed by longjmp or vfork
|
||||
AppendFlag("-Wconversion") #Warns about conversions between real and integer numbers and conversions between signed/unsigned numbers
|
||||
AppendFlag("-Wdangling-else") #Warns about confusing else statements
|
||||
# Disabled because of older GCC compilers being unhappy with it
|
||||
#AppendFlag("-Wdangling-pointer=2") #Warns about use of pointers with automatic lifetime
|
||||
AppendFlag("-Wempty-body") #Warns about empty conditional bodies
|
||||
AppendFlag("-Wfloat-conversion") #Warns about reduction of precision from double -> float conversions
|
||||
AppendFlag("-Waddress") #Prevents off uses of addresses
|
||||
AppendFlag("-Wlogical-op") #Warns about strange uses of logical operations in expressions
|
||||
#TODO: Enable this again when I have time to properly clean it all up. Hiding the functions in a namespace is plenty.
|
||||
#AppendFlag("-Wmissing-declarations") #Warns about global functions without any previous declaration
|
||||
AppendFlag("-Wmissing-field-initializers") #Warns about a structure missing some fields in it's initalizer
|
||||
#Note: padded is for masochists. That's coming from me. Only really enable this if your ready for a fun time.
|
||||
#AppendFlag("-Wpadded")
|
||||
AppendFlag("-Wredundant-decls") #Warns about declarations that happen more then once.
|
||||
AppendFlag("-Wctor-dtor-privacy") #Warns if a class appears unusable due to private ctor/dtors
|
||||
AppendFlag("-Wdelete-non-virtual-dtor") #Warns about using `delete` on a class that has virtual functions without a virtual dtor
|
||||
# Disabled because of older GCC compilers being unhappy with it
|
||||
#AppendFlag("-Winvalid-constexpr") #Warns that a function marked as constexpr can't possibly produce a constexpr expression
|
||||
AppendFlag("-Wnoexcept") #Warns when a noexcept expression is false due to throwing
|
||||
AppendFlag("-Wnoexcept-type")
|
||||
AppendFlag("-Wclass-memaccess") #Warns about accessing memory of a class. Which is likely invalid
|
||||
AppendFlag("-Wregister") #Warns of use for `register` keyword. Which has been depreicated
|
||||
AppendFlag("-Wreorder") # Warns about initlization order being wrong in a class' init list
|
||||
AppendFlag("-Wno-pessimizing-move") #Warns about copy elision being prevented my std::move
|
||||
AppendFlag("-Wno-redundant-move") #Warns about a std::move being redundant
|
||||
AppendFlag("-Wredundant-tags") #Warns about a class-key and enum-key being redundant
|
||||
AppendFlag("-Weffc++") #THEEE warning. Forces you to follow c++ guidelines for effective C++
|
||||
AppendFlag("-Wold-style-cast") #Warns about using old style C casts
|
||||
AppendFlag("-Woverloaded-virtual") #Warns when a function does not overload
|
||||
AppendFlag("-Wsign-promo") #Warns about signed promotion without being explicit
|
||||
AppendFlag("-Wmismatched-new-delete") #Warns about using new and free instead of new and delete
|
||||
AppendFlag("-Wmismatched-tags") #Warns about mismatched tags for an object.
|
||||
#AppendFlag("-Wmultiple-inheritance") #Warns about multiple inheritance (Leading to the diamond inheritance model)
|
||||
AppendFlag("-Wzero-as-null-pointer-constant") #Warns about using literal zero as a null pointer comparison. Zero might not be nullptr on some machines.
|
||||
AppendFlag("-Wcatch-value=3") #Warns about catches not catching by reference.
|
||||
AppendFlag("-Wsuggest-final-types") #Self explanatory
|
||||
AppendFlag("-Wsuggest-final-methods")# ^
|
||||
AppendFlag("-Wsuggest-override")# ^
|
||||
# Disabled because of older GCC compilers being unhappy with it
|
||||
##AppendFlag("-Wuse-after-free") #Warns about accessing a value after calling 'free' on it
|
||||
AppendFlag("-Wuseless-cast") #Warns about a cast that is useless.
|
||||
|
||||
# Starting other weird flags
|
||||
AppendFlag("-fdiagnostics-show-template-tree") # Shows the template diagnostic info as a tree instead.
|
||||
AppendFlag("-fdiagnostics-path-format=inline-events")
|
||||
|
||||
#set(FGL_WARNINGS "${FGL_WARNINGS_GENERIC} -Wpessimizing-move -Wpedantic -Weffc++ -pedantic-errors -Wnoexcept -Wuninitialized -Wunused -Wunused-parameter -Winit-self -Wconversion -Wuseless-cast -Wextra-semi -Wsuggest-final-types -Wsuggest-final-methods -Wsuggest-override -Wformat-signedness -Wno-format-zero-length -Wmissing-include-dirs -Wshift-overflow=2 -Walloc-zero -Walloca -Wsign-promo -Wconversion -Wduplicated-branches -Wduplicated-cond -Wshadow -Wshadow=local -Wvirtual-inheritance -Wno-virtual-move-assign -Wunsafe-loop-optimizations -Wnormalized -Wpacked -Wredundant-decls -Wctor-dtor-privacy -Wdeprecated-copy-dtor -Wstrict-null-sentinel -Wold-style-cast -Woverloaded-virtual -Wzero-as-null-pointer-constant -Wconditionally-supported -Wwrite-strings -Wunused-const-variable=2 -Wdouble-promotion -Wpointer-arith -Wcast-align=strict -Wcast-qual -Wconversion -Wsign-conversion -Wimplicit-fallthrough=1 -Wmisleading-indentation -Wdangling-else -Wdate-time -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation=2 -Wswitch-default -Wstringop-overflow=4 -Warray-bounds=2 -Wattribute-alias=2 -Wcatch-value=2 -Wplacement-new=2 -Wtrampolines -Winvalid-imported-macros -Winvalid-imported-macros")
|
||||
|
||||
set(FGL_CONFIG "-std=c++20 -fmax-errors=3 -fconcepts-diagnostics-depth=8")
|
||||
|
||||
# Optimization flags
|
||||
set(FGL_OPTIMIZATION_FLAGS_RELEASE "-O2 -s -flto=auto -fdevirtualize-at-ltrans -fdevirtualize-speculatively -funroll-loops") # System agonistc flags
|
||||
set(FGL_OPTIMIZATION_FLAGS_DEBUG "-O0 -g -fstrict-aliasing -fno-omit-frame-pointer -ftrapv -fverbose-asm -femit-class-debug-always") # Debug flags
|
||||
set(FGL_OPTIMIZATION_FLAGS_SYSTEM "-march=native -flto=auto -fdeclone-ctor-dtor -fgcse -fgcse-las -fgcse-sm -ftree-loop-im -fivopts -ftree-loop-ivcanon -fira-hoist-pressure -fsched-pressure -fsched-spec-load -fipa-pta -s -ffat-lto-objects -fno-enforce-eh-specs -fstrict-enums") # System specific flags. Probably not portable
|
||||
set(FGL_OPTIMIZATION_FLAGS_RELWITHDEBINFO "-O2 -g -fdevirtualize-at-ltrans -fdevirtualize-speculatively -fdeclone-ctor-dtor -funroll-loops")
|
||||
|
||||
# Final flag sets
|
||||
set(FGL_FLAGS_DEBUG "${FGL_CONFIG} ${FGL_WARNINGS} ${FGL_DEBUG}")
|
||||
set(FGL_FLAGS_SYSTEM "${FGL_CONFIG} ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
set(FGL_FLAGS_RELEASE "${FGL_CONFIG} ${FGL_SYSTEM_SAFE} ${FGL_WARNINGS}")
|
||||
set(FGL_FLAGS_RELWITHDEBINFO "${FGL_CONFIG} ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
|
||||
# Final sets
|
||||
set(FGL_FLAGS "${FGL_OPTIMIZATION_FLAGS_${UPPER_BUILD_TYPE}} ${FGL_FLAGS_${UPPER_BUILD_TYPE}}" PARENT_SCOPE) # Flags for our shit
|
||||
#set(FGL_FLAGS "${FGL_OPTIMIZATION_FLAGS_${UPPER_BUILD_TYPE}}" PARENT_SCOPE)
|
||||
set(FGL_CHILD_FLAGS "${FGL_OPTIMIZATION_FLAGS_${UPPER_BUILD_TYPE}}" PARENT_SCOPE) # Child flags for adding optmization to anything we build ourselves but doesn't follow our standard
|
||||
set(CMAKE_CXX_FLAGS "${FGL_CHILD_FLAGS}")
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(CompilerPostSetup)
|
||||
endfunction()
|
||||
11
cmake_modules/linux.cmake
Normal file
11
cmake_modules/linux.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
# /cmake_modules/linux.cmake
|
||||
|
||||
if (UNIX AND (NOT APPLE))
|
||||
set(which_program "which")
|
||||
set(os_path_separator "/")
|
||||
function(PlatformPreSetup)
|
||||
endfunction()
|
||||
|
||||
function(PlatformPostSetup)
|
||||
endfunction()
|
||||
endif () # if(UNIX AND (NOT APPLE))
|
||||
23
cmake_modules/msvc.cmake
Normal file
23
cmake_modules/msvc.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
function(CompilerPreSetup)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
#These two flags added with older gcc versions and Qt causes a compiler segfault -Wmismatched-tags -Wredundant-tags
|
||||
# STL has some warnings that prevent -Werror level compilation "-Wnull-dereference"
|
||||
set(FGL_WARNINGS "-Wall -Wundef -Wextra -Wpessimizing-move -Wpedantic -Weffc++ -pedantic-errors -Wnoexcept -Wuninitialized -Wunused -Wunused-parameter -Winit-self -Wconversion -Wuseless-cast -Wextra-semi -Wsuggest-final-types -Wsuggest-final-methods -Wsuggest-override -Wformat-signedness -Wno-format-zero-length -Wmissing-include-dirs -Wshift-overflow=2 -Walloc-zero -Walloca -Wsign-promo -Wconversion -Wduplicated-branches -Wduplicated-cond -Wshadow -Wshadow=local -Wvirtual-inheritance -Wno-virtual-move-assign -Wunsafe-loop-optimizations -Wnormalized -Wpacked -Wredundant-decls -Wctor-dtor-privacy -Wdeprecated-copy-dtor -Wstrict-null-sentinel -Wold-style-cast -Woverloaded-virtual -Wzero-as-null-pointer-constant -Wconditionally-supported -Wwrite-strings -Wunused-const-variable=2 -Wdouble-promotion -Wpointer-arith -Wcast-align=strict -Wcast-qual -Wconversion -Wsign-conversion -Wimplicit-fallthrough=1 -Wmisleading-indentation -Wdangling-else -Wdate-time -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation=2 -Wswitch-default -Wstringop-overflow=4 -Warray-bounds=2 -Wattribute-alias=2 -Wcatch-value=2 -Wplacement-new=2 -Wtrampolines -Winvalid-imported-macros -Winvalid-imported-macros")
|
||||
|
||||
set(FGL_CONFIG "-std=c++20 -fmax-errors=3 -fconcepts-diagnostics-depth=4")
|
||||
set(FGL_DEBUG "-Og -g -fstrict-aliasing -fno-omit-frame-pointer -fstack-check -ftrapv -fverbose-asm")
|
||||
#Generates system specific stuff (IE requires AVX)
|
||||
set(FGL_SYSTEM_SPECIFIC "-march=native -fgcse -fgcse-las -fgcse-sm -fdeclone-ctor-dtor -fdevirtualize-speculatively -ftree-loop-im -fivopts -ftree-loop-ivcanon -fira-hoist-pressure -fsched-pressure -fsched-spec-load -fipa-pta -flto=auto -s -ffat-lto-objects -fno-enforce-eh-specs -fstrict-enums -funroll-loops")
|
||||
#Generates safe optimization flags
|
||||
set(FGL_SYSTEM_SAFE "-O3 -fdevirtualize-at-ltrans -s")
|
||||
set(FGL_FLAGS_DEBUG "${FGL_WARNINGS} ${FGL_CONFIG} ${FGL_DEBUG}")
|
||||
set(FGL_FLAGS_SYSTEM "${FLG_CONFIG} -DNDEBUG ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
set(FGL_FLAGS_RELEASE "${FGL_CONFIG} -DNDEBUG -s ${FGL_SYSTEM_SAFE} ${FGL_WARNINGS} -Werror")
|
||||
set(FGL_FLAGS_RELWITHDEBINFO "${FGL_CONFIG} -DNDEBUG -g ${FGL_SYSTEM_SAFE} ${FGL_SYSTEM_SPECIFIC}")
|
||||
set(FGL_FLAGS "${FGL_FLAGS_${UPPER_BUILD_TYPE}}" PARENT_SCOPE) # PARENT_SCOPE sends the variable up one level.
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
function(CompilerPostSetup)
|
||||
|
||||
endfunction()
|
||||
23
cmake_modules/msys2.cmake
Normal file
23
cmake_modules/msys2.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
SET(COPY_MSYS2_BINARIES OFF CACHE BOOL "Whether to copy the DLLs from the corresponding MSYS2 prefix")
|
||||
IF (${COPY_MSYS2_BINARIES})
|
||||
GET_PARENT_PATH(${CMAKE_C_COMPILER} "/" compiler_folder)
|
||||
MESSAGE(DEBUG "compiler_folder: ${compiler_folder}")
|
||||
MESSAGE(DEBUG "BINARY FOLDER: ${BINARY_FOLDER}")
|
||||
SET(msys2_prefix_bin ${compiler_folder})
|
||||
FOREACH (
|
||||
DLL
|
||||
libsqlite3-0.dll
|
||||
libgcc_s_seh-1.dll
|
||||
libgcc_s_dw2-1.dll
|
||||
libstdc++-6.dll
|
||||
libwinpthread-1.dll
|
||||
)
|
||||
SET(file2copy "${msys2_prefix_bin}/${DLL}")
|
||||
IF (EXISTS ${file2copy})
|
||||
MESSAGE(DEBUG "COPYING: ${file2copy}")
|
||||
CONFIGURE_FILE(${file2copy} "${BINARY_FOLDER}/${DLL}" COPYONLY)
|
||||
endif () # EXISTS ${file2copy}
|
||||
ENDFOREACH ()
|
||||
ENDIF ()
|
||||
11
cmake_modules/profiling.cmake
Normal file
11
cmake_modules/profiling.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
option(ATLAS_PROFILE_ENABLE "" OFF)
|
||||
|
||||
if (${ATLAS_PROFILE_ENABLE} STREQUAL "ON")
|
||||
option(TRACY_ENABLE "" ON)
|
||||
option(TRACY_ON_DEMAND "" ON) #Reduces memory usage if profile isn't attached and makes memory profiling possible.
|
||||
option(TRACY_NO_BROADCAST "" ON)
|
||||
option(TRACY_NO_VSYNC_CAPTURE "" ON)
|
||||
option(TRACY_NO_FRAME_IMAGE "" ON)
|
||||
else ()
|
||||
option(TRACY_ENABLE "" OFF)
|
||||
endif ()
|
||||
4
cmake_modules/qt.cmake
Normal file
4
cmake_modules/qt.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
# /cmake_modules/qt.cmake
|
||||
|
||||
set(QT_VERSION "6.4.3" CACHE STRING "Version of Qt being used.")
|
||||
set(QT_PATH "C:/Qt/${QT_VERSION}/mingw_64" CACHE PATH "Prefix in which to find Qt.")
|
||||
24
cmake_modules/utils.cmake
Normal file
24
cmake_modules/utils.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
# /cmake_modules/utils.cmake
|
||||
|
||||
set(os_path_separator "")
|
||||
FUNCTION(GET_PARENT_PATH absolute_path path_separator return_var)
|
||||
message(DEBUG "absolute_path: ${absolute_path}")
|
||||
string(REPLACE ${path_separator} ";" absolute_path_list ${absolute_path}) # Converts Compiler path to ';' seperated list
|
||||
message(DEBUG "absolute_path_list: ${absolute_path_list}")
|
||||
LIST(POP_BACK absolute_path_list)
|
||||
message(DEBUG "absolute_path_list: ${absolute_path_list}")
|
||||
LIST(JOIN absolute_path_list "/" parent_path) # Converts ';' seperated list to path
|
||||
message(DEBUG "parent_path: ${parent_path}")
|
||||
SET(${return_var} ${parent_path} PARENT_SCOPE)
|
||||
RETURN(${return_var})
|
||||
ENDFUNCTION()
|
||||
|
||||
set(which_program "") # To be filled in by win32, linux, or apple modules.
|
||||
FUNCTION(WHICH program2find)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${which_program} ${program2find}
|
||||
RESULT_VARIABLE exit_code
|
||||
OUTPUT_VARIABLE return_value
|
||||
)
|
||||
|
||||
ENDFUNCTION()
|
||||
72
cmake_modules/versioninfo.cmake
Normal file
72
cmake_modules/versioninfo.cmake
Normal file
@@ -0,0 +1,72 @@
|
||||
# /cmake_modules/versioninfo.cmake
|
||||
|
||||
function(SetVersionInfo)
|
||||
endfunction()
|
||||
|
||||
# find_package(Git)
|
||||
# if (NOT Git_FOUND AND NOT DEFINED BYPASS_GIT_REQUIREMENT)
|
||||
# message(FATAL_ERROR
|
||||
# "HEY YOU! YEAH YOU! READ ME WITH YOUR EYES. Git was not found!
|
||||
# DO **NOT** **EXPECT** **SUPPORT** if your sending us log
|
||||
# information without filling these in manually or letting cmake find git.
|
||||
# Go read the docs to figure out how to do this. Or fix your git install for cmake")
|
||||
# endif ()
|
||||
#
|
||||
# if (DEFINED ATLAS_GIT_BRANCH)
|
||||
# message("-- Set git branch string to ${ATLAS_GIT_BRANCH}")
|
||||
# else ()
|
||||
# #Get the git branch us currently
|
||||
# execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ATLAS_GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# message("-- Set git branch string to: ${ATLAS_GIT_BRANCH}")
|
||||
# endif ()
|
||||
#
|
||||
# if (DEFINED ATLAS_GIT_REVISION)
|
||||
# message("-- Set git revision to: ${ATLAS_GIT_REVISION}")
|
||||
# else ()
|
||||
# execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format=%H WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ATLAS_GIT_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# message("-- Set git revision to ${ATLAS_GIT_REVISION}")
|
||||
# endif ()
|
||||
#
|
||||
# if (DEFINED ATLAS_GIT_TAG)
|
||||
# message("-- Git tag set to: ${ATLAS_GIT_TAG}")
|
||||
# else ()
|
||||
# execute_process(COMMAND ${GIT_EXECUTABLE} ls-remote --tags --sort=-v:refname https://github.com/KJNeko/Atlas.git v*.*.?
|
||||
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ATLAS_REMOTE_V_TAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# string(REPLACE "\n" ";" ATLAS_REMOTE_V_TAGS_LIST ${ATLAS_REMOTE_V_TAGS})
|
||||
# list(LENGTH ATLAS_REMOTE_V_TAGS_LIST ATLAS_REMOTE_TAGS_COUNT)
|
||||
# message("-- Tag count: ${ATLAS_REMOTE_TAGS_COUNT}")
|
||||
# list(GET ATLAS_REMOTE_V_TAGS_LIST 0 ATLAS_LATEST_TAG)
|
||||
# message("-- Latest tag: ${ATLAS_LATEST_TAG}")
|
||||
# string(REPLACE "/" ";" ATLAS_TAG_SPLIT ${ATLAS_LATEST_TAG})
|
||||
# list(GET ATLAS_TAG_SPLIT 2 ATLAS_PURE_TAG)
|
||||
# message("-- Pure tag: ${ATLAS_PURE_TAG}")
|
||||
# if (ATLAS_PURE_TAG STREQUAL "")
|
||||
# message(FATAL_ERROR "Was unable to pull latest tag from git. Please define it manually via -DATLAS_GIT_TAG.")
|
||||
# endif ()
|
||||
# set(ATLAS_GIT_TAG ${ATLAS_PURE_TAG})
|
||||
# message("-- Git tag set to: ${ATLAS_GIT_TAG}")
|
||||
# endif ()
|
||||
#
|
||||
# if (DEFINED ATLAS_GIT_REVISION_BRIEF)
|
||||
# message("-- Set git revision to: ${ATLAS_GIT_REVISION_BRIEF}")
|
||||
# else ()
|
||||
# execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format=%h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ATLAS_GIT_REVISION_BRIEF OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# message("-- Set git revision to ${ATLAS_GIT_REVISION_BRIEF}")
|
||||
# endif ()
|
||||
#
|
||||
# if (DEFINED ATLAS_GIT_CREATED_TIME)
|
||||
# message("-- Set git created time to: ${ATLAS_GIT_CREATED_TIME}")
|
||||
# else ()
|
||||
# string(TIMESTAMP ATLAS_GIT_CREATED_TIME)
|
||||
# message("-- Set git created time to ${ATLAS_GIT_CREATED_TIME}")
|
||||
# endif ()
|
||||
#
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_GIT_BRANCH="${ATLAS_GIT_BRANCH}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_GIT_TAG="${ATLAS_GIT_TAG}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_GIT_REVISION="${ATLAS_GIT_REVISION}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_GIT_REVISION_BRIEF="${ATLAS_GIT_REVISION_BRIEF}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_GIT_CREATED_TIME="${ATLAS_GIT_CREATED_TIME}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_COMPILER_ID="${CMAKE_CXX_COMPILER_ID}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_COMPILER_VER="${CMAKE_CXX_COMPILER_VERSION}")
|
||||
# target_compile_definitions(${PROJECT_NAME} PRIVATE ATLAS_PLATFORM_ID="${CMAKE_CXX_PLATFORM_ID}")
|
||||
#endfunction()
|
||||
23
cmake_modules/win32.cmake
Normal file
23
cmake_modules/win32.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
# /cmake_modules/win32.cmake
|
||||
|
||||
if (WIN32)
|
||||
set(which_program "where")
|
||||
set(os_path_separator "\\")
|
||||
|
||||
set(
|
||||
NEEDED_QT_FOLDERS
|
||||
"${CMAKE_BINARY_DIR}/bin/data"
|
||||
"${CMAKE_BINARY_DIR}/bin/iconengines"
|
||||
"${CMAKE_BINARY_DIR}/bin/imageformats"
|
||||
"${CMAKE_BINARY_DIR}/bin/networkinformation"
|
||||
"${CMAKE_BINARY_DIR}/bin/platforms"
|
||||
"${CMAKE_BINARY_DIR}/bin/styles"
|
||||
"${CMAKE_BINARY_DIR}/bin/tls"
|
||||
)
|
||||
function(PlatformPreSetup)
|
||||
endfunction() # PlatformPreSetup
|
||||
|
||||
function(PlatformPostSetup)
|
||||
endfunction() # PlatformPostSetup
|
||||
|
||||
endif () # if (WIN32)
|
||||
1
dependencies/glfw3
vendored
Submodule
1
dependencies/glfw3
vendored
Submodule
Submodule dependencies/glfw3 added at 9959dc69ca
1
dependencies/glm
vendored
Submodule
1
dependencies/glm
vendored
Submodule
Submodule dependencies/glm added at 586a402397
1
dependencies/tracy
vendored
Submodule
1
dependencies/tracy
vendored
Submodule
Submodule dependencies/tracy added at af73dba73e
1
dependencies/vma
vendored
Submodule
1
dependencies/vma
vendored
Submodule
Submodule dependencies/vma added at 5e43c795da
BIN
docs/libfgl_custom/TitorV2.png
Normal file
BIN
docs/libfgl_custom/TitorV2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 321 KiB |
6
docs/libfgl_custom/custom.css
Normal file
6
docs/libfgl_custom/custom.css
Normal file
@@ -0,0 +1,6 @@
|
||||
html {
|
||||
--top-height: 140px;
|
||||
--side-nav-fixed-width: 360px;
|
||||
--side-nav-arrow-opacity: 0.1;
|
||||
--side-nav-arrow-hover-opacity: 0.9;
|
||||
}
|
||||
89
docs/libfgl_custom/header_template.html
Normal file
89
docs/libfgl_custom/header_template.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<!-- HTML header for doxygen 1.9.3-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout=1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css"/>
|
||||
<link rel="shortcut icon" href="$relpath^tempus.ico" type="image/x-icon"/>
|
||||
$extrastylesheet
|
||||
|
||||
|
||||
<!-- BEGIN CUSTOMIZATIONS -->
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||
<script type="text/javascript"> DoxygenAwesomeDarkModeToggle.init() </script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
|
||||
<script type="text/javascript"> DoxygenAwesomeFragmentCopyButton.init() </script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
|
||||
<script type="text/javascript"> DoxygenAwesomeParagraphLink.init() </script>
|
||||
<!-- END CUSTOMIZATIONS -->
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr>
|
||||
<td colspan="2">$searchbox</td>
|
||||
</tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
240
docs/libfgl_custom/layout_template.xml
Normal file
240
docs/libfgl_custom/layout_template.xml
Normal file
@@ -0,0 +1,240 @@
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.9.3 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="Components" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="concepts" visible="yes" title="">
|
||||
</tab>
|
||||
<tab type="interfaces" visible="yes" title="">
|
||||
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="structs" visible="yes" title="">
|
||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
</tab>
|
||||
<tab type="exceptions" visible="yes" title="">
|
||||
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<constructors title=""/>
|
||||
<functions title=""/>
|
||||
<related title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<sequences title=""/>
|
||||
<dictionaries title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
BIN
docs/libfgl_custom/tempus.ico
Normal file
BIN
docs/libfgl_custom/tempus.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
prebuild.bat
Normal file
1
prebuild.bat
Normal file
@@ -0,0 +1 @@
|
||||
cmake -G"MinGW Makefiles" -DCMAKE_CXX_COMPILER="C:/mingw64/bin/g++.exe" -DCMAKE_C_COMPILER="C:/mingw64/bin/gcc.exe" -DCMAKE_BUILD_TYPE=debug -Bbuild .
|
||||
56
shaders/composition.frag
Normal file
56
shaders/composition.frag
Normal file
@@ -0,0 +1,56 @@
|
||||
#version 450
|
||||
|
||||
layout (input_attachment_index = 0, binding = 0) uniform subpassInput i_position;
|
||||
layout (input_attachment_index = 1, binding = 1) uniform subpassInput i_normal;
|
||||
layout (input_attachment_index = 2, binding = 2) uniform subpassInput i_albedo;
|
||||
|
||||
layout (location = 0) in vec2 in_uv;
|
||||
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
/*
|
||||
struct Light
|
||||
{
|
||||
vec4 position;
|
||||
vec3 color;
|
||||
float radius;
|
||||
};
|
||||
|
||||
layout (std410, binding = 3) buffer LightBuffer
|
||||
{
|
||||
Light lights[];
|
||||
};
|
||||
*/
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 position = subpassLoad(i_position).xyz;
|
||||
vec3 normal = subpassLoad(i_normal).xyz;
|
||||
vec3 albedo = subpassLoad(i_albedo).xyz;
|
||||
|
||||
#define ambient 0.05
|
||||
|
||||
vec3 frag_color = albedo.rbg * ambient;
|
||||
|
||||
// Calculate sun light
|
||||
vec3 sun_dir = normalize(vec3(0.0, -0.5, 0.5));
|
||||
float sun_intensity = max(0.0, dot(normal, sun_dir));
|
||||
frag_color += sun_intensity * 0.5;
|
||||
|
||||
/*
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
vec3 L = lights[i].position.xyz - fragPos;
|
||||
float distance = length(L);
|
||||
L = normalize(L);
|
||||
float atten = lights[i].radius / (pow(dist, 3.0) + 1.0);
|
||||
|
||||
vec3 N = normalize(normal);
|
||||
float NdotL = max(0.0, dot(N, L));
|
||||
vec3 diff = lights[i].color * albedo.rgb * NdotL * atten;
|
||||
|
||||
frag_color += diff;
|
||||
}*/
|
||||
|
||||
out_color = vec4(frag_color, 1.0);
|
||||
}
|
||||
14
shaders/composition.vert
Normal file
14
shaders/composition.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) out vec2 out_uv;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
out_uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||
gl_Position = vec4(out_uv * 2.0f - 1.0f, 0.0f, 1.0f);
|
||||
}
|
||||
40
shaders/gbuffer.frag
Normal file
40
shaders/gbuffer.frag
Normal file
@@ -0,0 +1,40 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 in_normal;
|
||||
layout (location = 1) in vec3 in_color;
|
||||
layout (location = 2) in vec3 in_world_pos;
|
||||
|
||||
layout (location = 0) out vec4 out_color;
|
||||
layout (location = 1) out vec4 out_position;
|
||||
layout (location = 2) out vec4 out_normal;
|
||||
layout (location = 3) out vec4 out_albedo;
|
||||
|
||||
layout (set = 0, binding = 0) uniform CameraInfo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
} ubo;
|
||||
|
||||
#define NEAR_PLANE 0.1f
|
||||
#define FAR_PLANE 100.0f
|
||||
|
||||
float linearDepth(float depth)
|
||||
{
|
||||
float z = depth * 2.0f - 1.0f;
|
||||
return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
out_position = vec4(in_world_pos, 1.0f);
|
||||
|
||||
vec3 N = normalize(in_normal);
|
||||
|
||||
out_normal = vec4(N, 1.0);
|
||||
|
||||
out_albedo.rbg = in_color;
|
||||
|
||||
out_position.a = linearDepth(out_position.z);
|
||||
|
||||
out_color = vec4(0.0);
|
||||
}
|
||||
35
shaders/gbuffer.vert
Normal file
35
shaders/gbuffer.vert
Normal file
@@ -0,0 +1,35 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 1) in vec3 color;
|
||||
layout (location = 2) in vec3 normal;
|
||||
layout (location = 3) in vec2 uv;
|
||||
|
||||
layout (location = 4) in mat4 instance_model_matrix;
|
||||
layout (location = 8) in mat4 instance_normal_matrix;
|
||||
|
||||
layout (location = 0) out vec3 out_normal;
|
||||
layout (location = 1) out vec3 out_color;
|
||||
layout (location = 2) out vec3 out_world_pos;
|
||||
layout (location = 3) out vec3 out_tangent;
|
||||
|
||||
layout (set = 0, binding = 0) uniform CameraInfo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
} ubo;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 position_world = instance_model_matrix * vec4(position, 1.0);
|
||||
|
||||
gl_Position = ubo.projection * ubo.view * position_world;
|
||||
|
||||
out_world_pos = vec3(instance_model_matrix * vec4(position, 1.0));
|
||||
|
||||
mat3 normal_matrix = transpose(inverse(mat3(instance_model_matrix)));
|
||||
|
||||
out_normal = normalize(normal_matrix * normal);
|
||||
|
||||
out_color = color;
|
||||
}
|
||||
43
shaders/point_light.frag
Normal file
43
shaders/point_light.frag
Normal file
@@ -0,0 +1,43 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec2 frag_offset;
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout (set = 0, binding = 0) uniform GlobalUbo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
vec4 ambient_light_color; // w is intensity
|
||||
} ubo;
|
||||
|
||||
layout (set = 0, binding = 1) uniform LightingUBO
|
||||
{
|
||||
PointLight lights[10];
|
||||
int num;
|
||||
} point_lights;
|
||||
|
||||
layout (push_constant) uniform Push
|
||||
{
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
float radius;
|
||||
} push;
|
||||
|
||||
const float M_PI = 3.14151926538;
|
||||
|
||||
void main() {
|
||||
float dis = sqrt(dot(frag_offset, frag_offset));
|
||||
|
||||
if (dis >= 1.0)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
out_color = vec4(push.color.xyz, 0.5 * (cos(dis * M_PI) + 1.0));
|
||||
}
|
||||
53
shaders/point_light.vert
Normal file
53
shaders/point_light.vert
Normal file
@@ -0,0 +1,53 @@
|
||||
#version 450
|
||||
|
||||
const vec2 OFFSETS[6] = vec2[](
|
||||
vec2(-1.0, -1.0),
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(1.0, -1.0),
|
||||
vec2(1.0, -1.0),
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(1.0, 1.0)
|
||||
);
|
||||
|
||||
layout (location = 0) out vec2 frag_offset;
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout (set = 0, binding = 0) uniform GlobalUbo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
vec4 ambient_light_color; // w is intensity
|
||||
} ubo;
|
||||
|
||||
layout (set = 0, binding = 1) uniform LightingUBO
|
||||
{
|
||||
PointLight lights[10];
|
||||
int num;
|
||||
} point_lights;
|
||||
|
||||
layout (push_constant) uniform Push
|
||||
{
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
float radius;
|
||||
} push;
|
||||
|
||||
void main() {
|
||||
|
||||
frag_offset = OFFSETS[gl_VertexIndex];
|
||||
vec3 camera_right_world = { ubo.view[0][0], ubo.view[1][0], ubo.view[2][0] };
|
||||
vec3 camera_up_world = { ubo.view[0][1], ubo.view[1][1], ubo.view[2][1] };
|
||||
|
||||
vec3 position_world = push.position.xyz + push.radius * frag_offset.x * camera_right_world + push.radius * frag_offset.y * camera_up_world;
|
||||
|
||||
gl_Position = ubo.projection * ubo.view * vec4(position_world, 1.0);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +1,59 @@
|
||||
#version 450
|
||||
|
||||
|
||||
layout (location = 0) in vec3 frag_color;
|
||||
layout (location = 1) in vec3 frag_pos_world;
|
||||
layout (location = 2) in vec3 frag_normal_world;
|
||||
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
layout (push_constant) uniform Push {
|
||||
mat4 transform;
|
||||
vec3 color;
|
||||
} push;
|
||||
layout (set = 0, binding = 0) uniform CameraInfo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
} ubo;
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout (set = 0, binding = 2) uniform lights
|
||||
{
|
||||
int num_point_lights;
|
||||
PointLight lights[10];
|
||||
} point_lights;
|
||||
|
||||
const vec4 ambient_light_color = vec4(0.5, 0.5, 0.5, 1.0); // w is intensity
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(frag_color, 1.0);
|
||||
vec3 diffuse_light = ambient_light_color.xyz * ambient_light_color.w;
|
||||
vec3 specular_light = vec3(0.0);
|
||||
vec3 surface_normal = normalize(frag_normal_world);
|
||||
|
||||
vec3 camera_pos_world = ubo.inverse_view[3].xyz;
|
||||
vec3 view_direction = normalize(camera_pos_world - frag_pos_world);
|
||||
|
||||
for (int i = 0; i < point_lights.num_point_lights; ++i)
|
||||
{
|
||||
PointLight light = point_lights.lights[i];
|
||||
vec3 direction_to_light = light.position.xyz - frag_pos_world;
|
||||
float attenuation = 1.0 / dot(direction_to_light, direction_to_light); // distance squared
|
||||
direction_to_light = normalize(direction_to_light);
|
||||
|
||||
float cos_ang_incidence = max(dot(surface_normal, direction_to_light), 0);
|
||||
vec3 intensity = light.color.xyz * light.color.w * attenuation;
|
||||
|
||||
diffuse_light += intensity * cos_ang_incidence;
|
||||
|
||||
//specular light
|
||||
vec3 half_angle = normalize(direction_to_light + view_direction);
|
||||
float blinnTerm = dot(surface_normal, half_angle);
|
||||
blinnTerm = clamp(blinnTerm, 0, 1);
|
||||
blinnTerm = pow(blinnTerm, 64.0); // higher -> shaper highlight
|
||||
specular_light += intensity * blinnTerm;
|
||||
}
|
||||
|
||||
out_color = vec4(diffuse_light * frag_color + specular_light * frag_color, 1.0);
|
||||
}
|
||||
@@ -2,17 +2,39 @@
|
||||
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 1) in vec3 color;
|
||||
layout (location = 2) in vec3 normal;
|
||||
layout (location = 3) in vec2 uv;
|
||||
|
||||
layout (location = 4) in mat4 instance_model_matrix;
|
||||
layout (location = 8) in mat4 instance_normal_matrix;
|
||||
|
||||
layout (location = 0) out vec3 frag_color;
|
||||
layout (location = 1) out vec3 frag_pos_world;
|
||||
layout (location = 2) out vec3 frag_normal_world;
|
||||
|
||||
layout (push_constant) uniform Push {
|
||||
mat4 transform;
|
||||
vec3 color;
|
||||
} push;
|
||||
layout (set = 0, binding = 0) uniform CameraInfo {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 inverse_view;
|
||||
} ubo;
|
||||
|
||||
void main()
|
||||
struct PointLight
|
||||
{
|
||||
//gl_Position = vec4(push.transform * position + push.offset, 0.0, 1.0);
|
||||
gl_Position = push.transform * vec4(position, 1.0);
|
||||
vec4 position;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout (set = 0, binding = 2) uniform lights
|
||||
{
|
||||
int num_point_lights;
|
||||
PointLight light[10];
|
||||
} point_lights;
|
||||
|
||||
void main() {
|
||||
vec4 position_world = instance_model_matrix * vec4(position, 1.0);
|
||||
gl_Position = ubo.projection * ubo.view * position_world;
|
||||
|
||||
frag_normal_world = normalize(mat3(instance_normal_matrix) * normal);
|
||||
frag_pos_world = position_world.xyz;
|
||||
frag_color = color;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
|
||||
add_subdirectory(vma)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(engine)
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC FGLEngine)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE FGLEngine Vulkan::Vulkan glfw glm FGLImGui Tracy::TracyClient GPUOpen::VulkanMemoryAllocator)
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "engine/EngineContext.hpp"
|
||||
|
||||
int main()
|
||||
try
|
||||
{
|
||||
fgl::engine::EngineContext engine_ctx {};
|
||||
|
||||
@@ -15,13 +14,3 @@ try
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
std::cerr << "\n\n Exception caught:\n\t" << e.what() << std::endl;
|
||||
std::abort();
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
std::cerr << "\n\n An unknown error has occured.\n";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
183
src/engine/Attachment.hpp
Normal file
183
src/engine/Attachment.hpp
Normal file
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// Created by kj16609 on 12/4/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "Device.hpp"
|
||||
#include "concepts/is_attachment.hpp"
|
||||
#include "engine/image/Image.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct AttachmentResources
|
||||
{
|
||||
std::vector< std::shared_ptr< Image > > m_images {};
|
||||
std::vector< std::shared_ptr< ImageView > > m_image_views {};
|
||||
};
|
||||
|
||||
template <
|
||||
vk::AttachmentLoadOp load_op,
|
||||
vk::AttachmentStoreOp store_op,
|
||||
vk::ImageLayout inital_layout,
|
||||
vk::ImageLayout final_layout,
|
||||
vk::ImageUsageFlags usage >
|
||||
class Attachment
|
||||
{
|
||||
vk::AttachmentDescription description {};
|
||||
std::uint32_t index { std::numeric_limits< std::uint32_t >::max() };
|
||||
|
||||
public:
|
||||
|
||||
vk::ClearValue m_clear_value {};
|
||||
|
||||
void setClear( vk::ClearColorValue value ) { m_clear_value = value; }
|
||||
|
||||
void setClear( vk::ClearDepthStencilValue value ) { m_clear_value = value; }
|
||||
|
||||
AttachmentResources m_attachment_resources {};
|
||||
|
||||
void setIndex( const std::uint32_t idx ) { index = idx; }
|
||||
|
||||
constexpr static vk::AttachmentLoadOp loadOp = load_op;
|
||||
constexpr static vk::AttachmentStoreOp storeOp = store_op;
|
||||
constexpr static vk::ImageLayout InitalLayout = inital_layout;
|
||||
constexpr static vk::ImageLayout FinalLayout = final_layout;
|
||||
|
||||
Attachment( const vk::Format format )
|
||||
{
|
||||
assert( format != vk::Format::eUndefined && "Attachment format must not be undefined" );
|
||||
description.format = format;
|
||||
description.samples = vk::SampleCountFlagBits::e1;
|
||||
description.loadOp = load_op;
|
||||
description.storeOp = store_op;
|
||||
description.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
|
||||
description.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
|
||||
description.initialLayout = inital_layout;
|
||||
description.finalLayout = final_layout;
|
||||
}
|
||||
|
||||
void attachImageView( std::uint16_t frame_idx, std::shared_ptr< ImageView > image_view )
|
||||
{
|
||||
auto& image_views = m_attachment_resources.m_image_views;
|
||||
if ( image_views.size() <= frame_idx ) image_views.resize( frame_idx + 1 );
|
||||
image_views[ frame_idx ] = std::move( image_view );
|
||||
}
|
||||
|
||||
void linkImage( std::uint16_t frame_idx, Image& image ) { attachImageView( frame_idx, image.view() ); }
|
||||
|
||||
void linkImages( std::vector< Image >& images )
|
||||
{
|
||||
for ( std::uint16_t i = 0; i < images.size(); ++i )
|
||||
{
|
||||
linkImage( i, images[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
void createResources( const std::uint32_t count, vk::Extent2D extent )
|
||||
{
|
||||
for ( std::uint16_t i = 0; i < count; ++i )
|
||||
{
|
||||
auto& images = m_attachment_resources.m_images;
|
||||
auto& image_views = m_attachment_resources.m_image_views;
|
||||
images.emplace_back( std::make_shared< Image >(
|
||||
extent,
|
||||
description.format,
|
||||
usage | vk::ImageUsageFlagBits::eInputAttachment,
|
||||
inital_layout,
|
||||
final_layout ) );
|
||||
image_views.emplace_back( images.back()->view() );
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentResources resources() { return m_attachment_resources; }
|
||||
|
||||
vk::AttachmentDescription& desc() { return description; }
|
||||
|
||||
std::uint32_t getIndex() const
|
||||
{
|
||||
assert(
|
||||
index != std::numeric_limits< std::uint32_t >::max()
|
||||
&& "Attachment must be registered in RenderPass before use" );
|
||||
return index;
|
||||
}
|
||||
|
||||
friend class RenderPass;
|
||||
};
|
||||
|
||||
template < is_attachment AttachmentT, vk::ImageLayout layout >
|
||||
struct InputAttachment
|
||||
{
|
||||
static constexpr bool is_input { true };
|
||||
static constexpr vk::ImageLayout m_layout { layout };
|
||||
|
||||
using Attachment = AttachmentT;
|
||||
};
|
||||
|
||||
template < is_attachment AttachmentT, vk::ImageLayout layout >
|
||||
struct UsedAttachment
|
||||
{
|
||||
static constexpr bool is_input { false };
|
||||
static constexpr vk::ImageLayout m_layout { layout };
|
||||
|
||||
using Attachment = AttachmentT;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_input_attachment = requires( T a ) {
|
||||
{
|
||||
a.is_input
|
||||
} -> std::same_as< const bool& >;
|
||||
{
|
||||
a.m_layout
|
||||
} -> std::same_as< const vk::ImageLayout& >;
|
||||
} && T::is_input;
|
||||
|
||||
template < typename T >
|
||||
concept is_used_attachment = requires( T a ) {
|
||||
{
|
||||
a.is_input
|
||||
} -> std::same_as< const bool& >;
|
||||
{
|
||||
a.m_layout
|
||||
} -> std::same_as< const vk::ImageLayout& >;
|
||||
} && !T::is_input;
|
||||
|
||||
template < typename T > concept is_wrapped_attachment = is_input_attachment< T > || is_used_attachment< T >;
|
||||
|
||||
template < typename T >
|
||||
requires is_wrapped_attachment< T >
|
||||
using UnwrappedAttachment = std::conditional_t< is_wrapped_attachment< T >, typename T::Attachment, T >;
|
||||
|
||||
using ColoredPresentAttachment = Attachment<
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::ePresentSrcKHR,
|
||||
vk::ImageUsageFlagBits::eColorAttachment >;
|
||||
|
||||
using DepthAttachment = Attachment<
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||
vk::ImageUsageFlagBits::eDepthStencilAttachment >;
|
||||
|
||||
using ColorAttachment = Attachment<
|
||||
vk::AttachmentLoadOp::eClear,
|
||||
vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::ImageUsageFlagBits::eColorAttachment >;
|
||||
|
||||
static_assert( is_input_attachment< InputAttachment< ColorAttachment, vk::ImageLayout::eShaderReadOnlyOptimal > > );
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -2,16 +2,27 @@
|
||||
|
||||
file(GLOB_RECURSE CPP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/**.cpp")
|
||||
|
||||
find_package(Vulkan REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(glm REQUIRED)
|
||||
|
||||
set(TRACY_ENABLE "" OFF)
|
||||
set(TRACY_ON_DEMAND "" ON)
|
||||
|
||||
add_library(FGLEngine STATIC ${CPP_SOURCES})
|
||||
target_link_libraries(FGLEngine PRIVATE Vulkan::Vulkan glfw glm FGLImGui tracy)
|
||||
target_precompile_headers(FGLEngine PRIVATE
|
||||
<vector>
|
||||
<iostream>
|
||||
<vulkan/vulkan.h>
|
||||
<tracy/Tracy.hpp>
|
||||
<vma/vma_impl.hpp>
|
||||
<glm/glm.hpp>
|
||||
<glm/gtc/matrix_transform.hpp>
|
||||
<glm/gtc/constants.hpp>
|
||||
<array>
|
||||
<chrono>
|
||||
)
|
||||
|
||||
target_compile_definitions(FGLEngine PUBLIC VULKAN_HPP_FLAGS_MASK_TYPE_AS_PUBLIC)
|
||||
|
||||
target_link_libraries(FGLEngine PUBLIC Vulkan::Vulkan glfw glm FGLImGui Tracy::TracyClient VMA)
|
||||
target_include_directories(FGLEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
set_target_properties(FGLEngine PROPERTIES COMPILE_FLAGS ${FGL_FLAGS})
|
||||
#target_compile_definitions(FGLEngine PRIVATE TRACY_ENABLE="1")
|
||||
|
||||
#target_compile_definitions(FGLEngine PRIVATE TRACY_ENABLE=1)
|
||||
target_compile_definitions(FGLEngine PRIVATE ENABLE_IMGUI=1)
|
||||
|
||||
@@ -33,4 +33,84 @@ namespace fgl::engine
|
||||
projection_matrix[ 3 ][ 2 ] = -( far * near ) / ( far - near );
|
||||
}
|
||||
|
||||
void Camera::setViewDirection( glm::vec3 position, glm::vec3 direction, glm::vec3 up )
|
||||
{
|
||||
const glm::vec3 w { glm::normalize( direction ) };
|
||||
const glm::vec3 u { glm::normalize( glm::cross( w, up ) ) };
|
||||
const glm::vec3 v { glm::cross( w, u ) };
|
||||
|
||||
view_matrix = glm::mat4 { 1.0f };
|
||||
view_matrix[ 0 ][ 0 ] = u.x;
|
||||
view_matrix[ 1 ][ 0 ] = u.y;
|
||||
view_matrix[ 2 ][ 0 ] = u.z;
|
||||
view_matrix[ 0 ][ 1 ] = v.x;
|
||||
view_matrix[ 1 ][ 1 ] = v.y;
|
||||
view_matrix[ 2 ][ 1 ] = v.z;
|
||||
view_matrix[ 0 ][ 2 ] = w.x;
|
||||
view_matrix[ 1 ][ 2 ] = w.y;
|
||||
view_matrix[ 2 ][ 2 ] = w.z;
|
||||
view_matrix[ 3 ][ 0 ] = -glm::dot( u, position );
|
||||
view_matrix[ 3 ][ 1 ] = -glm::dot( v, position );
|
||||
view_matrix[ 3 ][ 2 ] = -glm::dot( w, position );
|
||||
|
||||
inverse_view_matrix = glm::mat4 { 1.0f };
|
||||
inverse_view_matrix[ 0 ][ 0 ] = u.x;
|
||||
inverse_view_matrix[ 0 ][ 1 ] = u.y;
|
||||
inverse_view_matrix[ 0 ][ 2 ] = u.z;
|
||||
inverse_view_matrix[ 1 ][ 0 ] = v.x;
|
||||
inverse_view_matrix[ 1 ][ 1 ] = v.y;
|
||||
inverse_view_matrix[ 1 ][ 2 ] = v.z;
|
||||
inverse_view_matrix[ 2 ][ 0 ] = w.x;
|
||||
inverse_view_matrix[ 2 ][ 1 ] = w.y;
|
||||
inverse_view_matrix[ 2 ][ 2 ] = w.z;
|
||||
inverse_view_matrix[ 3 ][ 0 ] = position.x;
|
||||
inverse_view_matrix[ 3 ][ 1 ] = position.y;
|
||||
inverse_view_matrix[ 3 ][ 2 ] = position.z;
|
||||
}
|
||||
|
||||
void Camera::setViewTarget( glm::vec3 position, glm::vec3 target, glm::vec3 up )
|
||||
{
|
||||
setViewDirection( position, target - position, up );
|
||||
}
|
||||
|
||||
void Camera::setViewYXZ( glm::vec3 position, glm::vec3 rotation )
|
||||
{
|
||||
const float c3 { glm::cos( rotation.z ) };
|
||||
const float s3 { glm::sin( rotation.z ) };
|
||||
const float c2 { glm::cos( rotation.x ) };
|
||||
const float s2 { glm::sin( rotation.x ) };
|
||||
const float c1 { glm::cos( rotation.y ) };
|
||||
const float s1 { glm::sin( rotation.y ) };
|
||||
const glm::vec3 u { ( c1 * c3 + s1 * s2 * s3 ), ( c2 * s3 ), ( c1 * s2 * s3 - c3 * s1 ) };
|
||||
const glm::vec3 v { ( c3 * s1 * s2 - c1 * s3 ), ( c2 * c3 ), ( c1 * c3 * s2 + s1 * s3 ) };
|
||||
const glm::vec3 w { ( c2 * s1 ), ( -s2 ), ( c1 * c2 ) };
|
||||
view_matrix = glm::mat4 { 1.0f };
|
||||
view_matrix[ 0 ][ 0 ] = u.x;
|
||||
view_matrix[ 1 ][ 0 ] = u.y;
|
||||
view_matrix[ 2 ][ 0 ] = u.z;
|
||||
view_matrix[ 0 ][ 1 ] = v.x;
|
||||
view_matrix[ 1 ][ 1 ] = v.y;
|
||||
view_matrix[ 2 ][ 1 ] = v.z;
|
||||
view_matrix[ 0 ][ 2 ] = w.x;
|
||||
view_matrix[ 1 ][ 2 ] = w.y;
|
||||
view_matrix[ 2 ][ 2 ] = w.z;
|
||||
view_matrix[ 3 ][ 0 ] = -glm::dot( u, position );
|
||||
view_matrix[ 3 ][ 1 ] = -glm::dot( v, position );
|
||||
view_matrix[ 3 ][ 2 ] = -glm::dot( w, position );
|
||||
|
||||
inverse_view_matrix = glm::mat4 { 1.0f };
|
||||
inverse_view_matrix[ 0 ][ 0 ] = u.x;
|
||||
inverse_view_matrix[ 0 ][ 1 ] = u.y;
|
||||
inverse_view_matrix[ 0 ][ 2 ] = u.z;
|
||||
inverse_view_matrix[ 1 ][ 0 ] = v.x;
|
||||
inverse_view_matrix[ 1 ][ 1 ] = v.y;
|
||||
inverse_view_matrix[ 1 ][ 2 ] = v.z;
|
||||
inverse_view_matrix[ 2 ][ 0 ] = w.x;
|
||||
inverse_view_matrix[ 2 ][ 1 ] = w.y;
|
||||
inverse_view_matrix[ 2 ][ 2 ] = w.z;
|
||||
inverse_view_matrix[ 3 ][ 0 ] = position.x;
|
||||
inverse_view_matrix[ 3 ][ 1 ] = position.y;
|
||||
inverse_view_matrix[ 3 ][ 2 ] = position.z;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -12,14 +12,26 @@ namespace fgl::engine
|
||||
{
|
||||
class Camera
|
||||
{
|
||||
glm::mat4 projection_matrix { 1.f };
|
||||
glm::mat4 projection_matrix { 1.0f };
|
||||
glm::mat4 view_matrix { 1.0f };
|
||||
glm::mat4 inverse_view_matrix { 1.0f };
|
||||
|
||||
public:
|
||||
|
||||
const glm::mat4& getProjectionMatrix() const { return projection_matrix; }
|
||||
|
||||
const glm::mat4& getViewMatrix() const { return view_matrix; }
|
||||
|
||||
const glm::mat4& getInverseView() const { return inverse_view_matrix; }
|
||||
|
||||
void setOrthographicProjection( float left, float right, float top, float bottom, float near, float far );
|
||||
void setPerspectiveProjection( float fovy, float aspect, float near, float far );
|
||||
|
||||
const glm::vec3 getPosition() const { return glm::vec3( inverse_view_matrix[ 3 ] ); }
|
||||
|
||||
void setViewDirection( glm::vec3 pos, glm::vec3 direction, glm::vec3 up = glm::vec3 { 0.0f, -1.0f, 0.0f } );
|
||||
void setViewTarget( glm::vec3 pos, glm::vec3 target, glm::vec3 up = glm::vec3 { 0.0f, -1.0f, 0.0f } );
|
||||
void setViewYXZ( glm::vec3 pos, glm::vec3 rotation );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
#include "Device.hpp"
|
||||
|
||||
#include "engine/descriptors/DescriptorPool.hpp"
|
||||
|
||||
// std headers
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
PFN_vkCreateDebugUtilsMessengerEXT pfnVkCreateDebugUtilsMessengerEXT { nullptr };
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT pfnVkDestroyDebugUtilsMessengerEXT { nullptr };
|
||||
PFN_vkSetDebugUtilsObjectNameEXT pfnVkSetDebugUtilsObjectNameEXT { nullptr };
|
||||
|
||||
// local callback functions
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||
static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(
|
||||
[[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
[[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
@@ -21,33 +24,59 @@ namespace fgl::engine
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
VkResult CreateDebugUtilsMessengerEXT(
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDebugUtilsMessengerEXT* pDebugMessenger )
|
||||
const VkDebugUtilsMessengerCreateInfoEXT* create_info,
|
||||
const VkAllocationCallbacks* allocator,
|
||||
VkDebugUtilsMessengerEXT* messenger )
|
||||
{
|
||||
auto func = reinterpret_cast<
|
||||
PFN_vkCreateDebugUtilsMessengerEXT >( vkGetInstanceProcAddr( instance, "vkCreateDebugUtilsMessengerEXT" ) );
|
||||
if ( func != nullptr )
|
||||
{
|
||||
return func( instance, pCreateInfo, pAllocator, pDebugMessenger );
|
||||
return pfnVkCreateDebugUtilsMessengerEXT( instance, create_info, allocator, messenger );
|
||||
}
|
||||
else
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(
|
||||
VkInstance instance, VkDebugUtilsMessengerEXT messenger, VkAllocationCallbacks const * pAllocator )
|
||||
{
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
return pfnVkDestroyDebugUtilsMessengerEXT( instance, messenger, pAllocator );
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
vkSetDebugUtilsObjectNameEXT( VkDevice device, const VkDebugUtilsObjectNameInfoEXT* nameInfo )
|
||||
{
|
||||
return pfnVkSetDebugUtilsObjectNameEXT( device, nameInfo );
|
||||
}
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
vk::Result CreateDebugUtilsMessengerEXT(
|
||||
vk::Instance instance,
|
||||
const vk::DebugUtilsMessengerCreateInfoEXT& pCreateInfo,
|
||||
[[maybe_unused]] const vk::AllocationCallbacks* pAllocator,
|
||||
vk::DebugUtilsMessengerEXT* pDebugMessenger )
|
||||
{
|
||||
return instance.createDebugUtilsMessengerEXT( &pCreateInfo, pAllocator, pDebugMessenger );
|
||||
}
|
||||
|
||||
void DestroyDebugUtilsMessengerEXT(
|
||||
VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator )
|
||||
vk::Instance instance,
|
||||
vk::DebugUtilsMessengerEXT debugMessenger,
|
||||
[[maybe_unused]] const vk::AllocationCallbacks* pAllocator )
|
||||
{
|
||||
auto func = reinterpret_cast< PFN_vkDestroyDebugUtilsMessengerEXT >(
|
||||
vkGetInstanceProcAddr( instance, "vkDestroyDebugUtilsMessengerEXT" ) );
|
||||
if ( func != nullptr )
|
||||
{
|
||||
func( instance, debugMessenger, pAllocator );
|
||||
instance.destroyDebugUtilsMessengerEXT( debugMessenger );
|
||||
}
|
||||
|
||||
inline static Device* global_device { nullptr };
|
||||
|
||||
Device& Device::init( Window& window )
|
||||
{
|
||||
global_device = new Device( window );
|
||||
DescriptorPool::init( *global_device );
|
||||
return *global_device;
|
||||
}
|
||||
|
||||
Device& Device::getInstance()
|
||||
{
|
||||
assert( global_device && "Device not initialized" );
|
||||
return *global_device;
|
||||
}
|
||||
|
||||
// class member functions
|
||||
@@ -58,9 +87,14 @@ namespace fgl::engine
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
createLogicalDevice();
|
||||
createVMAAllocator();
|
||||
createCommandPool();
|
||||
initImGui();
|
||||
}
|
||||
|
||||
void Device::initImGui()
|
||||
{}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
vkDestroyCommandPool( device_, m_commandPool, nullptr );
|
||||
@@ -82,23 +116,21 @@ namespace fgl::engine
|
||||
throw std::runtime_error( "validation layers requested, but not available!" );
|
||||
}
|
||||
|
||||
VkApplicationInfo appInfo = {};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.pApplicationName = "LittleVulkanEngine App";
|
||||
vk::ApplicationInfo appInfo {};
|
||||
appInfo.pApplicationName = "Mecha Game";
|
||||
appInfo.applicationVersion = VK_MAKE_VERSION( 1, 0, 0 );
|
||||
appInfo.pEngineName = "No Engine";
|
||||
appInfo.pEngineName = "titor";
|
||||
appInfo.engineVersion = VK_MAKE_VERSION( 1, 0, 0 );
|
||||
appInfo.apiVersion = VK_API_VERSION_1_0;
|
||||
appInfo.apiVersion = VK_API_VERSION_1_2;
|
||||
|
||||
VkInstanceCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
vk::InstanceCreateInfo createInfo {};
|
||||
createInfo.pApplicationInfo = &appInfo;
|
||||
|
||||
auto extensions = getRequiredExtensions();
|
||||
createInfo.enabledExtensionCount = static_cast< uint32_t >( extensions.size() );
|
||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
|
||||
vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo {};
|
||||
if ( enableValidationLayers )
|
||||
{
|
||||
createInfo.enabledLayerCount = static_cast< uint32_t >( validationLayers.size() );
|
||||
@@ -113,68 +145,58 @@ namespace fgl::engine
|
||||
createInfo.pNext = nullptr;
|
||||
}
|
||||
|
||||
if ( vkCreateInstance( &createInfo, nullptr, &m_instance ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create instance!" );
|
||||
}
|
||||
if ( vk::createInstance( &createInfo, nullptr, &m_instance ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "Failed to create Vulkan instance" );
|
||||
|
||||
hasGflwRequiredInstanceExtensions();
|
||||
}
|
||||
|
||||
void Device::pickPhysicalDevice()
|
||||
{
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices( m_instance, &deviceCount, nullptr );
|
||||
if ( deviceCount == 0 )
|
||||
{
|
||||
throw std::runtime_error( "failed to find GPUs with Vulkan support!" );
|
||||
}
|
||||
std::cout << "Device count: " << deviceCount << std::endl;
|
||||
std::vector< VkPhysicalDevice > devices( deviceCount );
|
||||
vkEnumeratePhysicalDevices( m_instance, &deviceCount, devices.data() );
|
||||
std::vector< vk::PhysicalDevice > devices { m_instance.enumeratePhysicalDevices() };
|
||||
|
||||
bool found { false };
|
||||
|
||||
for ( const auto& device : devices )
|
||||
{
|
||||
if ( isDeviceSuitable( device ) )
|
||||
{
|
||||
m_physical_device = device;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_physical_device == VK_NULL_HANDLE )
|
||||
m_properties = m_physical_device.getProperties();
|
||||
|
||||
if ( !found )
|
||||
{
|
||||
throw std::runtime_error( "failed to find a suitable GPU!" );
|
||||
}
|
||||
|
||||
vkGetPhysicalDeviceProperties( m_physical_device, &m_properties );
|
||||
std::cout << "physical device: " << m_properties.deviceName << std::endl;
|
||||
}
|
||||
|
||||
void Device::createLogicalDevice()
|
||||
{
|
||||
QueueFamilyIndices indices = findQueueFamilies( m_physical_device );
|
||||
const QueueFamilyIndices indices { findQueueFamilies( m_physical_device ) };
|
||||
|
||||
std::vector< VkDeviceQueueCreateInfo > queueCreateInfos;
|
||||
std::vector< vk::DeviceQueueCreateInfo > queueCreateInfos;
|
||||
std::set< uint32_t > uniqueQueueFamilies = { indices.graphicsFamily, indices.presentFamily };
|
||||
|
||||
float queuePriority = 1.0f;
|
||||
for ( uint32_t queueFamily : uniqueQueueFamilies )
|
||||
{
|
||||
VkDeviceQueueCreateInfo queueCreateInfo = {};
|
||||
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
vk::DeviceQueueCreateInfo queueCreateInfo = {};
|
||||
queueCreateInfo.queueFamilyIndex = queueFamily;
|
||||
queueCreateInfo.queueCount = 1;
|
||||
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||||
queueCreateInfos.push_back( queueCreateInfo );
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures = {};
|
||||
vk::PhysicalDeviceFeatures deviceFeatures = {};
|
||||
deviceFeatures.samplerAnisotropy = VK_TRUE;
|
||||
deviceFeatures.multiDrawIndirect = VK_TRUE;
|
||||
|
||||
VkDeviceCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
|
||||
vk::DeviceCreateInfo createInfo {};
|
||||
createInfo.queueCreateInfoCount = static_cast< uint32_t >( queueCreateInfos.size() );
|
||||
createInfo.pQueueCreateInfos = queueCreateInfos.data();
|
||||
|
||||
@@ -182,6 +204,23 @@ namespace fgl::engine
|
||||
createInfo.enabledExtensionCount = static_cast< uint32_t >( deviceExtensions.size() );
|
||||
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
||||
|
||||
//Get device extension list
|
||||
const auto supported_extensions { m_physical_device.enumerateDeviceExtensionProperties() };
|
||||
std::cout << "Supported extensions:" << std::endl;
|
||||
for ( auto& desired_ext : deviceExtensions )
|
||||
{
|
||||
bool found { false };
|
||||
for ( auto& supported_ext : supported_extensions )
|
||||
{
|
||||
if ( strcmp( desired_ext, supported_ext.extensionName ) == 0 )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << "\t" << desired_ext << ": " << found << std::endl;
|
||||
}
|
||||
|
||||
// might not really be necessary anymore because device specific validation layers
|
||||
// have been deprecated
|
||||
if ( enableValidationLayers )
|
||||
@@ -194,25 +233,24 @@ namespace fgl::engine
|
||||
createInfo.enabledLayerCount = 0;
|
||||
}
|
||||
|
||||
if ( vkCreateDevice( m_physical_device, &createInfo, nullptr, &device_ ) != VK_SUCCESS )
|
||||
if ( m_physical_device.createDevice( &createInfo, nullptr, &device_ ) != vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to create logical device!" );
|
||||
}
|
||||
|
||||
vkGetDeviceQueue( device_, indices.graphicsFamily, 0, &graphicsQueue_ );
|
||||
vkGetDeviceQueue( device_, indices.presentFamily, 0, &presentQueue_ );
|
||||
device_.getQueue( indices.graphicsFamily, 0, &graphicsQueue_ );
|
||||
device_.getQueue( indices.presentFamily, 0, &presentQueue_ );
|
||||
}
|
||||
|
||||
void Device::createCommandPool()
|
||||
{
|
||||
QueueFamilyIndices queueFamilyIndices = findPhysicalQueueFamilies();
|
||||
|
||||
VkCommandPoolCreateInfo poolInfo = {};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
vk::CommandPoolCreateInfo poolInfo = {};
|
||||
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
|
||||
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
poolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer | vk::CommandPoolCreateFlagBits::eTransient;
|
||||
|
||||
if ( vkCreateCommandPool( device_, &poolInfo, nullptr, &m_commandPool ) != VK_SUCCESS )
|
||||
if ( device_.createCommandPool( &poolInfo, nullptr, &m_commandPool ) != vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to create command pool!" );
|
||||
}
|
||||
@@ -220,37 +258,42 @@ namespace fgl::engine
|
||||
|
||||
void Device::createSurface()
|
||||
{
|
||||
m_window.createWindowSurface( m_instance, &surface_ );
|
||||
surface_ = m_window.createWindowSurface( m_instance );
|
||||
}
|
||||
|
||||
bool Device::isDeviceSuitable( VkPhysicalDevice device )
|
||||
bool Device::isDeviceSuitable( vk::PhysicalDevice device )
|
||||
{
|
||||
QueueFamilyIndices indices = findQueueFamilies( device );
|
||||
const QueueFamilyIndices indices { findQueueFamilies( device ) };
|
||||
|
||||
bool extensionsSupported = checkDeviceExtensionSupport( device );
|
||||
const bool extensionsSupported { checkDeviceExtensionSupport( device ) };
|
||||
|
||||
bool swapChainAdequate = false;
|
||||
bool swapChainAdequate { false };
|
||||
if ( extensionsSupported )
|
||||
{
|
||||
SwapChainSupportDetails swapChainSupport = querySwapChainSupport( device );
|
||||
const SwapChainSupportDetails swapChainSupport { querySwapChainSupport( device ) };
|
||||
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures supportedFeatures;
|
||||
vkGetPhysicalDeviceFeatures( device, &supportedFeatures );
|
||||
vk::PhysicalDeviceFeatures supportedFeatures;
|
||||
device.getFeatures( &supportedFeatures );
|
||||
|
||||
std::cout << "Device: " << device.getProperties().deviceName << std::endl;
|
||||
std::cout << "\tgraphicsFamily: " << indices.graphicsFamily << std::endl;
|
||||
std::cout << "\tpresentFamily: " << indices.presentFamily << std::endl;
|
||||
std::cout << "\textensionsSupported: " << extensionsSupported << std::endl;
|
||||
std::cout << "\tswapChainAdequate: " << swapChainAdequate << std::endl;
|
||||
std::cout << "\tsamplerAnisotropy: " << supportedFeatures.samplerAnisotropy << std::endl;
|
||||
|
||||
return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
|
||||
}
|
||||
|
||||
void Device::populateDebugMessengerCreateInfo( VkDebugUtilsMessengerCreateInfoEXT& createInfo )
|
||||
void Device::populateDebugMessengerCreateInfo( vk::DebugUtilsMessengerCreateInfoEXT& createInfo )
|
||||
{
|
||||
createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
createInfo.messageSeverity =
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
|
||||
createInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
|
||||
| vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation
|
||||
| vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance;
|
||||
createInfo.pfnUserCallback = debugCallback;
|
||||
createInfo.pUserData = nullptr; // Optional
|
||||
}
|
||||
@@ -258,9 +301,30 @@ namespace fgl::engine
|
||||
void Device::setupDebugMessenger()
|
||||
{
|
||||
if ( !enableValidationLayers ) return;
|
||||
VkDebugUtilsMessengerCreateInfoEXT createInfo;
|
||||
|
||||
pfnVkCreateDebugUtilsMessengerEXT = reinterpret_cast<
|
||||
PFN_vkCreateDebugUtilsMessengerEXT >( m_instance.getProcAddr( "vkCreateDebugUtilsMessengerEXT" ) );
|
||||
pfnVkDestroyDebugUtilsMessengerEXT = reinterpret_cast<
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT >( m_instance.getProcAddr( "vkDestroyDebugUtilsMessengerEXT" ) );
|
||||
pfnVkSetDebugUtilsObjectNameEXT =
|
||||
reinterpret_cast< PFN_vkSetDebugUtilsObjectNameEXT >( m_instance
|
||||
.getProcAddr( "vkSetDebugUtilsObjectNameEXT" ) );
|
||||
|
||||
if ( !pfnVkCreateDebugUtilsMessengerEXT || !pfnVkDestroyDebugUtilsMessengerEXT )
|
||||
{
|
||||
throw std::runtime_error( "failed to load debug messenger functions!" );
|
||||
}
|
||||
|
||||
if ( !pfnVkSetDebugUtilsObjectNameEXT )
|
||||
{
|
||||
throw std::runtime_error( "failed to load debug object name function!" );
|
||||
}
|
||||
|
||||
vk::DebugUtilsMessengerCreateInfoEXT createInfo {};
|
||||
populateDebugMessengerCreateInfo( createInfo );
|
||||
if ( CreateDebugUtilsMessengerEXT( m_instance, &createInfo, nullptr, &m_debugMessenger ) != VK_SUCCESS )
|
||||
|
||||
if ( CreateDebugUtilsMessengerEXT( m_instance, createInfo, nullptr, &m_debugMessenger )
|
||||
!= vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to set up debug messenger!" );
|
||||
}
|
||||
@@ -268,11 +332,7 @@ namespace fgl::engine
|
||||
|
||||
bool Device::checkValidationLayerSupport()
|
||||
{
|
||||
uint32_t layerCount;
|
||||
vkEnumerateInstanceLayerProperties( &layerCount, nullptr );
|
||||
|
||||
std::vector< VkLayerProperties > availableLayers( layerCount );
|
||||
vkEnumerateInstanceLayerProperties( &layerCount, availableLayers.data() );
|
||||
std::vector< vk::LayerProperties > availableLayers { vk::enumerateInstanceLayerProperties() };
|
||||
|
||||
for ( const char* layerName : validationLayers )
|
||||
{
|
||||
@@ -314,10 +374,7 @@ namespace fgl::engine
|
||||
|
||||
void Device::hasGflwRequiredInstanceExtensions()
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties( nullptr, &extensionCount, nullptr );
|
||||
std::vector< VkExtensionProperties > extensions( extensionCount );
|
||||
vkEnumerateInstanceExtensionProperties( nullptr, &extensionCount, extensions.data() );
|
||||
std::vector< vk::ExtensionProperties > extensions { vk::enumerateInstanceExtensionProperties() };
|
||||
|
||||
std::cout << "available extensions:" << std::endl;
|
||||
std::unordered_set< std::string > available;
|
||||
@@ -329,53 +386,58 @@ namespace fgl::engine
|
||||
|
||||
std::cout << "required extensions:" << std::endl;
|
||||
auto requiredExtensions = getRequiredExtensions();
|
||||
for ( const auto& required : requiredExtensions )
|
||||
{
|
||||
std::cout << "\t" << required << std::endl;
|
||||
if ( available.find( required ) == available.end() )
|
||||
for ( const char* required : requiredExtensions )
|
||||
{
|
||||
if ( std::find_if(
|
||||
extensions.begin(),
|
||||
extensions.end(),
|
||||
[ required ]( const vk::ExtensionProperties& prop )
|
||||
{ return std::strcmp( prop.extensionName, required ); } )
|
||||
== extensions.end() )
|
||||
throw std::runtime_error( "Missing required glfw extension" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::checkDeviceExtensionSupport( VkPhysicalDevice device )
|
||||
bool Device::checkDeviceExtensionSupport( vk::PhysicalDevice device )
|
||||
{
|
||||
uint32_t extensionCount;
|
||||
vkEnumerateDeviceExtensionProperties( device, nullptr, &extensionCount, nullptr );
|
||||
|
||||
std::vector< VkExtensionProperties > availableExtensions( extensionCount );
|
||||
vkEnumerateDeviceExtensionProperties( device, nullptr, &extensionCount, availableExtensions.data() );
|
||||
const std::vector< vk::ExtensionProperties > availableExtensions {
|
||||
device.enumerateDeviceExtensionProperties()
|
||||
};
|
||||
|
||||
std::set< std::string > requiredExtensions( deviceExtensions.begin(), deviceExtensions.end() );
|
||||
|
||||
for ( const auto& extension : availableExtensions )
|
||||
std::uint32_t required_count { static_cast< std::uint32_t >( requiredExtensions.size() ) };
|
||||
std::uint32_t found_count { 0 };
|
||||
|
||||
for ( const auto required : deviceExtensions )
|
||||
{
|
||||
requiredExtensions.erase( extension.extensionName );
|
||||
if ( std::find_if(
|
||||
availableExtensions.begin(),
|
||||
availableExtensions.end(),
|
||||
[ required ]( const vk::ExtensionProperties& prop )
|
||||
{ return std::strcmp( prop.extensionName, required ); } )
|
||||
!= availableExtensions.end() )
|
||||
found_count++;
|
||||
}
|
||||
|
||||
return requiredExtensions.empty();
|
||||
return found_count == required_count;
|
||||
}
|
||||
|
||||
QueueFamilyIndices Device::findQueueFamilies( VkPhysicalDevice device )
|
||||
QueueFamilyIndices Device::findQueueFamilies( vk::PhysicalDevice device )
|
||||
{
|
||||
QueueFamilyIndices indices;
|
||||
QueueFamilyIndices indices {};
|
||||
|
||||
uint32_t queueFamilyCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties( device, &queueFamilyCount, nullptr );
|
||||
std::vector< vk::QueueFamilyProperties > queueFamilies { device.getQueueFamilyProperties() };
|
||||
|
||||
std::vector< VkQueueFamilyProperties > queueFamilies( queueFamilyCount );
|
||||
vkGetPhysicalDeviceQueueFamilyProperties( device, &queueFamilyCount, queueFamilies.data() );
|
||||
|
||||
int i = 0;
|
||||
int i { 0 };
|
||||
for ( const auto& queueFamily : queueFamilies )
|
||||
{
|
||||
if ( queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT )
|
||||
if ( queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics )
|
||||
{
|
||||
indices.graphicsFamily = i;
|
||||
indices.graphicsFamilyHasValue = true;
|
||||
}
|
||||
VkBool32 presentSupport = false;
|
||||
vk::Bool32 presentSupport { VK_FALSE };
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR( device, i, surface_, &presentSupport );
|
||||
if ( queueFamily.queueCount > 0 && presentSupport )
|
||||
{
|
||||
@@ -393,45 +455,51 @@ namespace fgl::engine
|
||||
return indices;
|
||||
}
|
||||
|
||||
SwapChainSupportDetails Device::querySwapChainSupport( VkPhysicalDevice device )
|
||||
SwapChainSupportDetails Device::querySwapChainSupport( vk::PhysicalDevice device )
|
||||
{
|
||||
SwapChainSupportDetails details;
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR( device, surface_, &details.capabilities );
|
||||
|
||||
uint32_t formatCount;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR( device, surface_, &formatCount, nullptr );
|
||||
if ( device.getSurfaceCapabilitiesKHR( surface_, &details.capabilities ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to get surface capabilities" );
|
||||
|
||||
uint32_t formatCount { 0 };
|
||||
if ( device.getSurfaceFormatsKHR( surface_, &formatCount, nullptr ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to get surface formats" );
|
||||
|
||||
if ( formatCount != 0 )
|
||||
{
|
||||
details.formats.resize( formatCount );
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR( device, surface_, &formatCount, details.formats.data() );
|
||||
if ( device.getSurfaceFormatsKHR( surface_, &formatCount, details.formats.data() ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to get surface formats" );
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR( device, surface_, &presentModeCount, nullptr );
|
||||
uint32_t presentModeCount { 0 };
|
||||
if ( device.getSurfacePresentModesKHR( surface_, &presentModeCount, nullptr ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to get surface present modes" );
|
||||
|
||||
if ( presentModeCount != 0 )
|
||||
{
|
||||
details.presentModes.resize( presentModeCount );
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
device, surface_, &presentModeCount, details.presentModes.data() );
|
||||
if ( device.getSurfacePresentModesKHR( surface_, &presentModeCount, details.presentModes.data() )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to get surface present modes" );
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
VkFormat Device::findSupportedFormat(
|
||||
const std::vector< VkFormat >& candidates, VkImageTiling tiling, VkFormatFeatureFlags features )
|
||||
vk::Format Device::findSupportedFormat(
|
||||
const std::vector< vk::Format >& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features )
|
||||
{
|
||||
for ( VkFormat format : candidates )
|
||||
for ( vk::Format format : candidates )
|
||||
{
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties( m_physical_device, format, &props );
|
||||
vk::FormatProperties props;
|
||||
m_physical_device.getFormatProperties( format, &props );
|
||||
|
||||
if ( tiling == VK_IMAGE_TILING_LINEAR && ( props.linearTilingFeatures & features ) == features )
|
||||
if ( tiling == vk::ImageTiling::eLinear && ( props.linearTilingFeatures & features ) == features )
|
||||
{
|
||||
return format;
|
||||
}
|
||||
else if ( tiling == VK_IMAGE_TILING_OPTIMAL && ( props.optimalTilingFeatures & features ) == features )
|
||||
else if ( tiling == vk::ImageTiling::eOptimal && ( props.optimalTilingFeatures & features ) == features )
|
||||
{
|
||||
return format;
|
||||
}
|
||||
@@ -439,10 +507,10 @@ namespace fgl::engine
|
||||
throw std::runtime_error( "failed to find supported format!" );
|
||||
}
|
||||
|
||||
uint32_t Device::findMemoryType( uint32_t typeFilter, VkMemoryPropertyFlags properties )
|
||||
uint32_t Device::findMemoryType( uint32_t typeFilter, vk::MemoryPropertyFlags properties )
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties memProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties( m_physical_device, &memProperties );
|
||||
vk::PhysicalDeviceMemoryProperties memProperties;
|
||||
m_physical_device.getMemoryProperties( &memProperties );
|
||||
for ( uint32_t i = 0; i < memProperties.memoryTypeCount; i++ )
|
||||
{
|
||||
if ( ( typeFilter & ( 1 << i ) )
|
||||
@@ -455,137 +523,103 @@ namespace fgl::engine
|
||||
throw std::runtime_error( "failed to find suitable memory type!" );
|
||||
}
|
||||
|
||||
void Device::createBuffer(
|
||||
VkDeviceSize size,
|
||||
VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkBuffer& buffer,
|
||||
VkDeviceMemory& bufferMemory )
|
||||
vk::CommandBuffer Device::beginSingleTimeCommands()
|
||||
{
|
||||
VkBufferCreateInfo bufferInfo {};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = size;
|
||||
bufferInfo.usage = usage;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
if ( vkCreateBuffer( device_, &bufferInfo, nullptr, &buffer ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create vertex buffer!" );
|
||||
}
|
||||
|
||||
VkMemoryRequirements memRequirements;
|
||||
vkGetBufferMemoryRequirements( device_, buffer, &memRequirements );
|
||||
|
||||
VkMemoryAllocateInfo allocInfo {};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memRequirements.size;
|
||||
allocInfo.memoryTypeIndex = findMemoryType( memRequirements.memoryTypeBits, properties );
|
||||
|
||||
if ( vkAllocateMemory( device_, &allocInfo, nullptr, &bufferMemory ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to allocate vertex buffer memory!" );
|
||||
}
|
||||
|
||||
vkBindBufferMemory( device_, buffer, bufferMemory, 0 );
|
||||
}
|
||||
|
||||
VkCommandBuffer Device::beginSingleTimeCommands()
|
||||
{
|
||||
VkCommandBufferAllocateInfo allocInfo {};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
vk::CommandBufferAllocateInfo allocInfo {};
|
||||
allocInfo.level = vk::CommandBufferLevel::ePrimary;
|
||||
allocInfo.commandPool = m_commandPool;
|
||||
allocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer commandBuffer;
|
||||
vkAllocateCommandBuffers( device_, &allocInfo, &commandBuffer );
|
||||
vk::CommandBuffer commandBuffer {};
|
||||
if ( device_.allocateCommandBuffers( &allocInfo, &commandBuffer ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to allocate command buffers!" );
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo {};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
vk::CommandBufferBeginInfo beginInfo {};
|
||||
beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
|
||||
commandBuffer.begin( beginInfo );
|
||||
|
||||
vkBeginCommandBuffer( commandBuffer, &beginInfo );
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void Device::endSingleTimeCommands( VkCommandBuffer commandBuffer )
|
||||
void Device::endSingleTimeCommands( vk::CommandBuffer commandBuffer )
|
||||
{
|
||||
vkEndCommandBuffer( commandBuffer );
|
||||
|
||||
VkSubmitInfo submitInfo {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
vk::SubmitInfo submitInfo {};
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
|
||||
vkQueueSubmit( graphicsQueue_, 1, &submitInfo, VK_NULL_HANDLE );
|
||||
vkQueueWaitIdle( graphicsQueue_ );
|
||||
if ( graphicsQueue_.submit( 1, &submitInfo, VK_NULL_HANDLE ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to submit single time command buffer!" );
|
||||
|
||||
vkFreeCommandBuffers( device_, m_commandPool, 1, &commandBuffer );
|
||||
}
|
||||
graphicsQueue_.waitIdle();
|
||||
|
||||
void Device::copyBuffer( VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size )
|
||||
{
|
||||
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
||||
|
||||
VkBufferCopy copyRegion {};
|
||||
copyRegion.srcOffset = 0; // Optional
|
||||
copyRegion.dstOffset = 0; // Optional
|
||||
copyRegion.size = size;
|
||||
vkCmdCopyBuffer( commandBuffer, srcBuffer, dstBuffer, 1, ©Region );
|
||||
|
||||
endSingleTimeCommands( commandBuffer );
|
||||
device_.freeCommandBuffers( m_commandPool, 1, &commandBuffer );
|
||||
}
|
||||
|
||||
void Device::
|
||||
copyBufferToImage( VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t layerCount )
|
||||
copyBufferToImage( vk::Buffer buffer, vk::Image image, uint32_t width, uint32_t height, uint32_t layerCount )
|
||||
{
|
||||
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
||||
vk::CommandBuffer commandBuffer { beginSingleTimeCommands() };
|
||||
|
||||
VkBufferImageCopy region {};
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = 0;
|
||||
region.bufferRowLength = 0;
|
||||
region.bufferImageHeight = 0;
|
||||
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
region.imageSubresource.mipLevel = 0;
|
||||
region.imageSubresource.baseArrayLayer = 0;
|
||||
region.imageSubresource.layerCount = layerCount;
|
||||
|
||||
region.imageOffset = { 0, 0, 0 };
|
||||
region.imageExtent = { width, height, 1 };
|
||||
region.imageOffset = vk::Offset3D { 0, 0, 0 };
|
||||
region.imageExtent = vk::Extent3D { width, height, 1 };
|
||||
|
||||
vkCmdCopyBufferToImage( commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion );
|
||||
commandBuffer.copyBufferToImage( buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, ®ion );
|
||||
endSingleTimeCommands( commandBuffer );
|
||||
}
|
||||
|
||||
void Device::createImageWithInfo(
|
||||
const VkImageCreateInfo& imageInfo,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkImage& image,
|
||||
VkDeviceMemory& imageMemory )
|
||||
void Device::createVMAAllocator()
|
||||
{
|
||||
if ( vkCreateImage( device_, &imageInfo, nullptr, &image ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create image!" );
|
||||
VmaVulkanFunctions vk_func {};
|
||||
vk_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
|
||||
vk_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
|
||||
|
||||
VmaAllocatorCreateInfo create_info {};
|
||||
create_info.physicalDevice = m_physical_device;
|
||||
create_info.device = device_;
|
||||
create_info.pVulkanFunctions = &vk_func;
|
||||
create_info.instance = m_instance;
|
||||
create_info.vulkanApiVersion = VK_API_VERSION_1_0;
|
||||
|
||||
if ( vmaCreateAllocator( &create_info, &m_allocator ) != VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to create VMA allocator" );
|
||||
}
|
||||
|
||||
VkMemoryRequirements memRequirements;
|
||||
vkGetImageMemoryRequirements( device_, image, &memRequirements );
|
||||
|
||||
VkMemoryAllocateInfo allocInfo {};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memRequirements.size;
|
||||
allocInfo.memoryTypeIndex = findMemoryType( memRequirements.memoryTypeBits, properties );
|
||||
|
||||
if ( vkAllocateMemory( device_, &allocInfo, nullptr, &imageMemory ) != VK_SUCCESS )
|
||||
void Device::copyBuffer(
|
||||
vk::Buffer dst, vk::Buffer src, vk::DeviceSize dst_offset, vk::DeviceSize src_offset, vk::DeviceSize size )
|
||||
{
|
||||
throw std::runtime_error( "failed to allocate image memory!" );
|
||||
vk::CommandBuffer commandBuffer { beginSingleTimeCommands() };
|
||||
|
||||
vk::BufferCopy copyRegion {};
|
||||
copyRegion.size = size;
|
||||
copyRegion.srcOffset = src_offset;
|
||||
copyRegion.dstOffset = dst_offset;
|
||||
|
||||
commandBuffer.copyBuffer( src, dst, 1, ©Region );
|
||||
|
||||
endSingleTimeCommands( commandBuffer );
|
||||
}
|
||||
|
||||
if ( vkBindImageMemory( device_, image, imageMemory, 0 ) != VK_SUCCESS )
|
||||
vk::Result Device::setDebugUtilsObjectName( const vk::DebugUtilsObjectNameInfoEXT& nameInfo )
|
||||
{
|
||||
throw std::runtime_error( "failed to bind image memory!" );
|
||||
if ( device().setDebugUtilsObjectNameEXT( &nameInfo ) != vk::Result::eSuccess )
|
||||
{
|
||||
std::cout << "Failed to set debug object name" << std::endl;
|
||||
throw std::runtime_error( "Failed to set debug object name" );
|
||||
}
|
||||
return vk::Result::eSuccess;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,63 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "Window.hpp"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vulkan/vulkan_handles.hpp>
|
||||
|
||||
// std lib headers
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Window.hpp"
|
||||
#include "engine/concepts/is_suballocation.hpp"
|
||||
#include "vma/vma_impl.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct SwapChainSupportDetails
|
||||
{
|
||||
VkSurfaceCapabilitiesKHR capabilities {};
|
||||
std::vector< VkSurfaceFormatKHR > formats {};
|
||||
std::vector< VkPresentModeKHR > presentModes {};
|
||||
vk::SurfaceCapabilitiesKHR capabilities {};
|
||||
std::vector< vk::SurfaceFormatKHR > formats {};
|
||||
std::vector< vk::PresentModeKHR > presentModes {};
|
||||
};
|
||||
|
||||
struct QueueFamilyIndices
|
||||
{
|
||||
uint32_t graphicsFamily;
|
||||
uint32_t presentFamily;
|
||||
bool graphicsFamilyHasValue = false;
|
||||
bool presentFamilyHasValue = false;
|
||||
uint32_t graphicsFamily { 0 };
|
||||
uint32_t presentFamily { 0 };
|
||||
bool graphicsFamilyHasValue { false };
|
||||
bool presentFamilyHasValue { false };
|
||||
|
||||
bool isComplete() { return graphicsFamilyHasValue && presentFamilyHasValue; }
|
||||
bool isComplete() const { return graphicsFamilyHasValue && presentFamilyHasValue; }
|
||||
};
|
||||
|
||||
class Device
|
||||
{
|
||||
VkInstance m_instance { nullptr };
|
||||
VkDebugUtilsMessengerEXT m_debugMessenger { nullptr };
|
||||
VkPhysicalDevice m_physical_device { VK_NULL_HANDLE };
|
||||
vk::Instance m_instance { VK_NULL_HANDLE };
|
||||
vk::DebugUtilsMessengerEXT m_debugMessenger { VK_NULL_HANDLE };
|
||||
vk::PhysicalDevice m_physical_device { VK_NULL_HANDLE };
|
||||
Window& m_window;
|
||||
VkCommandPool m_commandPool { nullptr };
|
||||
vk::CommandPool m_commandPool { VK_NULL_HANDLE };
|
||||
|
||||
VkDevice device_ { nullptr };
|
||||
VkSurfaceKHR surface_ { nullptr };
|
||||
VkQueue graphicsQueue_ { nullptr };
|
||||
VkQueue presentQueue_ { nullptr };
|
||||
VmaAllocator m_allocator { VK_NULL_HANDLE };
|
||||
|
||||
vk::Device device_ { VK_NULL_HANDLE };
|
||||
vk::SurfaceKHR surface_ { VK_NULL_HANDLE };
|
||||
vk::Queue graphicsQueue_ { VK_NULL_HANDLE };
|
||||
vk::Queue presentQueue_ { VK_NULL_HANDLE };
|
||||
|
||||
const std::vector< const char* > validationLayers { "VK_LAYER_KHRONOS_validation" };
|
||||
const std::vector< const char* > deviceExtensions { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
const std::vector< const char* > deviceExtensions { VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME };
|
||||
|
||||
void copyBuffer(
|
||||
vk::Buffer dst,
|
||||
vk::Buffer src,
|
||||
vk::DeviceSize dst_offset,
|
||||
vk::DeviceSize src_offset,
|
||||
vk::DeviceSize size = VK_WHOLE_SIZE );
|
||||
|
||||
public:
|
||||
|
||||
vk::PhysicalDeviceProperties m_properties {};
|
||||
|
||||
static Device& init( Window& window );
|
||||
static Device& getInstance();
|
||||
|
||||
template < typename Dst, typename Src >
|
||||
requires( is_buffer< Dst > || is_suballocation< Dst > ) && (is_buffer< Src > || is_suballocation< Src >)
|
||||
void copyBuffer(
|
||||
Dst& dst,
|
||||
Src& src,
|
||||
vk::DeviceSize dst_offset,
|
||||
vk::DeviceSize src_offset,
|
||||
vk::DeviceSize size = VK_WHOLE_SIZE )
|
||||
{
|
||||
copyBuffer( dst.getBuffer(), src.getBuffer(), dst_offset, src_offset, size );
|
||||
}
|
||||
|
||||
template < typename Dst, typename Src >
|
||||
requires is_suballocation< Dst > && is_suballocation< Src >
|
||||
void copyBuffer( Dst& dst, Src& src, vk::DeviceSize size )
|
||||
{
|
||||
copyBuffer( dst, src, dst.offset(), src.offset(), size );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void createInstance();
|
||||
void setupDebugMessenger();
|
||||
void createSurface();
|
||||
void pickPhysicalDevice();
|
||||
void createLogicalDevice();
|
||||
void createVMAAllocator();
|
||||
void createCommandPool();
|
||||
|
||||
// helper functions
|
||||
bool isDeviceSuitable( VkPhysicalDevice device );
|
||||
bool isDeviceSuitable( vk::PhysicalDevice device );
|
||||
std::vector< const char* > getRequiredExtensions();
|
||||
bool checkValidationLayerSupport();
|
||||
QueueFamilyIndices findQueueFamilies( VkPhysicalDevice device );
|
||||
void populateDebugMessengerCreateInfo( VkDebugUtilsMessengerCreateInfoEXT& createInfo );
|
||||
QueueFamilyIndices findQueueFamilies( vk::PhysicalDevice device );
|
||||
void populateDebugMessengerCreateInfo( vk::DebugUtilsMessengerCreateInfoEXT& createInfo );
|
||||
void hasGflwRequiredInstanceExtensions();
|
||||
bool checkDeviceExtensionSupport( VkPhysicalDevice device );
|
||||
SwapChainSupportDetails querySwapChainSupport( VkPhysicalDevice device );
|
||||
bool checkDeviceExtensionSupport( vk::PhysicalDevice device );
|
||||
SwapChainSupportDetails querySwapChainSupport( vk::PhysicalDevice device );
|
||||
|
||||
void initImGui();
|
||||
|
||||
public:
|
||||
|
||||
@@ -67,57 +112,51 @@ namespace fgl::engine
|
||||
const bool enableValidationLayers = true;
|
||||
#endif
|
||||
|
||||
vk::Result setDebugUtilsObjectName( const vk::DebugUtilsObjectNameInfoEXT& nameInfo );
|
||||
|
||||
private:
|
||||
|
||||
Device( Window& window );
|
||||
~Device();
|
||||
|
||||
public:
|
||||
|
||||
// Not copyable or movable
|
||||
Device( const Device& ) = delete;
|
||||
Device& operator=( const Device& ) = delete;
|
||||
Device( Device&& ) = delete;
|
||||
Device& operator=( Device&& ) = delete;
|
||||
|
||||
VkCommandPool getCommandPool() { return m_commandPool; }
|
||||
vk::CommandPool getCommandPool() { return m_commandPool; }
|
||||
|
||||
VkDevice device() { return device_; }
|
||||
vk::Device device() { return device_; }
|
||||
|
||||
VkInstance instance() { return m_instance; }
|
||||
vk::Instance instance() { return m_instance; }
|
||||
|
||||
VkPhysicalDevice phyDevice() { return m_physical_device; }
|
||||
vk::PhysicalDevice phyDevice() { return m_physical_device; }
|
||||
|
||||
VkSurfaceKHR surface() { return surface_; }
|
||||
vk::SurfaceKHR surface() { return surface_; }
|
||||
|
||||
VkQueue graphicsQueue() { return graphicsQueue_; }
|
||||
vk::Queue graphicsQueue() { return graphicsQueue_; }
|
||||
|
||||
VkQueue presentQueue() { return presentQueue_; }
|
||||
vk::Queue presentQueue() { return presentQueue_; }
|
||||
|
||||
VmaAllocator allocator() { return m_allocator; }
|
||||
|
||||
SwapChainSupportDetails getSwapChainSupport() { return querySwapChainSupport( m_physical_device ); }
|
||||
|
||||
uint32_t findMemoryType( uint32_t typeFilter, VkMemoryPropertyFlags properties );
|
||||
uint32_t findMemoryType( uint32_t typeFilter, vk::MemoryPropertyFlags properties );
|
||||
|
||||
QueueFamilyIndices findPhysicalQueueFamilies() { return findQueueFamilies( m_physical_device ); }
|
||||
|
||||
VkFormat findSupportedFormat(
|
||||
const std::vector< VkFormat >& candidates, VkImageTiling tiling, VkFormatFeatureFlags features );
|
||||
vk::Format findSupportedFormat(
|
||||
const std::vector< vk::Format >& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features );
|
||||
|
||||
// Buffer Helper Functions
|
||||
void createBuffer(
|
||||
VkDeviceSize size,
|
||||
VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkBuffer& buffer,
|
||||
VkDeviceMemory& bufferMemory );
|
||||
VkCommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands( VkCommandBuffer commandBuffer );
|
||||
void copyBuffer( VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size );
|
||||
void copyBufferToImage( VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t layerCount );
|
||||
vk::CommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands( vk::CommandBuffer commandBuffer );
|
||||
|
||||
void createImageWithInfo(
|
||||
const VkImageCreateInfo& imageInfo,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkImage& image,
|
||||
VkDeviceMemory& imageMemory );
|
||||
|
||||
VkPhysicalDeviceProperties m_properties;
|
||||
void copyBufferToImage(
|
||||
vk::Buffer buffer, vk::Image image, uint32_t width, uint32_t height, uint32_t layerCount );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "EngineContext.hpp"
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ON
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
@@ -13,21 +13,107 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "RenderSystem.hpp"
|
||||
#include "createCube.cpp"
|
||||
#include "engine/debug_gui/DebugGUI.hpp"
|
||||
#include "KeyboardMovementController.hpp"
|
||||
#include "engine/buffers/UniqueFrameSuballocation.hpp"
|
||||
#include "engine/descriptors/Descriptor.hpp"
|
||||
#include "engine/descriptors/DescriptorPool.hpp"
|
||||
#include "engine/literals/size.hpp"
|
||||
#include "engine/pipeline/PipelineT.hpp"
|
||||
#include "engine/systems/EntityRendererSystem.hpp"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_impl_glfw.h"
|
||||
#include "imgui/imgui_impl_vulkan.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
constexpr float MAX_DELTA_TIME { 0.5 };
|
||||
|
||||
EngineContext::EngineContext()
|
||||
{
|
||||
using namespace fgl::literals::size_literals;
|
||||
initGlobalStagingBuffer( 128_MiB );
|
||||
loadGameObjects();
|
||||
#ifdef ENABLE_IMGUI
|
||||
initImGui();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EngineContext::run()
|
||||
{
|
||||
RenderSystem render_system { m_device, m_renderer.getSwapChainRenderPass() };
|
||||
using namespace fgl::literals::size_literals;
|
||||
Buffer global_ubo_buffer { 512_KiB,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible }; // 512 KB
|
||||
|
||||
PerFrameSuballocation< HostSingleT< CameraInfo > > camera_info { global_ubo_buffer,
|
||||
SwapChain::MAX_FRAMES_IN_FLIGHT };
|
||||
|
||||
PerFrameSuballocation< HostSingleT< PointLight > > point_lights { global_ubo_buffer,
|
||||
SwapChain::MAX_FRAMES_IN_FLIGHT };
|
||||
|
||||
constexpr std::uint32_t matrix_default_size { 16_MiB };
|
||||
constexpr std::uint32_t draw_parameter_default_size { 16_MiB };
|
||||
|
||||
std::array< Buffer, SwapChain::MAX_FRAMES_IN_FLIGHT > matrix_info_buffers {
|
||||
{ { matrix_default_size,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible },
|
||||
{ matrix_default_size,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible } }
|
||||
// Should be in the BAR area
|
||||
};
|
||||
|
||||
std::array< Buffer, SwapChain::MAX_FRAMES_IN_FLIGHT > draw_parameter_buffers {
|
||||
{ { draw_parameter_default_size,
|
||||
vk::BufferUsageFlagBits::eIndirectBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible },
|
||||
{ draw_parameter_default_size,
|
||||
vk::BufferUsageFlagBits::eIndirectBuffer,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible } }
|
||||
};
|
||||
|
||||
std::array< DescriptorSet, SwapChain::MAX_FRAMES_IN_FLIGHT > global_descriptor_sets {
|
||||
{ { GlobalDescriptorSet::createLayout() }, { GlobalDescriptorSet::createLayout() } }
|
||||
};
|
||||
|
||||
for ( std::uint8_t i = 0; i < SwapChain::MAX_FRAMES_IN_FLIGHT; ++i )
|
||||
{
|
||||
global_descriptor_sets[ i ].setMaxIDX( 2 );
|
||||
global_descriptor_sets[ i ].bindUniformBuffer( 0, camera_info[ i ] );
|
||||
global_descriptor_sets[ i ].bindUniformBuffer( 2, point_lights[ i ] );
|
||||
global_descriptor_sets[ i ].update();
|
||||
}
|
||||
|
||||
Camera camera {};
|
||||
|
||||
auto viewer { GameObject::createGameObject() };
|
||||
viewer.transform.translation.z = -2.5f;
|
||||
KeyboardMovementController camera_controller {};
|
||||
|
||||
auto current_time { std::chrono::high_resolution_clock::now() };
|
||||
|
||||
while ( !m_window.shouldClose() )
|
||||
{
|
||||
glfwPollEvents();
|
||||
|
||||
const auto new_time { std::chrono::high_resolution_clock::now() };
|
||||
auto delta_time {
|
||||
std::chrono::duration< float, std::chrono::seconds::period >( new_time - current_time ).count()
|
||||
};
|
||||
current_time = new_time;
|
||||
delta_time = glm::min( delta_time, MAX_DELTA_TIME );
|
||||
|
||||
camera_controller.moveInPlaneXZ( m_window.window(), delta_time, viewer );
|
||||
camera.setViewYXZ( viewer.transform.translation, viewer.transform.rotation );
|
||||
|
||||
const float aspect { m_renderer.getAspectRatio() };
|
||||
|
||||
//camera.setOrthographicProjection( -aspect, aspect, -1, 1, -1, 1 );
|
||||
@@ -35,38 +121,238 @@ namespace fgl::engine
|
||||
|
||||
if ( auto command_buffer = m_renderer.beginFrame(); command_buffer )
|
||||
{
|
||||
//Update
|
||||
const std::uint8_t frame_index { m_renderer.getFrameIndex() };
|
||||
|
||||
FrameInfo frame_info { frame_index,
|
||||
delta_time,
|
||||
command_buffer,
|
||||
camera,
|
||||
global_descriptor_sets[ frame_index ],
|
||||
game_objects,
|
||||
m_renderer.getCurrentTracyCTX(),
|
||||
matrix_info_buffers[ frame_index ],
|
||||
draw_parameter_buffers[ frame_index ],
|
||||
m_renderer.getGBufferDescriptor( frame_index ) };
|
||||
|
||||
CameraInfo current_camera_info { .projection = camera.getProjectionMatrix(),
|
||||
.view = camera.getViewMatrix(),
|
||||
.inverse_view = camera.getInverseView() };
|
||||
|
||||
camera_info[ frame_index ] = current_camera_info;
|
||||
|
||||
#ifdef ENABLE_IMGUI
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
{
|
||||
ImGui::Begin( "Titor Dev Menu" );
|
||||
|
||||
ImGui::Text( "Framerate" );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%.1f FPS", ImGui::GetIO().Framerate );
|
||||
|
||||
ImGui::Text( "Frame Time" );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "%.3f ms", 1000.0f / ImGui::GetIO().Framerate );
|
||||
|
||||
if ( ImGui::CollapsingHeader( "Camera" ) )
|
||||
{
|
||||
ImGui::PushItemWidth( 80 );
|
||||
ImGui::Text( "Position: " );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Pos X", &viewer.transform.translation.x, 0.1f );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Pos Y", &viewer.transform.translation.y, 0.1f );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Pos Z", &viewer.transform.translation.z, 0.1f );
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushItemWidth( 80 );
|
||||
ImGui::Text( "Rotation: " );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Rot X", &viewer.transform.rotation.x, 0.1f, 0.0f, glm::two_pi< float >() );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Rot Y", &viewer.transform.rotation.y, 0.1f, 0.0f, glm::two_pi< float >() );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Rot Z", &viewer.transform.rotation.z, 0.1f, 0.0f, glm::two_pi< float >() );
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
//Render
|
||||
ImGui::Render();
|
||||
ImDrawData* data { ImGui::GetDrawData() };
|
||||
#endif
|
||||
|
||||
m_renderer.beginSwapchainRendererPass( command_buffer );
|
||||
|
||||
render_system.renderGameObjects( command_buffer, game_objects, camera );
|
||||
m_entity_renderer.pass( frame_info );
|
||||
|
||||
#ifdef ENABLE_IMGUI
|
||||
ImGui_ImplVulkan_RenderDrawData( data, command_buffer );
|
||||
#endif
|
||||
|
||||
m_renderer.endSwapchainRendererPass( command_buffer );
|
||||
m_renderer.endFrame();
|
||||
FrameMark;
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for( 16.66ms );
|
||||
//std::this_thread::sleep_until( new_time + 12ms );
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle( m_device.device() );
|
||||
Device::getInstance().device().waitIdle();
|
||||
}
|
||||
|
||||
void EngineContext::loadGameObjects()
|
||||
{
|
||||
std::shared_ptr< Model > model { createCubeModel( m_device, { 0.0f, 0.0f, 0.0f } ) };
|
||||
auto command_buffer { Device::getInstance().beginSingleTimeCommands() };
|
||||
|
||||
auto cube = GameObject::createGameObject();
|
||||
cube.model = model;
|
||||
cube.transform.translation = { 0.0f, 0.0f, 2.0f };
|
||||
cube.transform.scale = { 0.5f, 0.5f, 0.5f };
|
||||
{
|
||||
std::shared_ptr< Model > model { Model::createModel(
|
||||
Device::getInstance(),
|
||||
"models/night_heron8.obj",
|
||||
m_entity_renderer.getVertexBuffer(),
|
||||
m_entity_renderer.getIndexBuffer() ) };
|
||||
|
||||
game_objects.push_back( std::move( cube ) );
|
||||
for ( int i = 0; i < 16; ++i )
|
||||
{
|
||||
auto night_heron = GameObject::createGameObject();
|
||||
night_heron.model = model;
|
||||
night_heron.transform.translation = { -5.0f + ( i * 1.0 ), 0.5f, 0.0f };
|
||||
night_heron.transform.scale = { 0.05f, 0.05f, 0.05f };
|
||||
night_heron.transform.rotation = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
night_heron.model->syncBuffers( command_buffer );
|
||||
|
||||
game_objects.emplace( night_heron.getId(), std::move( night_heron ) );
|
||||
}
|
||||
}
|
||||
/*
|
||||
{
|
||||
std::shared_ptr< Model > model { Model::createModel(
|
||||
Device::getInstance(),
|
||||
"models/smooth_vase.obj",
|
||||
m_entity_renderer.getVertexBuffer(),
|
||||
m_entity_renderer.getIndexBuffer() ) };
|
||||
|
||||
auto smooth_vase = GameObject::createGameObject();
|
||||
smooth_vase.model = model;
|
||||
|
||||
smooth_vase.transform.translation = { -0.5f, 0.5f, .0f };
|
||||
smooth_vase.transform.scale = { 3.0f, 1.5f, 3.0f };
|
||||
|
||||
smooth_vase.model->syncBuffers( command_buffer );
|
||||
|
||||
game_objects.emplace( smooth_vase.getId(), std::move( smooth_vase ) );
|
||||
}
|
||||
{
|
||||
std::shared_ptr< Model > flat_model { Model::createModel(
|
||||
Device::getInstance(),
|
||||
"models/flat_vase.obj",
|
||||
m_entity_renderer.getVertexBuffer(),
|
||||
m_entity_renderer.getIndexBuffer() ) };
|
||||
|
||||
auto flat_vase = GameObject::createGameObject();
|
||||
flat_vase.model = flat_model;
|
||||
//flat_vase.transform.translation = { 0.5f, 0.5f, 0.0f };
|
||||
flat_vase.transform.translation = { 0.5f, 0.5f, 0.0f };
|
||||
flat_vase.transform.scale = { 3.0f, 1.5f, 3.0f };
|
||||
|
||||
flat_vase.model->syncBuffers( command_buffer );
|
||||
|
||||
game_objects.emplace( flat_vase.getId(), std::move( flat_vase ) );
|
||||
}
|
||||
{
|
||||
std::shared_ptr< Model > quad_model { Model::createModel(
|
||||
Device::getInstance(),
|
||||
"models/quad.obj",
|
||||
m_entity_renderer.getVertexBuffer(),
|
||||
m_entity_renderer.getIndexBuffer() ) };
|
||||
|
||||
auto floor = GameObject::createGameObject();
|
||||
floor.model = quad_model;
|
||||
floor.transform.translation = { 0.0f, 0.5f, 0.0f };
|
||||
floor.transform.scale = { 3.0f, 1.0f, 3.0f };
|
||||
floor.is_world = true;
|
||||
|
||||
floor.model->syncBuffers( command_buffer );
|
||||
|
||||
game_objects.emplace( floor.getId(), std::move( floor ) );
|
||||
}
|
||||
*/
|
||||
|
||||
Device::getInstance().endSingleTimeCommands( command_buffer );
|
||||
|
||||
std::vector< glm::vec3 > lightColors {
|
||||
{ 1.f, .1f, .1f }, { .1f, .1f, 1.f }, { .1f, 1.f, .1f },
|
||||
{ 1.f, 1.f, .1f }, { .1f, 1.f, 1.f }, { 1.f, 1.f, 1.f } //
|
||||
};
|
||||
|
||||
for ( std::size_t i = 0; i < lightColors.size(); ++i )
|
||||
{
|
||||
auto point_light { GameObject::makePointLight( 0.2f ) };
|
||||
point_light.color = lightColors[ i ];
|
||||
auto rotate_light = glm::rotate(
|
||||
glm::mat4( 1.0f ),
|
||||
( static_cast< float >( i ) * glm::two_pi< float >() ) / static_cast< float >( lightColors.size() ),
|
||||
{ 0.0f, -1.0f, 0.0f } );
|
||||
point_light.transform.translation = glm::vec3( rotate_light * glm::vec4( -1.0f, -1.0f, -1.0f, -1.0f ) );
|
||||
game_objects.emplace( point_light.getId(), std::move( point_light ) );
|
||||
}
|
||||
}
|
||||
|
||||
EngineContext::EngineContext()
|
||||
void EngineContext::initImGui()
|
||||
{
|
||||
loadGameObjects();
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io { ImGui::GetIO() };
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
debug::initDebugGUI();
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
auto& device { Device::getInstance() };
|
||||
|
||||
ImGui_ImplGlfw_InitForVulkan( m_window.window(), true );
|
||||
ImGui_ImplVulkan_InitInfo init_info {
|
||||
.Instance = device.instance(),
|
||||
.PhysicalDevice = device.phyDevice(),
|
||||
.Device = device.device(),
|
||||
.QueueFamily = device.findPhysicalQueueFamilies().graphicsFamily,
|
||||
.Queue = device.graphicsQueue(),
|
||||
.PipelineCache = VK_NULL_HANDLE,
|
||||
.DescriptorPool = DescriptorPool::getInstance().getVkPool(),
|
||||
.Subpass = 1,
|
||||
.MinImageCount = 2,
|
||||
.ImageCount = 2,
|
||||
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.UseDynamicRendering = VK_FALSE,
|
||||
.ColorAttachmentFormat = {},
|
||||
.Allocator = VK_NULL_HANDLE,
|
||||
.CheckVkResultFn = VK_NULL_HANDLE,
|
||||
};
|
||||
|
||||
ImGui_ImplVulkan_Init( &init_info, m_renderer.getSwapChainRenderPass() );
|
||||
}
|
||||
|
||||
EngineContext::~EngineContext()
|
||||
{
|
||||
#ifdef ENABLE_IMGUI
|
||||
cleanupImGui();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EngineContext::cleanupImGui()
|
||||
{
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -12,26 +12,33 @@
|
||||
#include "Model.hpp"
|
||||
#include "Renderer.hpp"
|
||||
#include "Window.hpp"
|
||||
#include "engine/descriptors/DescriptorPool.hpp"
|
||||
#include "engine/systems/EntityRendererSystem.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class EngineContext
|
||||
{
|
||||
static constexpr int DEFAULT_WIDTH { 800 };
|
||||
static constexpr int DEFAULT_HEIGHT { 600 };
|
||||
static constexpr int DEFAULT_WIDTH { 1920 };
|
||||
static constexpr int DEFAULT_HEIGHT { 1080 };
|
||||
|
||||
Window m_window { DEFAULT_WIDTH, DEFAULT_HEIGHT, "titor Engine" };
|
||||
Device m_device { m_window };
|
||||
Renderer m_renderer { m_window, m_device };
|
||||
Renderer m_renderer { m_window };
|
||||
|
||||
std::vector< GameObject > game_objects;
|
||||
GameObject::Map game_objects {};
|
||||
|
||||
EntityRendererSystem m_entity_renderer { Device::getInstance(), m_renderer.getSwapChainRenderPass() };
|
||||
|
||||
void loadGameObjects();
|
||||
|
||||
void initImGui();
|
||||
void cleanupImGui();
|
||||
|
||||
public:
|
||||
|
||||
EngineContext();
|
||||
~EngineContext();
|
||||
EngineContext( EngineContext&& other ) = delete;
|
||||
EngineContext( const EngineContext& other ) = delete;
|
||||
EngineContext& operator=( const EngineContext& other ) = delete;
|
||||
|
||||
75
src/engine/FrameInfo.hpp
Normal file
75
src/engine/FrameInfo.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by kj16609 on 11/30/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
//clang-format: off
|
||||
#include <tracy/TracyVulkan.hpp>
|
||||
//clang-format: on
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "GameObject.hpp"
|
||||
#include "PushConstant.hpp"
|
||||
#include "engine/descriptors/Descriptor.hpp"
|
||||
#include "engine/descriptors/DescriptorSet.hpp"
|
||||
#include "engine/descriptors/DescriptorSetLayout.hpp"
|
||||
|
||||
#define MAX_LIGHTS 10
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
glm::vec4 position {}; //ignore w
|
||||
glm::vec4 color {};
|
||||
};
|
||||
|
||||
struct CameraInfo
|
||||
{
|
||||
glm::mat4 projection { 1.0f };
|
||||
glm::mat4 view { 1.0f };
|
||||
glm::mat4 inverse_view { 1.0f };
|
||||
glm::vec4 ambient_light_color { 1.0f, 1.0f, 1.0f, 0.02f };
|
||||
};
|
||||
|
||||
struct PointLightUBO
|
||||
{
|
||||
alignas( 16 ) PointLight point_lights[ MAX_LIGHTS ] {};
|
||||
alignas( 16 ) int num_lights { 0 };
|
||||
};
|
||||
|
||||
using CameraDescriptor = Descriptor< 0, vk::DescriptorType::eUniformBuffer, vk::ShaderStageFlagBits::eAllGraphics >;
|
||||
using LightDescriptor = Descriptor< 2, vk::DescriptorType::eUniformBuffer, vk::ShaderStageFlagBits::eAllGraphics >;
|
||||
|
||||
using GlobalDescriptorSet = DescriptorSetLayout< 0, CameraDescriptor, EmptyDescriptor< 1 >, LightDescriptor >;
|
||||
|
||||
using PositionDescriptor = AttachmentDescriptor< 0, vk::ShaderStageFlagBits::eFragment >;
|
||||
using NormalDescriptor = AttachmentDescriptor< 1, vk::ShaderStageFlagBits::eFragment >;
|
||||
using AlbedoDescriptor = AttachmentDescriptor< 2, vk::ShaderStageFlagBits::eFragment >;
|
||||
|
||||
static_assert( is_descriptor< PositionDescriptor > );
|
||||
|
||||
using GBufferDescriptorSet = DescriptorSetLayout< 0, PositionDescriptor, NormalDescriptor, AlbedoDescriptor >;
|
||||
|
||||
struct FrameInfo
|
||||
{
|
||||
int frame_idx;
|
||||
float frame_time;
|
||||
vk::CommandBuffer command_buffer;
|
||||
Camera& camera;
|
||||
DescriptorSet& global_descriptor_set;
|
||||
GameObject::Map& game_objects;
|
||||
TracyVkCtx tracy_ctx;
|
||||
|
||||
//Buffers
|
||||
Buffer& model_matrix_info_buffer;
|
||||
Buffer& draw_parameter_buffer;
|
||||
|
||||
DescriptorSet& gbuffer_descriptor_set;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
64
src/engine/GameObject.cpp
Normal file
64
src/engine/GameObject.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by kj16609 on 11/30/23.
|
||||
//
|
||||
|
||||
#include "GameObject.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
glm::mat4 TransformComponent::mat4() const
|
||||
{
|
||||
const float c3 { glm::cos( rotation.z ) };
|
||||
const float s3 { glm::sin( rotation.z ) };
|
||||
const float c2 { glm::cos( rotation.x ) };
|
||||
const float s2 { glm::sin( rotation.x ) };
|
||||
const float c1 { glm::cos( rotation.y ) };
|
||||
const float s1 { glm::sin( rotation.y ) };
|
||||
|
||||
return glm::mat4 {
|
||||
{ scale.x * ( c1 * c3 + s1 * s2 * s3 ), scale.x * ( c2 * s3 ), scale.x * ( c1 * s2 * s3 - c3 * s1 ), 0.0f },
|
||||
{ scale.y * ( c3 * s1 * s2 - c1 * s3 ), scale.y * ( c2 * c3 ), scale.y * ( c1 * c3 * s2 + s1 * s3 ), 0.0f },
|
||||
{ scale.z * ( c2 * s1 ), scale.z * ( -s2 ), scale.z * ( c1 * c2 ), 0.0f },
|
||||
{ translation.x, translation.y, translation.z, 1.0f }
|
||||
};
|
||||
}
|
||||
|
||||
glm::mat3 TransformComponent::normalMatrix() const
|
||||
{
|
||||
const float c3 { glm::cos( rotation.z ) };
|
||||
const float s3 { glm::sin( rotation.z ) };
|
||||
const float c2 { glm::cos( rotation.x ) };
|
||||
const float s2 { glm::sin( rotation.x ) };
|
||||
const float c1 { glm::cos( rotation.y ) };
|
||||
const float s1 { glm::sin( rotation.y ) };
|
||||
const glm::vec3 invScale { 1.0f / scale };
|
||||
|
||||
return glm::mat3 {
|
||||
{
|
||||
invScale.x * ( c1 * c3 + s1 * s2 * s3 ),
|
||||
invScale.x * ( c2 * s3 ),
|
||||
invScale.x * ( c1 * s2 * s3 - c3 * s1 ),
|
||||
},
|
||||
{
|
||||
invScale.y * ( c3 * s1 * s2 - c1 * s3 ),
|
||||
invScale.y * ( c2 * c3 ),
|
||||
invScale.y * ( c1 * c3 * s2 + s1 * s3 ),
|
||||
},
|
||||
{
|
||||
invScale.z * ( c2 * s1 ),
|
||||
invScale.z * ( -s2 ),
|
||||
invScale.z * ( c1 * c2 ),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
GameObject GameObject::makePointLight( float intensity, float radius, glm::vec3 color )
|
||||
{
|
||||
GameObject game_obj { createGameObject() };
|
||||
game_obj.color = color;
|
||||
game_obj.transform.scale.x = radius;
|
||||
game_obj.point_light = std::make_unique< PointLightComponent >();
|
||||
game_obj.point_light->light_intensity = intensity;
|
||||
return game_obj;
|
||||
}
|
||||
} // namespace fgl::engine
|
||||
@@ -4,9 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Model.hpp"
|
||||
|
||||
@@ -15,30 +18,18 @@ namespace fgl::engine
|
||||
|
||||
struct TransformComponent
|
||||
{
|
||||
glm::vec3 translation {};
|
||||
glm::vec3 translation { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec3 scale { 1.0f, 1.0f, 1.0f };
|
||||
glm::vec3 rotation {};
|
||||
glm::vec3 rotation { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
glm::mat4 mat4() const
|
||||
glm::mat4 mat4() const;
|
||||
|
||||
glm::mat3 normalMatrix() const;
|
||||
};
|
||||
|
||||
struct PointLightComponent
|
||||
{
|
||||
const float c3 { glm::cos( rotation.z ) };
|
||||
const float s3 { glm::sin( rotation.z ) };
|
||||
const float c2 { glm::cos( rotation.x ) };
|
||||
const float s2 { glm::sin( rotation.x ) };
|
||||
const float c1 { glm::cos( rotation.y ) };
|
||||
const float s1 { glm::sin( rotation.y ) };
|
||||
|
||||
return glm::mat4 { { scale.x * ( c1 * c3 + s1 * s2 * s3 ),
|
||||
scale.x * ( c2 * s3 ),
|
||||
scale.x * ( c1 * s2 * s3 - c3 * s1 ),
|
||||
0.0f },
|
||||
{ scale.y * ( c3 * s1 * s2 - c1 * s3 ),
|
||||
scale.y * ( c2 * c3 ),
|
||||
scale.y * ( c1 * c3 * s2 + s1 * s3 ),
|
||||
0.0f },
|
||||
{ scale.z * ( c2 * s1 ), scale.z * ( -s2 ), scale.z * ( c1 * c2 ), 0.0f },
|
||||
{ translation.x, translation.y, translation.z, 1.0f } };
|
||||
}
|
||||
float light_intensity { 1.0f };
|
||||
};
|
||||
|
||||
class GameObject
|
||||
@@ -46,12 +37,15 @@ namespace fgl::engine
|
||||
public:
|
||||
|
||||
using ID = unsigned int;
|
||||
using Map = std::unordered_map< ID, GameObject >;
|
||||
|
||||
ID m_id;
|
||||
bool is_world { false };
|
||||
|
||||
std::shared_ptr< Model > model {};
|
||||
glm::vec3 color {};
|
||||
TransformComponent transform {};
|
||||
std::unique_ptr< PointLightComponent > point_light { nullptr };
|
||||
|
||||
private:
|
||||
|
||||
@@ -71,6 +65,9 @@ namespace fgl::engine
|
||||
return { current_id++ };
|
||||
}
|
||||
|
||||
static GameObject
|
||||
makePointLight( float intensity = 10.0f, float radius = 0.1f, glm::vec3 color = glm::vec3( 1.0f ) );
|
||||
|
||||
ID getId() const { return m_id; }
|
||||
};
|
||||
|
||||
|
||||
80
src/engine/KeyboardMovementController.cpp
Normal file
80
src/engine/KeyboardMovementController.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// Created by kj16609 on 11/28/23.
|
||||
//
|
||||
|
||||
#include "KeyboardMovementController.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
void KeyboardMovementController::moveInPlaneXZ( GLFWwindow* window, float dt, fgl::engine::GameObject& target )
|
||||
{
|
||||
glm::vec3 rotate { 0.0f };
|
||||
|
||||
if ( glfwGetKey( window, key_mappings.look_right ) == GLFW_PRESS ) rotate.y += 1.f;
|
||||
if ( glfwGetKey( window, key_mappings.look_left ) == GLFW_PRESS ) rotate.y -= 1.f;
|
||||
if ( glfwGetKey( window, key_mappings.look_up ) == GLFW_PRESS ) rotate.x += 1.f;
|
||||
if ( glfwGetKey( window, key_mappings.look_down ) == GLFW_PRESS ) rotate.x -= 1.f;
|
||||
|
||||
static bool cursor_enabled { true };
|
||||
static std::chrono::time_point last_pressed { std::chrono::steady_clock::now() };
|
||||
|
||||
if ( glfwGetKey( window, GLFW_KEY_ESCAPE ) == GLFW_PRESS )
|
||||
{
|
||||
const auto now { std::chrono::steady_clock::now() };
|
||||
using namespace std::chrono_literals;
|
||||
if ( now - last_pressed > 1s )
|
||||
{
|
||||
cursor_enabled = !cursor_enabled;
|
||||
last_pressed = now;
|
||||
}
|
||||
}
|
||||
|
||||
if ( cursor_enabled )
|
||||
glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_NORMAL );
|
||||
else
|
||||
glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_DISABLED );
|
||||
|
||||
if ( !cursor_enabled )
|
||||
{
|
||||
//Take cursor input
|
||||
double xpos { 0.0f };
|
||||
double ypos { 0.0f };
|
||||
glfwGetCursorPos( window, &xpos, &ypos );
|
||||
|
||||
target.transform.rotation.y +=
|
||||
static_cast< float >( ( xpos * 0.006 ) * static_cast< double >( look_speed ) );
|
||||
target.transform.rotation.x -=
|
||||
static_cast< float >( ( ypos * 0.006 ) * static_cast< double >( look_speed ) );
|
||||
|
||||
glfwSetCursorPos( window, 0, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( glm::dot( rotate, rotate ) > std::numeric_limits< float >::epsilon() )
|
||||
target.transform.rotation += look_speed * dt * glm::normalize( rotate );
|
||||
|
||||
target.transform.rotation.x = glm::clamp( target.transform.rotation.x, -1.5f, 1.5f );
|
||||
target.transform.rotation.y = glm::mod( target.transform.rotation.y, glm::two_pi< float >() );
|
||||
}
|
||||
|
||||
const float yaw { target.transform.rotation.y };
|
||||
const glm::vec3 forward_dir { std::sin( yaw ), 0.0f, std::cos( yaw ) };
|
||||
const glm::vec3 right_dir { forward_dir.z, 0.0f, -forward_dir.x };
|
||||
const glm::vec3 up_dir { 0.0f, -0.1f, 0.0f };
|
||||
|
||||
glm::vec3 move_dir { 0.0f };
|
||||
if ( glfwGetKey( window, key_mappings.move_forward ) == GLFW_PRESS ) move_dir += forward_dir;
|
||||
if ( glfwGetKey( window, key_mappings.move_backward ) == GLFW_PRESS ) move_dir -= forward_dir;
|
||||
if ( glfwGetKey( window, key_mappings.move_right ) == GLFW_PRESS ) move_dir += right_dir;
|
||||
if ( glfwGetKey( window, key_mappings.move_left ) == GLFW_PRESS ) move_dir -= right_dir;
|
||||
if ( glfwGetKey( window, key_mappings.move_up ) == GLFW_PRESS ) move_dir += up_dir;
|
||||
if ( glfwGetKey( window, key_mappings.move_down ) == GLFW_PRESS ) move_dir -= up_dir;
|
||||
|
||||
if ( glm::dot( move_dir, move_dir ) > std::numeric_limits< float >::epsilon() )
|
||||
target.transform.translation += move_speed * dt * glm::normalize( move_dir );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
36
src/engine/KeyboardMovementController.hpp
Normal file
36
src/engine/KeyboardMovementController.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Created by kj16609 on 11/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameObject.hpp"
|
||||
#include "Window.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class KeyboardMovementController
|
||||
{
|
||||
public:
|
||||
|
||||
struct KeyMappings
|
||||
{
|
||||
int move_left { GLFW_KEY_A };
|
||||
int move_right { GLFW_KEY_D };
|
||||
int move_forward { GLFW_KEY_W };
|
||||
int move_backward { GLFW_KEY_S };
|
||||
int move_up { GLFW_KEY_E };
|
||||
int move_down { GLFW_KEY_Q };
|
||||
int look_left { GLFW_KEY_LEFT };
|
||||
int look_right { GLFW_KEY_RIGHT };
|
||||
int look_up { GLFW_KEY_UP };
|
||||
int look_down { GLFW_KEY_DOWN };
|
||||
} key_mappings;
|
||||
|
||||
float move_speed { 3.0f };
|
||||
float look_speed { 1.5f };
|
||||
|
||||
void moveInPlaneXZ( GLFWwindow* window, float dt, GameObject& target );
|
||||
};
|
||||
} // namespace fgl::engine
|
||||
@@ -7,73 +7,212 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wuseless-cast"
|
||||
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include "tinyobjloader/tiny_obj_loader.h"
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "engine/buffers/Buffer.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
struct hash< fgl::engine::Vertex >
|
||||
{
|
||||
size_t operator()( const fgl::engine::Vertex& vertex ) const
|
||||
{
|
||||
std::size_t seed { 0 };
|
||||
fgl::engine::hashCombine( seed, vertex.m_position, vertex.m_color, vertex.m_normal, vertex.m_uv );
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
Model::Model( Device& device, const std::vector< Vertex >& verts ) : m_device( device )
|
||||
vk::DrawIndexedIndirectCommand Model::
|
||||
buildParameters( VertexBufferSuballocation& vertex_buffer, IndexBufferSuballocation& index_buffer )
|
||||
{
|
||||
createVertexBuffers( verts );
|
||||
vk::DrawIndexedIndirectCommand cmd;
|
||||
|
||||
cmd.indexCount = index_buffer.count();
|
||||
cmd.firstIndex = index_buffer.getOffsetCount();
|
||||
|
||||
cmd.vertexOffset = static_cast< std::int32_t >( vertex_buffer.getOffsetCount() );
|
||||
|
||||
cmd.firstInstance = 0;
|
||||
cmd.instanceCount = 1;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
Model::Model( Device& device, const Builder& builder ) :
|
||||
m_device( device ),
|
||||
m_vertex_buffer( builder.m_vertex_buffer, builder.verts ),
|
||||
has_index_buffer( builder.m_index_buffer.size() > 0 ),
|
||||
m_index_buffer( builder.m_index_buffer, builder.indicies ),
|
||||
m_draw_parameters( buildParameters( m_vertex_buffer, m_index_buffer ) )
|
||||
{}
|
||||
|
||||
std::unique_ptr< Model > Model::
|
||||
createModel( Device& device, const std::filesystem::path& path, Buffer& vertex_buffer, Buffer& index_buffer )
|
||||
{
|
||||
vkDestroyBuffer( m_device.device(), m_vertex_buffer, nullptr );
|
||||
vkFreeMemory( m_device.device(), m_buffer_memory, nullptr );
|
||||
Builder builder { vertex_buffer, index_buffer };
|
||||
builder.loadModel( path );
|
||||
|
||||
return std::make_unique< Model >( device, builder );
|
||||
}
|
||||
|
||||
void Model::createVertexBuffers( const std::vector< Vertex >& verts )
|
||||
void Model::syncBuffers( vk::CommandBuffer& cmd_buffer )
|
||||
{
|
||||
m_vertex_count = static_cast< std::uint32_t >( verts.size() );
|
||||
assert( verts.size() >= 3 && "Vertex count must be at least 3" );
|
||||
m_vertex_buffer.stage( cmd_buffer );
|
||||
|
||||
VkDeviceSize buffer_size { sizeof( verts[ 0 ] ) * m_vertex_count };
|
||||
|
||||
m_device.createBuffer(
|
||||
buffer_size,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
m_vertex_buffer,
|
||||
m_buffer_memory );
|
||||
|
||||
void* data { nullptr };
|
||||
vkMapMemory( m_device.device(), m_buffer_memory, 0, buffer_size, 0, &data );
|
||||
|
||||
std::memcpy( data, verts.data(), static_cast< std::size_t >( buffer_size ) );
|
||||
vkUnmapMemory( m_device.device(), m_buffer_memory );
|
||||
m_index_buffer.stage( cmd_buffer );
|
||||
}
|
||||
|
||||
void Model::bind( VkCommandBuffer buffer )
|
||||
void Model::bind( vk::CommandBuffer& cmd_buffer )
|
||||
{
|
||||
VkBuffer buffers[] { m_vertex_buffer };
|
||||
VkDeviceSize offsets[] = { 0 };
|
||||
std::vector< vk::Buffer > vertex_buffers { m_vertex_buffer.getVkBuffer() };
|
||||
|
||||
vkCmdBindVertexBuffers( buffer, 0, 1, buffers, offsets );
|
||||
cmd_buffer.bindVertexBuffers( 0, vertex_buffers, { 0 } );
|
||||
|
||||
if ( has_index_buffer ) cmd_buffer.bindIndexBuffer( m_index_buffer.getVkBuffer(), 0, vk::IndexType::eUint32 );
|
||||
}
|
||||
|
||||
void Model::draw( VkCommandBuffer buffer )
|
||||
void Model::draw( vk::CommandBuffer& cmd_buffer )
|
||||
{
|
||||
vkCmdDraw( buffer, m_vertex_count, 1, 0, 0 );
|
||||
cmd_buffer.drawIndexed(
|
||||
m_draw_parameters.indexCount,
|
||||
m_draw_parameters.instanceCount,
|
||||
m_draw_parameters.firstIndex,
|
||||
m_draw_parameters.vertexOffset,
|
||||
m_draw_parameters.firstInstance );
|
||||
}
|
||||
|
||||
std::vector< VkVertexInputBindingDescription > Model::Vertex::getBindingDescriptions()
|
||||
std::vector< vk::VertexInputBindingDescription > Vertex::getBindingDescriptions()
|
||||
{
|
||||
std::vector< VkVertexInputBindingDescription > binding_descriptions {
|
||||
{ .binding = 0, .stride = sizeof( Vertex ), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX }
|
||||
std::vector< vk::VertexInputBindingDescription > binding_descriptions {
|
||||
{ 0, sizeof( Vertex ), vk::VertexInputRate::eVertex },
|
||||
{ 1, sizeof( ModelMatrixInfo ), vk::VertexInputRate::eInstance }
|
||||
};
|
||||
|
||||
return binding_descriptions;
|
||||
}
|
||||
|
||||
std::vector< VkVertexInputAttributeDescription > Model::Vertex::getAttributeDescriptions()
|
||||
std::vector< vk::VertexInputAttributeDescription > Vertex::getAttributeDescriptions()
|
||||
{
|
||||
std::vector< VkVertexInputAttributeDescription > attribute_descriptions {
|
||||
{ .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof( Vertex, m_pos ) },
|
||||
{ .location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof( Vertex, m_color ) },
|
||||
};
|
||||
std::vector< vk::VertexInputAttributeDescription > attribute_descriptions {};
|
||||
|
||||
attribute_descriptions.emplace_back( 0, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_position ) );
|
||||
attribute_descriptions.emplace_back( 1, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_color ) );
|
||||
attribute_descriptions.emplace_back( 2, 0, vk::Format::eR32G32B32Sfloat, offsetof( Vertex, m_normal ) );
|
||||
attribute_descriptions.emplace_back( 3, 0, vk::Format::eR32G32Sfloat, offsetof( Vertex, m_uv ) );
|
||||
|
||||
//Normal Matrix
|
||||
attribute_descriptions.emplace_back( 4, 1, vk::Format::eR32G32B32A32Sfloat, 0 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 5, 1, vk::Format::eR32G32B32A32Sfloat, 1 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 6, 1, vk::Format::eR32G32B32A32Sfloat, 2 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 7, 1, vk::Format::eR32G32B32A32Sfloat, 3 * sizeof( glm::vec4 ) );
|
||||
|
||||
//Normal Matrix
|
||||
attribute_descriptions.emplace_back( 8, 1, vk::Format::eR32G32B32A32Sfloat, 4 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 9, 1, vk::Format::eR32G32B32A32Sfloat, 5 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 10, 1, vk::Format::eR32G32B32A32Sfloat, 6 * sizeof( glm::vec4 ) );
|
||||
attribute_descriptions.emplace_back( 11, 1, vk::Format::eR32G32B32A32Sfloat, 7 * sizeof( glm::vec4 ) );
|
||||
|
||||
static_assert( 8 * sizeof( glm::vec4 ) == sizeof( ModelMatrixInfo ) );
|
||||
|
||||
return attribute_descriptions;
|
||||
}
|
||||
|
||||
void Model::Builder::loadModel( const std::filesystem::path& filepath )
|
||||
{
|
||||
verts.clear();
|
||||
indicies.clear();
|
||||
|
||||
tinyobj::attrib_t attrib {};
|
||||
std::vector< tinyobj::shape_t > shapes {};
|
||||
std::vector< tinyobj::material_t > materials {};
|
||||
std::string warn {};
|
||||
std::string error {};
|
||||
|
||||
if ( !tinyobj::LoadObj( &attrib, &shapes, &materials, &warn, &error, filepath.string< char >().c_str() ) )
|
||||
throw std::runtime_error( warn + error );
|
||||
|
||||
std::unordered_map< Vertex, std::uint32_t > unique_verts {};
|
||||
|
||||
for ( const auto& shape : shapes )
|
||||
{
|
||||
for ( const auto& index : shape.mesh.indices )
|
||||
{
|
||||
Vertex vert {};
|
||||
if ( index.vertex_index >= 0 )
|
||||
{
|
||||
vert.m_position = {
|
||||
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
|
||||
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
|
||||
attrib.vertices[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ],
|
||||
};
|
||||
|
||||
vert.m_color = { attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 0 ) ],
|
||||
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 1 ) ],
|
||||
attrib.colors[ static_cast< std::uint64_t >( 3 * index.vertex_index + 2 ) ] };
|
||||
|
||||
assert( vert.m_color[ 0 ] > 0.2f );
|
||||
assert( vert.m_color[ 1 ] > 0.2f );
|
||||
assert( vert.m_color[ 2 ] > 0.2f );
|
||||
}
|
||||
|
||||
if ( index.normal_index >= 0 )
|
||||
{
|
||||
vert.m_normal = {
|
||||
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 0 ) ],
|
||||
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 1 ) ],
|
||||
attrib.normals[ static_cast< std::uint64_t >( 3 * index.normal_index + 2 ) ],
|
||||
};
|
||||
}
|
||||
|
||||
if ( index.texcoord_index >= 0 )
|
||||
{
|
||||
vert.m_uv = {
|
||||
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 0 ) ],
|
||||
attrib.texcoords[ static_cast< std::uint64_t >( 3 * index.texcoord_index + 1 ) ],
|
||||
};
|
||||
}
|
||||
|
||||
if ( auto itter = unique_verts.find( vert ); itter != unique_verts.end() )
|
||||
{
|
||||
indicies.push_back( unique_verts[ vert ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto insert_op { unique_verts.insert( std::make_pair( vert, verts.size() ) ) };
|
||||
verts.emplace_back( vert );
|
||||
|
||||
if ( insert_op.second )
|
||||
indicies.push_back( insert_op.first->second );
|
||||
else
|
||||
throw std::runtime_error( "Failed to insert new vertex" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << unique_verts.size() << " unique verts" << std::endl;
|
||||
}
|
||||
} // namespace fgl::engine
|
||||
@@ -8,43 +8,97 @@
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Device.hpp"
|
||||
#include "engine/buffers/Buffer.hpp"
|
||||
#include "engine/buffers/BufferSuballocation.hpp"
|
||||
#include "engine/buffers/vector/DeviceVector.hpp"
|
||||
#include "engine/buffers/vector/HostVector.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 m_position { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec3 m_color { 1.0f, 1.0f, 1.0f };
|
||||
glm::vec3 m_normal { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec2 m_uv { 0.0f, 0.0f };
|
||||
|
||||
static std::vector< vk::VertexInputBindingDescription > getBindingDescriptions();
|
||||
static std::vector< vk::VertexInputAttributeDescription > getAttributeDescriptions();
|
||||
|
||||
bool operator==( const Vertex& other ) const
|
||||
{
|
||||
return m_position == other.m_position && m_color == other.m_color && m_normal == other.m_normal
|
||||
&& m_uv == other.m_uv;
|
||||
}
|
||||
};
|
||||
|
||||
struct ModelMatrixInfo
|
||||
{
|
||||
glm::mat4 model_matrix;
|
||||
glm::mat4 normal_matrix;
|
||||
};
|
||||
|
||||
using VertexBufferSuballocation = DeviceVector< Vertex >;
|
||||
|
||||
using IndexBufferSuballocation = DeviceVector< std::uint32_t >;
|
||||
|
||||
using DrawParameterBufferSuballocation = HostVector< vk::DrawIndexedIndirectCommand >;
|
||||
|
||||
using ModelMatrixInfoBufferSuballocation = HostVector< ModelMatrixInfo >;
|
||||
|
||||
class Model
|
||||
{
|
||||
Device& m_device;
|
||||
VkBuffer m_vertex_buffer;
|
||||
VkDeviceMemory m_buffer_memory;
|
||||
std::uint32_t m_vertex_count;
|
||||
VertexBufferSuballocation m_vertex_buffer;
|
||||
|
||||
bool has_index_buffer { false };
|
||||
IndexBufferSuballocation m_index_buffer;
|
||||
|
||||
vk::DrawIndexedIndirectCommand
|
||||
buildParameters( VertexBufferSuballocation& vertex_buffer, IndexBufferSuballocation& index_buffer );
|
||||
|
||||
vk::DrawIndexedIndirectCommand m_draw_parameters;
|
||||
|
||||
public:
|
||||
|
||||
struct Vertex
|
||||
struct Builder
|
||||
{
|
||||
glm::vec3 m_pos;
|
||||
glm::vec3 m_color;
|
||||
std::vector< Vertex > verts {};
|
||||
std::vector< std::uint32_t > indicies {};
|
||||
Buffer& m_vertex_buffer;
|
||||
Buffer& m_index_buffer;
|
||||
|
||||
static std::vector< VkVertexInputBindingDescription > getBindingDescriptions();
|
||||
static std::vector< VkVertexInputAttributeDescription > getAttributeDescriptions();
|
||||
Builder() = delete;
|
||||
|
||||
Builder( Buffer& parent_vertex_buffer, Buffer& parent_index_buffer ) :
|
||||
m_vertex_buffer( parent_vertex_buffer ),
|
||||
m_index_buffer( parent_index_buffer )
|
||||
{}
|
||||
|
||||
void loadModel( const std::filesystem::path& filepath );
|
||||
};
|
||||
|
||||
private:
|
||||
vk::DrawIndexedIndirectCommand getDrawCommand() const { return m_draw_parameters; }
|
||||
|
||||
void createVertexBuffers( const std::vector< Vertex >& verts );
|
||||
static std::unique_ptr< Model > createModel(
|
||||
Device& device, const std::filesystem::path& path, Buffer& vertex_buffer, Buffer& index_buffer );
|
||||
|
||||
public:
|
||||
void syncBuffers( vk::CommandBuffer& cmd_buffer );
|
||||
|
||||
void bind( VkCommandBuffer buffer );
|
||||
void draw( VkCommandBuffer buffer );
|
||||
void bind( vk::CommandBuffer& cmd_buffer );
|
||||
void draw( vk::CommandBuffer& cmd_buffer );
|
||||
|
||||
Model( Device& device, const std::vector< Vertex >& verts );
|
||||
Model( Device& device, const Builder& builder );
|
||||
|
||||
~Model();
|
||||
~Model() = default;
|
||||
|
||||
Model( const Model& model ) = delete;
|
||||
Model& operator=( const Model& other ) = delete;
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/27/23.
|
||||
//
|
||||
|
||||
#include "Pipeline.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
#include "Model.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
std::vector< std::byte > Pipeline::readFile( const std::filesystem::path& path )
|
||||
{
|
||||
if ( std::ifstream ifs( path, std::ios::binary | std::ios::ate ); ifs )
|
||||
{
|
||||
std::vector< std::byte > data;
|
||||
|
||||
data.resize( static_cast< std::size_t >( ifs.tellg() ) );
|
||||
ifs.seekg( 0, std::ios::beg );
|
||||
|
||||
ifs.read( reinterpret_cast< char* >( data.data() ), static_cast< long >( data.size() ) );
|
||||
|
||||
return data;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error( "Failed to load file: " + path.string() );
|
||||
}
|
||||
|
||||
void Pipeline::createGraphicsPipeline(
|
||||
const std::filesystem::path& vert, const std::filesystem::path& frag, const PipelineConfigInfo& info )
|
||||
{
|
||||
const std::vector< std::byte > vert_data { readFile( vert ) };
|
||||
const std::vector< std::byte > frag_data { readFile( frag ) };
|
||||
|
||||
createShaderModule( vert_data, &m_vert_shader );
|
||||
createShaderModule( frag_data, &m_frag_shader );
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[ 2 ] {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = m_vert_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = m_frag_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr }
|
||||
|
||||
};
|
||||
|
||||
auto binding_descriptions { Model::Vertex::getBindingDescriptions() };
|
||||
auto attribute_descriptions { Model::Vertex::getAttributeDescriptions() };
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = static_cast< std::uint32_t >( binding_descriptions.size() ),
|
||||
.pVertexBindingDescriptions = binding_descriptions.data(),
|
||||
.vertexAttributeDescriptionCount = static_cast< std::uint32_t >( attribute_descriptions.size() ),
|
||||
.pVertexAttributeDescriptions = attribute_descriptions.data(),
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_info {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 2,
|
||||
.pStages = shaderStages,
|
||||
.pVertexInputState = &vertex_input_info,
|
||||
.pInputAssemblyState = &info.assembly_info,
|
||||
.pViewportState = &info.viewport_info,
|
||||
.pRasterizationState = &info.rasterization_info,
|
||||
.pMultisampleState = &info.multisample_info,
|
||||
.pDepthStencilState = &info.depth_stencil_info,
|
||||
.pColorBlendState = &info.color_blend_info,
|
||||
.pDynamicState = &info.dynamic_state_info,
|
||||
|
||||
.layout = info.layout,
|
||||
.renderPass = info.render_pass,
|
||||
.subpass = info.subpass,
|
||||
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1,
|
||||
};
|
||||
|
||||
if ( vkCreateGraphicsPipelines( m_device.device(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &m_vk_pipeline )
|
||||
!= VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to create graphics pipeline" );
|
||||
}
|
||||
|
||||
void Pipeline::createShaderModule( const std::vector< std::byte >& code, VkShaderModule* module )
|
||||
{
|
||||
VkShaderModuleCreateInfo create_info { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = code.size(),
|
||||
.pCode = reinterpret_cast< const std::uint32_t* >( code.data() ) };
|
||||
|
||||
if ( vkCreateShaderModule( m_device.device(), &create_info, nullptr, module ) != VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to create shader module" );
|
||||
}
|
||||
|
||||
Pipeline::Pipeline(
|
||||
Device& device,
|
||||
const std::filesystem::path& vert,
|
||||
const std::filesystem::path& frag,
|
||||
const PipelineConfigInfo& info ) :
|
||||
m_device( device )
|
||||
{
|
||||
createGraphicsPipeline( vert, frag, info );
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline()
|
||||
{
|
||||
vkDestroyShaderModule( m_device.device(), m_vert_shader, nullptr );
|
||||
vkDestroyShaderModule( m_device.device(), m_frag_shader, nullptr );
|
||||
|
||||
vkDestroyPipeline( m_device.device(), m_vk_pipeline, nullptr );
|
||||
}
|
||||
|
||||
void Pipeline::defaultConfig( PipelineConfigInfo& info )
|
||||
{
|
||||
info.viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
info.viewport_info.viewportCount = 1;
|
||||
info.viewport_info.pViewports = nullptr;
|
||||
info.viewport_info.scissorCount = 1;
|
||||
info.viewport_info.pScissors = nullptr;
|
||||
|
||||
info.assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
info.assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
info.assembly_info.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
info.rasterization_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
info.rasterization_info.depthClampEnable = VK_FALSE;
|
||||
info.rasterization_info.rasterizerDiscardEnable = VK_FALSE;
|
||||
info.rasterization_info.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
info.rasterization_info.cullMode = VK_CULL_MODE_NONE;
|
||||
info.rasterization_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
info.rasterization_info.depthBiasEnable = VK_FALSE;
|
||||
info.rasterization_info.depthBiasConstantFactor = 0.0f;
|
||||
info.rasterization_info.depthBiasClamp = 0.0f;
|
||||
info.rasterization_info.depthBiasSlopeFactor = 0.0f;
|
||||
info.rasterization_info.lineWidth = 1.0f;
|
||||
|
||||
info.multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
info.multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.multisample_info.sampleShadingEnable = VK_FALSE;
|
||||
info.multisample_info.minSampleShading = 1.0f;
|
||||
info.multisample_info.pSampleMask = nullptr;
|
||||
info.multisample_info.alphaToCoverageEnable = VK_FALSE;
|
||||
info.multisample_info.alphaToOneEnable = VK_FALSE;
|
||||
|
||||
info.color_blend_attachment.blendEnable = VK_FALSE;
|
||||
info.color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
info.color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
info.color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
info.color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
info.color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
info.color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
info.color_blend_attachment.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
info.color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
info.color_blend_info.logicOpEnable = VK_FALSE;
|
||||
info.color_blend_info.logicOp = VK_LOGIC_OP_COPY;
|
||||
info.color_blend_info.attachmentCount = 1;
|
||||
info.color_blend_info.pAttachments = &info.color_blend_attachment;
|
||||
info.color_blend_info.blendConstants[ 0 ] = 0.0f;
|
||||
info.color_blend_info.blendConstants[ 1 ] = 0.0f;
|
||||
info.color_blend_info.blendConstants[ 2 ] = 0.0f;
|
||||
info.color_blend_info.blendConstants[ 3 ] = 0.0f;
|
||||
|
||||
info.depth_stencil_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
info.depth_stencil_info.depthTestEnable = VK_TRUE;
|
||||
info.depth_stencil_info.depthWriteEnable = VK_TRUE;
|
||||
info.depth_stencil_info.depthCompareOp = VK_COMPARE_OP_LESS;
|
||||
info.depth_stencil_info.depthBoundsTestEnable = VK_FALSE;
|
||||
info.depth_stencil_info.stencilTestEnable = VK_FALSE;
|
||||
info.depth_stencil_info.front = {};
|
||||
info.depth_stencil_info.back = {};
|
||||
info.depth_stencil_info.minDepthBounds = 0.0f;
|
||||
info.depth_stencil_info.maxDepthBounds = 1.0f;
|
||||
|
||||
info.dynamic_state_enables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||
info.dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
info.dynamic_state_info.pDynamicStates = info.dynamic_state_enables.data();
|
||||
info.dynamic_state_info.dynamicStateCount = static_cast< std::uint32_t >( info.dynamic_state_enables.size() );
|
||||
info.dynamic_state_info.flags = 0;
|
||||
}
|
||||
|
||||
void Pipeline::bind( VkCommandBuffer command_buffer )
|
||||
{
|
||||
vkCmdBindPipeline( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_vk_pipeline );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/27/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "Device.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct PipelineConfigInfo
|
||||
{
|
||||
VkPipelineViewportStateCreateInfo viewport_info;
|
||||
VkPipelineInputAssemblyStateCreateInfo assembly_info;
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_info;
|
||||
VkPipelineMultisampleStateCreateInfo multisample_info;
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment;
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_info;
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_info;
|
||||
|
||||
std::vector< VkDynamicState > dynamic_state_enables;
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state_info;
|
||||
|
||||
VkPipelineLayout layout { nullptr };
|
||||
VkRenderPass render_pass { nullptr };
|
||||
std::uint32_t subpass { 0 };
|
||||
|
||||
PipelineConfigInfo() = default;
|
||||
PipelineConfigInfo( const PipelineConfigInfo& other ) = delete;
|
||||
PipelineConfigInfo& operator=( const PipelineConfigInfo& ) = delete;
|
||||
};
|
||||
|
||||
class Pipeline
|
||||
{
|
||||
Device& m_device;
|
||||
VkPipeline m_vk_pipeline;
|
||||
VkShaderModule m_vert_shader;
|
||||
VkShaderModule m_frag_shader;
|
||||
|
||||
static std::vector< std::byte > readFile( const std::filesystem::path& path );
|
||||
|
||||
void createGraphicsPipeline(
|
||||
const std::filesystem::path& vert, const std::filesystem::path& frag, const PipelineConfigInfo& info );
|
||||
|
||||
void createShaderModule( const std::vector< std::byte >& code, VkShaderModule* module );
|
||||
|
||||
public:
|
||||
|
||||
void bind( VkCommandBuffer command_buffer );
|
||||
|
||||
Pipeline(
|
||||
Device& device,
|
||||
const std::filesystem::path& vert,
|
||||
const std::filesystem::path& frag,
|
||||
const PipelineConfigInfo& info );
|
||||
|
||||
static void defaultConfig( PipelineConfigInfo& info );
|
||||
|
||||
~Pipeline();
|
||||
|
||||
Pipeline( const Pipeline& other ) = delete;
|
||||
Pipeline& operator=( const Pipeline& ) = delete;
|
||||
};
|
||||
} // namespace fgl::engine
|
||||
35
src/engine/PushConstant.hpp
Normal file
35
src/engine/PushConstant.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by kj16609 on 12/7/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T, vk::ShaderStageFlags stages, std::uint16_t offset = 0 >
|
||||
struct PushConstant
|
||||
{
|
||||
using Type = T;
|
||||
|
||||
constexpr static vk::PushConstantRange m_range { stages, offset, sizeof( T ) };
|
||||
|
||||
PushConstant()
|
||||
{
|
||||
static_assert( sizeof( T ) <= 128, "Push constant range size must be less or equal to 128 bytes" );
|
||||
}
|
||||
|
||||
static void push( vk::CommandBuffer command_buffer, vk::PipelineLayout m_pipeline_layout, T& data )
|
||||
{
|
||||
command_buffer.pushConstants( m_pipeline_layout, stages, offset, sizeof( T ), &data );
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_constant_range = requires( T t ) {
|
||||
{
|
||||
t.m_range
|
||||
} -> std::same_as< const vk::PushConstantRange& >;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
34
src/engine/RenderPass.cpp
Normal file
34
src/engine/RenderPass.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by kj16609 on 12/31/23.
|
||||
//
|
||||
|
||||
#include "RenderPass.hpp"
|
||||
|
||||
#include "Device.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
vk::RenderPass RenderPass::create()
|
||||
{
|
||||
auto device { Device::getInstance().device() };
|
||||
|
||||
vk::RenderPassCreateInfo info;
|
||||
info.attachmentCount = static_cast< std::uint32_t >( attachment_descriptions.size() );
|
||||
info.pAttachments = attachment_descriptions.data();
|
||||
info.subpassCount = static_cast< std::uint32_t >( subpass_descriptions.size() );
|
||||
info.pSubpasses = subpass_descriptions.data();
|
||||
info.dependencyCount = static_cast< std::uint32_t >( dependencies.size() );
|
||||
info.pDependencies = dependencies.data();
|
||||
|
||||
vk::RenderPass render_pass {};
|
||||
|
||||
if ( device.createRenderPass( &info, nullptr, &render_pass ) != vk::Result::eSuccess ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "failed to create render pass!" );
|
||||
}
|
||||
else
|
||||
return render_pass;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
107
src/engine/RenderPass.hpp
Normal file
107
src/engine/RenderPass.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Created by kj16609 on 12/30/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Subpass.hpp"
|
||||
#include "engine/image/ImageView.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct RenderPassResources
|
||||
{
|
||||
std::vector< std::vector< std::shared_ptr< ImageView > > > m_image_views;
|
||||
|
||||
RenderPassResources() = delete;
|
||||
RenderPassResources( const RenderPassResources& ) = delete;
|
||||
|
||||
RenderPassResources( std::vector< std::vector< std::shared_ptr< ImageView > > >&& image_views ) :
|
||||
m_image_views( std::move( image_views ) )
|
||||
{}
|
||||
|
||||
std::vector< vk::ImageView > forFrame( std::uint32_t frame ) const
|
||||
{
|
||||
std::vector< vk::ImageView > views;
|
||||
|
||||
for ( auto& view : m_image_views.at( frame ) ) views.push_back( view->view() );
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
~RenderPassResources() { std::cout << "Cleaing resources" << std::endl; }
|
||||
};
|
||||
|
||||
class RenderPass
|
||||
{
|
||||
std::vector< vk::AttachmentDescription > attachment_descriptions {};
|
||||
std::vector< vk::ClearValue > m_clear_values {};
|
||||
|
||||
std::vector< vk::SubpassDescription > subpass_descriptions {};
|
||||
std::vector< vk::SubpassDependency > dependencies {};
|
||||
|
||||
std::vector< AttachmentResources > m_attachment_resources {};
|
||||
|
||||
public:
|
||||
|
||||
std::vector< vk::ClearValue > getClearValues() const { return m_clear_values; }
|
||||
|
||||
template < typename SubpassT >
|
||||
requires is_subpass< SubpassT >
|
||||
void registerSubpass( SubpassT& subpass )
|
||||
{
|
||||
subpass_descriptions.emplace_back( subpass.description() );
|
||||
|
||||
for ( auto& dependency : subpass.dependencies )
|
||||
{
|
||||
dependencies.push_back( dependency );
|
||||
}
|
||||
}
|
||||
|
||||
template < is_attachment... Attachments >
|
||||
void registerAttachments( Attachments&... attachments )
|
||||
{
|
||||
attachment_descriptions.reserve( sizeof...( Attachments ) );
|
||||
m_clear_values.reserve( sizeof...( Attachments ) );
|
||||
|
||||
( ( attachments.setIndex( static_cast< std::uint32_t >( attachment_descriptions.size() ) ),
|
||||
attachment_descriptions.push_back( attachments.desc() ),
|
||||
m_clear_values.push_back( attachments.m_clear_value ) ),
|
||||
... );
|
||||
|
||||
m_attachment_resources.reserve( sizeof...( Attachments ) );
|
||||
|
||||
( ( m_attachment_resources.emplace_back( attachments.m_attachment_resources ) ), ... );
|
||||
}
|
||||
|
||||
std::unique_ptr< RenderPassResources > resources( std::uint16_t frame_count )
|
||||
{
|
||||
assert( m_attachment_resources.size() > 0 && "Must register attachments before getting resources" );
|
||||
|
||||
//Each attachment will have a vector of image views, one for each frame
|
||||
// We need to seperate them out so that we have a vector of image views for each frame
|
||||
std::vector< std::vector< std::shared_ptr< ImageView > > > views;
|
||||
|
||||
views.resize( frame_count );
|
||||
|
||||
for ( auto& attachment : m_attachment_resources )
|
||||
{
|
||||
assert(
|
||||
attachment.m_image_views.size() == frame_count
|
||||
&& "Attachment image views must be equal to frame count" );
|
||||
for ( std::uint16_t i = 0; i < attachment.m_image_views.size(); ++i )
|
||||
{
|
||||
views[ i ].emplace_back( attachment.m_image_views[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique< RenderPassResources >( std::move( views ) );
|
||||
}
|
||||
|
||||
vk::RenderPass create();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,98 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/27/23.
|
||||
//
|
||||
|
||||
#include "RenderSystem.hpp"
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ON
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "engine/debug_gui/DebugGUI.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct SimplePushConstantData
|
||||
{
|
||||
glm::mat4 transform { 1.0f };
|
||||
alignas( 16 ) glm::vec3 color;
|
||||
};
|
||||
|
||||
void RenderSystem::createPipelineLayout()
|
||||
{
|
||||
VkPushConstantRange push_constant_range { .stageFlags =
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = sizeof( SimplePushConstantData ) };
|
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_info { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 0,
|
||||
.pSetLayouts = nullptr,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &push_constant_range };
|
||||
|
||||
if ( vkCreatePipelineLayout( m_device.device(), &pipeline_layout_info, nullptr, &m_pipeline_layout )
|
||||
!= VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to create pipeline layout" );
|
||||
}
|
||||
|
||||
void RenderSystem::createPipeline( VkRenderPass render_pass )
|
||||
{
|
||||
PipelineConfigInfo pipeline_config {};
|
||||
Pipeline::defaultConfig( pipeline_config );
|
||||
|
||||
pipeline_config.render_pass = render_pass;
|
||||
pipeline_config.layout = m_pipeline_layout;
|
||||
|
||||
m_pipeline = std::make_unique<
|
||||
Pipeline >( m_device, "shaders/simple_shader.vert.spv", "shaders/simple_shader.frag.spv", pipeline_config );
|
||||
}
|
||||
|
||||
RenderSystem::RenderSystem( Device& device, VkRenderPass render_pass ) : m_device( device )
|
||||
{
|
||||
createPipelineLayout();
|
||||
createPipeline( render_pass );
|
||||
}
|
||||
|
||||
RenderSystem::~RenderSystem()
|
||||
{
|
||||
vkDestroyPipelineLayout( m_device.device(), m_pipeline_layout, nullptr );
|
||||
}
|
||||
|
||||
void RenderSystem::renderGameObjects(
|
||||
VkCommandBuffer command_buffer, std::vector< GameObject >& game_objects, const Camera& camera )
|
||||
{
|
||||
//TracyVkZone( m_device.getCurrentTracyCTX(), command_buffer, "Render game objects" );
|
||||
m_pipeline->bind( command_buffer );
|
||||
|
||||
for ( auto& obj : game_objects )
|
||||
{
|
||||
obj.transform.rotation.y = glm::mod( obj.transform.rotation.y + 0.01f, glm::two_pi< float >() );
|
||||
obj.transform.rotation.x = glm::mod( obj.transform.rotation.x + 0.01f, glm::two_pi< float >() );
|
||||
obj.transform.rotation.z = glm::mod( obj.transform.rotation.z + 0.01f, glm::two_pi< float >() );
|
||||
|
||||
SimplePushConstantData push { .transform = camera.getProjectionMatrix() * obj.transform.mat4(),
|
||||
.color = obj.color };
|
||||
|
||||
vkCmdPushConstants(
|
||||
command_buffer,
|
||||
m_pipeline_layout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0,
|
||||
sizeof( SimplePushConstantData ),
|
||||
&push );
|
||||
|
||||
obj.model->bind( command_buffer );
|
||||
obj.model->draw( command_buffer );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "Device.hpp"
|
||||
#include "GameObject.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "Pipeline.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class RenderSystem
|
||||
{
|
||||
Device& m_device;
|
||||
|
||||
std::unique_ptr< Pipeline > m_pipeline;
|
||||
VkPipelineLayout m_pipeline_layout;
|
||||
|
||||
void createPipelineLayout();
|
||||
void createPipeline( VkRenderPass render_pass );
|
||||
|
||||
public:
|
||||
|
||||
void renderGameObjects(
|
||||
VkCommandBuffer command_buffer, std::vector< GameObject >& objects, const Camera& camera );
|
||||
|
||||
RenderSystem( Device& device, VkRenderPass render_pass );
|
||||
~RenderSystem();
|
||||
RenderSystem( RenderSystem&& other ) = delete;
|
||||
RenderSystem( const RenderSystem& other ) = delete;
|
||||
RenderSystem& operator=( const RenderSystem& other ) = delete;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -16,22 +16,19 @@
|
||||
#include "Device.hpp"
|
||||
#include "SwapChain.hpp"
|
||||
#include "Window.hpp"
|
||||
#include "engine/debug_gui/DebugGUI.hpp"
|
||||
|
||||
//clang-format: off
|
||||
#include <Tracy/tracy/TracyVulkan.hpp>
|
||||
#include <tracy/TracyVulkan.hpp>
|
||||
|
||||
//clang-format: on
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
Renderer::Renderer( Window& window, Device& device ) : m_window( window ), m_device( device )
|
||||
Renderer::Renderer( Window& window ) : m_window( window )
|
||||
{
|
||||
recreateSwapchain();
|
||||
createCommandBuffers();
|
||||
|
||||
debug::initDebugGUI();
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
@@ -43,27 +40,20 @@ namespace fgl::engine
|
||||
{
|
||||
m_command_buffer.resize( SwapChain::MAX_FRAMES_IN_FLIGHT );
|
||||
|
||||
VkCommandBufferAllocateInfo alloc_info { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = m_device.getCommandPool(),
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount =
|
||||
static_cast< std::uint32_t >( m_command_buffer.size() ) };
|
||||
vk::CommandBufferAllocateInfo alloc_info {};
|
||||
alloc_info.pNext = VK_NULL_HANDLE;
|
||||
alloc_info.commandPool = Device::getInstance().getCommandPool();
|
||||
alloc_info.level = vk::CommandBufferLevel::ePrimary;
|
||||
alloc_info.commandBufferCount = static_cast< std::uint32_t >( m_command_buffer.size() );
|
||||
|
||||
if ( vkAllocateCommandBuffers( m_device.device(), &alloc_info, m_command_buffer.data() ) != VK_SUCCESS )
|
||||
if ( Device::getInstance().device().allocateCommandBuffers( &alloc_info, m_command_buffer.data() )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "Failed to allocate command buffers" );
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
for ( auto& cmd_buffer : m_command_buffer )
|
||||
{
|
||||
auto context =
|
||||
TracyVkContext( m_device.phyDevice(), m_device.device(), m_device.graphicsQueue(), cmd_buffer )
|
||||
m_tracy_ctx.emplace_back( context );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Renderer::recreateSwapchain()
|
||||
{
|
||||
std::cout << "Rebuilding swap chain" << std::endl;
|
||||
auto extent { m_window.getExtent() };
|
||||
|
||||
while ( extent.width == 0 || extent.height == 0 )
|
||||
@@ -72,14 +62,14 @@ namespace fgl::engine
|
||||
glfwWaitEvents();
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle( m_device.device() );
|
||||
Device::getInstance().device().waitIdle();
|
||||
|
||||
if ( m_swapchain == nullptr )
|
||||
m_swapchain = std::make_unique< SwapChain >( m_device, extent );
|
||||
m_swapchain = std::make_unique< SwapChain >( extent );
|
||||
else
|
||||
{
|
||||
std::shared_ptr< SwapChain > old_swap_chain { std::move( m_swapchain ) };
|
||||
m_swapchain = std::make_unique< SwapChain >( m_device, extent, old_swap_chain );
|
||||
m_swapchain = std::make_unique< SwapChain >( extent, old_swap_chain );
|
||||
|
||||
if ( !old_swap_chain->compareSwapFormats( *m_swapchain.get() ) )
|
||||
throw std::runtime_error( "Swap chain image(or depth) format has changed!" );
|
||||
@@ -90,40 +80,35 @@ namespace fgl::engine
|
||||
{
|
||||
if ( m_command_buffer.size() == 0 ) return;
|
||||
|
||||
for ( auto& ctx : m_tracy_ctx ) TracyVkDestroy( ctx );
|
||||
|
||||
vkFreeCommandBuffers(
|
||||
m_device.device(),
|
||||
m_device.getCommandPool(),
|
||||
Device::getInstance().device().freeCommandBuffers(
|
||||
Device::getInstance().getCommandPool(),
|
||||
static_cast< std::uint32_t >( m_command_buffer.size() ),
|
||||
m_command_buffer.data() );
|
||||
}
|
||||
|
||||
VkCommandBuffer Renderer::beginFrame()
|
||||
vk::CommandBuffer Renderer::beginFrame()
|
||||
{
|
||||
assert( !is_frame_started && "Cannot begin frame while frame is already in progress" );
|
||||
auto result { m_swapchain->acquireNextImage( ¤t_image_idx ) };
|
||||
vk::Result result { m_swapchain->acquireNextImage( ¤t_image_idx ) };
|
||||
|
||||
if ( result == VK_ERROR_OUT_OF_DATE_KHR )
|
||||
if ( result == vk::Result::eErrorOutOfDateKHR )
|
||||
{
|
||||
recreateSwapchain();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR )
|
||||
if ( result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR )
|
||||
throw std::runtime_error( "Failed to acquire support chain image" );
|
||||
|
||||
is_frame_started = true;
|
||||
auto command_buffer { getCurrentCommandbuffer() };
|
||||
|
||||
VkCommandBufferBeginInfo begin_info {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
};
|
||||
vk::CommandBufferBeginInfo begin_info {};
|
||||
begin_info.pNext = VK_NULL_HANDLE;
|
||||
begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
begin_info.pInheritanceInfo = VK_NULL_HANDLE;
|
||||
|
||||
if ( vkBeginCommandBuffer( command_buffer, &begin_info ) != VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to begin recording to command buffer" );
|
||||
|
||||
TracyVkCollect( getCurrentTracyCTX(), command_buffer );
|
||||
command_buffer.begin( begin_info );
|
||||
|
||||
return command_buffer;
|
||||
}
|
||||
@@ -139,54 +124,73 @@ namespace fgl::engine
|
||||
|
||||
const auto result { m_swapchain->submitCommandBuffers( &command_buffer, ¤t_image_idx ) };
|
||||
|
||||
if ( result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || m_window.wasWindowResized() )
|
||||
if ( result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR
|
||||
|| m_window.wasWindowResized() )
|
||||
{
|
||||
m_window.resetWindowResizedFlag();
|
||||
recreateSwapchain();
|
||||
}
|
||||
else if ( result != VK_SUCCESS )
|
||||
else if ( result != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "Failed to submit commmand buffer" );
|
||||
|
||||
is_frame_started = false;
|
||||
current_frame_idx = ( current_frame_idx + 1 ) % SwapChain::MAX_FRAMES_IN_FLIGHT;
|
||||
current_frame_idx = static_cast< std::uint8_t >( ( current_frame_idx + 1 ) % SwapChain::MAX_FRAMES_IN_FLIGHT );
|
||||
}
|
||||
|
||||
void Renderer::beginSwapchainRendererPass( VkCommandBuffer buffer )
|
||||
void Renderer::beginSwapchainRendererPass( vk::CommandBuffer buffer )
|
||||
{
|
||||
assert( is_frame_started && "Cannot call beginSwapChainRenderPass if frame is not in progress" );
|
||||
assert(
|
||||
buffer == getCurrentCommandbuffer()
|
||||
&& "Cannot begin render pass on command buffer from a different frame" );
|
||||
|
||||
std::array< VkClearValue, 2 > clear_values {};
|
||||
clear_values[ 0 ].color = { { 0.1f, 0.1f, 0.1f, 0.1f } };
|
||||
clear_values[ 1 ].depthStencil = { 1.0f, 0 };
|
||||
//TODO: Attach this stuff into the swapchain creation via attachments and request it again here
|
||||
//std::array< vk::ClearValue, 5 > clear_values {};
|
||||
|
||||
VkRenderPassBeginInfo render_pass_info { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = m_swapchain->getRenderPass(),
|
||||
.framebuffer = m_swapchain->getFrameBuffer( current_image_idx ),
|
||||
.renderArea = { .offset = { 0, 0 },
|
||||
.extent = m_swapchain->getSwapChainExtent() },
|
||||
.clearValueCount = static_cast< std::uint32_t >( clear_values.size() ),
|
||||
.pClearValues = clear_values.data() };
|
||||
std::vector< vk::ClearValue > clear_values { m_swapchain->getClearValues() };
|
||||
|
||||
vkCmdBeginRenderPass( buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE );
|
||||
vk::RenderPassBeginInfo render_pass_info {};
|
||||
render_pass_info.pNext = VK_NULL_HANDLE;
|
||||
render_pass_info.renderPass = m_swapchain->getRenderPass();
|
||||
render_pass_info.framebuffer = m_swapchain->getFrameBuffer( static_cast< int >( current_image_idx ) );
|
||||
render_pass_info.renderArea = { .offset = { 0, 0 }, .extent = m_swapchain->getSwapChainExtent() };
|
||||
render_pass_info.clearValueCount = static_cast< std::uint32_t >( clear_values.size() );
|
||||
render_pass_info.pClearValues = clear_values.data();
|
||||
|
||||
VkViewport viewport {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast< float >( m_swapchain->getSwapChainExtent().width ),
|
||||
.height = static_cast< float >( m_swapchain->getSwapChainExtent().height ),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
buffer.beginRenderPass( &render_pass_info, vk::SubpassContents::eInline );
|
||||
|
||||
VkRect2D scissor { { 0, 0 }, m_swapchain->getSwapChainExtent() };
|
||||
vkCmdSetViewport( buffer, 0, 1, &viewport );
|
||||
vkCmdSetScissor( buffer, 0, 1, &scissor );
|
||||
vk::Viewport viewport {};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = static_cast< float >( m_swapchain->getSwapChainExtent().width );
|
||||
viewport.height = static_cast< float >( m_swapchain->getSwapChainExtent().height );
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
vk::Rect2D scissor { { 0, 0 }, m_swapchain->getSwapChainExtent() };
|
||||
buffer.setViewport( 0, 1, &viewport );
|
||||
buffer.setScissor( 0, 1, &scissor );
|
||||
}
|
||||
|
||||
void Renderer::endSwapchainRendererPass( VkCommandBuffer buffer )
|
||||
void Renderer::nextPass()
|
||||
{
|
||||
vk::Viewport viewport {};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = static_cast< float >( m_swapchain->getSwapChainExtent().width );
|
||||
viewport.height = static_cast< float >( m_swapchain->getSwapChainExtent().height );
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
vk::Rect2D scissor { { 0, 0 }, m_swapchain->getSwapChainExtent() };
|
||||
|
||||
auto buffer = getCurrentCommandbuffer();
|
||||
buffer.nextSubpass( vk::SubpassContents::eInline );
|
||||
buffer.setViewport( 0, 1, &viewport );
|
||||
buffer.setScissor( 0, 1, &scissor );
|
||||
}
|
||||
|
||||
void Renderer::endSwapchainRendererPass( vk::CommandBuffer buffer )
|
||||
{
|
||||
assert( is_frame_started && "Cannot call endSwapChainRenderPass if frame is not in progress" );
|
||||
assert(
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "Window.hpp"
|
||||
|
||||
//clang-format: off
|
||||
#include <Tracy/tracy/TracyVulkan.hpp>
|
||||
#include <tracy/TracyVulkan.hpp>
|
||||
|
||||
//clang-format: on
|
||||
|
||||
@@ -25,24 +25,28 @@ namespace fgl::engine
|
||||
class Renderer
|
||||
{
|
||||
Window& m_window;
|
||||
Device& m_device;
|
||||
std::unique_ptr< SwapChain > m_swapchain { std::make_unique< SwapChain >( m_device, m_window.getExtent() ) };
|
||||
std::unique_ptr< SwapChain > m_swapchain { std::make_unique< SwapChain >( m_window.getExtent() ) };
|
||||
|
||||
std::vector< VkCommandBuffer > m_command_buffer;
|
||||
std::vector< vk::CommandBuffer > m_command_buffer {};
|
||||
|
||||
std::vector< TracyVkCtx > m_tracy_ctx;
|
||||
std::vector< TracyVkCtx > m_tracy_ctx {};
|
||||
|
||||
void createCommandBuffers();
|
||||
void freeCommandBuffers();
|
||||
void recreateSwapchain();
|
||||
|
||||
uint32_t current_image_idx {};
|
||||
int current_frame_idx { 0 };
|
||||
std::uint8_t current_frame_idx { 0 };
|
||||
bool is_frame_started { false };
|
||||
|
||||
public:
|
||||
|
||||
int getFrameIndex() const
|
||||
DescriptorSet& getGBufferDescriptor( std::uint8_t frame_idx ) const
|
||||
{
|
||||
return m_swapchain->getGBufferDescriptor( frame_idx );
|
||||
}
|
||||
|
||||
std::uint8_t getFrameIndex() const
|
||||
{
|
||||
assert( is_frame_started && "Cannot get frame index while frame not in progress" );
|
||||
return current_frame_idx;
|
||||
@@ -50,28 +54,36 @@ namespace fgl::engine
|
||||
|
||||
bool isFrameInProgress() const { return is_frame_started; }
|
||||
|
||||
VkCommandBuffer getCurrentCommandbuffer() const
|
||||
vk::CommandBuffer getCurrentCommandbuffer() const
|
||||
{
|
||||
assert( is_frame_started && "Cannot get command buffer while frame not in progress" );
|
||||
return m_command_buffer[ current_frame_idx ];
|
||||
}
|
||||
|
||||
TracyVkCtx getCurrentTracyCTX() const { return m_tracy_ctx[ current_image_idx ]; }
|
||||
TracyVkCtx getCurrentTracyCTX() const
|
||||
{
|
||||
#ifdef TRACY_ENABLED
|
||||
return m_tracy_ctx[ current_frame_idx ];
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
VkRenderPass getSwapChainRenderPass() const { return m_swapchain->getRenderPass(); }
|
||||
vk::RenderPass getSwapChainRenderPass() const { return m_swapchain->getRenderPass(); }
|
||||
|
||||
float getAspectRatio() const { return m_swapchain->extentAspectRatio(); }
|
||||
|
||||
VkCommandBuffer beginFrame();
|
||||
vk::CommandBuffer beginFrame();
|
||||
void endFrame();
|
||||
void beginSwapchainRendererPass( VkCommandBuffer buffer );
|
||||
void endSwapchainRendererPass( VkCommandBuffer buffer );
|
||||
void beginSwapchainRendererPass( vk::CommandBuffer buffer );
|
||||
void endSwapchainRendererPass( vk::CommandBuffer buffer );
|
||||
|
||||
Renderer( Window& window, Device& device );
|
||||
Renderer( Window& window );
|
||||
~Renderer();
|
||||
Renderer( Renderer&& other ) = delete;
|
||||
Renderer( const Renderer& other ) = delete;
|
||||
Renderer& operator=( const Renderer& other ) = delete;
|
||||
void nextPass();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
173
src/engine/Subpass.hpp
Normal file
173
src/engine/Subpass.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// Created by kj16609 on 12/30/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Attachment.hpp"
|
||||
#include "engine/concepts/is_attachment.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
concept is_subpass = requires( T a ) {
|
||||
{
|
||||
a.description()
|
||||
} -> std::same_as< vk::SubpassDescription >;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
using UnwrapInputAttachment = std::conditional_t< is_input_attachment< T >, typename T::Attachment, T >;
|
||||
|
||||
template <
|
||||
vk::PipelineBindPoint bind_point,
|
||||
is_wrapped_attachment Attachment,
|
||||
is_wrapped_attachment... Attachments >
|
||||
class Subpass
|
||||
{
|
||||
//! Set to true if the first attachment is a valid depth stencil attachment
|
||||
constexpr static bool has_depth_stencil_attachment { Attachment::m_layout
|
||||
== vk::ImageLayout::eDepthStencilAttachmentOptimal };
|
||||
|
||||
constexpr static std::uint32_t attachment_count { ( ( !is_input_attachment< Attachments > ? 1 : 0 ) + ... )
|
||||
+ ( has_depth_stencil_attachment ? 0 : 1 ) };
|
||||
|
||||
constexpr static std::uint32_t input_attachments { ( ( is_input_attachment< Attachments > ? 1 : 0 ) + ... ) };
|
||||
|
||||
std::uint32_t index;
|
||||
std::array< vk::AttachmentReference, attachment_count > attachment_references {};
|
||||
vk::AttachmentReference depth_stencil_reference {};
|
||||
|
||||
std::array< vk::AttachmentReference, input_attachments > input_attachment_references {};
|
||||
|
||||
vk::SubpassDescription subpass_description {};
|
||||
|
||||
std::vector< vk::SubpassDependency > dependencies {};
|
||||
|
||||
std::vector< vk::WriteDescriptorSet > m_descriptor_writes {};
|
||||
|
||||
std::uint32_t current_input_idx { 0 };
|
||||
std::uint32_t current_attachment_idx { 0 };
|
||||
|
||||
template < is_wrapped_attachment T >
|
||||
void registerAttachment( UnwrappedAttachment< T >& attachment )
|
||||
{
|
||||
if constexpr ( is_input_attachment< T > )
|
||||
{
|
||||
input_attachment_references[ current_input_idx++ ] = { attachment.getIndex(), T::m_layout };
|
||||
}
|
||||
else if constexpr ( is_used_attachment< T > )
|
||||
{
|
||||
attachment_references[ current_attachment_idx++ ] = { attachment.getIndex(), T::m_layout };
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
std::uint32_t getIndex() const { return index; }
|
||||
|
||||
Subpass(
|
||||
const std::uint32_t idx,
|
||||
UnwrappedAttachment< Attachment >& first_attachment,
|
||||
UnwrappedAttachment< Attachments >&... attachments ) :
|
||||
index( idx )
|
||||
{
|
||||
//TODO: Redo this check. As this will prevent any input attachments from being used as a depth input (Which may be done?)
|
||||
static_assert(
|
||||
( ( Attachments::m_layout != vk::ImageLayout::eDepthStencilAttachmentOptimal ) && ... ),
|
||||
"Depth stencil must be at attachment index 0 in template parameters" );
|
||||
|
||||
if constexpr ( has_depth_stencil_attachment )
|
||||
{
|
||||
depth_stencil_reference.layout = Attachment::m_layout;
|
||||
depth_stencil_reference.attachment = first_attachment.getIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
registerAttachment< Attachment >( first_attachment );
|
||||
}
|
||||
|
||||
( ( registerAttachment< Attachments >( attachments ) ), ... );
|
||||
|
||||
subpass_description.pipelineBindPoint = bind_point;
|
||||
subpass_description.pColorAttachments = attachment_references.data();
|
||||
subpass_description.colorAttachmentCount = static_cast< std::uint32_t >( attachment_references.size() );
|
||||
|
||||
subpass_description.pDepthStencilAttachment =
|
||||
has_depth_stencil_attachment ? &depth_stencil_reference : nullptr;
|
||||
|
||||
subpass_description.pInputAttachments = input_attachment_references.data();
|
||||
subpass_description.inputAttachmentCount =
|
||||
static_cast< std::uint32_t >( input_attachment_references.size() );
|
||||
}
|
||||
|
||||
vk::SubpassDescription description() { return subpass_description; }
|
||||
|
||||
friend class RenderPass;
|
||||
|
||||
void registerDependency(
|
||||
std::uint32_t src_subpass,
|
||||
std::uint32_t dst_subpass,
|
||||
vk::AccessFlags src_access_flags,
|
||||
vk::PipelineStageFlags src_stage_flags,
|
||||
vk::AccessFlags dst_access_flags,
|
||||
vk::PipelineStageFlags dst_stage_flags,
|
||||
const vk::DependencyFlags dependency_flags )
|
||||
{
|
||||
vk::SubpassDependency subpass_dependency {};
|
||||
subpass_dependency.srcSubpass = src_subpass;
|
||||
subpass_dependency.dstSubpass = dst_subpass;
|
||||
|
||||
subpass_dependency.srcStageMask = src_stage_flags;
|
||||
subpass_dependency.srcAccessMask = src_access_flags;
|
||||
|
||||
subpass_dependency.dstStageMask = dst_stage_flags;
|
||||
subpass_dependency.dstAccessMask = dst_access_flags;
|
||||
|
||||
subpass_dependency.dependencyFlags = dependency_flags;
|
||||
|
||||
dependencies.push_back( subpass_dependency );
|
||||
}
|
||||
|
||||
template < is_subpass SrcT >
|
||||
void registerDependency(
|
||||
SrcT& parent,
|
||||
const vk::AccessFlags src_access_flags,
|
||||
const vk::PipelineStageFlags src_stage_flags,
|
||||
const vk::AccessFlags dst_access_flags,
|
||||
const vk::PipelineStageFlags dst_stage_flags,
|
||||
const vk::DependencyFlags dependency_flags )
|
||||
{
|
||||
registerDependency(
|
||||
parent.getIndex(),
|
||||
this->index,
|
||||
src_access_flags,
|
||||
src_stage_flags,
|
||||
dst_access_flags,
|
||||
dst_stage_flags,
|
||||
dependency_flags );
|
||||
}
|
||||
|
||||
void registerExternalDependency(
|
||||
const vk::AccessFlags access_flags,
|
||||
const vk::PipelineStageFlags stage_flags,
|
||||
const vk::DependencyFlags dependency_flags = {} )
|
||||
{
|
||||
registerDependency(
|
||||
VK_SUBPASS_EXTERNAL,
|
||||
this->index,
|
||||
access_flags,
|
||||
stage_flags,
|
||||
vk::AccessFlags( 0 ),
|
||||
stage_flags,
|
||||
dependency_flags );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "SwapChain.hpp"
|
||||
|
||||
#include "Attachment.hpp"
|
||||
#include "RenderPass.hpp"
|
||||
#include "Subpass.hpp"
|
||||
|
||||
// std
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
@@ -12,13 +16,12 @@
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
SwapChain::SwapChain( Device& deviceRef, VkExtent2D extent ) : device { deviceRef }, windowExtent { extent }
|
||||
SwapChain::SwapChain( vk::Extent2D extent ) : windowExtent( extent )
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
SwapChain::SwapChain( Device& deviceRef, VkExtent2D extent, std::shared_ptr< SwapChain > previous ) :
|
||||
device( deviceRef ),
|
||||
SwapChain::SwapChain( vk::Extent2D extent, std::shared_ptr< SwapChain > previous ) :
|
||||
windowExtent( extent ),
|
||||
old_swap_chain( previous )
|
||||
{
|
||||
@@ -29,79 +32,68 @@ namespace fgl::engine
|
||||
void SwapChain::init()
|
||||
{
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createRenderPass();
|
||||
createDepthResources();
|
||||
createFramebuffers();
|
||||
createSyncObjects();
|
||||
}
|
||||
|
||||
SwapChain::~SwapChain()
|
||||
{
|
||||
for ( auto imageView : m_swap_chain_views )
|
||||
{
|
||||
vkDestroyImageView( device.device(), imageView, nullptr );
|
||||
}
|
||||
m_swap_chain_views.clear();
|
||||
|
||||
if ( swapChain != nullptr )
|
||||
{
|
||||
vkDestroySwapchainKHR( device.device(), swapChain, nullptr );
|
||||
vkDestroySwapchainKHR( Device::getInstance().device(), swapChain, nullptr );
|
||||
swapChain = nullptr;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < m_depth_images.size(); i++ )
|
||||
{
|
||||
vkDestroyImageView( device.device(), m_depth_image_views[ i ], nullptr );
|
||||
vkDestroyImage( device.device(), m_depth_images[ i ], nullptr );
|
||||
vkFreeMemory( device.device(), m_depth_memory[ i ], nullptr );
|
||||
}
|
||||
|
||||
for ( auto framebuffer : m_swap_chain_buffers )
|
||||
{
|
||||
vkDestroyFramebuffer( device.device(), framebuffer, nullptr );
|
||||
vkDestroyFramebuffer( Device::getInstance().device(), framebuffer, nullptr );
|
||||
}
|
||||
|
||||
vkDestroyRenderPass( device.device(), m_render_pass, nullptr );
|
||||
vkDestroyRenderPass( Device::getInstance().device(), m_render_pass, nullptr );
|
||||
|
||||
// cleanup synchronization objects
|
||||
for ( size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++ )
|
||||
{
|
||||
vkDestroySemaphore( device.device(), renderFinishedSemaphores[ i ], nullptr );
|
||||
vkDestroySemaphore( device.device(), imageAvailableSemaphores[ i ], nullptr );
|
||||
vkDestroyFence( device.device(), inFlightFences[ i ], nullptr );
|
||||
vkDestroySemaphore( Device::getInstance().device(), renderFinishedSemaphores[ i ], nullptr );
|
||||
vkDestroySemaphore( Device::getInstance().device(), imageAvailableSemaphores[ i ], nullptr );
|
||||
vkDestroyFence( Device::getInstance().device(), inFlightFences[ i ], nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
VkResult SwapChain::acquireNextImage( uint32_t* imageIndex )
|
||||
vk::Result SwapChain::acquireNextImage( uint32_t* imageIndex )
|
||||
{
|
||||
vkWaitForFences(
|
||||
device.device(), 1, &inFlightFences[ currentFrame ], VK_TRUE, std::numeric_limits< uint64_t >::max() );
|
||||
if ( Device::getInstance()
|
||||
.device()
|
||||
.waitForFences( 1, &inFlightFences[ currentFrame ], VK_TRUE, std::numeric_limits< uint64_t >::max() )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to wait for fences!" );
|
||||
|
||||
VkResult result = vkAcquireNextImageKHR(
|
||||
device.device(),
|
||||
vk::Result result { Device::getInstance().device().acquireNextImageKHR(
|
||||
swapChain,
|
||||
std::numeric_limits< uint64_t >::max(),
|
||||
imageAvailableSemaphores[ currentFrame ], // must be a not signaled semaphore
|
||||
VK_NULL_HANDLE,
|
||||
imageIndex );
|
||||
imageIndex ) };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VkResult SwapChain::submitCommandBuffers( const VkCommandBuffer* buffers, uint32_t* imageIndex )
|
||||
vk::Result SwapChain::submitCommandBuffers( const vk::CommandBuffer* buffers, uint32_t* imageIndex )
|
||||
{
|
||||
if ( imagesInFlight[ *imageIndex ] != VK_NULL_HANDLE )
|
||||
{
|
||||
vkWaitForFences( device.device(), 1, &imagesInFlight[ *imageIndex ], VK_TRUE, UINT64_MAX );
|
||||
if ( Device::getInstance().device().waitForFences(
|
||||
1, &imagesInFlight[ *imageIndex ], VK_TRUE, std::numeric_limits< uint64_t >::max() )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to wait for fences!" );
|
||||
}
|
||||
imagesInFlight[ *imageIndex ] = inFlightFences[ currentFrame ];
|
||||
|
||||
VkSubmitInfo submitInfo = {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
vk::SubmitInfo submitInfo {};
|
||||
|
||||
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[ currentFrame ] };
|
||||
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
vk::Semaphore waitSemaphores[] = { imageAvailableSemaphores[ currentFrame ] };
|
||||
vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||
submitInfo.pWaitDstStageMask = waitStages;
|
||||
@@ -109,42 +101,50 @@ namespace fgl::engine
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = buffers;
|
||||
|
||||
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[ currentFrame ] };
|
||||
vk::Semaphore signalSemaphores[] = { renderFinishedSemaphores[ currentFrame ] };
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
vkResetFences( device.device(), 1, &inFlightFences[ currentFrame ] );
|
||||
if ( vkQueueSubmit( device.graphicsQueue(), 1, &submitInfo, inFlightFences[ currentFrame ] ) != VK_SUCCESS )
|
||||
if ( Device::getInstance().device().resetFences( 1, &inFlightFences[ currentFrame ] ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to reset fences!" );
|
||||
|
||||
if ( Device::getInstance().graphicsQueue().submit( 1, &submitInfo, inFlightFences[ currentFrame ] )
|
||||
!= vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to submit draw command buffer!" );
|
||||
}
|
||||
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
vk::PresentInfoKHR presentInfo = {};
|
||||
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = signalSemaphores;
|
||||
|
||||
VkSwapchainKHR swapChains[] = { swapChain };
|
||||
vk::SwapchainKHR swapChains[] = { swapChain };
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = swapChains;
|
||||
|
||||
presentInfo.pImageIndices = imageIndex;
|
||||
|
||||
auto result = vkQueuePresentKHR( device.presentQueue(), &presentInfo );
|
||||
if ( auto present_result = Device::getInstance().presentQueue().presentKHR( &presentInfo );
|
||||
present_result != vk::Result::eSuccess )
|
||||
{
|
||||
if ( present_result == vk::Result::eSuboptimalKHR ) return vk::Result::eSuboptimalKHR;
|
||||
|
||||
throw std::runtime_error( "failed to present swap chain image!" );
|
||||
}
|
||||
|
||||
currentFrame = ( currentFrame + 1 ) % MAX_FRAMES_IN_FLIGHT;
|
||||
|
||||
return result;
|
||||
return vk::Result::eSuccess;
|
||||
}
|
||||
|
||||
void SwapChain::createSwapChain()
|
||||
{
|
||||
SwapChainSupportDetails swapChainSupport = device.getSwapChainSupport();
|
||||
SwapChainSupportDetails swapChainSupport { Device::getInstance().getSwapChainSupport() };
|
||||
|
||||
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat( swapChainSupport.formats );
|
||||
VkPresentModeKHR presentMode = chooseSwapPresentMode( swapChainSupport.presentModes );
|
||||
VkExtent2D extent = chooseSwapExtent( swapChainSupport.capabilities );
|
||||
vk::SurfaceFormatKHR surfaceFormat { chooseSwapSurfaceFormat( swapChainSupport.formats ) };
|
||||
vk::PresentModeKHR presentMode { chooseSwapPresentMode( swapChainSupport.presentModes ) };
|
||||
vk::Extent2D extent { chooseSwapExtent( swapChainSupport.capabilities ) };
|
||||
|
||||
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
|
||||
if ( swapChainSupport.capabilities.maxImageCount > 0
|
||||
@@ -153,153 +153,210 @@ namespace fgl::engine
|
||||
imageCount = swapChainSupport.capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
createInfo.surface = device.surface();
|
||||
vk::SwapchainCreateInfoKHR createInfo = {};
|
||||
createInfo.surface = Device::getInstance().surface();
|
||||
|
||||
createInfo.minImageCount = imageCount;
|
||||
createInfo.imageFormat = surfaceFormat.format;
|
||||
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||
createInfo.imageExtent = extent;
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
createInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
|
||||
QueueFamilyIndices indices = device.findPhysicalQueueFamilies();
|
||||
QueueFamilyIndices indices = Device::getInstance().findPhysicalQueueFamilies();
|
||||
uint32_t queueFamilyIndices[] = { indices.graphicsFamily, indices.presentFamily };
|
||||
|
||||
if ( indices.graphicsFamily != indices.presentFamily )
|
||||
{
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
createInfo.imageSharingMode = vk::SharingMode::eConcurrent;
|
||||
createInfo.queueFamilyIndexCount = 2;
|
||||
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
||||
}
|
||||
else
|
||||
{
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
createInfo.imageSharingMode = vk::SharingMode::eExclusive;
|
||||
createInfo.queueFamilyIndexCount = 0; // Optional
|
||||
createInfo.pQueueFamilyIndices = nullptr; // Optional
|
||||
}
|
||||
|
||||
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
|
||||
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||
|
||||
createInfo.presentMode = presentMode;
|
||||
createInfo.clipped = VK_TRUE;
|
||||
|
||||
createInfo.oldSwapchain = old_swap_chain == nullptr ? VK_NULL_HANDLE : old_swap_chain->swapChain;
|
||||
|
||||
if ( vkCreateSwapchainKHR( device.device(), &createInfo, nullptr, &swapChain ) != VK_SUCCESS )
|
||||
if ( Device::getInstance().device().createSwapchainKHR( &createInfo, nullptr, &swapChain )
|
||||
!= vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to create swap chain!" );
|
||||
}
|
||||
|
||||
// we only specified a minimum number of images in the swap chain, so the implementation is
|
||||
// allowed to create a swap chain with more. That's why we'll first query the final number of
|
||||
// images with vkGetSwapchainImagesKHR, then resize the container and finally call it again to
|
||||
// retrieve the handles.
|
||||
vkGetSwapchainImagesKHR( device.device(), swapChain, &imageCount, nullptr );
|
||||
m_swap_chain_images.resize( imageCount );
|
||||
vkGetSwapchainImagesKHR( device.device(), swapChain, &imageCount, m_swap_chain_images.data() );
|
||||
std::vector< vk::Image > swap_chain_images {
|
||||
Device::getInstance().device().getSwapchainImagesKHR( swapChain )
|
||||
};
|
||||
|
||||
for ( std::uint64_t i = 0; i < swap_chain_images.size(); i++ )
|
||||
{
|
||||
m_swap_chain_images
|
||||
.emplace_back( extent, surfaceFormat.format, swap_chain_images[ i ], createInfo.imageUsage );
|
||||
}
|
||||
|
||||
m_swap_chain_format = surfaceFormat.format;
|
||||
m_swap_chain_extent = extent;
|
||||
}
|
||||
|
||||
void SwapChain::createImageViews()
|
||||
{
|
||||
m_swap_chain_views.resize( m_swap_chain_images.size() );
|
||||
for ( size_t i = 0; i < m_swap_chain_images.size(); i++ )
|
||||
{
|
||||
VkImageViewCreateInfo viewInfo {};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = m_swap_chain_images[ i ];
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = m_swap_chain_format;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
if ( vkCreateImageView( device.device(), &viewInfo, nullptr, &m_swap_chain_views[ i ] ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create texture image view!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwapChain::createRenderPass()
|
||||
{
|
||||
VkAttachmentDescription depthAttachment {};
|
||||
depthAttachment.format = findDepthFormat();
|
||||
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
//Present attachment
|
||||
ColoredPresentAttachment colorAttachment { getSwapChainImageFormat() };
|
||||
|
||||
VkAttachmentReference depthAttachmentRef {};
|
||||
depthAttachmentRef.attachment = 1;
|
||||
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentDescription colorAttachment = {};
|
||||
colorAttachment.format = getSwapChainImageFormat();
|
||||
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef = {};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass = {};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
subpass.pDepthStencilAttachment = &depthAttachmentRef;
|
||||
|
||||
VkSubpassDependency dependency = {};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.srcStageMask =
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.dstStageMask =
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
std::array< VkAttachmentDescription, 2 > attachments = { colorAttachment, depthAttachment };
|
||||
VkRenderPassCreateInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = static_cast< uint32_t >( attachments.size() );
|
||||
renderPassInfo.pAttachments = attachments.data();
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
renderPassInfo.dependencyCount = 1;
|
||||
renderPassInfo.pDependencies = &dependency;
|
||||
|
||||
if ( vkCreateRenderPass( device.device(), &renderPassInfo, nullptr, &m_render_pass ) != VK_SUCCESS )
|
||||
for ( int i = 0; i < imageCount(); ++i )
|
||||
{
|
||||
throw std::runtime_error( "failed to create render pass!" );
|
||||
m_swap_chain_images[ i ].setName( "SwapChainImage: " + std::to_string( i ) );
|
||||
}
|
||||
|
||||
colorAttachment.linkImages( m_swap_chain_images );
|
||||
|
||||
// G-Buffer
|
||||
ColorAttachment g_buffer_position { vk::Format::eR16G16B16A16Sfloat };
|
||||
ColorAttachment g_buffer_normal { vk::Format::eR16G16B16A16Sfloat };
|
||||
ColorAttachment g_buffer_albedo { vk::Format::eR8G8B8A8Unorm };
|
||||
|
||||
g_buffer_position.createResources( imageCount(), getSwapChainExtent() );
|
||||
g_buffer_normal.createResources( imageCount(), getSwapChainExtent() );
|
||||
g_buffer_albedo.createResources( imageCount(), getSwapChainExtent() );
|
||||
|
||||
g_buffer_position.setClear( vk::ClearColorValue( std::array< float, 4 > { 0.0f, 0.0f, 0.0f, 0.0f } ) );
|
||||
g_buffer_normal.setClear( vk::ClearColorValue( std::array< float, 4 > { 0.0f, 0.0f, 0.0f, 0.0f } ) );
|
||||
g_buffer_albedo.setClear( vk::ClearColorValue( std::array< float, 4 > { 0.0f, 0.0f, 0.0f, 0.0f } ) );
|
||||
|
||||
for ( int i = 0; i < imageCount(); ++i )
|
||||
{
|
||||
g_buffer_position.m_attachment_resources.m_images[ i ]
|
||||
->setName( "GBufferPosition: " + std::to_string( i ) );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < imageCount(); ++i )
|
||||
{
|
||||
g_buffer_normal.m_attachment_resources.m_images[ i ]->setName( "GBufferNormal: " + std::to_string( i ) );
|
||||
}
|
||||
for ( int i = 0; i < imageCount(); ++i )
|
||||
{
|
||||
g_buffer_albedo.m_attachment_resources.m_images[ i ]->setName( "GBufferAlbedo: " + std::to_string( i ) );
|
||||
}
|
||||
|
||||
RenderPass render_pass {};
|
||||
|
||||
DepthAttachment depthAttachment { findDepthFormat() };
|
||||
depthAttachment.createResources( imageCount(), getSwapChainExtent() );
|
||||
depthAttachment.setClear( vk::ClearDepthStencilValue( 1.0f, 0 ) );
|
||||
|
||||
render_pass.registerAttachments(
|
||||
colorAttachment, depthAttachment, g_buffer_position, g_buffer_normal, g_buffer_albedo );
|
||||
|
||||
for ( int i = 0; i < SwapChain::MAX_FRAMES_IN_FLIGHT; ++i )
|
||||
{
|
||||
auto set { std::make_unique< DescriptorSet >( GBufferDescriptorSet::createLayout() ) };
|
||||
set->setMaxIDX( 2 );
|
||||
|
||||
set->bindAttachment(
|
||||
0,
|
||||
*( g_buffer_position.resources().m_image_views[ i ].get() ),
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
set->bindAttachment(
|
||||
1, *( g_buffer_normal.resources().m_image_views[ i ].get() ), vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
set->bindAttachment(
|
||||
2, *( g_buffer_albedo.resources().m_image_views[ i ].get() ), vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
set->update();
|
||||
|
||||
m_gbuffer_descriptor_set[ i ] = std::move( set );
|
||||
}
|
||||
|
||||
static_assert( is_attachment< ColoredPresentAttachment > );
|
||||
static_assert( is_attachment< DepthAttachment > );
|
||||
|
||||
Subpass<
|
||||
vk::PipelineBindPoint::eGraphics,
|
||||
UsedAttachment< DepthAttachment, vk::ImageLayout::eDepthStencilAttachmentOptimal >,
|
||||
UsedAttachment< ColoredPresentAttachment, vk::ImageLayout::eColorAttachmentOptimal >,
|
||||
UsedAttachment< ColorAttachment, vk::ImageLayout::eColorAttachmentOptimal >,
|
||||
UsedAttachment< ColorAttachment, vk::ImageLayout::eColorAttachmentOptimal >,
|
||||
UsedAttachment< ColorAttachment, vk::ImageLayout::eColorAttachmentOptimal > >
|
||||
g_buffer_subpass {
|
||||
0, depthAttachment, colorAttachment, g_buffer_position, g_buffer_normal, g_buffer_albedo
|
||||
};
|
||||
|
||||
g_buffer_subpass.registerExternalDependency(
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests );
|
||||
|
||||
g_buffer_subpass.registerExternalDependency(
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::PipelineStageFlagBits::eColorAttachmentOutput );
|
||||
|
||||
Subpass<
|
||||
vk::PipelineBindPoint::eGraphics,
|
||||
UsedAttachment< DepthAttachment, vk::ImageLayout::eDepthStencilAttachmentOptimal >,
|
||||
UsedAttachment< ColoredPresentAttachment, vk::ImageLayout::eColorAttachmentOptimal >,
|
||||
InputAttachment< ColorAttachment, vk::ImageLayout::eShaderReadOnlyOptimal >,
|
||||
InputAttachment< ColorAttachment, vk::ImageLayout::eShaderReadOnlyOptimal >,
|
||||
InputAttachment< ColorAttachment, vk::ImageLayout::eShaderReadOnlyOptimal > >
|
||||
present_subpass {
|
||||
1, depthAttachment, colorAttachment, g_buffer_position, g_buffer_normal, g_buffer_albedo
|
||||
};
|
||||
|
||||
/*
|
||||
// This dependency transitions the input attachment from color attachment to input attachment read
|
||||
dependencies[ 2 ].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dependencies[ 2 ].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
dependencies[ 2 ].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
dependencies[ 2 ].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
|
||||
dependencies[ 2 ].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
*/
|
||||
|
||||
present_subpass.registerDependency(
|
||||
g_buffer_subpass,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eInputAttachmentRead,
|
||||
vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::DependencyFlagBits::eByRegion );
|
||||
|
||||
present_subpass.registerDependency(
|
||||
present_subpass.getIndex(),
|
||||
VK_SUBPASS_EXTERNAL,
|
||||
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eMemoryRead,
|
||||
vk::PipelineStageFlagBits::eBottomOfPipe,
|
||||
vk::DependencyFlagBits::eByRegion );
|
||||
|
||||
render_pass.registerSubpass( g_buffer_subpass );
|
||||
render_pass.registerSubpass( present_subpass );
|
||||
|
||||
m_render_pass = render_pass.create();
|
||||
|
||||
m_render_pass_resources = render_pass.resources( imageCount() );
|
||||
m_clear_values = render_pass.getClearValues();
|
||||
}
|
||||
|
||||
void SwapChain::createFramebuffers()
|
||||
{
|
||||
m_swap_chain_buffers.resize( imageCount() );
|
||||
for ( size_t i = 0; i < imageCount(); i++ )
|
||||
for ( uint8_t i = 0; i < imageCount(); i++ )
|
||||
{
|
||||
std::array< VkImageView, 2 > attachments = { m_swap_chain_views[ i ], m_depth_image_views[ i ] };
|
||||
//TODO: Fix image shit. It's dying because the image is being nuked
|
||||
std::vector< vk::ImageView > attachments { m_render_pass_resources->forFrame( i ) };
|
||||
|
||||
VkExtent2D swapChainExtent = getSwapChainExtent();
|
||||
VkFramebufferCreateInfo framebufferInfo = {};
|
||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
//Fill attachments for this frame
|
||||
const vk::Extent2D swapChainExtent { getSwapChainExtent() };
|
||||
vk::FramebufferCreateInfo framebufferInfo = {};
|
||||
framebufferInfo.renderPass = m_render_pass;
|
||||
framebufferInfo.attachmentCount = static_cast< uint32_t >( attachments.size() );
|
||||
framebufferInfo.pAttachments = attachments.data();
|
||||
@@ -307,63 +364,16 @@ namespace fgl::engine
|
||||
framebufferInfo.height = swapChainExtent.height;
|
||||
framebufferInfo.layers = 1;
|
||||
|
||||
if ( vkCreateFramebuffer( device.device(), &framebufferInfo, nullptr, &m_swap_chain_buffers[ i ] )
|
||||
!= VK_SUCCESS )
|
||||
if ( Device::getInstance()
|
||||
.device()
|
||||
.createFramebuffer( &framebufferInfo, nullptr, &( m_swap_chain_buffers[ i ] ) )
|
||||
!= vk::Result::eSuccess )
|
||||
{
|
||||
throw std::runtime_error( "failed to create framebuffer!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwapChain::createDepthResources()
|
||||
{
|
||||
VkFormat depthFormat = findDepthFormat();
|
||||
m_swap_chain_depth_format = depthFormat;
|
||||
VkExtent2D swapChainExtent = getSwapChainExtent();
|
||||
|
||||
m_depth_images.resize( imageCount() );
|
||||
m_depth_memory.resize( imageCount() );
|
||||
m_depth_image_views.resize( imageCount() );
|
||||
|
||||
for ( int i = 0; i < m_depth_images.size(); i++ )
|
||||
{
|
||||
VkImageCreateInfo imageInfo {};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent.width = swapChainExtent.width;
|
||||
imageInfo.extent.height = swapChainExtent.height;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.format = depthFormat;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.flags = 0;
|
||||
|
||||
device.createImageWithInfo(
|
||||
imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_depth_images[ i ], m_depth_memory[ i ] );
|
||||
|
||||
VkImageViewCreateInfo viewInfo {};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = m_depth_images[ i ];
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = depthFormat;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
if ( vkCreateImageView( device.device(), &viewInfo, nullptr, &m_depth_image_views[ i ] ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create texture image view!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwapChain::createSyncObjects()
|
||||
{
|
||||
imageAvailableSemaphores.resize( MAX_FRAMES_IN_FLIGHT );
|
||||
@@ -371,32 +381,32 @@ namespace fgl::engine
|
||||
inFlightFences.resize( MAX_FRAMES_IN_FLIGHT );
|
||||
imagesInFlight.resize( imageCount(), VK_NULL_HANDLE );
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreInfo = {};
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
vk::SemaphoreCreateInfo semaphoreInfo {};
|
||||
|
||||
VkFenceCreateInfo fenceInfo = {};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
vk::FenceCreateInfo fenceInfo {};
|
||||
fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
|
||||
|
||||
for ( size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++ )
|
||||
{
|
||||
if ( vkCreateSemaphore( device.device(), &semaphoreInfo, nullptr, &imageAvailableSemaphores[ i ] )
|
||||
!= VK_SUCCESS
|
||||
|| vkCreateSemaphore( device.device(), &semaphoreInfo, nullptr, &renderFinishedSemaphores[ i ] )
|
||||
!= VK_SUCCESS
|
||||
|| vkCreateFence( device.device(), &fenceInfo, nullptr, &inFlightFences[ i ] ) != VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "failed to create synchronization objects for a frame!" );
|
||||
}
|
||||
auto vk_device { Device::getInstance().device() };
|
||||
if ( vk_device.createSemaphore( &semaphoreInfo, nullptr, &imageAvailableSemaphores[ i ] )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to create image available semaphore!" );
|
||||
if ( vk_device.createSemaphore( &semaphoreInfo, nullptr, &renderFinishedSemaphores[ i ] )
|
||||
!= vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to create render finished semaphore!" );
|
||||
if ( vk_device.createFence( &fenceInfo, nullptr, &inFlightFences[ i ] ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "failed to create in flight fence!" );
|
||||
}
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR SwapChain::chooseSwapSurfaceFormat( const std::vector< VkSurfaceFormatKHR >& availableFormats )
|
||||
vk::SurfaceFormatKHR SwapChain::chooseSwapSurfaceFormat( const std::vector< vk::SurfaceFormatKHR >&
|
||||
availableFormats )
|
||||
{
|
||||
for ( const auto& availableFormat : availableFormats )
|
||||
{
|
||||
if ( availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB
|
||||
&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR )
|
||||
if ( availableFormat.format == vk::Format::eB8G8R8A8Srgb
|
||||
&& availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear )
|
||||
{
|
||||
return availableFormat;
|
||||
}
|
||||
@@ -405,11 +415,12 @@ namespace fgl::engine
|
||||
return availableFormats[ 0 ];
|
||||
}
|
||||
|
||||
VkPresentModeKHR SwapChain::chooseSwapPresentMode( const std::vector< VkPresentModeKHR >& availablePresentModes )
|
||||
vk::PresentModeKHR SwapChain::chooseSwapPresentMode( const std::vector< vk::PresentModeKHR >&
|
||||
availablePresentModes )
|
||||
{
|
||||
for ( const auto& availablePresentMode : availablePresentModes )
|
||||
{
|
||||
if ( availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR )
|
||||
if ( availablePresentMode == vk::PresentModeKHR::eMailbox )
|
||||
{
|
||||
std::cout << "Present mode: Mailbox" << std::endl;
|
||||
return availablePresentMode;
|
||||
@@ -424,10 +435,10 @@ namespace fgl::engine
|
||||
// }
|
||||
|
||||
std::cout << "Present mode: V-Sync" << std::endl;
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
return vk::PresentModeKHR::eFifo;
|
||||
}
|
||||
|
||||
VkExtent2D SwapChain::chooseSwapExtent( const VkSurfaceCapabilitiesKHR& capabilities )
|
||||
vk::Extent2D SwapChain::chooseSwapExtent( const vk::SurfaceCapabilitiesKHR& capabilities )
|
||||
{
|
||||
if ( capabilities.currentExtent.width != std::numeric_limits< uint32_t >::max() )
|
||||
{
|
||||
@@ -435,7 +446,7 @@ namespace fgl::engine
|
||||
}
|
||||
else
|
||||
{
|
||||
VkExtent2D actualExtent = windowExtent;
|
||||
vk::Extent2D actualExtent = windowExtent;
|
||||
actualExtent.width = std::
|
||||
max( capabilities.minImageExtent.width,
|
||||
std::min( capabilities.maxImageExtent.width, actualExtent.width ) );
|
||||
@@ -447,12 +458,12 @@ namespace fgl::engine
|
||||
}
|
||||
}
|
||||
|
||||
VkFormat SwapChain::findDepthFormat()
|
||||
vk::Format SwapChain::findDepthFormat()
|
||||
{
|
||||
return device.findSupportedFormat(
|
||||
{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT );
|
||||
return Device::getInstance().findSupportedFormat(
|
||||
{ vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint },
|
||||
vk::ImageTiling::eOptimal,
|
||||
vk::FormatFeatureFlagBits::eDepthStencilAttachment );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Device.hpp"
|
||||
#include "FrameInfo.hpp"
|
||||
#include "RenderPass.hpp"
|
||||
#include "engine/image/Image.hpp"
|
||||
|
||||
// vulkan headers
|
||||
#include <vulkan/vulkan.h>
|
||||
@@ -12,68 +15,78 @@
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class SwapChain
|
||||
{
|
||||
VkFormat m_swap_chain_format;
|
||||
VkFormat m_swap_chain_depth_format;
|
||||
VkExtent2D m_swap_chain_extent;
|
||||
vk::Format m_swap_chain_format { vk::Format::eUndefined };
|
||||
vk::Format m_swap_chain_depth_format { findDepthFormat() };
|
||||
vk::Extent2D m_swap_chain_extent { 0, 0 };
|
||||
|
||||
std::vector< VkFramebuffer > m_swap_chain_buffers;
|
||||
VkRenderPass m_render_pass;
|
||||
std::vector< vk::Framebuffer > m_swap_chain_buffers {};
|
||||
vk::RenderPass m_render_pass { VK_NULL_HANDLE };
|
||||
std::unique_ptr< RenderPassResources > m_render_pass_resources { nullptr };
|
||||
|
||||
std::vector< VkImage > m_depth_images;
|
||||
std::vector< VkDeviceMemory > m_depth_memory;
|
||||
std::vector< VkImageView > m_depth_image_views;
|
||||
std::vector< VkImage > m_swap_chain_images;
|
||||
std::vector< VkImageView > m_swap_chain_views;
|
||||
std::vector< Image > m_swap_chain_images {};
|
||||
|
||||
Device& device;
|
||||
VkExtent2D windowExtent;
|
||||
vk::Extent2D windowExtent;
|
||||
|
||||
VkSwapchainKHR swapChain;
|
||||
std::shared_ptr< SwapChain > old_swap_chain;
|
||||
vk::SwapchainKHR swapChain { VK_NULL_HANDLE };
|
||||
std::shared_ptr< SwapChain > old_swap_chain {};
|
||||
|
||||
std::vector< VkSemaphore > imageAvailableSemaphores;
|
||||
std::vector< VkSemaphore > renderFinishedSemaphores;
|
||||
std::vector< VkFence > inFlightFences;
|
||||
std::vector< VkFence > imagesInFlight;
|
||||
std::vector< vk::Semaphore > imageAvailableSemaphores {};
|
||||
std::vector< vk::Semaphore > renderFinishedSemaphores {};
|
||||
std::vector< vk::Fence > inFlightFences {};
|
||||
std::vector< vk::Fence > imagesInFlight {};
|
||||
size_t currentFrame { 0 };
|
||||
|
||||
std::vector< vk::ClearValue > m_clear_values {};
|
||||
|
||||
void init();
|
||||
void createSwapChain();
|
||||
void createImageViews();
|
||||
void createDepthResources();
|
||||
void createRenderPass();
|
||||
void createFramebuffers();
|
||||
void createSyncObjects();
|
||||
|
||||
// Helper functions
|
||||
VkSurfaceFormatKHR chooseSwapSurfaceFormat( const std::vector< VkSurfaceFormatKHR >& availableFormats );
|
||||
VkPresentModeKHR chooseSwapPresentMode( const std::vector< VkPresentModeKHR >& availablePresentModes );
|
||||
VkExtent2D chooseSwapExtent( const VkSurfaceCapabilitiesKHR& capabilities );
|
||||
vk::SurfaceFormatKHR chooseSwapSurfaceFormat( const std::vector< vk::SurfaceFormatKHR >& availableFormats );
|
||||
vk::PresentModeKHR chooseSwapPresentMode( const std::vector< vk::PresentModeKHR >& availablePresentModes );
|
||||
vk::Extent2D chooseSwapExtent( const vk::SurfaceCapabilitiesKHR& capabilities );
|
||||
|
||||
public:
|
||||
|
||||
static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
|
||||
std::vector< vk::ClearValue > getClearValues() const { return m_clear_values; }
|
||||
|
||||
SwapChain( Device& deviceRef, VkExtent2D windowExtent );
|
||||
SwapChain( Device& deviceRef, VkExtent2D windowExtent, std::shared_ptr< SwapChain > previous );
|
||||
DescriptorSet& getGBufferDescriptor( std::uint8_t frame_idx ) const
|
||||
{
|
||||
assert( frame_idx < MAX_FRAMES_IN_FLIGHT && "Frame index out of range" );
|
||||
assert(
|
||||
m_gbuffer_descriptor_set.size() == MAX_FRAMES_IN_FLIGHT && "GBuffer descriptor set not initialized" );
|
||||
return *m_gbuffer_descriptor_set[ frame_idx ];
|
||||
}
|
||||
|
||||
static constexpr std::uint8_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
std::array< std::unique_ptr< DescriptorSet >, MAX_FRAMES_IN_FLIGHT > m_gbuffer_descriptor_set {};
|
||||
|
||||
SwapChain( vk::Extent2D windowExtent );
|
||||
SwapChain( vk::Extent2D windowExtent, std::shared_ptr< SwapChain > previous );
|
||||
~SwapChain();
|
||||
|
||||
SwapChain( const SwapChain& ) = delete;
|
||||
SwapChain& operator=( const SwapChain& ) = delete;
|
||||
|
||||
VkFramebuffer getFrameBuffer( int index ) const { return m_swap_chain_buffers[ index ]; }
|
||||
vk::Framebuffer getFrameBuffer( int index ) const
|
||||
{
|
||||
return m_swap_chain_buffers[ static_cast< std::size_t >( index ) ];
|
||||
}
|
||||
|
||||
VkRenderPass getRenderPass() const { return m_render_pass; }
|
||||
vk::RenderPass getRenderPass() const { return m_render_pass; }
|
||||
|
||||
VkImageView getImageView( int index ) const { return m_swap_chain_views[ index ]; }
|
||||
std::uint8_t imageCount() const { return static_cast< std::uint8_t >( m_swap_chain_images.size() ); }
|
||||
|
||||
size_t imageCount() const { return m_swap_chain_images.size(); }
|
||||
vk::Format getSwapChainImageFormat() const { return m_swap_chain_format; }
|
||||
|
||||
VkFormat getSwapChainImageFormat() const { return m_swap_chain_format; }
|
||||
|
||||
VkExtent2D getSwapChainExtent() const { return m_swap_chain_extent; }
|
||||
vk::Extent2D getSwapChainExtent() const { return m_swap_chain_extent; }
|
||||
|
||||
uint32_t width() const { return m_swap_chain_extent.width; }
|
||||
|
||||
@@ -91,10 +104,10 @@ namespace fgl::engine
|
||||
/ static_cast< float >( m_swap_chain_extent.height );
|
||||
}
|
||||
|
||||
VkFormat findDepthFormat();
|
||||
vk::Format findDepthFormat();
|
||||
|
||||
VkResult acquireNextImage( uint32_t* imageIndex );
|
||||
VkResult submitCommandBuffers( const VkCommandBuffer* buffers, uint32_t* imageIndex );
|
||||
vk::Result acquireNextImage( uint32_t* imageIndex );
|
||||
vk::Result submitCommandBuffers( const vk::CommandBuffer* buffers, uint32_t* imageIndex );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
|
||||
#include "Window.hpp"
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vulkan/vulkan_handles.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "engine/Device.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
Window::Window( const int w, const int h, std::string window_name ) :
|
||||
@@ -14,6 +19,7 @@ namespace fgl::engine
|
||||
m_name( window_name )
|
||||
{
|
||||
initWindow();
|
||||
Device::init( *this );
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
@@ -29,14 +35,18 @@ namespace fgl::engine
|
||||
glfwWindowHint( GLFW_RESIZABLE, GLFW_TRUE );
|
||||
|
||||
m_window = glfwCreateWindow( m_width, m_height, m_name.data(), nullptr, nullptr );
|
||||
//ImGui_ImplGlfw_InitForVulkan( m_window, true );
|
||||
glfwSetWindowUserPointer( m_window, this );
|
||||
glfwSetFramebufferSizeCallback( m_window, framebufferResizeCallback );
|
||||
}
|
||||
|
||||
void Window::createWindowSurface( VkInstance instance, VkSurfaceKHR* surface )
|
||||
vk::SurfaceKHR Window::createWindowSurface( vk::Instance instance )
|
||||
{
|
||||
if ( glfwCreateWindowSurface( instance, m_window, nullptr, surface ) != VK_SUCCESS )
|
||||
VkSurfaceKHR temp_surface { VK_NULL_HANDLE };
|
||||
if ( glfwCreateWindowSurface( instance, m_window, nullptr, &temp_surface ) != VK_SUCCESS )
|
||||
throw std::runtime_error( "Failed to create window surface" );
|
||||
|
||||
return vk::SurfaceKHR( temp_surface );
|
||||
}
|
||||
|
||||
void Window::framebufferResizeCallback( GLFWwindow* glfw_window, int width, int height )
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vulkan/vulkan_handles.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -35,14 +37,14 @@ namespace fgl::engine
|
||||
|
||||
bool shouldClose() { return glfwWindowShouldClose( m_window ); }
|
||||
|
||||
void createWindowSurface( VkInstance instance, VkSurfaceKHR* surface );
|
||||
vk::SurfaceKHR createWindowSurface( vk::Instance instance );
|
||||
|
||||
VkExtent2D getExtent()
|
||||
{
|
||||
return { static_cast< std::uint32_t >( m_width ), static_cast< std::uint32_t >( m_height ) };
|
||||
}
|
||||
|
||||
GLFWwindow* window() { return m_window; }
|
||||
GLFWwindow* window() const { return m_window; }
|
||||
|
||||
Window( const int w, const int h, std::string window_name );
|
||||
Window() = delete;
|
||||
|
||||
42
src/engine/align.hpp
Normal file
42
src/engine/align.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by kj16609 on 12/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
requires std::is_integral_v< T1 > && std::is_integral_v< T2 >
|
||||
constexpr inline T1 align( const T1 operand, const T2 alignment )
|
||||
{
|
||||
if ( alignment == 0 || alignment == 1 ) return operand;
|
||||
|
||||
if ( operand == alignment ) return operand;
|
||||
|
||||
if constexpr ( std::same_as< T1, T2 > )
|
||||
{
|
||||
return ( ( operand + ( alignment - 1 ) ) & ~( alignment - 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( ( operand + ( static_cast< T1 >( alignment ) - 1 ) ) & ~( static_cast< T1 >( alignment ) - 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T1, typename T2, typename... T2s >
|
||||
constexpr inline T1 align( const T1 operand, const T2 alignment, const T2s... alignments )
|
||||
{
|
||||
return align( align( operand, alignment ), alignments... );
|
||||
}
|
||||
|
||||
static_assert( align( 0, 1 ) == 0 );
|
||||
static_assert( align( 123, 256 ) == 256 );
|
||||
static_assert( align( 256, 256 ) == 256 );
|
||||
static_assert( align( 6, 32, 128 ) == 128 );
|
||||
static_assert( align( 6, 1, 1, 6 ) == 6 );
|
||||
|
||||
} // namespace fgl::engine
|
||||
117
src/engine/buffers/Buffer.cpp
Normal file
117
src/engine/buffers/Buffer.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// Created by kj16609 on 12/30/23.
|
||||
//
|
||||
|
||||
#include "Buffer.hpp"
|
||||
|
||||
#include "engine/Device.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
std::unique_ptr< Buffer > global_staging_buffer { nullptr };
|
||||
|
||||
void initGlobalStagingBuffer( std::uint64_t size )
|
||||
{
|
||||
using namespace fgl::literals::size_literals;
|
||||
global_staging_buffer = std::make_unique< Buffer >(
|
||||
size,
|
||||
vk::BufferUsageFlagBits::eTransferSrc,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eDeviceLocal );
|
||||
}
|
||||
|
||||
Buffer& getGlobalStagingBuffer()
|
||||
{
|
||||
assert( global_staging_buffer && "Global staging buffer not initialized" );
|
||||
return *global_staging_buffer.get();
|
||||
}
|
||||
|
||||
BufferHandle::BufferHandle(
|
||||
vk::DeviceSize memory_size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags memory_properties ) :
|
||||
m_memory_size( memory_size ),
|
||||
m_usage( usage ),
|
||||
m_memory_properties( memory_properties )
|
||||
{
|
||||
alloc( memory_size );
|
||||
}
|
||||
|
||||
BufferHandle::~BufferHandle()
|
||||
{
|
||||
dealloc();
|
||||
}
|
||||
|
||||
void BufferHandle::dealloc()
|
||||
{
|
||||
vmaDestroyBuffer( Device::getInstance().allocator(), m_buffer, m_allocation );
|
||||
}
|
||||
|
||||
void BufferHandle::alloc( vk::DeviceSize memory_size )
|
||||
{
|
||||
m_memory_size = memory_size;
|
||||
vk::BufferCreateInfo buffer_info {};
|
||||
buffer_info.pNext = VK_NULL_HANDLE;
|
||||
buffer_info.flags = {};
|
||||
buffer_info.size = m_memory_size;
|
||||
buffer_info.usage = m_usage;
|
||||
buffer_info.sharingMode = vk::SharingMode::eExclusive;
|
||||
buffer_info.queueFamilyIndexCount = 0;
|
||||
buffer_info.pQueueFamilyIndices = VK_NULL_HANDLE;
|
||||
|
||||
VmaAllocationCreateInfo alloc_info {};
|
||||
|
||||
alloc_info.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
|
||||
if ( m_memory_properties & vk::MemoryPropertyFlagBits::eHostVisible )
|
||||
alloc_info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
|
||||
|
||||
if ( m_usage & vk::BufferUsageFlagBits::eTransferSrc )
|
||||
{
|
||||
//Remove VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM BIT if we are transfer src
|
||||
alloc_info.flags &= ~VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
|
||||
|
||||
alloc_info.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||
}
|
||||
|
||||
const VkBufferCreateInfo& vk_buffer_info = buffer_info;
|
||||
VkBuffer buffer { VK_NULL_HANDLE };
|
||||
if ( vmaCreateBuffer(
|
||||
Device::getInstance().allocator(), &vk_buffer_info, &alloc_info, &buffer, &m_allocation, nullptr )
|
||||
!= VK_SUCCESS )
|
||||
{
|
||||
throw std::runtime_error( "Failed to allocate" );
|
||||
}
|
||||
|
||||
m_buffer = buffer;
|
||||
|
||||
vmaGetAllocationInfo( Device::getInstance().allocator(), m_allocation, &m_alloc_info );
|
||||
}
|
||||
|
||||
vk::DeviceSize Buffer::alignment()
|
||||
{
|
||||
vk::DeviceSize size { 0 };
|
||||
|
||||
if ( m_usage & vk::BufferUsageFlagBits::eUniformBuffer )
|
||||
{
|
||||
size = std::max( size, Device::getInstance().m_properties.limits.minUniformBufferOffsetAlignment );
|
||||
}
|
||||
|
||||
if ( m_memory_properties & vk::MemoryPropertyFlagBits::eHostVisible )
|
||||
{
|
||||
size = std::max( size, Device::getInstance().m_properties.limits.nonCoherentAtomSize );
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{}
|
||||
|
||||
Buffer::
|
||||
Buffer( vk::DeviceSize memory_size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags memory_properties ) :
|
||||
m_handle( std::make_shared< BufferHandle >( memory_size, usage, memory_properties ) ),
|
||||
m_usage( usage ),
|
||||
m_memory_properties( memory_properties )
|
||||
{
|
||||
m_free_blocks.insert( m_free_blocks.begin(), { 0, memory_size } );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
273
src/engine/buffers/Buffer.hpp
Normal file
273
src/engine/buffers/Buffer.hpp
Normal file
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// Created by kj16609 on 11/30/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tracy/Tracy.hpp>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "engine/align.hpp"
|
||||
#include "engine/literals/size.hpp"
|
||||
#include "vma/vma_impl.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class BufferHandle;
|
||||
|
||||
struct BufferSuballocationInfo
|
||||
{
|
||||
std::shared_ptr< BufferHandle > buffer;
|
||||
vk::DeviceSize offset;
|
||||
vk::DeviceSize size;
|
||||
};
|
||||
|
||||
//TODO: Dynamic/onDemand resizing of Buffer for suballocations
|
||||
//TODO: Defragmentation
|
||||
|
||||
class Device;
|
||||
|
||||
class BufferHandle
|
||||
{
|
||||
vk::Buffer m_buffer;
|
||||
VmaAllocation m_allocation {};
|
||||
VmaAllocationInfo m_alloc_info;
|
||||
|
||||
vk::DeviceSize m_memory_size;
|
||||
|
||||
vk::BufferUsageFlags m_usage;
|
||||
vk::MemoryPropertyFlags m_memory_properties;
|
||||
|
||||
void alloc( vk::DeviceSize memory_size );
|
||||
void dealloc();
|
||||
|
||||
public:
|
||||
|
||||
BufferHandle() = delete;
|
||||
BufferHandle( const BufferHandle& other ) = delete;
|
||||
BufferHandle& operator=( const BufferHandle& other ) = delete;
|
||||
BufferHandle( BufferHandle&& other ) = delete;
|
||||
BufferHandle& operator=( BufferHandle&& other ) = delete;
|
||||
|
||||
friend class Buffer;
|
||||
|
||||
BufferHandle(
|
||||
vk::DeviceSize memory_size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags memory_properties );
|
||||
|
||||
~BufferHandle();
|
||||
};
|
||||
|
||||
class Buffer
|
||||
{
|
||||
std::shared_ptr< BufferHandle > m_handle;
|
||||
|
||||
public:
|
||||
|
||||
vk::Buffer& getVkBuffer() { return m_handle->m_buffer; }
|
||||
|
||||
vk::BufferUsageFlags m_usage;
|
||||
|
||||
vk::MemoryPropertyFlags m_memory_properties;
|
||||
|
||||
//! Returns the required alignment for this buffer.
|
||||
vk::DeviceSize alignment();
|
||||
|
||||
~Buffer();
|
||||
Buffer() = delete;
|
||||
Buffer( vk::DeviceSize memory_size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags memory_properties );
|
||||
|
||||
Buffer( const Buffer& other ) = delete;
|
||||
Buffer( Buffer&& other ) = delete;
|
||||
Buffer& operator=( const Buffer& other ) = delete;
|
||||
|
||||
private:
|
||||
|
||||
//! @brief List of all active suballocations
|
||||
//! <offset, size>
|
||||
std::map< vk::DeviceSize, vk::DeviceSize > m_suballocations {};
|
||||
|
||||
//! @brief list of any free blocks
|
||||
//! @note All blocks are amalgamated to the largest they can expand to.
|
||||
//! <offset, size>
|
||||
std::vector< std::pair< vk::DeviceSize, vk::DeviceSize > > m_free_blocks {};
|
||||
|
||||
public:
|
||||
|
||||
//! Returns the vulkan buffer handle for this buffer
|
||||
vk::Buffer getBuffer() const { return m_handle->m_buffer; }
|
||||
|
||||
//! Returns the vulkan memory handle for this buffer
|
||||
vk::DeviceMemory getMemory() const { return m_handle->m_alloc_info.deviceMemory; }
|
||||
|
||||
//! Total memory size of the buffer
|
||||
vk::DeviceSize size() const { return m_handle->m_memory_size; }
|
||||
|
||||
void* map( BufferSuballocationInfo& info )
|
||||
{
|
||||
assert(
|
||||
info.offset + info.size <= m_handle->m_memory_size
|
||||
&& "BufferSuballocationT::map() called with invalid size" );
|
||||
assert(
|
||||
m_handle->m_alloc_info.pMappedData
|
||||
&& "BufferSuballocationT::map() called on buffer with no mapped data" );
|
||||
|
||||
return static_cast< std::byte* >( m_handle->m_alloc_info.pMappedData ) + info.offset;
|
||||
}
|
||||
|
||||
bool isMappable() const { return m_handle->m_alloc_info.pMappedData != nullptr; }
|
||||
|
||||
//! Returns a allocation block from this buffer. Block will be aligned with nonUniformBufferOffsetAlignment
|
||||
//! and nonCoherentAtomSize if required (is_uniform_buffer and is_host_visible respectively)
|
||||
/**
|
||||
* @param memory_size Size of each N
|
||||
* @param number of blocks to allocate. (Returns as 1 block with each N being an aligned offset)
|
||||
* @param alignment The alignment to use.
|
||||
* @return
|
||||
*
|
||||
* @note Alignment is forced to be at least the size of the largest alignment required by the device.
|
||||
* (`alignment` vs `nonCoherentAtomSize` vs `minUniformBufferOffsetAlignment`)
|
||||
* @par
|
||||
* @note Alignment for ubo is 0 if the buffer is not a uniform buffer
|
||||
* @par
|
||||
* @note Alignment for atom_size is 0 if buffer is not host visible
|
||||
*/
|
||||
BufferSuballocationInfo suballocate( vk::DeviceSize memory_size, std::uint32_t allignment )
|
||||
{
|
||||
ZoneScoped;
|
||||
//Calculate alignment from alignment, ubo_alignment, and atom_size_alignment
|
||||
memory_size = align( memory_size, alignment() );
|
||||
|
||||
//Find a free space.
|
||||
auto itter = std::find_if(
|
||||
m_free_blocks.begin(),
|
||||
m_free_blocks.end(),
|
||||
[ this, memory_size, allignment ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
|
||||
{
|
||||
auto [ offset, size ] = pair;
|
||||
|
||||
const auto new_offset = align( offset, alignment(), allignment );
|
||||
size -= new_offset - offset;
|
||||
|
||||
return size >= memory_size;
|
||||
} );
|
||||
|
||||
if ( itter == m_free_blocks.end() )
|
||||
{
|
||||
std::cout << "====== !!! OOM !!! ======\n"
|
||||
"====== Allocated Blocks ======\n";
|
||||
|
||||
for ( auto [ offset, size ] : m_free_blocks )
|
||||
{
|
||||
std::cout << "Offset: " << std::hex << offset << " Size: " << std::dec << size << "\n";
|
||||
|
||||
std::cout << "Aligned offset: " << std::hex << align( offset, alignment(), allignment )
|
||||
<< " Size: " << std::dec << size << "\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cout << "=============================\n"
|
||||
<< "Attempted to allocate block of size: "
|
||||
<< fgl::literals::size_literals::to_string( memory_size ) << std::endl;
|
||||
|
||||
throw std::runtime_error( "Failed to find free space" );
|
||||
}
|
||||
|
||||
//Allocate
|
||||
auto [ offset, size ] = *itter;
|
||||
m_free_blocks.erase( itter );
|
||||
|
||||
const auto aligned_offset { align( offset, alignment(), allignment ) };
|
||||
|
||||
if ( aligned_offset != offset )
|
||||
{
|
||||
m_free_blocks.emplace_back( std::make_pair( offset, aligned_offset - offset ) );
|
||||
offset = aligned_offset;
|
||||
size -= aligned_offset - offset;
|
||||
}
|
||||
|
||||
m_suballocations.insert_or_assign( offset, memory_size );
|
||||
|
||||
if ( size - memory_size > 0 )
|
||||
m_free_blocks.emplace_back( std::make_pair( offset + memory_size, size - memory_size ) );
|
||||
|
||||
return { m_handle, offset, memory_size };
|
||||
}
|
||||
|
||||
void free( BufferSuballocationInfo& info )
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
{
|
||||
//Find the suballocation
|
||||
auto itter = m_suballocations.find( info.offset );
|
||||
|
||||
if ( itter == m_suballocations.end() ) throw std::runtime_error( "Failed to find suballocation" );
|
||||
|
||||
//Remove the suballocation
|
||||
m_suballocations.erase( itter );
|
||||
}
|
||||
|
||||
// Forward check
|
||||
{
|
||||
ZoneScopedN( "Forward check" );
|
||||
auto itter = std::find_if(
|
||||
m_free_blocks.begin(),
|
||||
m_free_blocks.end(),
|
||||
[ &info ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
|
||||
{
|
||||
auto& [ offset, size ] = pair;
|
||||
return offset > info.offset && offset == info.offset + info.size;
|
||||
} );
|
||||
|
||||
//If itter is not end then we have found a block where itter->offset > offset
|
||||
|
||||
if ( itter != m_free_blocks.end() )
|
||||
{
|
||||
auto& [ free_offset, free_size ] = *itter;
|
||||
info.size += free_size; // Add their size to ours
|
||||
|
||||
//Nuke block
|
||||
m_free_blocks.erase( itter );
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards check
|
||||
{
|
||||
ZoneScopedN( "Backwards check" );
|
||||
auto prev_block = std::find_if(
|
||||
m_free_blocks.begin(),
|
||||
m_free_blocks.end(),
|
||||
[ &info ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
|
||||
{
|
||||
auto& [ offset, size ] = pair;
|
||||
return offset + size + 1 == info.offset;
|
||||
} );
|
||||
|
||||
if ( prev_block != m_free_blocks.end() )
|
||||
{
|
||||
auto& [ offset, size ] = *prev_block;
|
||||
size += info.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
//No block before us. We are the free block
|
||||
m_free_blocks.push_back( { info.offset, info.size } );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void initGlobalStagingBuffer( std::uint64_t size );
|
||||
Buffer& getGlobalStagingBuffer();
|
||||
} // namespace fgl::engine
|
||||
153
src/engine/buffers/BufferSuballocation.hpp
Normal file
153
src/engine/buffers/BufferSuballocation.hpp
Normal file
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// Created by kj16609 on 12/23/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Buffer.hpp"
|
||||
#include "engine/Device.hpp"
|
||||
#include "engine/concepts/is_suballocation.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class BufferSuballocation
|
||||
{
|
||||
protected:
|
||||
|
||||
Buffer& m_buffer;
|
||||
BufferSuballocationInfo m_info {};
|
||||
|
||||
void* m_mapped { nullptr };
|
||||
|
||||
void flush( vk::DeviceSize beg, vk::DeviceSize end )
|
||||
{
|
||||
assert( m_mapped != nullptr && "BufferSuballocationT::flush() called before map()" );
|
||||
vk::MappedMemoryRange range {};
|
||||
range.memory = m_buffer.getMemory();
|
||||
range.offset = m_info.offset + beg;
|
||||
|
||||
const vk::DeviceSize min_atom_size { Device::getInstance().m_properties.limits.nonCoherentAtomSize };
|
||||
const auto size { end - beg };
|
||||
|
||||
range.size = align( size, min_atom_size );
|
||||
|
||||
if ( range.size > m_info.size ) range.size = VK_WHOLE_SIZE;
|
||||
|
||||
if ( Device::getInstance().device().flushMappedMemoryRanges( 1, &range ) != vk::Result::eSuccess )
|
||||
throw std::runtime_error( "Failed to flush memory" );
|
||||
}
|
||||
|
||||
BufferSuballocation& operator=( BufferSuballocation&& other )
|
||||
{
|
||||
//Free ourselves if we are valid
|
||||
if ( this->m_info.offset != std::numeric_limits< decltype( m_info.offset ) >::max() )
|
||||
m_buffer.free( m_info );
|
||||
|
||||
//Take their info
|
||||
m_info = other.m_info;
|
||||
|
||||
//Set other to be invalid
|
||||
other.m_info.offset = std::numeric_limits< decltype( m_info.offset ) >::max();
|
||||
other.m_info.size = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using value_type = void;
|
||||
|
||||
BufferSuballocation() = delete;
|
||||
BufferSuballocation( const BufferSuballocation& ) = delete;
|
||||
BufferSuballocation& operator=( const BufferSuballocation& ) = delete;
|
||||
|
||||
BufferSuballocation( BufferSuballocation&& other ) : m_buffer( other.m_buffer )
|
||||
{
|
||||
if ( this->m_info.offset != std::numeric_limits< decltype( m_info.offset ) >::max() )
|
||||
m_buffer.free( m_info );
|
||||
|
||||
m_info = other.m_info;
|
||||
m_mapped = other.m_mapped;
|
||||
|
||||
other.m_info.offset = std::numeric_limits< decltype( m_info.offset ) >::max();
|
||||
other.m_info.size = 0;
|
||||
other.m_mapped = nullptr;
|
||||
}
|
||||
|
||||
BufferSuballocation( Buffer& buffer, const std::size_t memory_size, const std::uint32_t alignment = 1 ) :
|
||||
m_buffer( buffer ),
|
||||
m_info( buffer.suballocate( memory_size, alignment ) ),
|
||||
m_mapped( m_buffer.isMappable() ? buffer.map( m_info ) : nullptr )
|
||||
{
|
||||
assert( memory_size != 0 && "BufferSuballocation::BufferSuballocation() called with memory_size == 0" );
|
||||
}
|
||||
|
||||
BufferSuballocation(
|
||||
std::unique_ptr< Buffer >& buffer_ptr, const std::size_t memory_size, const std::uint32_t alignment = 1 ) :
|
||||
BufferSuballocation( *buffer_ptr.get(), memory_size, alignment )
|
||||
{
|
||||
assert( buffer_ptr != nullptr && "BufferSuballocation::BufferSuballocation() called with nullptr" );
|
||||
}
|
||||
|
||||
vk::DeviceSize size() const { return m_info.size; }
|
||||
|
||||
Buffer& getBuffer() const { return m_buffer; }
|
||||
|
||||
vk::Buffer getVkBuffer() const { return m_buffer.getBuffer(); }
|
||||
|
||||
vk::DeviceSize getOffset() const { return m_info.offset; }
|
||||
|
||||
vk::DeviceSize offset() const { return m_info.offset; }
|
||||
|
||||
vk::DescriptorBufferInfo descriptorInfo() const
|
||||
{
|
||||
vk::DescriptorBufferInfo info {};
|
||||
info.buffer = m_buffer.getBuffer();
|
||||
info.offset = m_info.offset;
|
||||
info.range = m_info.size;
|
||||
return info;
|
||||
}
|
||||
|
||||
~BufferSuballocation()
|
||||
{
|
||||
if ( m_info.offset != std::numeric_limits< decltype( m_info.offset ) >::max() ) m_buffer.free( m_info );
|
||||
}
|
||||
};
|
||||
|
||||
//! Single element allocation of T
|
||||
template < typename T >
|
||||
struct HostSingleT final : public BufferSuballocation
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
HostSingleT() = delete;
|
||||
HostSingleT( const HostSingleT& ) = delete;
|
||||
HostSingleT( HostSingleT&& ) = delete;
|
||||
HostSingleT& operator=( const HostSingleT& ) = delete;
|
||||
|
||||
HostSingleT( Buffer& buffer ) : BufferSuballocation( buffer, sizeof( T ), alignof( T ) ) {}
|
||||
|
||||
HostSingleT& operator=( T& t )
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
*static_cast< T* >( this->m_mapped ) = t;
|
||||
|
||||
flush();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void flush() { BufferSuballocation::flush( 0, this->m_info.size ); }
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_typed_suballocation = requires( T t ) {
|
||||
{
|
||||
t.operator->()
|
||||
} -> std::same_as< typename T::value_type* >;
|
||||
requires is_buffer< typename T::BufferT >;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
41
src/engine/buffers/UniqueFrameSuballocation.hpp
Normal file
41
src/engine/buffers/UniqueFrameSuballocation.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by kj16609 on 12/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
//! Wrapper class to allow for easy per-frame suballocation
|
||||
template < is_suballocation Suballocation >
|
||||
class PerFrameSuballocation
|
||||
{
|
||||
Buffer& m_buffer;
|
||||
std::vector< std::unique_ptr< Suballocation > > m_suballocations {};
|
||||
|
||||
public:
|
||||
|
||||
PerFrameSuballocation( Buffer& buffer, const std::uint32_t frames_in_flight ) : m_buffer( buffer )
|
||||
{
|
||||
m_suballocations.reserve( frames_in_flight );
|
||||
for ( std::uint8_t i = 0; i < frames_in_flight; ++i )
|
||||
{
|
||||
static_assert(
|
||||
std::is_default_constructible_v< typename Suballocation::value_type >,
|
||||
"value_type must be default constructible" );
|
||||
m_suballocations.emplace_back( std::make_unique< Suballocation >( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
void setMaxFramesInFlight( std::uint8_t max ) { m_suballocations.resize( max ); }
|
||||
|
||||
Suballocation& operator[]( std::uint8_t index ) { return *m_suballocations[ index ]; }
|
||||
|
||||
void bindForFrame( vk::CommandBuffer& cmd_buffer, std::uint8_t frame_idx )
|
||||
{
|
||||
m_suballocations[ frame_idx ].bind( cmd_buffer );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
66
src/engine/buffers/vector/BufferVector.hpp
Normal file
66
src/engine/buffers/vector/BufferVector.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by kj16609 on 12/29/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "engine/buffers/BufferSuballocation.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
//! Number of spares to allocate when resizing beyond the current capacity + current spare
|
||||
constexpr std::uint32_t SPARE_ALLOCATION_COUNT { 16 };
|
||||
|
||||
class BufferVector : public BufferSuballocation
|
||||
{
|
||||
protected:
|
||||
|
||||
std::uint32_t m_count;
|
||||
std::uint32_t m_stride;
|
||||
|
||||
//TODO: Implement spare
|
||||
//std::uint32_t m_spare_count { 0 };
|
||||
|
||||
BufferVector() = delete;
|
||||
|
||||
BufferVector( Buffer& buffer, std::uint32_t count, std::uint32_t stride ) :
|
||||
BufferSuballocation( buffer, count * stride, 1 ),
|
||||
m_count( count ),
|
||||
m_stride( stride )
|
||||
{}
|
||||
|
||||
BufferVector( const BufferVector& ) = delete;
|
||||
BufferVector( BufferVector&& ) = delete;
|
||||
|
||||
BufferVector& operator=( BufferVector&& other )
|
||||
{
|
||||
this->m_buffer.free( this->m_info );
|
||||
this->m_info = other.m_info;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BufferVector& operator=( const BufferVector& ) = delete;
|
||||
|
||||
public:
|
||||
|
||||
//! Returns the offset count from the start of the buffer to the first element
|
||||
[[nodiscard]] std::uint32_t getOffsetCount()
|
||||
{
|
||||
return static_cast< std::uint32_t >( this->m_info.offset / m_stride );
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t count() const noexcept { return m_count; }
|
||||
|
||||
[[nodiscard]] std::uint32_t stride() const noexcept { return m_stride; }
|
||||
|
||||
void resize( const std::uint32_t count )
|
||||
{
|
||||
BufferVector other { this->m_buffer, count, m_stride };
|
||||
|
||||
Device::getInstance().copyBuffer( this->m_buffer, other.m_buffer, 0, 0, this->size() );
|
||||
|
||||
*this = std::move( other );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
59
src/engine/buffers/vector/DeviceVector.hpp
Normal file
59
src/engine/buffers/vector/DeviceVector.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Created by kj16609 on 12/29/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BufferVector.hpp"
|
||||
#include "HostVector.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
class DeviceVector final : public BufferVector
|
||||
{
|
||||
std::unique_ptr< HostVector< T > > m_staging_buffer {};
|
||||
|
||||
public:
|
||||
|
||||
DeviceVector( Buffer& buffer, const std::uint32_t count = 1 ) : BufferVector( buffer, count, sizeof( T ) )
|
||||
{
|
||||
assert( count != 0 && "BufferSuballocationVector::BufferSuballocationVector() called with count == 0" );
|
||||
}
|
||||
|
||||
bool hasStaging() const { return m_staging_buffer != nullptr; }
|
||||
|
||||
void createStaging( const std::vector< T >& data )
|
||||
{
|
||||
m_staging_buffer = std::make_unique< HostVector< T > >( getGlobalStagingBuffer(), data );
|
||||
}
|
||||
|
||||
HostVector< T >& getStaging() { return *m_staging_buffer; }
|
||||
|
||||
void stage( vk::CommandBuffer& command_buffer )
|
||||
{
|
||||
assert( m_staging_buffer && "DeviceVector::stage() called without staging buffer" );
|
||||
|
||||
//Copy
|
||||
vk::BufferCopy copy_region { m_staging_buffer->offset(), this->m_info.offset, this->m_info.size };
|
||||
|
||||
command_buffer.copyBuffer( m_staging_buffer->getVkBuffer(), this->m_buffer.getVkBuffer(), copy_region );
|
||||
}
|
||||
|
||||
void dropStaging() { m_staging_buffer.reset(); }
|
||||
|
||||
/**
|
||||
* @brief Constructs a new DeviceVector from a vector, Requires a command buffer to copy the data to the device
|
||||
* @param buffer
|
||||
* @param data
|
||||
* @param command_buffer
|
||||
*/
|
||||
DeviceVector( Buffer& buffer, const std::vector< T >& data ) :
|
||||
DeviceVector( buffer, static_cast< std::uint32_t >( data.size() ) )
|
||||
{
|
||||
createStaging( data );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
100
src/engine/buffers/vector/HostVector.hpp
Normal file
100
src/engine/buffers/vector/HostVector.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// Created by kj16609 on 12/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BufferVector.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
class DeviceVector;
|
||||
|
||||
/**
|
||||
* A vector device with the ability to flush to the device.
|
||||
* @tparam T
|
||||
* @tparam Buffer
|
||||
*/
|
||||
template < typename T >
|
||||
class HostVector final : public BufferVector
|
||||
{
|
||||
public:
|
||||
|
||||
using value_type = T;
|
||||
|
||||
HostVector() = delete;
|
||||
HostVector( const HostVector& ) = delete;
|
||||
HostVector& operator=( const HostVector& ) = delete;
|
||||
HostVector& operator=( HostVector&& ) = delete;
|
||||
HostVector( HostVector&& other ) = delete;
|
||||
|
||||
HostVector( Buffer& buffer, const std::uint32_t count = 1 ) : BufferVector( buffer, count, sizeof( T ) )
|
||||
{
|
||||
assert( count != 0 && "BufferSuballocationVector::BufferSuballocationVector() called with count == 0" );
|
||||
}
|
||||
|
||||
HostVector( Buffer& buffer, const std::vector< T >& vec ) :
|
||||
HostVector( buffer, static_cast< std::uint32_t >( vec.size() ) )
|
||||
{
|
||||
if ( this->m_stride == sizeof( T ) )
|
||||
{
|
||||
std::memcpy( this->m_mapped, vec.data(), this->count() * sizeof( T ) );
|
||||
}
|
||||
else
|
||||
assert( "Stride must be equal to sizeof(T)" );
|
||||
|
||||
this->flush();
|
||||
}
|
||||
|
||||
void flush() { this->flushRange( 0, this->count() ); }
|
||||
|
||||
void flushRange( const std::uint32_t start_idx, const std::uint32_t end_idx )
|
||||
{
|
||||
assert(
|
||||
start_idx < this->m_count && "BufferSuballocationVector::flushRange start_idx index out of bounds" );
|
||||
assert( end_idx <= this->m_count && "BufferSuballocationVector::flushRange end_idx index out of bounds" );
|
||||
assert(
|
||||
start_idx < end_idx
|
||||
&& "BufferSuballocationVector::flushRange start_idx index must be less than end_idx index" );
|
||||
assert(
|
||||
( end_idx - start_idx ) != 0
|
||||
&& "BufferSuballocationVector::flushRange end_idx must be at least +1 from start_idx" );
|
||||
|
||||
BufferSuballocation::flush( start_idx * this->m_stride, ( end_idx - start_idx ) * this->m_stride );
|
||||
}
|
||||
|
||||
HostVector& operator=( const std::vector< T >& vec )
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
assert( this->m_count == vec.size() && "HostVector::operator=() called with vector of different size" );
|
||||
|
||||
if ( this->m_stride == sizeof( T ) )
|
||||
{
|
||||
std::memcpy( this->m_mapped, vec.data(), this->count() * sizeof( T ) );
|
||||
}
|
||||
else
|
||||
assert( "Stride must be equal to sizeof(T)" );
|
||||
|
||||
this->flush();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void push_back( const T& t )
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
resize( this->m_count + 1 );
|
||||
|
||||
*this[ this->m_count++ ] = t;
|
||||
|
||||
//--this->m_spare_count;
|
||||
}
|
||||
|
||||
~HostVector() {}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
43
src/engine/concepts/is_attachment.hpp
Normal file
43
src/engine/concepts/is_attachment.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Created by kj16609 on 1/2/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct ImageView;
|
||||
class Image;
|
||||
struct AttachmentResources;
|
||||
|
||||
template < typename T >
|
||||
concept is_attachment = requires( T a ) {
|
||||
{
|
||||
a.desc()
|
||||
} -> std::same_as< vk::AttachmentDescription& >;
|
||||
{
|
||||
a.getIndex()
|
||||
} -> std::same_as< std::uint32_t >;
|
||||
{
|
||||
a.setIndex( std::declval< std::uint32_t >() )
|
||||
};
|
||||
{
|
||||
a.attachImageView( std::declval< std::uint16_t >(), std::declval< std::shared_ptr< ImageView > >() )
|
||||
};
|
||||
{
|
||||
a.linkImage( std::declval< std::uint16_t >(), std::declval< Image& >() )
|
||||
};
|
||||
{
|
||||
a.resources()
|
||||
} -> std::same_as< AttachmentResources >;
|
||||
{
|
||||
a.m_clear_value
|
||||
} -> std::same_as< vk::ClearValue& >;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_depth_attachment = is_attachment< T > && T::Layout == vk::ImageLayout::eDepthStencilAttachmentOptimal;
|
||||
|
||||
template < typename T >
|
||||
concept is_color_attachment = is_attachment< T > && T::Layout == vk::ImageLayout::eColorAttachmentOptimal;
|
||||
} // namespace fgl::engine
|
||||
27
src/engine/concepts/is_bindable.hpp
Normal file
27
src/engine/concepts/is_bindable.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by kj16609 on 12/31/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
template < typename T >
|
||||
concept is_bindable_buffer = requires( T t ) {
|
||||
{
|
||||
t.descriptorInfo()
|
||||
} -> std::same_as< vk::DescriptorBufferInfo >;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_bindable_image = requires( T t ) {
|
||||
{
|
||||
t.descriptorInfo()
|
||||
} -> std::same_as< vk::DescriptorImageInfo >;
|
||||
};
|
||||
|
||||
template < typename T > concept is_bindable = is_bindable_image< T > || is_bindable_buffer< T >;
|
||||
|
||||
} // namespace fgl::engine
|
||||
19
src/engine/concepts/is_buffer.hpp
Normal file
19
src/engine/concepts/is_buffer.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Created by kj16609 on 12/29/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "engine/buffers/Buffer.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct BufferSuballocationInfo;
|
||||
|
||||
template < typename T > concept is_buffer = std::same_as< T, Buffer >;
|
||||
|
||||
template < typename T > concept is_buffer_ref = is_buffer< std::remove_reference_t< T > >;
|
||||
|
||||
} // namespace fgl::engine
|
||||
61
src/engine/concepts/is_descriptor.hpp
Normal file
61
src/engine/concepts/is_descriptor.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Created by kj16609 on 1/1/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
/**
|
||||
* Descriptor must have a `static constexpr bool is_empty` member with the value `true`
|
||||
* Descriptor must also be valid in `is_descriptor`
|
||||
* @tparam T
|
||||
*/
|
||||
template < typename T >
|
||||
concept is_empty_descriptor = requires( T t ) {
|
||||
{
|
||||
t.m_binding_idx
|
||||
} -> std::same_as< const std::uint16_t& >;
|
||||
{
|
||||
t.is_empty
|
||||
} -> std::same_as< const bool& >;
|
||||
} && T::is_empty;
|
||||
|
||||
template < typename T >
|
||||
concept is_layout_descriptor = requires( T t ) {
|
||||
{
|
||||
t.m_binding_idx
|
||||
} -> std::same_as< const std::uint16_t& >;
|
||||
{
|
||||
t.m_layout_binding
|
||||
} -> std::same_as< const vk::DescriptorSetLayoutBinding& >;
|
||||
{
|
||||
t.m_descriptor_type
|
||||
} -> std::same_as< const vk::DescriptorType& >;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
concept is_image_descriptor =
|
||||
is_layout_descriptor< T > && ( T::m_descriptor_type == vk::DescriptorType::eSampledImage );
|
||||
|
||||
template < typename T >
|
||||
concept is_attachment_descriptor =
|
||||
is_layout_descriptor< T > && ( T::m_descriptor_type == vk::DescriptorType::eInputAttachment );
|
||||
|
||||
template < typename T >
|
||||
concept is_uniform_buffer_desciptor =
|
||||
is_layout_descriptor< T > && ( T::m_descriptor_type == vk::DescriptorType::eUniformBuffer );
|
||||
|
||||
template < typename T >
|
||||
concept is_storage_buffer_descriptor =
|
||||
is_layout_descriptor< T > && ( T::m_descriptor_type == vk::DescriptorType::eStorageBuffer );
|
||||
|
||||
template < typename T >
|
||||
concept is_buffer_descriptor = is_uniform_buffer_desciptor< T > || is_storage_buffer_descriptor< T >;
|
||||
|
||||
template < typename T > concept is_descriptor = is_empty_descriptor< T > || is_layout_descriptor< T >;
|
||||
} // namespace fgl::engine
|
||||
23
src/engine/concepts/is_image.hpp
Normal file
23
src/engine/concepts/is_image.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by kj16609 on 1/2/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
concept is_image = requires( T a ) {
|
||||
{
|
||||
a.getVkImage()
|
||||
} -> std::same_as< vk::Image& >;
|
||||
{
|
||||
a.format()
|
||||
} -> std::same_as< vk::Format >;
|
||||
{
|
||||
a.extent()
|
||||
} -> std::same_as< vk::Extent2D >;
|
||||
};
|
||||
|
||||
}
|
||||
30
src/engine/concepts/is_suballocation.hpp
Normal file
30
src/engine/concepts/is_suballocation.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by kj16609 on 12/29/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "is_buffer.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
concept is_suballocation = requires( T t ) {
|
||||
{
|
||||
t.getBuffer()
|
||||
} -> is_buffer_ref;
|
||||
{
|
||||
t.getVkBuffer()
|
||||
} -> std::same_as< vk::Buffer >;
|
||||
{
|
||||
t.getOffset()
|
||||
} -> std::same_as< vk::DeviceSize >;
|
||||
{
|
||||
t.size()
|
||||
} -> std::same_as< vk::DeviceSize >;
|
||||
{
|
||||
t.descriptorInfo()
|
||||
} -> std::same_as< vk::DescriptorBufferInfo >;
|
||||
};
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// temporary helper function, creates a 1x1x1 cube centered at offset
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Model.hpp"
|
||||
|
||||
using namespace fgl::engine;
|
||||
|
||||
std::unique_ptr< Model > createCubeModel( Device& device, glm::vec3 offset )
|
||||
{
|
||||
std::vector< Model::Vertex > vertices {
|
||||
|
||||
// left face (white)
|
||||
{ { -.5f, -.5f, -.5f }, { .9f, .9f, .9f } },
|
||||
{ { -.5f, .5f, .5f }, { .9f, .9f, .9f } },
|
||||
{ { -.5f, -.5f, .5f }, { .9f, .9f, .9f } },
|
||||
{ { -.5f, -.5f, -.5f }, { .9f, .9f, .9f } },
|
||||
{ { -.5f, .5f, -.5f }, { .9f, .9f, .9f } },
|
||||
{ { -.5f, .5f, .5f }, { .9f, .9f, .9f } },
|
||||
|
||||
// right face (yellow)
|
||||
{ { .5f, -.5f, -.5f }, { .8f, .8f, .1f } },
|
||||
{ { .5f, .5f, .5f }, { .8f, .8f, .1f } },
|
||||
{ { .5f, -.5f, .5f }, { .8f, .8f, .1f } },
|
||||
{ { .5f, -.5f, -.5f }, { .8f, .8f, .1f } },
|
||||
{ { .5f, .5f, -.5f }, { .8f, .8f, .1f } },
|
||||
{ { .5f, .5f, .5f }, { .8f, .8f, .1f } },
|
||||
|
||||
// top face (orange, remember y axis points down)
|
||||
{ { -.5f, -.5f, -.5f }, { .9f, .6f, .1f } },
|
||||
{ { .5f, -.5f, .5f }, { .9f, .6f, .1f } },
|
||||
{ { -.5f, -.5f, .5f }, { .9f, .6f, .1f } },
|
||||
{ { -.5f, -.5f, -.5f }, { .9f, .6f, .1f } },
|
||||
{ { .5f, -.5f, -.5f }, { .9f, .6f, .1f } },
|
||||
{ { .5f, -.5f, .5f }, { .9f, .6f, .1f } },
|
||||
|
||||
// bottom face (red)
|
||||
{ { -.5f, .5f, -.5f }, { .8f, .1f, .1f } },
|
||||
{ { .5f, .5f, .5f }, { .8f, .1f, .1f } },
|
||||
{ { -.5f, .5f, .5f }, { .8f, .1f, .1f } },
|
||||
{ { -.5f, .5f, -.5f }, { .8f, .1f, .1f } },
|
||||
{ { .5f, .5f, -.5f }, { .8f, .1f, .1f } },
|
||||
{ { .5f, .5f, .5f }, { .8f, .1f, .1f } },
|
||||
|
||||
// nose face (blue)
|
||||
{ { -.5f, -.5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
{ { .5f, .5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
{ { -.5f, .5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
{ { -.5f, -.5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
{ { .5f, -.5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
{ { .5f, .5f, 0.5f }, { .1f, .1f, .8f } },
|
||||
|
||||
// tail face (green)
|
||||
{ { -.5f, -.5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
{ { .5f, .5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
{ { -.5f, .5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
{ { -.5f, -.5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
{ { .5f, -.5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
{ { .5f, .5f, -0.5f }, { .1f, .8f, .1f } },
|
||||
|
||||
};
|
||||
for ( auto& v : vertices )
|
||||
{
|
||||
v.m_pos += offset;
|
||||
}
|
||||
return std::make_unique< Model >( device, vertices );
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/28/23.
|
||||
//
|
||||
|
||||
#include "DebugGUI.hpp"
|
||||
|
||||
namespace fgl::engine::debug
|
||||
{
|
||||
void initDebugGUI()
|
||||
{}
|
||||
|
||||
} // namespace fgl::engine::debug
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/28/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_impl_glfw.h"
|
||||
#include "imgui/imgui_impl_vulkan.h"
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace fgl::engine::debug
|
||||
{
|
||||
void initDebugGUI();
|
||||
}
|
||||
81
src/engine/descriptors/Descriptor.hpp
Normal file
81
src/engine/descriptors/Descriptor.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Created by kj16609 on 12/7/23.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "engine/buffers/Buffer.hpp"
|
||||
#include "engine/concepts/is_bindable.hpp"
|
||||
#include "engine/concepts/is_descriptor.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template <
|
||||
std::uint16_t binding_idx,
|
||||
vk::DescriptorType descriptor_type,
|
||||
vk::ShaderStageFlags stage_flags = vk::ShaderStageFlagBits::eAll,
|
||||
std::uint16_t binding_count = 1 >
|
||||
struct Descriptor
|
||||
{
|
||||
static constexpr std::uint16_t m_binding_idx { binding_idx };
|
||||
static constexpr std::uint16_t m_count { binding_count };
|
||||
|
||||
static constexpr vk::DescriptorType m_descriptor_type { descriptor_type };
|
||||
|
||||
consteval static vk::DescriptorSetLayoutBinding generateLayoutBinding()
|
||||
{
|
||||
vk::DescriptorSetLayoutBinding layout_binding {};
|
||||
layout_binding.binding = binding_idx;
|
||||
layout_binding.descriptorType = descriptor_type;
|
||||
layout_binding.descriptorCount = binding_count;
|
||||
layout_binding.stageFlags = stage_flags;
|
||||
layout_binding.pImmutableSamplers = VK_NULL_HANDLE;
|
||||
return layout_binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* The layout binding is used during pipeline creation to provide information to the pipeline about the descriptor set layout.
|
||||
* This is used to construct the actual layout and can be done at compiletime.
|
||||
*/
|
||||
static constexpr vk::DescriptorSetLayoutBinding m_layout_binding { generateLayoutBinding() };
|
||||
};
|
||||
|
||||
template < std::uint16_t binding_idx, vk::ShaderStageFlags stage_flags >
|
||||
using ImageDescriptor = Descriptor< binding_idx, vk::DescriptorType::eSampledImage, stage_flags >;
|
||||
|
||||
template < std::uint16_t binding_idx, vk::ShaderStageFlags stage_flags >
|
||||
using AttachmentDescriptor = Descriptor< binding_idx, vk::DescriptorType::eInputAttachment, stage_flags >;
|
||||
|
||||
template < std::uint16_t binding_idx, vk::ShaderStageFlags stage_flags >
|
||||
using StorageDescriptor = Descriptor< binding_idx, vk::DescriptorType::eStorageBuffer, stage_flags >;
|
||||
|
||||
template < std::uint16_t binding_idx, vk::ShaderStageFlags stage_flags >
|
||||
using UniformDescriptor = Descriptor< binding_idx, vk::DescriptorType::eUniformBuffer, stage_flags >;
|
||||
|
||||
template < std::uint16_t idx >
|
||||
struct EmptyDescriptor
|
||||
{
|
||||
static constexpr std::uint16_t m_binding_idx { idx };
|
||||
static constexpr bool is_empty { true };
|
||||
};
|
||||
|
||||
static_assert( is_empty_descriptor< EmptyDescriptor< 0 > > );
|
||||
|
||||
//! Returns the maximum binding index for a list of given descriptors
|
||||
template < is_descriptor Current, is_descriptor... Bindings >
|
||||
consteval std::uint16_t getMaxDescriptorIDX()
|
||||
{
|
||||
if constexpr ( sizeof...( Bindings ) == 0 )
|
||||
{
|
||||
return Current::m_binding_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::max( Current::m_binding_idx, getMaxDescriptorIDX< Bindings... >() );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user