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>
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -4,10 +4,7 @@ obj/
|
|||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
|
||||
*.user
|
||||
|
||||
test-results
|
||||
cmake-build-vs-debug/
|
||||
cmake-build-debug/
|
||||
cmake-build-debug-msvc/
|
||||
cmake-build-debug-msvc-vs/
|
||||
/.idea/
|
||||
bazel-*/
|
||||
test-results
|
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
|
||||
[shadow-engine] <<static lib>> as engine
|
||||
[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
|
||||
|
||||
[test-game] <<dll>> as game
|
||||
|
||||
shengine <- editor
|
||||
runner -> shengine
|
||||
engine <-editor
|
||||
runner -> engine
|
||||
|
||||
game ..> shengine : uses
|
||||
game ..> engine
|
||||
|
||||
runner --> 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}
|
||||
|
||||
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
|
||||
\chapter*{Asd}
|
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
|
||||
#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 {
|
||||
|
||||
/// <summary>
|
||||
/// Represents the application
|
||||
/// </summary>
|
||||
class ShadowApplication
|
||||
class ShadowApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the singleton instance
|
||||
|
@ -28,7 +21,7 @@ namespace ShadowEngine {
|
|||
/// <summary>
|
||||
/// The module manager instance
|
||||
/// </summary>
|
||||
ModuleManager moduleManager;
|
||||
//ShadowEngine::ShadowModuleManager moduleManager;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the running state.
|
||||
|
@ -37,24 +30,20 @@ namespace ShadowEngine {
|
|||
bool running = true;
|
||||
|
||||
bool no_gui = false;
|
||||
|
||||
std::string game = "";
|
||||
|
||||
void loadGame();
|
||||
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
ShadowApplication(int argc, char* argv[]);
|
||||
virtual ~ShadowApplication();
|
||||
virtual ~ShadowApplication();
|
||||
|
||||
/// <summary>
|
||||
/// Static getter for the singleton instance
|
||||
/// </summary>
|
||||
/// Use this for accessing the Application
|
||||
/// <returns>The current application reference</returns>
|
||||
static ShadowApplication& Get();
|
||||
static ShadowApplication& Get() { return *instance; };
|
||||
|
||||
/// <summary>
|
||||
/// Returns the active window used for rendering
|
||||
|
@ -63,11 +52,9 @@ namespace ShadowEngine {
|
|||
//ShadowWindow& const GetWindow() const { return window_; };
|
||||
//void SetWindow(ShadowWindow w) { window_ = w; }
|
||||
|
||||
ShadowEngine::ModuleManager& GetModuleManager() { return moduleManager; };
|
||||
//ShadowEngine::ShadowModuleManager& GetModuleManager() { return moduleManager; };
|
||||
|
||||
void Init();
|
||||
void Start();
|
||||
|
||||
void PollEvents();
|
||||
void Init();
|
||||
void Start();
|
||||
};
|
||||
}
|
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 "string-helpers.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "string-helpers.h"
|
||||
|
||||
namespace Shadow::SFF {
|
||||
|
||||
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]") {
|
||||
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 <iomanip>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <SDL.h>
|
||||
#include "core/ShadowApplication.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -13,7 +15,8 @@ int main(int argc, char *argv[])
|
|||
for(int ndx{}; ndx != argc; ++ndx) {
|
||||
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);
|
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)
|
||||
{
|
|
@ -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