Vulkan renderer
This commit is contained in:
parent
a127e95741
commit
55258615c5
12
.idea/editor.xml
generated
12
.idea/editor.xml
generated
@ -245,17 +245,29 @@
|
|||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppCodeStyle/UseAutoCommonCase/@EntryValue" value="Never" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue" value="true" type="bool" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue" value="true" type="bool" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="NEXT_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="NEXT_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/EMPTY_BLOCK_STYLE/@EntryValue" value="TOGETHER_SAME_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/EMPTY_BLOCK_STYLE/@EntryValue" value="TOGETHER_SAME_LINE" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/REQUIRES_EXPRESSION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/REQUIRES_EXPRESSION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
|
||||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="true" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="true" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="CHOP_IF_LONG" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="true" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="true" type="bool" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_CTOR_INITIALIZER_STYLE/@EntryValue" value="CHOP_IF_LONG" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="CHOP_IF_LONG" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/CppPreferForwardDeclaration/PreferForwardDeclarations/@EntryValue" value="false" type="bool" />
|
||||||
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=0AFB7787612DF743B09AD9412E48D4CC/@EntryIndexedValue" value="<NamingElement Priority="7" Title="Local variables"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement>" type="string" />
|
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=0AFB7787612DF743B09AD9412E48D4CC/@EntryIndexedValue" value="<NamingElement Priority="7" Title="Local variables"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement>" type="string" />
|
||||||
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=0B82708A1BA7774EB13D27F245698A56/@EntryIndexedValue" value="<NamingElement Priority="1" Title="Classes and structs"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement>" type="string" />
|
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=0B82708A1BA7774EB13D27F245698A56/@EntryIndexedValue" value="<NamingElement Priority="1" Title="Classes and structs"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement>" type="string" />
|
||||||
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=1350D079A82E0740947E85445B5AF47C/@EntryIndexedValue" value="<NamingElement Priority="6" Title="Parameters"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement>" type="string" />
|
<option name="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=1350D079A82E0740947E85445B5AF47C/@EntryIndexedValue" value="<NamingElement Priority="6" Title="Parameters"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement>" type="string" />
|
||||||
|
|||||||
@ -19,9 +19,14 @@ project (EtrediaEmpires LANGUAGES CXX)
|
|||||||
|
|
||||||
add_subdirectory(sdk/map)
|
add_subdirectory(sdk/map)
|
||||||
add_subdirectory(sdk/assets)
|
add_subdirectory(sdk/assets)
|
||||||
|
|
||||||
add_subdirectory(p7lib)
|
add_subdirectory(p7lib)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
|
|
||||||
add_subdirectory(server/lib)
|
add_subdirectory(server/lib)
|
||||||
add_subdirectory(engine-api)
|
add_subdirectory(engine-api)
|
||||||
|
|
||||||
add_subdirectory(renderers/magnum-renderer)
|
add_subdirectory(renderers/magnum-renderer)
|
||||||
|
add_subdirectory(renderers/vulkan)
|
||||||
|
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
Binary file not shown.
Binary file not shown.
@ -1,4 +1,12 @@
|
|||||||
function(add_library_target TARGET_NAME)
|
function(add_library_target TARGET_NAME)
|
||||||
|
cmake_parse_arguments(
|
||||||
|
ADDITIONAL
|
||||||
|
""
|
||||||
|
""
|
||||||
|
"MODULES;MODULE_BASEDIRS"
|
||||||
|
""
|
||||||
|
${ARGN}
|
||||||
|
)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@ -22,23 +30,14 @@ function(add_library_target TARGET_NAME)
|
|||||||
modules/*.ixx
|
modules/*.ixx
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${TARGET_NAME} PUBLIC FILE_SET modules TYPE CXX_MODULES BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${MODULES})
|
list(APPEND MODULES ${ADDITIONAL_MODULES})
|
||||||
|
set (MODULE_BASEDIRS ${PROJECT_SOURCE_DIR})
|
||||||
|
list(APPEND MODULE_BASEDIRS ${ADDITIONAL_MODULE_BASEDIRS})
|
||||||
|
|
||||||
|
target_sources(${TARGET_NAME} PUBLIC FILE_SET modules TYPE CXX_MODULES BASE_DIRS ${MODULE_BASEDIRS} FILES ${MODULES})
|
||||||
target_sources(${TARGET_NAME} PUBLIC FILE_SET headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${HEADERS})
|
target_sources(${TARGET_NAME} PUBLIC FILE_SET headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${HEADERS})
|
||||||
target_sources(${TARGET_NAME} PRIVATE ${SOURCES})
|
target_sources(${TARGET_NAME} PRIVATE ${SOURCES})
|
||||||
|
|
||||||
# MSVC STD module linking
|
|
||||||
# target_compile_options(${TARGET_NAME} PRIVATE
|
|
||||||
# /EHsc
|
|
||||||
# "SHELL:/reference \"std=$ENV{STD_MODULE_DIR}/std.ifc\""
|
|
||||||
# "SHELL:/reference \"std.compat=$ENV{STD_MODULE_DIR}/std.compat.ifc\""
|
|
||||||
#)
|
|
||||||
#target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.obj")
|
|
||||||
# target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.compat.obj")
|
|
||||||
#target_compile_options(${TARGET_NAME} PRIVATE
|
|
||||||
# /wd5050
|
|
||||||
#)
|
|
||||||
#target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23 INTERFACE cxx_std_23)
|
|
||||||
|
|
||||||
# Clang modules
|
# Clang modules
|
||||||
# target_compile_options(${TARGET_NAME} PRIVATE
|
# target_compile_options(${TARGET_NAME} PRIVATE
|
||||||
# -fmodules
|
# -fmodules
|
||||||
|
|||||||
@ -62,8 +62,8 @@ export namespace ee_engine {
|
|||||||
void SetCamera(const CameraTransform& camera);
|
void SetCamera(const CameraTransform& camera);
|
||||||
|
|
||||||
constexpr CameraTransform getCamera() const noexcept { return camera; };
|
constexpr CameraTransform getCamera() const noexcept { return camera; };
|
||||||
glm::vec2 ScreenToWorld(const float screenx, const float screenY);
|
glm::vec2 ScreenToWorld(float screenX, float screenY) const;
|
||||||
glm::vec2 ScreenToWorld(const glm::vec2& screenPos);
|
glm::vec2 ScreenToWorld(const glm::vec2& screenPos) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _isReady;
|
bool _isReady;
|
||||||
@ -77,12 +77,12 @@ export namespace ee_engine {
|
|||||||
MapMesh *mapMesh;
|
MapMesh *mapMesh;
|
||||||
ShaderRefs shaders;
|
ShaderRefs shaders;
|
||||||
|
|
||||||
struct RegionIndecies {
|
struct RegionRanges {
|
||||||
std::size_t startIndex;
|
std::size_t offset;
|
||||||
std::size_t provincesCount;
|
std::size_t provincesCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<std::uint32_t /*id*/, RegionIndecies> regionsMapping;
|
std::unordered_map<std::uint32_t /*id*/, RegionRanges> regionsMapping;
|
||||||
|
|
||||||
explicit IRenderer(GLFWwindow *window);
|
explicit IRenderer(GLFWwindow *window);
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ export namespace ee_engine {
|
|||||||
|
|
||||||
virtual void ApplyCamera() = 0;
|
virtual void ApplyCamera() = 0;
|
||||||
|
|
||||||
virtual void UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO>& data) = 0;
|
virtual void UpdateProvinces(std::size_t offset, const std::vector<ProvinceSSBO>& data) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateRegionCmd(RendererCommand& command);
|
void UpdateRegionCmd(RendererCommand& command);
|
||||||
|
|||||||
@ -31,7 +31,7 @@ void ee_engine::IRenderer::Draw() {
|
|||||||
DrawUI();
|
DrawUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ee_engine::IRenderer::ResizeWindow(int width, int height) {
|
void ee_engine::IRenderer::ResizeWindow(const int width, const int height) {
|
||||||
screenWidth = width;
|
screenWidth = width;
|
||||||
screenHeight = height;
|
screenHeight = height;
|
||||||
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
||||||
@ -45,15 +45,15 @@ void ee_engine::IRenderer::SetCamera(const CameraTransform& camera) {
|
|||||||
|
|
||||||
this->camera = camera;
|
this->camera = camera;
|
||||||
|
|
||||||
float halfHeight = camera.zoom;
|
const float halfHeight = camera.zoom;
|
||||||
float halfWidth = camera.zoom * screenAspect;
|
const float halfWidth = camera.zoom * screenAspect;
|
||||||
|
|
||||||
glm::mat4 P = glm::ortho(
|
const glm::mat4 P = glm::ortho(
|
||||||
-halfWidth, +halfWidth,
|
-halfWidth, +halfWidth,
|
||||||
-halfHeight, +halfHeight
|
-halfHeight, +halfHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
glm::mat4 V = glm::translate(
|
const glm::mat4 V = glm::translate(
|
||||||
glm::mat4(1.0f),
|
glm::mat4(1.0f),
|
||||||
glm::vec3(-camera.position, 0.0f)
|
glm::vec3(-camera.position, 0.0f)
|
||||||
);
|
);
|
||||||
@ -64,11 +64,11 @@ void ee_engine::IRenderer::SetCamera(const CameraTransform& camera) {
|
|||||||
ApplyCamera();
|
ApplyCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 ee_engine::IRenderer::ScreenToWorld(const float screenX, const float screenY) {
|
glm::vec2 ee_engine::IRenderer::ScreenToWorld(const float screenX, const float screenY) const {
|
||||||
return ScreenToWorld(glm::vec2(screenX, screenY));
|
return ScreenToWorld(glm::vec2(screenX, screenY));
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 ee_engine::IRenderer::ScreenToWorld(const glm::vec2& screenPos) {
|
glm::vec2 ee_engine::IRenderer::ScreenToWorld(const glm::vec2& screenPos) const {
|
||||||
float x = 2.0f * screenPos.x / screenWidth - 1.0f;
|
float x = 2.0f * screenPos.x / screenWidth - 1.0f;
|
||||||
float y = -2.0f * screenPos.y / screenHeight + 1.0f;
|
float y = -2.0f * screenPos.y / screenHeight + 1.0f;
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ glm::vec2 ee_engine::IRenderer::ScreenToWorld(const glm::vec2& screenPos) {
|
|||||||
return glm::vec2(world) / world.w;
|
return glm::vec2(world) / world.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ee_engine::IRenderer::UpdateRegionCmd(ee_engine::RendererCommand& command) {
|
void ee_engine::IRenderer::UpdateRegionCmd(RendererCommand& command) {
|
||||||
if (!regionsMapping.contains(command.region.id)) {
|
if (!regionsMapping.contains(command.region.id)) {
|
||||||
LOG(WARNING, "Updating unregistered region with id: %d", command.region.id);
|
LOG(WARNING, "Updating unregistered region with id: %d", command.region.id);
|
||||||
return;
|
return;
|
||||||
@ -90,14 +90,14 @@ void ee_engine::IRenderer::UpdateRegionCmd(ee_engine::RendererCommand& command)
|
|||||||
command.region.id, command.region.data.regionId);
|
command.region.id, command.region.data.regionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegionIndecies mapping = regionsMapping[command.region.id];
|
RegionRanges mapping = regionsMapping[command.region.id];
|
||||||
std::vector<ProvinceSSBO> buffer;
|
std::vector<ProvinceSSBO> buffer;
|
||||||
buffer.reserve(mapping.provincesCount);
|
buffer.reserve(mapping.provincesCount);
|
||||||
for (std::size_t i = 0; i < mapping.provincesCount; i++) {
|
for (std::size_t i = 0; i < mapping.provincesCount; i++) {
|
||||||
buffer.push_back(command.region.data);
|
buffer.push_back(command.region.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateProvinces(mapping.startIndex, buffer);
|
UpdateProvinces(mapping.offset, buffer);
|
||||||
|
|
||||||
command.type = ee_engine::RendererCommand::COMPLETED;
|
command.type = RendererCommand::COMPLETED;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ add_executable_target(${EXE})
|
|||||||
target_link_libraries(${EXE} PUBLIC utils)
|
target_link_libraries(${EXE} PUBLIC utils)
|
||||||
target_link_libraries(${EXE} PUBLIC engine-api)
|
target_link_libraries(${EXE} PUBLIC engine-api)
|
||||||
target_link_libraries(${EXE} PUBLIC magnum-renderer)
|
target_link_libraries(${EXE} PUBLIC magnum-renderer)
|
||||||
|
target_link_libraries(${EXE} PUBLIC vulkan-renderer)
|
||||||
|
|
||||||
target_link_libraries(${EXE} PRIVATE Boost::lockfree)
|
target_link_libraries(${EXE} PRIVATE Boost::lockfree)
|
||||||
target_link_libraries(${EXE} PRIVATE xxHash::xxhash)
|
target_link_libraries(${EXE} PRIVATE xxHash::xxhash)
|
||||||
|
|||||||
@ -10,17 +10,17 @@ ee_engine::InputHandlers Actions::mappings{
|
|||||||
.mapScroll = MapScroll
|
.mapScroll = MapScroll
|
||||||
};
|
};
|
||||||
|
|
||||||
void Actions::Zoom(float dx, float dy) {
|
void Actions::Zoom(const float dx, const float dy) {
|
||||||
ee_engine::CameraTransform cam = Engine::renderer->getCamera();
|
ee_engine::CameraTransform cam = Engine::renderer->getCamera();
|
||||||
cam.zoom *= pow(1.1f, -dy);
|
cam.zoom *= pow(1.1f, -dy);
|
||||||
Engine::renderer->SetCamera(cam);
|
Engine::renderer->SetCamera(cam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actions::MapScroll(float x, float y, float lastX, float lastY) {
|
void Actions::MapScroll(const float x, const float y, const float lastX, const float lastY) {
|
||||||
ee_engine::CameraTransform cam = Engine::renderer->getCamera();
|
ee_engine::CameraTransform cam = Engine::renderer->getCamera();
|
||||||
|
|
||||||
glm::vec2 pos = Engine::renderer->ScreenToWorld(x, y);
|
const glm::vec2 pos = Engine::renderer->ScreenToWorld(x, y);
|
||||||
glm::vec2 lastPos = Engine::renderer->ScreenToWorld(lastX, lastY);
|
const glm::vec2 lastPos = Engine::renderer->ScreenToWorld(lastX, lastY);
|
||||||
cam.position -= pos - lastPos;
|
cam.position -= pos - lastPos;
|
||||||
|
|
||||||
Engine::renderer->SetCamera(cam);
|
Engine::renderer->SetCamera(cam);
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import utils;
|
|||||||
|
|
||||||
import Engine;
|
import Engine;
|
||||||
import Actions;
|
import Actions;
|
||||||
import MagnumRender;
|
import VulkanRender;
|
||||||
import GLFWInput;
|
import GLFWInput;
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ int main(int argc, char **argv) {
|
|||||||
glfwInit();
|
glfwInit();
|
||||||
|
|
||||||
// Vulkan only
|
// Vulkan only
|
||||||
// glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
|
||||||
GLFWwindow *window = glfwCreateWindow(800, 600, "Etredia Empires", nullptr, nullptr);
|
GLFWwindow *window = glfwCreateWindow(800, 600, "Etredia Empires", nullptr, nullptr);
|
||||||
|
|
||||||
@ -38,16 +38,17 @@ int main(int argc, char **argv) {
|
|||||||
Engine::assetManager->LoadDir(ee_assets::MATERIAL, "assets/materials");
|
Engine::assetManager->LoadDir(ee_assets::MATERIAL, "assets/materials");
|
||||||
|
|
||||||
// Load assets
|
// Load assets
|
||||||
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/ET.map");
|
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/regions.map");
|
||||||
Engine::assetManager->LoadAsync(ee_assets::SAVE, "assets/scenarios/empires.save");
|
Engine::assetManager->LoadAsync(ee_assets::SAVE, "assets/scenarios/empires.save");
|
||||||
|
|
||||||
// Setup input manager TODO: builder
|
// Setup input manager TODO: builder
|
||||||
Engine::input = Allocate<GLFWInput>(Actions::mappings);
|
Engine::input = Allocate<GLFWInput>(Actions::mappings);
|
||||||
|
|
||||||
// Setup renderer TODO: builder
|
// Setup renderer TODO: builder
|
||||||
Engine::renderer = reinterpret_cast<ee_engine::IRenderer*>(Allocate<ee_magnum::MagnumRenderer>(window));
|
//Engine::renderer = reinterpret_cast<ee_engine::IRenderer*>(Allocate<ee_magnum::MagnumRenderer>(window));
|
||||||
|
Engine::renderer = reinterpret_cast<ee_engine::IRenderer*>(Allocate<ee_vulkan::VulkanRenderer>(window));
|
||||||
|
|
||||||
ee_assets::AssetMapMesh& regions = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/ET.map");
|
ee_assets::AssetMapMesh& regions = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/regions.map");
|
||||||
Engine::renderer->RegisterMapMesh(®ions.content);
|
Engine::renderer->RegisterMapMesh(®ions.content);
|
||||||
|
|
||||||
//TODO: ShaderRefs builder from set of AssetMaterial
|
//TODO: ShaderRefs builder from set of AssetMaterial
|
||||||
@ -64,19 +65,20 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
Engine::renderer->Init();
|
Engine::renderer->Init();
|
||||||
Engine::renderer->SetCamera({
|
Engine::renderer->SetCamera({
|
||||||
//.position = {1000.0f, 1000.0f},
|
.position = {1000.0f, 1000.0f},
|
||||||
.zoom = 2000.0f
|
.zoom = 2000.0f
|
||||||
|
//.zoom = 1.0f
|
||||||
});
|
});
|
||||||
|
|
||||||
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *window, int width, int height) {
|
glfwSetFramebufferSizeCallback(window, [](GLFWwindow*, const int width, const int height) {
|
||||||
Engine::renderer->ResizeWindow(width, height);
|
Engine::renderer->ResizeWindow(width, height);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup Client TODO: builder
|
// Setup Client TODO: builder
|
||||||
Engine::client = Allocate<Client>();
|
Engine::client = Allocate<Client>();
|
||||||
// AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save");
|
const AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save");
|
||||||
// Engine::client->LoadSave(save);
|
Engine::client->LoadSave(save);
|
||||||
// Engine::client->Init();
|
Engine::client->Init();
|
||||||
Engine::client->Start();
|
Engine::client->Start();
|
||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
@ -99,5 +101,4 @@ int main(int argc, char **argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use CRTP where possible in classes with virtual/override
|
|
||||||
// TODO: Leaks detection
|
// TODO: Leaks detection
|
||||||
|
|||||||
@ -18,8 +18,8 @@ using namespace Magnum;
|
|||||||
export namespace ee_magnum {
|
export namespace ee_magnum {
|
||||||
class MagnumRenderer final : public ee_engine::IRenderer {
|
class MagnumRenderer final : public ee_engine::IRenderer {
|
||||||
public:
|
public:
|
||||||
MagnumRenderer(GLFWwindow *window);
|
explicit MagnumRenderer(GLFWwindow *window);
|
||||||
~MagnumRenderer();
|
~MagnumRenderer() override;
|
||||||
|
|
||||||
void Init() override;
|
void Init() override;
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
@ -31,8 +31,7 @@ export namespace ee_magnum {
|
|||||||
|
|
||||||
void ApplyCamera() override;
|
void ApplyCamera() override;
|
||||||
|
|
||||||
virtual void
|
void UpdateProvinces(std::size_t offset, const std::vector<ee_engine::ProvinceSSBO>& data) override;
|
||||||
UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO>& data) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Platform::GLContext context;
|
Platform::GLContext context;
|
||||||
@ -44,7 +43,5 @@ export namespace ee_magnum {
|
|||||||
void ValidateSupport();
|
void ValidateSupport();
|
||||||
void LoadShaders();
|
void LoadShaders();
|
||||||
void LoadProvinces();
|
void LoadProvinces();
|
||||||
|
|
||||||
void ApplyMapMVP();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ void ee_magnum::MagnumRenderer::LoadProvinces() {
|
|||||||
provincesData.push_back(data);
|
provincesData.push_back(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
regionsMapping[region] = {.startIndex = regionIndex, .provincesCount = provs.size()};
|
regionsMapping[region] = {.offset = regionIndex, .provincesCount = provs.size()};
|
||||||
regionIndex += provs.size() * sizeof(ee_engine::ProvinceSSBO);
|
regionIndex += provs.size() * sizeof(ee_engine::ProvinceSSBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,10 +107,10 @@ void ee_magnum::MagnumRenderer::LoadProvinces() {
|
|||||||
provinceSSBO.bind(GL::Buffer::Target::ShaderStorage, ProvinceMaterial::PROVINCES_DATA_BINDING);
|
provinceSSBO.bind(GL::Buffer::Target::ShaderStorage, ProvinceMaterial::PROVINCES_DATA_BINDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ee_magnum::MagnumRenderer::UpdateProvinces(const std::size_t index,
|
void ee_magnum::MagnumRenderer::UpdateProvinces(const std::size_t offset,
|
||||||
const std::vector<ee_engine::ProvinceSSBO>& data) {
|
const std::vector<ee_engine::ProvinceSSBO>& data) {
|
||||||
Containers::ArrayView provincesData(data.data(), data.size());
|
Containers::ArrayView provincesData(data.data(), data.size());
|
||||||
provinceSSBO.setSubData(index, provincesData);
|
provinceSSBO.setSubData(offset, provincesData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ee_magnum::MagnumRenderer::Shutdown() {
|
void ee_magnum::MagnumRenderer::Shutdown() {
|
||||||
29
renderers/vulkan/CMakeLists.txt
Normal file
29
renderers/vulkan/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
|
||||||
|
|
||||||
|
set(LIB vulkan-renderer)
|
||||||
|
|
||||||
|
# Find dependencies
|
||||||
|
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
find_package(glm CONFIG REQUIRED)
|
||||||
|
|
||||||
|
# Link library
|
||||||
|
|
||||||
|
add_library_target(${LIB}
|
||||||
|
MODULES ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include/vulkan/vulkan.cppm
|
||||||
|
MODULE_BASEDIRS ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include/
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(${LIB}
|
||||||
|
PUBLIC
|
||||||
|
VULKAN_HPP_NO_CONSTRUCTORS
|
||||||
|
VULKAN_HPP_ENABLE_STD_MODULE
|
||||||
|
VULKAN_HPP_STD_MODULE=std
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${LIB} PUBLIC engine-api)
|
||||||
|
target_link_libraries(${LIB} PUBLIC utils)
|
||||||
|
|
||||||
|
target_link_libraries(${LIB} PUBLIC p7)
|
||||||
|
target_link_libraries(${LIB} PRIVATE glm::glm)
|
||||||
|
target_link_libraries(${LIB} PRIVATE Vulkan::Vulkan)
|
||||||
89
renderers/vulkan/modules/VulkanRenderer.cppm
Normal file
89
renderers/vulkan/modules/VulkanRenderer.cppm
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
export module VulkanRender;
|
||||||
|
import Render;
|
||||||
|
import views;
|
||||||
|
import vulkan_render;
|
||||||
|
import std;
|
||||||
|
|
||||||
|
namespace ee_vulkan {
|
||||||
|
template <typename T> using lazy = std::optional<T>;
|
||||||
|
constexpr std::uint64_t TIMEOUT = std::numeric_limits<std::uint64_t>::max();
|
||||||
|
|
||||||
|
struct DrawConfig {
|
||||||
|
static constexpr vk::ClearValue CLEAR_COLOR = {.color = {std::array{1.0f, 1.0f, 1.0f, 1.0f}}};
|
||||||
|
static constexpr vk::ClearValue CLEAR_DEPTH = {.depthStencil = {1.0f, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mesh {
|
||||||
|
lazy<vulkan_render::Buffer<
|
||||||
|
glm::vec2,
|
||||||
|
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||||
|
vulkan_render::BufferMode::STAGING_ACCESS
|
||||||
|
>> vertices;
|
||||||
|
lazy<vulkan_render::Buffer<
|
||||||
|
std::uint32_t,
|
||||||
|
vk::BufferUsageFlagBits::eIndexBuffer,
|
||||||
|
vulkan_render::BufferMode::STAGING_ACCESS
|
||||||
|
>> indices;
|
||||||
|
lazy<vulkan_render::Buffer<
|
||||||
|
ee_engine::ProvinceSSBO,
|
||||||
|
vk::BufferUsageFlagBits::eStorageBuffer,
|
||||||
|
vulkan_render::BufferMode::STAGING_ACCESS
|
||||||
|
>> storage;
|
||||||
|
|
||||||
|
lazy<vulkan_render::Buffer<
|
||||||
|
vk::DrawIndexedIndirectCommand,
|
||||||
|
vk::BufferUsageFlagBits::eIndirectBuffer,
|
||||||
|
vulkan_render::BufferMode::STAGING_ACCESS
|
||||||
|
>> indirectCommands;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ee_vulkan {
|
||||||
|
class VulkanRenderer final : public ee_engine::IRenderer {
|
||||||
|
public:
|
||||||
|
explicit VulkanRenderer(GLFWwindow *window);
|
||||||
|
~VulkanRenderer() override;
|
||||||
|
|
||||||
|
void Init() override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Clear() override;
|
||||||
|
void DrawMap() override;
|
||||||
|
void DrawUI() override;
|
||||||
|
void ResizeWindow(int width, int height) override;
|
||||||
|
|
||||||
|
void ApplyCamera() override;
|
||||||
|
|
||||||
|
void UpdateProvinces(std::size_t offset, const std::vector<ee_engine::ProvinceSSBO>& data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::uint32_t MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
|
|
||||||
|
bool isDrawing = false;
|
||||||
|
|
||||||
|
lazy<vulkan_render::Context> context;
|
||||||
|
vulkan_render::Surface surface;
|
||||||
|
lazy<vulkan_render::Swapchain> swapchain;
|
||||||
|
lazy<vulkan_render::RenderCommands> renderCommands;
|
||||||
|
lazy<vulkan_render::Pipeline> provincesPipeline;
|
||||||
|
lazy<vulkan_render::SyncManager> syncManager;
|
||||||
|
|
||||||
|
lazy<vulkan_render::Texture<vulkan_render::TextureMode::DEPTH>> depth;
|
||||||
|
lazy<vulkan_render::Uniform<glm::mat4>> viewProjectionUniform;
|
||||||
|
Mesh provinces;
|
||||||
|
|
||||||
|
std::size_t currentFrame = 0;
|
||||||
|
|
||||||
|
void RecreateSwapchain();
|
||||||
|
|
||||||
|
void LoadProvincesMaterial();
|
||||||
|
void LoadProvinces();
|
||||||
|
void UpdateData(std::uint32_t frame);
|
||||||
|
};
|
||||||
|
}
|
||||||
80
renderers/vulkan/modules/control.cppm
Normal file
80
renderers/vulkan/modules/control.cppm
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Control partition module is responsible for configuration and usage of Vulkan operation objects
|
||||||
|
* including command buffers, dynamic rendering, synchronization objects and so on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module vulkan_render:control;
|
||||||
|
import :hardware;
|
||||||
|
import :resources;
|
||||||
|
import :data;
|
||||||
|
import std;
|
||||||
|
import vulkan_hpp;
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
|
||||||
|
class Command {
|
||||||
|
public:
|
||||||
|
const Context &context;
|
||||||
|
vk::raii::CommandBuffer handle;
|
||||||
|
|
||||||
|
Command(const Context &context, vk::raii::CommandBuffer&& buffer);
|
||||||
|
Command(const Context &context, const vk::raii::CommandPool &pool);
|
||||||
|
|
||||||
|
Command(Command&&) noexcept = default;
|
||||||
|
Command(const Command&) = delete;
|
||||||
|
Command& operator=(const Command&) = delete;
|
||||||
|
|
||||||
|
void Begin() const;
|
||||||
|
void Submit(const vk::raii::Queue &queue) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of Vulkan commands buffers for each frame in flight.
|
||||||
|
*/
|
||||||
|
class RenderCommands {
|
||||||
|
public:
|
||||||
|
vk::raii::CommandPool graphicsPool;
|
||||||
|
vk::raii::CommandPool transferPool;
|
||||||
|
|
||||||
|
std::vector<Command> graphicsBuffers;
|
||||||
|
std::vector<Command> transferBuffers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create pools and initialize all command buffers.
|
||||||
|
* @param context Vulkan context with ready device.
|
||||||
|
* @param maxFramesInFlight amount of created buffers for each purpose.
|
||||||
|
*/
|
||||||
|
RenderCommands(const Context &context, std::uint32_t maxFramesInFlight);
|
||||||
|
|
||||||
|
void SubmitStagingBuffer(const BaseBuffer<BufferMode::STAGING_ACCESS> &buffer) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static vk::raii::CommandPool MakePool(const Context &context, std::uint32_t queueFamily);
|
||||||
|
static std::vector<Command> MakeBuffers(
|
||||||
|
const Context &context,
|
||||||
|
const vk::raii::CommandPool &pool,
|
||||||
|
std::uint32_t size
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: sync2 with timeline-semaphore(s)
|
||||||
|
class SyncManager {
|
||||||
|
public:
|
||||||
|
const Context &context;
|
||||||
|
const std::uint32_t TIMEOUT;
|
||||||
|
|
||||||
|
std::vector<vk::raii::Semaphore> imageAvailable;
|
||||||
|
std::vector<vk::raii::Semaphore> renderFinished;
|
||||||
|
std::vector<vk::raii::Fence> inFlightFences;
|
||||||
|
std::vector<vk::Fence> imagesInFlight;
|
||||||
|
|
||||||
|
SyncManager(const Context &context, std::uint32_t maxFramesInFlight, std::uint64_t timeout);
|
||||||
|
void WaitInFlightFence(std::size_t currentFrame);
|
||||||
|
void WaitImageInFlight(std::size_t currentFrame, std::uint32_t imageIndex);
|
||||||
|
vk::Result AcquireNextImage(
|
||||||
|
const Swapchain &swapchain,
|
||||||
|
std::size_t currentFrame,
|
||||||
|
std::uint32_t &imageIndex
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
276
renderers/vulkan/modules/data.cppm
Normal file
276
renderers/vulkan/modules/data.cppm
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
/**
|
||||||
|
* Data partition module is responsible for configuration user-specific Vulkan objects
|
||||||
|
* like buffers, textures, transforms and so on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module;
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
|
||||||
|
#else
|
||||||
|
#define NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
export module vulkan_render:data;
|
||||||
|
import :hardware;
|
||||||
|
import :resources;
|
||||||
|
import std;
|
||||||
|
import vulkan_hpp;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
namespace vulkan_render {
|
||||||
|
struct NoMember {
|
||||||
|
constexpr NoMember() = default;
|
||||||
|
constexpr NoMember(std::nullptr_t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, bool condition>
|
||||||
|
using optional = std::conditional_t<condition, T, NoMember>;
|
||||||
|
|
||||||
|
template <bool hasStaging>
|
||||||
|
using StagingBuffer = optional<vk::raii::Buffer, hasStaging>;
|
||||||
|
|
||||||
|
template <bool hasStaging>
|
||||||
|
using StagingDeviceMemory = optional<vk::raii::DeviceMemory, hasStaging>;
|
||||||
|
|
||||||
|
template <typename T, bool isMapped>
|
||||||
|
using MappedMemory = optional<std::span<T>, isMapped>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
|
||||||
|
enum class BufferMode {
|
||||||
|
STATIC,
|
||||||
|
CPU_ACCESS,
|
||||||
|
STAGING_ACCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
template <BufferMode mode>
|
||||||
|
class BaseBuffer {
|
||||||
|
public:
|
||||||
|
const Context &context;
|
||||||
|
|
||||||
|
vk::raii::Buffer handle = nullptr;
|
||||||
|
vk::raii::DeviceMemory memory = nullptr;
|
||||||
|
|
||||||
|
NO_UNIQUE_ADDRESS StagingBuffer<mode == BufferMode::STAGING_ACCESS> stagingHandle = nullptr;
|
||||||
|
NO_UNIQUE_ADDRESS StagingDeviceMemory<mode == BufferMode::STAGING_ACCESS> stagingMemory = nullptr;
|
||||||
|
|
||||||
|
std::size_t memorySize;
|
||||||
|
|
||||||
|
explicit BaseBuffer(const Context &context, const std::size_t memorySize) :
|
||||||
|
context(context),
|
||||||
|
memorySize(memorySize) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, vk::BufferUsageFlagBits usage, BufferMode mode>
|
||||||
|
class Buffer : public BaseBuffer<mode> {
|
||||||
|
public:
|
||||||
|
NO_UNIQUE_ADDRESS MappedMemory<T, mode != BufferMode::STATIC> content;
|
||||||
|
static constexpr std::size_t elementSize = sizeof(T);
|
||||||
|
|
||||||
|
Buffer(const Context &context, const std::span<T> data) requires (mode == BufferMode::STATIC)
|
||||||
|
: BaseBuffer<mode>(context, data.size() * elementSize) {
|
||||||
|
|
||||||
|
MakeInstance(
|
||||||
|
usage,
|
||||||
|
vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||||
|
this->handle,
|
||||||
|
this->memory
|
||||||
|
);
|
||||||
|
|
||||||
|
void *mappedMemory = this->memory.mapMemory(0, this->memorySize);
|
||||||
|
std::ranges::copy(data, static_cast<T*>(mappedMemory));
|
||||||
|
this->memory.unmapMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::CPU_ACCESS)
|
||||||
|
: BaseBuffer<mode>(context, size * elementSize) {
|
||||||
|
|
||||||
|
MakeInstance(
|
||||||
|
usage,
|
||||||
|
vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||||
|
this->handle,
|
||||||
|
this->memory
|
||||||
|
);
|
||||||
|
|
||||||
|
void *mappedMemory = this->memory.mapMemory(0, this->memorySize);
|
||||||
|
|
||||||
|
content = std::span<T>(static_cast<T*>(mappedMemory), size);
|
||||||
|
std::ranges::fill(content, T{});
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::STAGING_ACCESS)
|
||||||
|
: BaseBuffer<mode>(context, size * elementSize) {
|
||||||
|
MakeInstance(
|
||||||
|
vk::Flags(vk::BufferUsageFlagBits::eTransferDst) | usage,
|
||||||
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||||
|
this->handle,
|
||||||
|
this->memory
|
||||||
|
);
|
||||||
|
|
||||||
|
MakeInstance(
|
||||||
|
vk::BufferUsageFlagBits::eTransferSrc,
|
||||||
|
vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||||
|
this->stagingHandle,
|
||||||
|
this->stagingMemory
|
||||||
|
);
|
||||||
|
|
||||||
|
void *mappedMemory = this->stagingMemory.mapMemory(0, this->memorySize);
|
||||||
|
|
||||||
|
content = std::span<T>(static_cast<T*>(mappedMemory), size);
|
||||||
|
std::ranges::fill(content, T{});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MakeInstance(
|
||||||
|
const vk::BufferUsageFlags &usageFlags,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags,
|
||||||
|
vk::raii::Buffer &buffer,
|
||||||
|
vk::raii::DeviceMemory &memory
|
||||||
|
) {
|
||||||
|
buffer = this->context.device.handle.createBuffer({
|
||||||
|
.size = this->memorySize,
|
||||||
|
.usage = usageFlags,
|
||||||
|
.sharingMode = vk::SharingMode::eExclusive
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
std::optional<vk::raii::DeviceMemory> allocated = this->context.AllocateBufferMemory(
|
||||||
|
buffer,
|
||||||
|
memoryFlags
|
||||||
|
);
|
||||||
|
allocated.has_value()
|
||||||
|
) {
|
||||||
|
memory = std::move(allocated).value();
|
||||||
|
buffer.bindMemory(memory, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextureMode {
|
||||||
|
RENDER,
|
||||||
|
DEPTH
|
||||||
|
};
|
||||||
|
|
||||||
|
template <TextureMode mode>
|
||||||
|
class Texture {
|
||||||
|
public:
|
||||||
|
const Context &context;
|
||||||
|
const std::uint32_t width;
|
||||||
|
const std::uint32_t height;
|
||||||
|
|
||||||
|
NO_UNIQUE_ADDRESS MappedMemory<std::byte, mode == TextureMode::RENDER> data;
|
||||||
|
|
||||||
|
vk::raii::Image handle = nullptr;
|
||||||
|
vk::raii::ImageView view = nullptr;
|
||||||
|
vk::raii::DeviceMemory memory = nullptr;
|
||||||
|
|
||||||
|
const vk::Format format;
|
||||||
|
|
||||||
|
Texture(
|
||||||
|
const Context &context,
|
||||||
|
const std::uint32_t width, const std::uint32_t height,
|
||||||
|
const vk::Format &format,
|
||||||
|
const std::span<std::byte> textureData
|
||||||
|
) requires (mode == TextureMode::RENDER)
|
||||||
|
: context(context), width(width), height(height), data(textureData), format(format) {
|
||||||
|
Buffer<
|
||||||
|
std::byte,
|
||||||
|
vk::BufferUsageFlagBits::eTransferSrc,
|
||||||
|
BufferMode::STATIC
|
||||||
|
> stagingBuffer(context, data);
|
||||||
|
|
||||||
|
MakeInstance(
|
||||||
|
vk::Flags(vk::ImageUsageFlagBits::eTransferDst) | vk::ImageUsageFlagBits::eSampled,
|
||||||
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||||
|
handle,
|
||||||
|
memory
|
||||||
|
);
|
||||||
|
|
||||||
|
/* TODO: Transition image layout
|
||||||
|
* Copy buffer to image
|
||||||
|
* Transition image layout
|
||||||
|
* Create view
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture(
|
||||||
|
const Context &context,
|
||||||
|
const Surface &surface
|
||||||
|
) requires (mode == TextureMode::DEPTH)
|
||||||
|
: context(context), width(surface.extent.width), height(surface.extent.height), format(vk::Format::eD32SfloatS8Uint) {
|
||||||
|
MakeInstance(
|
||||||
|
vk::ImageUsageFlagBits::eDepthStencilAttachment,
|
||||||
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||||
|
handle,
|
||||||
|
memory
|
||||||
|
);
|
||||||
|
MakeView(vk::ImageAspectFlagBits::eDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture(Texture&&) noexcept = default;
|
||||||
|
Texture(const Texture&) = delete;
|
||||||
|
Texture& operator=(const Texture&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MakeInstance(
|
||||||
|
const vk::ImageUsageFlags &usageFlags,
|
||||||
|
const vk::MemoryPropertyFlags &memoryflags,
|
||||||
|
vk::raii::Image &image,
|
||||||
|
vk::raii::DeviceMemory &memory
|
||||||
|
) const {
|
||||||
|
image = context.device.handle.createImage({
|
||||||
|
.imageType = vk::ImageType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.extent = {.width = width, .height = height, .depth = 1},
|
||||||
|
.mipLevels = 1,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.samples = vk::SampleCountFlagBits::e1,
|
||||||
|
.tiling = vk::ImageTiling::eOptimal,
|
||||||
|
.usage = usageFlags,
|
||||||
|
.sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
.initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
std::optional<vk::raii::DeviceMemory> allocated = context.AllocateImageMemory(
|
||||||
|
handle,
|
||||||
|
memoryflags
|
||||||
|
);
|
||||||
|
allocated.has_value()
|
||||||
|
) {
|
||||||
|
memory = std::move(allocated).value();
|
||||||
|
handle.bindMemory(memory, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeView(const vk::ImageAspectFlags &flags) {
|
||||||
|
view = context.device.handle.createImageView({
|
||||||
|
.image = handle,
|
||||||
|
.viewType = vk::ImageViewType::e2D,
|
||||||
|
.format = format,
|
||||||
|
.subresourceRange = {
|
||||||
|
.aspectMask = flags,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Uniform {
|
||||||
|
public:
|
||||||
|
std::vector<Buffer<T, vk::BufferUsageFlagBits::eUniformBuffer, BufferMode::CPU_ACCESS>> buffers;
|
||||||
|
explicit Uniform(const Context &context, std::uint32_t maxFramesInFlight) {
|
||||||
|
buffers.reserve(maxFramesInFlight);
|
||||||
|
for (std::uint32_t i = 0; i < maxFramesInFlight; i++) {
|
||||||
|
buffers.push_back(Buffer<T, vk::BufferUsageFlagBits::eUniformBuffer, BufferMode::CPU_ACCESS>(context, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
30
renderers/vulkan/modules/factory.cppm
Normal file
30
renderers/vulkan/modules/factory.cppm
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Factory partition module is responsible for construction of render objects
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module vulkan_render:factory;
|
||||||
|
import :hardware;
|
||||||
|
import :resources;
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
|
||||||
|
class ResourcesFactory {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Initialize frames swapchain
|
||||||
|
* @param context Vulkan context with initialized device and surface capabilities.
|
||||||
|
* @param surface surface origin. Extent will be clamped with frames size.
|
||||||
|
*/
|
||||||
|
static Swapchain CreateSwapchain(const Context &context,Surface &surface);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Initialize images and views for swapchain frames
|
||||||
|
* @param context Vulkan context with ready device.
|
||||||
|
* @param swapchain frames target with initialized swapchain origin.
|
||||||
|
* @param surface surface origin.
|
||||||
|
*/
|
||||||
|
static void SetupFrames(const Context &context, Swapchain &swapchain, const Surface &surface);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
195
renderers/vulkan/modules/hardware.cppm
Normal file
195
renderers/vulkan/modules/hardware.cppm
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* Hardware partition module is responsible for any Vulkan operations dependent on
|
||||||
|
* hardware capabilities including devices and extensions initialization, memory allocations and so on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module vulkan_render:hardware;
|
||||||
|
import :resources;
|
||||||
|
import std;
|
||||||
|
import vulkan_hpp;
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
|
||||||
|
struct PhysicalDeviceCapabilities {
|
||||||
|
vk::PhysicalDeviceProperties properties;
|
||||||
|
|
||||||
|
using Features1 = vk::PhysicalDeviceFeatures2;
|
||||||
|
using Features12 = vk::PhysicalDeviceVulkan12Features;
|
||||||
|
using Features13 = vk::PhysicalDeviceVulkan13Features;
|
||||||
|
using Features = vk::StructureChain<Features1, Features12, Features13>;
|
||||||
|
|
||||||
|
Features features;
|
||||||
|
Features enabledFeatures;
|
||||||
|
|
||||||
|
Features1& GetFeatures1() { return features.get<Features1>(); }
|
||||||
|
Features12& GetFeatures12() { return features.get<Features12>(); }
|
||||||
|
Features13& GetFeatures13() { return features.get<Features13>(); }
|
||||||
|
Features1& GetEnabledFeatures1() { return enabledFeatures.get<Features1>(); }
|
||||||
|
Features12& GetEnabledFeatures12() { return enabledFeatures.get<Features12>(); }
|
||||||
|
Features13& GetEnabledFeatures13() { return enabledFeatures.get<Features13>(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority scoring rules to select one physical device among available.
|
||||||
|
*/
|
||||||
|
struct PhysicalDevicePriorities {
|
||||||
|
struct GPU {
|
||||||
|
std::uint32_t DISCRETE = 1000;
|
||||||
|
std::uint32_t INTEGRATED = 1;
|
||||||
|
std::uint32_t VIRTUAL = 1;
|
||||||
|
std::uint32_t CPU = 0;
|
||||||
|
std::uint32_t OTHER = 0;
|
||||||
|
} gpu;
|
||||||
|
|
||||||
|
struct QueueFamilies {
|
||||||
|
std::uint32_t PRESENT = 500;
|
||||||
|
} queueFamilies;
|
||||||
|
|
||||||
|
struct SurfaceFormats {
|
||||||
|
std::uint32_t B8G8R8_SRGB = 10;
|
||||||
|
} surfaceFormats;
|
||||||
|
|
||||||
|
struct PresentModes {
|
||||||
|
std::uint32_t FIFO_RELAXED = 10;
|
||||||
|
} presentModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SurfaceCapabilities {
|
||||||
|
vk::SurfaceCapabilitiesKHR capabilities;
|
||||||
|
std::vector<vk::SurfaceFormatKHR> availableFormats;
|
||||||
|
std::vector<vk::PresentModeKHR> availablePresentModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
/**
|
||||||
|
* Vulkan objects associated with physical device.
|
||||||
|
*/
|
||||||
|
struct PhysicalDevice {
|
||||||
|
/**
|
||||||
|
* Ids of queue families by their usage.
|
||||||
|
*/
|
||||||
|
struct QueueFamilies {
|
||||||
|
static constexpr std::uint32_t NO_VALUE = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
std::uint32_t transfer = NO_VALUE;
|
||||||
|
std::uint32_t graphics = NO_VALUE;
|
||||||
|
std::uint32_t present = NO_VALUE;
|
||||||
|
} queueFamilies;
|
||||||
|
|
||||||
|
struct Memory {
|
||||||
|
vk::PhysicalDeviceMemoryProperties properties;
|
||||||
|
} memory;
|
||||||
|
|
||||||
|
vk::raii::PhysicalDevice handle = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vulkan objects associated with logical device.
|
||||||
|
*/
|
||||||
|
struct LogicalDevice {
|
||||||
|
vk::raii::Device handle = nullptr;
|
||||||
|
|
||||||
|
static constexpr float DEFAULT_QUEUE_PRIORITY = 1.0f;
|
||||||
|
vk::raii::Queue transfer = nullptr;
|
||||||
|
vk::raii::Queue graphics = nullptr;
|
||||||
|
vk::raii::Queue present = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vulkan device and hardware context.
|
||||||
|
* @warning Physical and logical devices need to be defined either directly
|
||||||
|
* or using vulkan_render::Context::SetupPhysicalDevice and vulkan_render::Context::SetupLogicalDevice
|
||||||
|
* before context usage.
|
||||||
|
*/
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
vk::raii::Context context;
|
||||||
|
vk::raii::Instance vulkan;
|
||||||
|
|
||||||
|
LogicalDevice device {};
|
||||||
|
PhysicalDevice physicalDevice {};
|
||||||
|
|
||||||
|
/// Loaded on physical device selection in vulkan_render::Context::SetupPhysicalDevice.
|
||||||
|
PhysicalDeviceCapabilities physicalDeviceCapabilities;
|
||||||
|
/// Loaded on physical device selection in vulkan_render::Context::SetupPhysicalDevice.
|
||||||
|
SurfaceCapabilities surfaceCapabilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize VkInstance.
|
||||||
|
* @param appName application name to register in Vulkan.
|
||||||
|
*/
|
||||||
|
explicit Context(const char* appName);
|
||||||
|
/**
|
||||||
|
* Initialize VkInstance.
|
||||||
|
* @param instanceInfo custom Vulkan configuration.
|
||||||
|
*/
|
||||||
|
explicit Context(const vk::InstanceCreateInfo &instanceInfo);
|
||||||
|
/**
|
||||||
|
* Initialize VkInstance.
|
||||||
|
* @param appName application name to register in Vulkan.
|
||||||
|
* @param extensions list of vulkan enabled extensions labels.
|
||||||
|
* @param layers list of vulkan enabled layers labels.
|
||||||
|
*/
|
||||||
|
Context(const char* appName, const std::vector<const char*> &extensions, const std::vector<const char*> &layers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select available physical device by priority.
|
||||||
|
*
|
||||||
|
* @note Updates vulkan_render::Context::physicalDevice and vulkan_render::Context::physicalDeviceCapabilities
|
||||||
|
* if at least one device is available, otherwise no effect.
|
||||||
|
*
|
||||||
|
* @param surface instance to be capable with.
|
||||||
|
* @param priorities configuration of physical device selection rules.
|
||||||
|
* @param[out] selectedFormat surface format of selected physical device. Undefined if no device was selected.
|
||||||
|
* @param[out] selectedPresentMode present mode of selected physical device. Undefined if no device was selected.
|
||||||
|
*/
|
||||||
|
void SetupPhysicalDevice(
|
||||||
|
const Surface &surface,
|
||||||
|
const PhysicalDevicePriorities &priorities,
|
||||||
|
vk::SurfaceFormatKHR &selectedFormat,
|
||||||
|
vk::PresentModeKHR &selectedPresentMode
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Initialize logical device with its available queues.
|
||||||
|
* @param extensions list of enabled device extensions.
|
||||||
|
*/
|
||||||
|
void SetupLogicalDevice(const std::vector<const char*> &extensions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find memory type available on device for specified requirements.
|
||||||
|
* @return id of memory type or std::nullopt if no suitable memory found.
|
||||||
|
*/
|
||||||
|
std::optional<std::uint32_t> FindMemoryType(
|
||||||
|
const vk::MemoryRequirements &requirements,
|
||||||
|
const vk::MemoryPropertyFlags &flags
|
||||||
|
);
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> AllocateMemory(
|
||||||
|
const vk::MemoryRequirements2 &memoryRequirements,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const;
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> AllocateBufferMemory(
|
||||||
|
const vk::raii::Buffer &buffer,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const;
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> AllocateImageMemory(
|
||||||
|
const vk::raii::Image &image,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
vk::raii::Instance MakeInstance(
|
||||||
|
const char *appName,
|
||||||
|
const std::vector<const char*> &extensions,
|
||||||
|
const std::vector<const char*> &layers
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
38
renderers/vulkan/modules/resources.cppm
Normal file
38
renderers/vulkan/modules/resources.cppm
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Resources partition module is responsible for definition of
|
||||||
|
* Vulkan resource objects including images, surface and so on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module vulkan_render:resources;
|
||||||
|
import std;
|
||||||
|
import vulkan_hpp;
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
/**
|
||||||
|
* Vulkan objects associated with surface.
|
||||||
|
* @note handle needs to be set manually according your window context.
|
||||||
|
*/
|
||||||
|
struct Surface {
|
||||||
|
vk::raii::SurfaceKHR handle = nullptr;
|
||||||
|
vk::SurfaceFormatKHR format;
|
||||||
|
vk::PresentModeKHR presentMode;
|
||||||
|
vk::Extent2D extent;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vulkan objects associated with frames swapchain.
|
||||||
|
* @note Can be created with vulkan_render::ResourcesFactory::CreateSwapchain.
|
||||||
|
*/
|
||||||
|
struct Swapchain {
|
||||||
|
vk::raii::SwapchainKHR handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Images used as targets to render frames.
|
||||||
|
*/
|
||||||
|
std::vector<vk::Image> frames;
|
||||||
|
/**
|
||||||
|
* Views describing how the swapchain frames are accessed during rendering.
|
||||||
|
*/
|
||||||
|
std::vector<vk::raii::ImageView> frameViews;
|
||||||
|
};
|
||||||
|
}
|
||||||
8
renderers/vulkan/modules/vulkan_render.cppm
Normal file
8
renderers/vulkan/modules/vulkan_render.cppm
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export module vulkan_render;
|
||||||
|
export import vulkan_hpp;
|
||||||
|
export import :hardware;
|
||||||
|
export import :workflow;
|
||||||
|
export import :control;
|
||||||
|
export import :resources;
|
||||||
|
export import :data;
|
||||||
|
export import :factory;
|
||||||
157
renderers/vulkan/modules/workflow.cppm
Normal file
157
renderers/vulkan/modules/workflow.cppm
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* Workflow partition module is responsible for configuration of rendering stages
|
||||||
|
* including pipelines, render passes and so on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module vulkan_render:workflow;
|
||||||
|
import :hardware;
|
||||||
|
import :resources;
|
||||||
|
import :data;
|
||||||
|
import std;
|
||||||
|
import vulkan_hpp;
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
using DescriptorBindings = std::vector<vk::DescriptorSetLayoutBinding>;
|
||||||
|
using DescriptorCounts = std::unordered_map<vk::DescriptorType /*type*/, std::uint8_t /*count*/>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content of SPIR-V shaders binary data for a single pipeline.
|
||||||
|
*/
|
||||||
|
struct ShaderProgram {
|
||||||
|
std::span<std::byte> vertex;
|
||||||
|
std::span<std::byte> fragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of vertex shader entry point.
|
||||||
|
*/
|
||||||
|
const char* vertexMainFun = "main";
|
||||||
|
/**
|
||||||
|
* Name of fragment shader entry point.
|
||||||
|
*/
|
||||||
|
const char* fragmentMainFun = "main";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderAttributeFormat {
|
||||||
|
static constexpr vk::Format FLOAT = vk::Format::eR32Sfloat;
|
||||||
|
static constexpr vk::Format VEC2 = vk::Format::eR32G32Sfloat;
|
||||||
|
static constexpr vk::Format VEC3 = vk::Format::eR32G32B32Sfloat;
|
||||||
|
static constexpr vk::Format VEC4 = vk::Format::eR32G32B32A32Sfloat;
|
||||||
|
|
||||||
|
ShaderAttributeFormat() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shaders input attributes binding.
|
||||||
|
*/
|
||||||
|
struct ShaderBindings {
|
||||||
|
vk::VertexInputBindingDescription vertexBinding;
|
||||||
|
std::uint32_t vertexAttributesCount;
|
||||||
|
vk::VertexInputAttributeDescription vertexAttributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of descriptions needed for Vulkan pipeline initialization.
|
||||||
|
*/
|
||||||
|
struct PipelineConfig {
|
||||||
|
vk::PipelineVertexInputStateCreateInfo vertexInputInfo;
|
||||||
|
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
|
||||||
|
vk::PipelineViewportStateCreateInfo viewportStateInfo;
|
||||||
|
vk::PipelineRasterizationStateCreateInfo rasterizerInfo;
|
||||||
|
vk::PipelineMultisampleStateCreateInfo multisamplingInfo;
|
||||||
|
vk::PipelineDepthStencilStateCreateInfo depthStencilInfo;
|
||||||
|
vk::PipelineColorBlendStateCreateInfo colorBlendInfo;
|
||||||
|
vk::PipelineDynamicStateCreateInfo dynamicStateInfo;
|
||||||
|
vk::DescriptorSetLayoutCreateInfo descriptorsInfo;
|
||||||
|
|
||||||
|
// SRC - new color, DST - old color
|
||||||
|
std::vector<vk::PipelineColorBlendAttachmentState> colorBlendAttachmentStates;
|
||||||
|
std::vector<vk::DynamicState> dynamicStates;
|
||||||
|
std::vector<vk::DescriptorSetLayoutBinding> descriptors;
|
||||||
|
|
||||||
|
vk::Viewport defaultViewport;
|
||||||
|
vk::Rect2D defaultScissors;
|
||||||
|
vk::PushConstantRange pushConstantRange;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DescriptorInfo {
|
||||||
|
std::variant<
|
||||||
|
vk::DescriptorBufferInfo,
|
||||||
|
vk::DescriptorImageInfo
|
||||||
|
> info;
|
||||||
|
std::uint32_t bindingPosition;
|
||||||
|
vk::DescriptorType type;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace vulkan_render {
|
||||||
|
/**
|
||||||
|
* Vulkan pipeline constructed on context, shaders and descriptors.
|
||||||
|
*/
|
||||||
|
class Pipeline {
|
||||||
|
public:
|
||||||
|
const Context &context;
|
||||||
|
const Surface &surface;
|
||||||
|
const Swapchain &swapchain;
|
||||||
|
const std::uint32_t maxFramesInFlight;
|
||||||
|
DescriptorCounts descriptorCounts;
|
||||||
|
|
||||||
|
vk::raii::DescriptorSetLayout descriptorsLayout;
|
||||||
|
vk::raii::RenderPass renderPass;
|
||||||
|
vk::raii::PipelineLayout layout;
|
||||||
|
vk::raii::Pipeline handle;
|
||||||
|
|
||||||
|
vk::Viewport viewport;
|
||||||
|
vk::Rect2D scissors;
|
||||||
|
|
||||||
|
vk::raii::DescriptorPool descriptorPool;
|
||||||
|
vk::raii::DescriptorSets descriptorSets;
|
||||||
|
|
||||||
|
std::vector<vk::raii::Framebuffer> framebuffers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Pipeline on shaders and their input.
|
||||||
|
* @param context Vulkan context with ready device.
|
||||||
|
* @param surface
|
||||||
|
* @param swapchain swapchain frame images used to allocate descriptors.
|
||||||
|
* @param config pipeline configuration.
|
||||||
|
* @param shaderProgram SPIR-V shaders binaries.
|
||||||
|
* @param maxFramesInFlight max number of frames in flight used to allocate descriptors .
|
||||||
|
*/
|
||||||
|
Pipeline(
|
||||||
|
const Context &context,
|
||||||
|
const Surface &surface,
|
||||||
|
const Swapchain &swapchain,
|
||||||
|
const Texture<TextureMode::DEPTH> &depth,
|
||||||
|
const PipelineConfig &config,
|
||||||
|
const ShaderProgram &shaderProgram,
|
||||||
|
std::uint32_t maxFramesInFlight
|
||||||
|
);
|
||||||
|
|
||||||
|
void UpdateDescriptors(const std::vector<DescriptorInfo> &descriptors, std::uint32_t frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct default pipeline configuration for given shaders input layout.
|
||||||
|
* @param surface surface origin.
|
||||||
|
* @param bindings shaders input attributes binding definition.
|
||||||
|
* @param descriptors descriptors binding definition (e.g. uniforms).
|
||||||
|
* @param pushConstants definition for small performant data set.
|
||||||
|
* @return default pipeline configuration.
|
||||||
|
*/
|
||||||
|
static PipelineConfig MakeConfig(
|
||||||
|
const Surface &surface,
|
||||||
|
const ShaderBindings &bindings,
|
||||||
|
const DescriptorBindings &descriptors,
|
||||||
|
const vk::PushConstantRange& pushConstants
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
vk::raii::DescriptorSetLayout MakeDescriptorsLayout(const PipelineConfig &config) const;
|
||||||
|
vk::raii::PipelineLayout MakeLayout(const PipelineConfig &config) const;
|
||||||
|
vk::raii::Pipeline MakeInstance(const PipelineConfig &config, const ShaderProgram &shaderProgram) const;
|
||||||
|
vk::raii::DescriptorPool MakeDescriptorPool(const DescriptorBindings &descriptors);
|
||||||
|
vk::raii::DescriptorSets MakeDescriptorSets() const;
|
||||||
|
vk::raii::RenderPass MakerRenderPass() const;
|
||||||
|
void MakeFramebuffers(const Texture<TextureMode::DEPTH> &depth);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
356
renderers/vulkan/src/VulkanRenderer.cpp
Normal file
356
renderers/vulkan/src/VulkanRenderer.cpp
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#define GLFW_INCLUDE_VULKAN
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
module VulkanRender;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
ee_vulkan::VulkanRenderer::VulkanRenderer(GLFWwindow *window) : IRenderer(window) {
|
||||||
|
std::uint32_t glfwExtensionsCount;
|
||||||
|
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionsCount);
|
||||||
|
|
||||||
|
context.emplace(
|
||||||
|
"EtrediaEmpires",
|
||||||
|
std::vector(glfwExtensions, glfwExtensions + glfwExtensionsCount),
|
||||||
|
std::vector{"VK_LAYER_KHRONOS_validation"}
|
||||||
|
);
|
||||||
|
|
||||||
|
VkSurfaceKHR vulkanSurface;
|
||||||
|
glfwCreateWindowSurface(*context->vulkan, window, nullptr, &vulkanSurface);
|
||||||
|
surface.handle = vk::raii::SurfaceKHR(context->vulkan, vulkanSurface);
|
||||||
|
surface.extent = {
|
||||||
|
.width = static_cast<std::uint32_t>(screenWidth),
|
||||||
|
.height = static_cast<std::uint32_t>(screenHeight)
|
||||||
|
};
|
||||||
|
|
||||||
|
context->SetupPhysicalDevice(surface, {}, surface.format, surface.presentMode);
|
||||||
|
context->SetupLogicalDevice({vk::KHRSwapchainExtensionName});
|
||||||
|
|
||||||
|
swapchain.emplace(
|
||||||
|
vulkan_render::ResourcesFactory::CreateSwapchain(*context, surface)
|
||||||
|
);
|
||||||
|
renderCommands.emplace(*context, MAX_FRAMES_IN_FLIGHT);
|
||||||
|
syncManager.emplace(*context, MAX_FRAMES_IN_FLIGHT, TIMEOUT);
|
||||||
|
|
||||||
|
LOG(INFO, "Renderer created");
|
||||||
|
}
|
||||||
|
|
||||||
|
ee_vulkan::VulkanRenderer::~VulkanRenderer() {}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::Init() {
|
||||||
|
depth.emplace(*context, surface);
|
||||||
|
|
||||||
|
LoadProvincesMaterial();
|
||||||
|
|
||||||
|
viewProjectionUniform.emplace(*context, MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
|
LoadProvinces();
|
||||||
|
|
||||||
|
isDrawing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::Shutdown() {
|
||||||
|
context->device.handle.waitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::Clear() {}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::DrawMap() {
|
||||||
|
if (!isDrawing) return;
|
||||||
|
|
||||||
|
syncManager->WaitInFlightFence(currentFrame);
|
||||||
|
|
||||||
|
std::uint32_t imageIndex = 0;
|
||||||
|
switch (vk::Result result = syncManager->AcquireNextImage(*swapchain, currentFrame, imageIndex)) {
|
||||||
|
case vk::Result::eSuccess:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
|
case vk::Result::eSuboptimalKHR:
|
||||||
|
LOG(WARNING, "Swapchain needs to be recreated");
|
||||||
|
RecreateSwapchain();
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG(ERROR, "Failed to get next image index. Code: %d", result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateData(currentFrame);
|
||||||
|
|
||||||
|
syncManager->WaitImageInFlight(currentFrame, imageIndex);
|
||||||
|
|
||||||
|
const vulkan_render::Command &renderCommand = renderCommands->graphicsBuffers[currentFrame];
|
||||||
|
renderCommand.handle.reset();
|
||||||
|
renderCommand.Begin();
|
||||||
|
|
||||||
|
std::array clears = {DrawConfig::CLEAR_COLOR, DrawConfig::CLEAR_DEPTH};
|
||||||
|
renderCommand.handle.beginRenderPass({
|
||||||
|
.renderPass = provincesPipeline->renderPass,
|
||||||
|
.framebuffer = provincesPipeline->framebuffers[imageIndex],
|
||||||
|
.renderArea = {
|
||||||
|
.offset = {.x = 0, .y = 0},
|
||||||
|
.extent = surface.extent
|
||||||
|
},
|
||||||
|
.clearValueCount = static_cast<std::uint32_t>(clears.size()),
|
||||||
|
.pClearValues = clears.data()
|
||||||
|
}, vk::SubpassContents::eInline);
|
||||||
|
|
||||||
|
renderCommand.handle.bindPipeline(vk::PipelineBindPoint::eGraphics, provincesPipeline->handle);
|
||||||
|
renderCommand.handle.setViewport(0, {provincesPipeline->viewport});
|
||||||
|
renderCommand.handle.setScissor(0, {provincesPipeline->scissors});
|
||||||
|
|
||||||
|
renderCommand.handle.bindVertexBuffers(
|
||||||
|
0,
|
||||||
|
{provinces.vertices->handle},
|
||||||
|
{0}
|
||||||
|
);
|
||||||
|
renderCommand.handle.bindIndexBuffer(
|
||||||
|
provinces.indices->handle,
|
||||||
|
0,
|
||||||
|
vk::IndexType::eUint32
|
||||||
|
);
|
||||||
|
|
||||||
|
renderCommand.handle.bindDescriptorSets(
|
||||||
|
vk::PipelineBindPoint::eGraphics,
|
||||||
|
provincesPipeline->layout,
|
||||||
|
0,
|
||||||
|
{provincesPipeline->descriptorSets[currentFrame]},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
renderCommand.handle.drawIndexedIndirect(
|
||||||
|
provinces.indirectCommands->handle,
|
||||||
|
0,
|
||||||
|
provinces.indirectCommands->content.size(),
|
||||||
|
provinces.indirectCommands->elementSize
|
||||||
|
);
|
||||||
|
|
||||||
|
renderCommand.handle.endRenderPass();
|
||||||
|
renderCommand.handle.end();
|
||||||
|
|
||||||
|
context->device.handle.resetFences({syncManager->inFlightFences[currentFrame]});
|
||||||
|
|
||||||
|
vk::CommandBufferSubmitInfo cmdSubmitInfo {
|
||||||
|
.commandBuffer = renderCommand.handle
|
||||||
|
};
|
||||||
|
vk::SemaphoreSubmitInfo waitSemaphoreInfo {
|
||||||
|
.semaphore = syncManager->imageAvailable[currentFrame],
|
||||||
|
.stageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput
|
||||||
|
};
|
||||||
|
vk::SemaphoreSubmitInfo signalSemaphoreInfo {
|
||||||
|
.semaphore = syncManager->renderFinished[currentFrame],
|
||||||
|
.stageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput
|
||||||
|
};
|
||||||
|
const vk::SubmitInfo2 submitInfo {
|
||||||
|
.waitSemaphoreInfoCount = 1,
|
||||||
|
.pWaitSemaphoreInfos = &waitSemaphoreInfo,
|
||||||
|
.commandBufferInfoCount = 1,
|
||||||
|
.pCommandBufferInfos = &cmdSubmitInfo,
|
||||||
|
.signalSemaphoreInfoCount = 1,
|
||||||
|
.pSignalSemaphoreInfos = &signalSemaphoreInfo,
|
||||||
|
};
|
||||||
|
context->device.graphics.submit2({submitInfo}, syncManager->inFlightFences[currentFrame]);
|
||||||
|
|
||||||
|
vk::SwapchainKHR swapchains[] = {swapchain->handle};
|
||||||
|
switch (
|
||||||
|
vk::Result result = context->device.present.presentKHR({
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = &signalSemaphoreInfo.semaphore,
|
||||||
|
.swapchainCount = 1,
|
||||||
|
.pSwapchains = swapchains,
|
||||||
|
.pImageIndices = &imageIndex
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
case vk::Result::eSuccess:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
|
case vk::Result::eSuboptimalKHR:
|
||||||
|
LOG(WARNING, "Swapchain needs to be recreated");
|
||||||
|
RecreateSwapchain();
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG(ERROR, "Failed to get next image index. Code: %d", result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::DrawUI() {}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::ResizeWindow(const int width, const int height) {
|
||||||
|
IRenderer::ResizeWindow(width, height);
|
||||||
|
surface.extent = {
|
||||||
|
.width = static_cast<std::uint32_t>(screenWidth),
|
||||||
|
.height = static_cast<std::uint32_t>(screenHeight)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::ApplyCamera() {
|
||||||
|
viewProjection[1][1] *= -1;
|
||||||
|
viewProjectionInversed[1][1] *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::UpdateProvinces(const std::size_t offset, const std::vector<ee_engine::ProvinceSSBO> &data) {
|
||||||
|
const std::size_t elementOffset = offset / provinces.storage->elementSize;
|
||||||
|
std::ranges::copy(data, provinces.storage->content.data() + elementOffset);
|
||||||
|
renderCommands->SubmitStagingBuffer(*provinces.storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::RecreateSwapchain() {
|
||||||
|
// We should not render with minimized window
|
||||||
|
if (screenWidth == 0 || screenHeight == 0)
|
||||||
|
{
|
||||||
|
isDrawing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isDrawing = true;
|
||||||
|
|
||||||
|
context->device.handle.waitIdle();
|
||||||
|
swapchain = vulkan_render::ResourcesFactory::CreateSwapchain(*context, surface);
|
||||||
|
depth.emplace(*context, surface);
|
||||||
|
LoadProvincesMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::LoadProvincesMaterial() {
|
||||||
|
constexpr vulkan_render::ShaderBindings shaderBindings {
|
||||||
|
.vertexBinding = {
|
||||||
|
.binding = 0,
|
||||||
|
.stride = sizeof(glm::vec2),
|
||||||
|
.inputRate = vk::VertexInputRate::eVertex
|
||||||
|
},
|
||||||
|
.vertexAttributesCount = 1,
|
||||||
|
.vertexAttributes = {
|
||||||
|
.location = 0,
|
||||||
|
.binding = 0,
|
||||||
|
.format = vulkan_render::ShaderAttributeFormat::VEC2,
|
||||||
|
.offset = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::DescriptorSetLayoutBinding viewProjection {
|
||||||
|
.binding = 0,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.pImmutableSamplers = nullptr
|
||||||
|
};
|
||||||
|
const vk::DescriptorSetLayoutBinding provincesSSBO {
|
||||||
|
.binding = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::Flags(vk::ShaderStageFlagBits::eVertex) | vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.pImmutableSamplers = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::PushConstantRange model {
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.offset = 0,
|
||||||
|
.size = sizeof(glm::mat4)
|
||||||
|
};
|
||||||
|
|
||||||
|
vulkan_render::PipelineConfig provincesMaterial = vulkan_render::Pipeline::MakeConfig(
|
||||||
|
surface,
|
||||||
|
shaderBindings,
|
||||||
|
{viewProjection, provincesSSBO},
|
||||||
|
model
|
||||||
|
);
|
||||||
|
vulkan_render::ShaderProgram provincesShader {
|
||||||
|
.vertex = {reinterpret_cast<std::byte*>(shaders.vertProvinces->content), shaders.vertProvinces->size},
|
||||||
|
.fragment = {reinterpret_cast<std::byte*>(shaders.fragProvinces->content), shaders.fragProvinces->size}
|
||||||
|
};
|
||||||
|
|
||||||
|
provincesPipeline.emplace(
|
||||||
|
*context,
|
||||||
|
surface,
|
||||||
|
*swapchain,
|
||||||
|
*depth,
|
||||||
|
provincesMaterial,
|
||||||
|
provincesShader,
|
||||||
|
MAX_FRAMES_IN_FLIGHT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::LoadProvinces() {
|
||||||
|
std::vector<ee_engine::ProvinceSSBO> provincesData;
|
||||||
|
std::vector<glm::vec2> verticesData;
|
||||||
|
std::vector<std::uint32_t> indicesData;
|
||||||
|
std::vector<vk::DrawIndexedIndirectCommand> indirectCommandsData;
|
||||||
|
|
||||||
|
std::size_t regionOffset = 0;
|
||||||
|
std::uint32_t indicesOffset = 0;
|
||||||
|
std::int32_t verticesOffset = 0;
|
||||||
|
for (auto& [region, provs] : mapMesh->regions) {
|
||||||
|
for (auto&[vertices, indices] : provs) {
|
||||||
|
indirectCommandsData.push_back({
|
||||||
|
.indexCount = static_cast<std::uint32_t>(indices.size()),
|
||||||
|
.instanceCount = 1,
|
||||||
|
.firstIndex = indicesOffset,
|
||||||
|
.vertexOffset = verticesOffset,
|
||||||
|
.firstInstance = static_cast<std::uint32_t>(provincesData.size())
|
||||||
|
});
|
||||||
|
|
||||||
|
verticesData.append_range(vertices);
|
||||||
|
indicesData.append_range(indices);
|
||||||
|
|
||||||
|
ee_engine::ProvinceSSBO data{
|
||||||
|
.regionId = region,
|
||||||
|
.color = 0xFF0000
|
||||||
|
};
|
||||||
|
provincesData.push_back(data);
|
||||||
|
|
||||||
|
indicesOffset += indices.size();
|
||||||
|
verticesOffset += vertices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
regionsMapping[region] = {.offset = regionOffset, .provincesCount = provs.size()};
|
||||||
|
regionOffset += provs.size() * sizeof(ee_engine::ProvinceSSBO);
|
||||||
|
}
|
||||||
|
|
||||||
|
provinces.vertices.emplace(*context, verticesData.size());
|
||||||
|
provinces.indices.emplace(*context, indicesData.size());
|
||||||
|
provinces.storage.emplace(*context, provincesData.size());
|
||||||
|
provinces.indirectCommands.emplace(*context, indirectCommandsData.size());
|
||||||
|
|
||||||
|
std::ranges::copy(verticesData, provinces.vertices->content.data());
|
||||||
|
std::ranges::copy(indicesData, provinces.indices->content.data());
|
||||||
|
std::ranges::copy(provincesData, provinces.storage->content.data());
|
||||||
|
std::ranges::copy(indirectCommandsData, provinces.indirectCommands->content.data());
|
||||||
|
|
||||||
|
renderCommands->SubmitStagingBuffer(*provinces.vertices);
|
||||||
|
renderCommands->SubmitStagingBuffer(*provinces.indices);
|
||||||
|
renderCommands->SubmitStagingBuffer(*provinces.storage);
|
||||||
|
renderCommands->SubmitStagingBuffer(*provinces.indirectCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ee_vulkan::VulkanRenderer::UpdateData(const std::uint32_t frame) {
|
||||||
|
std::span<glm::mat4> &vpBuffer = viewProjectionUniform->buffers[frame].content;
|
||||||
|
vpBuffer[0] = viewProjection;
|
||||||
|
|
||||||
|
provincesPipeline->UpdateDescriptors({
|
||||||
|
{
|
||||||
|
.info = vk::DescriptorBufferInfo{
|
||||||
|
.buffer = viewProjectionUniform->buffers[frame].handle,
|
||||||
|
.offset = 0,
|
||||||
|
.range = viewProjectionUniform->buffers[frame].memorySize
|
||||||
|
},
|
||||||
|
.bindingPosition = 0,
|
||||||
|
.type = vk::DescriptorType::eUniformBuffer
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.info = vk::DescriptorBufferInfo{
|
||||||
|
.buffer = provinces.storage->handle,
|
||||||
|
.offset = 0,
|
||||||
|
.range = provinces.storage->memorySize
|
||||||
|
},
|
||||||
|
.bindingPosition = 1,
|
||||||
|
.type = vk::DescriptorType::eStorageBuffer
|
||||||
|
}
|
||||||
|
}, frame);
|
||||||
|
}
|
||||||
155
renderers/vulkan/src/control.cpp
Normal file
155
renderers/vulkan/src/control.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
module vulkan_render;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
vulkan_render::Command::Command(const Context &context, vk::raii::CommandBuffer &&buffer) :
|
||||||
|
context(context),
|
||||||
|
handle(std::move(buffer)) {}
|
||||||
|
|
||||||
|
vulkan_render::Command::Command(const Context &context, const vk::raii::CommandPool &pool) :
|
||||||
|
context(context),
|
||||||
|
handle(std::move(
|
||||||
|
context.device.handle.allocateCommandBuffers({
|
||||||
|
.commandPool = pool,
|
||||||
|
.level = vk::CommandBufferLevel::ePrimary, //TODO: reuse some commands using SECONDARY
|
||||||
|
.commandBufferCount = 1
|
||||||
|
}).front()
|
||||||
|
)) {}
|
||||||
|
|
||||||
|
void vulkan_render::Command::Begin() const {
|
||||||
|
handle.begin({
|
||||||
|
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::Command::Submit(const vk::raii::Queue &queue) const {
|
||||||
|
handle.end();
|
||||||
|
|
||||||
|
vk::CommandBufferSubmitInfo cmdSubmitInfo {
|
||||||
|
.commandBuffer = handle
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::SubmitInfo2 submitInfo {
|
||||||
|
.commandBufferInfoCount = 1,
|
||||||
|
.pCommandBufferInfos = &cmdSubmitInfo,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::raii::Fence fence = context.device.handle.createFence({});
|
||||||
|
queue.submit2({submitInfo}, fence);
|
||||||
|
if (
|
||||||
|
vk::Result result = context.device.handle.waitForFences({fence}, true, std::numeric_limits<std::uint64_t>::max());
|
||||||
|
result != vk::Result::eSuccess
|
||||||
|
) {
|
||||||
|
LOG(WARNING, "Command buffer submit timeout. Code: %d", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkan_render::RenderCommands::RenderCommands(const Context &context, const std::uint32_t maxFramesInFlight) :
|
||||||
|
graphicsPool(MakePool(context, context.physicalDevice.queueFamilies.graphics)),
|
||||||
|
transferPool(MakePool(context, context.physicalDevice.queueFamilies.transfer)) {
|
||||||
|
|
||||||
|
graphicsBuffers = MakeBuffers(context, graphicsPool, maxFramesInFlight);
|
||||||
|
transferBuffers = MakeBuffers(context, transferPool, maxFramesInFlight);
|
||||||
|
|
||||||
|
LOG(INFO, "Command buffers initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::RenderCommands::SubmitStagingBuffer(const BaseBuffer<BufferMode::STAGING_ACCESS> &buffer) const {
|
||||||
|
const Command cmd(buffer.context, transferPool);
|
||||||
|
cmd.Begin();
|
||||||
|
|
||||||
|
vk::BufferCopy2 copyRegion {
|
||||||
|
.srcOffset = 0,
|
||||||
|
.dstOffset = 0,
|
||||||
|
.size = buffer.memorySize
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.handle.copyBuffer2({
|
||||||
|
.srcBuffer = buffer.stagingHandle,
|
||||||
|
.dstBuffer = buffer.handle,
|
||||||
|
.regionCount = 1,
|
||||||
|
.pRegions = ©Region
|
||||||
|
});
|
||||||
|
|
||||||
|
cmd.Submit(buffer.context.device.transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::CommandPool vulkan_render::RenderCommands::MakePool(const Context &context, const std::uint32_t queueFamily) {
|
||||||
|
return context.device.handle.createCommandPool({
|
||||||
|
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
||||||
|
.queueFamilyIndex = queueFamily,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<vulkan_render::Command> vulkan_render::RenderCommands::MakeBuffers(
|
||||||
|
const Context &context,
|
||||||
|
const vk::raii::CommandPool &pool,
|
||||||
|
const std::uint32_t size
|
||||||
|
) {
|
||||||
|
std::vector<vk::raii::CommandBuffer> buffers = context.device.handle.allocateCommandBuffers({
|
||||||
|
.commandPool = pool,
|
||||||
|
.level = vk::CommandBufferLevel::ePrimary, //TODO: reuse some commands using SECONDARY
|
||||||
|
.commandBufferCount = size
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<Command> result;
|
||||||
|
result.reserve(buffers.size());
|
||||||
|
for (vk::raii::CommandBuffer &buffer : buffers) {
|
||||||
|
result.emplace_back(context, std::move(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkan_render::SyncManager::SyncManager(
|
||||||
|
const Context &context,
|
||||||
|
const std::uint32_t maxFramesInFlight,
|
||||||
|
const std::uint64_t timeout
|
||||||
|
) :
|
||||||
|
context(context), TIMEOUT(timeout) {
|
||||||
|
imageAvailable.reserve(maxFramesInFlight);
|
||||||
|
renderFinished.reserve(maxFramesInFlight);
|
||||||
|
inFlightFences.reserve(maxFramesInFlight);
|
||||||
|
imagesInFlight.resize(maxFramesInFlight, nullptr);
|
||||||
|
|
||||||
|
for (std::uint32_t i = 0; i < maxFramesInFlight; i++) {
|
||||||
|
imageAvailable.push_back(context.device.handle.createSemaphore({}));
|
||||||
|
renderFinished.push_back(context.device.handle.createSemaphore({}));
|
||||||
|
inFlightFences.push_back(context.device.handle.createFence({.flags = vk::FenceCreateFlagBits::eSignaled}));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO, "Sync objects initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::SyncManager::WaitInFlightFence(const std::size_t currentFrame) {
|
||||||
|
if (
|
||||||
|
vk::Result result = context.device.handle.waitForFences({inFlightFences[currentFrame]}, vk::True, TIMEOUT);
|
||||||
|
result != vk::Result::eSuccess
|
||||||
|
)
|
||||||
|
LOG(ERROR, "Waiting semaphore timeout. Code: %d", result);
|
||||||
|
}
|
||||||
|
void vulkan_render::SyncManager::WaitImageInFlight(const std::size_t currentFrame, const std::uint32_t imageIndex) {
|
||||||
|
if (imagesInFlight[imageIndex] == nullptr) return;
|
||||||
|
if (
|
||||||
|
vk::Result result = context.device.handle.waitForFences({imagesInFlight[imageIndex]}, vk::True, TIMEOUT);
|
||||||
|
result != vk::Result::eSuccess
|
||||||
|
)
|
||||||
|
LOG(ERROR, "Waiting semaphore timeout. Code: %d", result);
|
||||||
|
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Result vulkan_render::SyncManager::AcquireNextImage(
|
||||||
|
const Swapchain &swapchain,
|
||||||
|
const std::size_t currentFrame,
|
||||||
|
std::uint32_t &imageIndex
|
||||||
|
) const {
|
||||||
|
auto [result, newImageIndex] = context.device.handle.acquireNextImage2KHR({
|
||||||
|
.swapchain = swapchain.handle,
|
||||||
|
.timeout = TIMEOUT,
|
||||||
|
.semaphore = imageAvailable[currentFrame],
|
||||||
|
.deviceMask = 1
|
||||||
|
});
|
||||||
|
|
||||||
|
imageIndex = newImageIndex;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
87
renderers/vulkan/src/factory.cpp
Normal file
87
renderers/vulkan/src/factory.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
module vulkan_render;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
vulkan_render::Swapchain vulkan_render::ResourcesFactory::CreateSwapchain(const Context &context, Surface &surface) {
|
||||||
|
// Clamp extent
|
||||||
|
surface.extent.width = std::max(
|
||||||
|
context.surfaceCapabilities.capabilities.minImageExtent.width,
|
||||||
|
(std::min)(context.surfaceCapabilities.capabilities.maxImageExtent.width, surface.extent.width)
|
||||||
|
);
|
||||||
|
surface.extent.height = std::max(
|
||||||
|
context.surfaceCapabilities.capabilities.minImageExtent.height,
|
||||||
|
(std::min)(context.surfaceCapabilities.capabilities.maxImageExtent.height, surface.extent.height)
|
||||||
|
);
|
||||||
|
|
||||||
|
std::uint32_t imagesCount = context.surfaceCapabilities.capabilities.minImageCount + 1;
|
||||||
|
// Clamp min images count
|
||||||
|
if (context.surfaceCapabilities.capabilities.maxImageCount > 0 && imagesCount > context.surfaceCapabilities.capabilities.maxImageCount)
|
||||||
|
imagesCount = context.surfaceCapabilities.capabilities.maxImageCount;
|
||||||
|
|
||||||
|
vk::SwapchainCreateInfoKHR swapchainCreateInfo {
|
||||||
|
.surface = surface.handle,
|
||||||
|
.minImageCount = imagesCount,
|
||||||
|
.imageFormat = surface.format.format,
|
||||||
|
.imageColorSpace = surface.format.colorSpace,
|
||||||
|
.imageExtent = surface.extent,
|
||||||
|
.imageArrayLayers = 1,
|
||||||
|
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
|
||||||
|
.preTransform = context.surfaceCapabilities.capabilities.currentTransform,
|
||||||
|
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque,
|
||||||
|
.presentMode = surface.presentMode,
|
||||||
|
.clipped = vk::True,
|
||||||
|
.oldSwapchain = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector queueFamiliesShare {context.physicalDevice.queueFamilies.graphics, context.physicalDevice.queueFamilies.present};
|
||||||
|
if (context.physicalDevice.queueFamilies.graphics != context.physicalDevice.queueFamilies.present)
|
||||||
|
{
|
||||||
|
swapchainCreateInfo.imageSharingMode = vk::SharingMode::eConcurrent;
|
||||||
|
swapchainCreateInfo.queueFamilyIndexCount = queueFamiliesShare.size();
|
||||||
|
swapchainCreateInfo.pQueueFamilyIndices = queueFamiliesShare.data();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
swapchainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive;
|
||||||
|
|
||||||
|
Swapchain swapchain {
|
||||||
|
.handle = context.device.handle.createSwapchainKHR(swapchainCreateInfo)
|
||||||
|
};
|
||||||
|
LOG(INFO, "Swapchain is created");
|
||||||
|
|
||||||
|
SetupFrames(context, swapchain, surface);
|
||||||
|
|
||||||
|
return swapchain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::ResourcesFactory::SetupFrames(const Context &context, Swapchain &swapchain, const Surface &surface) {
|
||||||
|
swapchain.frames.clear();
|
||||||
|
swapchain.frameViews.clear();
|
||||||
|
|
||||||
|
swapchain.frames = swapchain.handle.getImages();
|
||||||
|
LOG(INFO, "Frames are ready");
|
||||||
|
|
||||||
|
swapchain.frameViews.reserve(swapchain.frames.size());
|
||||||
|
for (size_t i = 0; i < swapchain.frames.size(); i++)
|
||||||
|
{
|
||||||
|
vk::ImageViewCreateInfo frameViewInfo {
|
||||||
|
.image = swapchain.frames[i],
|
||||||
|
.viewType = vk::ImageViewType::e2D,
|
||||||
|
.format = surface.format.format,
|
||||||
|
.components {
|
||||||
|
.r = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.g = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.b = vk::ComponentSwizzle::eIdentity,
|
||||||
|
.a = vk::ComponentSwizzle::eIdentity
|
||||||
|
},
|
||||||
|
.subresourceRange {
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
swapchain.frameViews.push_back(context.device.handle.createImageView(frameViewInfo));
|
||||||
|
}
|
||||||
|
LOG(INFO, "Frame views are ready.");
|
||||||
|
}
|
||||||
304
renderers/vulkan/src/hardware.cpp
Normal file
304
renderers/vulkan/src/hardware.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
module vulkan_render;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
template <typename T> using iterator = std::ranges::borrowed_iterator_t<std::vector<T>&>;
|
||||||
|
|
||||||
|
vulkan_render::Context::Context(const char *appName)
|
||||||
|
: vulkan(MakeInstance(appName, {}, {})) {}
|
||||||
|
vulkan_render::Context::Context(const vk::InstanceCreateInfo &instanceInfo)
|
||||||
|
: vulkan(vk::raii::Instance(context, instanceInfo)) {}
|
||||||
|
|
||||||
|
vulkan_render::Context::Context(
|
||||||
|
const char *appName,
|
||||||
|
const std::vector<const char*> &extensions,
|
||||||
|
const std::vector<const char*> &layers
|
||||||
|
)
|
||||||
|
: vulkan(MakeInstance(appName, extensions, layers)) {}
|
||||||
|
|
||||||
|
void vulkan_render::Context::SetupPhysicalDevice(
|
||||||
|
const Surface &surface,
|
||||||
|
const PhysicalDevicePriorities &priorities,
|
||||||
|
vk::SurfaceFormatKHR& selectedFormat,
|
||||||
|
vk::PresentModeKHR& selectedPresentMode
|
||||||
|
) {
|
||||||
|
// Find physical devices
|
||||||
|
const std::vector<vk::raii::PhysicalDevice> devices = vulkan.enumeratePhysicalDevices();
|
||||||
|
if (devices.size() == 0) {
|
||||||
|
LOG(CRITICAL, "No devices with Vulkan support found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG(INFO, "Found %zu Vulkan compatible physical devices.", devices.size());
|
||||||
|
|
||||||
|
if (devices.empty())
|
||||||
|
{
|
||||||
|
LOG(CRITICAL, "No compatible physical devices found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter and select device
|
||||||
|
std::uint32_t bestPriority = 0;
|
||||||
|
for (const vk::raii::PhysicalDevice &physicalDeviceVariant : devices)
|
||||||
|
{
|
||||||
|
std::uint32_t priority = 1;
|
||||||
|
|
||||||
|
// Check device features and properties
|
||||||
|
physicalDeviceCapabilities.features = physicalDeviceVariant.getFeatures2<
|
||||||
|
PhysicalDeviceCapabilities::Features1,
|
||||||
|
PhysicalDeviceCapabilities::Features12,
|
||||||
|
PhysicalDeviceCapabilities::Features13
|
||||||
|
>();
|
||||||
|
|
||||||
|
//TODO: provide features list to check and enable
|
||||||
|
const PhysicalDeviceCapabilities::Features1 &features1 = physicalDeviceCapabilities.GetFeatures1();
|
||||||
|
const PhysicalDeviceCapabilities::Features13 &features13 = physicalDeviceCapabilities.GetFeatures13();
|
||||||
|
if (
|
||||||
|
features1.features.wideLines != vk::True ||
|
||||||
|
features1.features.independentBlend != vk::True ||
|
||||||
|
features1.features.multiDrawIndirect != vk::True ||
|
||||||
|
|
||||||
|
features13.dynamicRendering != vk::True ||
|
||||||
|
features13.synchronization2 != vk::True ||
|
||||||
|
features13.maintenance4 != vk::True
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalDeviceCapabilities::Features1 &enabledFeatures1 = physicalDeviceCapabilities.GetEnabledFeatures1();
|
||||||
|
PhysicalDeviceCapabilities::Features13 &enabledFeatures13 = physicalDeviceCapabilities.GetEnabledFeatures13();
|
||||||
|
enabledFeatures1.features.wideLines = vk::True;
|
||||||
|
enabledFeatures1.features.independentBlend = vk::True;
|
||||||
|
enabledFeatures1.features.multiDrawIndirect = vk::True;
|
||||||
|
enabledFeatures13.dynamicRendering = vk::True;
|
||||||
|
enabledFeatures13.synchronization2 = vk::True;
|
||||||
|
enabledFeatures13.maintenance4 = vk::True;
|
||||||
|
|
||||||
|
physicalDeviceCapabilities.properties = physicalDeviceVariant.getProperties();
|
||||||
|
if (physicalDeviceCapabilities.properties.apiVersion < vk::makeApiVersion(0, 1, 3, 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check queue families
|
||||||
|
std::vector<vk::QueueFamilyProperties> queueFamiliesProperties = physicalDeviceVariant.getQueueFamilyProperties();
|
||||||
|
|
||||||
|
std::uint32_t i = 0;
|
||||||
|
PhysicalDevice::QueueFamilies queueFamiliesBuffer;
|
||||||
|
for (const vk::QueueFamilyProperties &familyProps : queueFamiliesProperties)
|
||||||
|
{
|
||||||
|
if (familyProps.queueFlags & vk::QueueFlagBits::eGraphics)
|
||||||
|
queueFamiliesBuffer.graphics = i;
|
||||||
|
|
||||||
|
if (
|
||||||
|
queueFamiliesBuffer.graphics != queueFamiliesBuffer.present ||
|
||||||
|
queueFamiliesBuffer.graphics == PhysicalDevice::QueueFamilies::NO_VALUE
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (physicalDeviceVariant.getSurfaceSupportKHR(i, surface.handle))
|
||||||
|
queueFamiliesBuffer.present = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
queueFamiliesBuffer.graphics == PhysicalDevice::QueueFamilies::NO_VALUE ||
|
||||||
|
queueFamiliesBuffer.present == PhysicalDevice::QueueFamilies::NO_VALUE
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
queueFamiliesBuffer.transfer = queueFamiliesBuffer.graphics;
|
||||||
|
|
||||||
|
// Check surface info
|
||||||
|
SurfaceCapabilities surfaceCapabilitiesBuffer {};
|
||||||
|
|
||||||
|
surfaceCapabilitiesBuffer.availableFormats = physicalDeviceVariant.getSurfaceFormatsKHR(surface.handle);
|
||||||
|
surfaceCapabilitiesBuffer.availablePresentModes = physicalDeviceVariant.getSurfacePresentModesKHR(surface.handle);
|
||||||
|
|
||||||
|
if (
|
||||||
|
surfaceCapabilitiesBuffer.availableFormats.empty() ||
|
||||||
|
surfaceCapabilitiesBuffer.availablePresentModes.empty()
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Rate and select physical device
|
||||||
|
if (queueFamiliesBuffer.graphics == queueFamiliesBuffer.present)
|
||||||
|
priority += priorities.queueFamilies.PRESENT;
|
||||||
|
|
||||||
|
switch (physicalDeviceCapabilities.properties.deviceType) {
|
||||||
|
case vk::PhysicalDeviceType::eDiscreteGpu:
|
||||||
|
priority += priorities.gpu.DISCRETE;
|
||||||
|
break;
|
||||||
|
case vk::PhysicalDeviceType::eIntegratedGpu:
|
||||||
|
priority += priorities.gpu.INTEGRATED;
|
||||||
|
break;
|
||||||
|
case vk::PhysicalDeviceType::eVirtualGpu:
|
||||||
|
priority += priorities.gpu.VIRTUAL;
|
||||||
|
break;
|
||||||
|
case vk::PhysicalDeviceType::eCpu:
|
||||||
|
priority += priorities.gpu.CPU;
|
||||||
|
break;
|
||||||
|
case vk::PhysicalDeviceType::eOther:
|
||||||
|
priority += priorities.gpu.OTHER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::SurfaceFormatKHR formatBuffer {
|
||||||
|
.format = vk::Format::eB8G8R8A8Unorm,
|
||||||
|
.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear
|
||||||
|
};
|
||||||
|
for (const vk::SurfaceFormatKHR &format : surfaceCapabilitiesBuffer.availableFormats)
|
||||||
|
{
|
||||||
|
if (format.format == vk::Format::eB8G8R8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
||||||
|
{
|
||||||
|
formatBuffer = format;
|
||||||
|
priority += priorities.surfaceFormats.B8G8R8_SRGB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::PresentModeKHR presentModeBuffer = vk::PresentModeKHR::eFifo;
|
||||||
|
for (const vk::PresentModeKHR &presentMode : surfaceCapabilitiesBuffer.availablePresentModes)
|
||||||
|
{
|
||||||
|
if (presentMode == vk::PresentModeKHR::eFifoRelaxed)
|
||||||
|
{
|
||||||
|
presentModeBuffer = presentMode;
|
||||||
|
priority += priorities.presentModes.FIFO_RELAXED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priority > bestPriority)
|
||||||
|
{
|
||||||
|
bestPriority = priority;
|
||||||
|
physicalDevice.handle = physicalDeviceVariant;
|
||||||
|
physicalDevice.queueFamilies = queueFamiliesBuffer;
|
||||||
|
surfaceCapabilities = surfaceCapabilitiesBuffer;
|
||||||
|
selectedFormat = formatBuffer;
|
||||||
|
selectedPresentMode = presentModeBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
surfaceCapabilities.capabilities = physicalDevice.handle.getSurfaceCapabilitiesKHR(surface.handle);
|
||||||
|
|
||||||
|
LOG(INFO, "%hs is selected for render with %d priority score", physicalDeviceCapabilities.properties.deviceName, bestPriority);
|
||||||
|
|
||||||
|
physicalDevice.memory.properties = physicalDevice.handle.getMemoryProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::Context::SetupLogicalDevice(const std::vector<const char*> &extensions) {
|
||||||
|
// Define queues
|
||||||
|
std::vector<vk::DeviceQueueCreateInfo> queuesCreateInfos;
|
||||||
|
const std::set uniqueQueueFamilies {
|
||||||
|
physicalDevice.queueFamilies.transfer,
|
||||||
|
physicalDevice.queueFamilies.graphics,
|
||||||
|
physicalDevice.queueFamilies.present
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::uint32_t queueFamily : uniqueQueueFamilies)
|
||||||
|
{
|
||||||
|
vk::DeviceQueueCreateInfo queueCreateInfo {
|
||||||
|
.queueFamilyIndex = queueFamily,
|
||||||
|
.queueCount = 1,
|
||||||
|
.pQueuePriorities = &LogicalDevice::DEFAULT_QUEUE_PRIORITY
|
||||||
|
};
|
||||||
|
|
||||||
|
queuesCreateInfos.push_back(queueCreateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create logical device
|
||||||
|
const vk::DeviceCreateInfo deviceInfo {
|
||||||
|
.pNext = &physicalDeviceCapabilities.enabledFeatures.get(),
|
||||||
|
.queueCreateInfoCount = static_cast<std::uint32_t>(queuesCreateInfos.size()),
|
||||||
|
.pQueueCreateInfos = queuesCreateInfos.data(),
|
||||||
|
.enabledExtensionCount = static_cast<std::uint32_t>(extensions.size()),
|
||||||
|
.ppEnabledExtensionNames = extensions.data(),
|
||||||
|
};
|
||||||
|
device.handle = physicalDevice.handle.createDevice(deviceInfo);
|
||||||
|
LOG(INFO, "Logical device is created.");
|
||||||
|
|
||||||
|
// Save queues descriptor
|
||||||
|
device.transfer = device.handle.getQueue(physicalDevice.queueFamilies.transfer, 0);
|
||||||
|
device.graphics = device.handle.getQueue(physicalDevice.queueFamilies.graphics, 0);
|
||||||
|
device.present = device.handle.getQueue(physicalDevice.queueFamilies.present, 0);
|
||||||
|
LOG(INFO, "Queues registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uint32_t> vulkan_render::Context::FindMemoryType(const vk::MemoryRequirements &requirements, const vk::MemoryPropertyFlags &flags) {
|
||||||
|
std::optional<std::uint32_t> memoryType;
|
||||||
|
|
||||||
|
for (std::uint32_t i = 0; i < physicalDevice.memory.properties.memoryTypeCount; i++)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
requirements.memoryTypeBits & (1 << i) &&
|
||||||
|
(physicalDevice.memory.properties.memoryTypes[i].propertyFlags & flags) == flags
|
||||||
|
) {
|
||||||
|
memoryType = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!memoryType.has_value()) {
|
||||||
|
LOG(CRITICAL, "Failed to find GPU memory with requirements %d and flags %d", requirements.memoryTypeBits, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memoryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> vulkan_render::Context::AllocateMemory(
|
||||||
|
const vk::MemoryRequirements2 &memoryRequirements,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const {
|
||||||
|
std::optional<std::uint32_t> memoryTypeId;
|
||||||
|
const vk::PhysicalDeviceMemoryProperties &memoryProps = physicalDevice.memory.properties;
|
||||||
|
for (std::uint32_t i = 0; i < memoryProps.memoryTypeCount; i++) {
|
||||||
|
if (
|
||||||
|
memoryRequirements.memoryRequirements.memoryTypeBits & (1 << i) &&
|
||||||
|
(memoryProps.memoryTypes[i].propertyFlags & memoryFlags) == memoryFlags
|
||||||
|
) {
|
||||||
|
memoryTypeId = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!memoryTypeId.has_value()) {
|
||||||
|
LOG(CRITICAL, "Failed to find suitable GPU memory. Flags: %d", memoryFlags);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.handle.allocateMemory({
|
||||||
|
.allocationSize = memoryRequirements.memoryRequirements.size,
|
||||||
|
.memoryTypeIndex = memoryTypeId.value()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> vulkan_render::Context::AllocateBufferMemory(
|
||||||
|
const vk::raii::Buffer &buffer,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const {
|
||||||
|
return AllocateMemory(device.handle.getBufferMemoryRequirements2({.buffer = buffer}), memoryFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<vk::raii::DeviceMemory> vulkan_render::Context::AllocateImageMemory(
|
||||||
|
const vk::raii::Image &image,
|
||||||
|
const vk::MemoryPropertyFlags &memoryFlags
|
||||||
|
) const {
|
||||||
|
return AllocateMemory(device.handle.getImageMemoryRequirements2({.image = image}), memoryFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::Instance vulkan_render::Context::MakeInstance(
|
||||||
|
const char *appName,
|
||||||
|
const std::vector<const char*> &extensions,
|
||||||
|
const std::vector<const char*> &layers
|
||||||
|
) const {
|
||||||
|
const vk::ApplicationInfo appInfo {
|
||||||
|
.pApplicationName = appName,
|
||||||
|
.applicationVersion = vk::makeApiVersion(0, 1, 0, 0),
|
||||||
|
.pEngineName = "No Engine",
|
||||||
|
.engineVersion = vk::makeApiVersion(0, 1, 0, 0),
|
||||||
|
.apiVersion = vk::makeApiVersion(0, 1, 3, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::InstanceCreateInfo createInfo {
|
||||||
|
.pApplicationInfo = &appInfo,
|
||||||
|
.enabledLayerCount = static_cast<std::uint32_t>(layers.size()),
|
||||||
|
.ppEnabledLayerNames = layers.data(),
|
||||||
|
.enabledExtensionCount = static_cast<std::uint32_t>(extensions.size()),
|
||||||
|
.ppEnabledExtensionNames = extensions.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return vk::raii::Instance(context, createInfo);
|
||||||
|
}
|
||||||
361
renderers/vulkan/src/workflow.cpp
Normal file
361
renderers/vulkan/src/workflow.cpp
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
module vulkan_render;
|
||||||
|
import utils;
|
||||||
|
|
||||||
|
vulkan_render::Pipeline::Pipeline(
|
||||||
|
const Context &context,
|
||||||
|
const Surface &surface,
|
||||||
|
const Swapchain &swapchain,
|
||||||
|
const Texture<TextureMode::DEPTH> &depth,
|
||||||
|
const PipelineConfig &config,
|
||||||
|
const ShaderProgram &shaderProgram,
|
||||||
|
const std::uint32_t maxFramesInFlight
|
||||||
|
) : context(context),
|
||||||
|
surface(surface),
|
||||||
|
swapchain(swapchain),
|
||||||
|
maxFramesInFlight(maxFramesInFlight),
|
||||||
|
descriptorsLayout(MakeDescriptorsLayout(config)),
|
||||||
|
layout(MakeLayout(config)),
|
||||||
|
renderPass(MakerRenderPass()),
|
||||||
|
handle(MakeInstance(config, shaderProgram)),
|
||||||
|
viewport(config.defaultViewport),
|
||||||
|
scissors(config.defaultScissors),
|
||||||
|
descriptorPool(MakeDescriptorPool(config.descriptors)),
|
||||||
|
descriptorSets(MakeDescriptorSets()) {
|
||||||
|
MakeFramebuffers(depth);
|
||||||
|
LOG(INFO, "Pipeline created");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::Pipeline::UpdateDescriptors(
|
||||||
|
const std::vector<DescriptorInfo> &descriptors,
|
||||||
|
const std::uint32_t frame
|
||||||
|
) {
|
||||||
|
std::vector<vk::WriteDescriptorSet> writes;
|
||||||
|
writes.reserve(descriptors.size());
|
||||||
|
|
||||||
|
for (const auto &[info, bindingPosition, type] : descriptors) {
|
||||||
|
vk::WriteDescriptorSet write {
|
||||||
|
.dstSet = descriptorSets[frame],
|
||||||
|
.dstBinding = bindingPosition,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = type,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case vk::DescriptorType::eStorageBuffer:
|
||||||
|
case vk::DescriptorType::eUniformBuffer:
|
||||||
|
write.pBufferInfo = &std::get<vk::DescriptorBufferInfo>(info);
|
||||||
|
break;
|
||||||
|
case vk::DescriptorType::eCombinedImageSampler:
|
||||||
|
write.pImageInfo = &std::get<vk::DescriptorImageInfo>(info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
writes.push_back(write);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.device.handle.updateDescriptorSets(writes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkan_render::PipelineConfig vulkan_render::Pipeline::MakeConfig(
|
||||||
|
const Surface &surface,
|
||||||
|
const ShaderBindings &bindings,
|
||||||
|
const DescriptorBindings &descriptors,
|
||||||
|
const vk::PushConstantRange &pushConstants
|
||||||
|
) {
|
||||||
|
PipelineConfig config;
|
||||||
|
|
||||||
|
config.dynamicStates.push_back(vk::DynamicState::eViewport);
|
||||||
|
config.dynamicStates.push_back(vk::DynamicState::eScissor);
|
||||||
|
|
||||||
|
config.colorBlendAttachmentStates.push_back({
|
||||||
|
.blendEnable = vk::True,
|
||||||
|
.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha,
|
||||||
|
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
||||||
|
.colorBlendOp = vk::BlendOp::eAdd,
|
||||||
|
.srcAlphaBlendFactor = vk::BlendFactor::eOne,
|
||||||
|
.dstAlphaBlendFactor = vk::BlendFactor::eZero,
|
||||||
|
.alphaBlendOp = vk::BlendOp::eAdd,
|
||||||
|
.colorWriteMask = vk::Flags(vk::ColorComponentFlagBits::eR) | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB
|
||||||
|
});
|
||||||
|
|
||||||
|
config.descriptors = descriptors;
|
||||||
|
|
||||||
|
config.vertexInputInfo = {
|
||||||
|
.vertexBindingDescriptionCount = 1,
|
||||||
|
.pVertexBindingDescriptions = &bindings.vertexBinding,
|
||||||
|
.vertexAttributeDescriptionCount = bindings.vertexAttributesCount,
|
||||||
|
.pVertexAttributeDescriptions = &bindings.vertexAttributes
|
||||||
|
};
|
||||||
|
|
||||||
|
config.inputAssemblyInfo = {
|
||||||
|
.topology = vk::PrimitiveTopology::eTriangleList,
|
||||||
|
.primitiveRestartEnable = vk::False
|
||||||
|
};
|
||||||
|
|
||||||
|
config.rasterizerInfo = {
|
||||||
|
.depthClampEnable = vk::False,
|
||||||
|
.rasterizerDiscardEnable = vk::False,
|
||||||
|
.polygonMode = vk::PolygonMode::eFill,
|
||||||
|
.cullMode = vk::CullModeFlagBits::eNone, //TODO: BACK_BIT
|
||||||
|
.frontFace = vk::FrontFace::eCounterClockwise,
|
||||||
|
.depthBiasEnable = vk::False,
|
||||||
|
.lineWidth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.multisamplingInfo = {
|
||||||
|
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||||
|
.sampleShadingEnable = vk::False,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.depthStencilInfo = {
|
||||||
|
.depthTestEnable = vk::False,//TODO
|
||||||
|
.depthWriteEnable = vk::False,//TODO
|
||||||
|
.depthCompareOp = vk::CompareOp::eLess,
|
||||||
|
.depthBoundsTestEnable = vk::False,
|
||||||
|
.stencilTestEnable = vk::False,
|
||||||
|
.front = {},
|
||||||
|
.back = {},
|
||||||
|
.minDepthBounds = 0.0f,
|
||||||
|
.maxDepthBounds = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.dynamicStateInfo = {
|
||||||
|
.dynamicStateCount = static_cast<std::uint32_t>(config.dynamicStates.size()),
|
||||||
|
.pDynamicStates = config.dynamicStates.data()
|
||||||
|
};
|
||||||
|
|
||||||
|
config.descriptorsInfo = {
|
||||||
|
.bindingCount = static_cast<std::uint32_t>(config.descriptors.size()),
|
||||||
|
.pBindings = config.descriptors.data()
|
||||||
|
};
|
||||||
|
|
||||||
|
config.pushConstantRange = pushConstants;
|
||||||
|
|
||||||
|
config.defaultViewport = {
|
||||||
|
.x = 0.0f,
|
||||||
|
.y = 0.0f,
|
||||||
|
.width = static_cast<float>(surface.extent.width),
|
||||||
|
.height = static_cast<float>(surface.extent.height),
|
||||||
|
.minDepth = 0.0f,
|
||||||
|
.maxDepth = 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
config.defaultScissors = {
|
||||||
|
.offset = {.x = 0, .y = 0},
|
||||||
|
.extent = surface.extent
|
||||||
|
};
|
||||||
|
|
||||||
|
config.viewportStateInfo = {
|
||||||
|
.viewportCount = 1,
|
||||||
|
.pViewports = &config.defaultViewport,
|
||||||
|
.scissorCount = 1,
|
||||||
|
.pScissors = &config.defaultScissors
|
||||||
|
};
|
||||||
|
|
||||||
|
config.colorBlendInfo = {
|
||||||
|
.logicOpEnable = vk::False,
|
||||||
|
.attachmentCount = static_cast<std::uint32_t>(config.colorBlendAttachmentStates.size()),
|
||||||
|
.pAttachments = config.colorBlendAttachmentStates.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::DescriptorSetLayout vulkan_render::Pipeline::MakeDescriptorsLayout(const PipelineConfig &config) const {
|
||||||
|
return context.device.handle.createDescriptorSetLayout(config.descriptorsInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::PipelineLayout vulkan_render::Pipeline::MakeLayout(const PipelineConfig &config) const {
|
||||||
|
const vk::PipelineLayoutCreateInfo layoutInfo {
|
||||||
|
.setLayoutCount = 1,
|
||||||
|
.pSetLayouts = &*descriptorsLayout,
|
||||||
|
.pushConstantRangeCount = 1,
|
||||||
|
.pPushConstantRanges = &config.pushConstantRange
|
||||||
|
};
|
||||||
|
|
||||||
|
return context.device.handle.createPipelineLayout(layoutInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::Pipeline vulkan_render::Pipeline::MakeInstance(
|
||||||
|
const PipelineConfig &config,
|
||||||
|
const ShaderProgram &shaderProgram
|
||||||
|
) const {
|
||||||
|
// Configure shaders
|
||||||
|
std::vector<vk::PipelineShaderStageCreateInfo> stagesInfo;
|
||||||
|
stagesInfo.reserve(3);
|
||||||
|
|
||||||
|
// Vertex
|
||||||
|
vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
|
||||||
|
.codeSize = shaderProgram.vertex.size(),
|
||||||
|
.pCode = reinterpret_cast<const std::uint32_t*>(shaderProgram.vertex.data())
|
||||||
|
};
|
||||||
|
const vk::raii::ShaderModule vertexModule = context.device.handle.createShaderModule(shaderModuleCreateInfo);
|
||||||
|
stagesInfo.push_back({
|
||||||
|
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vertexModule,
|
||||||
|
.pName = shaderProgram.vertexMainFun,
|
||||||
|
.pSpecializationInfo = nullptr
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fragment
|
||||||
|
shaderModuleCreateInfo = {
|
||||||
|
.codeSize = shaderProgram.fragment.size(),
|
||||||
|
.pCode = reinterpret_cast<const std::uint32_t*>(shaderProgram.fragment.data())
|
||||||
|
};
|
||||||
|
const vk::raii::ShaderModule fragmentModule = context.device.handle.createShaderModule(shaderModuleCreateInfo);
|
||||||
|
stagesInfo.push_back({
|
||||||
|
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fragmentModule,
|
||||||
|
.pName = shaderProgram.fragmentMainFun,
|
||||||
|
.pSpecializationInfo = nullptr
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
vk::StructureChain<vk::GraphicsPipelineCreateInfo, vk::PipelineRenderingCreateInfo> pipelineInfo(
|
||||||
|
{
|
||||||
|
.stageCount = static_cast<std::uint32_t>(stagesInfo.size()),
|
||||||
|
.pStages = stagesInfo.data(),
|
||||||
|
.pVertexInputState = &config.vertexInputInfo,
|
||||||
|
.pInputAssemblyState = &config.inputAssemblyInfo,
|
||||||
|
.pViewportState = &config.viewportStateInfo,
|
||||||
|
.pRasterizationState = &config.rasterizerInfo,
|
||||||
|
.pMultisampleState = &config.multisamplingInfo,
|
||||||
|
.pDepthStencilState = &config.depthStencilInfo,
|
||||||
|
.pColorBlendState = &config.colorBlendInfo,
|
||||||
|
.pDynamicState = &config.dynamicStateInfo,
|
||||||
|
.layout = layout,
|
||||||
|
.renderPass = renderPass,
|
||||||
|
.basePipelineHandle = nullptr,
|
||||||
|
.basePipelineIndex = -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachmentFormats = &surface.format.format,
|
||||||
|
.depthAttachmentFormat = vk::Format::eD32SfloatS8Uint
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return context.device.handle.createGraphicsPipeline(nullptr, pipelineInfo.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::DescriptorPool vulkan_render::Pipeline::MakeDescriptorPool(const DescriptorBindings &descriptors) {
|
||||||
|
descriptorCounts.clear();
|
||||||
|
for (const vk::DescriptorSetLayoutBinding &binding : descriptors) {
|
||||||
|
descriptorCounts[binding.descriptorType] += binding.descriptorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<vk::DescriptorPoolSize> poolSizes;
|
||||||
|
poolSizes.reserve(descriptorCounts.size());
|
||||||
|
for (const auto &[type, count] : descriptorCounts) {
|
||||||
|
poolSizes.push_back({
|
||||||
|
.type = type,
|
||||||
|
.descriptorCount = count * maxFramesInFlight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.device.handle.createDescriptorPool({
|
||||||
|
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
|
||||||
|
.maxSets = static_cast<std::uint32_t>(maxFramesInFlight),
|
||||||
|
.poolSizeCount = static_cast<std::uint32_t>(poolSizes.size()),
|
||||||
|
.pPoolSizes = poolSizes.data()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::DescriptorSets vulkan_render::Pipeline::MakeDescriptorSets() const {
|
||||||
|
std::vector layouts(maxFramesInFlight, *descriptorsLayout);
|
||||||
|
|
||||||
|
return vk::raii::DescriptorSets(
|
||||||
|
context.device.handle,
|
||||||
|
{
|
||||||
|
.descriptorPool = descriptorPool,
|
||||||
|
.descriptorSetCount = maxFramesInFlight,
|
||||||
|
.pSetLayouts = layouts.data()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::raii::RenderPass vulkan_render::Pipeline::MakerRenderPass() const {
|
||||||
|
const vk::AttachmentDescription colorAttachmentDescription {
|
||||||
|
.format = surface.format.format,
|
||||||
|
.samples = vk::SampleCountFlagBits::e1, // No multisampling
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||||
|
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
||||||
|
.initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.finalLayout = vk::ImageLayout::ePresentSrcKHR
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::AttachmentReference colorAttachmentRef {
|
||||||
|
.attachment = 0,
|
||||||
|
.layout = vk::ImageLayout::eColorAttachmentOptimal
|
||||||
|
};
|
||||||
|
|
||||||
|
const vk::AttachmentDescription depthAttachmentDescription {
|
||||||
|
.format = vk::Format::eD32SfloatS8Uint,
|
||||||
|
.samples = vk::SampleCountFlagBits::e1,
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eDontCare,
|
||||||
|
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||||
|
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare ,
|
||||||
|
.initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::AttachmentReference depthAttachmentRef {
|
||||||
|
.attachment = 1,
|
||||||
|
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::SubpassDescription subpass {
|
||||||
|
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachments = &colorAttachmentRef,
|
||||||
|
.pDepthStencilAttachment = &depthAttachmentRef
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::SubpassDependency dependency {
|
||||||
|
.srcSubpass = vk::SubpassExternal,
|
||||||
|
.dstSubpass = 0,
|
||||||
|
.srcStageMask = vk::Flags(vk::PipelineStageFlagBits::eColorAttachmentOutput) | vk::PipelineStageFlagBits::eLateFragmentTests,
|
||||||
|
.dstStageMask = vk::Flags(vk::PipelineStageFlagBits::eColorAttachmentOutput) | vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||||
|
.dstAccessMask = vk::Flags(vk::AccessFlagBits::eColorAttachmentWrite) | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array attachments = {colorAttachmentDescription, depthAttachmentDescription};
|
||||||
|
const vk::RenderPassCreateInfo baseRenderPassInfo {
|
||||||
|
.attachmentCount = static_cast<std::uint32_t>(attachments.size()),
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.subpassCount = 1,
|
||||||
|
.pSubpasses = &subpass,
|
||||||
|
.dependencyCount = 1,
|
||||||
|
.pDependencies = &dependency
|
||||||
|
};
|
||||||
|
|
||||||
|
return context.device.handle.createRenderPass(baseRenderPassInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkan_render::Pipeline::MakeFramebuffers(const Texture<TextureMode::DEPTH> &depth) {
|
||||||
|
framebuffers.reserve(swapchain.frameViews.size());
|
||||||
|
for (const vk::raii::ImageView &frameView : swapchain.frameViews)
|
||||||
|
{
|
||||||
|
std::array attachments = {*frameView, *depth.view};
|
||||||
|
|
||||||
|
vk::FramebufferCreateInfo frameBufferInfo {
|
||||||
|
.renderPass = renderPass,
|
||||||
|
.attachmentCount = static_cast<std::uint32_t>(attachments.size()),
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.width = surface.extent.width,
|
||||||
|
.height = surface.extent.height,
|
||||||
|
.layers = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
framebuffers.emplace_back(context.device.handle.createFramebuffer(frameBufferInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
4
sdk/assets/shaders/compile.cmd
Normal file
4
sdk/assets/shaders/compile.cmd
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
glslc provinces/provinces.frag -O -o ../../../assets/shaders/provinces.frag
|
||||||
|
glslc provinces/provinces.vert -O -o ../../../assets/shaders/provinces.vert
|
||||||
10
sdk/assets/shaders/provinces/provinces.frag
Normal file
10
sdk/assets/shaders/provinces/provinces.frag
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 fragColor;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = vec4(fragColor, 1.0);
|
||||||
|
}
|
||||||
35
sdk/assets/shaders/provinces/provinces.vert
Normal file
35
sdk/assets/shaders/provinces/provinces.vert
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform Camera {
|
||||||
|
mat4 viewProjection;
|
||||||
|
} camera;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Model {
|
||||||
|
mat4 model;
|
||||||
|
} model;
|
||||||
|
|
||||||
|
struct Province {
|
||||||
|
uint regionId;
|
||||||
|
uint color;
|
||||||
|
};
|
||||||
|
layout(std430, binding = 1) readonly buffer Data {
|
||||||
|
Province provinces[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 fragColor;
|
||||||
|
|
||||||
|
vec3 unpackColor(uint c) {
|
||||||
|
return vec3(
|
||||||
|
c & 0xFFu,
|
||||||
|
(c >> 8) & 0xFFu,
|
||||||
|
(c >> 16) & 0xFFu
|
||||||
|
) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = camera.viewProjection * vec4(position, 0.0, 1.0);
|
||||||
|
fragColor = unpackColor(provinces[gl_InstanceIndex].color);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user