Compare commits
No commits in common. "387bc849853a102fd712c03a4fce92251315aa94" and "09de216b0af462893e97b4cfc2571ed08d902eb1" have entirely different histories.
387bc84985
...
09de216b0a
17
.clwb/.bazelproject
Normal file
17
.clwb/.bazelproject
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
directories:
|
||||||
|
# Add the directories you want added as source here
|
||||||
|
# By default, we've added your entire workspace ('.')
|
||||||
|
.
|
||||||
|
|
||||||
|
# Automatically includes all relevant targets under the 'directories' above
|
||||||
|
derive_targets_from_directories: true
|
||||||
|
|
||||||
|
targets:
|
||||||
|
# If source code isn't resolving, add additional targets that compile it here
|
||||||
|
|
||||||
|
additional_languages:
|
||||||
|
# Uncomment any additional languages you want supported
|
||||||
|
# dart
|
||||||
|
# javascript
|
||||||
|
# python
|
||||||
|
# typescript
|
11
.clwb/.blaze/modules/.project-data-dir.iml
Normal file
11
.clwb/.blaze/modules/.project-data-dir.iml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.system.id="Blaze" type="BLAZE_CPP_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$/../..">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../.idea" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.." />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
16
.clwb/.blaze/modules/.workspace.iml
Normal file
16
.clwb/.blaze/modules/.workspace.iml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.system.id="Blaze" type="BLAZE_CPP_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$/../../..">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../.." isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-bin" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-genfiles" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-out" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-testlogs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-umbra" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../.." />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
.clwb/.idea/.gitignore
vendored
Normal file
8
.clwb/.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
1
.clwb/.idea/.name
Normal file
1
.clwb/.idea/.name
Normal file
|
@ -0,0 +1 @@
|
||||||
|
umbra
|
9
.clwb/.idea/modules.xml
Normal file
9
.clwb/.idea/modules.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" filepath="$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.workspace.iml" filepath="$PROJECT_DIR$/.blaze/modules/.workspace.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.clwb/.idea/vcs.xml
Normal file
6
.clwb/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -4,10 +4,7 @@ obj/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
|
|
||||||
|
*.user
|
||||||
|
|
||||||
|
bazel-*/
|
||||||
test-results
|
test-results
|
||||||
cmake-build-vs-debug/
|
|
||||||
cmake-build-debug/
|
|
||||||
cmake-build-debug-msvc/
|
|
||||||
cmake-build-debug-msvc-vs/
|
|
||||||
/.idea/
|
|
||||||
|
|
13
.idea/.idea.umbra/.idea/.gitignore
vendored
Normal file
13
.idea/.idea.umbra/.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/modules.xml
|
||||||
|
/.idea.umbra.iml
|
||||||
|
/contentModel.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
4
.idea/.idea.umbra/.idea/encodings.xml
Normal file
4
.idea/.idea.umbra/.idea/encodings.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
8
.idea/.idea.umbra/.idea/indexLayout.xml
Normal file
8
.idea/.idea.umbra/.idea/indexLayout.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.umbra/.idea/vcs.xml
Normal file
6
.idea/.idea.umbra/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -1,80 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.24)
|
|
||||||
|
|
||||||
Include(FetchContent)
|
|
||||||
|
|
||||||
# Fetch SDL for the runtime
|
|
||||||
FetchContent_Declare(
|
|
||||||
SDL2
|
|
||||||
URL https://www.libsdl.org/release/SDL2-devel-2.24.0-VC.zip
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(SDL2)
|
|
||||||
set(SDL2_DIR ${sdl2_SOURCE_DIR})
|
|
||||||
list(PREPEND CMAKE_PREFIX_PATH "${sdl2_SOURCE_DIR}/cmake")
|
|
||||||
|
|
||||||
# Fetch Catch2 for the file format tests
|
|
||||||
FetchContent_Declare(
|
|
||||||
Catch2
|
|
||||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
|
||||||
GIT_TAG v2.13.9 # or a later release
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(Catch2)
|
|
||||||
|
|
||||||
# Fetch GLM for the renderer
|
|
||||||
FetchContent_Declare(
|
|
||||||
glm
|
|
||||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
|
||||||
GIT_TAG 0.9.9.2
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_GetProperties(glm)
|
|
||||||
if(NOT glm_POPULATED)
|
|
||||||
FetchContent_Populate(glm)
|
|
||||||
set(GLM_TEST_ENABLE OFF CACHE BOOL "" FORCE)
|
|
||||||
add_subdirectory(${glm_SOURCE_DIR} ${glm_BINARY_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Fetch SpdLog for.. loggin
|
|
||||||
FetchContent_Declare(
|
|
||||||
spdlog
|
|
||||||
GIT_REPOSITORY https://github.com/gabime/spdlog.git
|
|
||||||
GIT_TAG v1.10.0
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_GetProperties(spdlog)
|
|
||||||
if(NOT spdlog_POPULATED)
|
|
||||||
FetchContent_Populate(spdlog)
|
|
||||||
add_subdirectory(${spdlog_SOURCE_DIR} ${spdlog_BINARY_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
imgui
|
|
||||||
GIT_REPOSITORY https://github.com/ocornut/imgui
|
|
||||||
GIT_TAG 71a0701920dbc83155f718182f01132d1ec2d51e
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(imgui)
|
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
dylib
|
|
||||||
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
|
|
||||||
GIT_TAG "v2.1.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(dylib)
|
|
||||||
|
|
||||||
# Import some find files
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
|
||||||
|
|
||||||
project(umbra)
|
|
||||||
|
|
||||||
set(CMAKE_STATIC_LIBRARY_PREFIX "")
|
|
||||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
|
||||||
|
|
||||||
# Core engine
|
|
||||||
add_subdirectory(projs/shadow/shadow-engine)
|
|
||||||
|
|
||||||
# Runtime executable
|
|
||||||
add_subdirectory(projs/shadow/shadow-runtime)
|
|
||||||
|
|
||||||
add_subdirectory(projs/test-game)
|
|
7
WORKSPACE
Normal file
7
WORKSPACE
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
workspace(
|
||||||
|
name = "umbra",
|
||||||
|
)
|
||||||
|
|
||||||
|
load("//vendor:deps.bzl", "deps")
|
||||||
|
|
||||||
|
deps()
|
|
@ -1,27 +0,0 @@
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
find_package(Vulkan REQUIRED)
|
|
||||||
|
|
||||||
FILE(GLOB_RECURSE SOURCES ${imgui_SOURCE_DIR}/*.cpp)
|
|
||||||
FILE(GLOB_RECURSE HEADERS ${imgui_SOURCE_DIR}/*.h)
|
|
||||||
|
|
||||||
FILE(GLOB_RECURSE HEADERS ${imgui_SOURCE_DIR}/backends/imgui_impl_vulkan.h)
|
|
||||||
FILE(GLOB_RECURSE SOURCES ${imgui_SOURCE_DIR}/backends/imgui_impl_vulkan.cpp)
|
|
||||||
|
|
||||||
add_library(imgui OBJECT
|
|
||||||
${imgui_SOURCE_DIR}/imgui.cpp
|
|
||||||
${imgui_SOURCE_DIR}/imgui_demo.cpp
|
|
||||||
${imgui_SOURCE_DIR}/imgui_draw.cpp
|
|
||||||
${imgui_SOURCE_DIR}/imgui_tables.cpp
|
|
||||||
${imgui_SOURCE_DIR}/imgui_widgets.cpp
|
|
||||||
${imgui_SOURCE_DIR}/backends/imgui_impl_sdl.cpp
|
|
||||||
${imgui_SOURCE_DIR}/backends/imgui_impl_vulkan.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(imgui
|
|
||||||
PUBLIC
|
|
||||||
${SDL2_INCLUDE_DIRS}
|
|
||||||
${imgui_SOURCE_DIR}
|
|
||||||
${imgui_SOURCE_DIR}/backends
|
|
||||||
)
|
|
||||||
target_link_libraries(imgui PRIVATE SDL2::SDL2 Vulkan::Vulkan)
|
|
41
imgui.ini
41
imgui.ini
|
@ -1,41 +0,0 @@
|
||||||
[Window][Debug##Default]
|
|
||||||
Pos=876,8
|
|
||||||
Size=315,195
|
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000008,0
|
|
||||||
|
|
||||||
[Window][Dear ImGui Demo]
|
|
||||||
Pos=1193,178
|
|
||||||
Size=62,25
|
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000007,0
|
|
||||||
|
|
||||||
[Window][Game module window]
|
|
||||||
Pos=1193,8
|
|
||||||
Size=62,50
|
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000004,0
|
|
||||||
|
|
||||||
[Window][Time]
|
|
||||||
Pos=1193,60
|
|
||||||
Size=62,49
|
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000005,0
|
|
||||||
|
|
||||||
[Window][Active Modules]
|
|
||||||
Pos=1193,111
|
|
||||||
Size=62,65
|
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000006,0
|
|
||||||
|
|
||||||
[Docking][Data]
|
|
||||||
DockNode ID=0x00000001 Pos=1196,188 Size=379,195 Split=X
|
|
||||||
DockNode ID=0x00000008 Parent=0x00000001 SizeRef=652,419 HiddenTabBar=1 Selected=0x55954704
|
|
||||||
DockNode ID=0x00000009 Parent=0x00000001 SizeRef=129,419 Split=Y
|
|
||||||
DockNode ID=0x00000002 Parent=0x00000009 SizeRef=219,34 Split=Y Selected=0xFC1D20C0
|
|
||||||
DockNode ID=0x00000004 Parent=0x00000002 SizeRef=219,64 Selected=0xFC1D20C0
|
|
||||||
DockNode ID=0x00000005 Parent=0x00000002 SizeRef=219,62 Selected=0xE75A179E
|
|
||||||
DockNode ID=0x00000003 Parent=0x00000009 SizeRef=219,31 Split=Y Selected=0xEE305C78
|
|
||||||
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=219,142 Selected=0xEE305C78
|
|
||||||
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=219,55 Selected=0xE87781F4
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
@startuml
|
|
||||||
|
|
||||||
class ShadowApplication <<Singleton>> {
|
|
||||||
-ModuleManager module_manager
|
|
||||||
~void packagePrivateMethod()
|
|
||||||
#{abstract} char protectedMethod(int param)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModuleManager <<Singleton>> {
|
|
||||||
-vector<string, Module> moduels
|
|
||||||
---
|
|
||||||
+void AddModule(Module* mo)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Module {
|
|
||||||
+string domain
|
|
||||||
---
|
|
||||||
+void Init()
|
|
||||||
+void Update()
|
|
||||||
+void ShutDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@enduml
|
|
|
@ -1,29 +1,14 @@
|
||||||
@startuml
|
@startuml
|
||||||
|
[shadow-engine] <<static lib>> as engine
|
||||||
[shadow-light] <<exe>> as editor
|
[shadow-light] <<exe>> as editor
|
||||||
|
|
||||||
[shadow-entity] <<static lib>> as shentity
|
|
||||||
[shadow-file-format] <<static lib>> as shff
|
|
||||||
[shadow-reflection] <<static lib>> as shreflection
|
|
||||||
[shadow-renderer] <<static lib>> as shrenderer
|
|
||||||
[shadow-utilty] <<static lib>> as shutitily
|
|
||||||
|
|
||||||
[shadow-engine] <<static/dynamic lib>> as shengine
|
|
||||||
|
|
||||||
shentity --* shengine
|
|
||||||
shff --* shengine
|
|
||||||
shreflection --* shengine
|
|
||||||
shrenderer --* shengine
|
|
||||||
shutitily --* shengine
|
|
||||||
|
|
||||||
|
|
||||||
[shadow-runner] <<exe>> as runner
|
[shadow-runner] <<exe>> as runner
|
||||||
|
|
||||||
[test-game] <<dll>> as game
|
[test-game] <<dll>> as game
|
||||||
|
|
||||||
shengine <- editor
|
engine <-editor
|
||||||
runner -> shengine
|
runner -> engine
|
||||||
|
|
||||||
game ..> shengine : uses
|
game ..> engine
|
||||||
|
|
||||||
runner --> game : loads
|
runner --> game : loads
|
||||||
editor --> game : loads
|
editor --> game : loads
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
@startuml
|
|
||||||
!include <material/file>
|
|
||||||
|
|
||||||
autoactivate on
|
|
||||||
autonumber
|
|
||||||
|
|
||||||
participant main as "int main(args)"
|
|
||||||
|
|
||||||
participant app as "ShadowApplication" <<(O,#ADD1B2) singleton>>
|
|
||||||
|
|
||||||
participant moduleMg as "ModuleManager" <<(O,#ADD1B2) singleton>>
|
|
||||||
|
|
||||||
|
|
||||||
'participant gameDll as "longName" : <$ma_file{scale=0.5}> <<DLL>>
|
|
||||||
|
|
||||||
participant "Game DLL" as dll <<$ma_file{scale=0.5}>> #LightGray <<DLL>>
|
|
||||||
|
|
||||||
-> main
|
|
||||||
main -> app ** : create
|
|
||||||
activate app
|
|
||||||
app -> moduleMg ** : create
|
|
||||||
return app
|
|
||||||
|
|
||||||
main -> app : LoadGame
|
|
||||||
|
|
||||||
app -> app : load
|
|
||||||
app -> dll ** : create
|
|
||||||
return
|
|
||||||
|
|
||||||
app -> dll : sh_main
|
|
||||||
|
|
||||||
loop for each needed module
|
|
||||||
dll -> moduleMg : AddModule()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
main -> app : Init()
|
|
||||||
app -> moduleMg : Init()
|
|
||||||
|
|
||||||
loop module in modules
|
|
||||||
collections module as "Module" <<(O,#ADD1B2)entity>>
|
|
||||||
moduleMg -> module : Init()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,47 +1 @@
|
||||||
\chapter{Disclaimer}
|
\chapter*{Asd}
|
||||||
|
|
||||||
This document only describes the ideas that I plan to explore in my thesis.
|
|
||||||
This is not the final project description for the thesis.
|
|
||||||
|
|
||||||
\chapter{Overview}
|
|
||||||
|
|
||||||
The idea behind the project is to establish the base for a game engine.
|
|
||||||
What this means is that the end product is not something that can compete with the likes of Unity and Unreal engine.
|
|
||||||
The goal is to lay the foundation for something that could compete in the future.
|
|
||||||
This means that the project will need to make crutial decisions on what frameworks libraries to use, and how the core of the engine should be architected.
|
|
||||||
|
|
||||||
The planned engine is the spiritual successor to my old engine called ShadowEngine.
|
|
||||||
This means that some parts of the old codebase will be used but every line will have to be rigorously checked.
|
|
||||||
This project is not a one person project, I'm planning on involving friends of mine. My responsibilities and the code that I write will constitute as the thesis.
|
|
||||||
|
|
||||||
\section{Technical information}
|
|
||||||
The project will be mainly written in C++, but some higher level functionality might be implemented in C# For windowing and interacting with the OS it will use SDL2.
|
|
||||||
The rendering API is going to be Vulkan.
|
|
||||||
Our main focus initially is Windows both for development and for running, as that is what all planned participants have access to.
|
|
||||||
|
|
||||||
\chapter{Main tasks}
|
|
||||||
\section{Build system}
|
|
||||||
The first question that needs decision for the project is what build system to use.
|
|
||||||
There are many different C++ build systems. The most common is Cmake and MSBuild. These are both quite capable.
|
|
||||||
\subsection{Cmake}
|
|
||||||
Cmake is an old powerful build system. it is capable of building almost anything. It is a generator for make files witch are even older.
|
|
||||||
Make files and in turn Cmake is highly organized around the actual compilation commands that get run in the end.
|
|
||||||
This makes them harder to configure.
|
|
||||||
(Magical strings and bad docs)
|
|
||||||
|
|
||||||
\subsection{MSbuild}
|
|
||||||
MSbuild is Microsoft's build system, it is quite powerful and has really good integration with Visual Studio.
|
|
||||||
It is also capable of integrating C# projects to the same workspace.
|
|
||||||
Sadly MSbuild can only build C++ on Windows witch means we can't support other platforms in the future.
|
|
||||||
MSbuild is also not supported by other IDEs like Clion meaning it would be a total vendor lock in.
|
|
||||||
|
|
||||||
\subsection{Bazel}
|
|
||||||
Bazel is a little known build system as it is mostly used by large projects.
|
|
||||||
It was developed for Google's internal repository, as they keep all of their code in a single monolithic repository.
|
|
||||||
This means the Bazel is capable of building basically any language (C/C++, Java C# JS/TS, etc.).
|
|
||||||
Bazel is also capable of managing project dependencies without using Git Submodules.
|
|
||||||
|
|
||||||
\subsection{Decision}
|
|
||||||
After trying both Msbuild and Bazel the decision was to use Bazel.
|
|
||||||
Sadly the Clion Bazel plugin has problems on windows,
|
|
||||||
but theses might be fixable with a few PRs or just shipping our own version of the plugin, as it is available on Github
|
|
17
projs/shadow-engine/BUILD.bazel
Normal file
17
projs/shadow-engine/BUILD.bazel
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "shadow-engine",
|
||||||
|
srcs = glob(["src/**/*.cpp", "src/**/*.h"]),
|
||||||
|
hdrs = glob(["src/**/*.h"]),
|
||||||
|
strip_include_prefix = "src/",
|
||||||
|
includes = [],
|
||||||
|
copts = [
|
||||||
|
"/std:c++20"
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//projs/shadow-utility",
|
||||||
|
"@sdl2"
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
61
projs/shadow-engine/shadow-engine.cpp
Normal file
61
projs/shadow-engine/shadow-engine.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <glm.hpp>
|
||||||
|
|
||||||
|
// You must include the command line parameters for your main function to be recognized by SDL
|
||||||
|
int main(int argc, char** args) {
|
||||||
|
|
||||||
|
// Pointers to our window and surface
|
||||||
|
SDL_Surface* winSurface = NULL;
|
||||||
|
SDL_Window* window = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize SDL. SDL_Init will return -1 if it fails.
|
||||||
|
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
|
||||||
|
std::cout << "Error initializing SDL: " << SDL_GetError() << std::endl;
|
||||||
|
system("pause");
|
||||||
|
// End the program
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our window
|
||||||
|
window = SDL_CreateWindow( "Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN );
|
||||||
|
|
||||||
|
// Make sure creating the window succeeded
|
||||||
|
if ( !window ) {
|
||||||
|
std::cout << "Error creating window: " << SDL_GetError() << std::endl;
|
||||||
|
system("pause");
|
||||||
|
// End the program
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the surface from the window
|
||||||
|
winSurface = SDL_GetWindowSurface( window );
|
||||||
|
|
||||||
|
// Make sure getting the surface succeeded
|
||||||
|
if ( !winSurface ) {
|
||||||
|
std::cout << "Error getting surface: " << SDL_GetError() << std::endl;
|
||||||
|
system("pause");
|
||||||
|
// End the program
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the window with a white rectangle
|
||||||
|
SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 255, 255, 255 ) );
|
||||||
|
|
||||||
|
// Update the window display
|
||||||
|
SDL_UpdateWindowSurface( window );
|
||||||
|
|
||||||
|
// Wait
|
||||||
|
system("pause");
|
||||||
|
|
||||||
|
// Destroy the window. This will also destroy the surface
|
||||||
|
SDL_DestroyWindow( window );
|
||||||
|
|
||||||
|
// Quit SDL
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
// End the program
|
||||||
|
return 0;
|
||||||
|
}
|
77
projs/shadow-engine/src/core/ShadowApplication.cpp
Normal file
77
projs/shadow-engine/src/core/ShadowApplication.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#include "ShadowApplication.h"
|
||||||
|
|
||||||
|
#include "Time.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ShadowEngine {
|
||||||
|
|
||||||
|
ShadowApplication* ShadowApplication::instance = nullptr;
|
||||||
|
|
||||||
|
ShadowApplication::ShadowApplication(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
instance = this;
|
||||||
|
|
||||||
|
if(argc > 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
std::string param(argv[i]);
|
||||||
|
if(param == "-no-gui")
|
||||||
|
{
|
||||||
|
this->no_gui = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//game = _setupFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ShadowApplication::~ShadowApplication()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowApplication::Init()
|
||||||
|
{
|
||||||
|
// Initialize SDL. SDL_Init will return -1 if it fails.
|
||||||
|
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
|
||||||
|
//std::cout << "Error initializing SDL: " << SDL_GetError() << std::endl;
|
||||||
|
//system("pause");
|
||||||
|
// End the program
|
||||||
|
//return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_ = new ShadowWindow(800,450);
|
||||||
|
|
||||||
|
/*
|
||||||
|
moduleManager.PushModule(new Log());
|
||||||
|
moduleManager.PushModule(new EventSystem::ShadowEventManager());
|
||||||
|
moduleManager.PushModule(new SDLPlatform::SDLModule());
|
||||||
|
moduleManager.PushModule(new Rendering::Renderer());
|
||||||
|
|
||||||
|
moduleManager.PushModule(new Assets::AssetManager());
|
||||||
|
|
||||||
|
if(!no_gui)
|
||||||
|
moduleManager.PushModule(new DebugGui::ImGuiModule());
|
||||||
|
|
||||||
|
moduleManager.PushModule(new InputSystem::ShadowActionSystem());
|
||||||
|
//moduleManager.PushModule(new Debug::DebugModule());
|
||||||
|
moduleManager.PushModule(new EntitySystem::EntitySystem());
|
||||||
|
|
||||||
|
|
||||||
|
game->Init();
|
||||||
|
|
||||||
|
moduleManager.Init();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowApplication::Start()
|
||||||
|
{
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
Time::UpdateTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "ShadowWindow.h"
|
#include "ShadowWindow.h"
|
||||||
#include "ModuleManager.h"
|
|
||||||
#include "exports.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_sdl.h"
|
|
||||||
#include "imgui_impl_vulkan.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
namespace ShadowEngine {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the application
|
/// Represents the application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ShadowApplication
|
class ShadowApplication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is the singleton instance
|
/// This is the singleton instance
|
||||||
|
@ -28,7 +21,7 @@ namespace ShadowEngine {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The module manager instance
|
/// The module manager instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ModuleManager moduleManager;
|
//ShadowEngine::ShadowModuleManager moduleManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the running state.
|
/// Represents the running state.
|
||||||
|
@ -38,23 +31,19 @@ namespace ShadowEngine {
|
||||||
|
|
||||||
bool no_gui = false;
|
bool no_gui = false;
|
||||||
|
|
||||||
std::string game = "";
|
|
||||||
|
|
||||||
void loadGame();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ShadowApplication(int argc, char* argv[]);
|
ShadowApplication(int argc, char* argv[]);
|
||||||
virtual ~ShadowApplication();
|
virtual ~ShadowApplication();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Static getter for the singleton instance
|
/// Static getter for the singleton instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// Use this for accessing the Application
|
/// Use this for accessing the Application
|
||||||
/// <returns>The current application reference</returns>
|
/// <returns>The current application reference</returns>
|
||||||
static ShadowApplication& Get();
|
static ShadowApplication& Get() { return *instance; };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the active window used for rendering
|
/// Returns the active window used for rendering
|
||||||
|
@ -63,11 +52,9 @@ namespace ShadowEngine {
|
||||||
//ShadowWindow& const GetWindow() const { return window_; };
|
//ShadowWindow& const GetWindow() const { return window_; };
|
||||||
//void SetWindow(ShadowWindow w) { window_ = w; }
|
//void SetWindow(ShadowWindow w) { window_ = w; }
|
||||||
|
|
||||||
ShadowEngine::ModuleManager& GetModuleManager() { return moduleManager; };
|
//ShadowEngine::ShadowModuleManager& GetModuleManager() { return moduleManager; };
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
void PollEvents();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
37
projs/shadow-engine/src/core/ShadowWindow.cpp
Normal file
37
projs/shadow-engine/src/core/ShadowWindow.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "ShadowWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
||||||
|
{
|
||||||
|
// Create our window
|
||||||
|
sdlWindowPtr = SDL_CreateWindow( "Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Width, Height, SDL_WINDOW_SHOWN );
|
||||||
|
|
||||||
|
// Make sure creating the window succeeded
|
||||||
|
if ( !sdlWindowPtr ) {
|
||||||
|
//Raise an error in the log
|
||||||
|
//std::cout << "Error creating window: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the surface from the window
|
||||||
|
sdlSurface = SDL_GetWindowSurface( sdlWindowPtr );
|
||||||
|
|
||||||
|
// Make sure getting the surface succeeded
|
||||||
|
if ( !sdlSurface ) {
|
||||||
|
//Raise an error in the log
|
||||||
|
//std::cout << "Error getting surface: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create window
|
||||||
|
/*
|
||||||
|
this->winPtr = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W, H,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
|
||||||
|
SH_CORE_ASSERT(winPtr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||||
|
|
||||||
|
context = ShadowEngine::Rendering::GraphicsContext::Create(this);
|
||||||
|
context->Init();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
ShadowWindow::~ShadowWindow()
|
||||||
|
{
|
||||||
|
}
|
22
projs/shadow-engine/src/core/ShadowWindow.h
Normal file
22
projs/shadow-engine/src/core/ShadowWindow.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
class ShadowWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
int Height;
|
||||||
|
int Width;
|
||||||
|
|
||||||
|
SDL_Window* sdlWindowPtr;
|
||||||
|
|
||||||
|
SDL_Surface* sdlSurface = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
//ShadowEngine::Ref<ShadowEngine::Rendering::GraphicsContext> context;
|
||||||
|
|
||||||
|
ShadowWindow(int W, int H);
|
||||||
|
|
||||||
|
~ShadowWindow();
|
||||||
|
};
|
20
projs/shadow-engine/src/core/Time.cpp
Normal file
20
projs/shadow-engine/src/core/Time.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "Time.h"
|
||||||
|
//#include <SDL_hints.h>
|
||||||
|
//#include <SDL.h>
|
||||||
|
|
||||||
|
int Time::NOW = 0;//SDL_GetPerformanceCounter();
|
||||||
|
int Time::LAST = 0;
|
||||||
|
double Time::deltaTime_ms = 0;
|
||||||
|
double Time::deltaTime = 0;
|
||||||
|
|
||||||
|
void Time::UpdateTime()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
NOW = SDL_GetTicks();
|
||||||
|
deltaTime_ms = LAST > 0 ? (NOW - LAST) *10 : (1.0f / 60.0f);
|
||||||
|
deltaTime_ms = deltaTime_ms == 0 ? (1.0f / 60.0f) : deltaTime_ms;
|
||||||
|
|
||||||
|
LAST = NOW;
|
||||||
|
deltaTime = deltaTime_ms * 0.001;
|
||||||
|
*/
|
||||||
|
}
|
14
projs/shadow-engine/src/core/Time.h
Normal file
14
projs/shadow-engine/src/core/Time.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Time
|
||||||
|
{
|
||||||
|
static int NOW;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static int LAST;
|
||||||
|
|
||||||
|
static double deltaTime;
|
||||||
|
static double deltaTime_ms;
|
||||||
|
|
||||||
|
static void UpdateTime();
|
||||||
|
};
|
31
projs/shadow-file-format/BUILD
Normal file
31
projs/shadow-file-format/BUILD
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||||
|
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "shadow-file-format",
|
||||||
|
srcs = glob(["src/**/*.cpp", "src/**/*.h"]),
|
||||||
|
hdrs = glob(["src/**/*.h"]),
|
||||||
|
strip_include_prefix = "src/",
|
||||||
|
includes = [],
|
||||||
|
copts = [
|
||||||
|
"/std:c++17"
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//projs/shadow-utility"
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "test",
|
||||||
|
srcs = glob(["test/**/*.cpp", "test/**/*.h"]),
|
||||||
|
includes = [],
|
||||||
|
defines = [ "CATCH_CONFIG_MAIN" ],
|
||||||
|
copts = [
|
||||||
|
"/std:c++17"
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//projs/shadow-file-format",
|
||||||
|
"@catch2"
|
||||||
|
],
|
||||||
|
)
|
|
@ -1,8 +1,9 @@
|
||||||
#include "SFFParser.h"
|
#include "SFFParser.h"
|
||||||
#include "string-helpers.h"
|
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "string-helpers.h"
|
||||||
|
|
||||||
namespace Shadow::SFF {
|
namespace Shadow::SFF {
|
||||||
|
|
||||||
SFFElement* SFFParser::ReadFromStream(std::istream& stream)
|
SFFElement* SFFParser::ReadFromStream(std::istream& stream)
|
|
@ -1,5 +1,4 @@
|
||||||
#define CATCH_CONFIG_MAIN
|
#include <catch2/catch.hpp>
|
||||||
#include "catch2/catch.hpp"
|
|
||||||
|
|
||||||
TEST_CASE("15 is less than 20", "[numbers]") {
|
TEST_CASE("15 is less than 20", "[numbers]") {
|
||||||
REQUIRE(15 < 20);
|
REQUIRE(15 < 20);
|
16
projs/shadow-runtime/BUILD.bazel
Normal file
16
projs/shadow-runtime/BUILD.bazel
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "shadow-runtime",
|
||||||
|
srcs = glob(["src/**/*.cpp", "src/**/*.h"]),
|
||||||
|
includes = [],
|
||||||
|
copts = [
|
||||||
|
"/std:c++20"
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//projs/shadow-engine",
|
||||||
|
"@sdl2"
|
||||||
|
],
|
||||||
|
)
|
|
@ -1,10 +1,12 @@
|
||||||
#include "core/ShadowApplication.h"
|
#include "main.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <SDL.h>
|
#include "core/ShadowApplication.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -13,7 +15,8 @@ int main(int argc, char *argv[])
|
||||||
for(int ndx{}; ndx != argc; ++ndx) {
|
for(int ndx{}; ndx != argc; ++ndx) {
|
||||||
std::cout << "argv[" << ndx << "] == " << std::quoted(argv[ndx]) << '\n';
|
std::cout << "argv[" << ndx << "] == " << std::quoted(argv[ndx]) << '\n';
|
||||||
}
|
}
|
||||||
std::cout << "argv[" << argc << "] == " << static_cast<void*>(argv[argc]) << '\n';
|
std::cout << "argv[" << argc << "] == "
|
||||||
|
<< static_cast<void*>(argv[argc]) << '\n';
|
||||||
/*...*/
|
/*...*/
|
||||||
|
|
||||||
ShadowEngine::ShadowApplication app(argc, argv);
|
ShadowEngine::ShadowApplication app(argc, argv);
|
8
projs/shadow-runtime/src/main.h
Normal file
8
projs/shadow-runtime/src/main.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
//
|
||||||
|
// Created by dpete on 2022-06-20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef UMBRA_MAIN_H
|
||||||
|
#define UMBRA_MAIN_H
|
||||||
|
|
||||||
|
#endif //UMBRA_MAIN_H
|
15
projs/shadow-utility/BUILD
Normal file
15
projs/shadow-utility/BUILD
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "shadow-utility",
|
||||||
|
srcs = glob(["**/*.cpp", "**/*.h"]),
|
||||||
|
hdrs = glob(["**/*.h"]),
|
||||||
|
strip_include_prefix = "src/",
|
||||||
|
includes = [],
|
||||||
|
copts = [
|
||||||
|
"/std:c++20"
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
#include "../inc/string-helpers.h"
|
#include "string-helpers.h"
|
||||||
|
|
||||||
std::vector<std::string> explode(const std::string& s, const char& c)
|
std::vector<std::string> explode(const std::string& s, const char& c)
|
||||||
{
|
{
|
|
@ -1,44 +0,0 @@
|
||||||
find_package(Vulkan REQUIRED)
|
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
|
||||||
find_package(imgui REQUIRED)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
||||||
|
|
||||||
FILE(GLOB_RECURSE SOURCES
|
|
||||||
core/src/*.cpp
|
|
||||||
shadow-entity/src/*.cpp
|
|
||||||
shadow-renderer/src/*.cpp
|
|
||||||
shadow-reflection/src/*.cpp
|
|
||||||
shadow-utility/src/*.cpp
|
|
||||||
)
|
|
||||||
FILE(GLOB_RECURSE HEADERS
|
|
||||||
core/inc/*.h
|
|
||||||
shadow-entity/inc/*.h
|
|
||||||
shadow-renderer/inc/*.h
|
|
||||||
shadow-reflection/inc/*.h
|
|
||||||
shadow-utility/inc/*.h
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(shadow-engine SHARED ${SOURCES} $<TARGET_OBJECTS:imgui>)
|
|
||||||
|
|
||||||
target_include_directories(shadow-engine
|
|
||||||
PRIVATE ${SDL2_INCLUDE_DIRS}
|
|
||||||
PUBLIC
|
|
||||||
core/inc
|
|
||||||
shadow-entity/inc
|
|
||||||
shadow-renderer/inc
|
|
||||||
shadow-reflection/inc
|
|
||||||
shadow-utility/inc
|
|
||||||
${glm_SOURCE_DIR}
|
|
||||||
INTERFACE
|
|
||||||
${imgui_SOURCE_DIR}
|
|
||||||
${imgui_SOURCE_DIR}/backends)
|
|
||||||
|
|
||||||
target_link_libraries(shadow-engine
|
|
||||||
PUBLIC Vulkan::Vulkan SDL2::SDL2 spdlog dylib imgui
|
|
||||||
)
|
|
||||||
target_compile_definitions(shadow-engine PRIVATE "EXPORTING_SH_ENGINE")
|
|
||||||
|
|
||||||
target_link_options(shadow-engine PUBLIC -Wl,--export-all-symbols)
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
#ifndef UMBRA_MODULE_H
|
|
||||||
#define UMBRA_MODULE_H
|
|
||||||
|
|
||||||
#include "SHObject.h"
|
|
||||||
#include "SDL_events.h"
|
|
||||||
#include <memory>
|
|
||||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ShadowModules are the base of the engine. They add core abilities.
|
|
||||||
/// </summary>
|
|
||||||
class Module : public SHObject
|
|
||||||
{
|
|
||||||
SHObject_Base(Module)
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pre Init is called when the module is added to the engine
|
|
||||||
/// </summary>
|
|
||||||
virtual void PreInit() = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Init is called after all the modules are added
|
|
||||||
/// </summary>
|
|
||||||
virtual void Init() = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// update is called each frame
|
|
||||||
/// </summary>
|
|
||||||
virtual void Update(int frame) = 0;
|
|
||||||
|
|
||||||
virtual void Recreate() = 0;
|
|
||||||
|
|
||||||
virtual void PreRender() = 0;
|
|
||||||
|
|
||||||
virtual void Render(VkCommandBuffer& commands, int frame) = 0;
|
|
||||||
|
|
||||||
virtual void LateRender(VkCommandBuffer& commands, int frame) = 0;
|
|
||||||
|
|
||||||
virtual void OverlayRender() = 0;
|
|
||||||
|
|
||||||
virtual void AfterFrameEnd() = 0;
|
|
||||||
|
|
||||||
virtual void Destroy() = 0;
|
|
||||||
|
|
||||||
virtual void Event(SDL_Event* e) = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the name of the module
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
virtual std::string GetName() {
|
|
||||||
return this->GetType();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class especially for modules that are renderers.
|
|
||||||
* Allows the engine to access state from the renderer independent of implementation.
|
|
||||||
*/
|
|
||||||
class RendererModule : public Module {
|
|
||||||
public:
|
|
||||||
// Begin the render pass using the given commands.
|
|
||||||
// Will call out through the regular modules to gather geometry to render.
|
|
||||||
virtual void BeginRenderPass(const std::unique_ptr<vlkx::RenderCommand>& commands) = 0;
|
|
||||||
|
|
||||||
virtual void EnableEditor() = 0;
|
|
||||||
|
|
||||||
virtual VkExtent2D GetRenderExtent() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // ShadowEngine
|
|
||||||
|
|
||||||
#endif //UMBRA_MODULE_H
|
|
|
@ -1,68 +0,0 @@
|
||||||
#ifndef UMBRA_MODULEMANAGER_H
|
|
||||||
#define UMBRA_MODULEMANAGER_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <list>
|
|
||||||
#include "Module.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
struct ModuleRef{
|
|
||||||
public:
|
|
||||||
std::shared_ptr<Module> module;
|
|
||||||
std::string domain;
|
|
||||||
|
|
||||||
// Reinterpret this module as if it were a Renderer Module.
|
|
||||||
// A shortcut for `std::static_pointer_cast<std::shared_ptr<RendererModule>>(ShadowEngine::ModuleManager::instance->GetModule("renderer"))
|
|
||||||
std::shared_ptr<RendererModule> operator->() const { return std::static_pointer_cast<RendererModule>(module); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModuleManager {
|
|
||||||
public:
|
|
||||||
static API ModuleManager *instance;
|
|
||||||
static ModuleManager* getInstance() { return instance; }
|
|
||||||
|
|
||||||
std::list<ModuleRef> modules;
|
|
||||||
ModuleRef renderer;
|
|
||||||
|
|
||||||
ModuleManager();
|
|
||||||
|
|
||||||
~ModuleManager();
|
|
||||||
|
|
||||||
void PushModule(const std::shared_ptr<Module>& module, const std::string& domain);
|
|
||||||
|
|
||||||
Module &GetModule(const std::string& name);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T *GetModuleByType() {
|
|
||||||
for (auto &module: modules) {
|
|
||||||
if (module.module->GetTypeId() == T::TypeId())
|
|
||||||
return dynamic_cast<T *>(module.module.get());
|
|
||||||
}
|
|
||||||
//SH_CORE_ERROR("Can't find the module {0}", T::Type());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Init();
|
|
||||||
|
|
||||||
void Update(int frame);
|
|
||||||
|
|
||||||
void LateRender(VkCommandBuffer& commands, int frame);
|
|
||||||
|
|
||||||
void OverlayRender();
|
|
||||||
|
|
||||||
void Recreate();
|
|
||||||
|
|
||||||
void Render(VkCommandBuffer& commands, int frame);
|
|
||||||
|
|
||||||
void PreRender();
|
|
||||||
|
|
||||||
void AfterFrameEnd();
|
|
||||||
|
|
||||||
void Destroy();
|
|
||||||
|
|
||||||
void Event(SDL_Event* evt);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //UMBRA_MODULEMANAGER_H
|
|
|
@ -1,49 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 30/08/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef UMBRA_SDL2MODULE_H
|
|
||||||
#define UMBRA_SDL2MODULE_H
|
|
||||||
|
|
||||||
#include "Module.h"
|
|
||||||
#include "ShadowWindow.h"
|
|
||||||
|
|
||||||
#include "SDL.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
class SDL2Module : public Module {
|
|
||||||
SHObject_Base(SDL2Module)
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShadowEngine::ShadowWindow* _window;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Init() override;
|
|
||||||
|
|
||||||
void PreInit() override;
|
|
||||||
|
|
||||||
void Update(int frame) override;
|
|
||||||
|
|
||||||
void Recreate() override;
|
|
||||||
|
|
||||||
void Render(VkCommandBuffer& commands, int frame) override;
|
|
||||||
|
|
||||||
void OverlayRender() override;
|
|
||||||
|
|
||||||
void LateRender(VkCommandBuffer& commands, int frame) override;
|
|
||||||
|
|
||||||
std::string GetName() override;
|
|
||||||
|
|
||||||
void AfterFrameEnd() override;
|
|
||||||
|
|
||||||
void PreRender() override;
|
|
||||||
|
|
||||||
void Destroy() override;
|
|
||||||
|
|
||||||
void Event(SDL_Event* e) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //UMBRA_SDL2MODULE_H
|
|
|
@ -1,25 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "SDL.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
class ShadowWindow {
|
|
||||||
public:
|
|
||||||
|
|
||||||
int Height;
|
|
||||||
int Width;
|
|
||||||
|
|
||||||
SDL_Window *sdlWindowPtr;
|
|
||||||
|
|
||||||
SDL_Surface *sdlSurface = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
//ShadowEngine::Ref<ShadowEngine::Rendering::GraphicsContext> context;
|
|
||||||
|
|
||||||
ShadowWindow(int W, int H);
|
|
||||||
|
|
||||||
~ShadowWindow();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
#include "exports.h"
|
|
||||||
|
|
||||||
class Time
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
static API int NOW;
|
|
||||||
static API int LAST;
|
|
||||||
|
|
||||||
static API double deltaTime;
|
|
||||||
static API double deltaTime_ms;
|
|
||||||
|
|
||||||
static API double timeSinceStart;
|
|
||||||
static API double startTime;
|
|
||||||
|
|
||||||
static void UpdateTime();
|
|
||||||
};
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 31/08/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef UMBRA_DEBUGMODULE_H
|
|
||||||
#define UMBRA_DEBUGMODULE_H
|
|
||||||
|
|
||||||
#include "SDL_events.h"
|
|
||||||
#include "core/Module.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine::Debug {
|
|
||||||
|
|
||||||
class DebugModule : public Module {
|
|
||||||
|
|
||||||
SHObject_Base(DebugModule)
|
|
||||||
|
|
||||||
bool active;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void Render(VkCommandBuffer& commands, int frame) override {};
|
|
||||||
|
|
||||||
void PreInit() override { };
|
|
||||||
|
|
||||||
void Init() override { };
|
|
||||||
|
|
||||||
void Recreate() override {};
|
|
||||||
|
|
||||||
void OverlayRender() override;
|
|
||||||
|
|
||||||
void Update(int frame) override { };
|
|
||||||
|
|
||||||
void LateRender(VkCommandBuffer& commands, int frame) override { };
|
|
||||||
|
|
||||||
void AfterFrameEnd() override { };
|
|
||||||
|
|
||||||
void PreRender() override { };
|
|
||||||
|
|
||||||
void Destroy() override {};
|
|
||||||
|
|
||||||
void Event(SDL_Event* e) override {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //UMBRA_DEBUGMODULE_H
|
|
|
@ -1,16 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 04/09/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
# if defined(EXPORTING_SH_ENGINE)
|
|
||||||
# define API __declspec(dllexport)
|
|
||||||
# else
|
|
||||||
# define API __declspec(dllimport)
|
|
||||||
# endif
|
|
||||||
#else // non windows
|
|
||||||
# define SH_EXPORT
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// ShadowEntity temporary namespace
|
|
||||||
namespace SE {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universally Unique ID.
|
|
||||||
* 128 Bits.
|
|
||||||
*
|
|
||||||
* Unique per runtime only - the suitability for serialization is undetermined.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class UUID {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data storage; 128 bits.
|
|
||||||
* 2 x 64 bit
|
|
||||||
* 4 x 32 bit
|
|
||||||
* 16 x 8 bit
|
|
||||||
*/
|
|
||||||
|
|
||||||
union Data {
|
|
||||||
uint64_t u64[2];
|
|
||||||
uint32_t u32[4];
|
|
||||||
uint8_t u8[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Create a new, unused, UUID.
|
|
||||||
static UUID Generate();
|
|
||||||
// Check whether the UUID is correctly formed.
|
|
||||||
static bool IsValidStr(char const* str);
|
|
||||||
|
|
||||||
// Create an empty UUID.
|
|
||||||
inline UUID() { std::memset(&data.u8, 0, 16); }
|
|
||||||
// Create a UUID based on the given values.
|
|
||||||
inline UUID(uint64_t i0, uint64_t i1) { data.u64[0] = i0; data.u64[1] = i1; }
|
|
||||||
inline UUID(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3) { data.u32[0] = i0; data.u32[1] = i1; data.u32[2] = i2; data.u32[3] = i3; }
|
|
||||||
inline explicit UUID(std::string const& str) : UUID(str.c_str()) {}
|
|
||||||
// Create a UUID from the given format.
|
|
||||||
explicit UUID(char const* str);
|
|
||||||
|
|
||||||
// Check whether the UUID is nonzero.
|
|
||||||
inline bool IsValid() const { return data.u64[0] != 0 && data.u64[1] != 0; }
|
|
||||||
// Set the UUID to zero.
|
|
||||||
inline void Clear() { std::memset(&data.u8, 0, 16); }
|
|
||||||
|
|
||||||
// Get a section of the UUID's data as the given bit width.
|
|
||||||
inline uint8_t GetU8(size_t idx) const { return data.u8[idx]; }
|
|
||||||
inline uint32_t GetU32(size_t idx) const { return data.u32[idx]; }
|
|
||||||
inline uint64_t GetU64(size_t idx) const { return data.u64[idx]; }
|
|
||||||
|
|
||||||
// Check whether this and a given UUID are in/equal.
|
|
||||||
__inline bool operator==(UUID const& other) const { return data.u64[0] == other.data.u64[0] && data.u64[1] == other.data.u64[1]; }
|
|
||||||
__inline bool operator!=(UUID const& other) const { return !(*this == other); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Data data {};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,348 +0,0 @@
|
||||||
#include "ShadowApplication.h"
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "stb_image.h"
|
|
||||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
||||||
#include "time.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_vulkan.h"
|
|
||||||
#include "imgui_impl_sdl.h"
|
|
||||||
#include <vlkx/vulkan/VulkanManager.h>
|
|
||||||
#include "vlkx/render/Camera.h"
|
|
||||||
#include "vlkx/render/render_pass/ScreenRenderPass.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
|
||||||
#include "vlkx/render/Geometry.h"
|
|
||||||
#include "temp/model/Builder.h"
|
|
||||||
|
|
||||||
#define CATCH(x) \
|
|
||||||
try { x } catch (std::exception& e) { spdlog::error(e.what()); exit(0); }
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
struct SkyboxTransform {
|
|
||||||
alignas(sizeof(glm::mat4)) glm::mat4 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlanetTransform {
|
|
||||||
alignas(sizeof(glm::mat4)) glm::mat4 model;
|
|
||||||
alignas(sizeof(glm::mat4)) glm::mat4 projection;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
alignas(sizeof(glm::vec4)) glm::vec4 dirTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::ScreenRenderPassManager> passManager;
|
|
||||||
std::unique_ptr<vlkx::RenderCommand> renderCommands;
|
|
||||||
std::unique_ptr<vlkx::UserPerspectiveCamera> camera;
|
|
||||||
std::unique_ptr<vlkx::UniformBuffer> light;
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::PushConstant> skyboxConstant;
|
|
||||||
std::unique_ptr<vlkx::PushConstant> planetConstant;
|
|
||||||
std::unique_ptr<vlkxtemp::Model> skyboxModel;
|
|
||||||
std::unique_ptr<vlkxtemp::Model> planetModel;
|
|
||||||
std::unique_ptr<vlkxtemp::Model> asteroidModel;
|
|
||||||
|
|
||||||
float aspectRatio;
|
|
||||||
|
|
||||||
ShadowApplication* ShadowApplication::instance = nullptr;
|
|
||||||
|
|
||||||
ShadowApplication::ShadowApplication(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
if(argc > 1)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < argc; i++)
|
|
||||||
{
|
|
||||||
std::string param(argv[i]);
|
|
||||||
if(param == "-no-gui")
|
|
||||||
{
|
|
||||||
this->no_gui = true;
|
|
||||||
}
|
|
||||||
if(param == "-game")
|
|
||||||
{
|
|
||||||
this->game = argv[i+1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//game = _setupFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ShadowApplication::~ShadowApplication()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::Init()
|
|
||||||
{
|
|
||||||
// Initialize SDL. SDL_Init will return -1 if it fails.
|
|
||||||
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
|
|
||||||
//std::cout << "Error initializing SDL: " << SDL_GetError() << std::endl;
|
|
||||||
//system("pause");
|
|
||||||
// End the program
|
|
||||||
//return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
window_ = new ShadowWindow(800,800);
|
|
||||||
|
|
||||||
CATCH(VulkanManager::getInstance()->initVulkan(window_->sdlWindowPtr);)
|
|
||||||
|
|
||||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
|
||||||
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
VkDescriptorPool imGuiPool;
|
|
||||||
VulkanManager* vk = VulkanManager::getInstance();
|
|
||||||
VkDescriptorPoolSize pool_sizes[] =
|
|
||||||
{
|
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDescriptorPoolCreateInfo pool_info = {};
|
|
||||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
||||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
|
||||||
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
|
|
||||||
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
|
|
||||||
pool_info.pPoolSizes = pool_sizes;
|
|
||||||
vkCreateDescriptorPool(vk->getDevice()->logical, &pool_info, VK_NULL_HANDLE, &imGuiPool);
|
|
||||||
|
|
||||||
// Setup Platform/Renderer backends
|
|
||||||
ImGui_ImplSDL2_InitForVulkan(window_->sdlWindowPtr);
|
|
||||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
|
||||||
init_info.Instance = vk->getVulkan();
|
|
||||||
init_info.PhysicalDevice = vk->getDevice()->physical;
|
|
||||||
init_info.Device = vk->getDevice()->logical;
|
|
||||||
init_info.QueueFamily = vk->getDevice()->queueData.graphics;
|
|
||||||
init_info.Queue = vk->getDevice()->graphicsQueue;
|
|
||||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
|
||||||
init_info.DescriptorPool = imGuiPool;
|
|
||||||
init_info.Subpass = 1;
|
|
||||||
init_info.MinImageCount = vk->getSwapchain()->images.size();
|
|
||||||
init_info.ImageCount = vk->getSwapchain()->images.size();
|
|
||||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
init_info.Allocator = VK_NULL_HANDLE;
|
|
||||||
init_info.CheckVkResultFn = nullptr;
|
|
||||||
|
|
||||||
vlkxtemp::ModelBuilder::ShaderPool pool;
|
|
||||||
|
|
||||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
|
||||||
skyboxConstant = std::make_unique<vlkx::PushConstant>(sizeof(SkyboxTransform), 2);
|
|
||||||
planetConstant = std::make_unique<vlkx::PushConstant>(sizeof(PlanetTransform), 2);
|
|
||||||
light = std::make_unique<vlkx::UniformBuffer>(sizeof(Light), 2);
|
|
||||||
|
|
||||||
aspectRatio = (float) window_->Width / window_->Height;
|
|
||||||
|
|
||||||
vlkx::Camera::Config conf {};
|
|
||||||
camera = vlkx::UserPerspectiveCamera::create( {}, {}, { 110, aspectRatio });
|
|
||||||
|
|
||||||
using vlkxtemp::ModelBuilder;
|
|
||||||
|
|
||||||
|
|
||||||
const vlkx::RefCountedTexture::CubemapLocation skybox {
|
|
||||||
"resources/planets/bg",
|
|
||||||
{
|
|
||||||
"left.png", "right.png",
|
|
||||||
"top.png", "bottom.png",
|
|
||||||
"front.png", "back.png"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
skyboxModel = ModelBuilder {
|
|
||||||
"Skybox", 2, aspectRatio,
|
|
||||||
ModelBuilder::SingleMeshModel { "resources/planets/skybox.obj", 1,
|
|
||||||
{ { ModelBuilder::TextureType::Cubemap, { { skybox } } } }
|
|
||||||
}}
|
|
||||||
.bindTextures(ModelBuilder::TextureType::Cubemap, 1)
|
|
||||||
.pushStage(VK_SHADER_STAGE_VERTEX_BIT)
|
|
||||||
.pushConstant(skyboxConstant.get(), 0)
|
|
||||||
.shader(VK_SHADER_STAGE_VERTEX_BIT, "resources/planets/skybox.vert.spv")
|
|
||||||
.shader(VK_SHADER_STAGE_FRAGMENT_BIT, "resources/planets/skybox.frag.spv")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
planetModel = ModelBuilder {
|
|
||||||
"Walrus", 2, aspectRatio,
|
|
||||||
ModelBuilder::SingleMeshModel {"resources/walrus/walrus.obj", 1,
|
|
||||||
{{ ModelBuilder::TextureType::Diffuse, { { "resources/walrus/texture.png" } } } }
|
|
||||||
}}
|
|
||||||
.bindTextures(ModelBuilder::TextureType::Diffuse, 2)
|
|
||||||
.uniform(VK_SHADER_STAGE_FRAGMENT_BIT, {{1, 1}})
|
|
||||||
.uniformBuffer(1, *light)
|
|
||||||
.pushStage(VK_SHADER_STAGE_VERTEX_BIT)
|
|
||||||
.pushConstant(planetConstant.get(), 0)
|
|
||||||
.shader(VK_SHADER_STAGE_VERTEX_BIT, "resources/walrus/walrus.vert.spv")
|
|
||||||
.shader(VK_SHADER_STAGE_FRAGMENT_BIT, "resources/walrus/walrus.frag.spv")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
passManager = std::make_unique<vlkx::ScreenRenderPassManager>(vlkx::RendererConfig { 2 });
|
|
||||||
|
|
||||||
passManager->initializeRenderPass();
|
|
||||||
|
|
||||||
skyboxModel->update(true, VulkanManager::getInstance()->getSwapchain()->extent, VK_SAMPLE_COUNT_1_BIT, *passManager->getPass(), 0);
|
|
||||||
int cursorX, cursorY;
|
|
||||||
SDL_GetMouseState(&cursorX, &cursorY);
|
|
||||||
camera->setPos({ cursorX, cursorY });
|
|
||||||
|
|
||||||
planetModel->update(true, VulkanManager::getInstance()->getSwapchain()->extent, VK_SAMPLE_COUNT_1_BIT, *passManager->getPass(), 0);
|
|
||||||
|
|
||||||
ImGui_ImplVulkan_Init(&init_info, **passManager->getPass());
|
|
||||||
// Upload Fonts
|
|
||||||
VkTools::immediateExecute([](const VkCommandBuffer& commands) { ImGui_ImplVulkan_CreateFontsTexture(commands); }, VulkanManager::getInstance()->getDevice());
|
|
||||||
|
|
||||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printMatrix(glm::mat4 mat) {
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
|
||||||
for (size_t j = 0; j < 4; j++) {
|
|
||||||
std::cout << mat[i][j] << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << std::endl << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateData(int frame) {
|
|
||||||
const float elapsed_time = Time::timeSinceStart;
|
|
||||||
|
|
||||||
const glm::vec3 lightDir{glm::sin(elapsed_time * 0.0006f), -0.3f,
|
|
||||||
glm::cos(elapsed_time * 0.0006f)};
|
|
||||||
*light->getData<Light>(frame) =
|
|
||||||
{glm::vec4{lightDir, elapsed_time}};
|
|
||||||
light->upload(frame);
|
|
||||||
|
|
||||||
glm::mat4 modelMatrix { 1 };
|
|
||||||
modelMatrix = glm::rotate(modelMatrix, elapsed_time * glm::radians(0.0005f), glm::vec3 { 0, 1, 0 });
|
|
||||||
const vlkx::Camera& cam = camera->getCamera();
|
|
||||||
|
|
||||||
const glm::mat4 view = glm::lookAt(glm::vec3{3.0f}, glm::vec3{0.0f},
|
|
||||||
glm::vec3{0.0f, 1.0f, 0.0f});
|
|
||||||
const glm::mat4 proj = glm::perspective(
|
|
||||||
glm::radians(45.0f), aspectRatio,
|
|
||||||
0.1f, 100.0f);
|
|
||||||
|
|
||||||
glm::mat4 planetProjection = cam.getProjMatrix() * cam.getViewMatrix();
|
|
||||||
*planetConstant->getData<PlanetTransform>(frame) = { modelMatrix, planetProjection };
|
|
||||||
glm::mat4 skyboxMat = cam.getProjMatrix() * cam.getSkyboxView();
|
|
||||||
skyboxConstant->getData<SkyboxTransform>(frame)->value = skyboxMat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void imGuiStartDraw() {
|
|
||||||
ImGui_ImplVulkan_NewFrame();
|
|
||||||
ImGui_ImplSDL2_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
}
|
|
||||||
void imGuiEndDraw(const VkCommandBuffer& commands) {
|
|
||||||
ImGui::Render();
|
|
||||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commands);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showDebugWindow(std::unique_ptr<vlkx::UserPerspectiveCamera>* cam) {
|
|
||||||
#define camPos cam->get()->getCamera().getPosition()
|
|
||||||
#define camFwd cam->get()->getCamera().getForward()
|
|
||||||
ImGui::Begin("Camera Debug");
|
|
||||||
ImGui::Text("Camera position: (x %f, y %f, z %f)", camPos.x, camPos.y, camPos.z );
|
|
||||||
ImGui::Text("Camera target: (x %f, y %f, z %f)", camPos.x + camFwd.x, camPos.y + camFwd.y, camPos.z + camFwd.z);
|
|
||||||
ImGui::Text("Camera FOV: %f", cam->get()->getCamera().getFieldOfView());
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::PollEvents() {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) { // poll until all events are handled!
|
|
||||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
||||||
|
|
||||||
switch(event.type) {
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
switch (event.key.keysym.sym) {
|
|
||||||
case SDLK_ESCAPE:
|
|
||||||
camera->reset(); break;
|
|
||||||
case SDLK_w:
|
|
||||||
camera->press(vlkx::Camera::Input::Up, Time::deltaTime); break;
|
|
||||||
case SDLK_s:
|
|
||||||
camera->press(vlkx::Camera::Input::Down, Time::deltaTime); break;
|
|
||||||
case SDLK_a:
|
|
||||||
camera->press(vlkx::Camera::Input::Left, Time::deltaTime); break;
|
|
||||||
case SDLK_d:
|
|
||||||
camera->press(vlkx::Camera::Input::Right, Time::deltaTime); break;
|
|
||||||
} break;
|
|
||||||
case SDL_MOUSEMOTION:
|
|
||||||
camera->move(-event.motion.xrel, -event.motion.yrel); break;
|
|
||||||
|
|
||||||
case SDL_MOUSEWHEEL:
|
|
||||||
camera->scroll(event.wheel.y, 1, 170); break;
|
|
||||||
case SDL_QUIT:
|
|
||||||
running = false; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::Start()
|
|
||||||
{
|
|
||||||
const auto update = [](const int frame) { updateData(frame); };
|
|
||||||
|
|
||||||
while (running)
|
|
||||||
{
|
|
||||||
PollEvents();
|
|
||||||
|
|
||||||
const auto result = renderCommands->execute(renderCommands->getFrame(), VulkanManager::getInstance()->getSwapchain()->swapChain, update,
|
|
||||||
[](const VkCommandBuffer& buffer, uint32_t frame) {
|
|
||||||
passManager->getPass()->execute(buffer, frame, {
|
|
||||||
// Render our model
|
|
||||||
[&frame](const VkCommandBuffer& commands) {
|
|
||||||
planetModel->draw(commands, frame, 1);
|
|
||||||
skyboxModel->draw(commands, frame, 1);
|
|
||||||
},
|
|
||||||
// Render ImGUI
|
|
||||||
[&](const VkCommandBuffer& commands) {
|
|
||||||
imGuiStartDraw();
|
|
||||||
|
|
||||||
bool showDemo = true;
|
|
||||||
//if (showDemo)
|
|
||||||
// ImGui::ShowDemoWindow(&showDemo);
|
|
||||||
|
|
||||||
showDebugWindow(&camera);
|
|
||||||
|
|
||||||
imGuiEndDraw(commands);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.has_value())
|
|
||||||
throw std::runtime_error("Resizing is not implemented");
|
|
||||||
|
|
||||||
renderCommands->nextFrame();
|
|
||||||
|
|
||||||
Time::UpdateTime();
|
|
||||||
|
|
||||||
camera->active(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
vkDeviceWaitIdle(VulkanManager::getInstance()->getDevice()->logical);
|
|
||||||
|
|
||||||
ImGui_ImplVulkan_Shutdown();
|
|
||||||
ImGui_ImplSDL2_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
|
|
||||||
SDL_DestroyWindow(window_->sdlWindowPtr);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 2022-07-06.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "core/Module.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
SHObject_Base_Impl(Module)
|
|
||||||
|
|
||||||
} // ShadowEngine
|
|
|
@ -1,123 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 2022-07-06.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "core/ModuleManager.h"
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
ShadowEngine::ModuleManager* ShadowEngine::ModuleManager::instance = nullptr;
|
|
||||||
|
|
||||||
ShadowEngine::ModuleManager::ModuleManager()
|
|
||||||
{
|
|
||||||
if (instance != nullptr)
|
|
||||||
{
|
|
||||||
//ERROR
|
|
||||||
}
|
|
||||||
instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowEngine::ModuleManager::~ModuleManager()
|
|
||||||
= default;
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::PushModule(const std::shared_ptr<Module>& module, const std::string& domain)
|
|
||||||
{
|
|
||||||
ModuleRef r = {module, domain};
|
|
||||||
modules.emplace_back(r);
|
|
||||||
if (domain == "renderer")
|
|
||||||
renderer = r;
|
|
||||||
module->PreInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowEngine::Module& ShadowEngine::ModuleManager::GetModule(const std::string& name)
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
if (module.module->GetName() == name)
|
|
||||||
return *module.module;
|
|
||||||
}
|
|
||||||
//SH_ASSERT(false, "Can't find the module");
|
|
||||||
throw std::runtime_error("Can't find the module");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Init()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Destroy()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::PreRender()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->PreRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Event(SDL_Event* evt)
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Event(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Update(int frame)
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Update(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::LateRender(VkCommandBuffer& commands, int frame)
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->LateRender(commands, frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Render(VkCommandBuffer& commands, int frame)
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Render(commands, frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::OverlayRender()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->OverlayRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::Recreate()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->Recreate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ShadowEngine::ModuleManager::AfterFrameEnd()
|
|
||||||
{
|
|
||||||
for (auto& module : modules)
|
|
||||||
{
|
|
||||||
module.module->AfterFrameEnd();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 30/08/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "core/SDL2Module.h"
|
|
||||||
#include "core/ShadowWindow.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "imgui_impl_sdl.h"
|
|
||||||
|
|
||||||
SHObject_Base_Impl(ShadowEngine::SDL2Module)
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::PreInit() {
|
|
||||||
// Initialize SDL. SDL_Init will return -1 if it fails.
|
|
||||||
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
|
|
||||||
spdlog::error("Error creating window: " + std::string(SDL_GetError()));
|
|
||||||
//system("pause");
|
|
||||||
// End the program
|
|
||||||
//return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_window = new ShadowWindow(1280,720);
|
|
||||||
SDL_SetWindowResizable(_window->sdlWindowPtr, SDL_TRUE);
|
|
||||||
//SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Update(int frame) {}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Recreate() {}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Render(VkCommandBuffer& commands, int frame) {}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::OverlayRender() {}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::LateRender(VkCommandBuffer& commands, int frame) {}
|
|
||||||
|
|
||||||
std::string ShadowEngine::SDL2Module::GetName() {
|
|
||||||
return this->GetType();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::AfterFrameEnd() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Event(SDL_Event *e) {
|
|
||||||
ImGui_ImplSDL2_ProcessEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::Destroy() {
|
|
||||||
SDL_DestroyWindow(_window->sdlWindowPtr);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowEngine::SDL2Module::PreRender() {
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
|
|
||||||
#include "core/ShadowApplication.h"
|
|
||||||
#include "core/Time.h"
|
|
||||||
#include "core/SDL2Module.h"
|
|
||||||
#include "debug/DebugModule.h"
|
|
||||||
#include "dylib.hpp"
|
|
||||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_impl_sdl.h>
|
|
||||||
#include <vlkx/vulkan/VulkanModule.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
|
|
||||||
#define CATCH(x) \
|
|
||||||
try { x } catch (std::exception& e) { spdlog::error(e.what()); exit(0); }
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
dylib* gameLib;
|
|
||||||
|
|
||||||
ShadowApplication* ShadowApplication::instance = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::RenderCommand> renderCommands;
|
|
||||||
|
|
||||||
ShadowApplication::ShadowApplication(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
if(argc > 1)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < argc; i++)
|
|
||||||
{
|
|
||||||
std::string param(argv[i]);
|
|
||||||
if(param == "-no-gui")
|
|
||||||
{
|
|
||||||
this->no_gui = true;
|
|
||||||
}
|
|
||||||
if(param == "-game")
|
|
||||||
{
|
|
||||||
this->game = argv[i+1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowApplication::~ShadowApplication()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::loadGame(){
|
|
||||||
if(game.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
void (*gameInti)(ShadowApplication*);
|
|
||||||
|
|
||||||
try {
|
|
||||||
gameLib = new dylib("./", game);
|
|
||||||
|
|
||||||
gameInti = gameLib->get_function<void(ShadowApplication*)>("shadow_main");
|
|
||||||
|
|
||||||
gameInti(this);
|
|
||||||
}
|
|
||||||
catch (std::exception& e) {
|
|
||||||
spdlog::error(e.what());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::Init()
|
|
||||||
{
|
|
||||||
moduleManager.PushModule(std::make_shared<SDL2Module>(),"core");
|
|
||||||
auto renderer = std::make_shared<VulkanModule>();
|
|
||||||
renderer->EnableEditor();
|
|
||||||
moduleManager.PushModule(renderer, "renderer");
|
|
||||||
|
|
||||||
loadGame();
|
|
||||||
|
|
||||||
moduleManager.PushModule(std::make_shared<Debug::DebugModule>(), "core");
|
|
||||||
|
|
||||||
moduleManager.Init();
|
|
||||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowApplication::Start()
|
|
||||||
{
|
|
||||||
SDL_Event event;
|
|
||||||
while (running)
|
|
||||||
{
|
|
||||||
while (SDL_PollEvent(&event)) { // poll until all events are handled!
|
|
||||||
moduleManager.Event(&event);
|
|
||||||
if (event.type == SDL_QUIT)
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleManager.PreRender();
|
|
||||||
|
|
||||||
moduleManager.renderer->BeginRenderPass(renderCommands);
|
|
||||||
|
|
||||||
moduleManager.AfterFrameEnd();
|
|
||||||
|
|
||||||
renderCommands->nextFrame();
|
|
||||||
Time::UpdateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleManager.Destroy();
|
|
||||||
|
|
||||||
delete gameLib;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowApplication& ShadowApplication::Get() { return *instance; };
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#include "core/ShadowWindow.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
ShadowEngine::ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
|
||||||
{
|
|
||||||
// Create our window
|
|
||||||
sdlWindowPtr = SDL_CreateWindow( "Candlefire", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Width, Height, SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN );
|
|
||||||
|
|
||||||
// Make sure creating the window succeeded
|
|
||||||
if ( !sdlWindowPtr ) {
|
|
||||||
//Raise an error in the log
|
|
||||||
spdlog::error("Error creating window: " + std::string(SDL_GetError()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowEngine::ShadowWindow::~ShadowWindow()
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#include "core/Time.h"
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
API int Time::NOW = 0;//SDL_GetPerformanceCounter();
|
|
||||||
API int Time::LAST = 0;
|
|
||||||
API double lastFrame = 0;
|
|
||||||
API double Time::deltaTime_ms = 0;
|
|
||||||
API double Time::deltaTime = 0;
|
|
||||||
API double Time::startTime = 0;
|
|
||||||
API double Time::timeSinceStart = 0;
|
|
||||||
|
|
||||||
void Time::UpdateTime()
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
auto now = system_clock::now();
|
|
||||||
auto now_ms = time_point_cast<milliseconds>(now);
|
|
||||||
|
|
||||||
auto value = now_ms.time_since_epoch();
|
|
||||||
double duration = value.count();
|
|
||||||
|
|
||||||
deltaTime = duration - lastFrame;
|
|
||||||
if (startTime == 0)
|
|
||||||
startTime = duration;
|
|
||||||
timeSinceStart = duration - startTime;
|
|
||||||
|
|
||||||
lastFrame = duration;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// Created by dpete on 31/08/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "debug/DebugModule.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "core/Time.h"
|
|
||||||
#include "core/ModuleManager.h"
|
|
||||||
|
|
||||||
SHObject_Base_Impl(ShadowEngine::Debug::DebugModule)
|
|
||||||
|
|
||||||
void ShadowEngine::Debug::DebugModule::OverlayRender() {
|
|
||||||
|
|
||||||
if (ImGui::Begin("Time", &active, ImGuiWindowFlags_MenuBar)) {
|
|
||||||
ImGui::Text("delta time in ms: %lf", Time::deltaTime_ms);
|
|
||||||
ImGui::Text("delta time in s: %lf", Time::deltaTime);
|
|
||||||
ImGui::Text("LAST time in: %d", Time::LAST);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
|
|
||||||
if (ImGui::Begin("Active Modules", &active, ImGuiWindowFlags_MenuBar)) {
|
|
||||||
|
|
||||||
ShadowEngine::ModuleManager *m = ShadowEngine::ModuleManager::instance;
|
|
||||||
|
|
||||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.4f, 1.0f), "Active Modules:");
|
|
||||||
for (auto &module: m->modules) {
|
|
||||||
ImGui::Text("%s", module.module->GetName().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
#include <id/UUID.h>
|
|
||||||
|
|
||||||
namespace SE {
|
|
||||||
static_assert(sizeof(UUID) == 16, "UUID has incorrect size");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify that a string has the correct format;
|
|
||||||
* XXXXXXXX-XXXX-XXX-XXXXX-XXXXXXXXXXXX
|
|
||||||
*
|
|
||||||
* The length must be 36.
|
|
||||||
* There must be dashes at index 8, 13, 18 and 23.
|
|
||||||
* @param str the input string
|
|
||||||
* @return whether the UUID string is correctly formed
|
|
||||||
*/
|
|
||||||
bool UUID::IsValidStr(const char *str) {
|
|
||||||
size_t const len = strlen(str);
|
|
||||||
if (len != 36) return false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
char c = str[i];
|
|
||||||
if (c == '-') {
|
|
||||||
if (i != 8 && i != 13 && i != 18 && i != 23) return false;
|
|
||||||
} else if (! std::isxdigit(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(char const* str ) {
|
|
||||||
// A single byte is two hex characters.
|
|
||||||
// Store them here so that we can use them later.
|
|
||||||
char c0 = '\0', c1;
|
|
||||||
|
|
||||||
size_t const len = strlen( str );
|
|
||||||
uint32_t byteIdx = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++ ) {
|
|
||||||
char const c = str[i];
|
|
||||||
if ( c == '-' )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Scan for pairs of characters.
|
|
||||||
// Only assign a byte if two have been parsed.
|
|
||||||
if (c0 == '\0') {
|
|
||||||
c0 = c;
|
|
||||||
} else {
|
|
||||||
c1 = c;
|
|
||||||
data.u8[byteIdx++] = std::stoi(std::string(c0, c1));
|
|
||||||
// Reset the first char so that we can return to scanning a pair.
|
|
||||||
c0 = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <id/UUID.h> // Shadow-Engine/core/inc/id
|
|
||||||
|
|
||||||
namespace SE {
|
|
||||||
|
|
||||||
// An ID for a section of the scene (which may be unloaded separately of the level; for ie. open world Cells.)
|
|
||||||
using EntitySectionID = UUID;
|
|
||||||
// An ID for a scene (which contains all sections and entities).
|
|
||||||
using EntitySceneID = UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains the common data and functions for the Entity System's identifiers.
|
|
||||||
*/
|
|
||||||
struct IDContainer {
|
|
||||||
public:
|
|
||||||
IDContainer() = default;
|
|
||||||
explicit IDContainer(uint64_t v) : id(v) {}
|
|
||||||
|
|
||||||
// Check if the ID is valid (non-zero)
|
|
||||||
__inline bool Valid() const { return id != 0; }
|
|
||||||
// Set this ID to be invalid (zero).
|
|
||||||
__inline void Invalidate() { id = 0; }
|
|
||||||
// Check for in/equality against another ID.
|
|
||||||
__inline bool operator==(IDContainer const& other) const { return id == other.id; }
|
|
||||||
__inline bool operator!=(IDContainer const& other) const { return id != other.id; }
|
|
||||||
|
|
||||||
uint64_t id;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ID used for an Entity.
|
|
||||||
* Is only guaranteed to be unique in the current scene (ie. a level change may also change the IDs of the entities within).
|
|
||||||
*/
|
|
||||||
struct EntityID : public IDContainer {
|
|
||||||
/**
|
|
||||||
* @return a new, unused Entity ID.
|
|
||||||
*/
|
|
||||||
static EntityID Generate();
|
|
||||||
|
|
||||||
EntityID() = default;
|
|
||||||
explicit EntityID(uint64_t v) : IDContainer(v) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ID used for a Component (a segment of data attached to an Entity).
|
|
||||||
* Is only guaranteed to be unique in the current scene (ie. a level change may also change the IDs of the components within).
|
|
||||||
*/
|
|
||||||
struct ComponentID : public IDContainer {
|
|
||||||
/**
|
|
||||||
* @return a new, unused Component ID.
|
|
||||||
*/
|
|
||||||
static ComponentID Generate();
|
|
||||||
|
|
||||||
ComponentID() = default;
|
|
||||||
explicit ComponentID(uint64_t v) : IDContainer(v) {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
#include <id/ID.h>
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
namespace SE {
|
|
||||||
static std::atomic<uint64_t> entityID = 1;
|
|
||||||
EntityID EntityID::Generate() {
|
|
||||||
EntityID id(entityID++);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::atomic<uint64_t> componentID = 1;
|
|
||||||
ComponentID ComponentID::Generate() {
|
|
||||||
ComponentID id(componentID++);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
|
|
||||||
# Set up Catch2 testing
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "cmake")
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
# Set up asset sourceset
|
|
||||||
FILE(GLOB_RECURSE SOURCES src/*.cpp src/*.h)
|
|
||||||
FILE(GLOB_RECURSE TESTS test/*.cpp)
|
|
||||||
|
|
||||||
add_library(shadow-asset ${SOURCES})
|
|
||||||
|
|
||||||
# Set up test executable
|
|
||||||
add_executable(shadow-asset-test ${TESTS})
|
|
||||||
target_link_libraries(shadow-asset-test PRIVATE Catch2::Catch2 shadow-utils)
|
|
||||||
|
|
||||||
# Enable testing on the executable
|
|
||||||
include(CTest)
|
|
||||||
include(Catch)
|
|
||||||
catch_discover_tests(shadow-asset-test)
|
|
|
@ -1,90 +0,0 @@
|
||||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
||||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function(catch_discover_tests TARGET)
|
|
||||||
cmake_parse_arguments(
|
|
||||||
""
|
|
||||||
""
|
|
||||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
|
|
||||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
|
|
||||||
${ARGN}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT _WORKING_DIRECTORY)
|
|
||||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
|
||||||
endif()
|
|
||||||
if(NOT _TEST_LIST)
|
|
||||||
set(_TEST_LIST ${TARGET}_TESTS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
## Generate a unique name based on the extra arguments
|
|
||||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
|
|
||||||
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
|
||||||
|
|
||||||
# Define rule to generate test list for aforementioned test executable
|
|
||||||
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
|
|
||||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
|
|
||||||
get_property(crosscompiling_emulator
|
|
||||||
TARGET ${TARGET}
|
|
||||||
PROPERTY CROSSCOMPILING_EMULATOR
|
|
||||||
)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET ${TARGET} POST_BUILD
|
|
||||||
BYPRODUCTS "${ctest_tests_file}"
|
|
||||||
COMMAND "${CMAKE_COMMAND}"
|
|
||||||
-D "TEST_TARGET=${TARGET}"
|
|
||||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
|
||||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
|
||||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
|
||||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
|
||||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
|
||||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
|
||||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
|
||||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
|
||||||
-D "TEST_LIST=${_TEST_LIST}"
|
|
||||||
-D "TEST_REPORTER=${_REPORTER}"
|
|
||||||
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
|
||||||
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
|
|
||||||
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
|
|
||||||
-D "CTEST_FILE=${ctest_tests_file}"
|
|
||||||
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
file(WRITE "${ctest_include_file}"
|
|
||||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
|
||||||
" include(\"${ctest_tests_file}\")\n"
|
|
||||||
"else()\n"
|
|
||||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
|
||||||
"endif()\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
|
||||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
|
||||||
set_property(DIRECTORY
|
|
||||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
|
||||||
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
|
||||||
if (NOT ${test_include_file_set})
|
|
||||||
set_property(DIRECTORY
|
|
||||||
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
|
||||||
CatchAddTests.cmake
|
|
||||||
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
|
|
||||||
)
|
|
|
@ -1,135 +0,0 @@
|
||||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
||||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
|
||||||
|
|
||||||
set(prefix "${TEST_PREFIX}")
|
|
||||||
set(suffix "${TEST_SUFFIX}")
|
|
||||||
set(spec ${TEST_SPEC})
|
|
||||||
set(extra_args ${TEST_EXTRA_ARGS})
|
|
||||||
set(properties ${TEST_PROPERTIES})
|
|
||||||
set(reporter ${TEST_REPORTER})
|
|
||||||
set(output_dir ${TEST_OUTPUT_DIR})
|
|
||||||
set(output_prefix ${TEST_OUTPUT_PREFIX})
|
|
||||||
set(output_suffix ${TEST_OUTPUT_SUFFIX})
|
|
||||||
set(script)
|
|
||||||
set(suite)
|
|
||||||
set(tests)
|
|
||||||
|
|
||||||
function(add_command NAME)
|
|
||||||
set(_args "")
|
|
||||||
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
|
|
||||||
math(EXPR _last_arg ${ARGC}-1)
|
|
||||||
foreach(_n RANGE 1 ${_last_arg})
|
|
||||||
set(_arg "${ARGV${_n}}")
|
|
||||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
|
||||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
|
||||||
else()
|
|
||||||
set(_args "${_args} ${_arg}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Run test executable to get list of available tests
|
|
||||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
|
||||||
OUTPUT_VARIABLE output
|
|
||||||
RESULT_VARIABLE result
|
|
||||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
|
||||||
)
|
|
||||||
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
|
|
||||||
if(${result} EQUAL 0)
|
|
||||||
message(WARNING
|
|
||||||
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
|
|
||||||
)
|
|
||||||
elseif(${result} LESS 0)
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
|
||||||
" Result: ${result}\n"
|
|
||||||
" Output: ${output}\n"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
string(REPLACE "\n" ";" output "${output}")
|
|
||||||
|
|
||||||
# Run test executable to get list of available reporters
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters
|
|
||||||
OUTPUT_VARIABLE reporters_output
|
|
||||||
RESULT_VARIABLE reporters_result
|
|
||||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
|
||||||
)
|
|
||||||
if(${reporters_result} EQUAL 0)
|
|
||||||
message(WARNING
|
|
||||||
"Test executable '${TEST_EXECUTABLE}' contains no reporters!\n"
|
|
||||||
)
|
|
||||||
elseif(${reporters_result} LESS 0)
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
|
||||||
" Result: ${reporters_result}\n"
|
|
||||||
" Output: ${reporters_output}\n"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
string(FIND "${reporters_output}" "${reporter}" reporter_is_valid)
|
|
||||||
if(reporter AND ${reporter_is_valid} EQUAL -1)
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"\"${reporter}\" is not a valid reporter!\n"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Prepare reporter
|
|
||||||
if(reporter)
|
|
||||||
set(reporter_arg "--reporter ${reporter}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Prepare output dir
|
|
||||||
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
|
|
||||||
set(output_dir "${TEST_WORKING_DIR}/${output_dir}")
|
|
||||||
if(NOT EXISTS ${output_dir})
|
|
||||||
file(MAKE_DIRECTORY ${output_dir})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Parse output
|
|
||||||
foreach(line ${output})
|
|
||||||
set(../test ${line})
|
|
||||||
# Escape characters in test case names that would be parsed by Catch2
|
|
||||||
set(test_name ${test})
|
|
||||||
#foreach(char , [ ])
|
|
||||||
#string(REPLACE ${char} "\\${char}" test_name ${test_name})
|
|
||||||
#endforeach(char)
|
|
||||||
# ...add output dir
|
|
||||||
if(output_dir)
|
|
||||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name})
|
|
||||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ...and add to script
|
|
||||||
add_command(add_test
|
|
||||||
"${prefix}${test}${suffix}"
|
|
||||||
${TEST_EXECUTOR}
|
|
||||||
"${TEST_EXECUTABLE}"
|
|
||||||
"${test_name}"
|
|
||||||
${extra_args}
|
|
||||||
"${reporter_arg}"
|
|
||||||
"${output_dir_arg}"
|
|
||||||
)
|
|
||||||
add_command(set_tests_properties
|
|
||||||
"${prefix}${test}${suffix}"
|
|
||||||
PROPERTIES
|
|
||||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
|
||||||
${properties}
|
|
||||||
)
|
|
||||||
list(APPEND tests "${prefix}${test}${suffix}")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Create a list of all discovered tests, which users may use to e.g. set
|
|
||||||
# properties on the tests
|
|
||||||
add_command(set ${TEST_LIST} ${tests})
|
|
||||||
|
|
||||||
# Write CTest script
|
|
||||||
file(WRITE "${CTEST_FILE}" "${script}")
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,61 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#include "exports.h"
|
|
||||||
|
|
||||||
namespace ShadowEngine {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This is the base class for every class in the Engine that uses runtime reflection.
|
|
||||||
|
|
||||||
* Currently it provides a runtime TypeID and TypeName witch can be accesed as static and as class memebers.
|
|
||||||
* The ID is a int type number witch is generated incramently, on the first call to get a type.
|
|
||||||
|
|
||||||
* Each class that inherits from this or it's parent inheris form it must implement the
|
|
||||||
SHObject::GetType and SHObject::GetTypeId methodes and make it's own static methodes.
|
|
||||||
To make it easier a standard implementation of these can be used with the SHObject_Base() macro
|
|
||||||
witch implements all of these functions. It uses the typeid().name of the class.
|
|
||||||
|
|
||||||
*/
|
|
||||||
class SHObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \brief Generates a new UID for each call
|
|
||||||
* \return the next Unique ID that was just generated
|
|
||||||
*/
|
|
||||||
API static uint64_t GenerateId() noexcept;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \brief Returns the top level class type name of the object
|
|
||||||
* \return The class Class name as a string
|
|
||||||
*/
|
|
||||||
virtual const std::string& GetType() const = 0;
|
|
||||||
/**
|
|
||||||
* \brief Gets the top level type ID
|
|
||||||
* \return UID of the class
|
|
||||||
*/
|
|
||||||
virtual const uint64_t GetTypeId() const = 0;
|
|
||||||
|
|
||||||
virtual ~SHObject() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Macro to make the override functions of SHObject. This should be added in each derived class
|
|
||||||
* \param type The type of the class
|
|
||||||
*/
|
|
||||||
#define SHObject_Base(type) \
|
|
||||||
public: \
|
|
||||||
static const std::string& Type(); \
|
|
||||||
static uint64_t TypeId(); \
|
|
||||||
const std::string& GetType() const override { return Type(); } \
|
|
||||||
const uint64_t GetTypeId() const override { return type::TypeId(); } \
|
|
||||||
private:
|
|
||||||
|
|
||||||
#define SHObject_Base_Impl(type) \
|
|
||||||
const std::string& type::Type() { static const std::string t = typeid(type).name(); return t; } \
|
|
||||||
uint64_t type::TypeId() { static const uint64_t id = GenerateId(); return id; }
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
#include "../inc/SHObject.h"
|
|
||||||
|
|
||||||
uint64_t ShadowEngine::SHObject::GenerateId() noexcept {
|
|
||||||
static uint64_t count = 0;
|
|
||||||
return ++count;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
|
|
||||||
=====
|
|
||||||
|
|
||||||
This folder exists to store VLKX renderer objects that are TEMPORARY.
|
|
||||||
It currently contains:
|
|
||||||
|
|
||||||
=====
|
|
||||||
|
|
||||||
Model Loader system
|
|
||||||
|
|
||||||
The Model Loader is temporarily implemented as a raw file reader and OBJ parser.
|
|
||||||
It should be removed when the File Format system is able to parse model and texture files.
|
|
||||||
|
|
||||||
====
|
|
||||||
|
|
||||||
Model Abstraction system
|
|
||||||
|
|
||||||
The Model Abstraction allows you to create a model with:
|
|
||||||
- A mesh
|
|
||||||
- An arbitrary number of textures
|
|
||||||
- A push constant
|
|
||||||
- An arbitrarily large uniform buffer
|
|
||||||
- A fragment and vertex shader
|
|
||||||
|
|
||||||
In all, it contains a custom Render Pipeline that will be used to draw the model.
|
|
||||||
This allows for drastic and visually appealing effects.
|
|
||||||
|
|
||||||
It should be maintained and moved into the appropriate Shadow module once ready.
|
|
||||||
|
|
||||||
====
|
|
||||||
|
|
||||||
Model Builder system
|
|
||||||
|
|
||||||
The model Builder allows for simple construction of Models using the Model Abstraction system above.
|
|
||||||
It consumes the Model Loader and returns a Model Abstraction.
|
|
||||||
|
|
||||||
It should be MOVED into the File Format parsing system once it is ready.
|
|
|
@ -1,137 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "vlkx/render/shader/Pipeline.h"
|
|
||||||
#include "Loader.h"
|
|
||||||
#include "vlkx/render/render_pass/GenericRenderPass.h"
|
|
||||||
#include "vlkx/vulkan/abstraction/Descriptor.h"
|
|
||||||
|
|
||||||
namespace vlkxtemp {
|
|
||||||
|
|
||||||
class Model;
|
|
||||||
|
|
||||||
class ModelBuilder {
|
|
||||||
public:
|
|
||||||
|
|
||||||
using ShaderPool = vlkx::ShaderModule::ReleasePool;
|
|
||||||
using TextureType = ModelLoader::TextureType;
|
|
||||||
using TexturePerMesh = std::array<std::vector<std::unique_ptr<vlkx::SamplableImage>>, static_cast<int>(TextureType::Count)>;
|
|
||||||
using BindingPoints = std::map<TextureType, uint32_t>;
|
|
||||||
using TextureSource = vlkx::RefCountedTexture::ImageLocation;
|
|
||||||
using TextureSources = std::map<TextureType, std::vector<TextureSource>>;
|
|
||||||
|
|
||||||
class ModelResource {
|
|
||||||
public:
|
|
||||||
virtual ~ModelResource() = default;
|
|
||||||
virtual void load(ModelBuilder* builder) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SingleMeshModel : public ModelResource {
|
|
||||||
public:
|
|
||||||
SingleMeshModel(std::string&& path, int indexBase, TextureSources&& sources)
|
|
||||||
: objFile(std::move(path)), objIndexBase(indexBase), textureSources(std::move(sources)) {}
|
|
||||||
|
|
||||||
void load(ModelBuilder* builder) const override;
|
|
||||||
private:
|
|
||||||
const std::string objFile;
|
|
||||||
const int objIndexBase;
|
|
||||||
const TextureSources textureSources;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MultiMeshModel : public ModelResource {
|
|
||||||
public:
|
|
||||||
MultiMeshModel(std::string&& modelDir, std::string&& textureDir)
|
|
||||||
: models(std::move(modelDir)), textures(std::move(textureDir)) {}
|
|
||||||
|
|
||||||
void load(ModelBuilder* builder) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string models;
|
|
||||||
const std::string textures;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ModelPushConstant {
|
|
||||||
struct Meta {
|
|
||||||
const vlkx::PushConstant* constants;
|
|
||||||
uint32_t offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
VkShaderStageFlags stage;
|
|
||||||
std::vector<Meta> constants;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Descriptors = std::vector<std::unique_ptr<vlkx::StaticDescriptor>>;
|
|
||||||
|
|
||||||
ModelBuilder(std::string&& name, int frames, float aspect, const ModelResource& resource);
|
|
||||||
|
|
||||||
ModelBuilder(const ModelBuilder&) = delete;
|
|
||||||
ModelBuilder& operator=(const ModelBuilder&) = delete;
|
|
||||||
|
|
||||||
ModelBuilder& texture(TextureType type, const TextureSource& source);
|
|
||||||
ModelBuilder& bindTextures(TextureType type, uint32_t point);
|
|
||||||
ModelBuilder& instanceBuffer(vlkx::PerInstanceVertexBuffer* buffer);
|
|
||||||
ModelBuilder& uniform(VkShaderStageFlags stage, std::vector<vlkx::Descriptor::Meta::Binding>&& bindings);
|
|
||||||
ModelBuilder& uniformBuffer(uint32_t point, const vlkx::UniformBuffer& buffer);
|
|
||||||
ModelBuilder& pushStage(VkShaderStageFlags stage);
|
|
||||||
ModelBuilder& pushConstant(const vlkx::PushConstant* constant, uint32_t offset);
|
|
||||||
ModelBuilder& shader(VkShaderStageFlagBits stage, std::string&& file);
|
|
||||||
|
|
||||||
std::unique_ptr<Model> build();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Descriptors> createDescs() const;
|
|
||||||
|
|
||||||
const int frames;
|
|
||||||
const float aspectRatio;
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::StaticPerVertexBuffer> vertexBuffer;
|
|
||||||
std::vector<TexturePerMesh> textures;
|
|
||||||
TexturePerMesh sharedTextures;
|
|
||||||
BindingPoints bindPoints;
|
|
||||||
|
|
||||||
std::vector<vlkx::PerInstanceVertexBuffer*> instanceBuffers;
|
|
||||||
std::vector<vlkx::Descriptor::Meta> uniformMeta;
|
|
||||||
std::vector<vlkx::Descriptor::BufferInfos> uniformBufferMeta;
|
|
||||||
|
|
||||||
std::optional<ModelPushConstant> pushConstants;
|
|
||||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder> pipelineBuilder;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Model {
|
|
||||||
public:
|
|
||||||
|
|
||||||
Model(const Model&) = delete;
|
|
||||||
Model& operator=(const Model&) = delete;
|
|
||||||
|
|
||||||
void update(bool opaque, const VkExtent2D& frame, VkSampleCountFlagBits samples, const vlkx::RenderPass& pass, uint32_t subpass, bool flipY = true);
|
|
||||||
void draw(const VkCommandBuffer& commands, int frame, uint32_t instances) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend std::unique_ptr<Model> ModelBuilder::build();
|
|
||||||
using Descriptors = ModelBuilder::Descriptors;
|
|
||||||
using ModelPushConstant = ModelBuilder::ModelPushConstant;
|
|
||||||
using TexturePerMesh = ModelBuilder::TexturePerMesh;
|
|
||||||
|
|
||||||
Model(float aspectRatio,
|
|
||||||
std::unique_ptr<vlkx::StaticPerVertexBuffer>&& vertexBuffer,
|
|
||||||
std::vector<vlkx::PerInstanceVertexBuffer*>&& perInstanceBuffers,
|
|
||||||
std::optional<ModelPushConstant>&& pushConstants,
|
|
||||||
TexturePerMesh&& sharedTextures,
|
|
||||||
std::vector<TexturePerMesh>&& textures,
|
|
||||||
std::vector<Descriptors>&& descriptors,
|
|
||||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder>&& pipelineBuilder)
|
|
||||||
: aspectRatio(aspectRatio), vertexBuffer(std::move(vertexBuffer)), perInstanceBuffers(std::move(perInstanceBuffers)),
|
|
||||||
pushConstants(std::move(pushConstants)), sharedTextures(std::move(sharedTextures)), textures(std::move(textures)),
|
|
||||||
descriptors(std::move(descriptors)), pipelineBuilder(std::move(pipelineBuilder)) {}
|
|
||||||
|
|
||||||
const float aspectRatio;
|
|
||||||
const std::unique_ptr<vlkx::StaticPerVertexBuffer> vertexBuffer;
|
|
||||||
const std::vector<vlkx::PerInstanceVertexBuffer*> perInstanceBuffers;
|
|
||||||
const std::optional<ModelPushConstant> pushConstants;
|
|
||||||
const TexturePerMesh sharedTextures;
|
|
||||||
const std::vector<TexturePerMesh> textures;
|
|
||||||
const std::vector<Descriptors> descriptors;
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder> pipelineBuilder;
|
|
||||||
std::unique_ptr<vlkx::Pipeline> pipeline;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "vlkx/render/Geometry.h"
|
|
||||||
|
|
||||||
namespace vlkxtemp {
|
|
||||||
|
|
||||||
struct Wavefront {
|
|
||||||
Wavefront(std::string_view path, size_t base);
|
|
||||||
Wavefront(const Wavefront&) = delete;
|
|
||||||
|
|
||||||
Wavefront& operator=(const Wavefront&) = delete;
|
|
||||||
|
|
||||||
std::vector<uint32_t> indices;
|
|
||||||
std::vector<Geo::VertexAll> vertices;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModelLoader {
|
|
||||||
public:
|
|
||||||
enum class TextureType {
|
|
||||||
Diffuse,
|
|
||||||
Specular,
|
|
||||||
Reflection,
|
|
||||||
Cubemap,
|
|
||||||
Count
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureData {
|
|
||||||
TextureData(TextureData&&) noexcept = default;
|
|
||||||
TextureData& operator=(TextureData&&) noexcept = default;
|
|
||||||
|
|
||||||
std::string path;
|
|
||||||
TextureType type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MeshData {
|
|
||||||
MeshData() = default;
|
|
||||||
MeshData(MeshData&&) noexcept = default;
|
|
||||||
MeshData& operator=(MeshData&&) noexcept = default;
|
|
||||||
|
|
||||||
std::vector<Geo::VertexAll> vertices;
|
|
||||||
std::vector<uint32_t> indices;
|
|
||||||
std::vector<TextureData> textures;
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelLoader(const std::string& model, const std::string& textures);
|
|
||||||
|
|
||||||
ModelLoader(const ModelLoader&) = delete;
|
|
||||||
ModelLoader& operator=(const ModelLoader&) = delete;
|
|
||||||
|
|
||||||
const std::vector<MeshData>& getMeshes() const { return meshes; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::vector<MeshData> meshes;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#define GLM_FORCE_RADIAN
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
class Camera {
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum class Input {
|
|
||||||
Up, Down, Left, Right
|
|
||||||
};
|
|
||||||
struct Movement {
|
|
||||||
float moveSpeed = 10;
|
|
||||||
float turnSpeed = 0.0005f;
|
|
||||||
std::optional<glm::vec3> center;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera configuration; with defaults.
|
|
||||||
* Left and forward vectors are calculated from up, pos and target.
|
|
||||||
*/
|
|
||||||
struct Config {
|
|
||||||
float nearPlane = 0.1f; // The nearest a vertex can be to the camera before being clipped
|
|
||||||
float farPlane = 100; // The furthest a vertex can be from the camera before clipped
|
|
||||||
glm::vec3 upV{0, 1, 0}; // The vector pointing straight up from the camera
|
|
||||||
glm::vec3 pos{0, 0, 0}; // The position of the camera in the world
|
|
||||||
glm::vec3 target{1, 0, 0}; // The point the camera is looking at
|
|
||||||
};
|
|
||||||
|
|
||||||
Camera(const Camera &) = delete;
|
|
||||||
|
|
||||||
Camera &operator=(const Camera &) = delete;
|
|
||||||
|
|
||||||
virtual ~Camera() = default;
|
|
||||||
|
|
||||||
Camera &move(const glm::vec3 &delta);
|
|
||||||
|
|
||||||
Camera &setPos(const glm::vec3 &pos);
|
|
||||||
|
|
||||||
Camera &up(const glm::vec3 &up);
|
|
||||||
|
|
||||||
Camera &forward(const glm::vec3 &forward);
|
|
||||||
|
|
||||||
glm::mat4 getViewMatrix() const;
|
|
||||||
|
|
||||||
glm::mat4 getSkyboxView() const {
|
|
||||||
return glm::mat4{glm::mat3{getViewMatrix()}};
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual glm::mat4 getProjMatrix() const = 0;
|
|
||||||
|
|
||||||
const glm::vec3& getPosition() const { return position; }
|
|
||||||
const glm::vec3& getUp() const { return upVector; }
|
|
||||||
const glm::vec3& getForward() const { return frontVector; }
|
|
||||||
const glm::vec3& getRight() const { return rightVector; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit Camera(const Config &conf) : nearPlane(conf.nearPlane), farPlane(conf.farPlane), position(conf.pos),
|
|
||||||
upVector(glm::normalize(conf.upV)) {
|
|
||||||
forward(conf.target - position);
|
|
||||||
}
|
|
||||||
|
|
||||||
const float nearPlane;
|
|
||||||
const float farPlane;
|
|
||||||
private:
|
|
||||||
|
|
||||||
glm::vec3 position;
|
|
||||||
glm::vec3 upVector;
|
|
||||||
glm::vec3 frontVector;
|
|
||||||
glm::vec3 rightVector;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class PerspectiveCamera : public Camera {
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct Frustum {
|
|
||||||
float fov;
|
|
||||||
float aspect;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RT {
|
|
||||||
glm::vec3 up;
|
|
||||||
glm::vec3 forward;
|
|
||||||
glm::vec3 right;
|
|
||||||
};
|
|
||||||
|
|
||||||
PerspectiveCamera(const Camera::Config &conf, const Frustum &frus) :
|
|
||||||
Camera(conf), aspectRatio(frus.aspect), fov(frus.fov) {}
|
|
||||||
|
|
||||||
PerspectiveCamera(const PerspectiveCamera &) = delete;
|
|
||||||
|
|
||||||
PerspectiveCamera &operator=(const PerspectiveCamera &) = delete;
|
|
||||||
|
|
||||||
PerspectiveCamera &fieldOfView(float newFov);
|
|
||||||
|
|
||||||
RT getRT() const;
|
|
||||||
|
|
||||||
glm::mat4 getProjMatrix() const override;
|
|
||||||
|
|
||||||
float getFieldOfView() const { return fov; }
|
|
||||||
|
|
||||||
float getAspect() const { return aspectRatio; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const float aspectRatio;
|
|
||||||
float fov;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OrthographicCamera : public Camera {
|
|
||||||
public:
|
|
||||||
struct OrthoConfig {
|
|
||||||
float width;
|
|
||||||
float aspect;
|
|
||||||
};
|
|
||||||
|
|
||||||
static OrthoConfig getFullscreenConfig() {
|
|
||||||
return {2, 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
OrthographicCamera(const Camera::Config &config, const OrthoConfig &ortho)
|
|
||||||
: Camera(config), aspectRatio(ortho.aspect), width(ortho.width) {}
|
|
||||||
|
|
||||||
OrthographicCamera(const OrthographicCamera &) = delete;
|
|
||||||
|
|
||||||
OrthographicCamera &operator=(const OrthographicCamera &) = delete;
|
|
||||||
|
|
||||||
OrthographicCamera &setWidth(float width);
|
|
||||||
|
|
||||||
glm::mat4 getProjMatrix() const override;
|
|
||||||
|
|
||||||
float getWidth() const { return width; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const float aspectRatio;
|
|
||||||
float width;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
class UserCamera {
|
|
||||||
public:
|
|
||||||
UserCamera(const UserCamera &) = delete;
|
|
||||||
|
|
||||||
UserCamera &operator=(const UserCamera &) = delete;
|
|
||||||
|
|
||||||
virtual ~UserCamera() = default;
|
|
||||||
|
|
||||||
void setInternal(std::function<void(Type *)> op);
|
|
||||||
|
|
||||||
void setPos(const glm::dvec2 &pos) { cursorPos = pos; }
|
|
||||||
|
|
||||||
void move(double x, double y);
|
|
||||||
|
|
||||||
bool scroll(double delta, double min, double max);
|
|
||||||
|
|
||||||
void press(Camera::Input key, float time);
|
|
||||||
|
|
||||||
void active(bool active) { isActive = active; }
|
|
||||||
|
|
||||||
const Type &getCamera() const { return *camera; }
|
|
||||||
|
|
||||||
UserCamera(const Camera::Movement &movement, std::unique_ptr<Type> &&cam)
|
|
||||||
: config(movement), camera(std::move(cam)) {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
|
|
||||||
const Camera::Movement config;
|
|
||||||
bool isActive = false;
|
|
||||||
|
|
||||||
std::unique_ptr<Type> camera;
|
|
||||||
glm::dvec2 cursorPos;
|
|
||||||
glm::vec3 refForward;
|
|
||||||
glm::vec3 refLeft;
|
|
||||||
|
|
||||||
float pitch;
|
|
||||||
float yaw;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserPerspectiveCamera : public UserCamera<PerspectiveCamera> {
|
|
||||||
public:
|
|
||||||
static std::unique_ptr<UserPerspectiveCamera>
|
|
||||||
create(const Camera::Movement &movement, const Camera::Config &config,
|
|
||||||
const PerspectiveCamera::Frustum &frustum) {
|
|
||||||
return std::make_unique<UserPerspectiveCamera>(movement,
|
|
||||||
std::make_unique<PerspectiveCamera>(config, frustum));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using UserCamera<PerspectiveCamera>::UserCamera;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserOrthoCamera : public UserCamera<OrthographicCamera> {
|
|
||||||
public:
|
|
||||||
static std::unique_ptr<UserOrthoCamera> create(const Camera::Movement &movement, const Camera::Config &config,
|
|
||||||
const OrthographicCamera::OrthoConfig &ortho) {
|
|
||||||
return std::make_unique<UserOrthoCamera>(movement, std::make_unique<OrthographicCamera>(config, ortho));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using UserCamera<OrthographicCamera>::UserCamera;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define GLM_FORCE_RADIAN
|
|
||||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
// Global namespace for all classes to do with geometry in a level.
|
|
||||||
namespace Geo {
|
|
||||||
|
|
||||||
// The core components of a given mesh.
|
|
||||||
enum MeshType {
|
|
||||||
Triangle, // A construction of triangles
|
|
||||||
Quad, // A construction of quads
|
|
||||||
Cube, // A single Cube.
|
|
||||||
Sphere // A single Sphere.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Contains standard uniforms for shader files.
|
|
||||||
struct UniformBufferObject {
|
|
||||||
glm::mat4 model; // Model transform matrix.
|
|
||||||
glm::mat4 view; // View matrix.
|
|
||||||
glm::mat4 proj; // Projection matrix.
|
|
||||||
};
|
|
||||||
|
|
||||||
// All of the metadata involved with a vertex.
|
|
||||||
struct VertexAll {
|
|
||||||
glm::vec3 position; // XYZ coordinates of the vertex's position.
|
|
||||||
glm::vec3 normal; // Unit vector pointing away from the outer surface of the vertex.
|
|
||||||
glm::vec2 texture; // The u/v coordinates of this vertex in the bound texture.
|
|
||||||
|
|
||||||
// How fast should vertex data be read from RAM?
|
|
||||||
static VkVertexInputBindingDescription getBindingDesc() {
|
|
||||||
VkVertexInputBindingDescription desc = {};
|
|
||||||
desc.binding = 0;
|
|
||||||
desc.stride = sizeof(VertexAll);
|
|
||||||
desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How should vertexes be handled?
|
|
||||||
static std::vector<VkVertexInputAttributeDescription> getAttributeDesc() {
|
|
||||||
return {
|
|
||||||
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, position)) },
|
|
||||||
{ 0, 1, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, normal)) },
|
|
||||||
{ 0, 2, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, texture)) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// All of the metadata involved with a vertex.
|
|
||||||
struct VertexColor {
|
|
||||||
glm::vec3 position; // XYZ coordinates of the vertex's position.
|
|
||||||
glm::vec3 color; // The color of the vertex.
|
|
||||||
|
|
||||||
// How fast should vertex data be read from RAM?
|
|
||||||
static VkVertexInputBindingDescription getBindingDesc() {
|
|
||||||
VkVertexInputBindingDescription desc = {};
|
|
||||||
desc.binding = 0;
|
|
||||||
desc.stride = sizeof(VertexColor);
|
|
||||||
desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How should vertexes be handled?
|
|
||||||
static std::vector<VkVertexInputAttributeDescription> getAttributeDesc() {
|
|
||||||
return {
|
|
||||||
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexColor, position)) },
|
|
||||||
{ 0, 1, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexColor, color)) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Contains data about a given Mesh.
|
|
||||||
class Mesh {
|
|
||||||
public:
|
|
||||||
// Pre-load the data for a triangle into the given buffers.
|
|
||||||
static void setTriData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices);
|
|
||||||
// Pre-load the data for a quad into the given buffers.
|
|
||||||
static void setQuadData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices);
|
|
||||||
// Pre-load the data for a cube into the given buffers.
|
|
||||||
static void setCubeData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices);
|
|
||||||
// Pre-load the data for a sphere into the given buffers.
|
|
||||||
static void setSphereData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,242 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include "vlkx/vulkan/abstraction/ImageUsage.h"
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include "GenericRenderPass.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The common base class for rendering and computing passes that run on the GPU.
|
|
||||||
* Provides some utility methods for handling attachment metadata between subpasses.
|
|
||||||
*/
|
|
||||||
class CommonPass {
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit CommonPass(int passes) : numPasses(passes) {}
|
|
||||||
|
|
||||||
// Delete the copy and move constructors
|
|
||||||
CommonPass(const CommonPass&) = delete;
|
|
||||||
CommonPass& operator=(const CommonPass&) = delete;
|
|
||||||
virtual ~CommonPass() = default;
|
|
||||||
|
|
||||||
// Get the image layout of the given image at the start of this pass
|
|
||||||
VkImageLayout getInitialLayout(const std::string& name) const;
|
|
||||||
// Get the image layout of the given image at the end of this pass
|
|
||||||
VkImageLayout getFinalLayout(const std::string& name) const;
|
|
||||||
// Get the image layout of the given image before the given subpass starts
|
|
||||||
VkImageLayout getSubpassLayout(const std::string& name, int subpass) const;
|
|
||||||
|
|
||||||
// Update the state of the given image's usage tracker.
|
|
||||||
void update(const std::string& name, MultiImageTracker& tracker) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Some metadata about the usage of an image between subpasses.
|
|
||||||
*/
|
|
||||||
struct Usages {
|
|
||||||
Usages(const int last, const ImageUsage* prev, const ImageUsage* curr) : lastSubpass(last), lastUsage(*prev), currentUsage(*curr) {}
|
|
||||||
const int lastSubpass;
|
|
||||||
const ImageUsage& lastUsage;
|
|
||||||
const ImageUsage& currentUsage;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the usage of an image in the pass to its' tracker.
|
|
||||||
void addUsage(std::string&& name, UsageTracker&& tracker);
|
|
||||||
|
|
||||||
// Get the full history of the image's usages up to this rendering pass.
|
|
||||||
const UsageTracker& getHistory(const std::string& name) const;
|
|
||||||
|
|
||||||
// Get the usage of an image at the start of the given pass.
|
|
||||||
const ImageUsage* getUsage(const std::string& name, int pass) const;
|
|
||||||
|
|
||||||
// Retrieve image usage data, but only if the image is barriered at the given pass.
|
|
||||||
std::optional<Usages> checkForSync(const std::string& name, int pass) const;
|
|
||||||
|
|
||||||
// Validate that the subpass is valid for the given image.
|
|
||||||
// The meaning of includeVirtual is defined by the child implementation.
|
|
||||||
void validate(int pass, const std::string& image, bool includeVirtual) const;
|
|
||||||
|
|
||||||
int getVirtualInitial() const { return -1; }
|
|
||||||
int getVirtualFinal() const { return numPasses; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::map<std::string, UsageTracker> usageHistory;
|
|
||||||
const int numPasses;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Common Pass implementation for Graphics passes; that is, render passes that output to color buffers
|
|
||||||
* for presentation to the screen, or to be used as textures in such.
|
|
||||||
* The instance of the GraphicsPass can be stored and reused to create multiple RenderPassBuilders.
|
|
||||||
* In this way it is essentially a RenderPassBuilderFactory.
|
|
||||||
*/
|
|
||||||
class GraphicsPass : public CommonPass {
|
|
||||||
public:
|
|
||||||
|
|
||||||
using LocationGetter = std::function<int(int pass)>;
|
|
||||||
|
|
||||||
explicit GraphicsPass(int passes) : CommonPass {passes} {}
|
|
||||||
|
|
||||||
GraphicsPass(const GraphicsPass&) = delete;
|
|
||||||
GraphicsPass& operator=(const GraphicsPass&) = delete;
|
|
||||||
|
|
||||||
// Get the default render ops for a color buffer.
|
|
||||||
static RenderPassBuilder::Attachment::OpsType getDefaultOps() {
|
|
||||||
return RenderPassBuilder::Attachment::ColorOps { VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the default render ops for a stencil buffer.
|
|
||||||
static RenderPassBuilder::Attachment::OpsType getStencilOps() {
|
|
||||||
return RenderPassBuilder::Attachment::StencilDepthOps { VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an image reference that is used in this render pass.
|
|
||||||
* @param name the name of the image used
|
|
||||||
* @param history the usage history of the image, for tracking purposes
|
|
||||||
* @param getter a function to get the location of the image, only if the image is used as a render target.
|
|
||||||
* @param ops optional; uses the static defaults if not present.
|
|
||||||
* @return the index into the VkAttachmentDescriptions.
|
|
||||||
*/
|
|
||||||
int add(const std::string& name, UsageTracker&& history, LocationGetter&& getter, const std::optional<RenderPassBuilder::Attachment::OpsType> ops = std::nullopt);
|
|
||||||
|
|
||||||
#define fluent GraphicsPass&
|
|
||||||
|
|
||||||
// Specifies that the source image will be resolved to the single destination at the given pass.
|
|
||||||
fluent addMultisample(const std::string& source, const std::string& dest, int pass);
|
|
||||||
|
|
||||||
// Build a RenderPassBuilder with the information provided so far.
|
|
||||||
std::unique_ptr<RenderPassBuilder> build(int framebuffers);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct AttachmentMeta {
|
|
||||||
int index;
|
|
||||||
LocationGetter getter;
|
|
||||||
vlkx::RenderPassBuilder::Attachment::OpsType ops;
|
|
||||||
std::map<int, std::string> multisample;
|
|
||||||
};
|
|
||||||
|
|
||||||
void setAttachments();
|
|
||||||
void setSubpasses();
|
|
||||||
void setDependencies();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first subpass where the given image is used as a render target.
|
|
||||||
* @param history the usage history of the image; what it was used at at each subpass.
|
|
||||||
* @return nullopt if the image was not used as a render target, the index of the subpass where it was, if not.
|
|
||||||
*/
|
|
||||||
std::optional<int> getFirstRenderTarget(const UsageTracker& history) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the operations that should be used for the given image attachment.
|
|
||||||
* If the user specified ops, it will be checekd against the history.
|
|
||||||
* @param name the name of the image to use as the attachment
|
|
||||||
* @param history the usage history of the attachment, for internal checks
|
|
||||||
* @param userOps operations to use for the image, as an optional override.
|
|
||||||
* @return the ColorOps to use for the given attachment.
|
|
||||||
*/
|
|
||||||
RenderPassBuilder::Attachment::OpsType getOps(const std::string& name, const UsageTracker& history, const std::optional<RenderPassBuilder::Attachment::OpsType>& userOps) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the usage type of the image.
|
|
||||||
* Assumption: an image is only ever used as a color OR depth stencil. Never both.
|
|
||||||
* Assumption: Multisample == RenderTarget
|
|
||||||
* @param name the name of the image to check
|
|
||||||
* @param history the history of the image's usages in the GPU.
|
|
||||||
* @return whether the image is a RenderTarget or a DepthStencil buffer.
|
|
||||||
*/
|
|
||||||
ImageUsage::Type getUsageType(const std::string& name, const UsageTracker& history) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the image is used as type at subpass in its' history.
|
|
||||||
*/
|
|
||||||
bool verifyImageUsage(const UsageTracker& history, int subpass, ImageUsage::Type type) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the subpass is virtual.
|
|
||||||
* For a Render Pass, virtual means it is a preprocessing step.
|
|
||||||
*/
|
|
||||||
bool isVirtual(int subpass) const {
|
|
||||||
return subpass == getVirtualInitial() || subpass == getVirtualFinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the subpass index; for virtual passes, it uses an EXTERNAL subpass.
|
|
||||||
*/
|
|
||||||
uint32_t checkSubpass(int subpass) const {
|
|
||||||
return isVirtual(subpass) ? VK_SUBPASS_EXTERNAL : (uint32_t) subpass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the image's usages are compatible with a render pass.
|
|
||||||
* For example, compute shader linear buffers cannot be used as render targets, etc.
|
|
||||||
*/
|
|
||||||
void verifyHistory(const std::string& image, const UsageTracker& history) const;
|
|
||||||
|
|
||||||
std::map<std::string, AttachmentMeta> metas;
|
|
||||||
std::unique_ptr<vlkx::RenderPassBuilder> builder;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Common Pass implementation for Compute Shaders.
|
|
||||||
* That is, shaders that do not write to color buffers.
|
|
||||||
* A subpass can execute multiple compute shaders unbarriered, which increases efficiency.
|
|
||||||
* We still need to transition images between passes when necessary, hence the wrapper.
|
|
||||||
*/
|
|
||||||
class ComputePass : public CommonPass {
|
|
||||||
public:
|
|
||||||
|
|
||||||
ComputePass(const ComputePass&) = delete;
|
|
||||||
ComputePass& operator=(const ComputePass&) = delete;
|
|
||||||
|
|
||||||
#define fluent ComputePass&
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the given image as an attachment to the compute shader pass.
|
|
||||||
* @param name the name of the image
|
|
||||||
* @param history the usage history of the image
|
|
||||||
* @return the ComputePass instance, for chaining.
|
|
||||||
*/
|
|
||||||
fluent add(std::string&& name, UsageTracker&& history);
|
|
||||||
fluent add(const std::string& name, UsageTracker&& history) {
|
|
||||||
return add(std::string(name), std::move(history));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run computeOps, insert memory barriers to transition used images into the appropriate format.
|
|
||||||
* Images must be a superset of all images that were called with add().
|
|
||||||
* Compute_ops must be equal to the number of subpasses.
|
|
||||||
* Commands must be recording.
|
|
||||||
* @param commands the command buffer to write into.
|
|
||||||
* @param queueFamily the family to use for inserting barriers
|
|
||||||
* @param images the list of images that were used in the compute pass
|
|
||||||
* @param computeOps the compute functions to upload to the GPU
|
|
||||||
*/
|
|
||||||
void execute(const VkCommandBuffer& commands, uint32_t queueFamily, const std::map<std::string, const VkImage*>& images, const std::vector<std::function<void()>>& computeOps) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a memory barrier, to transition the layout of the image from the previous to the curent.
|
|
||||||
* The barrier is performed using the given queue family.
|
|
||||||
* @param commands the command buffer to write into.
|
|
||||||
* @param queueFamily the family to use for inserting barriers.
|
|
||||||
* @param image the list of images that were used in the compute pass
|
|
||||||
* @param prev the previous usage of the image; the state being transitioned from
|
|
||||||
* @param current the new usage of the image; the state being transitioned to.
|
|
||||||
*/
|
|
||||||
void barrier(const VkCommandBuffer& commands, uint32_t queueFamily, const VkImage& image, const ImageUsage& prev, const ImageUsage& current) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify whether the previous usages of the given image in its' history is compatible with a compute shader.
|
|
||||||
* For example, a fragment shader output image is not compatible.
|
|
||||||
* @param name the name of the image being checked
|
|
||||||
* @param history the usage history of the image/
|
|
||||||
*/
|
|
||||||
void verify(const std::string& name, const UsageTracker& history) const;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,238 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
#include <variant>
|
|
||||||
#include <optional>
|
|
||||||
#include "vlkx/vulkan/Tools.h"
|
|
||||||
#include "vlkx/vulkan/abstraction/Image.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gathers the operations that the GPU should perform when rendering to a framebuffer.
|
|
||||||
* Subpasses and dependencies are to be configured automatically using the builder below.
|
|
||||||
* RenderPass objects are disposable, and should be discarded when the framebuffer changes.
|
|
||||||
*/
|
|
||||||
class RenderPass {
|
|
||||||
public:
|
|
||||||
using RenderFunc = std::function<void(const VkCommandBuffer& buffer)>;
|
|
||||||
|
|
||||||
// Delete copy and move constructors to prevent the GPU getting confused with what we're trying to do
|
|
||||||
RenderPass(const RenderPass&) = delete;
|
|
||||||
RenderPass& operator=(const RenderPass&) = delete;
|
|
||||||
~RenderPass();
|
|
||||||
|
|
||||||
RenderPass(int subpasses, VkRenderPass pass, std::vector<VkClearValue> clear, VkExtent2D ext, std::vector<VkFramebuffer> fbs, std::vector<int> attachs)
|
|
||||||
: subpassCount(subpasses), renderPass(pass), clearValues(std::move(clear)), extent(ext), framebuffers(std::move(fbs)), attachments(std::move(attachs)) {}
|
|
||||||
|
|
||||||
const VkRenderPass& operator*() const { return renderPass; }
|
|
||||||
|
|
||||||
int getAttachsInSubpass(int subpass) const {
|
|
||||||
return attachments[subpass];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload all of the subpass render commands to the command buffer.
|
|
||||||
* The size of ops must be equal to the number of subpasses in this render pass.
|
|
||||||
* @param commands the command buffer to execute on; must be recording
|
|
||||||
* @param imageIndex the index of the image on the swapchain that we're rendering to; the target framebuffer.
|
|
||||||
* @param ops the render operations to add onto the command buffer.
|
|
||||||
*/
|
|
||||||
void execute(const VkCommandBuffer& commands, int imageIndex, std::vector<RenderFunc> ops) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The number of sub-render-passes in this pass.
|
|
||||||
const int subpassCount;
|
|
||||||
// The VkRenderPass that this class wraps.
|
|
||||||
VkRenderPass renderPass;
|
|
||||||
// The clear values that will wipe all framebuffers to their empty states.
|
|
||||||
const std::vector<VkClearValue> clearValues;
|
|
||||||
// The size of the framebuffers (all are the same size)
|
|
||||||
const VkExtent2D extent;
|
|
||||||
// The framebuffers that we can render to
|
|
||||||
const std::vector<VkFramebuffer> framebuffers;
|
|
||||||
// The number of color attachments (sampled color images) in each subpass, by subpass index.
|
|
||||||
const std::vector<int> attachments;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stateful, fluent way to create Render Passes.
|
|
||||||
* This object can be stored and reused; when the window size changes, simply set the extent and
|
|
||||||
* export a new RenderPass to be used in the pipeline.
|
|
||||||
*
|
|
||||||
* Allows setting sub-passes, sub-pass dependencies, operations to read and write them, etc.
|
|
||||||
*/
|
|
||||||
class RenderPassBuilder {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information required to define an attachment to be used in a render pass.
|
|
||||||
* Contains information on the layout, the operations to use on read and write, etc.
|
|
||||||
*/
|
|
||||||
struct Attachment {
|
|
||||||
// Operations to use on color attachments.
|
|
||||||
struct ColorOps {
|
|
||||||
VkAttachmentLoadOp LOAD; // Load data in the color attachment
|
|
||||||
VkAttachmentStoreOp STORE; // Store data into the color attachment
|
|
||||||
};
|
|
||||||
|
|
||||||
// Operations to use on depth and stencil buffers.
|
|
||||||
struct StencilDepthOps {
|
|
||||||
VkAttachmentLoadOp DEPTH_LOAD; // Load data in the depth attachment
|
|
||||||
VkAttachmentStoreOp DEPTH_STORE; // Store data in the depth attachment
|
|
||||||
VkAttachmentLoadOp STENCIL_LOAD; // Load data in the stencil attachment
|
|
||||||
VkAttachmentStoreOp STENCIL_STORE; // Store data in the stencil attachment
|
|
||||||
};
|
|
||||||
|
|
||||||
using OpsType = std::variant<ColorOps, StencilDepthOps>;
|
|
||||||
|
|
||||||
// The operations that can be performed on this attachment
|
|
||||||
OpsType ops;
|
|
||||||
// The initial layout of an image in this attachment (pre-GPU upload)
|
|
||||||
VkImageLayout layoutInitial;
|
|
||||||
// The final layout of an image in this attachment (as seen by the shader)
|
|
||||||
VkImageLayout layoutFinal;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes attachments used in each subpass, in terms of Vulkan data.
|
|
||||||
* Attachment here is used in the conceptual sense, not referring to the Attachment struct above.
|
|
||||||
*
|
|
||||||
* If multisampleReferences is non-zero, its' size must be equal to colorReferences' size.
|
|
||||||
* Each index of multisampleReferences refers to the same-index colorReferences entry.
|
|
||||||
*
|
|
||||||
* If stencilDepthReference is non-zero, it is shared between all subpasses.
|
|
||||||
*/
|
|
||||||
struct SubpassAttachments {
|
|
||||||
std::vector<VkAttachmentReference> colorReferences;
|
|
||||||
std::vector<VkAttachmentReference> multisampleReferences;
|
|
||||||
std::optional<VkAttachmentReference> stencilDepthReference;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes the dependencies between each sub-pass.
|
|
||||||
* It describes how each subpass will read or modify the data written by the last subpass, if at all.
|
|
||||||
* This dependency information can allow the GPU to run some passes in parallel, and enforce the
|
|
||||||
* strict ordering of those that require it.
|
|
||||||
*/
|
|
||||||
struct SubpassDependency {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines some metadata about the subpass.
|
|
||||||
* Contains the index of the subpass, how it will use data from the last pass, and what exactly it will do.
|
|
||||||
*/
|
|
||||||
struct SubpassMeta {
|
|
||||||
/**
|
|
||||||
* Index of the subpass.
|
|
||||||
*/
|
|
||||||
uint32_t index;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes how we want to modify the data passed to us from the last subpass.
|
|
||||||
* Will change how the next subpass will wait for the completion of this subpass, if at all.
|
|
||||||
*
|
|
||||||
* VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT:
|
|
||||||
* read/write to the color attachment
|
|
||||||
*
|
|
||||||
* VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT:
|
|
||||||
* VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT:
|
|
||||||
* read/write to the depth or stencil attachment
|
|
||||||
*
|
|
||||||
* VK_PIPELINE_STAGE_VERTEX_SHADER_BIT:
|
|
||||||
* VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT:
|
|
||||||
* read all attachments
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
VkPipelineStageFlags stage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes how we want to synchronize with the subpass after this one.
|
|
||||||
*
|
|
||||||
* VK_ACCESS_SHADER_READ_BIT:
|
|
||||||
* VK_ACCESS_SHADER_WRITE_BIT:
|
|
||||||
* read a texture or write to a color buffer
|
|
||||||
*
|
|
||||||
* VK_ACCESS_COLOR_ATTACHMENT_READ_BIT:
|
|
||||||
* VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT:
|
|
||||||
* VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT:
|
|
||||||
* VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT:
|
|
||||||
* read/write to an attachment.
|
|
||||||
*
|
|
||||||
* VK_ACCESS_INPUT_ATTACHMENT_READ_BIT:
|
|
||||||
* for accessing the inputAttachment of a subpass.
|
|
||||||
*
|
|
||||||
* 0:
|
|
||||||
* do not write, but the next subpass will.
|
|
||||||
* Will automatically barrier the render pass.
|
|
||||||
*/
|
|
||||||
VkAccessFlags access;
|
|
||||||
};
|
|
||||||
|
|
||||||
SubpassMeta source; // The source subpass of this dependency (will take effect after this pass completes)
|
|
||||||
SubpassMeta destination; // The destination subpass of this dependency (will take effect before this pass)
|
|
||||||
VkDependencyFlags flags; // Other information that Vulkan needs to know about this dependency; for example, if we use an inputAttachment.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes a color attachment.
|
|
||||||
* Effectively RGB images that live entirely on the GPU.
|
|
||||||
*
|
|
||||||
* Describes the resolution mechanics of a multisampled image.
|
|
||||||
*/
|
|
||||||
struct ColorAttachmentMeta {
|
|
||||||
int location; // Where the GPU shaders expect this attachment to be available.
|
|
||||||
int descriptionIdx; // Index of this attachment in the VkAttachmentDescription data.
|
|
||||||
VkImageLayout layout; // Vulkan image layout. Shader optimized or host readable.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a list of VkAttachmentReference that describes the attachments used per subpass.
|
|
||||||
*/
|
|
||||||
static std::vector<VkAttachmentReference> parseColorReferences(std::vector<ColorAttachmentMeta> meta);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a list of VkAttachmentReference that describes the multisampling setup.
|
|
||||||
*/
|
|
||||||
static std::vector<VkAttachmentReference> parseMutisampling(int colorReferencesCount, std::vector<ColorAttachmentMeta> meta);
|
|
||||||
|
|
||||||
RenderPassBuilder(const RenderPassBuilder&) = delete;
|
|
||||||
RenderPassBuilder& operator=(const RenderPassBuilder&) = delete;
|
|
||||||
~RenderPassBuilder() = default;
|
|
||||||
RenderPassBuilder() = default;
|
|
||||||
|
|
||||||
/** Fluent API Features; chain calls to set data on the render pass.*/
|
|
||||||
#define fluent RenderPassBuilder&
|
|
||||||
|
|
||||||
// Set the number of framebuffers in the render pass
|
|
||||||
fluent setFramebufferCount(int count);
|
|
||||||
// Set an attachment description in the render pass
|
|
||||||
fluent setAttachment(int idx, const Attachment& attachment);
|
|
||||||
// Update the image backing an attachment. The function must be executable during execute() later on.
|
|
||||||
fluent updateAttachmentBacking(int idx, std::function<const Image&(int idx)>&& getBacking);
|
|
||||||
// Set a specific subpass. Use the static parse methods to create these vectors.
|
|
||||||
fluent setSubpass(int idx, std::vector<VkAttachmentReference>&& color, std::vector<VkAttachmentReference>&& multisample, VkAttachmentReference& depthStencil);
|
|
||||||
// Add a dependency between two subpasses.
|
|
||||||
fluent addDependency(const SubpassDependency& dep);
|
|
||||||
|
|
||||||
// Build the Render Pass with all the information given.
|
|
||||||
// Can be called multiple times with the same Builder.
|
|
||||||
[[nodiscard]] std::unique_ptr<vlkx::RenderPass> build() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Number of framebuffers in the render pass
|
|
||||||
std::optional<int> framebufferCount;
|
|
||||||
// Descriptions of used attachments
|
|
||||||
std::vector<VkAttachmentDescription> attachmentDescriptors;
|
|
||||||
// Functions to return attachment images.
|
|
||||||
std::vector<std::function<const Image&(int idx)>> attachmentGetters;
|
|
||||||
// Values to clear all attachments
|
|
||||||
std::vector<VkClearValue> clearValues;
|
|
||||||
// Descriptions of subpasses.
|
|
||||||
std::vector<SubpassAttachments> subpassAttachments;
|
|
||||||
// Descriptions of subpass dependencies.
|
|
||||||
std::vector<VkSubpassDependency> subpassDependencies;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <memory>
|
|
||||||
#include "GenericRenderPass.h"
|
|
||||||
#include "vlkx/vulkan/abstraction/ImageUsage.h"
|
|
||||||
#include "vlkx/vulkan/abstraction/Image.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
// A simple and versatile way to configure render passes.
|
|
||||||
// Intended to be used with the SimpleRenderPass and the ScreenRenderPass.
|
|
||||||
class RendererConfig {
|
|
||||||
public:
|
|
||||||
RendererConfig(std::vector<std::unique_ptr<vlkx::Image>>& destinations, bool toScreen = true) : renderImages(destinations) {
|
|
||||||
numOpaquePasses = 1;
|
|
||||||
rendersToScreen = toScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
RendererConfig(int passCount, std::vector<std::unique_ptr<vlkx::Image>>& destinations, bool toScreen = true, std::optional<int> firstTransparent = std::nullopt, std::optional<int> firstOverlay = std::nullopt);
|
|
||||||
|
|
||||||
// Get the number of passes that use the depth buffer.
|
|
||||||
int depthPasses() const {
|
|
||||||
return numOpaquePasses + numTransparentPasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the total number of passes.
|
|
||||||
int passes() const {
|
|
||||||
return depthPasses() + numOverlayPasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get whether any passes use the depth buffer.
|
|
||||||
bool usesDepth() const {
|
|
||||||
return depthPasses() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the render pass builder. Can be called multiple times.
|
|
||||||
void build();
|
|
||||||
|
|
||||||
RendererConfig(RendererConfig&) noexcept = default;
|
|
||||||
RendererConfig(const RendererConfig&) = default;
|
|
||||||
|
|
||||||
int numOpaquePasses = 0;
|
|
||||||
int numTransparentPasses = 0;
|
|
||||||
std::vector<std::unique_ptr<vlkx::Image>>& renderImages;
|
|
||||||
bool rendersToScreen;
|
|
||||||
private:
|
|
||||||
int numOverlayPasses = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores all of the information required to use an attachment.
|
|
||||||
* This is heavy, so is only used when the image is being finalized.
|
|
||||||
*/
|
|
||||||
struct AttachmentConfig {
|
|
||||||
AttachmentConfig(std::string_view name, std::optional<int>* index)
|
|
||||||
: name(name), index(*index) {}
|
|
||||||
|
|
||||||
AttachmentConfig& setOps(const RenderPassBuilder::Attachment::OpsType& ops) {
|
|
||||||
loadStoreOps = ops;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AttachmentConfig& setUsage(const ImageUsage& final) {
|
|
||||||
finalUsage = final;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
std::optional<int>& index;
|
|
||||||
std::optional<RenderPassBuilder::Attachment::OpsType> loadStoreOps;
|
|
||||||
std::optional<ImageUsage> finalUsage;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lighter version of the Attachment, used primarily in the On-Screen Pass Manager.
|
|
||||||
*/
|
|
||||||
struct Attachment {
|
|
||||||
explicit Attachment(std::string_view image) : name(image) {}
|
|
||||||
|
|
||||||
// Adds the image to the tracker and initializes state.
|
|
||||||
void add(MultiImageTracker& tracker, const Image& image) {
|
|
||||||
tracker.track(name, image.getUsage());
|
|
||||||
}
|
|
||||||
|
|
||||||
AttachmentConfig makeConfig() { return { name, &index }; }
|
|
||||||
|
|
||||||
const std::string name;
|
|
||||||
std::optional<int> index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Manages Render Passes that will output to the screen.
|
|
||||||
// This is necessarily exclusively a graphical pass.
|
|
||||||
// If necessary, a depth and stencil buffer will be maintained.
|
|
||||||
// The color buffer is automatically assumed to be the swapchain.
|
|
||||||
class ScreenRenderPassManager {
|
|
||||||
public:
|
|
||||||
explicit ScreenRenderPassManager(RendererConfig renderConfig) : config(renderConfig) {}
|
|
||||||
|
|
||||||
ScreenRenderPassManager(const ScreenRenderPassManager&) = delete;
|
|
||||||
ScreenRenderPassManager& operator=(const ScreenRenderPassManager&) = delete;
|
|
||||||
|
|
||||||
// Initialize the render pass we're managing.
|
|
||||||
void initializeRenderPass();
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::RenderPass>& getPass() { return pass; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Prepare the Render Pass builder.
|
|
||||||
void preparePassBuilder();
|
|
||||||
|
|
||||||
const RendererConfig config;
|
|
||||||
|
|
||||||
Attachment destinationInfo { "Destination" };
|
|
||||||
Attachment multisampleInfo { "Multisample" };
|
|
||||||
Attachment depthStencilInfo { "Depth-Stencil" };
|
|
||||||
|
|
||||||
std::unique_ptr<vlkx::Image> depthStencilImage;
|
|
||||||
std::unique_ptr<vlkx::RenderPassBuilder> passBuilder;
|
|
||||||
std::unique_ptr<vlkx::RenderPass> pass;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility namespace used to house a constructor for creating a "simple" render pass with all the defaults.
|
|
||||||
*/
|
|
||||||
namespace SimpleRenderPass {
|
|
||||||
static std::unique_ptr<RenderPassBuilder> createBuilder(int framebuffers, const RendererConfig& config, const AttachmentConfig& color, const AttachmentConfig* multisample, const AttachmentConfig* depthStencil, MultiImageTracker& tracker);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,205 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <optional>
|
|
||||||
#include <fstream>
|
|
||||||
#include "shadow/util/RefCounter.h"
|
|
||||||
#include "vlkx/vulkan/VulkanModule.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
class Pipeline;
|
|
||||||
|
|
||||||
// A simple wrapper for the shader module used by the pipeline.
|
|
||||||
class ShaderModule {
|
|
||||||
public:
|
|
||||||
using CountedShader = shadowutil::RefCounter<ShaderModule>;
|
|
||||||
using ReleasePool = CountedShader::AutoRelease;
|
|
||||||
|
|
||||||
ShaderModule(const std::string& path);
|
|
||||||
|
|
||||||
ShaderModule(const ShaderModule&) = delete;
|
|
||||||
ShaderModule& operator=(const ShaderModule&) = delete;
|
|
||||||
|
|
||||||
~ShaderModule() {
|
|
||||||
vkDestroyShaderModule(VulkanModule::getInstance()->getDevice()->logical, shader, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkShaderModule& operator*() const { return shader; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkShaderModule shader;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PipelineBuilder {
|
|
||||||
public:
|
|
||||||
PipelineBuilder(const PipelineBuilder&) = delete;
|
|
||||||
PipelineBuilder& operator=(const PipelineBuilder&) = delete;
|
|
||||||
|
|
||||||
virtual ~PipelineBuilder() {
|
|
||||||
vkDestroyPipelineCache(VulkanModule::getInstance()->getDevice()->logical, cache, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::unique_ptr<Pipeline> build() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PipelineBuilder(std::optional<int> maxCache);
|
|
||||||
|
|
||||||
void setName(std::string&& n) { name = std::move(name); }
|
|
||||||
|
|
||||||
void setLayout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& constants);
|
|
||||||
|
|
||||||
const std::string& getName() const { return name; }
|
|
||||||
|
|
||||||
bool hasLayout() const { return layoutInfo.has_value(); }
|
|
||||||
const VkPipelineLayoutCreateInfo& getLayout() const { return layoutInfo.value(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkPipelineCache cache;
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
std::optional<VkPipelineLayoutCreateInfo> layoutInfo;
|
|
||||||
std::vector<VkDescriptorSetLayout> descLayouts;
|
|
||||||
std::vector<VkPushConstantRange> constants;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use when creating Graphics pipelines.
|
|
||||||
* Internal state is preserved so that multiple pipelines can be created with one builder.
|
|
||||||
* However, shaders are single-usage. Bind a new shader before claling build again.
|
|
||||||
* See ShaderModule for more information, and how to change this.
|
|
||||||
*/
|
|
||||||
class GraphicsPipelineBuilder : public PipelineBuilder {
|
|
||||||
public:
|
|
||||||
struct Viewport {
|
|
||||||
VkViewport viewport;
|
|
||||||
VkRect2D scissor;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit GraphicsPipelineBuilder(std::optional<int> maxCache = std::nullopt);
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder(const GraphicsPipelineBuilder&) = delete;
|
|
||||||
GraphicsPipelineBuilder& operator=(const GraphicsPipelineBuilder&) = delete;
|
|
||||||
|
|
||||||
#define fluent GraphicsPipelineBuilder&
|
|
||||||
|
|
||||||
fluent name(std::string&& name);
|
|
||||||
fluent depthTest(bool enable, bool write);
|
|
||||||
fluent stencilTest(bool enable);
|
|
||||||
fluent multiSample(VkSampleCountFlagBits samples);
|
|
||||||
fluent topology(VkPrimitiveTopology topology);
|
|
||||||
fluent stencilOp(const VkStencilOpState& state, VkStencilFaceFlags flags);
|
|
||||||
|
|
||||||
fluent addVertex(uint32_t bindPoint, VkVertexInputBindingDescription&& desc, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
|
||||||
fluent layout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& constants);
|
|
||||||
fluent viewport(const Viewport& port, bool flipY = true);
|
|
||||||
fluent renderPass(const VkRenderPass& pass, uint32_t subpass);
|
|
||||||
|
|
||||||
fluent colorBlend(std::vector<VkPipelineColorBlendAttachmentState>&& states);
|
|
||||||
fluent shader(VkShaderStageFlagBits stage, std::string&& file);
|
|
||||||
|
|
||||||
std::unique_ptr<Pipeline> build() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct PassInfo {
|
|
||||||
VkRenderPass pass;
|
|
||||||
uint32_t subpass;
|
|
||||||
};
|
|
||||||
|
|
||||||
VkPipelineInputAssemblyStateCreateInfo assemblyInfo;
|
|
||||||
VkPipelineRasterizationStateCreateInfo rasterizationInfo;
|
|
||||||
VkPipelineMultisampleStateCreateInfo multisampleInfo;
|
|
||||||
VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
|
|
||||||
VkPipelineDynamicStateCreateInfo dynamicStateInfo;
|
|
||||||
|
|
||||||
std::vector<VkVertexInputBindingDescription> bindingDescs;
|
|
||||||
std::vector<VkVertexInputAttributeDescription> attrDescs;
|
|
||||||
|
|
||||||
std::optional<Viewport> viewportMeta;
|
|
||||||
std::optional<PassInfo> passMeta;
|
|
||||||
std::vector<VkPipelineColorBlendAttachmentState> blendStates;
|
|
||||||
std::map<VkShaderStageFlagBits, std::string> shaders;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use when creating Compute Shader pipelines.
|
|
||||||
* Internal state is preserved so that multiple pipelines can be created with one builder.
|
|
||||||
* However, shaders are single-usage. Bind a new shader before claling build again.
|
|
||||||
* See ShaderModule for more information, and how to change this.
|
|
||||||
*/
|
|
||||||
class ComputePipelineBuilder : public PipelineBuilder {
|
|
||||||
public:
|
|
||||||
explicit ComputePipelineBuilder(std::optional<int> maxCache = std::nullopt) : PipelineBuilder(maxCache) {}
|
|
||||||
|
|
||||||
ComputePipelineBuilder(const ComputePipelineBuilder&) = delete;
|
|
||||||
ComputePipelineBuilder& operator=(const ComputePipelineBuilder&) = delete;
|
|
||||||
|
|
||||||
#define fluent ComputePipelineBuilder&
|
|
||||||
|
|
||||||
fluent name(std::string&& name);
|
|
||||||
fluent layout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& pushConstants);
|
|
||||||
fluent shader(std::string&& path);
|
|
||||||
|
|
||||||
std::unique_ptr<Pipeline> build() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<std::string> shaderPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pipeline configures:
|
|
||||||
* - Shader Stages
|
|
||||||
* - Fixed Function stages
|
|
||||||
* - Vertex input bindings
|
|
||||||
* - Vertex attributes
|
|
||||||
* - Assembly
|
|
||||||
* - Tesselation
|
|
||||||
* - Viewport and Scissor
|
|
||||||
* - Rasterization
|
|
||||||
* - Multisampling
|
|
||||||
* - Depth testing
|
|
||||||
* - Stencil testing
|
|
||||||
* - Color blending
|
|
||||||
* - Dynamic states
|
|
||||||
* - Pipeline layout
|
|
||||||
* - Descriptor set layout
|
|
||||||
* - Push constant ranges
|
|
||||||
*
|
|
||||||
* Create a Pipeline with one of the builders above.
|
|
||||||
*/
|
|
||||||
class Pipeline {
|
|
||||||
public:
|
|
||||||
Pipeline(const Pipeline&) = delete;
|
|
||||||
Pipeline& operator=(const Pipeline&) = delete;
|
|
||||||
|
|
||||||
~Pipeline();
|
|
||||||
|
|
||||||
void bind(const VkCommandBuffer& buffer) const;
|
|
||||||
|
|
||||||
const VkPipeline& operator*() const { return pipeline; }
|
|
||||||
const VkPipelineLayout& getLayout() const { return layout; }
|
|
||||||
VkPipelineBindPoint getBind() const { return bindPoint; }
|
|
||||||
|
|
||||||
static VkPipelineColorBlendAttachmentState getAlphaBlendState(bool blending) {
|
|
||||||
return {
|
|
||||||
blending, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
||||||
VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
||||||
VK_BLEND_OP_ADD, VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend std::unique_ptr<Pipeline> GraphicsPipelineBuilder::build() const;
|
|
||||||
friend std::unique_ptr<Pipeline> ComputePipelineBuilder::build() const;
|
|
||||||
|
|
||||||
Pipeline(std::string name, const VkPipeline& line, const VkPipelineLayout& lay, VkPipelineBindPoint bPoint)
|
|
||||||
: name(std::move(name)), pipeline(line), layout(lay), bindPoint(bPoint) {}
|
|
||||||
|
|
||||||
const std::string name;
|
|
||||||
// The active Pipeline layout.
|
|
||||||
const VkPipelineLayout layout;
|
|
||||||
// The active Pipeline instance.
|
|
||||||
const VkPipeline pipeline;
|
|
||||||
// Whether this is a graphics or compute pipeline
|
|
||||||
const VkPipelineBindPoint bindPoint;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "vlkx/vulkan/abstraction/Image.h"
|
|
||||||
|
|
||||||
class SwapChain {
|
|
||||||
public:
|
|
||||||
SwapChain();
|
|
||||||
~SwapChain();
|
|
||||||
|
|
||||||
VkSwapchainKHR swapChain;
|
|
||||||
VkFormat format;
|
|
||||||
VkExtent2D extent;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<vlkx::Image>> images;
|
|
||||||
std::unique_ptr<vlkx::Image> multisampleImg;
|
|
||||||
|
|
||||||
VkSurfaceFormatKHR chooseFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
|
||||||
VkPresentModeKHR chooseMode(const std::vector<VkPresentModeKHR>& availableModes);
|
|
||||||
VkExtent2D chooseExtent(const VkSurfaceCapabilitiesKHR& capabilities);
|
|
||||||
|
|
||||||
void create(VkSurfaceKHR surface);
|
|
||||||
void destroy();
|
|
||||||
};
|
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vulkan/vk_mem_alloc.h>
|
|
||||||
#include "VulkanDevice.h"
|
|
||||||
#include "exports.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace VkTools {
|
|
||||||
struct ManagedImage {
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ManagedBuffer {
|
|
||||||
VkBuffer buffer;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern API VmaAllocator allocator;
|
|
||||||
|
|
||||||
ManagedImage createImage(VkFormat format, VkImageUsageFlags flags, VkExtent3D extent);
|
|
||||||
|
|
||||||
VkSampler createSampler(VkFilter filters, VkSamplerAddressMode mode, uint32_t mipping, VkDevice device);
|
|
||||||
|
|
||||||
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipping, uint32_t layers,
|
|
||||||
VkDevice device);
|
|
||||||
|
|
||||||
ManagedBuffer createGPUBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
|
|
||||||
VkDevice logicalDevice, VkPhysicalDevice physicalDevice, bool hostVisible = true);
|
|
||||||
|
|
||||||
void immediateExecute(const std::function<void(const VkCommandBuffer &)> &execute, VulkanDevice *dev);
|
|
||||||
|
|
||||||
void copyGPUBuffer(VkBuffer source, VkBuffer dest, VkDeviceSize length, VulkanDevice *dev);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
class ValidationAndExtension {
|
|
||||||
public:
|
|
||||||
ValidationAndExtension();
|
|
||||||
~ValidationAndExtension();
|
|
||||||
|
|
||||||
const std::vector<const char*> requiredValidations = {
|
|
||||||
"VK_LAYER_KHRONOS_validation",
|
|
||||||
//"VK_LAYER_LUNARG_api_dump"
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDebugReportCallbackEXT callback;
|
|
||||||
|
|
||||||
bool checkValidationSupport();
|
|
||||||
|
|
||||||
std::vector<const char*> getRequiredExtensions(SDL_Window* window, bool validationsRequired);
|
|
||||||
void setupDebugCallback(bool validationsRequired, VkInstance vulkan);
|
|
||||||
void destroy(bool validationsRequired, VkInstance vulkan);
|
|
||||||
|
|
||||||
|
|
||||||
VkResult createDebugReportCallbackEXT(
|
|
||||||
VkInstance vulkan,
|
|
||||||
const VkDebugReportCallbackCreateInfoEXT* info,
|
|
||||||
const VkAllocationCallbacks* allocator,
|
|
||||||
VkDebugReportCallbackEXT* callback) {
|
|
||||||
|
|
||||||
auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vulkan, "vkCreateDebugReportCallbackEXT");
|
|
||||||
|
|
||||||
if (func != nullptr) {
|
|
||||||
return func(vulkan, info, allocator, callback);
|
|
||||||
} else {
|
|
||||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroyDebugReportCallbackEXT(
|
|
||||||
VkInstance vulkan,
|
|
||||||
const VkDebugReportCallbackEXT callback,
|
|
||||||
const VkAllocationCallbacks* allocator) {
|
|
||||||
|
|
||||||
auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vulkan, "vkDestroyDebugReportCallbackEXT");
|
|
||||||
|
|
||||||
if (func != nullptr) {
|
|
||||||
func(vulkan, callback, allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <vlkx/vulkan/ValidationAndExtension.h>
|
|
||||||
|
|
||||||
struct SwapChainMeta {
|
|
||||||
VkSurfaceCapabilitiesKHR capabilities;
|
|
||||||
std::vector<VkSurfaceFormatKHR> formats;
|
|
||||||
std::vector<VkPresentModeKHR> modes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct QueueFamilies {
|
|
||||||
int graphics = -1;
|
|
||||||
int presentation = -1;
|
|
||||||
|
|
||||||
bool present() {
|
|
||||||
return graphics >= 0 && presentation >= 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VulkanDevice {
|
|
||||||
public:
|
|
||||||
VulkanDevice();
|
|
||||||
~VulkanDevice();
|
|
||||||
|
|
||||||
/** Physical Devices **/
|
|
||||||
VkPhysicalDevice physical;
|
|
||||||
VkPhysicalDeviceLimits limits;
|
|
||||||
SwapChainMeta swapChain;
|
|
||||||
QueueFamilies queueData;
|
|
||||||
|
|
||||||
std::vector<const char*> deviceExtensions = {
|
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
|
||||||
};
|
|
||||||
|
|
||||||
void choosePhysicalDevice(VkInstance* vulkan, VkSurfaceKHR surface);
|
|
||||||
bool isSuitable(VkPhysicalDevice device, VkSurfaceKHR surface);
|
|
||||||
bool isSupported(VkPhysicalDevice device);
|
|
||||||
|
|
||||||
SwapChainMeta checkSwapchain(VkPhysicalDevice device, VkSurfaceKHR surface);
|
|
||||||
QueueFamilies checkQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
|
||||||
|
|
||||||
QueueFamilies getQueues() { return queueData; }
|
|
||||||
|
|
||||||
/** Logical Devices **/
|
|
||||||
VkDevice logical;
|
|
||||||
VkQueue graphicsQueue;
|
|
||||||
VkQueue presentationQueue;
|
|
||||||
|
|
||||||
void createLogicalDevice(VkSurfaceKHR surface, bool validationRequired, ValidationAndExtension* validators);
|
|
||||||
void destroy();
|
|
||||||
};
|
|
|
@ -1,99 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vlkx/vulkan/ValidationAndExtension.h>
|
|
||||||
#include <vlkx/vulkan/VulkanDevice.h>
|
|
||||||
|
|
||||||
#include <vulkan/vk_mem_alloc.h>
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
|
|
||||||
#include <SDL_vulkan.h>
|
|
||||||
#include <core/Module.h>
|
|
||||||
#include "SwapChain.h"
|
|
||||||
|
|
||||||
namespace vlkx { class ScreenRenderPassManager; }
|
|
||||||
|
|
||||||
class VulkanModule : public ShadowEngine::RendererModule {
|
|
||||||
SHObject_Base(VulkanModule);
|
|
||||||
public:
|
|
||||||
|
|
||||||
VulkanModule();
|
|
||||||
~VulkanModule() override;
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
static const bool validationRequired = true;
|
|
||||||
#else
|
|
||||||
static const bool validationRequired = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void PreInit() override;
|
|
||||||
|
|
||||||
void Init() override;
|
|
||||||
|
|
||||||
void Recreate() override;
|
|
||||||
|
|
||||||
void Update(int frame) override;
|
|
||||||
|
|
||||||
void PreRender() override;
|
|
||||||
|
|
||||||
void Render(VkCommandBuffer& commands, int frame) override;
|
|
||||||
|
|
||||||
void OverlayRender() override;
|
|
||||||
|
|
||||||
void LateRender(VkCommandBuffer& commands, int frame) override;
|
|
||||||
|
|
||||||
void AfterFrameEnd() override;
|
|
||||||
|
|
||||||
void Destroy() override;
|
|
||||||
|
|
||||||
void Event(SDL_Event* e) override;
|
|
||||||
|
|
||||||
void BeginRenderPass(const std::unique_ptr<vlkx::RenderCommand>& commands) override;
|
|
||||||
|
|
||||||
void EnableEditor() override;
|
|
||||||
|
|
||||||
VkExtent2D GetRenderExtent() override;
|
|
||||||
|
|
||||||
// VulkanModule is a singleton class.
|
|
||||||
static VulkanModule* instance;
|
|
||||||
static VulkanModule* getInstance();
|
|
||||||
|
|
||||||
// Initialize all Vulkan context and prepare validations in debug mode.
|
|
||||||
void initVulkan(SDL_Window* window);
|
|
||||||
void createAppAndVulkanInstance(bool enableValidation, ValidationAndExtension* validations);
|
|
||||||
|
|
||||||
// Start and end a frame render.
|
|
||||||
void startDraw();
|
|
||||||
void endDraw();
|
|
||||||
|
|
||||||
// Cleanup after the application has closed.
|
|
||||||
void cleanup();
|
|
||||||
|
|
||||||
VkInstance getVulkan() { return vulkan; }
|
|
||||||
VulkanDevice* getDevice() { return device; }
|
|
||||||
SwapChain* getSwapchain() { return swapchain; }
|
|
||||||
VmaAllocator getAllocator() { return allocator; }
|
|
||||||
SDL_Window* getWind() { return wnd; }
|
|
||||||
const std::unique_ptr<vlkx::ScreenRenderPassManager>& getRenderPass();
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool editorEnabled = false;
|
|
||||||
std::vector<VkDescriptorSet> editorRenderPlanes;
|
|
||||||
std::vector<std::unique_ptr<vlkx::Image>> editorContentFrames;
|
|
||||||
|
|
||||||
// The SDL Window contains the size of the drawable area.
|
|
||||||
SDL_Window* wnd;
|
|
||||||
// To handle the validation of Vulkan API usage
|
|
||||||
ValidationAndExtension* validators{};
|
|
||||||
// To manage interaction with the hardware
|
|
||||||
VulkanDevice* device{};
|
|
||||||
// To handle the framebuffers
|
|
||||||
SwapChain* swapchain{};
|
|
||||||
// To handle automatic management of memory.
|
|
||||||
VmaAllocator allocator{};
|
|
||||||
// To manage the Vulkan context that was passed to us by the API
|
|
||||||
VkInstance vulkan{};
|
|
||||||
// To manage the canvas that was given to us by GLFW
|
|
||||||
VkSurfaceKHR surface{};
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,412 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vlkx/vulkan/VulkanDevice.h>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
#include "vlkx/vulkan/Tools.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
// Root buffer class.
|
|
||||||
// Used to store & move data between the CPU and GPU.
|
|
||||||
// Basically; hotspot!
|
|
||||||
// Utilities and subclasses exist to optimise and speed up transfer and management of data in bulk.
|
|
||||||
class Buffer {
|
|
||||||
public:
|
|
||||||
// Metadata of CPU->GPU data copying.
|
|
||||||
struct CopyMeta {
|
|
||||||
const void* data; // The start of data in RAM
|
|
||||||
VkDeviceSize length; // The amount of data to move
|
|
||||||
VkDeviceSize start; // The start (destination) in GPU memory
|
|
||||||
};
|
|
||||||
|
|
||||||
// Metadata of bulk CPU->GPU data copying.
|
|
||||||
struct BulkCopyMeta {
|
|
||||||
VkDeviceSize length; // The total data size of all transfers.
|
|
||||||
std::vector<CopyMeta> metas;
|
|
||||||
};
|
|
||||||
|
|
||||||
Buffer(const Buffer&) = delete;
|
|
||||||
Buffer& operator=(const Buffer&) = delete;
|
|
||||||
|
|
||||||
Buffer();
|
|
||||||
|
|
||||||
virtual ~Buffer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************/
|
|
||||||
/** */
|
|
||||||
/** START OF DATA BUFFERS */
|
|
||||||
/** */
|
|
||||||
/*******************************************/
|
|
||||||
|
|
||||||
// A buffer that stores data on GPU.
|
|
||||||
// Usage of the data is determined by child classes.
|
|
||||||
class DataBuffer : public Buffer {
|
|
||||||
public:
|
|
||||||
DataBuffer(const DataBuffer&) = delete;
|
|
||||||
DataBuffer& operator=(const DataBuffer&) = delete;
|
|
||||||
|
|
||||||
DataBuffer() = default;
|
|
||||||
|
|
||||||
~DataBuffer() override {
|
|
||||||
vmaDestroyBuffer(VkTools::allocator, managed.buffer, managed.allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using Buffer::Buffer;
|
|
||||||
|
|
||||||
void setBuffer(const VkTools::ManagedBuffer& buffer) { managed = buffer; }
|
|
||||||
VkTools::ManagedBuffer get() const { return managed; }
|
|
||||||
const VkBuffer& getBuffer() const { return managed.buffer; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkTools::ManagedBuffer managed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A buffer visible to both GPU and CPU.
|
|
||||||
// Useful for uploading data to GPU for format conversions.
|
|
||||||
class StagingBuffer : public DataBuffer {
|
|
||||||
public:
|
|
||||||
StagingBuffer(const BulkCopyMeta& copyMeta);
|
|
||||||
|
|
||||||
StagingBuffer(const StagingBuffer&) = delete;
|
|
||||||
StagingBuffer& operator=(const StagingBuffer&) = delete;
|
|
||||||
|
|
||||||
void copy(const VkBuffer& target) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const VkDeviceSize dataSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Root class of vertex buffers.
|
|
||||||
// Provides utilities for subclasses.
|
|
||||||
class VertexBuffer : public DataBuffer {
|
|
||||||
public:
|
|
||||||
VertexBuffer(const VertexBuffer&) = delete;
|
|
||||||
VertexBuffer& operator=(const VertexBuffer&) = delete;
|
|
||||||
|
|
||||||
// Get attributes of vertexes in the buffer
|
|
||||||
// Location will start from "start"
|
|
||||||
// Binding will not be set
|
|
||||||
std::vector<VkVertexInputAttributeDescription> getAttrs(uint32_t start) const;
|
|
||||||
|
|
||||||
// Draw these vertexes without a buffer per vertex.
|
|
||||||
static void draw(const VkCommandBuffer& buffer, uint32_t verts, uint32_t instances);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class DynamicBuffer;
|
|
||||||
|
|
||||||
explicit VertexBuffer(std::vector<VkVertexInputAttributeDescription>&& attrs) : DataBuffer(), attributes(attrs) {}
|
|
||||||
|
|
||||||
// Initialize device memory and the managed buffer.
|
|
||||||
// indices and vertexes are put in the same buffer.
|
|
||||||
// if dynamic, the buffer will be host visible, this allows dynamic text.
|
|
||||||
// otherwise, the buffer is device local.
|
|
||||||
void create(VkDeviceSize totalSize, bool dynamic, bool indexes);
|
|
||||||
|
|
||||||
const std::vector<VkVertexInputAttributeDescription> attributes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************/
|
|
||||||
/** */
|
|
||||||
/** END OF DATA BUFFERS */
|
|
||||||
/** */
|
|
||||||
/*******************************************/
|
|
||||||
|
|
||||||
// A simple plugin to allow the vertex buffer to be widened when reserved with a larger size.
|
|
||||||
class DynamicBuffer {
|
|
||||||
public:
|
|
||||||
DynamicBuffer(const DynamicBuffer&) = delete;
|
|
||||||
DynamicBuffer& operator=(const DynamicBuffer&) = delete;
|
|
||||||
|
|
||||||
~DynamicBuffer() = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
DynamicBuffer(size_t size, bool hasIndices, VertexBuffer* buffer);
|
|
||||||
|
|
||||||
// Reallocate the vertex buffer if the given pSize is larger than the available space
|
|
||||||
void resize(size_t pSize);
|
|
||||||
|
|
||||||
VkDeviceSize bufferSize() const { return size; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const bool hasIndices;
|
|
||||||
VertexBuffer* vertexBuffer;
|
|
||||||
VkDeviceSize size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************/
|
|
||||||
/** */
|
|
||||||
/** START OF PER-VERTEX BUFFERS */
|
|
||||||
/** */
|
|
||||||
/*******************************************/
|
|
||||||
|
|
||||||
// Root class of buffers that store per-vertex data.
|
|
||||||
// eg. shader data
|
|
||||||
class PerVertexBuffer : public VertexBuffer {
|
|
||||||
public:
|
|
||||||
// Interprets the layout of data in containers (vector, etc)
|
|
||||||
struct VertexDataMeta {
|
|
||||||
template <typename C>
|
|
||||||
VertexDataMeta(const C& cont, int unitsPerMesh) : data(cont.data()), unitsPerMesh(unitsPerMesh), sizePerMesh(sizeof(cont[0]) * unitsPerMesh) {}
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
VertexDataMeta(const C& cont) : VertexDataMeta(cont, static_cast<int>(cont.size())) {}
|
|
||||||
|
|
||||||
const void* data;
|
|
||||||
int unitsPerMesh;
|
|
||||||
size_t sizePerMesh;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Interface for buffer data.
|
|
||||||
class BufferDataMeta {
|
|
||||||
public:
|
|
||||||
virtual ~BufferDataMeta() = default;
|
|
||||||
// Populate info and return a bulk copy meta for copying the data to device
|
|
||||||
virtual BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const = 0;
|
|
||||||
// Indicate whether the buffer contains index data too
|
|
||||||
virtual bool hasIndices() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Meshes do not share indices, with variable vertices.
|
|
||||||
class NoIndexBufferMeta : public BufferDataMeta {
|
|
||||||
public:
|
|
||||||
explicit NoIndexBufferMeta(std::vector<VertexDataMeta>&& perVertex) : perMeshVertices(perVertex) {}
|
|
||||||
|
|
||||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
|
||||||
bool hasIndices() const override { return false; };
|
|
||||||
private:
|
|
||||||
const std::vector<VertexDataMeta> perMeshVertices;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Meshes share indices, with static vertices.
|
|
||||||
class SharedIndexMeta : public BufferDataMeta {
|
|
||||||
public:
|
|
||||||
SharedIndexMeta(int meshes, const VertexDataMeta& perVertex, const VertexDataMeta& sharedIndices) : meshes(meshes),
|
|
||||||
perMeshVertex(perVertex),
|
|
||||||
sharedIndices(sharedIndices) {}
|
|
||||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
|
||||||
bool hasIndices() const override { return true; };
|
|
||||||
private:
|
|
||||||
const int meshes;
|
|
||||||
const VertexDataMeta perMeshVertex;
|
|
||||||
const VertexDataMeta sharedIndices;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Meshes do not share indexes, with variable indices and vertices.
|
|
||||||
class NoShareMeta : public BufferDataMeta {
|
|
||||||
public:
|
|
||||||
struct PerMesh {
|
|
||||||
VertexDataMeta indices;
|
|
||||||
VertexDataMeta vertices;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit NoShareMeta(std::vector<PerMesh>&& perMesh) : perMeshMeta(std::move(perMesh)) {}
|
|
||||||
|
|
||||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
|
||||||
bool hasIndices() const override { return true; };
|
|
||||||
private:
|
|
||||||
const std::vector<PerMesh> perMeshMeta;
|
|
||||||
};
|
|
||||||
|
|
||||||
PerVertexBuffer(const PerVertexBuffer&) = delete;
|
|
||||||
PerVertexBuffer& operator=(const PerVertexBuffer&) = delete;
|
|
||||||
|
|
||||||
// Render mesh a given number of times, into a recording buffer.
|
|
||||||
void draw(const VkCommandBuffer& buffer, uint32_t bind, int index, uint32_t instances) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using VertexBuffer::VertexBuffer;
|
|
||||||
|
|
||||||
// Stores vertex data for buffers without indices
|
|
||||||
struct MeshDataNoIndex {
|
|
||||||
struct Info {
|
|
||||||
uint32_t vertexCount;
|
|
||||||
VkDeviceSize vertexStart;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Info> info;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stores vertex and index data for buffers with both
|
|
||||||
struct MeshDataIndex {
|
|
||||||
struct Info {
|
|
||||||
uint32_t indexCount;
|
|
||||||
VkDeviceSize indexStart;
|
|
||||||
VkDeviceSize vertexStart;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Info> info;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::variant<MeshDataNoIndex, MeshDataIndex>* getInfo() { return &meshDataInfo; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::variant<MeshDataNoIndex, MeshDataIndex> meshDataInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stores static data for one-time upload.
|
|
||||||
class StaticPerVertexBuffer : public PerVertexBuffer {
|
|
||||||
public:
|
|
||||||
StaticPerVertexBuffer(const BufferDataMeta& info, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
|
||||||
|
|
||||||
StaticPerVertexBuffer(const StaticPerVertexBuffer&) = delete;
|
|
||||||
StaticPerVertexBuffer& operator=(const StaticPerVertexBuffer&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stores host-visible data that can be reallocated.
|
|
||||||
class DynamicPerVertexBuffer : public PerVertexBuffer, public DynamicBuffer {
|
|
||||||
public:
|
|
||||||
DynamicPerVertexBuffer(size_t size, std::vector<VkVertexInputAttributeDescription>&& attrs) : PerVertexBuffer(std::move(attrs)), DynamicBuffer(size, true, this) {}
|
|
||||||
|
|
||||||
DynamicPerVertexBuffer(const DynamicPerVertexBuffer&) = delete;
|
|
||||||
DynamicPerVertexBuffer& operator=(const DynamicPerVertexBuffer&) = delete;
|
|
||||||
|
|
||||||
void copyToDevice(const BufferDataMeta& meta);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************/
|
|
||||||
/** */
|
|
||||||
/** END OF PER-VERTEX BUFFERS */
|
|
||||||
/** */
|
|
||||||
/*******************************************/
|
|
||||||
|
|
||||||
// Root class of buffers that store vertex data per instance of a mesh.
|
|
||||||
class PerInstanceVertexBuffer : public VertexBuffer {
|
|
||||||
public:
|
|
||||||
PerInstanceVertexBuffer(const PerInstanceVertexBuffer&) = delete;
|
|
||||||
PerInstanceVertexBuffer& operator=(const PerInstanceVertexBuffer&) = delete;
|
|
||||||
|
|
||||||
void bind(const VkCommandBuffer& commands, uint32_t bindPoint, int offset) const;
|
|
||||||
|
|
||||||
uint32_t getSize() const { return sizePerInstance; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PerInstanceVertexBuffer(uint32_t size, std::vector<VkVertexInputAttributeDescription>&& attrs) : VertexBuffer(std::move(attrs)), sizePerInstance(size) {}
|
|
||||||
private:
|
|
||||||
const uint32_t sizePerInstance;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stores vertices that are static per instance of the mesh.
|
|
||||||
class StaticPerInstanceBuffer : public PerInstanceVertexBuffer {
|
|
||||||
public:
|
|
||||||
StaticPerInstanceBuffer(uint32_t size, const void* data, uint32_t instances, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
StaticPerInstanceBuffer(const C& cont, std::vector<VkVertexInputAttributeDescription>&& attrs) : StaticPerInstanceBuffer(sizeof(cont[0]), cont.data(), CONTAINER_SIZE(cont), std::move(attrs)) {}
|
|
||||||
|
|
||||||
StaticPerInstanceBuffer(const StaticPerInstanceBuffer&) = delete;
|
|
||||||
StaticPerInstanceBuffer& operator=(const StaticPerInstanceBuffer&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stores vertices of meshes that are dynamic (ie. text, shifting meshes
|
|
||||||
class DynamicPerInstanceBuffer : public PerInstanceVertexBuffer, public DynamicBuffer {
|
|
||||||
public:
|
|
||||||
DynamicPerInstanceBuffer(uint32_t size, size_t maxInstances, std::vector<VkVertexInputAttributeDescription>&& attrs) : PerInstanceVertexBuffer(size, std::move(attrs)), DynamicBuffer(size * maxInstances, false, this) {}
|
|
||||||
|
|
||||||
DynamicPerInstanceBuffer(const DynamicPerInstanceBuffer&) = delete;
|
|
||||||
DynamicPerInstanceBuffer& operator=(const DynamicPerInstanceBuffer*) = delete;
|
|
||||||
|
|
||||||
void copyToDevice(const void* data, uint32_t instances);
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
void copyToDevice(const C& cont) {
|
|
||||||
copyToDevice(cont.data(), CONTAINER_SIZE(cont));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************/
|
|
||||||
/** */
|
|
||||||
/** END OF PER-INSTANCE BUFFERS */
|
|
||||||
/** */
|
|
||||||
/*******************************************/
|
|
||||||
|
|
||||||
// Holds uniform data on host and device.
|
|
||||||
// Supports superallocating (allocating more than one "set" of uniforms at once)
|
|
||||||
// Data is stored on host and device simultaneously, so set the host data and flush it to the device.
|
|
||||||
class UniformBuffer : public DataBuffer {
|
|
||||||
public:
|
|
||||||
UniformBuffer(size_t chunkSize, int chunks);
|
|
||||||
|
|
||||||
UniformBuffer(const UniformBuffer&) = delete;
|
|
||||||
UniformBuffer& operator=(const UniformBuffer&) = delete;
|
|
||||||
|
|
||||||
~UniformBuffer() override { delete data; }
|
|
||||||
|
|
||||||
// Whether this buffer holds a single chunk (is not superallocated).
|
|
||||||
// Simplifies certain algorithms significantly if you know this beforehand
|
|
||||||
bool isSingle() const { return numChunks == 1; }
|
|
||||||
|
|
||||||
// Get the data in the buffer, casted to the given type
|
|
||||||
template <typename DataType>
|
|
||||||
DataType* getData(int index) const {
|
|
||||||
checkIndex(index);
|
|
||||||
return reinterpret_cast<DataType*>(data + chunkSize * index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload (flush) the uniform to the GPU
|
|
||||||
void upload(int index) const;
|
|
||||||
void upload(int index, VkDeviceSize dataSize, VkDeviceSize start) const;
|
|
||||||
|
|
||||||
static VkDescriptorType getDescriptorType() { return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; }
|
|
||||||
|
|
||||||
VkDescriptorBufferInfo getDescriptorInfo(int index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void checkIndex(int index) const;
|
|
||||||
|
|
||||||
char* data;
|
|
||||||
const size_t chunkSize;
|
|
||||||
const int numChunks;
|
|
||||||
|
|
||||||
size_t chunkLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A small, highly efficient buffer much like the Uniform Buffer.
|
|
||||||
class PushConstant {
|
|
||||||
public:
|
|
||||||
// Size must be < 128.
|
|
||||||
PushConstant(size_t size, int numFrames);
|
|
||||||
|
|
||||||
PushConstant(const PushConstant&) = delete;
|
|
||||||
PushConstant& operator=(const PushConstant&) = delete;
|
|
||||||
|
|
||||||
~PushConstant() { delete[] data; }
|
|
||||||
|
|
||||||
// Whether this buffer holds a single chunk (is not superallocated).
|
|
||||||
// Simplifies certain algorithms significantly if you know this beforehand
|
|
||||||
bool isSingle() const { return numFrames == 1; }
|
|
||||||
|
|
||||||
uint32_t getSize() const { return sizePerFrame; }
|
|
||||||
|
|
||||||
// Get the data in the buffer, casted to the given type
|
|
||||||
template <typename DataType>
|
|
||||||
DataType* getData(int frame) const {
|
|
||||||
checkIndex(frame);
|
|
||||||
return reinterpret_cast<DataType*>(data + (sizePerFrame * frame));
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPushConstantRange makeRange(VkShaderStageFlags stage) {
|
|
||||||
return VkPushConstantRange { stage, 0, sizePerFrame };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload (flush) the uniform to the GPU
|
|
||||||
void upload(const VkCommandBuffer& commands, const VkPipelineLayout& pipelineLayout, int frame, uint32_t offset, VkShaderStageFlags stage) const;
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
void checkIndex(int index) const;
|
|
||||||
|
|
||||||
char* data;
|
|
||||||
const uint32_t sizePerFrame;
|
|
||||||
const int numFrames;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include "Queue.h"
|
|
||||||
#include "vlkx/vulkan/VulkanDevice.h"
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
// Root class of VkCommandBuffer wrappers.
|
|
||||||
class CommandBuffer {
|
|
||||||
public:
|
|
||||||
using Command = std::function<void(const VkCommandBuffer& buffer)>;
|
|
||||||
|
|
||||||
CommandBuffer(const CommandBuffer&) = delete;
|
|
||||||
CommandBuffer& operator=(const CommandBuffer&) = delete;
|
|
||||||
|
|
||||||
virtual ~CommandBuffer() {
|
|
||||||
vkDestroyCommandPool(dev->logical, pool, nullptr);
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
CommandBuffer();
|
|
||||||
|
|
||||||
void setPool(const VkCommandPool& newPool) { pool = newPool; }
|
|
||||||
VulkanDevice* dev;
|
|
||||||
private:
|
|
||||||
VkCommandPool pool;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A command buffer that will be immediately executed.
|
|
||||||
class ImmediateCommand : public CommandBuffer {
|
|
||||||
public:
|
|
||||||
ImmediateCommand(Queue queue);
|
|
||||||
|
|
||||||
ImmediateCommand(const ImmediateCommand&) = delete;
|
|
||||||
ImmediateCommand& operator=(const ImmediateCommand&) = delete;
|
|
||||||
|
|
||||||
void run(const Command& cmd);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Queue queue;
|
|
||||||
VkCommandBuffer commands;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A command buffer that will be reused every frame.
|
|
||||||
class RenderCommand : public CommandBuffer {
|
|
||||||
public:
|
|
||||||
using Command = std::function<void(const VkCommandBuffer& commands, uint32_t framebuffer)>;
|
|
||||||
using Update = std::function<void(int frame)>;
|
|
||||||
|
|
||||||
~RenderCommand() override {
|
|
||||||
// Destroy our own data
|
|
||||||
vkDestroySemaphore(dev->logical, renderDoneSem, nullptr);
|
|
||||||
vkDestroySemaphore(dev->logical, newImageSem, nullptr);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 2; i++) {
|
|
||||||
vkDestroyFence(dev->logical, inFlight[i], nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderCommand(int frames);
|
|
||||||
|
|
||||||
RenderCommand(const RenderCommand&) = delete;
|
|
||||||
RenderCommand& operator=(const RenderCommand&) = delete;
|
|
||||||
|
|
||||||
uint32_t getFrame() { return imageIndex; }
|
|
||||||
|
|
||||||
void nextFrame() { imageIndex = (imageIndex + 1) % 2; }
|
|
||||||
|
|
||||||
std::optional<VkResult> execute(int frame, const VkSwapchainKHR& swapchain, const Update& update, const Command& cmd);
|
|
||||||
// Renders a single frame out, no semaphores or fences.
|
|
||||||
std::optional<VkResult> executeSimple(int frame, const Update& update, const Command& cmd);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<VkCommandBuffer> commands;
|
|
||||||
// Raised when a new image is available
|
|
||||||
VkSemaphore newImageSem;
|
|
||||||
// Raised when a render is finished
|
|
||||||
VkSemaphore renderDoneSem;
|
|
||||||
// Stores fences for frames that are currently "in flight".
|
|
||||||
std::vector<VkFence> inFlight;
|
|
||||||
|
|
||||||
// The index of the texture that is currently being used by the GPU.
|
|
||||||
uint32_t imageIndex = 0;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <temp/model/Loader.h>
|
|
||||||
#include <vlkx/vulkan/VulkanModule.h>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
class Descriptor {
|
|
||||||
public:
|
|
||||||
using TextureType = vlkxtemp::ModelLoader::TextureType;
|
|
||||||
using BufferInfos = std::map<uint32_t, std::vector<VkDescriptorBufferInfo>>;
|
|
||||||
using ImageInfos = std::map<uint32_t, std::vector<VkDescriptorImageInfo>>;
|
|
||||||
|
|
||||||
struct Meta {
|
|
||||||
struct Binding {
|
|
||||||
uint32_t bindPoint;
|
|
||||||
uint32_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDescriptorType type;
|
|
||||||
VkShaderStageFlags stage;
|
|
||||||
std::vector<Binding> bindings;
|
|
||||||
};
|
|
||||||
|
|
||||||
Descriptor(const Descriptor&) = delete;
|
|
||||||
Descriptor& operator=(const Descriptor&) = delete;
|
|
||||||
|
|
||||||
virtual ~Descriptor() {
|
|
||||||
vkDestroyDescriptorSetLayout(VulkanModule::getInstance()->getDevice()->logical, layout, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkDescriptorSetLayout& getLayout() const { return layout; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit Descriptor() = default;
|
|
||||||
|
|
||||||
void setLayout(const VkDescriptorSetLayout& newLayout) { layout = newLayout; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkDescriptorSetLayout layout;
|
|
||||||
};
|
|
||||||
|
|
||||||
class StaticDescriptor : public Descriptor {
|
|
||||||
public:
|
|
||||||
|
|
||||||
StaticDescriptor(std::vector<Meta> metas);
|
|
||||||
StaticDescriptor(const StaticDescriptor&) = delete;
|
|
||||||
StaticDescriptor& operator=(const StaticDescriptor&) = delete;
|
|
||||||
|
|
||||||
~StaticDescriptor() override {
|
|
||||||
vkDestroyDescriptorPool(VulkanModule::getInstance()->getDevice()->logical, pool, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const StaticDescriptor& buffers(VkDescriptorType type, const BufferInfos& infos) const;
|
|
||||||
const StaticDescriptor& images(VkDescriptorType type, const ImageInfos& infos) const;
|
|
||||||
|
|
||||||
void bind(const VkCommandBuffer& commands, const VkPipelineLayout& layout, VkPipelineBindPoint bindPoint) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const StaticDescriptor& updateSet(const std::vector<VkWriteDescriptorSet>& write) const;
|
|
||||||
|
|
||||||
VkDescriptorPool pool;
|
|
||||||
VkDescriptorSet set;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: dynamic sets
|
|
||||||
}
|
|
|
@ -1,331 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "vlkx/vulkan/Tools.h"
|
|
||||||
#include "ImageUsage.h"
|
|
||||||
#include <shadow/util/RefCounter.h>
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
// Describes an image without initializing or storing any heavy data.
|
|
||||||
class ImageDescriptor {
|
|
||||||
public:
|
|
||||||
enum class Type { Single, Cubemap };
|
|
||||||
struct Dimension {
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
uint32_t channels;
|
|
||||||
|
|
||||||
VkExtent2D getExtent() const { return { width, height }; }
|
|
||||||
size_t getSize() const { return width * height * channels; }
|
|
||||||
};
|
|
||||||
|
|
||||||
Type getType() const { return type; }
|
|
||||||
VkExtent2D getExtent() const { return dimensions.getExtent(); }
|
|
||||||
uint32_t getWidth() const { return dimensions.width; }
|
|
||||||
uint32_t getHeight() const { return dimensions.height; }
|
|
||||||
uint32_t getChannels() const { return dimensions.channels; }
|
|
||||||
|
|
||||||
std::vector<void*> getData() const {
|
|
||||||
if (type == Type::Single) return { (void*) data };
|
|
||||||
std::vector<void*> dataPtrs;
|
|
||||||
dataPtrs.reserve(6);
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
for (size_t i = 0; i < 6; i++) {
|
|
||||||
dataPtrs.emplace_back((char*) data + offset);
|
|
||||||
offset += dimensions.getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataPtrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLayers() const { return type == Type::Single ? 1 : 6; }
|
|
||||||
|
|
||||||
ImageDescriptor(Type t, const Dimension& d, const void* ptr) : type(t), dimensions(d), data(ptr) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type type;
|
|
||||||
Dimension dimensions;
|
|
||||||
const void* data;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// A staging buffer specialized for uploading images.
|
|
||||||
class ImageStagingBuffer : public StagingBuffer {
|
|
||||||
public:
|
|
||||||
using StagingBuffer::StagingBuffer;
|
|
||||||
|
|
||||||
ImageStagingBuffer(const ImageStagingBuffer&) = delete;
|
|
||||||
ImageStagingBuffer& operator=(const ImageStagingBuffer&) = delete;
|
|
||||||
|
|
||||||
void copy(const VkImage& target, const VkExtent3D& extent, uint32_t layers) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Root class that stores image data on GPU buffers
|
|
||||||
class ImageBuffer : public Buffer {
|
|
||||||
public:
|
|
||||||
ImageBuffer(const ImageBuffer&) = delete;
|
|
||||||
ImageBuffer& operator=(const ImageBuffer&) = delete;
|
|
||||||
|
|
||||||
~ImageBuffer() override {
|
|
||||||
vmaDestroyImage(VkTools::allocator, image.image, image.allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& get() const { return image; }
|
|
||||||
const VkImage& getImage() const { return image.image; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using Buffer::Buffer;
|
|
||||||
|
|
||||||
void setImage(const VkTools::ManagedImage newImg) { image = newImg; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkTools::ManagedImage image;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base class of all images; stores the common data.
|
|
||||||
class Image {
|
|
||||||
public:
|
|
||||||
Image(const Image&) = delete;
|
|
||||||
Image& operator=(const Image&) = delete;
|
|
||||||
|
|
||||||
virtual ~Image() {
|
|
||||||
vkDestroyImageView(dev->logical, view, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static VkDescriptorType getSampleType() { return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; }
|
|
||||||
static VkDescriptorType getLinearType() { return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; }
|
|
||||||
|
|
||||||
static ImageDescriptor loadSingleFromDisk(std::string path, bool flipY);
|
|
||||||
// The following are left unimplemented intentionally.
|
|
||||||
//static ImageDescriptor loadSingleFromVFS(std::string path, bool flipY);
|
|
||||||
static ImageDescriptor loadCubeFromDisk(const std::string& directory, const std::array<std::string, 6>& files, bool flipY);
|
|
||||||
//static ImageDescriptor loadCubeFromVFS(std::string directory, const std::array<std::string, 6>& files, bool flipY);
|
|
||||||
|
|
||||||
virtual ImageUsage getUsage() const { return ImageUsage {}; }
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& operator*() const { return get(); }
|
|
||||||
|
|
||||||
virtual const VkTools::ManagedImage& get() const = 0;
|
|
||||||
virtual const VkImage& getImage() const = 0;
|
|
||||||
|
|
||||||
const VkImageView& getView() const { return view; }
|
|
||||||
const VkExtent2D& getExtent() const { return extent; }
|
|
||||||
VkFormat getFormat() const { return format; }
|
|
||||||
virtual VkSampleCountFlagBits getSamples() const { return VK_SAMPLE_COUNT_1_BIT; }
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Image(const VkExtent2D& ext, VkFormat form);
|
|
||||||
|
|
||||||
void setView(const VkImageView& imgView) { view = imgView; }
|
|
||||||
|
|
||||||
VulkanDevice* dev;
|
|
||||||
VkImageView view;
|
|
||||||
VkExtent2D extent;
|
|
||||||
VkFormat format;
|
|
||||||
VkSampleCountFlagBits sampleCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configures image sampling in a sensible and extensible way
|
|
||||||
class ImageSampler {
|
|
||||||
public:
|
|
||||||
struct Config {
|
|
||||||
explicit Config(VkFilter filter = VK_FILTER_LINEAR, VkSamplerAddressMode mode = VK_SAMPLER_ADDRESS_MODE_REPEAT) : filter(filter), mode(mode) {}
|
|
||||||
|
|
||||||
VkFilter filter;
|
|
||||||
VkSamplerAddressMode mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
ImageSampler(int mipLevels, const Config& config);
|
|
||||||
|
|
||||||
ImageSampler(const ImageSampler&) = delete;
|
|
||||||
ImageSampler& operator=(const ImageSampler&) = delete;
|
|
||||||
|
|
||||||
~ImageSampler() {
|
|
||||||
vkDestroySampler(dev->logical, sampler, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkSampler& operator*() const { return sampler; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkSampler sampler;
|
|
||||||
VulkanDevice* dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Root of images which can be sampled.
|
|
||||||
class SamplableImage {
|
|
||||||
public:
|
|
||||||
virtual ~SamplableImage() = default;
|
|
||||||
|
|
||||||
// Return a VkDescriptorImageInfo we can use to update sets.
|
|
||||||
virtual VkDescriptorImageInfo getInfo(VkImageLayout layout) const = 0;
|
|
||||||
VkDescriptorImageInfo getInfoForSampling() const { return getInfo(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }
|
|
||||||
VkDescriptorImageInfo getInfoForLinear() const { return getInfo(VK_IMAGE_LAYOUT_GENERAL); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// A samplable image that lives on the GPU, with optional mipmapping.
|
|
||||||
// Use a RefCountedTexture when loading from files.
|
|
||||||
class TextureImage : public Image, public SamplableImage {
|
|
||||||
public:
|
|
||||||
// Image metadata
|
|
||||||
struct Meta {
|
|
||||||
VkExtent2D getExtent() const { return { width, height }; }
|
|
||||||
VkExtent3D get3DExtent() const { return { width, height, channels }; }
|
|
||||||
|
|
||||||
Buffer::BulkCopyMeta getCopyMeta() const;
|
|
||||||
|
|
||||||
std::vector<void*> data;
|
|
||||||
std::vector<ImageUsage> usages;
|
|
||||||
VkFormat format;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
uint32_t channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
TextureImage(bool mipmapping, const ImageSampler::Config& samplerConfig, const Meta& meta);
|
|
||||||
TextureImage(bool mipmapping, const ImageDescriptor& image, const std::vector<ImageUsage>& usages, const ImageSampler::Config& config);
|
|
||||||
|
|
||||||
TextureImage(const TextureImage&) = delete;
|
|
||||||
TextureImage& operator=(const TextureImage&) = delete;
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
|
||||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
|
||||||
|
|
||||||
VkDescriptorImageInfo getInfo(VkImageLayout layout) const override {
|
|
||||||
return { *sampler, getView(), layout };
|
|
||||||
}
|
|
||||||
// Textures are sampled in fragment shaders.
|
|
||||||
ImageUsage getUsage() const override {
|
|
||||||
return ImageUsage::sampledFragment();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
class TextureBuffer : public ImageBuffer {
|
|
||||||
public:
|
|
||||||
TextureBuffer(bool mipmaps, const Meta& meta);
|
|
||||||
|
|
||||||
TextureBuffer(const TextureBuffer&) = delete;
|
|
||||||
TextureBuffer& operator=(const TextureBuffer&) = delete;
|
|
||||||
|
|
||||||
int getMipping() const { return mipLevels; }
|
|
||||||
private:
|
|
||||||
int mipLevels = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const TextureBuffer buffer;
|
|
||||||
const ImageSampler sampler;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A texture image that lives on GPU that is reference counted.
|
|
||||||
// This allows it to be reused multiple times without reading the file in more than once.
|
|
||||||
// It also allows the texture to be destructed automatically once nothing in the scene uses it, for eg. level changes.
|
|
||||||
class RefCountedTexture : public SamplableImage {
|
|
||||||
public:
|
|
||||||
// Cubemaps are simply 6 textures, so we include them here for easy instantiation.
|
|
||||||
struct CubemapLocation {
|
|
||||||
std::string directory;
|
|
||||||
std::array<std::string, 6> files;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reference Counting works on both individual files and cubemaps, so we put them together.
|
|
||||||
using ImageLocation = std::variant<std::string, CubemapLocation>;
|
|
||||||
|
|
||||||
RefCountedTexture(const ImageLocation& location, std::vector<ImageUsage> usages, const ImageSampler::Config& config)
|
|
||||||
: texture(get(location, std::move(usages), config)) {}
|
|
||||||
|
|
||||||
RefCountedTexture(RefCountedTexture&&) noexcept = default;
|
|
||||||
RefCountedTexture& operator=(RefCountedTexture&&) noexcept = default;
|
|
||||||
|
|
||||||
VkDescriptorImageInfo getInfo(VkImageLayout layout) const override {
|
|
||||||
return texture->getInfo(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Image* operator->() const { return texture.operator->(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
using ReferenceCounter = shadowutil::RefCounter<TextureImage>;
|
|
||||||
// Get or load the specified image.
|
|
||||||
static ReferenceCounter get(const ImageLocation& location, const std::vector<ImageUsage>& usages, const ImageSampler::Config& config);
|
|
||||||
|
|
||||||
ReferenceCounter texture;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: unowned, offscreen images.
|
|
||||||
|
|
||||||
// Image that can be used as a depth / stencil buffer attachment.
|
|
||||||
class DepthStencilImage : public Image {
|
|
||||||
public:
|
|
||||||
DepthStencilImage(const DepthStencilImage&) = delete;
|
|
||||||
DepthStencilImage& operator=(const DepthStencilImage&) = delete;
|
|
||||||
|
|
||||||
DepthStencilImage(const VkExtent2D& extent);
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
|
||||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
class DepthStencilBuffer : public ImageBuffer {
|
|
||||||
public:
|
|
||||||
DepthStencilBuffer(const VkExtent2D& extent, VkFormat format);
|
|
||||||
DepthStencilBuffer(const DepthStencilBuffer&) = delete;
|
|
||||||
DepthStencilBuffer& operator=(const DepthStencilBuffer&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DepthStencilBuffer buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Image that references an existing image on the swapchain
|
|
||||||
class SwapchainImage : public Image {
|
|
||||||
public:
|
|
||||||
SwapchainImage(const SwapchainImage&) = delete;
|
|
||||||
SwapchainImage& operator=(const SwapchainImage&) = delete;
|
|
||||||
|
|
||||||
SwapchainImage(const VkImage& image, const VkExtent2D& extent, VkFormat format);
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& get() const override { return managed; }
|
|
||||||
const VkImage& getImage() const override { return image; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VkImage image;
|
|
||||||
VkTools::ManagedImage managed;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MultisampleImage : public Image {
|
|
||||||
public:
|
|
||||||
enum class Mode {
|
|
||||||
MostEfficient,
|
|
||||||
Highest
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unique_ptr<Image> createColor(const Image& targetImage, Mode mode);
|
|
||||||
static std::unique_ptr<Image> createDepthStencilMS(const VkExtent2D& extent, Mode mode);
|
|
||||||
static std::unique_ptr<Image> createDepthStencil(VkExtent2D& extent, std::optional<Mode> mode);
|
|
||||||
|
|
||||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
|
||||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
|
||||||
|
|
||||||
VkSampleCountFlagBits getSamples() const override { return samples; }
|
|
||||||
private:
|
|
||||||
class MultisampleBuffer : public ImageBuffer {
|
|
||||||
public:
|
|
||||||
enum class Type { Color, DepthStencil };
|
|
||||||
|
|
||||||
MultisampleBuffer(Type type, const VkExtent2D& extent, VkFormat format, VkSampleCountFlagBits samples);
|
|
||||||
MultisampleBuffer(const MultisampleBuffer&) = delete;
|
|
||||||
MultisampleBuffer& operator=(const MultisampleBuffer&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
MultisampleImage(const VkExtent2D& extent, VkFormat format, Mode mode, MultisampleBuffer::Type type);
|
|
||||||
|
|
||||||
VkSampleCountFlagBits chooseSamples(Mode mode);
|
|
||||||
|
|
||||||
const VkSampleCountFlagBits samples;
|
|
||||||
const MultisampleBuffer buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,297 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
/**
|
|
||||||
* Describes how an image (collection of color data) will be used in the GPU.
|
|
||||||
* Has three parts; an overall description, access methods, and location of access.
|
|
||||||
* Use the static methods to create an instance, or the blank initializer and set fields as required.
|
|
||||||
*/
|
|
||||||
class ImageUsage {
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
DontCare, // Image is unused
|
|
||||||
RenderTarget, // Color attachment is used
|
|
||||||
DepthStencil, // Depth / Stencil buffer is used
|
|
||||||
Multisample, // Resolves to a multisampled image
|
|
||||||
Presentation, // Framebuffer for presentation
|
|
||||||
LinearAccess, // Host-Readable
|
|
||||||
InputAttachment, // Only what we write, is read.
|
|
||||||
Sampled, // Image is sampled (ie. texture)
|
|
||||||
Transfer // Image is used as an intermediate for transfer.
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Access {
|
|
||||||
DontCare, // Image is unused
|
|
||||||
ReadOnly, // Read Only access.
|
|
||||||
WriteOnly, // Write Only access.
|
|
||||||
ReadWrite, // Read/Write access.
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Location {
|
|
||||||
DontCare, // Image is unused
|
|
||||||
Host, // Image only exists on the host and will be transferred.
|
|
||||||
VertexShader, // Image is only used in the VertexAll Shader.
|
|
||||||
FragmentShader, // Image is only used in the Fragment Shader.
|
|
||||||
ComputeShader, // Image is only used in a Compute Shader.
|
|
||||||
Other, // Reserved.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sampled in a fragment shader. Read-Only.
|
|
||||||
static ImageUsage sampledFragment() { return { Type::Sampled, Access::ReadOnly, Location::FragmentShader }; };
|
|
||||||
// Used as a render target (a render pass will output to this image). Read/Write.
|
|
||||||
static ImageUsage renderTarget(int loc) { return { Type::RenderTarget, Access::ReadWrite, Location::Other, loc}; };
|
|
||||||
// Resolves to a multisampled image. Write-Only.
|
|
||||||
static ImageUsage multisample() { return { Type::Multisample, Access::WriteOnly, Location::Other }; };
|
|
||||||
// A depth or stencil buffer. Access is given, but must not be DontCare.
|
|
||||||
static ImageUsage depthStencil(Access acc) { return { Type::DepthStencil, acc, Location::Other}; };
|
|
||||||
// Used to show to the user. Write-Only.
|
|
||||||
static ImageUsage presentation() { return { Type::Presentation, Access::ReadOnly, Location::Other }; };
|
|
||||||
// Input attachment for a fragment shader. Usually a texture. Read-Only.
|
|
||||||
static ImageUsage input() { return { Type::InputAttachment, Access::ReadOnly, Location::FragmentShader }; };
|
|
||||||
// Linearly accessed image. For a compute shader. Access is given, but must not be DontCare.
|
|
||||||
static ImageUsage compute(Access acc) { return { Type::LinearAccess, acc, Location::ComputeShader }; };
|
|
||||||
|
|
||||||
explicit ImageUsage() : ImageUsage(Type::DontCare, Access::DontCare, Location::DontCare) {}
|
|
||||||
|
|
||||||
bool operator==(const ImageUsage& other) const {
|
|
||||||
return type == other.type && access == other.access && location == other.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageUsageFlagBits getUsageFlags() const {
|
|
||||||
switch (type) {
|
|
||||||
case Type::DontCare: throw std::runtime_error("No usage for type DontCare");
|
|
||||||
case Type::RenderTarget:
|
|
||||||
case Type::Multisample:
|
|
||||||
case Type::Presentation:
|
|
||||||
return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
||||||
case Type::DepthStencil:
|
|
||||||
return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
||||||
case Type::LinearAccess:
|
|
||||||
return VK_IMAGE_USAGE_STORAGE_BIT;
|
|
||||||
case Type::Sampled:
|
|
||||||
return VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
||||||
case Type::Transfer:
|
|
||||||
switch (access) {
|
|
||||||
case Access::DontCare: throw std::runtime_error("No access type specified for transfer usage.");
|
|
||||||
case Access::ReadOnly: return VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
||||||
case Access::WriteOnly: return VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
||||||
case Access::ReadWrite: throw std::runtime_error("ReadWrite access type for Transfer usage is invalid.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static VkImageUsageFlags getFlagsForUsage(const std::vector<ImageUsage>& usages) {
|
|
||||||
auto flags = 0;
|
|
||||||
for (const auto& usage : usages) {
|
|
||||||
if (usage.type != Type::DontCare)
|
|
||||||
flags |= usage.getUsageFlags();
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<VkImageUsageFlags>(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageLayout getLayout() const {
|
|
||||||
switch (type) {
|
|
||||||
case Type::DontCare: return VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
case Type::RenderTarget:
|
|
||||||
case Type::Multisample:
|
|
||||||
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
||||||
case Type::DepthStencil: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
||||||
case Type::Presentation: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
||||||
case Type::LinearAccess: return VK_IMAGE_LAYOUT_GENERAL;
|
|
||||||
case Type::Sampled: return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
||||||
case Type::Transfer: return access == Access::ReadOnly ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
case Type::InputAttachment:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipelineStageFlags getStage() const {
|
|
||||||
switch (type) {
|
|
||||||
case Type::DontCare: return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
||||||
case Type::RenderTarget:
|
|
||||||
case Type::Multisample:
|
|
||||||
case Type::Presentation:
|
|
||||||
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
||||||
case Type::DepthStencil:
|
|
||||||
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
||||||
case Type::LinearAccess:
|
|
||||||
case Type::Sampled:
|
|
||||||
switch (location) {
|
|
||||||
case Location::Host: return VK_PIPELINE_STAGE_HOST_BIT;
|
|
||||||
case Location::FragmentShader: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
||||||
case Location::ComputeShader: return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
||||||
|
|
||||||
case Location::VertexShader:
|
|
||||||
case Location::Other:
|
|
||||||
throw std::runtime_error("Linear or sampled attachments must not be used in VertexAll or Other stages.");
|
|
||||||
case Location::DontCare: throw std::runtime_error("Linear or sampled attachments must have an access.");
|
|
||||||
}
|
|
||||||
|
|
||||||
case Type::Transfer:
|
|
||||||
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Access getAccess() const { return access; }
|
|
||||||
|
|
||||||
Type getType() const { return type; }
|
|
||||||
|
|
||||||
VkAccessFlags getAccessFlags() const {
|
|
||||||
switch (type) {
|
|
||||||
case Type::DontCare: return VK_ACCESS_NONE_KHR;
|
|
||||||
case Type::RenderTarget: return getReadOrWrite(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
|
||||||
case Type::DepthStencil: return getReadOrWrite(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
|
|
||||||
case Type::Multisample: return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
||||||
case Type::Presentation: return 0;
|
|
||||||
case Type::LinearAccess:
|
|
||||||
case Type::Sampled:
|
|
||||||
return location == Location::Host ? getReadOrWrite(VK_ACCESS_HOST_READ_BIT, VK_ACCESS_HOST_WRITE_BIT) : getReadOrWrite(VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT);
|
|
||||||
case Type::Transfer:
|
|
||||||
return getReadOrWrite(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
||||||
case Type::InputAttachment:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ImageUsage(Type t, Access a, Location l, std::optional<int> att = std::nullopt)
|
|
||||||
: type(t), access(a), location(l), attachment(att) {}
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
Access access;
|
|
||||||
Location location;
|
|
||||||
std::optional<int> attachment;
|
|
||||||
|
|
||||||
inline VkAccessFlags getReadOrWrite(VkAccessFlags read, VkAccessFlags write) const {
|
|
||||||
VkAccessFlags flag = 0;
|
|
||||||
if (access == Access::ReadOnly || access == Access::ReadWrite)
|
|
||||||
flag |= read;
|
|
||||||
if (access == Access::WriteOnly || access == Access::ReadWrite)
|
|
||||||
flag |= write;
|
|
||||||
return flag;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes how a single image will be used in each stage of a render pass.
|
|
||||||
* Allows a single image to be written to during one pass, read in a second, and presented in the final, and tracked.
|
|
||||||
* Helps figure out the optimizations that Vulkan can do to this image and the render passes that use it.
|
|
||||||
*/
|
|
||||||
class UsageTracker {
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit UsageTracker(const ImageUsage& initial) : initialUsage(initial) {}
|
|
||||||
explicit UsageTracker() = default;
|
|
||||||
|
|
||||||
UsageTracker(UsageTracker&&) noexcept = default;
|
|
||||||
UsageTracker& operator=(UsageTracker&&) noexcept = default;
|
|
||||||
|
|
||||||
// Fluent API; chain calls in a builder pattern.
|
|
||||||
#define fluent UsageTracker&
|
|
||||||
|
|
||||||
fluent add(int pass, const ImageUsage& usage) {
|
|
||||||
usageAtSubpass.insert( { pass, usage} );
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluent add(int start, int end, const ImageUsage& usage) {
|
|
||||||
for (int subpass = start; subpass <= end; ++subpass)
|
|
||||||
add(subpass, usage);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluent addMultisample(int pass, std::string_view name) {
|
|
||||||
multisamples.insert( { pass, std::string(name) } );
|
|
||||||
return add(pass, ImageUsage::multisample());
|
|
||||||
}
|
|
||||||
|
|
||||||
fluent setFinal(const ImageUsage& usage) {
|
|
||||||
finalUsage = usage;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<ImageUsage> getUsages() const {
|
|
||||||
size_t count = usageAtSubpass.size() + (finalUsage.has_value() ? 1 : 0) + 1;
|
|
||||||
|
|
||||||
std::vector<ImageUsage> usages;
|
|
||||||
usages.reserve(count);
|
|
||||||
usages.emplace_back(initialUsage);
|
|
||||||
|
|
||||||
for(const auto& pair : usageAtSubpass)
|
|
||||||
usages.emplace_back(pair.second);
|
|
||||||
|
|
||||||
if (finalUsage.has_value())
|
|
||||||
usages.emplace_back(finalUsage.value());
|
|
||||||
|
|
||||||
return usages;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] const std::map<int, ImageUsage>& getUsageMap() const {
|
|
||||||
return usageAtSubpass;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageUsage& getInitialUsage() { return initialUsage; }
|
|
||||||
std::optional<ImageUsage> getFinalUsage() { return finalUsage; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<int, ImageUsage> usageAtSubpass;
|
|
||||||
ImageUsage initialUsage;
|
|
||||||
std::optional<ImageUsage> finalUsage;
|
|
||||||
std::map<int, std::string> multisamples;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple wrapper that allows tracking the current usage of multiple images.
|
|
||||||
*/
|
|
||||||
class MultiImageTracker {
|
|
||||||
public:
|
|
||||||
MultiImageTracker() = default;
|
|
||||||
|
|
||||||
MultiImageTracker(const MultiImageTracker&) = delete;
|
|
||||||
MultiImageTracker& operator=(const MultiImageTracker&) = delete;
|
|
||||||
|
|
||||||
// Fluent API; chain calls in a builder pattern
|
|
||||||
#undef fluent
|
|
||||||
#define fluent MultiImageTracker&
|
|
||||||
|
|
||||||
fluent track(std::string&& name, const ImageUsage& usage) {
|
|
||||||
images.insert( { std::move(name), usage } );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluent track(const std::string& name, const ImageUsage& usage) {
|
|
||||||
return track(std::string(name), usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
fluent update(const std::string& name, const ImageUsage& usage) {
|
|
||||||
auto iter = images.find(name);
|
|
||||||
iter->second = usage;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool isTracking(const std::string& image) const {
|
|
||||||
return images.contains(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] const ImageUsage& get(const std::string& image) const {
|
|
||||||
return images.at(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<std::string, ImageUsage> images;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
struct Queue {
|
|
||||||
VkQueue queue;
|
|
||||||
int queueIndex;
|
|
||||||
};
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,129 +0,0 @@
|
||||||
#include <vlkx/render/Camera.h>
|
|
||||||
|
|
||||||
using namespace vlkx;
|
|
||||||
|
|
||||||
Camera& Camera::move(const glm::vec3 &delta) {
|
|
||||||
position += delta;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera &Camera::setPos(const glm::vec3 &pos) {
|
|
||||||
position = pos;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera &Camera::up(const glm::vec3 &up) {
|
|
||||||
upVector = glm::normalize(up);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera &Camera::forward(const glm::vec3 &forward) {
|
|
||||||
frontVector = glm::normalize(forward);
|
|
||||||
rightVector = glm::normalize(glm::cross(frontVector, upVector));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 Camera::getViewMatrix() const {
|
|
||||||
return glm::lookAt(position, position + frontVector, upVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
PerspectiveCamera &PerspectiveCamera::fieldOfView(float newFov) {
|
|
||||||
fov = newFov;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PerspectiveCamera::RT PerspectiveCamera::getRT() const {
|
|
||||||
const glm::vec3 upVec = glm::normalize(glm::cross(getRight(), getForward()));
|
|
||||||
const float fovTan = glm::tan(glm::radians(fov));
|
|
||||||
return { upVec * fovTan, getForward(), getRight() * fovTan * aspectRatio };
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 PerspectiveCamera::getProjMatrix() const {
|
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);
|
|
||||||
proj[1][1] = -proj[1][1];
|
|
||||||
return proj;
|
|
||||||
}
|
|
||||||
|
|
||||||
OrthographicCamera &OrthographicCamera::setWidth(float vWidth) {
|
|
||||||
width = vWidth;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 OrthographicCamera::getProjMatrix() const {
|
|
||||||
const float height = width / aspectRatio;
|
|
||||||
const auto halfSize = glm::vec2 { width, height } / 2.0f;
|
|
||||||
return glm::ortho(-halfSize.x, halfSize.x, -halfSize.y, halfSize.y, nearPlane, farPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void UserCamera<Type>::setInternal(std::function<void(Type *)> op) {
|
|
||||||
op(camera.get());
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void UserCamera<Type>::move(double x, double y) {
|
|
||||||
if (!isActive) return;
|
|
||||||
|
|
||||||
const auto offsetX = static_cast<float>(x * config.turnSpeed);
|
|
||||||
const auto offsetY = static_cast<float>(y * config.turnSpeed);
|
|
||||||
|
|
||||||
pitch = glm::clamp(pitch - offsetY, glm::radians(-89.9f), glm::radians(89.9f));
|
|
||||||
yaw = glm::mod(yaw - offsetX, glm::radians(360.0f));
|
|
||||||
camera->forward( { glm::cos(pitch) * glm::cos(yaw), glm::sin(pitch), glm::cos(pitch) * glm::sin(yaw) });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
bool UserCamera<Type>::scroll(double delta, double min, double max) {
|
|
||||||
if (!isActive) return false;
|
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Type, PerspectiveCamera>) {
|
|
||||||
auto newFov = (float) glm::clamp(camera->getFieldOfView() + delta, min, max);
|
|
||||||
if (newFov != camera->getFieldOfView()) {
|
|
||||||
camera->fieldOfView(newFov);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if constexpr (std::is_same_v<Type, OrthographicCamera>) {
|
|
||||||
const auto newWidth = (float) glm::clamp(camera->getWidth() + delta, min, max);
|
|
||||||
if (newWidth != camera->getWidth()) {
|
|
||||||
camera->setWidth(newWidth);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static_assert("Unhandled Camera Type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void UserCamera<Type>::press(Camera::Input key, float time) {
|
|
||||||
using Key = Camera::Input;
|
|
||||||
if (!isActive) return;
|
|
||||||
|
|
||||||
if (!config.center.has_value()) {
|
|
||||||
const float distance = time * config.moveSpeed;
|
|
||||||
switch (key) {
|
|
||||||
case Key::Up:
|
|
||||||
camera->move(+camera->getForward() * distance); break;
|
|
||||||
case Key::Down:
|
|
||||||
camera->move(-camera->getForward() * distance); break;
|
|
||||||
case Key::Left:
|
|
||||||
camera->move(-camera->getRight() * distance); break;
|
|
||||||
case Key::Right:
|
|
||||||
camera->move(+camera->getRight() * distance); break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void UserCamera<Type>::reset() {
|
|
||||||
refForward = camera->getForward();
|
|
||||||
refLeft = -camera->getRight();
|
|
||||||
pitch = yaw = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template class vlkx::UserCamera<PerspectiveCamera>;
|
|
||||||
template class vlkx::UserCamera<OrthographicCamera>;
|
|
|
@ -1,156 +0,0 @@
|
||||||
#include <vlkx/render/Geometry.h>
|
|
||||||
using namespace Geo;
|
|
||||||
|
|
||||||
void Mesh::setTriData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
|
||||||
std::vector<VertexAll> Vertices = {
|
|
||||||
{ { 0.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } },
|
|
||||||
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } },
|
|
||||||
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> Indices = {
|
|
||||||
0, 1, 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
vertices.clear(); indices.clear();
|
|
||||||
|
|
||||||
vertices = Vertices;
|
|
||||||
indices = Indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::setQuadData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
|
||||||
|
|
||||||
std::vector<VertexAll> Vertices = {
|
|
||||||
{ { -1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } },
|
|
||||||
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } },
|
|
||||||
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
|
||||||
{ { 1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> Indices = {
|
|
||||||
0, 1, 2,
|
|
||||||
0, 2, 3
|
|
||||||
};
|
|
||||||
|
|
||||||
vertices.clear(); indices.clear();
|
|
||||||
vertices = Vertices;
|
|
||||||
indices = Indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::setCubeData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
|
||||||
std::vector<VertexAll> Vertices = {
|
|
||||||
// Front
|
|
||||||
{ { -1.0f, -1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 0
|
|
||||||
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 1
|
|
||||||
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 2
|
|
||||||
{ { 1.0f, -1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 3
|
|
||||||
// Back
|
|
||||||
{ { 1.0, -1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0, 1.0 } }, // 4
|
|
||||||
{ { 1.0f, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0, 0.0 } }, // 5
|
|
||||||
{ { -1.0, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 1.0, 0.0 } }, // 6
|
|
||||||
{ { -1.0, -1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 1.0, 1.0 } }, // 7
|
|
||||||
// Left
|
|
||||||
{ { -1.0, -1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } }, // 8
|
|
||||||
{ { -1.0f, 1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0, 0.0 } }, // 9
|
|
||||||
{ { -1.0, 1.0, 1.0 },{ -1.0f, 0.0f, 0.0 },{ 1.0, 0.0 } }, // 10
|
|
||||||
{ { -1.0, -1.0, 1.0 },{ -1.0f, 0.0f, 0.0 },{ 1.0, 1.0 } }, // 11
|
|
||||||
// Right
|
|
||||||
{ { 1.0, -1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } }, // 12
|
|
||||||
{ { 1.0f, 1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 0.0 } }, // 13
|
|
||||||
{ { 1.0, 1.0, -1.0 },{ 1.0f, 0.0f, 0.0 },{ 1.0, 0.0 } }, // 14
|
|
||||||
{ { 1.0, -1.0, -1.0 },{ 1.0f, 0.0f, 0.0 },{ 1.0, 1.0 } }, // 15
|
|
||||||
// Top
|
|
||||||
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0, 1.0 } }, // 16
|
|
||||||
{ { -1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0, 0.0 } }, // 17
|
|
||||||
{ { 1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 1.0, 0.0 } }, // 18
|
|
||||||
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.0 },{ 1.0, 1.0 } }, // 19
|
|
||||||
// Bottom
|
|
||||||
{ { -1.0f, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0, 1.0 } }, // 20
|
|
||||||
{ { -1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0, 0.0 } }, // 21
|
|
||||||
{ { 1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 1.0, 0.0 } }, // 22
|
|
||||||
{ { 1.0, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 1.0, 1.0 } }, // 23
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<uint32_t> Indices = {
|
|
||||||
0, 1, 2,
|
|
||||||
2, 3, 0,
|
|
||||||
|
|
||||||
4, 5, 6,
|
|
||||||
4, 6, 7,
|
|
||||||
|
|
||||||
8, 9, 10,
|
|
||||||
8, 10, 11,
|
|
||||||
|
|
||||||
12, 13, 14,
|
|
||||||
12, 14, 15,
|
|
||||||
|
|
||||||
16, 17, 18,
|
|
||||||
16, 18, 19,
|
|
||||||
|
|
||||||
20, 21, 22,
|
|
||||||
20, 22, 23
|
|
||||||
};
|
|
||||||
|
|
||||||
vertices.clear(); indices.clear();
|
|
||||||
vertices = Vertices;
|
|
||||||
indices = Indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Mesh::setSphereData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
|
||||||
std::vector<VertexAll> Vertices;
|
|
||||||
std::vector<uint32_t> Indices;
|
|
||||||
|
|
||||||
float latitudeBands = 20.0f;
|
|
||||||
float longitudeBands = 20.0f;
|
|
||||||
float radius = 1.0f;
|
|
||||||
|
|
||||||
for (float latNumber = 0; latNumber <= latitudeBands; latNumber++) {
|
|
||||||
float theta = latNumber * 3.14 / latitudeBands;
|
|
||||||
float sinTheta = sin(theta);
|
|
||||||
float cosTheta = cos(theta);
|
|
||||||
|
|
||||||
for (float longNumber = 0; longNumber <= longitudeBands; longNumber++) {
|
|
||||||
|
|
||||||
float phi = longNumber * 2 * 3.147 / longitudeBands;
|
|
||||||
float sinPhi = sin(phi);
|
|
||||||
float cosPhi = cos(phi);
|
|
||||||
|
|
||||||
VertexAll vs;
|
|
||||||
|
|
||||||
vs.texture.x = (longNumber / longitudeBands); // u
|
|
||||||
vs.texture.y = (latNumber / latitudeBands); // v
|
|
||||||
|
|
||||||
vs.normal.x = cosPhi * sinTheta; // normal x
|
|
||||||
vs.normal.y = cosTheta; // normal y
|
|
||||||
vs.normal.z = sinPhi * sinTheta; // normal z
|
|
||||||
|
|
||||||
vs.position.x = radius * vs.normal.x; // x
|
|
||||||
vs.position.y = radius * vs.normal.y; // y
|
|
||||||
vs.position.z = radius * vs.normal.z; // z
|
|
||||||
|
|
||||||
Vertices.push_back(vs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t latNumber = 0; latNumber < latitudeBands; latNumber++) {
|
|
||||||
for (uint32_t longNumber = 0; longNumber < longitudeBands; longNumber++) {
|
|
||||||
uint32_t first = (latNumber * (longitudeBands + 1)) + longNumber;
|
|
||||||
uint32_t second = first + longitudeBands + 1;
|
|
||||||
|
|
||||||
Indices.push_back(first);
|
|
||||||
Indices.push_back(second);
|
|
||||||
Indices.push_back(first + 1);
|
|
||||||
|
|
||||||
Indices.push_back(second);
|
|
||||||
Indices.push_back(second + 1);
|
|
||||||
Indices.push_back(first + 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices.clear(); indices.clear();
|
|
||||||
vertices = Vertices;
|
|
||||||
indices = Indices;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
#include "vlkx/render/shader/Pipeline.h"
|
|
||||||
#include "vlkx/vulkan/VulkanModule.h"
|
|
||||||
#include "shadow/util/File.h"
|
|
||||||
#include <numeric>
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
struct ShaderStage {
|
|
||||||
VkShaderStageFlagBits stage;
|
|
||||||
ShaderModule::CountedShader module;
|
|
||||||
};
|
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo createViewport(const GraphicsPipelineBuilder::Viewport& port) {
|
|
||||||
return {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, 1, &port.viewport, 1, &port.scissor
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipelineColorBlendStateCreateInfo createBlend(const std::vector<VkPipelineColorBlendAttachmentState>& states) {
|
|
||||||
return {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, VK_FALSE, VK_LOGIC_OP_CLEAR, static_cast<uint32_t>(states.size()), states.data(),
|
|
||||||
{ 0, 0, 0, 0 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipelineVertexInputStateCreateInfo createVertexInput(const std::vector<VkVertexInputBindingDescription>& bindingDescs,
|
|
||||||
const std::vector<VkVertexInputAttributeDescription>& attrDescs) {
|
|
||||||
return {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0,
|
|
||||||
static_cast<uint32_t>(bindingDescs.size()), bindingDescs.data(),
|
|
||||||
static_cast<uint32_t>(attrDescs.size()), attrDescs.data()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ShaderStage> createShader(const std::map<VkShaderStageFlagBits, std::string>& shaderMap) {
|
|
||||||
std::vector<ShaderStage> stages;
|
|
||||||
stages.reserve(shaderMap.size());
|
|
||||||
for (const auto& pair : shaderMap) {
|
|
||||||
stages.push_back({ pair.first, ShaderModule::CountedShader::get(pair.second, pair.second) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return stages;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkPipelineShaderStageCreateInfo> createShaderStage(const std::vector<ShaderStage>& stages) {
|
|
||||||
static constexpr char entryPoint[] = "main";
|
|
||||||
std::vector<VkPipelineShaderStageCreateInfo> infos;
|
|
||||||
infos.reserve(stages.size());
|
|
||||||
for (const auto& stage : stages) {
|
|
||||||
infos.push_back({
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
||||||
nullptr, 0, stage.stage, **stage.module, entryPoint, nullptr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderModule::ShaderModule(const std::string &path) {
|
|
||||||
const shadowutil::FileData* file = shadowutil::loadFile(path);
|
|
||||||
const VkShaderModuleCreateInfo module {
|
|
||||||
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
||||||
nullptr, 0, file->size, reinterpret_cast<const uint32_t*>(file->data.data())
|
|
||||||
};
|
|
||||||
|
|
||||||
if (vkCreateShaderModule(VulkanModule::getInstance()->getDevice()->logical, &module, nullptr, &shader) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Unable to create shader module");
|
|
||||||
}
|
|
||||||
|
|
||||||
PipelineBuilder::PipelineBuilder(std::optional<int> maxCache) {
|
|
||||||
const VkPipelineCacheCreateInfo info {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
|
||||||
nullptr, 0, static_cast<size_t>(maxCache.value_or(0)), nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
if (vkCreatePipelineCache(VulkanModule::getInstance()->getDevice()->logical, &info, nullptr, &cache) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Unable to create pipeline cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineBuilder::setLayout(std::vector<VkDescriptorSetLayout>&& descs,
|
|
||||||
std::vector<VkPushConstantRange>&& pushConstants) {
|
|
||||||
std::vector<int> pushSizes (pushConstants.size());
|
|
||||||
for (size_t i = 0; i < pushConstants.size(); i++)
|
|
||||||
pushSizes[i] = pushConstants[i].size;
|
|
||||||
|
|
||||||
const auto totalSize = std::accumulate(pushSizes.begin(), pushSizes.end(), 0);
|
|
||||||
if (totalSize > 128)
|
|
||||||
throw std::runtime_error("Trying to set push constants of total size " + std::to_string(totalSize) + " into pipeline " + name);
|
|
||||||
|
|
||||||
descLayouts = std::move(descs);
|
|
||||||
constants = std::move(pushConstants);
|
|
||||||
layoutInfo.emplace(VkPipelineLayoutCreateInfo {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
||||||
nullptr, 0, static_cast<uint32_t>(descLayouts.size()), descLayouts.data(),
|
|
||||||
static_cast<uint32_t>(constants.size()), constants.data()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder::GraphicsPipelineBuilder(std::optional<int> maxCache) : PipelineBuilder(maxCache) {
|
|
||||||
assemblyInfo = {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_FALSE
|
|
||||||
};
|
|
||||||
|
|
||||||
rasterizationInfo = {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, VK_FALSE, VK_FALSE, VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
|
||||||
VK_FALSE, 0, 0, 0, 1
|
|
||||||
};
|
|
||||||
|
|
||||||
multisampleInfo = {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, VK_SAMPLE_COUNT_1_BIT, VK_FALSE, 0, nullptr, VK_FALSE, VK_FALSE
|
|
||||||
};
|
|
||||||
|
|
||||||
depthStencilInfo = {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, VK_FALSE, VK_FALSE,
|
|
||||||
VK_COMPARE_OP_LESS_OR_EQUAL, VK_FALSE, VK_FALSE,
|
|
||||||
{}, {}, 0, 1
|
|
||||||
};
|
|
||||||
|
|
||||||
dynamicStateInfo = {
|
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
||||||
nullptr, 0, 0, nullptr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::name(std::string &&name) {
|
|
||||||
setName(std::move(name));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::depthTest(bool enable, bool write) {
|
|
||||||
depthStencilInfo.depthTestEnable = enable;
|
|
||||||
depthStencilInfo.depthWriteEnable = write;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::stencilTest(bool enable) {
|
|
||||||
depthStencilInfo.stencilTestEnable = enable;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::multiSample(VkSampleCountFlagBits samples) {
|
|
||||||
multisampleInfo.rasterizationSamples = samples;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::topology(VkPrimitiveTopology topology) {
|
|
||||||
assemblyInfo.topology = topology;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::stencilOp(const VkStencilOpState &state,
|
|
||||||
VkStencilFaceFlags flags) {
|
|
||||||
if (flags & VK_STENCIL_FACE_FRONT_BIT)
|
|
||||||
depthStencilInfo.front = state;
|
|
||||||
if (flags & VK_STENCIL_FACE_BACK_BIT)
|
|
||||||
depthStencilInfo.back = state;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::addVertex(uint32_t bindPoint,
|
|
||||||
VkVertexInputBindingDescription &&desc,
|
|
||||||
std::vector<VkVertexInputAttributeDescription> &&attrs) {
|
|
||||||
desc.binding = bindPoint;
|
|
||||||
for (auto& attr : attrs)
|
|
||||||
attr.binding = bindPoint;
|
|
||||||
bindingDescs.push_back(desc);
|
|
||||||
|
|
||||||
attrDescs.reserve(attrDescs.size() + attrs.size());
|
|
||||||
std::move(attrs.begin(), attrs.end(), std::back_inserter(attrDescs));
|
|
||||||
attrs.clear();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::layout(std::vector<VkDescriptorSetLayout> &&descLayouts,
|
|
||||||
std::vector<VkPushConstantRange> &&constants) {
|
|
||||||
setLayout(std::move(descLayouts), std::move(constants));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::viewport(const vlkx::GraphicsPipelineBuilder::Viewport &port,
|
|
||||||
bool flipY) {
|
|
||||||
viewportMeta.emplace(port);
|
|
||||||
|
|
||||||
if (flipY) {
|
|
||||||
VkViewport& view = viewportMeta.value().viewport;
|
|
||||||
view.y += view.height;
|
|
||||||
view.height *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rasterizationInfo.frontFace = flipY ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::renderPass(const VkRenderPass &pass, uint32_t subpass) {
|
|
||||||
passMeta.emplace(PassInfo { pass, subpass });
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::colorBlend(
|
|
||||||
std::vector<VkPipelineColorBlendAttachmentState> &&states) {
|
|
||||||
blendStates = std::move(states);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::shader(VkShaderStageFlagBits stage, std::string &&file) {
|
|
||||||
shaders[stage] = std::move(file);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Pipeline> GraphicsPipelineBuilder::build() const {
|
|
||||||
if (!hasLayout())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no layout set");
|
|
||||||
if (!viewportMeta.has_value())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no viewport set");
|
|
||||||
if (!passMeta.has_value())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no render pass set");
|
|
||||||
if (blendStates.empty())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no color blend states.");
|
|
||||||
if (shaders.empty())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no shaders bound");
|
|
||||||
|
|
||||||
const auto viewportState = createViewport(viewportMeta.value());
|
|
||||||
const auto blendState = createBlend(blendStates);
|
|
||||||
const auto vertexState = createVertexInput(bindingDescs, attrDescs);
|
|
||||||
|
|
||||||
const auto shaderStages = createShader(shaders);
|
|
||||||
const auto shaderStageInfo = createShaderStage(shaderStages);
|
|
||||||
VkPipelineLayout pipelineLayout;
|
|
||||||
if (vkCreatePipelineLayout(VulkanModule::getInstance()->getDevice()->logical, &getLayout(), nullptr, &pipelineLayout) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Unable to create layout for pipeline " + getName());
|
|
||||||
|
|
||||||
const VkGraphicsPipelineCreateInfo createInfo {
|
|
||||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, nullptr, 0,
|
|
||||||
static_cast<uint32_t>(shaderStageInfo.size()), shaderStageInfo.data(),
|
|
||||||
&vertexState, &assemblyInfo, nullptr, &viewportState,
|
|
||||||
&rasterizationInfo, &multisampleInfo, &depthStencilInfo, &blendState, &dynamicStateInfo,
|
|
||||||
pipelineLayout, passMeta->pass, passMeta->subpass, VK_NULL_HANDLE, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
VkPipeline pipeline;
|
|
||||||
if (vkCreateGraphicsPipelines(VulkanModule::getInstance()->getDevice()->logical, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Failed to create pipeline " + getName());
|
|
||||||
|
|
||||||
return std::unique_ptr<Pipeline> {
|
|
||||||
new Pipeline(getName(), pipeline, pipelineLayout, VK_PIPELINE_BIND_POINT_GRAPHICS)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePipelineBuilder& ComputePipelineBuilder::name(std::string &&name) {
|
|
||||||
setName(std::move(name));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePipelineBuilder& ComputePipelineBuilder::layout(std::vector<VkDescriptorSetLayout> &&descLayouts,
|
|
||||||
std::vector<VkPushConstantRange> &&pushConstants) {
|
|
||||||
setLayout(std::move(descLayouts), std::move(pushConstants));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePipelineBuilder& ComputePipelineBuilder::shader(std::string &&path) {
|
|
||||||
shaderPath.emplace(std::move(path));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Pipeline> ComputePipelineBuilder::build() const {
|
|
||||||
if (!hasLayout())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no layout set");
|
|
||||||
if (!shaderPath.has_value())
|
|
||||||
throw std::runtime_error("Pipeline " + getName() + " has no shader set");
|
|
||||||
|
|
||||||
const auto shaderStages = createShader({{VK_SHADER_STAGE_COMPUTE_BIT, shaderPath.value()}});
|
|
||||||
const auto shaderStageInfo = createShaderStage(shaderStages);
|
|
||||||
if (shaderStageInfo.size() != 1)
|
|
||||||
throw std::runtime_error("Compute pipeline " + getName() + " must have exactly one shader bound");
|
|
||||||
|
|
||||||
VkPipelineLayout layout;
|
|
||||||
if (vkCreatePipelineLayout(VulkanModule::getInstance()->getDevice()->logical, &getLayout(), nullptr,
|
|
||||||
&layout) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Unable to create layout for compute pipeline " + getName());
|
|
||||||
|
|
||||||
const VkComputePipelineCreateInfo createInfo{
|
|
||||||
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
|
||||||
nullptr, 0,
|
|
||||||
shaderStageInfo[0], layout, VK_NULL_HANDLE, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
VkPipeline pipeline;
|
|
||||||
if (vkCreateComputePipelines(VulkanModule::getInstance()->getDevice()->logical, VK_NULL_HANDLE, 1, &createInfo,
|
|
||||||
nullptr, &pipeline) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("Unable to create compute pipeline " + getName());
|
|
||||||
|
|
||||||
return std::unique_ptr<Pipeline>{
|
|
||||||
new Pipeline{getName(), pipeline, layout, VK_PIPELINE_BIND_POINT_COMPUTE}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pipeline::bind(const VkCommandBuffer &buffer) const {
|
|
||||||
vkCmdBindPipeline(buffer, bindPoint, pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline::~Pipeline() {
|
|
||||||
vkDestroyPipeline(VulkanModule::getInstance()->getDevice()->logical, pipeline, nullptr);
|
|
||||||
vkDestroyPipelineLayout(VulkanModule::getInstance()->getDevice()->logical, layout, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,394 +0,0 @@
|
||||||
#include "vlkx/render/render_pass/GPUPass.h"
|
|
||||||
|
|
||||||
namespace vlkx {
|
|
||||||
|
|
||||||
inline bool needsSynchronization(const ImageUsage& prev, const ImageUsage& curr) {
|
|
||||||
if (curr == prev && curr.getAccess() == ImageUsage::Access::ReadOnly)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addUsageToSubpass(const ImageUsage& usage, RenderPassBuilder::SubpassDependency::SubpassMeta* pass) {
|
|
||||||
pass->stage |= usage.getStage();
|
|
||||||
pass->access |= usage.getAccessFlags();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommonPass::addUsage(std::string &&name, UsageTracker &&tracker) {
|
|
||||||
for (const auto& pair : tracker.getUsageMap())
|
|
||||||
validate(pair.first, name, false);
|
|
||||||
|
|
||||||
tracker.add(getVirtualInitial(), tracker.getInitialUsage());
|
|
||||||
if (tracker.getFinalUsage().has_value())
|
|
||||||
tracker.add(getVirtualFinal(), tracker.getFinalUsage().value());
|
|
||||||
|
|
||||||
usageHistory.emplace(std::move(name), std::move(tracker));
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageLayout CommonPass::getInitialLayout(const std::string &name) const {
|
|
||||||
return getHistory(name).getUsageMap().begin()->second.getLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageLayout CommonPass::getFinalLayout(const std::string &name) const {
|
|
||||||
return getHistory(name).getUsageMap().rbegin()->second.getLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageLayout CommonPass::getSubpassLayout(const std::string &name, int subpass) const {
|
|
||||||
validate(subpass, name, false);
|
|
||||||
return getUsage(name, subpass)->getLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommonPass::update(const std::string &name, MultiImageTracker &tracker) const {
|
|
||||||
tracker.update(name, getHistory(name).getUsageMap().rbegin()->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
const UsageTracker& CommonPass::getHistory(const std::string &name) const {
|
|
||||||
return usageHistory.at(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ImageUsage* CommonPass::getUsage(const std::string &name, int pass) const {
|
|
||||||
validate(pass, name, true);
|
|
||||||
const UsageTracker& history = getHistory(name);
|
|
||||||
const auto iter = history.getUsageMap().find(pass);
|
|
||||||
return iter != history.getUsageMap().end() ? &iter->second : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<CommonPass::Usages> CommonPass::checkForSync(const std::string &name, int pass) const {
|
|
||||||
validate(pass, name, true);
|
|
||||||
const UsageTracker& history = getHistory(name);
|
|
||||||
const auto currIter = history.getUsageMap().find(pass);
|
|
||||||
if (currIter == history.getUsageMap().end())
|
|
||||||
return std::nullopt;
|
|
||||||
const auto prevIter = std::prev(currIter);
|
|
||||||
|
|
||||||
const ImageUsage& prevUsage = prevIter->second;
|
|
||||||
const ImageUsage& currUsage = currIter->second;
|
|
||||||
|
|
||||||
if (!needsSynchronization(prevUsage, currUsage))
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const int prevSubpass = prevIter->first;
|
|
||||||
return CommonPass::Usages { prevSubpass, &prevUsage, &currUsage };
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommonPass::validate(int pass, const std::string &image, bool includeVirtual) const {
|
|
||||||
if (includeVirtual) {
|
|
||||||
if (!(pass >= getVirtualInitial() && pass <= getVirtualFinal()))
|
|
||||||
throw std::runtime_error("Subpass out of range.");
|
|
||||||
} else {
|
|
||||||
if (!(pass >= 0 && pass < numPasses))
|
|
||||||
throw std::runtime_error("nv Subpass out of range.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GraphicsPass::add(const std::string &name, UsageTracker &&history, std::function<int(int)> &&getter,
|
|
||||||
const std::optional<RenderPassBuilder::Attachment::OpsType> ops) {
|
|
||||||
verifyHistory(name, history);
|
|
||||||
|
|
||||||
const std::optional<int> needsGetter = getFirstRenderTarget(history);
|
|
||||||
if (needsGetter.has_value()) {
|
|
||||||
if (getter == nullptr)
|
|
||||||
throw std::runtime_error("Image " + name + " is used as a render target without a location getter.");
|
|
||||||
} else {
|
|
||||||
getter = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int attachmentLocation = static_cast<int>(metas.size());
|
|
||||||
metas.insert(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
AttachmentMeta {
|
|
||||||
attachmentLocation,
|
|
||||||
std::move(getter),
|
|
||||||
getOps(name, history, ops),
|
|
||||||
{}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
addUsage(std::string(name), std::move(history));
|
|
||||||
return attachmentLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsPass& GraphicsPass::addMultisample(const std::string &source, const std::string &dest, int pass) {
|
|
||||||
validate(pass, source, false);
|
|
||||||
|
|
||||||
const auto source_iter = usageHistory.find(source);
|
|
||||||
if (source_iter == usageHistory.end())
|
|
||||||
throw std::runtime_error("Usage history not found for source image " + source);
|
|
||||||
|
|
||||||
const UsageTracker& source_history = source_iter->second;
|
|
||||||
if (!verifyImageUsage(source_history, pass, ImageUsage::Type::RenderTarget))
|
|
||||||
throw std::runtime_error("Usage type for source image " + source + " must be render target.");
|
|
||||||
|
|
||||||
const auto dest_iter = usageHistory.find(dest);
|
|
||||||
if (dest_iter == usageHistory.end())
|
|
||||||
throw std::runtime_error("Usage history not found for destination image " + dest);
|
|
||||||
|
|
||||||
const UsageTracker& dest_history = dest_iter->second;
|
|
||||||
if (!verifyImageUsage(dest_history, pass, ImageUsage::Type::Multisample))
|
|
||||||
throw std::runtime_error("Usage type for destination image " + dest + " must be multisample");
|
|
||||||
|
|
||||||
auto& targetMap = metas[source].multisample;
|
|
||||||
const bool inserted = targetMap.insert( { pass, dest }).second;
|
|
||||||
|
|
||||||
if (!inserted)
|
|
||||||
throw std::runtime_error("Image " + source + " is already bound to a multisample.");
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsPass::setAttachments() {
|
|
||||||
for (const auto& pair : usageHistory) {
|
|
||||||
const std::string& name = pair.first;
|
|
||||||
const AttachmentMeta& meta = metas[name];
|
|
||||||
builder->setAttachment(meta.index, { meta.ops, getInitialLayout(name), getFinalLayout(name) } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsPass::setSubpasses() {
|
|
||||||
for (int pass = 0; pass < numPasses; ++pass) {
|
|
||||||
std::vector<RenderPassBuilder::ColorAttachmentMeta> colors;
|
|
||||||
std::vector<RenderPassBuilder::ColorAttachmentMeta> multisamples;
|
|
||||||
VkAttachmentReference dsRef { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED };
|
|
||||||
|
|
||||||
// Verify all images used, the long way around.
|
|
||||||
for (const auto& pair : usageHistory) {
|
|
||||||
const std::string& name = pair.first;
|
|
||||||
const ImageUsage* history = getUsage(name, pass);
|
|
||||||
if (history == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const AttachmentMeta& meta = metas[name];
|
|
||||||
const VkAttachmentReference ref { static_cast<uint32_t>(meta.index), history->getLayout() };
|
|
||||||
|
|
||||||
switch (history->getType()) {
|
|
||||||
// If we're looking at a render target, we need to figure out where it gets its' details from.
|
|
||||||
case ImageUsage::Type::RenderTarget: {
|
|
||||||
const int location = meta.getter(pass);
|
|
||||||
const auto iter = meta.multisample.find(pass);
|
|
||||||
if (iter != meta.multisample.end()) {
|
|
||||||
const std::string& target = iter->second;
|
|
||||||
const ImageUsage* targetUsage = getUsage(target, pass);
|
|
||||||
if (targetUsage == nullptr)
|
|
||||||
throw std::runtime_error("Expected target image to have a usage");
|
|
||||||
multisamples.push_back( { location, metas[target].index, targetUsage->getLayout() });
|
|
||||||
}
|
|
||||||
|
|
||||||
colors.push_back( { location, static_cast<int>(ref.attachment), ref.layout });
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ImageUsage::Type::DepthStencil:
|
|
||||||
if (dsRef.attachment != VK_ATTACHMENT_UNUSED)
|
|
||||||
throw std::runtime_error("Depth stencil used multiple times");
|
|
||||||
dsRef = ref;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ImageUsage::Type::Multisample:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Unreachable?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto colorRefs = RenderPassBuilder::parseColorReferences(colors);
|
|
||||||
auto multisampleRefs = RenderPassBuilder::parseMutisampling(colorRefs.size(), multisamples);
|
|
||||||
builder->setSubpass(pass, std::move(colorRefs), std::move(multisampleRefs), dsRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsPass::setDependencies() {
|
|
||||||
if (getVirtualFinal() != numPasses)
|
|
||||||
throw std::runtime_error("Virtual subpass mismatch");
|
|
||||||
for (int pass = 0; pass <= numPasses; ++pass) {
|
|
||||||
std::map<int, RenderPassBuilder::SubpassDependency> deps;
|
|
||||||
|
|
||||||
for (const auto& pair : usageHistory) {
|
|
||||||
const auto usage = checkForSync(pair.first, pass);
|
|
||||||
if (!usage.has_value()) continue;
|
|
||||||
|
|
||||||
const ImageUsage& prev = usage.value().lastUsage;
|
|
||||||
const ImageUsage& curr = usage.value().currentUsage;
|
|
||||||
const int sourcePass = usage.value().lastSubpass;
|
|
||||||
|
|
||||||
auto iter = deps.find(sourcePass);
|
|
||||||
if (iter == deps.end()) {
|
|
||||||
const RenderPassBuilder::SubpassDependency defaultDep {
|
|
||||||
{ checkSubpass(sourcePass), VK_PIPELINE_STAGE_NONE_KHR, VK_ACCESS_NONE_KHR },
|
|
||||||
{ checkSubpass(pass), VK_PIPELINE_STAGE_NONE_KHR, VK_ACCESS_NONE_KHR },
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
iter = deps.insert( { sourcePass, defaultDep } ).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
addUsageToSubpass(prev, &iter->second.source);
|
|
||||||
addUsageToSubpass(curr, &iter->second.destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& pair : deps) {
|
|
||||||
const RenderPassBuilder::SubpassDependency& dep = pair.second;
|
|
||||||
builder->addDependency(dep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<RenderPassBuilder> GraphicsPass::build(int framebuffers) {
|
|
||||||
builder = std::make_unique<RenderPassBuilder>();
|
|
||||||
builder->setFramebufferCount(framebuffers);
|
|
||||||
|
|
||||||
setAttachments();
|
|
||||||
setSubpasses();
|
|
||||||
setDependencies();
|
|
||||||
return std::move(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<int> GraphicsPass::getFirstRenderTarget(const UsageTracker &history) const {
|
|
||||||
for (const auto& pair : history.getUsageMap()) {
|
|
||||||
const int pass = pair.first;
|
|
||||||
if (isVirtual(pass)) continue;
|
|
||||||
|
|
||||||
if (pair.second.getType() == ImageUsage::Type::RenderTarget) return pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderPassBuilder::Attachment::OpsType GraphicsPass::getOps(const std::string &name, const UsageTracker &history,
|
|
||||||
const std::optional<RenderPassBuilder::Attachment::OpsType> &userOps) const {
|
|
||||||
const ImageUsage::Type type = getUsageType(name, history);
|
|
||||||
|
|
||||||
if (!userOps.has_value()) {
|
|
||||||
switch (type) {
|
|
||||||
case ImageUsage::Type::RenderTarget: return getDefaultOps();
|
|
||||||
case ImageUsage::Type::DepthStencil: return getStencilOps();
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Unreachable?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const RenderPassBuilder::Attachment::OpsType& ops = userOps.value();
|
|
||||||
switch (type) {
|
|
||||||
case ImageUsage::Type::RenderTarget:
|
|
||||||
case ImageUsage::Type::DepthStencil:
|
|
||||||
return ops;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Unreachable?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageUsage::Type GraphicsPass::getUsageType(const std::string &name, const UsageTracker &history) const {
|
|
||||||
ImageUsage::Type prev = ImageUsage::Type::DontCare;
|
|
||||||
|
|
||||||
for (const auto& pair : history.getUsageMap()) {
|
|
||||||
if (isVirtual(pair.first)) continue;
|
|
||||||
|
|
||||||
ImageUsage::Type type = pair.second.getType();
|
|
||||||
if (type == ImageUsage::Type::Multisample)
|
|
||||||
type = ImageUsage::Type::RenderTarget;
|
|
||||||
|
|
||||||
if (prev == ImageUsage::Type::DontCare) {
|
|
||||||
prev = type;
|
|
||||||
} else if (type != prev) {
|
|
||||||
throw std::runtime_error("Inconsistent usage type specified for " + name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev == ImageUsage::Type::DontCare)
|
|
||||||
throw std::runtime_error("Image " + name + " has no usages.");
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphicsPass::verifyImageUsage(const UsageTracker &history, int subpass, ImageUsage::Type type) const {
|
|
||||||
const auto iter = history.getUsageMap().find(subpass);
|
|
||||||
return iter != history.getUsageMap().end() && iter->second.getType() == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsPass::verifyHistory(const std::string &image, const UsageTracker &history) const {
|
|
||||||
for (const auto& pair : history.getUsageMap()) {
|
|
||||||
const ImageUsage::Type type = pair.second.getType();
|
|
||||||
if (type != ImageUsage::Type::RenderTarget && type != ImageUsage::Type::DepthStencil && type != ImageUsage::Type::Multisample)
|
|
||||||
throw std::runtime_error("Invalid usage of " + image + " at subpass " + std::to_string(pair.first));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePass &ComputePass::add(std::string &&name, UsageTracker &&history) {
|
|
||||||
verify(name, history);
|
|
||||||
addUsage(std::move(name), std::move(history));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComputePass::execute(const VkCommandBuffer &commands, uint32_t queueFamily,
|
|
||||||
const std::map<std::string, const VkImage*> &images,
|
|
||||||
const std::vector<std::function<void()>>& computeOps) const {
|
|
||||||
|
|
||||||
if (computeOps.size() != numPasses)
|
|
||||||
throw std::runtime_error("Compute shader mismatches ops and passes.");
|
|
||||||
|
|
||||||
if (getVirtualFinal() != numPasses)
|
|
||||||
throw std::runtime_error("Compute shader attempting to run too many subpasses");
|
|
||||||
|
|
||||||
for (int pass = 0; pass < numPasses; ++pass) {
|
|
||||||
for (const auto& pair : usageHistory) {
|
|
||||||
const std::string& image = pair.first;
|
|
||||||
const auto usage = checkForSync(image, pass);
|
|
||||||
if (!usage.has_value()) continue;
|
|
||||||
|
|
||||||
const auto iter = images.find(image);
|
|
||||||
if (iter == images.end())
|
|
||||||
throw std::runtime_error("Image " + image + " not provided");
|
|
||||||
|
|
||||||
barrier(commands, queueFamily, *iter->second, usage.value().lastUsage, usage.value().currentUsage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pass < numPasses)
|
|
||||||
computeOps[pass]();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComputePass::barrier(const VkCommandBuffer &commands, uint32_t queueFamily, const VkImage &image,
|
|
||||||
const ImageUsage &prev, const ImageUsage ¤t) const {
|
|
||||||
const VkImageMemoryBarrier barrier {
|
|
||||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
nullptr,
|
|
||||||
prev.getAccessFlags(),
|
|
||||||
current.getAccessFlags(),
|
|
||||||
prev.getLayout(),
|
|
||||||
current.getLayout(),
|
|
||||||
queueFamily,
|
|
||||||
queueFamily,
|
|
||||||
image,
|
|
||||||
{
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vkCmdPipelineBarrier(
|
|
||||||
commands,
|
|
||||||
prev.getStage(),
|
|
||||||
current.getStage(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
1,
|
|
||||||
&barrier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComputePass::verify(const std::string &name, const UsageTracker &history) const {
|
|
||||||
for (const auto& pair : history.getUsageMap()) {
|
|
||||||
const ImageUsage::Type type = pair.second.getType();
|
|
||||||
if (type != ImageUsage::Type::LinearAccess && type != ImageUsage::Type::Sampled && type != ImageUsage::Type::Transfer)
|
|
||||||
throw std::runtime_error("Compute shader using an attachment that is not guranteed to be readable.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user