From c83c423b42454ea159da9a29fd495f90230d5556 Mon Sep 17 00:00:00 2001 From: Bram Date: Mon, 5 Jan 2026 06:20:49 +0100 Subject: [PATCH] We be kinda rendering --- cmake/compile_slang_shaders.cmake | 82 +++ destrum/CMakeLists.txt | 57 +- destrum/assets_src/shaders/bindless.glsl | 35 ++ destrum/assets_src/shaders/materials.glsl | 19 + destrum/assets_src/shaders/mesh.frag | 125 ++++ destrum/assets_src/shaders/mesh.vert | 34 ++ destrum/assets_src/shaders/mesh_pcs.glsl | 15 + destrum/assets_src/shaders/scene_data.glsl | 28 + destrum/assets_src/shaders/vertex.glsl | 19 + destrum/assets_src/temp | 0 destrum/include/destrum/App.h | 11 + destrum/include/destrum/Graphics/Camera.h | 158 +++++ .../destrum/Graphics/{VImage.h => GPUImage.h} | 54 +- destrum/include/destrum/Graphics/GfxDevice.h | 45 +- destrum/include/destrum/Graphics/ImageCache.h | 56 ++ destrum/include/destrum/Graphics/Material.h | 30 + .../include/destrum/Graphics/MaterialCache.h | 42 ++ destrum/include/destrum/Graphics/MeshCache.h | 19 + .../destrum/Graphics/MeshDrawCommand.h | 27 + .../destrum/Graphics/Pipelines/MeshPipeline.h | 24 +- destrum/include/destrum/Graphics/Renderer.h | 72 +++ .../include/destrum/Graphics/Resources/Mesh.h | 67 ++- .../destrum/Graphics/Resources/NBuffer.h | 39 ++ destrum/include/destrum/Graphics/Util.h | 42 ++ destrum/include/destrum/Graphics/ids.h | 19 + .../include/destrum/Graphics/imageLoader.h | 36 ++ destrum/src/App.cpp | 24 +- destrum/src/Graphics/Camera.cpp | 195 ++++++ destrum/src/Graphics/GfxDevice.cpp | 565 ++++++++++++++++++ destrum/src/Graphics/ImageCache.cpp | 77 +++ destrum/src/Graphics/ImageLoader.cpp | 30 + .../ImmediateExecuter.cpp | 0 destrum/src/{graphics => Graphics}/Init.cpp | 0 destrum/src/Graphics/MeshCache.cpp | 86 +++ .../src/{graphics => Graphics}/Pipeline.cpp | 0 .../src/Graphics/Pipelines/MeshPipeline.cpp | 130 ++++ destrum/src/Graphics/Renderer.cpp | 149 +++++ destrum/src/Graphics/Resources/GPUImage.cpp | 1 + destrum/src/Graphics/Resources/NBuffer.cpp | 122 ++++ .../src/{graphics => Graphics}/Swapchain.cpp | 0 destrum/src/Graphics/Util.cpp | 316 ++++++++++ destrum/src/graphics/GfxDevice.cpp | 236 -------- destrum/src/graphics/MeshCache.cpp | 1 - destrum/src/graphics/Renderer.cpp | 0 destrum/src/graphics/Util.cpp | 79 --- destrum/src/graphics/VImage.cpp | 1 - destrum/third_party/CMakeLists.txt | 1 + lightkeeper/src/Lightkeeper.cpp | 3 +- 48 files changed, 2789 insertions(+), 382 deletions(-) create mode 100644 cmake/compile_slang_shaders.cmake create mode 100644 destrum/assets_src/shaders/bindless.glsl create mode 100644 destrum/assets_src/shaders/materials.glsl create mode 100644 destrum/assets_src/shaders/mesh.frag create mode 100644 destrum/assets_src/shaders/mesh.vert create mode 100644 destrum/assets_src/shaders/mesh_pcs.glsl create mode 100644 destrum/assets_src/shaders/scene_data.glsl create mode 100644 destrum/assets_src/shaders/vertex.glsl delete mode 100644 destrum/assets_src/temp create mode 100644 destrum/include/destrum/Graphics/Camera.h rename destrum/include/destrum/Graphics/{VImage.h => GPUImage.h} (53%) create mode 100644 destrum/include/destrum/Graphics/ImageCache.h create mode 100644 destrum/include/destrum/Graphics/Material.h create mode 100644 destrum/include/destrum/Graphics/MaterialCache.h create mode 100644 destrum/include/destrum/Graphics/MeshDrawCommand.h create mode 100644 destrum/include/destrum/Graphics/Resources/NBuffer.h create mode 100644 destrum/include/destrum/Graphics/ids.h create mode 100644 destrum/include/destrum/Graphics/imageLoader.h create mode 100644 destrum/src/Graphics/Camera.cpp create mode 100644 destrum/src/Graphics/GfxDevice.cpp create mode 100644 destrum/src/Graphics/ImageCache.cpp create mode 100644 destrum/src/Graphics/ImageLoader.cpp rename destrum/src/{graphics => Graphics}/ImmediateExecuter.cpp (100%) rename destrum/src/{graphics => Graphics}/Init.cpp (100%) create mode 100644 destrum/src/Graphics/MeshCache.cpp rename destrum/src/{graphics => Graphics}/Pipeline.cpp (100%) create mode 100644 destrum/src/Graphics/Pipelines/MeshPipeline.cpp create mode 100644 destrum/src/Graphics/Renderer.cpp create mode 100644 destrum/src/Graphics/Resources/GPUImage.cpp create mode 100644 destrum/src/Graphics/Resources/NBuffer.cpp rename destrum/src/{graphics => Graphics}/Swapchain.cpp (100%) create mode 100644 destrum/src/Graphics/Util.cpp delete mode 100644 destrum/src/graphics/GfxDevice.cpp delete mode 100644 destrum/src/graphics/MeshCache.cpp delete mode 100644 destrum/src/graphics/Renderer.cpp delete mode 100644 destrum/src/graphics/Util.cpp delete mode 100644 destrum/src/graphics/VImage.cpp diff --git a/cmake/compile_slang_shaders.cmake b/cmake/compile_slang_shaders.cmake new file mode 100644 index 0000000..6ec37b7 --- /dev/null +++ b/cmake/compile_slang_shaders.cmake @@ -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() diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index c6276e8..8495c8c 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -1,18 +1,27 @@ add_subdirectory(third_party) set(SRC_FILES - "src/App.cpp" + "src/App.cpp" - "src/Graphics/GfxDevice.cpp" - "src/Graphics/ImmediateExecuter.cpp" - "src/Graphics/Swapchain.cpp" - "src/Graphics/VImage.cpp" - "src/Graphics/Init.cpp" - "src/Graphics/Util.cpp" - "src/graphics/Pipeline.cpp" - "src/graphics/MeshCache.cpp" + "src/Graphics/Camera.cpp" + "src/Graphics/GfxDevice.cpp" + "src/Graphics/ImageCache.cpp" + "src/Graphics/ImageLoader.cpp" + "src/Graphics/ImmediateExecuter.cpp" + "src/Graphics/Init.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}) @@ -20,8 +29,8 @@ add_library(destrum ${SRC_FILES}) add_library(destrum::destrum ALIAS destrum) set_target_properties(destrum PROPERTIES - CXX_STANDARD 20 - CXX_EXTENSIONS OFF + CXX_STANDARD 20 + CXX_EXTENSIONS OFF ) #target_add_extra_warnings(destrum) @@ -37,9 +46,9 @@ target_link_libraries(destrum glm::glm nlohmann_json::nlohmann_json spdlog::spdlog + stb::image PRIVATE - stb::image freetype::freetype ) @@ -50,17 +59,17 @@ target_compile_definitions(destrum # VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason ) -if(WIN32) - if(BUILD_SHARED_LIBS) +if (WIN32) + if (BUILD_SHARED_LIBS) target_link_libraries(destrum PUBLIC SDL2::SDL2main SDL2::SDL2 ) - else() + else () target_link_libraries(destrum PUBLIC SDL2::SDL2main SDL2::SDL2-static ) - endif() -endif() + endif () +endif () target_compile_definitions(destrum PUBLIC @@ -77,4 +86,14 @@ set(DESTRUM_SHADER_OUT "${CMAKE_CURRENT_LIST_DIR}/assets_runtime/shaders") include(../cmake/compile_shaders.cmake) compile_glsl_to_spv(destrum "${DESTRUM_SHADER_SRC}" "${DESTRUM_SHADER_OUT}" DESTRUM_SPV) -add_dependencies(destrum destrum_shaders) \ No newline at end of file +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 +#) diff --git a/destrum/assets_src/shaders/bindless.glsl b/destrum/assets_src/shaders/bindless.glsl new file mode 100644 index 0000000..66b2e79 --- /dev/null +++ b/destrum/assets_src/shaders/bindless.glsl @@ -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); +} diff --git a/destrum/assets_src/shaders/materials.glsl b/destrum/assets_src/shaders/materials.glsl new file mode 100644 index 0000000..674f561 --- /dev/null +++ b/destrum/assets_src/shaders/materials.glsl @@ -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 diff --git a/destrum/assets_src/shaders/mesh.frag b/destrum/assets_src/shaders/mesh.frag new file mode 100644 index 0000000..ef4b52c --- /dev/null +++ b/destrum/assets_src/shaders/mesh.frag @@ -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); +} diff --git a/destrum/assets_src/shaders/mesh.vert b/destrum/assets_src/shaders/mesh.vert new file mode 100644 index 0000000..88754f3 --- /dev/null +++ b/destrum/assets_src/shaders/mesh.vert @@ -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); +} diff --git a/destrum/assets_src/shaders/mesh_pcs.glsl b/destrum/assets_src/shaders/mesh_pcs.glsl new file mode 100644 index 0000000..bba8c1b --- /dev/null +++ b/destrum/assets_src/shaders/mesh_pcs.glsl @@ -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; + diff --git a/destrum/assets_src/shaders/scene_data.glsl b/destrum/assets_src/shaders/scene_data.glsl new file mode 100644 index 0000000..c04d0f5 --- /dev/null +++ b/destrum/assets_src/shaders/scene_data.glsl @@ -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 diff --git a/destrum/assets_src/shaders/vertex.glsl b/destrum/assets_src/shaders/vertex.glsl new file mode 100644 index 0000000..96ecc65 --- /dev/null +++ b/destrum/assets_src/shaders/vertex.glsl @@ -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 diff --git a/destrum/assets_src/temp b/destrum/assets_src/temp deleted file mode 100644 index e69de29..0000000 diff --git a/destrum/include/destrum/App.h b/destrum/include/destrum/App.h index 6964c78..6a3d707 100644 --- a/destrum/include/destrum/App.h +++ b/destrum/include/destrum/App.h @@ -8,6 +8,7 @@ #include #include +#include class App { @@ -20,6 +21,8 @@ public: std::filesystem::path exeDir; }; + App(); + void init(const AppParams& params); void run(); void cleanup(); @@ -28,7 +31,15 @@ protected: SDL_Window* window{nullptr}; AppParams m_params{}; + CPUMesh testMesh{}; + MeshID testMeshID; + 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 gamePaused{false}; diff --git a/destrum/include/destrum/Graphics/Camera.h b/destrum/include/destrum/Graphics/Camera.h new file mode 100644 index 0000000..41295c7 --- /dev/null +++ b/destrum/include/destrum/Graphics/Camera.h @@ -0,0 +1,158 @@ +#ifndef VCAMERA_H +#define VCAMERA_H + +#include + +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 diff --git a/destrum/include/destrum/Graphics/VImage.h b/destrum/include/destrum/Graphics/GPUImage.h similarity index 53% rename from destrum/include/destrum/Graphics/VImage.h rename to destrum/include/destrum/Graphics/GPUImage.h index 4983f89..81a14af 100644 --- a/destrum/include/destrum/Graphics/VImage.h +++ b/destrum/include/destrum/Graphics/GPUImage.h @@ -1,5 +1,5 @@ -#ifndef VIMAGE_H -#define VIMAGE_H +#ifndef GPUIMAGE_H +#define GPUIMAGE_H #include #include @@ -7,29 +7,9 @@ #include #include -class VImage { -public: - glm::ivec2 getSize2D() const { return glm::ivec2{extent.width, extent.height}; } - VkExtent2D getExtent2D() const { return VkExtent2D{extent.width, extent.height}; } +#include - std::uint32_t getBindlessId() const - { - 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: +struct GPUImage { VkImage image; VkImageView imageView; VmaAllocation allocation; @@ -38,11 +18,29 @@ private: VkExtent3D extent; std::uint32_t mipLevels{1}; std::uint32_t numLayers{1}; + bool isCubemap{false}; 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 - - static const auto NULL_BINDLESS_ID = std::numeric_limits::max(); - }; -#endif //VIMAGE_H +#endif //GPUIMAGE_H diff --git a/destrum/include/destrum/Graphics/GfxDevice.h b/destrum/include/destrum/Graphics/GfxDevice.h index 6b97f53..4f2ae03 100644 --- a/destrum/include/destrum/Graphics/GfxDevice.h +++ b/destrum/include/destrum/Graphics/GfxDevice.h @@ -6,20 +6,25 @@ #include -#include +#include #include +#include #include #include #include #include +#include +#include #include "ImmediateExecuter.h" #include -using ImageId = std::uint32_t; -static const auto NULL_IMAGE_ID = std::numeric_limits::max(); +#include +#include + +#include "Util.h" namespace { using ImmediateExecuteFunction = std::function; @@ -40,14 +45,14 @@ public: void recreateSwapchain(int width, int height); VkCommandBuffer beginFrame(); - bool needsSwapchainRecreate() const { return swapchain.isDirty(); } + [[nodiscard]] bool needsSwapchainRecreate() const { return swapchain.isDirty(); } struct EndFrameProps { const VkClearColorValue clearColor{ {0.f, 0.f, 0.f, 1.f} }; 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 waitIdle(); @@ -67,6 +72,29 @@ public: 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 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: vkb::Instance instance; @@ -86,8 +114,11 @@ private: VulkanImmediateExecutor executor; - ImageId whiteImageId{NULL_IMAGE_ID}; - ImageId errorImageId{NULL_IMAGE_ID}; + ImageID whiteImageId{NULL_IMAGE_ID}; + ImageID errorImageId{NULL_IMAGE_ID}; + + ImageCache imageCache; + }; diff --git a/destrum/include/destrum/Graphics/ImageCache.h b/destrum/include/destrum/Graphics/ImageCache.h new file mode 100644 index 0000000..69c16c8 --- /dev/null +++ b/destrum/include/destrum/Graphics/ImageCache.h @@ -0,0 +1,56 @@ +#ifndef IMAGECACHE_H +#define IMAGECACHE_H + +#pragma once + +#include +#include +#include + +#include +#include + +// #include + +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 images; + GfxDevice& gfxDevice; + + struct LoadedImageInfo { + std::filesystem::path path; + VkFormat format; + VkImageUsageFlags usage; + bool mipMap; + }; + std::unordered_map loadedImagesInfo; + ImageID errorImageId{NULL_IMAGE_ID}; +}; + + +#endif //IMAGECACHE_H diff --git a/destrum/include/destrum/Graphics/Material.h b/destrum/include/destrum/Graphics/Material.h new file mode 100644 index 0000000..f6ca1b6 --- /dev/null +++ b/destrum/include/destrum/Graphics/Material.h @@ -0,0 +1,30 @@ +#ifndef MATERIAL_H +#define MATERIAL_H + +#include +#include + +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 diff --git a/destrum/include/destrum/Graphics/MaterialCache.h b/destrum/include/destrum/Graphics/MaterialCache.h new file mode 100644 index 0000000..35f13d3 --- /dev/null +++ b/destrum/include/destrum/Graphics/MaterialCache.h @@ -0,0 +1,42 @@ +#ifndef MATERIALCACHE_H +#define MATERIALCACHE_H + +#include + +#include +#include + +#include + +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 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 diff --git a/destrum/include/destrum/Graphics/MeshCache.h b/destrum/include/destrum/Graphics/MeshCache.h index 6fa3c2f..3da405b 100644 --- a/destrum/include/destrum/Graphics/MeshCache.h +++ b/destrum/include/destrum/Graphics/MeshCache.h @@ -1,4 +1,23 @@ #ifndef MESHCACHE_H #define MESHCACHE_H +#include + +#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 meshes; +}; + #endif //MESHCACHE_H diff --git a/destrum/include/destrum/Graphics/MeshDrawCommand.h b/destrum/include/destrum/Graphics/MeshDrawCommand.h new file mode 100644 index 0000000..f3aff65 --- /dev/null +++ b/destrum/include/destrum/Graphics/MeshDrawCommand.h @@ -0,0 +1,27 @@ +#ifndef MESHDRAWCOMMAND_H +#define MESHDRAWCOMMAND_H + +#include + +#include + + +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 diff --git a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h index 5b6ba03..fefd765 100644 --- a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h +++ b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h @@ -5,13 +5,27 @@ #include #include +#include +#include +#include +#include +#include + class MeshPipeline { public: MeshPipeline(); ~MeshPipeline(); 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& drawCommands, + const std::vector& sortedDrawCommands); private: @@ -19,6 +33,14 @@ private: std::unique_ptr m_pipeline; + struct PushConstants { + glm::mat4 transform; + VkDeviceAddress sceneDataBuffer; + VkDeviceAddress vertexBuffer; + std::uint32_t materialId; + std::uint32_t padding; + }; + }; #endif //MESHPIPELINE_H diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index d442fa9..9956ce7 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -1,10 +1,82 @@ #ifndef RENDERER_H #define RENDERER_H +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + class GameRenderer { 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: + void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate); + + MeshCache& meshCache; + // MaterialCache& materialCache; + + std::vector meshDrawCommands; + std::vector 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; }; #endif //RENDERER_H diff --git a/destrum/include/destrum/Graphics/Resources/Mesh.h b/destrum/include/destrum/Graphics/Resources/Mesh.h index f5b50d2..f0f0bd6 100644 --- a/destrum/include/destrum/Graphics/Resources/Mesh.h +++ b/destrum/include/destrum/Graphics/Resources/Mesh.h @@ -9,6 +9,8 @@ #include #include +#include + struct CPUMesh { std::vector indices; @@ -38,15 +40,66 @@ struct GPUMesh { // AABB glm::vec3 minPos; glm::vec3 maxPos; - math::Sphere boundingSphere; - - bool hasSkeleton{false}; - // skinned meshes only - GPUBuffer skinningDataBuffer; + // math::Sphere boundingSphere; }; -struct SkinnedMesh { - GPUBuffer skinnedVertexBuffer; +static std::vector vertices = { + // ======================= + // +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 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 }; diff --git a/destrum/include/destrum/Graphics/Resources/NBuffer.h b/destrum/include/destrum/Graphics/Resources/NBuffer.h new file mode 100644 index 0000000..caaa9ac --- /dev/null +++ b/destrum/include/destrum/Graphics/Resources/NBuffer.h @@ -0,0 +1,39 @@ +#ifndef NBUFFER_H +#define NBUFFER_H + +#include +#include + +#include + +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 stagingBuffers; + GPUBuffer gpuBuffer; + bool initialized{false}; +}; + +#endif //NBUFFER_H diff --git a/destrum/include/destrum/Graphics/Util.h b/destrum/include/destrum/Graphics/Util.h index 2e2775e..a5c769e 100644 --- a/destrum/include/destrum/Graphics/Util.h +++ b/destrum/include/destrum/Graphics/Util.h @@ -1,8 +1,11 @@ #ifndef UTIL_H #define UTIL_H +#include #include +#include "glm/vec4.hpp" + #define VK_CHECK(call) \ do { \ @@ -11,6 +14,18 @@ } while (0) 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( VkCommandBuffer cmd, VkImage image, @@ -35,5 +50,32 @@ namespace vkutil { int destW, int destH, 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 colorImageClearValue; + VkImageView depthImageView{VK_NULL_HANDLE}; + std::optional 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 diff --git a/destrum/include/destrum/Graphics/ids.h b/destrum/include/destrum/Graphics/ids.h new file mode 100644 index 0000000..6501758 --- /dev/null +++ b/destrum/include/destrum/Graphics/ids.h @@ -0,0 +1,19 @@ +#ifndef IDS_H +#define IDS_H + +#include +#include + +using MeshID = std::size_t; +constexpr MeshID NULL_MESH_ID = std::numeric_limits::max(); + +using ImageID = std::uint16_t; +constexpr ImageID NULL_IMAGE_ID = std::numeric_limits::max(); + +using MaterialId = std::uint32_t; +constexpr MaterialId NULL_MATERIAL_ID = std::numeric_limits::max(); + +using BindlessID = std::uint32_t; +constexpr BindlessID NULL_BINDLESS_ID = std::numeric_limits::max(); + +#endif //IDS_H diff --git a/destrum/include/destrum/Graphics/imageLoader.h b/destrum/include/destrum/Graphics/imageLoader.h new file mode 100644 index 0000000..339be54 --- /dev/null +++ b/destrum/include/destrum/Graphics/imageLoader.h @@ -0,0 +1,36 @@ +#ifndef IMAGELOADER_H +#define IMAGELOADER_H +#include + + +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 diff --git a/destrum/src/App.cpp b/destrum/src/App.cpp index e64c739..96b6cf5 100644 --- a/destrum/src/App.cpp +++ b/destrum/src/App.cpp @@ -6,6 +6,9 @@ #include "spdlog/spdlog.h" +App::App(): renderer{meshCache} { +} + void App::init(const AppParams& params) { m_params = params; @@ -30,11 +33,22 @@ void App::init(const AppParams& params) { } gfxDevice.init(window, params.appName, false); + renderer.init(gfxDevice, params.renderSize); //Read whole file auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt"); std::string fileStr(file.begin(), file.end()); 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(params.renderSize.x) / static_cast(params.renderSize.y); + camera.setAspectRatio(aspectRatio); + } void App::run() { @@ -97,8 +111,16 @@ void App::run() { } if (!gfxDevice.needsSwapchainRecreate()) { + renderer.beginDrawing(gfxDevice); + renderer.drawMesh(testMeshID, glm::mat4(1.f), 0); + renderer.endDrawing(); + + 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) { // Delay to not overload the CPU diff --git a/destrum/src/Graphics/Camera.cpp b/destrum/src/Graphics/Camera.cpp new file mode 100644 index 0000000..c3c0d44 --- /dev/null +++ b/destrum/src/Graphics/Camera.cpp @@ -0,0 +1,195 @@ +#include + +#include +#include +#include +#include +#include + +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(glfwGetWindowUserPointer(Window::gWindow)); +// +// #pragma region Keyboard Movement +// totalPitch = glm::clamp(totalPitch, -glm::half_pi() + 0.01f, glm::half_pi() - 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(), glm::half_pi()); +// +// 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(mouseX - lastMouseX); +// float yOffset = static_cast(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() + 0.01f, glm::half_pi() - 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); +} diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp new file mode 100644 index 0000000..c981c7f --- /dev/null +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -0,0 +1,565 @@ +#include + +#include "destrum/Graphics/Util.h" + +#define VOLK_IMPLEMENTATION +#include + +#define VMA_IMPLEMENTATION +#include +#include + +#include +#include + +#include + +#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 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, + ©Region); + + 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 +} diff --git a/destrum/src/Graphics/ImageCache.cpp b/destrum/src/Graphics/ImageCache.cpp new file mode 100644 index 0000000..726e9db --- /dev/null +++ b/destrum/src/Graphics/ImageCache.cpp @@ -0,0 +1,77 @@ +#include + +#include + +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(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(); +} diff --git a/destrum/src/Graphics/ImageLoader.cpp b/destrum/src/Graphics/ImageLoader.cpp new file mode 100644 index 0000000..e4fd2ab --- /dev/null +++ b/destrum/src/Graphics/ImageLoader.cpp @@ -0,0 +1,30 @@ +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +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 \ No newline at end of file diff --git a/destrum/src/graphics/ImmediateExecuter.cpp b/destrum/src/Graphics/ImmediateExecuter.cpp similarity index 100% rename from destrum/src/graphics/ImmediateExecuter.cpp rename to destrum/src/Graphics/ImmediateExecuter.cpp diff --git a/destrum/src/graphics/Init.cpp b/destrum/src/Graphics/Init.cpp similarity index 100% rename from destrum/src/graphics/Init.cpp rename to destrum/src/Graphics/Init.cpp diff --git a/destrum/src/Graphics/MeshCache.cpp b/destrum/src/Graphics/MeshCache.cpp new file mode 100644 index 0000000..0431856 --- /dev/null +++ b/destrum/src/Graphics/MeshCache.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include +#include +// #include + +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 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); + } +} diff --git a/destrum/src/graphics/Pipeline.cpp b/destrum/src/Graphics/Pipeline.cpp similarity index 100% rename from destrum/src/graphics/Pipeline.cpp rename to destrum/src/Graphics/Pipeline.cpp diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp new file mode 100644 index 0000000..83aeaf9 --- /dev/null +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -0,0 +1,130 @@ +#include +#include + +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(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( + 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& drawCommands, + const std::vector& 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); + } +} diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp new file mode 100644 index 0000000..88b2e58 --- /dev/null +++ b/destrum/src/Graphics/Renderer.cpp @@ -0,0 +1,149 @@ +#include + +#include + +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->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); + + } +} diff --git a/destrum/src/Graphics/Resources/GPUImage.cpp b/destrum/src/Graphics/Resources/GPUImage.cpp new file mode 100644 index 0000000..42c08af --- /dev/null +++ b/destrum/src/Graphics/Resources/GPUImage.cpp @@ -0,0 +1 @@ +#include diff --git a/destrum/src/Graphics/Resources/NBuffer.cpp b/destrum/src/Graphics/Resources/NBuffer.cpp new file mode 100644 index 0000000..af12e31 --- /dev/null +++ b/destrum/src/Graphics/Resources/NBuffer.cpp @@ -0,0 +1,122 @@ +#include + +#include + +#include +#include + +#include + +#include + +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(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 = ®ion, + }; + + 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); + } +} diff --git a/destrum/src/graphics/Swapchain.cpp b/destrum/src/Graphics/Swapchain.cpp similarity index 100% rename from destrum/src/graphics/Swapchain.cpp rename to destrum/src/Graphics/Swapchain.cpp diff --git a/destrum/src/Graphics/Util.cpp b/destrum/src/Graphics/Util.cpp new file mode 100644 index 0000000..d1d97ac --- /dev/null +++ b/destrum/src/Graphics/Util.cpp @@ -0,0 +1,316 @@ +#include +#include +#include + +#include + +#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 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; +} diff --git a/destrum/src/graphics/GfxDevice.cpp b/destrum/src/graphics/GfxDevice.cpp deleted file mode 100644 index a1d80c1..0000000 --- a/destrum/src/graphics/GfxDevice.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include - -#include "destrum/Graphics/Util.h" - -#define VOLK_IMPLEMENTATION -#include - -#define VMA_IMPLEMENTATION -#include - -#include -#include - -#include - -#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)); -} diff --git a/destrum/src/graphics/MeshCache.cpp b/destrum/src/graphics/MeshCache.cpp deleted file mode 100644 index 827fd54..0000000 --- a/destrum/src/graphics/MeshCache.cpp +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/destrum/src/graphics/Renderer.cpp b/destrum/src/graphics/Renderer.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/destrum/src/graphics/Util.cpp b/destrum/src/graphics/Util.cpp deleted file mode 100644 index 61e1645..0000000 --- a/destrum/src/graphics/Util.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include - -#include - -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); -} diff --git a/destrum/src/graphics/VImage.cpp b/destrum/src/graphics/VImage.cpp deleted file mode 100644 index f6561ce..0000000 --- a/destrum/src/graphics/VImage.cpp +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/destrum/third_party/CMakeLists.txt b/destrum/third_party/CMakeLists.txt index beb41b8..2e9a0a0 100644 --- a/destrum/third_party/CMakeLists.txt +++ b/destrum/third_party/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(glm) # stb add_subdirectory(stb) + # SDL if (NOT BUILD_SHARED_LIBS) set(SDL_SHARED_ENABLED_BY_DEFAULT OFF CACHE BOOL "Don't build SDL as shared lib") diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index 912994c..42cecb5 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -1,5 +1,4 @@ #include "Lightkeeper.h" -LightKeeper::LightKeeper() { - +LightKeeper::LightKeeper(): App() { }