We be kinda rendering

This commit is contained in:
2026-01-05 06:20:49 +01:00
parent 1168f9e5d1
commit c83c423b42
48 changed files with 2789 additions and 382 deletions

View File

@@ -0,0 +1,82 @@
# Usage:
# compile_slang_shaders(
# TARGET compile_shaders # custom target name to create
# SRC_DIR "${CMAKE_SOURCE_DIR}/assets_src/shaders"
# OUT_DIR "${CMAKE_SOURCE_DIR}/assets_runtime/shaders"
# # Optional:
# GLOB_PATTERN "*.slang" # default: "*.slang"
# EXTRA_ARGS -O3 # forwarded to slangc
# ALL # if present: target is built by default
# )
#
function(compile_slang_shaders)
set(options ALL)
set(oneValueArgs TARGET SRC_DIR OUT_DIR GLOB_PATTERN)
set(multiValueArgs EXTRA_ARGS)
cmake_parse_arguments(CSS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT CSS_TARGET)
message(FATAL_ERROR "compile_slang_shaders: TARGET is required")
endif()
if(NOT CSS_SRC_DIR)
message(FATAL_ERROR "compile_slang_shaders: SRC_DIR is required")
endif()
if(NOT CSS_OUT_DIR)
message(FATAL_ERROR "compile_slang_shaders: OUT_DIR is required")
endif()
if(NOT CSS_GLOB_PATTERN)
set(CSS_GLOB_PATTERN "*.slang")
endif()
find_program(SLANGC_EXE NAMES slangc slangc.exe REQUIRED)
# Collect shader sources recursively
file(GLOB_RECURSE _SLANG_SOURCES
CONFIGURE_DEPENDS
"${CSS_SRC_DIR}/${CSS_GLOB_PATTERN}"
)
if(_SLANG_SOURCES STREQUAL "")
message(STATUS "compile_slang_shaders: No sources found in ${CSS_SRC_DIR} (pattern: ${CSS_GLOB_PATTERN})")
endif()
# Ensure base output directory exists at configure time (nice-to-have)
file(MAKE_DIRECTORY "${CSS_OUT_DIR}")
set(_SPV_OUTPUTS "")
foreach(_SLANG_FILE IN LISTS _SLANG_SOURCES)
file(RELATIVE_PATH _REL_PATH "${CSS_SRC_DIR}" "${_SLANG_FILE}")
get_filename_component(_REL_DIR "${_REL_PATH}" DIRECTORY)
get_filename_component(_FILE_WE "${_SLANG_FILE}" NAME_WE)
set(_OUT_SUBDIR "${CSS_OUT_DIR}/${_REL_DIR}")
set(_OUT_SPV "${_OUT_SUBDIR}/${_FILE_WE}.spv")
add_custom_command(
OUTPUT "${_OUT_SPV}"
COMMAND "${CMAKE_COMMAND}" -E make_directory "${_OUT_SUBDIR}"
COMMAND "${SLANGC_EXE}"
"${_SLANG_FILE}"
-target spirv
-o "${_OUT_SPV}"
${CSS_EXTRA_ARGS}
DEPENDS "${_SLANG_FILE}"
COMMENT "Compiling Slang: ${_REL_PATH} -> ${_OUT_SPV}"
VERBATIM
)
list(APPEND _SPV_OUTPUTS "${_OUT_SPV}")
endforeach()
# Create target
if(CSS_ALL)
add_custom_target(${CSS_TARGET} ALL DEPENDS ${_SPV_OUTPUTS})
else()
add_custom_target(${CSS_TARGET} DEPENDS ${_SPV_OUTPUTS})
endif()
# Export outputs to caller if they want them (optional convenience)
set(${CSS_TARGET}_OUTPUTS "${_SPV_OUTPUTS}" PARENT_SCOPE)
endfunction()

View File

@@ -1,18 +1,27 @@
add_subdirectory(third_party) add_subdirectory(third_party)
set(SRC_FILES set(SRC_FILES
"src/App.cpp" "src/App.cpp"
"src/Graphics/GfxDevice.cpp" "src/Graphics/Camera.cpp"
"src/Graphics/ImmediateExecuter.cpp" "src/Graphics/GfxDevice.cpp"
"src/Graphics/Swapchain.cpp" "src/Graphics/ImageCache.cpp"
"src/Graphics/VImage.cpp" "src/Graphics/ImageLoader.cpp"
"src/Graphics/Init.cpp" "src/Graphics/ImmediateExecuter.cpp"
"src/Graphics/Util.cpp" "src/Graphics/Init.cpp"
"src/graphics/Pipeline.cpp" "src/Graphics/MeshCache.cpp"
"src/graphics/MeshCache.cpp" "src/Graphics/Pipeline.cpp"
"src/Graphics/Renderer.cpp"
"src/Graphics/Swapchain.cpp"
"src/Graphics/Util.cpp"
"src/FS/AssetFS.cpp" "src/Graphics/Resources/GPUImage.cpp"
"src/Graphics/Resources/NBuffer.cpp"
"src/Graphics/Pipelines/MeshPipeline.cpp"
"src/FS/AssetFS.cpp"
) )
add_library(destrum ${SRC_FILES}) add_library(destrum ${SRC_FILES})
@@ -20,8 +29,8 @@ add_library(destrum ${SRC_FILES})
add_library(destrum::destrum ALIAS destrum) add_library(destrum::destrum ALIAS destrum)
set_target_properties(destrum PROPERTIES set_target_properties(destrum PROPERTIES
CXX_STANDARD 20 CXX_STANDARD 20
CXX_EXTENSIONS OFF CXX_EXTENSIONS OFF
) )
#target_add_extra_warnings(destrum) #target_add_extra_warnings(destrum)
@@ -37,9 +46,9 @@ target_link_libraries(destrum
glm::glm glm::glm
nlohmann_json::nlohmann_json nlohmann_json::nlohmann_json
spdlog::spdlog spdlog::spdlog
stb::image
PRIVATE PRIVATE
stb::image
freetype::freetype freetype::freetype
) )
@@ -50,17 +59,17 @@ target_compile_definitions(destrum
# VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason # VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason
) )
if(WIN32) if (WIN32)
if(BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
target_link_libraries(destrum target_link_libraries(destrum
PUBLIC SDL2::SDL2main SDL2::SDL2 PUBLIC SDL2::SDL2main SDL2::SDL2
) )
else() else ()
target_link_libraries(destrum target_link_libraries(destrum
PUBLIC SDL2::SDL2main SDL2::SDL2-static PUBLIC SDL2::SDL2main SDL2::SDL2-static
) )
endif() endif ()
endif() endif ()
target_compile_definitions(destrum target_compile_definitions(destrum
PUBLIC PUBLIC
@@ -77,4 +86,14 @@ set(DESTRUM_SHADER_OUT "${CMAKE_CURRENT_LIST_DIR}/assets_runtime/shaders")
include(../cmake/compile_shaders.cmake) include(../cmake/compile_shaders.cmake)
compile_glsl_to_spv(destrum "${DESTRUM_SHADER_SRC}" "${DESTRUM_SHADER_OUT}" DESTRUM_SPV) compile_glsl_to_spv(destrum "${DESTRUM_SHADER_SRC}" "${DESTRUM_SHADER_OUT}" DESTRUM_SPV)
add_dependencies(destrum destrum_shaders) add_dependencies(destrum destrum_shaders)
#
#include(../cmake/compile_slang_shaders.cmake)
#
#compile_slang_shaders(
# TARGET compile_shaders
# SRC_DIR "${CMAKE_SOURCE_DIR}/destrum/assets_src/shaders"
# OUT_DIR "${CMAKE_SOURCE_DIR}/destrum/assets_runtime/shaders"
# EXTRA_ARGS -O3
# ALL
#)

View File

@@ -0,0 +1,35 @@
#extension GL_EXT_nonuniform_qualifier : enable
layout (set = 0, binding = 0) uniform texture2D textures[];
layout (set = 0, binding = 0) uniform texture2DMS texturesMS[];
layout (set = 0, binding = 0) uniform textureCube textureCubes[];
layout (set = 0, binding = 0) uniform texture2DArray textureArrays[];
layout (set = 0, binding = 1) uniform sampler samplers[];
#define NEAREST_SAMPLER_ID 0
#define LINEAR_SAMPLER_ID 1
#define SHADOW_SAMPLER_ID 2
vec4 sampleTexture2DNearest(uint texID, vec2 uv) {
return texture(nonuniformEXT(sampler2D(textures[texID], samplers[NEAREST_SAMPLER_ID])), uv);
}
vec4 sampleTexture2DMSNearest(uint texID, ivec2 p, int s) {
return texelFetch(nonuniformEXT(sampler2DMS(texturesMS[texID], samplers[NEAREST_SAMPLER_ID])), p, s);
}
vec4 sampleTexture2DLinear(uint texID, vec2 uv) {
return texture(nonuniformEXT(sampler2D(textures[texID], samplers[LINEAR_SAMPLER_ID])), uv);
}
vec4 sampleTextureCubeNearest(uint texID, vec3 p) {
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[NEAREST_SAMPLER_ID])), p);
}
vec4 sampleTextureCubeLinear(uint texID, vec3 p) {
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[LINEAR_SAMPLER_ID])), p);
}
float sampleTextureArrayShadow(uint texID, vec4 p) {
return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p);
}

View File

@@ -0,0 +1,19 @@
#ifndef MATERIALS_GLSL
#define MATERIALS_GLSL
#extension GL_EXT_buffer_reference : require
struct MaterialData {
vec4 baseColor;
vec4 metallicRoughnessEmissive;
uint diffuseTex;
uint normalTex;
uint metallicRoughnessTex;
uint emissiveTex;
};
layout (buffer_reference, std430) readonly buffer MaterialsBuffer {
MaterialData data[];
} materialsBuffer;
#endif // MATERIALS_GLSL

View File

@@ -0,0 +1,125 @@
#version 460
#extension GL_GOOGLE_include_directive : require
#include "bindless.glsl"
#include "scene_data.glsl"
#include "mesh_pcs.glsl"
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUV;
layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec4 inTangent;
layout (location = 4) in mat3 inTBN;
layout (location = 0) out vec4 outFragColor;
void main()
{
MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
// vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV);
// if (diffuse.a < 0.1) {
// discard;
// }
//
// vec3 baseColor = material.baseColor.rgb * diffuse.rgb;
//
// vec3 normal = normalize(inNormal).rgb;
// if (inTangent != vec4(0.0)) {
// // FIXME: sometimes Blender doesn't export tangents for some objects
// // for some reason. When we will start computing tangents manually,
// // this check can be removed
// normal = sampleTexture2DLinear(material.normalTex, inUV).rgb;
// // normal.y = 1 - normal.y; // flip to make OpenGL normal maps work
// normal = inTBN * normalize(normal * 2.0 - 1.0);
// normal = normalize(normal);
// }
//
// #ifdef PBR
// float metallicF = material.metallicRoughnessEmissive.r;
// float roughnessF = material.metallicRoughnessEmissive.g;
//
// vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
//
// float roughness = roughnessF * metallicRoughness.g;
// // roughness *= roughness; // from perceptual to linear
// roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
//
// float metallic = metallicF * metallicRoughness.b;
//
// vec3 dielectricSpecular = vec3(0.04);
// vec3 black = vec3(0.0);
// vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
// vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
// #else
// vec3 diffuseColor = baseColor;
// float metallic = 0.f;
// float roughness = 1.f;
// vec3 f0 = vec3(0.0);
// #endif
//
// vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
// vec3 fragPos = inPos.xyz;
// vec3 n = normal;
// vec3 v = normalize(cameraPos - fragPos);
//
// // diffuseColor = vec3(1.0);
// // baseColor = vec3(1.0);
//
// vec3 fragColor = vec3(0.0);
// for (int i = 0; i < pcs.sceneData.numLights; i++) {
// Light light = pcs.sceneData.lights.data[i];
//
// vec3 l = light.direction;
// if (light.type != TYPE_DIRECTIONAL_LIGHT) {
// l = normalize(light.position - fragPos);
// }
// float NoL = clamp(dot(n, l), 0.0, 1.0);
//
// float occlusion = 1.0;
// if (light.type == TYPE_DIRECTIONAL_LIGHT) {
// occlusion = calculateCSMOcclusion(
// fragPos, cameraPos, NoL,
// pcs.sceneData.csmShadowMapId,
// pcs.sceneData.cascadeFarPlaneZs,
// pcs.sceneData.csmLightSpaceTMs);
// } else if (light.type == TYPE_POINT_LIGHT && light.shadowMapID != 0) {
// occlusion = calculatePointShadow(
// fragPos, light.position, NoL,
// light.shadowMapID,
// pcs.sceneData.pointLightFarPlane);
// }
//
// fragColor += calculateLight(light, fragPos, n, v, l,
// diffuseColor, roughness, metallic, f0, occlusion);
// }
//
// // emissive
// float emissiveF = material.metallicRoughnessEmissive.b;
// vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
// fragColor += emissiveColor;
//
// // ambient
// fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
//
// #if 0
// // CSM DEBUG
// uint cascadeIndex = chooseCascade(inPos, cameraPos, pcs.sceneData.cascadeFarPlaneZs);
// fragColor *= debugShadowsFactor(cascadeIndex);
// #endif
//
// #if 0
// // TANGENT DEBUG
// if (inTangent == vec4(0.0)) {
// fragColor = vec3(1.0f, 0.0f, 0.0f);
// }
// #endif
//
// #if 1
// // NORMAL DEBUG
// // fragColor = normal;
// #endif
outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}

View File

@@ -0,0 +1,34 @@
#version 460
#extension GL_GOOGLE_include_directive : require
#include "mesh_pcs.glsl"
layout (location = 0) out vec3 outPos;
layout (location = 1) out vec2 outUV;
layout (location = 2) out vec3 outNormal;
layout (location = 3) out vec4 outTangent;
layout (location = 4) out mat3 outTBN;
void main()
{
Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex];
vec4 worldPos = pcs.transform * vec4(v.position, 1.0f);
gl_Position = pcs.sceneData.viewProj * worldPos;
outPos = worldPos.xyz;
outUV = vec2(v.uv_x, v.uv_y);
// A bit inefficient, but okay - this is needed for non-uniform scale
// models. See: http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/
// Simpler case, when everything is uniform
// outNormal = (pcs.transform * vec4(v.normal, 0.0)).xyz;
outNormal = mat3(transpose(inverse(pcs.transform))) * v.normal;
outTangent = v.tangent;
vec3 T = normalize(vec3(pcs.transform * v.tangent));
vec3 N = normalize(outNormal);
vec3 B = cross(N, T) * v.tangent.w;
outTBN = mat3(T, B, N);
}

View File

@@ -0,0 +1,15 @@
#extension GL_EXT_buffer_reference : require
#extension GL_EXT_scalar_block_layout: require
#include "scene_data.glsl"
#include "vertex.glsl"
layout (push_constant, scalar) uniform constants
{
mat4 transform;
SceneDataBuffer sceneData;
VertexBuffer vertexBuffer;
uint materialID;
uint padding;
} pcs;

View File

@@ -0,0 +1,28 @@
#ifndef SCENE_DATA_GLSL
#define SCENE_DATA_GLSL
#extension GL_EXT_scalar_block_layout: require
#extension GL_EXT_buffer_reference : require
#include "materials.glsl"
layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
// camera
mat4 view;
mat4 proj;
mat4 viewProj;
vec4 cameraPos;
// ambient
vec3 ambientColor;
float ambientIntensity;
// fog
vec3 fogColor;
float fogDensity;
MaterialsBuffer materials;
} sceneDataBuffer;
#endif // SCENE_DATA_GLSL

View File

@@ -0,0 +1,19 @@
#ifndef VERTEX_GLSL
#define VERTEX_GLSL
#extension GL_EXT_buffer_reference : require
struct Vertex {
vec3 position;
float uv_x;
vec3 normal;
float uv_y;
vec4 tangent;
};
layout (buffer_reference, std430) readonly buffer VertexBuffer {
Vertex vertices[];
};
#endif // VERTEX_GLSL

View File

@@ -8,6 +8,7 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <destrum/Graphics/GfxDevice.h> #include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/Renderer.h>
class App { class App {
@@ -20,6 +21,8 @@ public:
std::filesystem::path exeDir; std::filesystem::path exeDir;
}; };
App();
void init(const AppParams& params); void init(const AppParams& params);
void run(); void run();
void cleanup(); void cleanup();
@@ -28,7 +31,15 @@ protected:
SDL_Window* window{nullptr}; SDL_Window* window{nullptr};
AppParams m_params{}; AppParams m_params{};
CPUMesh testMesh{};
MeshID testMeshID;
GfxDevice gfxDevice; GfxDevice gfxDevice;
GameRenderer renderer;
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
MeshCache meshCache;
bool isRunning{false}; bool isRunning{false};
bool gamePaused{false}; bool gamePaused{false};

View File

@@ -0,0 +1,158 @@
#ifndef VCAMERA_H
#define VCAMERA_H
#include <glm/glm.hpp>
class Camera {
public:
struct Frustum {
glm::vec4 planes[6]; // left, right, bottom, top, near, far
void update(const glm::mat4& vpMatrix) {
// Extract frustum planes from VP matrix (row-major, GLM style)
glm::mat4 m = vpMatrix;
// Left
planes[0] = glm::vec4(
m[0][3] + m[0][0],
m[1][3] + m[1][0],
m[2][3] + m[2][0],
m[3][3] + m[3][0]
);
// Right
planes[1] = glm::vec4(
m[0][3] - m[0][0],
m[1][3] - m[1][0],
m[2][3] - m[2][0],
m[3][3] - m[3][0]
);
// Bottom
planes[2] = glm::vec4(
m[0][3] + m[0][1],
m[1][3] + m[1][1],
m[2][3] + m[2][1],
m[3][3] + m[3][1]
);
// Top
planes[3] = glm::vec4(
m[0][3] - m[0][1],
m[1][3] - m[1][1],
m[2][3] - m[2][1],
m[3][3] - m[3][1]
);
// Near
planes[4] = glm::vec4(
m[0][3] + m[0][2],
m[1][3] + m[1][2],
m[2][3] + m[2][2],
m[3][3] + m[3][2]
);
// Far
planes[5] = glm::vec4(
m[0][3] - m[0][2],
m[1][3] - m[1][2],
m[2][3] - m[2][2],
m[3][3] - m[3][2]
);
// Normalize all planes
for (auto& plane: planes) {
float length = glm::length(glm::vec3(plane));
plane /= length;
}
}
// Fast AABB-Frustum test
// [[nodiscard]] bool isBoxVisible(const AABB& box) const {
// for (const auto& plane: planes) {
// glm::vec3 positive = box.min;
// if (plane.x >= 0) positive.x = box.max.x;
// if (plane.y >= 0) positive.y = box.max.y;
// if (plane.z >= 0) positive.z = box.max.z;
//
// if (glm::dot(glm::vec3(plane), positive) + plane.w < 0) {
// return false;
// }
// }
// return true;
// }
};
explicit Camera(const glm::vec3& position, const glm::vec3& up);
void Update(float deltaTime);
void CalculateViewMatrix();
void CalculateProjectionMatrix();
[[nodiscard]] glm::mat4 GetViewMatrix() const { return m_viewMatrix; }
[[nodiscard]] glm::mat4 GetProjectionMatrix() const { return m_projectionMatrix; }
[[nodiscard]] glm::mat4 GetViewProjectionMatrix() const { return m_projectionMatrix * m_viewMatrix; }
void setAspectRatio(float aspectRatio) {
m_aspectRatio = aspectRatio;
this->CalculateViewMatrix();
this->CalculateProjectionMatrix();
}
glm::vec3 m_position{2.f, 0, 0};
void Translate(const glm::vec3& translation) {
m_position += translation;
}
void SetTarget(const glm::vec3& target);
void ClearTarget();
void Target(const glm::vec3& target);
[[nodiscard]] glm::vec3 GetTarget() const { return m_target; }
[[nodiscard]] bool IsTargetting() const { return m_useTarget; }
[[nodiscard]] const Frustum& GetFrustum() const { return m_frustum; }
[[nodiscard]] glm::vec3 GetPosition() const { return m_position; }
[[nodiscard]] glm::vec3 GetForward() const { return m_forward; }
[[nodiscard]] glm::vec3 GetUp() const { return m_up; }
[[nodiscard]] glm::vec3 GetRight() const { return m_right; }
void SetMovementSpeed(float speed) { m_movementSpeed = speed; }
[[nodiscard]] float GetMovementSpeed() const { return m_movementSpeed; }
private:
float fovAngle{90.f};
float fov{tanf(glm::radians(fovAngle) / 2.f)};
glm::vec3 m_forward{1, 0, 0};
glm::vec3 m_up{0, 1, 0};
glm::vec3 m_right{0, 0, 1};
glm::vec3 m_target{0.0f, 0.0f, 0.0f};
bool m_useTarget{false};
float totalPitch{0.0f};
float totalYaw{0.0f};
glm::mat4 m_viewMatrix{};
glm::mat4 m_invMatrix{};
glm::mat4 m_projectionMatrix{};
float m_aspectRatio{};
float m_cameraSpeed{};
float m_cameraSensitivity{};
float m_zNear{0.001f};
float m_zFar{1000.0f};
float m_iso{100.f};
float m_aperture{2.8f};
float m_shutterSpeed{1.f / 60.f};
float m_scrollSpeed = 2.0f;
Frustum m_frustum{};
float m_movementSpeed{2.0f};
};
#endif //VCAMERA_H

View File

@@ -1,5 +1,5 @@
#ifndef VIMAGE_H #ifndef GPUIMAGE_H
#define VIMAGE_H #define GPUIMAGE_H
#include <cstdint> #include <cstdint>
#include <string> #include <string>
@@ -7,29 +7,9 @@
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
class VImage { #include <destrum/Graphics/ids.h>
public:
glm::ivec2 getSize2D() const { return glm::ivec2{extent.width, extent.height}; }
VkExtent2D getExtent2D() const { return VkExtent2D{extent.width, extent.height}; }
std::uint32_t getBindlessId() const struct GPUImage {
{
assert(id != NULL_BINDLESS_ID && "Image wasn't added to bindless set");
return id;
}
void setBindlessId(const std::uint32_t id)
{
assert(id != NULL_BINDLESS_ID);
this->id = id;
}
bool isInitialized() const { return id != NULL_BINDLESS_ID; }
VkImage getImage() const { return image; }
VkImageView getImageView() const { return imageView; }
VmaAllocation getAllocation() const { return allocation; }
private:
VkImage image; VkImage image;
VkImageView imageView; VkImageView imageView;
VmaAllocation allocation; VmaAllocation allocation;
@@ -38,11 +18,29 @@ private:
VkExtent3D extent; VkExtent3D extent;
std::uint32_t mipLevels{1}; std::uint32_t mipLevels{1};
std::uint32_t numLayers{1}; std::uint32_t numLayers{1};
bool isCubemap{false};
std::string debugName{}; std::string debugName{};
[[nodiscard]] glm::ivec2 getSize2D() const { return glm::ivec2{extent.width, extent.height}; }
[[nodiscard]] VkExtent2D getExtent2D() const { return VkExtent2D{extent.width, extent.height}; }
[[nodiscard]] BindlessID getBindlessId() const
{
assert(id != NULL_BINDLESS_ID && "Image wasn't added to bindless set");
return id;
}
// should be called by ImageCache only
void setBindlessId(const std::uint32_t id)
{
assert(id != NULL_BINDLESS_ID);
this->id = id;
}
[[nodiscard]] bool isInitialized() const { return id != NULL_BINDLESS_ID; }
private:
std::uint32_t id{NULL_BINDLESS_ID}; // bindless id - always equals to ImageId std::uint32_t id{NULL_BINDLESS_ID}; // bindless id - always equals to ImageId
static const auto NULL_BINDLESS_ID = std::numeric_limits<std::uint32_t>::max();
}; };
#endif //VIMAGE_H #endif //GPUIMAGE_H

View File

@@ -6,20 +6,25 @@
#include <SDL.h> #include <SDL.h>
#include <destrum/Graphics/VImage.h> #include <destrum/Graphics/GPUImage.h>
#include <destrum/Graphics/Swapchain.h> #include <destrum/Graphics/Swapchain.h>
#include <destrum/Graphics/ImageCache.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <volk.h> #include <volk.h>
#include <VkBootstrap.h> #include <VkBootstrap.h>
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
#include <filesystem>
#include <optional>
#include "ImmediateExecuter.h" #include "ImmediateExecuter.h"
#include <destrum/Graphics/Swapchain.h> #include <destrum/Graphics/Swapchain.h>
using ImageId = std::uint32_t; #include <destrum/Graphics/Resources/Buffer.h>
static const auto NULL_IMAGE_ID = std::numeric_limits<std::uint32_t>::max(); #include <destrum/Graphics/ids.h>
#include "Util.h"
namespace { namespace {
using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>; using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>;
@@ -40,14 +45,14 @@ public:
void recreateSwapchain(int width, int height); void recreateSwapchain(int width, int height);
VkCommandBuffer beginFrame(); VkCommandBuffer beginFrame();
bool needsSwapchainRecreate() const { return swapchain.isDirty(); } [[nodiscard]] bool needsSwapchainRecreate() const { return swapchain.isDirty(); }
struct EndFrameProps { struct EndFrameProps {
const VkClearColorValue clearColor{ {0.f, 0.f, 0.f, 1.f} }; const VkClearColorValue clearColor{ {0.f, 0.f, 0.f, 1.f} };
glm::ivec4 drawImageBlitRect{}; // where to blit draw image to glm::ivec4 drawImageBlitRect{}; // where to blit draw image to
}; };
void endFrame(VkCommandBuffer cmd, const VImage& drawImage, const EndFrameProps& props); void endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props);
void cleanup(); void cleanup();
void waitIdle(); void waitIdle();
@@ -67,6 +72,29 @@ public:
VkExtent2D getSwapchainExtent() const { return swapchain.getExtent(); } VkExtent2D getSwapchainExtent() const { return swapchain.getExtent(); }
[[nodiscard]] GPUBuffer createBuffer(
std::size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO) const;
[[nodiscard]] VkDeviceAddress getBufferAddress(const GPUBuffer& buffer) const;
void destroyBuffer(const GPUBuffer& buffer) const;
VmaAllocator getAllocator() const { return allocator; }
ImageID createImage(const vkutil::CreateImageInfo& createInfo, const std::string& debugName = "", void* pixelData = nullptr, ImageID imageId = NULL_IMAGE_ID);
ImageID createDrawImage(VkFormat format, glm::ivec2 size, const std::string& debugName = "", ImageID imageId = NULL_IMAGE_ID);
ImageID loadImageFromFile(const std::filesystem::path& path, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, bool mipMap = false);
ImageID addImageToCache(GPUImage image);
const GPUImage& getImage(ImageID id) const;
void uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer = 0) const;
GPUImage createImageRaw(const vkutil::CreateImageInfo& createInfo, std::optional<VmaAllocationCreateInfo> customAllocationCreateInfo = std::nullopt) const;
GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkFormat format, VkImageUsageFlags usage, bool mipMap) const;
void destroyImage(const GPUImage& image) const;
private: private:
vkb::Instance instance; vkb::Instance instance;
@@ -86,8 +114,11 @@ private:
VulkanImmediateExecutor executor; VulkanImmediateExecutor executor;
ImageId whiteImageId{NULL_IMAGE_ID}; ImageID whiteImageId{NULL_IMAGE_ID};
ImageId errorImageId{NULL_IMAGE_ID}; ImageID errorImageId{NULL_IMAGE_ID};
ImageCache imageCache;
}; };

View File

@@ -0,0 +1,56 @@
#ifndef IMAGECACHE_H
#define IMAGECACHE_H
#pragma once
#include <filesystem>
#include <unordered_map>
#include <vector>
#include <destrum/Graphics/ids.h>
#include <destrum/Graphics/GPUImage.h>
// #include <destrum/Graphics/Vulkan/BindlessSetManager.h>
class GfxDevice;
class ImageCache {
friend class ResourcesInspector;
public:
ImageCache(GfxDevice& gfxDevice);
ImageID loadImageFromFile(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap);
ImageID addImage(GPUImage image);
ImageID addImage(ImageID id, GPUImage image);
const GPUImage& getImage(ImageID id) const;
ImageID getFreeImageId() const;
void destroyImages();
// BindlessSetManager bindlessSetManager;
void setErrorImageId(ImageID id) { errorImageId = id; }
private:
std::vector<GPUImage> images;
GfxDevice& gfxDevice;
struct LoadedImageInfo {
std::filesystem::path path;
VkFormat format;
VkImageUsageFlags usage;
bool mipMap;
};
std::unordered_map<ImageID, LoadedImageInfo> loadedImagesInfo;
ImageID errorImageId{NULL_IMAGE_ID};
};
#endif //IMAGECACHE_H

View File

@@ -0,0 +1,30 @@
#ifndef MATERIAL_H
#define MATERIAL_H
#include <string>
#include <glm/glm.hpp>
struct MaterialData {
glm::vec3 baseColor;
glm::vec4 metalRoughnessEmissive;
std::uint32_t diffuseTex;
std::uint32_t normalTex;
std::uint32_t metallicRoughnessTex;
std::uint32_t emissiveTex;
};
struct Material {
glm::vec3 baseColor{1.f, 1.f, 1.f};
float metallicFactor{0.f};
float roughnessFactor{0.7f};
float emissiveFactor{0.f};
ImageID diffuseTexture{NULL_IMAGE_ID};
// ImageId normalMapTexture{NULL_IMAGE_ID};
// ImageId metallicRoughnessTexture{NULL_IMAGE_ID};
// ImageId emissiveTexture{NULL_IMAGE_ID};
std::string name;
};
#endif //MATERIAL_H

View File

@@ -0,0 +1,42 @@
#ifndef MATERIALCACHE_H
#define MATERIALCACHE_H
#include <vector>
#include <destrum/Graphics/ids.h>
#include <destrum/Graphics/Material.h>
#include <destrum/Graphics/Resources/Buffer.h>
class GfxDevice;
class MaterialCache {
friend class ResourcesInspector;
public:
void init(GfxDevice& gfxDevice);
void cleanup(GfxDevice& gfxDevice);
MaterialId addMaterial(GfxDevice& gfxDevice, Material material);
const Material& getMaterial(MaterialId id) const;
MaterialId getFreeMaterialId() const;
MaterialId getPlaceholderMaterialId() const;
const GPUBuffer& getMaterialDataBuffer() const { return materialDataBuffer; }
VkDeviceAddress getMaterialDataBufferAddress() const { return materialDataBuffer.address; }
private:
std::vector<Material> materials;
static const auto MAX_MATERIALS = 1000;
GPUBuffer materialDataBuffer;
// material which is used for meshes without materials
MaterialId placeholderMaterialId{NULL_MATERIAL_ID};
ImageID defaultNormalMapTextureID{NULL_IMAGE_ID};
};
#endif //MATERIALCACHE_H

View File

@@ -1,4 +1,23 @@
#ifndef MESHCACHE_H #ifndef MESHCACHE_H
#define MESHCACHE_H #define MESHCACHE_H
#include <destrum/Graphics/Resources/Mesh.h>
#include "ids.h"
class GfxDevice;
class MeshCache {
public:
void cleanup(const GfxDevice& gfxDevice);
MeshID addMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh);
const GPUMesh& getMesh(MeshID id) const;
private:
void uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh& gpuMesh) const;
std::vector<GPUMesh> meshes;
};
#endif //MESHCACHE_H #endif //MESHCACHE_H

View File

@@ -0,0 +1,27 @@
#ifndef MESHDRAWCOMMAND_H
#define MESHDRAWCOMMAND_H
#include <glm/mat4x4.hpp>
#include <destrum/Graphics/ids.h>
struct MeshDrawCommand {
MeshID meshId;
glm::mat4 transformMatrix;
// for frustum culling
// math::Sphere worldBoundingSphere;
// If set - mesh will be drawn with overrideMaterialId
// instead of whatever material the mesh has
MaterialId materialId{NULL_MATERIAL_ID};
bool castShadow{true};
// skinned meshes only
// const SkinnedMesh* skinnedMesh{nullptr};
// std::uint32_t jointMatricesStartIndex;
};
#endif //MESHDRAWCOMMAND_H

View File

@@ -5,13 +5,27 @@
#include <memory> #include <memory>
#include <destrum/Graphics/Pipeline.h> #include <destrum/Graphics/Pipeline.h>
#include <destrum/Graphics/MeshCache.h>
#include <destrum/Graphics/MaterialCache.h>
#include <destrum/Graphics/Camera.h>
#include <destrum/Graphics/Resources/Buffer.h>
#include <destrum/Graphics/MeshDrawCommand.h>
class MeshPipeline { class MeshPipeline {
public: public:
MeshPipeline(); MeshPipeline();
~MeshPipeline(); ~MeshPipeline();
void init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat); void init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat);
void draw(); void draw(VkCommandBuffer cmd,
VkExtent2D renderExtent,
const GfxDevice& gfxDevice,
const MeshCache& meshCache,
const MaterialCache& materialCache,
const Camera& camera,
const GPUBuffer& sceneDataBuffer,
const std::vector<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& sortedDrawCommands);
private: private:
@@ -19,6 +33,14 @@ private:
std::unique_ptr<Pipeline> m_pipeline; std::unique_ptr<Pipeline> m_pipeline;
struct PushConstants {
glm::mat4 transform;
VkDeviceAddress sceneDataBuffer;
VkDeviceAddress vertexBuffer;
std::uint32_t materialId;
std::uint32_t padding;
};
}; };
#endif //MESHPIPELINE_H #endif //MESHPIPELINE_H

View File

@@ -1,10 +1,82 @@
#ifndef RENDERER_H #ifndef RENDERER_H
#define RENDERER_H #define RENDERER_H
#include <glm/vec3.hpp>
#include <destrum/Graphics/Camera.h>
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
#include <destrum/Graphics/MeshCache.h>
#include <destrum/Graphics/ids.h>
#include <destrum/Graphics/MeshDrawCommand.h>
#include <destrum/Graphics/Resources/NBuffer.h>
#include <destrum/Graphics/MaterialCache.h>
#include <memory>
class GameRenderer { class GameRenderer {
public: public:
struct SceneData {
const Camera& camera;
glm::vec3 ambientColor;
float ambientIntensity;
glm::vec3 fogColor;
float fogDensity;
};
explicit GameRenderer(MeshCache& meshCache);
void init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize);
void beginDrawing(GfxDevice& gfxDevice);
void endDrawing();
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
void cleanup();
void drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId);
private: private:
void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate);
MeshCache& meshCache;
// MaterialCache& materialCache;
std::vector<MeshDrawCommand> meshDrawCommands;
std::vector<std::size_t> sortedMeshDrawCommands;
VkFormat drawImageFormat{VK_FORMAT_R16G16B16A16_SFLOAT};
VkFormat depthImageFormat{VK_FORMAT_D32_SFLOAT};
ImageID drawImageId{NULL_IMAGE_ID};
ImageID depthImageId{NULL_IMAGE_ID};
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
struct GPUSceneData {
// camera
glm::mat4 view;
glm::mat4 proj;
glm::mat4 viewProj;
glm::vec4 cameraPos;
// ambient
glm::vec3 ambientColor;
float ambientIntensity;
// fog
glm::vec3 fogColor;
float fogDensity;
VkDeviceAddress materialsBuffer;
};
NBuffer sceneDataBuffer;
MaterialCache materialCache;
std::unique_ptr<MeshPipeline> meshPipeline;
}; };
#endif //RENDERER_H #endif //RENDERER_H

View File

@@ -9,6 +9,8 @@
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include <glm/vec4.hpp> #include <glm/vec4.hpp>
#include <destrum/Graphics/Resources/Buffer.h>
struct CPUMesh { struct CPUMesh {
std::vector<std::uint32_t> indices; std::vector<std::uint32_t> indices;
@@ -38,15 +40,66 @@ struct GPUMesh {
// AABB // AABB
glm::vec3 minPos; glm::vec3 minPos;
glm::vec3 maxPos; glm::vec3 maxPos;
math::Sphere boundingSphere; // math::Sphere boundingSphere;
bool hasSkeleton{false};
// skinned meshes only
GPUBuffer skinningDataBuffer;
}; };
struct SkinnedMesh { static std::vector<CPUMesh::Vertex> vertices = {
GPUBuffer skinnedVertexBuffer; // =======================
// +Z (Front)
// =======================
{{-0.5f, -0.5f, 0.5f}, 0.0f, {0, 0, 1}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, -0.5f, 0.5f}, 1.0f, {0, 0, 1}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, 0.5f, 0.5f}, 1.0f, {0, 0, 1}, 1.0f, {1, 0, 0, 1}},
{{-0.5f, 0.5f, 0.5f}, 0.0f, {0, 0, 1}, 1.0f, {1, 0, 0, 1}},
// =======================
// -Z (Back)
// =======================
{{ 0.5f, -0.5f, -0.5f}, 0.0f, {0, 0, -1}, 0.0f, {-1, 0, 0, 1}},
{{-0.5f, -0.5f, -0.5f}, 1.0f, {0, 0, -1}, 0.0f, {-1, 0, 0, 1}},
{{-0.5f, 0.5f, -0.5f}, 1.0f, {0, 0, -1}, 1.0f, {-1, 0, 0, 1}},
{{ 0.5f, 0.5f, -0.5f}, 0.0f, {0, 0, -1}, 1.0f, {-1, 0, 0, 1}},
// =======================
// +X (Right)
// =======================
{{ 0.5f, -0.5f, 0.5f}, 0.0f, {1, 0, 0}, 0.0f, {0, 0, -1, 1}},
{{ 0.5f, -0.5f, -0.5f}, 1.0f, {1, 0, 0}, 0.0f, {0, 0, -1, 1}},
{{ 0.5f, 0.5f, -0.5f}, 1.0f, {1, 0, 0}, 1.0f, {0, 0, -1, 1}},
{{ 0.5f, 0.5f, 0.5f}, 0.0f, {1, 0, 0}, 1.0f, {0, 0, -1, 1}},
// =======================
// -X (Left)
// =======================
{{-0.5f, -0.5f, -0.5f}, 0.0f, {-1, 0, 0}, 0.0f, {0, 0, 1, 1}},
{{-0.5f, -0.5f, 0.5f}, 1.0f, {-1, 0, 0}, 0.0f, {0, 0, 1, 1}},
{{-0.5f, 0.5f, 0.5f}, 1.0f, {-1, 0, 0}, 1.0f, {0, 0, 1, 1}},
{{-0.5f, 0.5f, -0.5f}, 0.0f, {-1, 0, 0}, 1.0f, {0, 0, 1, 1}},
// =======================
// +Y (Top)
// =======================
{{-0.5f, 0.5f, 0.5f}, 0.0f, {0, 1, 0}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, 0.5f, 0.5f}, 1.0f, {0, 1, 0}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, 0.5f, -0.5f}, 1.0f, {0, 1, 0}, 1.0f, {1, 0, 0, 1}},
{{-0.5f, 0.5f, -0.5f}, 0.0f, {0, 1, 0}, 1.0f, {1, 0, 0, 1}},
// =======================
// -Y (Bottom)
// =======================
{{-0.5f, -0.5f, -0.5f}, 0.0f, {0, -1, 0}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, -0.5f, -0.5f}, 1.0f, {0, -1, 0}, 0.0f, {1, 0, 0, 1}},
{{ 0.5f, -0.5f, 0.5f}, 1.0f, {0, -1, 0}, 1.0f, {1, 0, 0, 1}},
{{-0.5f, -0.5f, 0.5f}, 0.0f, {0, -1, 0}, 1.0f, {1, 0, 0, 1}},
};
static std::vector<uint32_t> indices = {
0, 1, 2, 2, 3, 0, // Front
4, 5, 6, 6, 7, 4, // Back
8, 9,10, 10,11, 8, // Right
12,13,14, 14,15,12, // Left
16,17,18, 18,19,16, // Top
20,21,22, 22,23,20 // Bottom
}; };

View File

@@ -0,0 +1,39 @@
#ifndef NBUFFER_H
#define NBUFFER_H
#include <string>
#include <vector>
#include <destrum/Graphics/Resources/Buffer.h>
class GfxDevice;
class NBuffer {
public:
void init(
GfxDevice& gfxDevice,
VkBufferUsageFlags usage,
std::size_t dataSize,
const std::string& debugName = "");
void cleanup(GfxDevice& gfxDevice);
void uploadNewData(
VkCommandBuffer cmd,
std::size_t frameIndex,
void* newData,
std::size_t dataSize,
std::size_t offset = 0,
bool sync = true) const;
const GPUBuffer& getBuffer() const { return gpuBuffer; }
private:
std::size_t framesInFlight{0};
std::size_t gpuBufferSize{0};
std::vector<GPUBuffer> stagingBuffers;
GPUBuffer gpuBuffer;
bool initialized{false};
};
#endif //NBUFFER_H

View File

@@ -1,8 +1,11 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
#include <filesystem>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include "glm/vec4.hpp"
#define VK_CHECK(call) \ #define VK_CHECK(call) \
do { \ do { \
@@ -11,6 +14,18 @@
} while (0) } while (0)
namespace vkutil { namespace vkutil {
struct CreateImageInfo {
VkFormat format;
VkImageUsageFlags usage;
VkImageCreateFlags flags;
VkExtent3D extent{};
std::uint32_t numLayers{1};
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
VkImageTiling tiling{VK_IMAGE_TILING_OPTIMAL};
bool mipMap{false};
bool isCubemap{false};
};
void transitionImage( void transitionImage(
VkCommandBuffer cmd, VkCommandBuffer cmd,
VkImage image, VkImage image,
@@ -35,5 +50,32 @@ namespace vkutil {
int destW, int destW,
int destH, int destH,
VkFilter filter); VkFilter filter);
void addDebugLabel(VkDevice device, VkImage image, const char* label);
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label);
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label);
void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label);
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label);
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label);
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label);
struct RenderingInfoParams {
VkExtent2D renderExtent;
VkImageView colorImageView{VK_NULL_HANDLE};
std::optional<glm::vec4> colorImageClearValue;
VkImageView depthImageView{VK_NULL_HANDLE};
std::optional<float> depthImageClearValue;
};
struct RenderInfo {
VkRenderingAttachmentInfo colorAttachment;
VkRenderingAttachmentInfo depthAttachment;
VkRenderingInfo renderingInfo;
};
RenderInfo createRenderingInfo(const RenderingInfoParams& params);
VkShaderModule loadShaderModule(const std::filesystem::path& path, VkDevice device);
} }
#endif //UTIL_H #endif //UTIL_H

View File

@@ -0,0 +1,19 @@
#ifndef IDS_H
#define IDS_H
#include <cstdint>
#include <limits>
using MeshID = std::size_t;
constexpr MeshID NULL_MESH_ID = std::numeric_limits<std::size_t>::max();
using ImageID = std::uint16_t;
constexpr ImageID NULL_IMAGE_ID = std::numeric_limits<std::uint16_t>::max();
using MaterialId = std::uint32_t;
constexpr MaterialId NULL_MATERIAL_ID = std::numeric_limits<std::uint32_t>::max();
using BindlessID = std::uint32_t;
constexpr BindlessID NULL_BINDLESS_ID = std::numeric_limits<std::uint32_t>::max();
#endif //IDS_H

View File

@@ -0,0 +1,36 @@
#ifndef IMAGELOADER_H
#define IMAGELOADER_H
#include <filesystem>
struct ImageData {
ImageData() = default;
~ImageData();
// move only
ImageData(ImageData&& o) = default;
ImageData& operator=(ImageData&& o) = default;
// no copies
ImageData(const ImageData& o) = delete;
ImageData& operator=(const ImageData& o) = delete;
// data
unsigned char* pixels{nullptr};
int width{0};
int height{0};
int channels{0};
// HDR only
float* hdrPixels{nullptr};
bool hdr{false};
int comp{0};
bool shouldSTBFree{false};
};
namespace util {
ImageData loadImage(const std::filesystem::path& p);
}
#endif //IMAGELOADER_H

View File

@@ -6,6 +6,9 @@
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
App::App(): renderer{meshCache} {
}
void App::init(const AppParams& params) { void App::init(const AppParams& params) {
m_params = params; m_params = params;
@@ -30,11 +33,22 @@ void App::init(const AppParams& params) {
} }
gfxDevice.init(window, params.appName, false); gfxDevice.init(window, params.appName, false);
renderer.init(gfxDevice, params.renderSize);
//Read whole file //Read whole file
auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt"); auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt");
std::string fileStr(file.begin(), file.end()); std::string fileStr(file.begin(), file.end());
spdlog::info("Read from assetfstest.txt: {}", fileStr); spdlog::info("Read from assetfstest.txt: {}", fileStr);
testMesh.name = "Test Mesh";
testMesh.vertices = vertices;
testMesh.indices = indices;
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
float aspectRatio = static_cast<float>(params.renderSize.x) / static_cast<float>(params.renderSize.y);
camera.setAspectRatio(aspectRatio);
} }
void App::run() { void App::run() {
@@ -97,8 +111,16 @@ void App::run() {
} }
if (!gfxDevice.needsSwapchainRecreate()) { if (!gfxDevice.needsSwapchainRecreate()) {
renderer.beginDrawing(gfxDevice);
renderer.drawMesh(testMeshID, glm::mat4(1.f), 0);
renderer.endDrawing();
auto cmd = gfxDevice.beginFrame(); auto cmd = gfxDevice.beginFrame();
gfxDevice.endFrame(cmd, VImage{}, {}); renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{
camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f
});
gfxDevice.endFrame(cmd, GPUImage{}, {});
} }
if (frameLimit) { if (frameLimit) {
// Delay to not overload the CPU // Delay to not overload the CPU

View File

@@ -0,0 +1,195 @@
#include <destrum/Graphics/Camera.h>
#include <iostream>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
Camera::Camera(const glm::vec3& position, const glm::vec3& up): m_position{position}, m_up{up} {
}
void Camera::Update(float deltaTime) {
// const auto MyWindow = static_cast<Window*>(glfwGetWindowUserPointer(Window::gWindow));
//
// #pragma region Keyboard Movement
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
//
// float MovementSpeed = m_movementSpeed;
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) {
// MovementSpeed *= 2.0f;
// }
//
// if (glfwGetKey(Window::gWindow, GLFW_KEY_W) == GLFW_PRESS) {
// m_position += m_forward * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_A) == GLFW_PRESS) {
// m_position -= m_right * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_S) == GLFW_PRESS) {
// m_position -= m_forward * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_D) == GLFW_PRESS) {
// m_position += m_right * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_E) == GLFW_PRESS) {
// m_position += m_up * deltaTime * MovementSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_Q) == GLFW_PRESS) {
// m_position -= m_up * deltaTime * MovementSpeed;
// }
//
// // Looking around with arrow keys
// constexpr float lookSpeed = 1.0f * glm::radians(1.f);
// if (glfwGetKey(Window::gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
// totalPitch += lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
// totalPitch -= lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
// totalYaw -= lookSpeed;
// }
// if (glfwGetKey(Window::gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
// totalYaw += lookSpeed;
// }
//
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>(), glm::half_pi<float>());
//
// const glm::mat4 yawMatrix = glm::rotate(glm::mat4(1.0f), -totalYaw, glm::vec3(0, 1, 0));
// const glm::mat4 pitchMatrix = glm::rotate(glm::mat4(1.0f), totalPitch, glm::vec3(0, 0, 1));
//
// const glm::mat4 rotationMatrix = yawMatrix * pitchMatrix;
//
// m_forward = glm::vec3(rotationMatrix * glm::vec4(1, 0, 0, 0));
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
// m_up = glm::normalize(glm::cross(m_right, m_forward));
// #pragma endregion
//
// #pragma region Mouse Looking
// static bool wasCursorLockedLastFrame = false;
// static double lastMouseX = 0.0;
// static double lastMouseY = 0.0;
// static bool firstMouse = true;
//
// bool isLocked = MyWindow->isCursorLocked();
// if (isLocked) {
// double mouseX, mouseY;
// glfwGetCursorPos(Window::gWindow, &mouseX, &mouseY);
//
// // Reset mouse reference when locking the cursor (prevents jump)
// if (!wasCursorLockedLastFrame) {
// lastMouseX = mouseX;
// lastMouseY = mouseY;
// firstMouse = false;
// }
//
// float xOffset = static_cast<float>(mouseX - lastMouseX);
// float yOffset = static_cast<float>(lastMouseY - mouseY); // Reversed: y goes up
//
// lastMouseX = mouseX;
// lastMouseY = mouseY;
//
// constexpr float sensitivity = 0.002f;
// xOffset *= sensitivity;
// yOffset *= sensitivity;
//
// totalYaw += xOffset;
// totalPitch += yOffset;
//
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
//
// m_frustum.update(m_projectionMatrix * m_viewMatrix);
// }
//
// wasCursorLockedLastFrame = isLocked;
// #pragma endregion
//
// #pragma region Controller Movement
//
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1)) {
// GLFWgamepadstate state;
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1) && glfwGetGamepadState(GLFW_JOYSTICK_1, &state)) {
// float moveSpeed = MovementSpeed * deltaTime;
// const float rotSpeed = 1.5f * deltaTime;
//
//
// //TODO: god knows what this is
// auto deadzone = [] (float value, float threshold = 0.1f) {
// if (fabs(value) < threshold)
// return 0.0f;
// const float sign = (value > 0) ? 1.0f : -1.0f;
// return sign * (fabs(value) - threshold) / (1.0f - threshold);
// };
//
// //LT is x2 speed
// const bool isLTPressed = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
// if (isLTPressed) {
// moveSpeed *= 3.0f;
// }
//
// const float lx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
// const float ly = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
// const float rx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
// const float ry = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
//
// m_position += m_forward * (-ly * moveSpeed);
// m_position += m_right * (lx * moveSpeed);
//
// const float lTrigger = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) / 2.0f;
// const float rTrigger = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) / 2.0f;
// const float vertical = (rTrigger - lTrigger);
// m_position += m_up * vertical * moveSpeed;
//
// totalYaw += rx * rotSpeed;
// totalPitch -= ry * rotSpeed;
// }
// }
// #pragma endregion
CalculateProjectionMatrix();
CalculateViewMatrix();
}
void Camera::CalculateViewMatrix() {
if (m_useTarget) {
m_forward = glm::normalize(m_target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
m_viewMatrix = glm::lookAt(m_position, m_target, m_up);
} else {
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
}
m_invMatrix = glm::inverse(m_viewMatrix);
}
void Camera::CalculateProjectionMatrix() {
m_projectionMatrix = glm::perspectiveRH_ZO(glm::radians(fovAngle), m_aspectRatio, m_zNear, m_zFar);
}
void Camera::ClearTarget() {
m_useTarget = false;
m_forward = glm::normalize(m_target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
}
void Camera::SetTarget(const glm::vec3& target) {
m_target = target;
m_useTarget = true;
// m_forward = glm::normalize(m_target - m_position);
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
// m_up = glm::normalize(glm::cross(m_right, m_forward));
}
void Camera::Target(const glm::vec3& target) {
glm::vec3 directionToTarget = glm::normalize(target - m_position);
m_forward = glm::normalize(target - m_position);
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
m_up = glm::normalize(glm::cross(m_right, m_forward));
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
m_invMatrix = glm::inverse(m_viewMatrix);
}

View File

@@ -0,0 +1,565 @@
#include <destrum/Graphics/GfxDevice.h>
#include "destrum/Graphics/Util.h"
#define VOLK_IMPLEMENTATION
#include <volk.h>
#define VMA_IMPLEMENTATION
#include <filesystem>
#include <vk_mem_alloc.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <destrum/Graphics/Init.h>
#include "destrum/Graphics/imageLoader.h"
#include "spdlog/spdlog.h"
GfxDevice::GfxDevice(): imageCache(*this) {
}
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
VK_CHECK(volkInitialize());
instance = vkb::InstanceBuilder{}
.set_app_name(appName.c_str())
.set_app_version(1, 0, 0)
.request_validation_layers()
.use_default_debug_messenger()
.require_api_version(1, 3, 0)
.build()
.value();
volkLoadInstance(instance);
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
if (res != SDL_TRUE) {
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
std::exit(1);
}
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
.imageCubeArray = VK_TRUE,
.geometryShader = VK_TRUE, // for im3d
.depthClamp = VK_TRUE,
.samplerAnisotropy = VK_TRUE,
};
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
.descriptorIndexing = true,
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
.scalarBlockLayout = true,
.bufferDeviceAddress = true,
};
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
.synchronization2 = true,
.dynamicRendering = true,
};
physicalDevice = vkb::PhysicalDeviceSelector{instance}
.set_minimum_version(1, 3)
.set_required_features(deviceFeatures)
.set_required_features_12(features12)
.set_required_features_13(features13)
.set_surface(surface)
.select()
.value();
device = vkb::DeviceBuilder{physicalDevice}.build().value();
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
//Vma
const auto vulkanFunctions = VmaVulkanFunctions{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
const auto allocatorInfo = VmaAllocatorCreateInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = physicalDevice,
.device = device,
.pVulkanFunctions = &vulkanFunctions,
.instance = instance,
};
vmaCreateAllocator(&allocatorInfo, &allocator);
executor.init(device, graphicsQueueFamily, graphicsQueue);
int w, h;
SDL_GetWindowSize(window, &w, &h);
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
swapchain.initSync(device);
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
auto& commandPool = frames[i].commandPool;
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
auto& mainCommandBuffer = frames[i].commandBuffer;
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
}
//
// { // create white texture
// std::uint32_t pixel = 0xFFFFFFFF;
// whiteImageId = createImage(
// {
// .format = VK_FORMAT_R8G8B8A8_UNORM,
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
// .extent = VkExtent3D{1, 1, 1},
// },
// "white texture",
// &pixel);
// }
}
void GfxDevice::recreateSwapchain(int width, int height) {
assert(width != 0 && height != 0);
waitIdle();
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
}
VkCommandBuffer GfxDevice::beginFrame() {
swapchain.beginFrame(getCurrentFrameIndex());
const auto& frame = getCurrentFrame();
const auto& cmd = frame.commandBuffer;
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
return cmd;
}
void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) {
// get swapchain image
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
if (swapchainImage == VK_NULL_HANDLE) {
spdlog::info("Swapchain is freaky, skipping frame...");
return;
}
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
swapchain.resetFences(getCurrentFrameIndex());
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
{
// clear swapchain image
VkImageSubresourceRange clearRange =
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
}
// if (props.copyImageIntoSwapchain) {
// copy from draw image into swapchain
// vkutil::transitionImage(
// cmd,
// drawImage.getImage(),
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transitionImage(
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
const auto filter = VK_FILTER_LINEAR;
// // if (props.drawImageBlitRect != glm::ivec4{}) {
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// props.drawImageBlitRect.x,
// props.drawImageBlitRect.y,
// props.drawImageBlitRect.z,
// props.drawImageBlitRect.w,
// filter);
// } else {
// // will stretch image to swapchain
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// getSwapchainExtent(),
// filter);
// }
// prepare for present
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VK_CHECK(vkEndCommandBuffer(cmd));
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
frameNumber++;
}
void GfxDevice::cleanup() {
}
void GfxDevice::waitIdle() {
VK_CHECK(vkDeviceWaitIdle(device));
}
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
executor.immediateSubmit(std::move(f));
}
GPUBuffer GfxDevice::createBuffer(
std::size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage) const
{
const auto bufferInfo = VkBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = allocSize,
.usage = usage,
};
const auto allocInfo = VmaAllocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT |
// TODO: allow to set VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT when needed
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = memoryUsage,
};
GPUBuffer buffer{};
VK_CHECK(vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer.buffer, &buffer.allocation, &buffer.info));
if ((usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) != 0) {
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = buffer.buffer,
};
buffer.address = vkGetBufferDeviceAddress(device, &deviceAdressInfo);
}
return buffer;
}
VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const
{
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = buffer.buffer,
};
return vkGetBufferDeviceAddress(device, &deviceAdressInfo);
}
void GfxDevice::destroyBuffer(const GPUBuffer& buffer) const
{
vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation);
}
//
// GPUImage GfxDevice::loadImageFromFileRaw(
// const std::filesystem::path& path,
// VkFormat format,
// VkImageUsageFlags usage,
// bool mipMap) const
// {
// auto data = util::loadImage(path);
// if (!data.pixels) {
// fmt::println("[error] failed to load image from '{}'", path.string());
// return getImage(errorImageId);
// }
//
// auto image = createImageRaw({
// .format = format,
// .usage = usage | //
// VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image
// VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips
// .extent =
// VkExtent3D{
// .width = (std::uint32_t)data.width,
// .height = (std::uint32_t)data.height,
// .depth = 1,
// },
// .mipMap = mipMap,
// });
// uploadImageData(image, data.pixels);
//
// image.debugName = path.string();
// vkutil::addDebugLabel(device, image.image, path.string().c_str());
//
// return image;
// }
ImageID GfxDevice::createImage(
const vkutil::CreateImageInfo& createInfo,
const std::string& debugName,
void* pixelData,
ImageID imageId)
{
auto image = createImageRaw(createInfo);
if (!debugName.empty()) {
vkutil::addDebugLabel(device, image.image, debugName.c_str());
image.debugName = debugName;
}
if (pixelData) {
uploadImageData(image, pixelData);
}
if (imageId != NULL_IMAGE_ID) {
return imageCache.addImage(imageId, std::move(image));
} else {
return addImageToCache(std::move(image));
}
}
ImageID GfxDevice::createDrawImage(
VkFormat format,
glm::ivec2 size,
const std::string& debugName,
ImageID imageId)
{
assert(size.x > 0 && size.y > 0);
const auto extent = VkExtent3D{
.width = (std::uint32_t)size.x,
.height = (std::uint32_t)size.y,
.depth = 1,
};
VkImageUsageFlags usages{};
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
const auto createImageInfo = vkutil::CreateImageInfo{
.format = format,
.usage = usages,
.extent = extent,
};
return createImage(createImageInfo, debugName, nullptr, imageId);
}
ImageID GfxDevice::loadImageFromFile(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap)
{
return imageCache.loadImageFromFile(path, format, usage, mipMap);
}
const GPUImage& GfxDevice::getImage(ImageID id) const
{
return imageCache.getImage(id);
}
ImageID GfxDevice::addImageToCache(GPUImage img)
{
return imageCache.addImage(std::move(img));
}
GPUImage GfxDevice::createImageRaw(
const vkutil::CreateImageInfo& createInfo,
std::optional<VmaAllocationCreateInfo> customAllocationCreateInfo) const
{
std::uint32_t mipLevels = 1;
if (createInfo.mipMap) {
const auto maxExtent = std::max(createInfo.extent.width, createInfo.extent.height);
mipLevels = (std::uint32_t)std::floor(std::log2(maxExtent)) + 1;
}
if (createInfo.isCubemap) {
assert(createInfo.numLayers % 6 == 0);
assert(!createInfo.mipMap);
assert((createInfo.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0);
}
auto imgInfo = VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = createInfo.flags,
.imageType = VK_IMAGE_TYPE_2D,
.format = createInfo.format,
.extent = createInfo.extent,
.mipLevels = mipLevels,
.arrayLayers = createInfo.numLayers,
.samples = createInfo.samples,
.tiling = createInfo.tiling,
.usage = createInfo.usage,
};
static const auto defaultAllocInfo = VmaAllocationCreateInfo{
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
};
const auto allocInfo = customAllocationCreateInfo.has_value() ?
customAllocationCreateInfo.value() :
defaultAllocInfo;
GPUImage image{};
image.format = createInfo.format;
image.usage = createInfo.usage;
image.extent = createInfo.extent;
image.mipLevels = mipLevels;
image.numLayers = createInfo.numLayers;
image.isCubemap = createInfo.isCubemap;
VK_CHECK(
vmaCreateImage(allocator, &imgInfo, &allocInfo, &image.image, &image.allocation, nullptr));
// create view only when usage flags allow it
bool shouldCreateView = ((createInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) ||
((createInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
if (shouldCreateView) {
VkImageAspectFlags aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT;
if (createInfo.format == VK_FORMAT_D32_SFLOAT) { // TODO: support other depth formats
aspectFlag = VK_IMAGE_ASPECT_DEPTH_BIT;
}
auto viewType =
createInfo.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
if (createInfo.isCubemap && createInfo.numLayers == 6) {
viewType = VK_IMAGE_VIEW_TYPE_CUBE;
}
const auto viewCreateInfo = VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image.image,
.viewType = viewType,
.format = createInfo.format,
.subresourceRange =
VkImageSubresourceRange{
.aspectMask = aspectFlag,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = createInfo.numLayers,
},
};
VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &image.imageView));
}
return image;
}
void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const
{
int numChannels = 4;
if (image.format == VK_FORMAT_R8_UNORM) {
// FIXME: support more types
numChannels = 1;
}
const auto dataSize =
image.extent.depth * image.extent.width * image.extent.height * numChannels;
const auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
memcpy(uploadBuffer.info.pMappedData, pixelData, dataSize);
executor.immediateSubmit([&](VkCommandBuffer cmd) {
assert(
(image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 &&
"Image needs to have VK_IMAGE_USAGE_TRANSFER_DST_BIT to upload data to it");
vkutil::transitionImage(
cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
const auto copyRegion = VkBufferImageCopy{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = layer,
.layerCount = 1,
},
.imageExtent = image.extent,
};
vkCmdCopyBufferToImage(
cmd,
uploadBuffer.buffer,
image.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion);
if (image.mipLevels > 1) {
assert(
(image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 &&
(image.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0 &&
"Image needs to have VK_IMAGE_USAGE_TRANSFER_{DST,SRC}_BIT to generate mip maps");
// graphics::generateMipmaps(
// cmd,
// image.image,
// VkExtent2D{image.extent.width, image.extent.height},
// image.mipLevels);
spdlog::warn("Yea dawg, i ain't written this yet :pray:");
} else {
vkutil::transitionImage(
cmd,
image.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
});
destroyBuffer(uploadBuffer);
}
GPUImage GfxDevice::loadImageFromFileRaw(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap) const
{
auto data = util::loadImage(path);
if (!data.pixels) {
fmt::println("[error] failed to load image from '{}'", path.string());
return getImage(errorImageId);
}
auto image = createImageRaw({
.format = format,
.usage = usage | //
VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image
VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips
.extent =
VkExtent3D{
.width = (std::uint32_t)data.width,
.height = (std::uint32_t)data.height,
.depth = 1,
},
.mipMap = mipMap,
});
uploadImageData(image, data.pixels);
image.debugName = path.string();
vkutil::addDebugLabel(device, image.image, path.string().c_str());
return image;
}
void GfxDevice::destroyImage(const GPUImage& image) const
{
vkDestroyImageView(device, image.imageView, nullptr);
vmaDestroyImage(allocator, image.image, image.allocation);
// TODO: if image has bindless id, update the set
}

View File

@@ -0,0 +1,77 @@
#include <destrum/Graphics/ImageCache.h>
#include <destrum/Graphics/GfxDevice.h>
ImageCache::ImageCache(GfxDevice& gfxDevice) : gfxDevice(gfxDevice)
{}
ImageID ImageCache::loadImageFromFile(
const std::filesystem::path& path,
VkFormat format,
VkImageUsageFlags usage,
bool mipMap)
{
for (const auto& [id, info] : loadedImagesInfo) {
// TODO: calculate some hash to not have to linear search every time?
if (info.path == path && info.format == format && info.usage == usage &&
info.mipMap == mipMap) {
// std::cout << "Already loaded: " << path << std::endl;
return id;
}
}
auto image = gfxDevice.loadImageFromFileRaw(path, format, usage, mipMap);
if (image.isInitialized() && image.getBindlessId() == errorImageId) {
return errorImageId;
}
const auto id = getFreeImageId();
addImage(id, std::move(image));
loadedImagesInfo.emplace(
id,
LoadedImageInfo{
.path = path,
.format = format,
.usage = usage,
.mipMap = mipMap,
});
return id;
}
ImageID ImageCache::addImage(GPUImage image)
{
return addImage(getFreeImageId(), std::move(image));
}
ImageID ImageCache::addImage(ImageID id, GPUImage image)
{
image.setBindlessId(static_cast<std::uint32_t>(id));
if (id != images.size()) {
images[id] = std::move(image); // replacing existing image
} else {
images.push_back(std::move(image));
}
// bindlessSetManager.addImage(gfxDevice.getDevice(), id, image.imageView);
return id;
}
const GPUImage& ImageCache::getImage(ImageID id) const
{
return images.at(id);
}
ImageID ImageCache::getFreeImageId() const
{
return images.size();
}
void ImageCache::destroyImages()
{
for (const auto& image : images) {
gfxDevice.destroyImage(image);
}
images.clear();
loadedImagesInfo.clear();
}

View File

@@ -0,0 +1,30 @@
#include <destrum/Graphics/imageLoader.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
ImageData::~ImageData()
{
if (shouldSTBFree) {
stbi_image_free(pixels);
stbi_image_free(hdrPixels);
}
}
namespace util
{
ImageData loadImage(const std::filesystem::path& p)
{
ImageData data;
data.shouldSTBFree = true;
if (stbi_is_hdr(p.string().c_str())) {
data.hdr = true;
data.hdrPixels = stbi_loadf(p.string().c_str(), &data.width, &data.height, &data.comp, 4);
} else {
data.pixels = stbi_load(p.string().c_str(), &data.width, &data.height, &data.channels, 4);
}
data.channels = 4;
return data;
}
} // namespace util

View File

@@ -0,0 +1,86 @@
#include <destrum/Graphics/MeshCache.h>
#include <destrum/Graphics/Resources/Mesh.h>
#include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/Util.h>
// #include <destrum/Math/Util.h>
MeshID MeshCache::addMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh)
{
auto gpuMesh = GPUMesh{
.numVertices = (std::uint32_t)cpuMesh.vertices.size(),
.numIndices = (std::uint32_t)cpuMesh.indices.size(),
.minPos = cpuMesh.minPos,
.maxPos = cpuMesh.maxPos,
};
std::vector<glm::vec3> positions(cpuMesh.vertices.size());
for (std::size_t i = 0; i < cpuMesh.vertices.size(); ++i) {
positions[i] = cpuMesh.vertices[i].position;
}
uploadMesh(gfxDevice, cpuMesh, gpuMesh);
const auto id = meshes.size();
meshes.push_back(std::move(gpuMesh));
return id;
}
void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh& gpuMesh) const
{
// create index buffer
const auto indexBufferSize = cpuMesh.indices.size() * sizeof(std::uint32_t);
gpuMesh.indexBuffer = gfxDevice.createBuffer(
indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
// create vertex buffer
const auto vertexBufferSize = cpuMesh.vertices.size() * sizeof(CPUMesh::Vertex);
gpuMesh.vertexBuffer = gfxDevice.createBuffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
const auto staging =
gfxDevice
.createBuffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
// copy data
void* data = staging.info.pMappedData;
memcpy(data, cpuMesh.vertices.data(), vertexBufferSize);
memcpy((char*)data + vertexBufferSize, cpuMesh.indices.data(), indexBufferSize);
gfxDevice.immediateSubmit([&](VkCommandBuffer cmd) {
const auto vertexCopy = VkBufferCopy{
.srcOffset = 0,
.dstOffset = 0,
.size = vertexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.vertexBuffer.buffer, 1, &vertexCopy);
const auto indexCopy = VkBufferCopy{
.srcOffset = vertexBufferSize,
.dstOffset = 0,
.size = indexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.indexBuffer.buffer, 1, &indexCopy);
});
gfxDevice.destroyBuffer(staging);
const auto vtxBufferName = cpuMesh.name + " (vtx)";
const auto idxBufferName = cpuMesh.name + " (idx)";
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.vertexBuffer.buffer, vtxBufferName.c_str());
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.indexBuffer.buffer, idxBufferName.c_str());
}
const GPUMesh& MeshCache::getMesh(MeshID id) const
{
return meshes.at(id);
}
void MeshCache::cleanup(const GfxDevice& gfxDevice)
{
for (const auto& mesh : meshes) {
gfxDevice.destroyBuffer(mesh.indexBuffer);
gfxDevice.destroyBuffer(mesh.vertexBuffer);
}
}

View File

@@ -0,0 +1,130 @@
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
#include <destrum/FS/AssetFS.h>
MeshPipeline::MeshPipeline() {
}
MeshPipeline::~MeshPipeline() {
}
void MeshPipeline::init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat) {
const auto& device = gfxDevice.getDevice();
// const auto vertexShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv"), device);
// const auto fragShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv"), device);
const auto vertexShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv");
const auto fragShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv");
// vkutil::addDebugLabel(device, vertexShader, "mesh.vert");
// vkutil::addDebugLabel(device, vertexShader, "mesh.frag");
const auto bufferRange = VkPushConstantRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = 0,
.size = sizeof(PushConstants),
};
const auto pushConstantRanges = std::array{bufferRange};
// const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()};
// m_pipelineLayout = vkutil::createPipelineLayout(device, layouts, pushConstantRanges);
// vkutil::addDebugLabel(device, pipelineLayout, "mesh pipeline layout");
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
// pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
// pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
if (vkCreatePipelineLayout(gfxDevice.getDevice().device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("Could not make pipleine layout");
}
PipelineConfigInfo pipelineConfig{};
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
pipelineConfig.name = "Mesh Pipeline";
pipelineConfig.pipelineLayout = m_pipelineLayout;
pipelineConfig.vertexAttributeDescriptions = {};
pipelineConfig.vertexBindingDescriptions = {};
pipelineConfig.colorAttachments = {drawImageFormat};
pipelineConfig.depthAttachment = depthImageFormat;
m_pipeline = std::make_unique<Pipeline>(
gfxDevice,
vertexShader.string(),
fragShader.string(),
pipelineConfig
);
}
void MeshPipeline::draw(VkCommandBuffer cmd,
VkExtent2D renderExtent,
const GfxDevice& gfxDevice,
const MeshCache& meshCache,
const MaterialCache& materialCache,
const Camera& camera,
const GPUBuffer& sceneDataBuffer,
const std::vector<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& sortedDrawCommands) {
// vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
m_pipeline->bind(cmd);
// gfxDevice.bindBindlessDescSet(cmd, pipelineLayout);
const auto viewport = VkViewport{
.x = 0,
.y = 0,
.width = (float)renderExtent.width,
.height = (float)renderExtent.height,
.minDepth = 0.f,
.maxDepth = 1.f,
};
vkCmdSetViewport(cmd, 0, 1, &viewport);
const auto scissor = VkRect2D{
.offset = {},
.extent = renderExtent,
};
vkCmdSetScissor(cmd, 0, 1, &scissor);
auto prevMeshId = NULL_MESH_ID;
// const auto frustum = edge::createFrustumFromCamera(camera);
for (const auto& dcIdx : drawCommands) {
// const auto& dc = drawCommands[dcIdx];
const auto& dc = dcIdx;
// if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
// continue;
// }
const auto& mesh = meshCache.getMesh(dc.meshId);
if (dc.meshId != prevMeshId) {
prevMeshId = dc.meshId;
vkCmdBindIndexBuffer(cmd, mesh.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
}
assert(dc.materialId != NULL_MATERIAL_ID);
const auto pushConstants = PushConstants{
.transform = dc.transformMatrix,
.sceneDataBuffer = sceneDataBuffer.address,
.vertexBuffer = mesh.vertexBuffer.address,
.materialId = dc.materialId,
};
vkCmdPushConstants(
cmd,
m_pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0,
sizeof(PushConstants),
&pushConstants);
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
}
}

View File

@@ -0,0 +1,149 @@
#include <destrum/Graphics/Renderer.h>
#include <destrum/Graphics/Util.h>
GameRenderer::GameRenderer(MeshCache& meshCache): meshCache{meshCache} {
}
void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) {
sceneDataBuffer.init(
gfxDevice,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
sizeof(GPUSceneData),
"scene data");
createDrawImage(gfxDevice, drawImageSize, true);
meshPipeline = std::make_unique<MeshPipeline>();
meshPipeline->init(gfxDevice, drawImageFormat, depthImageFormat);
}
void GameRenderer::beginDrawing(GfxDevice& gfxDevice) {
meshDrawCommands.clear();
}
void GameRenderer::endDrawing() {
//Sort the drawlist
}
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) {
const auto gpuSceneData = GPUSceneData{
.view = sceneData.camera.GetViewMatrix(),
.proj = sceneData.camera.GetProjectionMatrix(),
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
.ambientColor = {sceneData.ambientColor},
.ambientIntensity = sceneData.ambientIntensity,
.fogColor = {sceneData.fogColor},
.fogDensity = sceneData.fogDensity,
.materialsBuffer = materialCache.getMaterialDataBufferAddress(),
};
sceneDataBuffer.uploadNewData(
cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData));
const auto& drawImage = gfxDevice.getImage(drawImageId);
const auto& depthImage = gfxDevice.getImage(depthImageId);
// vkutil::cmdBeginLabel(cmd, "Geometry");
vkutil::transitionImage(
cmd,
drawImage.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vkutil::transitionImage(
cmd,
depthImage.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
const auto renderInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(),
.colorImageView = drawImage.imageView,
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
.depthImageView = depthImage.imageView,
.depthImageClearValue = 0.f,
});
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
meshPipeline->draw(
cmd,
drawImage.getExtent2D(),
gfxDevice,
meshCache,
materialCache,
camera,
sceneDataBuffer.getBuffer(),
meshDrawCommands,
sortedMeshDrawCommands);
vkCmdEndRendering(cmd);
// vkutil::cmdEndLabel(cmd);
}
void GameRenderer::cleanup() {
}
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId) {
const auto& mesh = meshCache.getMesh(id);
// const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
assert(materialId != NULL_MATERIAL_ID);
meshDrawCommands.push_back(MeshDrawCommand{
.meshId = id,
.transformMatrix = transform,
.materialId = materialId,
});
}
void GameRenderer::createDrawImage(GfxDevice& gfxDevice,
const glm::ivec2& drawImageSize,
bool firstCreate)
{
const VkExtent3D drawImageExtent{
.width = (std::uint32_t)drawImageSize.x,
.height = (std::uint32_t)drawImageSize.y,
.depth = 1,
};
constexpr VkSampleCountFlagBits noMsaa = VK_SAMPLE_COUNT_1_BIT;
{ // setup draw image (single-sampled)
VkImageUsageFlags usages{};
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
auto createImageInfo = vkutil::CreateImageInfo{
.format = drawImageFormat,
.usage = usages,
.extent = drawImageExtent,
.samples = noMsaa,
};
// reuse the same id if creating again
drawImageId = gfxDevice.createImage(createImageInfo, "draw image", nullptr, drawImageId);
if (firstCreate) {
// Optional: a separate post-fx target (ping-pong)
// postFXDrawImageId = gfxDevice.createImage(createImageInfo, "post FX draw image");
}
}
{ // setup depth image (single-sampled)
auto createInfo = vkutil::CreateImageInfo{
.format = depthImageFormat,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.extent = drawImageExtent,
.samples = noMsaa,
};
depthImageId = gfxDevice.createImage(createInfo, "depth image", nullptr, depthImageId);
}
}

View File

@@ -0,0 +1 @@
#include <destrum/Graphics/GPUImage.h>

View File

@@ -0,0 +1,122 @@
#include <destrum/Graphics/Resources/NBuffer.h>
#include <destrum/Graphics/GfxDevice.h>
#include <cassert>
#include <cstring>
#include <volk.h>
#include <destrum/Graphics/Util.h>
void NBuffer::init(
GfxDevice& gfxDevice,
VkBufferUsageFlags usage,
std::size_t dataSize,
const std::string& debugName)
{
assert(FRAMES_IN_FLIGHT > 0);
assert(dataSize > 0);
framesInFlight = FRAMES_IN_FLIGHT;
gpuBufferSize = dataSize;
gpuBuffer = gfxDevice.createBuffer(
dataSize, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE);
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuBuffer.buffer, debugName.c_str());
for (std::size_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
stagingBuffers.push_back(gfxDevice.createBuffer(
dataSize, usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST));
}
initialized = true;
}
void NBuffer::cleanup(GfxDevice& device)
{
for (const auto& stagingBuffer : stagingBuffers) {
device.destroyBuffer(stagingBuffer);
}
stagingBuffers.clear();
device.destroyBuffer(gpuBuffer);
initialized = false;
}
void NBuffer::uploadNewData(
VkCommandBuffer cmd,
std::size_t frameIndex,
void* newData,
std::size_t dataSize,
std::size_t offset,
bool sync) const
{
assert(initialized);
assert(frameIndex < framesInFlight);
assert(offset + dataSize <= gpuBufferSize && "NBuffer::uploadNewData: out of bounds write");
if (dataSize == 0) {
return;
}
// sync with previous read
if (sync) {
const auto bufferBarrier = VkBufferMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.buffer = gpuBuffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
const auto dependencyInfo = VkDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &bufferBarrier,
};
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
}
auto& staging = stagingBuffers[frameIndex];
auto* mappedData = reinterpret_cast<std::uint8_t*>(staging.info.pMappedData);
memcpy((void*)&mappedData[offset], newData, dataSize);
const auto region = VkBufferCopy2{
.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2,
.srcOffset = (VkDeviceSize)offset,
.dstOffset = (VkDeviceSize)offset,
.size = dataSize,
};
const auto bufCopyInfo = VkCopyBufferInfo2{
.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2,
.srcBuffer = staging.buffer,
.dstBuffer = gpuBuffer.buffer,
.regionCount = 1,
.pRegions = &region,
};
vkCmdCopyBuffer2(cmd, &bufCopyInfo);
if (sync) { // sync write
const auto bufferBarrier = VkBufferMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.buffer = gpuBuffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
const auto dependencyInfo = VkDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &bufferBarrier,
};
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
}
}

View File

@@ -0,0 +1,316 @@
#include <fstream>
#include <destrum/Graphics/Init.h>
#include <destrum/Graphics/Util.h>
#include <volk.h>
#include "spdlog/spdlog.h"
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
VkImageAspectFlags aspectMask =
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
VkImageMemoryBarrier2 imageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = currentLayout,
.newLayout = newLayout,
.image = image,
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
};
VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageBarrier,
};
vkCmdPipelineBarrier2(cmd, &depInfo);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
const auto blitRegion = VkImageBlit2{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.srcSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
{
{},
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
},
.dstSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
{
{(std::int32_t)destX, (std::int32_t)destY},
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
},
};
const auto blitInfo = VkBlitImageInfo2{
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.srcImage = source,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = destination,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &blitRegion,
.filter = filter,
};
vkCmdBlitImage2(cmd, &blitInfo);
}
void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE,
.objectHandle = (std::uint64_t)image,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_BUFFER,
.objectHandle = (std::uint64_t)buffer,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
assert(
(params.colorImageView || params.depthImageView != nullptr) &&
"Either draw or depth image should be present");
assert(
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
"renderExtent not specified");
RenderInfo ri;
if (params.colorImageView) {
ri.colorAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.colorImageView,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.colorImageClearValue) {
const auto col = params.colorImageClearValue.value();
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
}
}
if (params.depthImageView) {
ri.depthAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.depthImageClearValue) {
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
}
}
ri.renderingInfo = VkRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea =
VkRect2D{
.offset = {},
.extent = params.renderExtent,
},
.layerCount = 1,
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
};
return ri;
}
VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDevice device) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
spdlog::error("failed to open shader");
std::exit(1);
}
const auto fileSize = file.tellg();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
file.seekg(0);
file.read((char*)buffer.data(), fileSize);
file.close();
auto info = VkShaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = buffer.size() * sizeof(std::uint32_t),
.pCode = buffer.data(),
};
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &info, nullptr, &shaderModule) != VK_SUCCESS) {
spdlog::error("Failed to load");
std::exit(1);
}
return shaderModule;
}
void addDebugLabel(VkDevice device, VkImage image, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE,
.objectHandle = (std::uint64_t)image,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_IMAGE_VIEW,
.objectHandle = (std::uint64_t)imageView,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
.objectHandle = (std::uint64_t)shaderModule,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_PIPELINE,
.objectHandle = (std::uint64_t)pipeline,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT,
.objectHandle = (std::uint64_t)layout,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_BUFFER,
.objectHandle = (std::uint64_t)buffer,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label)
{
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_SAMPLER,
.objectHandle = (std::uint64_t)sampler,
.pObjectName = label,
};
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params)
{
assert(
(params.colorImageView || params.depthImageView != nullptr) &&
"Either draw or depth image should be present");
assert(
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
"renderExtent not specified");
vkutil::RenderInfo ri;
if (params.colorImageView) {
ri.colorAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.colorImageView,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.colorImageClearValue) {
const auto col = params.colorImageClearValue.value();
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
}
}
if (params.depthImageView) {
ri.depthAttachment = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = params.depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (params.depthImageClearValue) {
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
}
}
ri.renderingInfo = VkRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea =
VkRect2D{
.offset = {},
.extent = params.renderExtent,
},
.layerCount = 1,
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
};
return ri;
}

View File

@@ -1,236 +0,0 @@
#include <destrum/Graphics/GfxDevice.h>
#include "destrum/Graphics/Util.h"
#define VOLK_IMPLEMENTATION
#include <volk.h>
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <destrum/Graphics/Init.h>
#include "spdlog/spdlog.h"
GfxDevice::GfxDevice() {
}
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
VK_CHECK(volkInitialize());
instance = vkb::InstanceBuilder{}
.set_app_name(appName.c_str())
.set_app_version(1, 0, 0)
.request_validation_layers()
.use_default_debug_messenger()
.require_api_version(1, 3, 0)
.build()
.value();
volkLoadInstance(instance);
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
if (res != SDL_TRUE) {
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
std::exit(1);
}
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
.imageCubeArray = VK_TRUE,
.geometryShader = VK_TRUE, // for im3d
.depthClamp = VK_TRUE,
.samplerAnisotropy = VK_TRUE,
};
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
.descriptorIndexing = true,
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.descriptorBindingVariableDescriptorCount = true,
.runtimeDescriptorArray = true,
.scalarBlockLayout = true,
.bufferDeviceAddress = true,
};
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
.synchronization2 = true,
.dynamicRendering = true,
};
physicalDevice = vkb::PhysicalDeviceSelector{instance}
.set_minimum_version(1, 3)
.set_required_features(deviceFeatures)
.set_required_features_12(features12)
.set_required_features_13(features13)
.set_surface(surface)
.select()
.value();
device = vkb::DeviceBuilder{physicalDevice}.build().value();
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
//Vma
const auto vulkanFunctions = VmaVulkanFunctions{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
const auto allocatorInfo = VmaAllocatorCreateInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = physicalDevice,
.device = device,
.pVulkanFunctions = &vulkanFunctions,
.instance = instance,
};
vmaCreateAllocator(&allocatorInfo, &allocator);
executor.init(device, graphicsQueueFamily, graphicsQueue);
int w, h;
SDL_GetWindowSize(window, &w, &h);
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
swapchain.initSync(device);
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
auto& commandPool = frames[i].commandPool;
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
auto& mainCommandBuffer = frames[i].commandBuffer;
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
}
//
// { // create white texture
// std::uint32_t pixel = 0xFFFFFFFF;
// whiteImageId = createImage(
// {
// .format = VK_FORMAT_R8G8B8A8_UNORM,
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
// .extent = VkExtent3D{1, 1, 1},
// },
// "white texture",
// &pixel);
// }
}
void GfxDevice::recreateSwapchain(int width, int height) {
assert(width != 0 && height != 0);
waitIdle();
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
}
VkCommandBuffer GfxDevice::beginFrame() {
swapchain.beginFrame(getCurrentFrameIndex());
const auto& frame = getCurrentFrame();
const auto& cmd = frame.commandBuffer;
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
return cmd;
}
void GfxDevice::endFrame(VkCommandBuffer cmd, const VImage& drawImage, const EndFrameProps& props) {
// get swapchain image
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
if (swapchainImage == VK_NULL_HANDLE) {
std::printf("Swapchain is dirty, skipping frame...\n");
return;
}
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
swapchain.resetFences(getCurrentFrameIndex());
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
{
// clear swapchain image
VkImageSubresourceRange clearRange =
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
VkClearColorValue clearValue;
static int superFrameNumber = 0;
superFrameNumber += 1;
if (superFrameNumber > 255) {
superFrameNumber = 0;
}
float flash = std::abs(std::sin(superFrameNumber * 0.1f));
clearValue = { { 0.0f, 0.0f, flash, 1.0f } };
// const auto clearValue = props.clearColor;
vkCmdClearColorImage(cmd, swapchainImage, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);
}
// if (props.copyImageIntoSwapchain) {
// copy from draw image into swapchain
// vkutil::transitionImage(
// cmd,
// drawImage.getImage(),
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transitionImage(
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
const auto filter = VK_FILTER_LINEAR;
// // if (props.drawImageBlitRect != glm::ivec4{}) {
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// props.drawImageBlitRect.x,
// props.drawImageBlitRect.y,
// props.drawImageBlitRect.z,
// props.drawImageBlitRect.w,
// filter);
// } else {
// // will stretch image to swapchain
// vkutil::copyImageToImage(
// cmd,
// drawImage.getImage(),
// swapchainImage,
// drawImage.getExtent2D(),
// getSwapchainExtent(),
// filter);
// }
// prepare for present
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VK_CHECK(vkEndCommandBuffer(cmd));
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
frameNumber++;
}
void GfxDevice::cleanup() {
}
void GfxDevice::waitIdle() {
VK_CHECK(vkDeviceWaitIdle(device));
}
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
executor.immediateSubmit(std::move(f));
}

View File

@@ -1 +0,0 @@
#include <destrum/Graphics/MeshCache.h>

View File

@@ -1,79 +0,0 @@
#include <destrum/Graphics/Init.h>
#include <destrum/Graphics/Util.h>
#include <volk.h>
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
VkImageAspectFlags aspectMask =
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
VkImageMemoryBarrier2 imageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = currentLayout,
.newLayout = newLayout,
.image = image,
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
};
VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageBarrier,
};
vkCmdPipelineBarrier2(cmd, &depInfo);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
}
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
const auto blitRegion = VkImageBlit2{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.srcSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
{
{},
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
},
.dstSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
{
{(std::int32_t)destX, (std::int32_t)destY},
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
},
};
const auto blitInfo = VkBlitImageInfo2{
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.srcImage = source,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = destination,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &blitRegion,
.filter = filter,
};
vkCmdBlitImage2(cmd, &blitInfo);
}

View File

@@ -1 +0,0 @@
#include <destrum/Graphics/VImage.h>

View File

@@ -4,6 +4,7 @@ add_subdirectory(glm)
# stb # stb
add_subdirectory(stb) add_subdirectory(stb)
# SDL # SDL
if (NOT BUILD_SHARED_LIBS) if (NOT BUILD_SHARED_LIBS)
set(SDL_SHARED_ENABLED_BY_DEFAULT OFF CACHE BOOL "Don't build SDL as shared lib") set(SDL_SHARED_ENABLED_BY_DEFAULT OFF CACHE BOOL "Don't build SDL as shared lib")

View File

@@ -1,5 +1,4 @@
#include "Lightkeeper.h" #include "Lightkeeper.h"
LightKeeper::LightKeeper() { LightKeeper::LightKeeper(): App() {
} }