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/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" 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/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/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/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/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_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_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/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/=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" />
|
||||
|
||||
@ -19,9 +19,14 @@ project (EtrediaEmpires LANGUAGES CXX)
|
||||
|
||||
add_subdirectory(sdk/map)
|
||||
add_subdirectory(sdk/assets)
|
||||
|
||||
add_subdirectory(p7lib)
|
||||
add_subdirectory(utils)
|
||||
|
||||
add_subdirectory(server/lib)
|
||||
add_subdirectory(engine-api)
|
||||
|
||||
add_subdirectory(renderers/magnum-renderer)
|
||||
add_subdirectory(renderers/vulkan)
|
||||
|
||||
add_subdirectory(engine)
|
||||
Binary file not shown.
Binary file not shown.
@ -1,4 +1,12 @@
|
||||
function(add_library_target TARGET_NAME)
|
||||
cmake_parse_arguments(
|
||||
ADDITIONAL
|
||||
""
|
||||
""
|
||||
"MODULES;MODULE_BASEDIRS"
|
||||
""
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@ -22,23 +30,14 @@ function(add_library_target TARGET_NAME)
|
||||
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} 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
|
||||
# target_compile_options(${TARGET_NAME} PRIVATE
|
||||
# -fmodules
|
||||
|
||||
@ -62,8 +62,8 @@ export namespace ee_engine {
|
||||
void SetCamera(const CameraTransform& camera);
|
||||
|
||||
constexpr CameraTransform getCamera() const noexcept { return camera; };
|
||||
glm::vec2 ScreenToWorld(const float screenx, const float screenY);
|
||||
glm::vec2 ScreenToWorld(const glm::vec2& screenPos);
|
||||
glm::vec2 ScreenToWorld(float screenX, float screenY) const;
|
||||
glm::vec2 ScreenToWorld(const glm::vec2& screenPos) const;
|
||||
|
||||
protected:
|
||||
bool _isReady;
|
||||
@ -77,12 +77,12 @@ export namespace ee_engine {
|
||||
MapMesh *mapMesh;
|
||||
ShaderRefs shaders;
|
||||
|
||||
struct RegionIndecies {
|
||||
std::size_t startIndex;
|
||||
struct RegionRanges {
|
||||
std::size_t offset;
|
||||
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);
|
||||
|
||||
@ -92,7 +92,7 @@ export namespace ee_engine {
|
||||
|
||||
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:
|
||||
void UpdateRegionCmd(RendererCommand& command);
|
||||
|
||||
@ -31,7 +31,7 @@ void ee_engine::IRenderer::Draw() {
|
||||
DrawUI();
|
||||
}
|
||||
|
||||
void ee_engine::IRenderer::ResizeWindow(int width, int height) {
|
||||
void ee_engine::IRenderer::ResizeWindow(const int width, const int height) {
|
||||
screenWidth = width;
|
||||
screenHeight = height;
|
||||
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
|
||||
@ -45,15 +45,15 @@ void ee_engine::IRenderer::SetCamera(const CameraTransform& camera) {
|
||||
|
||||
this->camera = camera;
|
||||
|
||||
float halfHeight = camera.zoom;
|
||||
float halfWidth = camera.zoom * screenAspect;
|
||||
const float halfHeight = camera.zoom;
|
||||
const float halfWidth = camera.zoom * screenAspect;
|
||||
|
||||
glm::mat4 P = glm::ortho(
|
||||
const glm::mat4 P = glm::ortho(
|
||||
-halfWidth, +halfWidth,
|
||||
-halfHeight, +halfHeight
|
||||
);
|
||||
|
||||
glm::mat4 V = glm::translate(
|
||||
const glm::mat4 V = glm::translate(
|
||||
glm::mat4(1.0f),
|
||||
glm::vec3(-camera.position, 0.0f)
|
||||
);
|
||||
@ -64,11 +64,11 @@ void ee_engine::IRenderer::SetCamera(const CameraTransform& camera) {
|
||||
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));
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
void ee_engine::IRenderer::UpdateRegionCmd(ee_engine::RendererCommand& command) {
|
||||
void ee_engine::IRenderer::UpdateRegionCmd(RendererCommand& command) {
|
||||
if (!regionsMapping.contains(command.region.id)) {
|
||||
LOG(WARNING, "Updating unregistered region with id: %d", command.region.id);
|
||||
return;
|
||||
@ -90,14 +90,14 @@ void ee_engine::IRenderer::UpdateRegionCmd(ee_engine::RendererCommand& command)
|
||||
command.region.id, command.region.data.regionId);
|
||||
}
|
||||
|
||||
RegionIndecies mapping = regionsMapping[command.region.id];
|
||||
RegionRanges mapping = regionsMapping[command.region.id];
|
||||
std::vector<ProvinceSSBO> buffer;
|
||||
buffer.reserve(mapping.provincesCount);
|
||||
for (std::size_t i = 0; i < mapping.provincesCount; i++) {
|
||||
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 engine-api)
|
||||
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 xxHash::xxhash)
|
||||
|
||||
@ -10,17 +10,17 @@ ee_engine::InputHandlers Actions::mappings{
|
||||
.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();
|
||||
cam.zoom *= pow(1.1f, -dy);
|
||||
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();
|
||||
|
||||
glm::vec2 pos = Engine::renderer->ScreenToWorld(x, y);
|
||||
glm::vec2 lastPos = Engine::renderer->ScreenToWorld(lastX, lastY);
|
||||
const glm::vec2 pos = Engine::renderer->ScreenToWorld(x, y);
|
||||
const glm::vec2 lastPos = Engine::renderer->ScreenToWorld(lastX, lastY);
|
||||
cam.position -= pos - lastPos;
|
||||
|
||||
Engine::renderer->SetCamera(cam);
|
||||
|
||||
@ -5,7 +5,7 @@ import utils;
|
||||
|
||||
import Engine;
|
||||
import Actions;
|
||||
import MagnumRender;
|
||||
import VulkanRender;
|
||||
import GLFWInput;
|
||||
import std;
|
||||
|
||||
@ -20,7 +20,7 @@ int main(int argc, char **argv) {
|
||||
glfwInit();
|
||||
|
||||
// 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);
|
||||
|
||||
@ -38,16 +38,17 @@ int main(int argc, char **argv) {
|
||||
Engine::assetManager->LoadDir(ee_assets::MATERIAL, "assets/materials");
|
||||
|
||||
// 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");
|
||||
|
||||
// Setup input manager TODO: builder
|
||||
Engine::input = Allocate<GLFWInput>(Actions::mappings);
|
||||
|
||||
// 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);
|
||||
|
||||
//TODO: ShaderRefs builder from set of AssetMaterial
|
||||
@ -64,19 +65,20 @@ int main(int argc, char **argv) {
|
||||
|
||||
Engine::renderer->Init();
|
||||
Engine::renderer->SetCamera({
|
||||
//.position = {1000.0f, 1000.0f},
|
||||
.position = {1000.0f, 1000.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);
|
||||
});
|
||||
|
||||
// Setup Client TODO: builder
|
||||
Engine::client = Allocate<Client>();
|
||||
// AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save");
|
||||
// Engine::client->LoadSave(save);
|
||||
// Engine::client->Init();
|
||||
const AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save");
|
||||
Engine::client->LoadSave(save);
|
||||
Engine::client->Init();
|
||||
Engine::client->Start();
|
||||
|
||||
// Game loop
|
||||
@ -99,5 +101,4 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Use CRTP where possible in classes with virtual/override
|
||||
// TODO: Leaks detection
|
||||
|
||||
@ -18,8 +18,8 @@ using namespace Magnum;
|
||||
export namespace ee_magnum {
|
||||
class MagnumRenderer final : public ee_engine::IRenderer {
|
||||
public:
|
||||
MagnumRenderer(GLFWwindow *window);
|
||||
~MagnumRenderer();
|
||||
explicit MagnumRenderer(GLFWwindow *window);
|
||||
~MagnumRenderer() override;
|
||||
|
||||
void Init() override;
|
||||
void Shutdown() override;
|
||||
@ -31,8 +31,7 @@ export namespace ee_magnum {
|
||||
|
||||
void ApplyCamera() override;
|
||||
|
||||
virtual void
|
||||
UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO>& data) override;
|
||||
void UpdateProvinces(std::size_t offset, const std::vector<ee_engine::ProvinceSSBO>& data) override;
|
||||
|
||||
private:
|
||||
Platform::GLContext context;
|
||||
@ -44,7 +43,5 @@ export namespace ee_magnum {
|
||||
void ValidateSupport();
|
||||
void LoadShaders();
|
||||
void LoadProvinces();
|
||||
|
||||
void ApplyMapMVP();
|
||||
};
|
||||
}
|
||||
@ -95,7 +95,7 @@ void ee_magnum::MagnumRenderer::LoadProvinces() {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -107,10 +107,10 @@ void ee_magnum::MagnumRenderer::LoadProvinces() {
|
||||
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) {
|
||||
Containers::ArrayView provincesData(data.data(), data.size());
|
||||
provinceSSBO.setSubData(index, provincesData);
|
||||
provinceSSBO.setSubData(offset, provincesData);
|
||||
}
|
||||
|
||||
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