We be kinda rendering
This commit is contained in:
82
cmake/compile_slang_shaders.cmake
Normal file
82
cmake/compile_slang_shaders.cmake
Normal file
@@ -0,0 +1,82 @@
|
||||
# Usage:
|
||||
# compile_slang_shaders(
|
||||
# TARGET compile_shaders # custom target name to create
|
||||
# SRC_DIR "${CMAKE_SOURCE_DIR}/assets_src/shaders"
|
||||
# OUT_DIR "${CMAKE_SOURCE_DIR}/assets_runtime/shaders"
|
||||
# # Optional:
|
||||
# GLOB_PATTERN "*.slang" # default: "*.slang"
|
||||
# EXTRA_ARGS -O3 # forwarded to slangc
|
||||
# ALL # if present: target is built by default
|
||||
# )
|
||||
#
|
||||
function(compile_slang_shaders)
|
||||
set(options ALL)
|
||||
set(oneValueArgs TARGET SRC_DIR OUT_DIR GLOB_PATTERN)
|
||||
set(multiValueArgs EXTRA_ARGS)
|
||||
cmake_parse_arguments(CSS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT CSS_TARGET)
|
||||
message(FATAL_ERROR "compile_slang_shaders: TARGET is required")
|
||||
endif()
|
||||
if(NOT CSS_SRC_DIR)
|
||||
message(FATAL_ERROR "compile_slang_shaders: SRC_DIR is required")
|
||||
endif()
|
||||
if(NOT CSS_OUT_DIR)
|
||||
message(FATAL_ERROR "compile_slang_shaders: OUT_DIR is required")
|
||||
endif()
|
||||
|
||||
if(NOT CSS_GLOB_PATTERN)
|
||||
set(CSS_GLOB_PATTERN "*.slang")
|
||||
endif()
|
||||
|
||||
find_program(SLANGC_EXE NAMES slangc slangc.exe REQUIRED)
|
||||
|
||||
# Collect shader sources recursively
|
||||
file(GLOB_RECURSE _SLANG_SOURCES
|
||||
CONFIGURE_DEPENDS
|
||||
"${CSS_SRC_DIR}/${CSS_GLOB_PATTERN}"
|
||||
)
|
||||
|
||||
if(_SLANG_SOURCES STREQUAL "")
|
||||
message(STATUS "compile_slang_shaders: No sources found in ${CSS_SRC_DIR} (pattern: ${CSS_GLOB_PATTERN})")
|
||||
endif()
|
||||
|
||||
# Ensure base output directory exists at configure time (nice-to-have)
|
||||
file(MAKE_DIRECTORY "${CSS_OUT_DIR}")
|
||||
|
||||
set(_SPV_OUTPUTS "")
|
||||
|
||||
foreach(_SLANG_FILE IN LISTS _SLANG_SOURCES)
|
||||
file(RELATIVE_PATH _REL_PATH "${CSS_SRC_DIR}" "${_SLANG_FILE}")
|
||||
get_filename_component(_REL_DIR "${_REL_PATH}" DIRECTORY)
|
||||
get_filename_component(_FILE_WE "${_SLANG_FILE}" NAME_WE)
|
||||
|
||||
set(_OUT_SUBDIR "${CSS_OUT_DIR}/${_REL_DIR}")
|
||||
set(_OUT_SPV "${_OUT_SUBDIR}/${_FILE_WE}.spv")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${_OUT_SPV}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${_OUT_SUBDIR}"
|
||||
COMMAND "${SLANGC_EXE}"
|
||||
"${_SLANG_FILE}"
|
||||
-target spirv
|
||||
-o "${_OUT_SPV}"
|
||||
${CSS_EXTRA_ARGS}
|
||||
DEPENDS "${_SLANG_FILE}"
|
||||
COMMENT "Compiling Slang: ${_REL_PATH} -> ${_OUT_SPV}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND _SPV_OUTPUTS "${_OUT_SPV}")
|
||||
endforeach()
|
||||
|
||||
# Create target
|
||||
if(CSS_ALL)
|
||||
add_custom_target(${CSS_TARGET} ALL DEPENDS ${_SPV_OUTPUTS})
|
||||
else()
|
||||
add_custom_target(${CSS_TARGET} DEPENDS ${_SPV_OUTPUTS})
|
||||
endif()
|
||||
|
||||
# Export outputs to caller if they want them (optional convenience)
|
||||
set(${CSS_TARGET}_OUTPUTS "${_SPV_OUTPUTS}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -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
|
||||
@@ -78,3 +87,13 @@ include(../cmake/compile_shaders.cmake)
|
||||
|
||||
compile_glsl_to_spv(destrum "${DESTRUM_SHADER_SRC}" "${DESTRUM_SHADER_OUT}" DESTRUM_SPV)
|
||||
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
|
||||
#)
|
||||
|
||||
35
destrum/assets_src/shaders/bindless.glsl
Normal file
35
destrum/assets_src/shaders/bindless.glsl
Normal file
@@ -0,0 +1,35 @@
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
layout (set = 0, binding = 0) uniform texture2D textures[];
|
||||
layout (set = 0, binding = 0) uniform texture2DMS texturesMS[];
|
||||
layout (set = 0, binding = 0) uniform textureCube textureCubes[];
|
||||
layout (set = 0, binding = 0) uniform texture2DArray textureArrays[];
|
||||
layout (set = 0, binding = 1) uniform sampler samplers[];
|
||||
|
||||
#define NEAREST_SAMPLER_ID 0
|
||||
#define LINEAR_SAMPLER_ID 1
|
||||
#define SHADOW_SAMPLER_ID 2
|
||||
|
||||
vec4 sampleTexture2DNearest(uint texID, vec2 uv) {
|
||||
return texture(nonuniformEXT(sampler2D(textures[texID], samplers[NEAREST_SAMPLER_ID])), uv);
|
||||
}
|
||||
|
||||
vec4 sampleTexture2DMSNearest(uint texID, ivec2 p, int s) {
|
||||
return texelFetch(nonuniformEXT(sampler2DMS(texturesMS[texID], samplers[NEAREST_SAMPLER_ID])), p, s);
|
||||
}
|
||||
|
||||
vec4 sampleTexture2DLinear(uint texID, vec2 uv) {
|
||||
return texture(nonuniformEXT(sampler2D(textures[texID], samplers[LINEAR_SAMPLER_ID])), uv);
|
||||
}
|
||||
|
||||
vec4 sampleTextureCubeNearest(uint texID, vec3 p) {
|
||||
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[NEAREST_SAMPLER_ID])), p);
|
||||
}
|
||||
|
||||
vec4 sampleTextureCubeLinear(uint texID, vec3 p) {
|
||||
return texture(nonuniformEXT(samplerCube(textureCubes[texID], samplers[LINEAR_SAMPLER_ID])), p);
|
||||
}
|
||||
|
||||
float sampleTextureArrayShadow(uint texID, vec4 p) {
|
||||
return texture(nonuniformEXT(sampler2DArrayShadow(textureArrays[texID], samplers[SHADOW_SAMPLER_ID])), p);
|
||||
}
|
||||
19
destrum/assets_src/shaders/materials.glsl
Normal file
19
destrum/assets_src/shaders/materials.glsl
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef MATERIALS_GLSL
|
||||
#define MATERIALS_GLSL
|
||||
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
struct MaterialData {
|
||||
vec4 baseColor;
|
||||
vec4 metallicRoughnessEmissive;
|
||||
uint diffuseTex;
|
||||
uint normalTex;
|
||||
uint metallicRoughnessTex;
|
||||
uint emissiveTex;
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) readonly buffer MaterialsBuffer {
|
||||
MaterialData data[];
|
||||
} materialsBuffer;
|
||||
|
||||
#endif // MATERIALS_GLSL
|
||||
125
destrum/assets_src/shaders/mesh.frag
Normal file
125
destrum/assets_src/shaders/mesh.frag
Normal file
@@ -0,0 +1,125 @@
|
||||
#version 460
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#include "bindless.glsl"
|
||||
#include "scene_data.glsl"
|
||||
#include "mesh_pcs.glsl"
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec4 inTangent;
|
||||
layout (location = 4) in mat3 inTBN;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
|
||||
|
||||
// vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV);
|
||||
// if (diffuse.a < 0.1) {
|
||||
// discard;
|
||||
// }
|
||||
//
|
||||
// vec3 baseColor = material.baseColor.rgb * diffuse.rgb;
|
||||
//
|
||||
// vec3 normal = normalize(inNormal).rgb;
|
||||
// if (inTangent != vec4(0.0)) {
|
||||
// // FIXME: sometimes Blender doesn't export tangents for some objects
|
||||
// // for some reason. When we will start computing tangents manually,
|
||||
// // this check can be removed
|
||||
// normal = sampleTexture2DLinear(material.normalTex, inUV).rgb;
|
||||
// // normal.y = 1 - normal.y; // flip to make OpenGL normal maps work
|
||||
// normal = inTBN * normalize(normal * 2.0 - 1.0);
|
||||
// normal = normalize(normal);
|
||||
// }
|
||||
//
|
||||
// #ifdef PBR
|
||||
// float metallicF = material.metallicRoughnessEmissive.r;
|
||||
// float roughnessF = material.metallicRoughnessEmissive.g;
|
||||
//
|
||||
// vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
||||
//
|
||||
// float roughness = roughnessF * metallicRoughness.g;
|
||||
// // roughness *= roughness; // from perceptual to linear
|
||||
// roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
||||
//
|
||||
// float metallic = metallicF * metallicRoughness.b;
|
||||
//
|
||||
// vec3 dielectricSpecular = vec3(0.04);
|
||||
// vec3 black = vec3(0.0);
|
||||
// vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
|
||||
// vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
|
||||
// #else
|
||||
// vec3 diffuseColor = baseColor;
|
||||
// float metallic = 0.f;
|
||||
// float roughness = 1.f;
|
||||
// vec3 f0 = vec3(0.0);
|
||||
// #endif
|
||||
//
|
||||
// vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
||||
// vec3 fragPos = inPos.xyz;
|
||||
// vec3 n = normal;
|
||||
// vec3 v = normalize(cameraPos - fragPos);
|
||||
//
|
||||
// // diffuseColor = vec3(1.0);
|
||||
// // baseColor = vec3(1.0);
|
||||
//
|
||||
// vec3 fragColor = vec3(0.0);
|
||||
// for (int i = 0; i < pcs.sceneData.numLights; i++) {
|
||||
// Light light = pcs.sceneData.lights.data[i];
|
||||
//
|
||||
// vec3 l = light.direction;
|
||||
// if (light.type != TYPE_DIRECTIONAL_LIGHT) {
|
||||
// l = normalize(light.position - fragPos);
|
||||
// }
|
||||
// float NoL = clamp(dot(n, l), 0.0, 1.0);
|
||||
//
|
||||
// float occlusion = 1.0;
|
||||
// if (light.type == TYPE_DIRECTIONAL_LIGHT) {
|
||||
// occlusion = calculateCSMOcclusion(
|
||||
// fragPos, cameraPos, NoL,
|
||||
// pcs.sceneData.csmShadowMapId,
|
||||
// pcs.sceneData.cascadeFarPlaneZs,
|
||||
// pcs.sceneData.csmLightSpaceTMs);
|
||||
// } else if (light.type == TYPE_POINT_LIGHT && light.shadowMapID != 0) {
|
||||
// occlusion = calculatePointShadow(
|
||||
// fragPos, light.position, NoL,
|
||||
// light.shadowMapID,
|
||||
// pcs.sceneData.pointLightFarPlane);
|
||||
// }
|
||||
//
|
||||
// fragColor += calculateLight(light, fragPos, n, v, l,
|
||||
// diffuseColor, roughness, metallic, f0, occlusion);
|
||||
// }
|
||||
//
|
||||
// // emissive
|
||||
// float emissiveF = material.metallicRoughnessEmissive.b;
|
||||
// vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
||||
// fragColor += emissiveColor;
|
||||
//
|
||||
// // ambient
|
||||
// fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||
//
|
||||
// #if 0
|
||||
// // CSM DEBUG
|
||||
// uint cascadeIndex = chooseCascade(inPos, cameraPos, pcs.sceneData.cascadeFarPlaneZs);
|
||||
// fragColor *= debugShadowsFactor(cascadeIndex);
|
||||
// #endif
|
||||
//
|
||||
// #if 0
|
||||
// // TANGENT DEBUG
|
||||
// if (inTangent == vec4(0.0)) {
|
||||
// fragColor = vec3(1.0f, 0.0f, 0.0f);
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// #if 1
|
||||
// // NORMAL DEBUG
|
||||
// // fragColor = normal;
|
||||
// #endif
|
||||
|
||||
outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
34
destrum/assets_src/shaders/mesh.vert
Normal file
34
destrum/assets_src/shaders/mesh.vert
Normal file
@@ -0,0 +1,34 @@
|
||||
#version 460
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#include "mesh_pcs.glsl"
|
||||
|
||||
layout (location = 0) out vec3 outPos;
|
||||
layout (location = 1) out vec2 outUV;
|
||||
layout (location = 2) out vec3 outNormal;
|
||||
layout (location = 3) out vec4 outTangent;
|
||||
layout (location = 4) out mat3 outTBN;
|
||||
|
||||
void main()
|
||||
{
|
||||
Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex];
|
||||
|
||||
vec4 worldPos = pcs.transform * vec4(v.position, 1.0f);
|
||||
|
||||
gl_Position = pcs.sceneData.viewProj * worldPos;
|
||||
outPos = worldPos.xyz;
|
||||
outUV = vec2(v.uv_x, v.uv_y);
|
||||
// A bit inefficient, but okay - this is needed for non-uniform scale
|
||||
// models. See: http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/
|
||||
// Simpler case, when everything is uniform
|
||||
// outNormal = (pcs.transform * vec4(v.normal, 0.0)).xyz;
|
||||
outNormal = mat3(transpose(inverse(pcs.transform))) * v.normal;
|
||||
|
||||
outTangent = v.tangent;
|
||||
|
||||
vec3 T = normalize(vec3(pcs.transform * v.tangent));
|
||||
vec3 N = normalize(outNormal);
|
||||
vec3 B = cross(N, T) * v.tangent.w;
|
||||
outTBN = mat3(T, B, N);
|
||||
}
|
||||
15
destrum/assets_src/shaders/mesh_pcs.glsl
Normal file
15
destrum/assets_src/shaders/mesh_pcs.glsl
Normal file
@@ -0,0 +1,15 @@
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
#extension GL_EXT_scalar_block_layout: require
|
||||
|
||||
#include "scene_data.glsl"
|
||||
#include "vertex.glsl"
|
||||
|
||||
layout (push_constant, scalar) uniform constants
|
||||
{
|
||||
mat4 transform;
|
||||
SceneDataBuffer sceneData;
|
||||
VertexBuffer vertexBuffer;
|
||||
uint materialID;
|
||||
uint padding;
|
||||
} pcs;
|
||||
|
||||
28
destrum/assets_src/shaders/scene_data.glsl
Normal file
28
destrum/assets_src/shaders/scene_data.glsl
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef SCENE_DATA_GLSL
|
||||
#define SCENE_DATA_GLSL
|
||||
|
||||
|
||||
#extension GL_EXT_scalar_block_layout: require
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
#include "materials.glsl"
|
||||
|
||||
layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
||||
// camera
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 viewProj;
|
||||
vec4 cameraPos;
|
||||
|
||||
// ambient
|
||||
vec3 ambientColor;
|
||||
float ambientIntensity;
|
||||
|
||||
// fog
|
||||
vec3 fogColor;
|
||||
float fogDensity;
|
||||
|
||||
MaterialsBuffer materials;
|
||||
} sceneDataBuffer;
|
||||
|
||||
#endif // SCENE_DATA_GLSL
|
||||
19
destrum/assets_src/shaders/vertex.glsl
Normal file
19
destrum/assets_src/shaders/vertex.glsl
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef VERTEX_GLSL
|
||||
#define VERTEX_GLSL
|
||||
|
||||
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
struct Vertex {
|
||||
vec3 position;
|
||||
float uv_x;
|
||||
vec3 normal;
|
||||
float uv_y;
|
||||
vec4 tangent;
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) readonly buffer VertexBuffer {
|
||||
Vertex vertices[];
|
||||
};
|
||||
|
||||
#endif // VERTEX_GLSL
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
#include <destrum/Graphics/Renderer.h>
|
||||
|
||||
|
||||
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};
|
||||
|
||||
158
destrum/include/destrum/Graphics/Camera.h
Normal file
158
destrum/include/destrum/Graphics/Camera.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#ifndef VCAMERA_H
|
||||
#define VCAMERA_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
struct Frustum {
|
||||
glm::vec4 planes[6]; // left, right, bottom, top, near, far
|
||||
|
||||
void update(const glm::mat4& vpMatrix) {
|
||||
// Extract frustum planes from VP matrix (row-major, GLM style)
|
||||
glm::mat4 m = vpMatrix;
|
||||
|
||||
// Left
|
||||
planes[0] = glm::vec4(
|
||||
m[0][3] + m[0][0],
|
||||
m[1][3] + m[1][0],
|
||||
m[2][3] + m[2][0],
|
||||
m[3][3] + m[3][0]
|
||||
);
|
||||
// Right
|
||||
planes[1] = glm::vec4(
|
||||
m[0][3] - m[0][0],
|
||||
m[1][3] - m[1][0],
|
||||
m[2][3] - m[2][0],
|
||||
m[3][3] - m[3][0]
|
||||
);
|
||||
// Bottom
|
||||
planes[2] = glm::vec4(
|
||||
m[0][3] + m[0][1],
|
||||
m[1][3] + m[1][1],
|
||||
m[2][3] + m[2][1],
|
||||
m[3][3] + m[3][1]
|
||||
);
|
||||
// Top
|
||||
planes[3] = glm::vec4(
|
||||
m[0][3] - m[0][1],
|
||||
m[1][3] - m[1][1],
|
||||
m[2][3] - m[2][1],
|
||||
m[3][3] - m[3][1]
|
||||
);
|
||||
// Near
|
||||
planes[4] = glm::vec4(
|
||||
m[0][3] + m[0][2],
|
||||
m[1][3] + m[1][2],
|
||||
m[2][3] + m[2][2],
|
||||
m[3][3] + m[3][2]
|
||||
);
|
||||
// Far
|
||||
planes[5] = glm::vec4(
|
||||
m[0][3] - m[0][2],
|
||||
m[1][3] - m[1][2],
|
||||
m[2][3] - m[2][2],
|
||||
m[3][3] - m[3][2]
|
||||
);
|
||||
|
||||
// Normalize all planes
|
||||
for (auto& plane: planes) {
|
||||
float length = glm::length(glm::vec3(plane));
|
||||
plane /= length;
|
||||
}
|
||||
}
|
||||
|
||||
// Fast AABB-Frustum test
|
||||
// [[nodiscard]] bool isBoxVisible(const AABB& box) const {
|
||||
// for (const auto& plane: planes) {
|
||||
// glm::vec3 positive = box.min;
|
||||
// if (plane.x >= 0) positive.x = box.max.x;
|
||||
// if (plane.y >= 0) positive.y = box.max.y;
|
||||
// if (plane.z >= 0) positive.z = box.max.z;
|
||||
//
|
||||
// if (glm::dot(glm::vec3(plane), positive) + plane.w < 0) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
explicit Camera(const glm::vec3& position, const glm::vec3& up);
|
||||
void Update(float deltaTime);
|
||||
|
||||
void CalculateViewMatrix();
|
||||
void CalculateProjectionMatrix();
|
||||
|
||||
[[nodiscard]] glm::mat4 GetViewMatrix() const { return m_viewMatrix; }
|
||||
[[nodiscard]] glm::mat4 GetProjectionMatrix() const { return m_projectionMatrix; }
|
||||
|
||||
[[nodiscard]] glm::mat4 GetViewProjectionMatrix() const { return m_projectionMatrix * m_viewMatrix; }
|
||||
|
||||
void setAspectRatio(float aspectRatio) {
|
||||
m_aspectRatio = aspectRatio;
|
||||
this->CalculateViewMatrix();
|
||||
this->CalculateProjectionMatrix();
|
||||
}
|
||||
|
||||
glm::vec3 m_position{2.f, 0, 0};
|
||||
|
||||
void Translate(const glm::vec3& translation) {
|
||||
m_position += translation;
|
||||
}
|
||||
|
||||
void SetTarget(const glm::vec3& target);
|
||||
void ClearTarget();
|
||||
void Target(const glm::vec3& target);
|
||||
[[nodiscard]] glm::vec3 GetTarget() const { return m_target; }
|
||||
[[nodiscard]] bool IsTargetting() const { return m_useTarget; }
|
||||
|
||||
[[nodiscard]] const Frustum& GetFrustum() const { return m_frustum; }
|
||||
|
||||
[[nodiscard]] glm::vec3 GetPosition() const { return m_position; }
|
||||
[[nodiscard]] glm::vec3 GetForward() const { return m_forward; }
|
||||
[[nodiscard]] glm::vec3 GetUp() const { return m_up; }
|
||||
[[nodiscard]] glm::vec3 GetRight() const { return m_right; }
|
||||
|
||||
void SetMovementSpeed(float speed) { m_movementSpeed = speed; }
|
||||
[[nodiscard]] float GetMovementSpeed() const { return m_movementSpeed; }
|
||||
|
||||
private:
|
||||
float fovAngle{90.f};
|
||||
float fov{tanf(glm::radians(fovAngle) / 2.f)};
|
||||
|
||||
glm::vec3 m_forward{1, 0, 0};
|
||||
glm::vec3 m_up{0, 1, 0};
|
||||
glm::vec3 m_right{0, 0, 1};
|
||||
|
||||
glm::vec3 m_target{0.0f, 0.0f, 0.0f};
|
||||
bool m_useTarget{false};
|
||||
|
||||
float totalPitch{0.0f};
|
||||
float totalYaw{0.0f};
|
||||
|
||||
glm::mat4 m_viewMatrix{};
|
||||
glm::mat4 m_invMatrix{};
|
||||
|
||||
glm::mat4 m_projectionMatrix{};
|
||||
|
||||
float m_aspectRatio{};
|
||||
float m_cameraSpeed{};
|
||||
float m_cameraSensitivity{};
|
||||
|
||||
float m_zNear{0.001f};
|
||||
float m_zFar{1000.0f};
|
||||
|
||||
float m_iso{100.f};
|
||||
float m_aperture{2.8f};
|
||||
float m_shutterSpeed{1.f / 60.f};
|
||||
|
||||
float m_scrollSpeed = 2.0f;
|
||||
|
||||
Frustum m_frustum{};
|
||||
|
||||
float m_movementSpeed{2.0f};
|
||||
};
|
||||
|
||||
#endif //VCAMERA_H
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef VIMAGE_H
|
||||
#define VIMAGE_H
|
||||
#ifndef GPUIMAGE_H
|
||||
#define GPUIMAGE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -7,29 +7,9 @@
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class VImage {
|
||||
public:
|
||||
glm::ivec2 getSize2D() const { return glm::ivec2{extent.width, extent.height}; }
|
||||
VkExtent2D getExtent2D() const { return VkExtent2D{extent.width, extent.height}; }
|
||||
#include <destrum/Graphics/ids.h>
|
||||
|
||||
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<std::uint32_t>::max();
|
||||
|
||||
};
|
||||
|
||||
#endif //VIMAGE_H
|
||||
#endif //GPUIMAGE_H
|
||||
@@ -6,20 +6,25 @@
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <destrum/Graphics/VImage.h>
|
||||
#include <destrum/Graphics/GPUImage.h>
|
||||
#include <destrum/Graphics/Swapchain.h>
|
||||
#include <destrum/Graphics/ImageCache.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <volk.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "ImmediateExecuter.h"
|
||||
|
||||
#include <destrum/Graphics/Swapchain.h>
|
||||
|
||||
using ImageId = std::uint32_t;
|
||||
static const auto NULL_IMAGE_ID = std::numeric_limits<std::uint32_t>::max();
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
#include <destrum/Graphics/ids.h>
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
namespace {
|
||||
using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>;
|
||||
@@ -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<VmaAllocationCreateInfo> customAllocationCreateInfo = std::nullopt) const;
|
||||
GPUImage loadImageFromFileRaw(const std::filesystem::path& path, VkFormat format, VkImageUsageFlags usage, bool mipMap) const;
|
||||
void destroyImage(const GPUImage& image) const;
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
56
destrum/include/destrum/Graphics/ImageCache.h
Normal file
56
destrum/include/destrum/Graphics/ImageCache.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef IMAGECACHE_H
|
||||
#define IMAGECACHE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Graphics/ids.h>
|
||||
#include <destrum/Graphics/GPUImage.h>
|
||||
|
||||
// #include <destrum/Graphics/Vulkan/BindlessSetManager.h>
|
||||
|
||||
class GfxDevice;
|
||||
|
||||
class ImageCache {
|
||||
friend class ResourcesInspector;
|
||||
|
||||
public:
|
||||
ImageCache(GfxDevice& gfxDevice);
|
||||
|
||||
ImageID loadImageFromFile(
|
||||
const std::filesystem::path& path,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags usage,
|
||||
bool mipMap);
|
||||
|
||||
ImageID addImage(GPUImage image);
|
||||
ImageID addImage(ImageID id, GPUImage image);
|
||||
const GPUImage& getImage(ImageID id) const;
|
||||
|
||||
ImageID getFreeImageId() const;
|
||||
|
||||
void destroyImages();
|
||||
|
||||
// BindlessSetManager bindlessSetManager;
|
||||
|
||||
void setErrorImageId(ImageID id) { errorImageId = id; }
|
||||
|
||||
private:
|
||||
std::vector<GPUImage> images;
|
||||
GfxDevice& gfxDevice;
|
||||
|
||||
struct LoadedImageInfo {
|
||||
std::filesystem::path path;
|
||||
VkFormat format;
|
||||
VkImageUsageFlags usage;
|
||||
bool mipMap;
|
||||
};
|
||||
std::unordered_map<ImageID, LoadedImageInfo> loadedImagesInfo;
|
||||
ImageID errorImageId{NULL_IMAGE_ID};
|
||||
};
|
||||
|
||||
|
||||
#endif //IMAGECACHE_H
|
||||
30
destrum/include/destrum/Graphics/Material.h
Normal file
30
destrum/include/destrum/Graphics/Material.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef MATERIAL_H
|
||||
#define MATERIAL_H
|
||||
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct MaterialData {
|
||||
glm::vec3 baseColor;
|
||||
glm::vec4 metalRoughnessEmissive;
|
||||
std::uint32_t diffuseTex;
|
||||
std::uint32_t normalTex;
|
||||
std::uint32_t metallicRoughnessTex;
|
||||
std::uint32_t emissiveTex;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
glm::vec3 baseColor{1.f, 1.f, 1.f};
|
||||
float metallicFactor{0.f};
|
||||
float roughnessFactor{0.7f};
|
||||
float emissiveFactor{0.f};
|
||||
|
||||
ImageID diffuseTexture{NULL_IMAGE_ID};
|
||||
// ImageId normalMapTexture{NULL_IMAGE_ID};
|
||||
// ImageId metallicRoughnessTexture{NULL_IMAGE_ID};
|
||||
// ImageId emissiveTexture{NULL_IMAGE_ID};
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
#endif //MATERIAL_H
|
||||
42
destrum/include/destrum/Graphics/MaterialCache.h
Normal file
42
destrum/include/destrum/Graphics/MaterialCache.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef MATERIALCACHE_H
|
||||
#define MATERIALCACHE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Graphics/ids.h>
|
||||
#include <destrum/Graphics/Material.h>
|
||||
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
|
||||
class GfxDevice;
|
||||
|
||||
class MaterialCache {
|
||||
friend class ResourcesInspector;
|
||||
|
||||
public:
|
||||
void init(GfxDevice& gfxDevice);
|
||||
void cleanup(GfxDevice& gfxDevice);
|
||||
|
||||
MaterialId addMaterial(GfxDevice& gfxDevice, Material material);
|
||||
const Material& getMaterial(MaterialId id) const;
|
||||
|
||||
MaterialId getFreeMaterialId() const;
|
||||
MaterialId getPlaceholderMaterialId() const;
|
||||
|
||||
const GPUBuffer& getMaterialDataBuffer() const { return materialDataBuffer; }
|
||||
VkDeviceAddress getMaterialDataBufferAddress() const { return materialDataBuffer.address; }
|
||||
|
||||
private:
|
||||
std::vector<Material> materials;
|
||||
|
||||
static const auto MAX_MATERIALS = 1000;
|
||||
GPUBuffer materialDataBuffer;
|
||||
|
||||
// material which is used for meshes without materials
|
||||
MaterialId placeholderMaterialId{NULL_MATERIAL_ID};
|
||||
|
||||
ImageID defaultNormalMapTextureID{NULL_IMAGE_ID};
|
||||
};
|
||||
|
||||
|
||||
#endif //MATERIALCACHE_H
|
||||
@@ -1,4 +1,23 @@
|
||||
#ifndef MESHCACHE_H
|
||||
#define MESHCACHE_H
|
||||
|
||||
#include <destrum/Graphics/Resources/Mesh.h>
|
||||
|
||||
#include "ids.h"
|
||||
|
||||
class GfxDevice;
|
||||
|
||||
class MeshCache {
|
||||
public:
|
||||
void cleanup(const GfxDevice& gfxDevice);
|
||||
|
||||
MeshID addMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh);
|
||||
const GPUMesh& getMesh(MeshID id) const;
|
||||
|
||||
private:
|
||||
void uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh& gpuMesh) const;
|
||||
|
||||
std::vector<GPUMesh> meshes;
|
||||
};
|
||||
|
||||
#endif //MESHCACHE_H
|
||||
|
||||
27
destrum/include/destrum/Graphics/MeshDrawCommand.h
Normal file
27
destrum/include/destrum/Graphics/MeshDrawCommand.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef MESHDRAWCOMMAND_H
|
||||
#define MESHDRAWCOMMAND_H
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <destrum/Graphics/ids.h>
|
||||
|
||||
|
||||
struct MeshDrawCommand {
|
||||
MeshID meshId;
|
||||
glm::mat4 transformMatrix;
|
||||
|
||||
// for frustum culling
|
||||
// math::Sphere worldBoundingSphere;
|
||||
|
||||
// If set - mesh will be drawn with overrideMaterialId
|
||||
// instead of whatever material the mesh has
|
||||
MaterialId materialId{NULL_MATERIAL_ID};
|
||||
|
||||
bool castShadow{true};
|
||||
|
||||
// skinned meshes only
|
||||
// const SkinnedMesh* skinnedMesh{nullptr};
|
||||
// std::uint32_t jointMatricesStartIndex;
|
||||
};
|
||||
|
||||
#endif //MESHDRAWCOMMAND_H
|
||||
@@ -5,13 +5,27 @@
|
||||
#include <memory>
|
||||
#include <destrum/Graphics/Pipeline.h>
|
||||
|
||||
#include <destrum/Graphics/MeshCache.h>
|
||||
#include <destrum/Graphics/MaterialCache.h>
|
||||
#include <destrum/Graphics/Camera.h>
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
#include <destrum/Graphics/MeshDrawCommand.h>
|
||||
|
||||
class MeshPipeline {
|
||||
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<MeshDrawCommand>& drawCommands,
|
||||
const std::vector<std::size_t>& sortedDrawCommands);
|
||||
|
||||
|
||||
private:
|
||||
@@ -19,6 +33,14 @@ private:
|
||||
|
||||
std::unique_ptr<Pipeline> m_pipeline;
|
||||
|
||||
struct PushConstants {
|
||||
glm::mat4 transform;
|
||||
VkDeviceAddress sceneDataBuffer;
|
||||
VkDeviceAddress vertexBuffer;
|
||||
std::uint32_t materialId;
|
||||
std::uint32_t padding;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //MESHPIPELINE_H
|
||||
|
||||
@@ -1,10 +1,82 @@
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <destrum/Graphics/Camera.h>
|
||||
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
|
||||
#include <destrum/Graphics/MeshCache.h>
|
||||
#include <destrum/Graphics/ids.h>
|
||||
#include <destrum/Graphics/MeshDrawCommand.h>
|
||||
#include <destrum/Graphics/Resources/NBuffer.h>
|
||||
#include <destrum/Graphics/MaterialCache.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class GameRenderer {
|
||||
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<MeshDrawCommand> meshDrawCommands;
|
||||
std::vector<std::size_t> sortedMeshDrawCommands;
|
||||
|
||||
VkFormat drawImageFormat{VK_FORMAT_R16G16B16A16_SFLOAT};
|
||||
VkFormat depthImageFormat{VK_FORMAT_D32_SFLOAT};
|
||||
|
||||
ImageID drawImageId{NULL_IMAGE_ID};
|
||||
ImageID depthImageId{NULL_IMAGE_ID};
|
||||
|
||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||
|
||||
|
||||
struct GPUSceneData {
|
||||
// camera
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
glm::mat4 viewProj;
|
||||
glm::vec4 cameraPos;
|
||||
|
||||
// ambient
|
||||
glm::vec3 ambientColor;
|
||||
float ambientIntensity;
|
||||
|
||||
// fog
|
||||
glm::vec3 fogColor;
|
||||
float fogDensity;
|
||||
|
||||
VkDeviceAddress materialsBuffer;
|
||||
};
|
||||
|
||||
NBuffer sceneDataBuffer;
|
||||
|
||||
MaterialCache materialCache;
|
||||
|
||||
|
||||
std::unique_ptr<MeshPipeline> meshPipeline;
|
||||
};
|
||||
|
||||
#endif //RENDERER_H
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
|
||||
struct CPUMesh {
|
||||
std::vector<std::uint32_t> 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<CPUMesh::Vertex> 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<uint32_t> indices = {
|
||||
0, 1, 2, 2, 3, 0, // Front
|
||||
4, 5, 6, 6, 7, 4, // Back
|
||||
8, 9,10, 10,11, 8, // Right
|
||||
12,13,14, 14,15,12, // Left
|
||||
16,17,18, 18,19,16, // Top
|
||||
20,21,22, 22,23,20 // Bottom
|
||||
};
|
||||
|
||||
|
||||
|
||||
39
destrum/include/destrum/Graphics/Resources/NBuffer.h
Normal file
39
destrum/include/destrum/Graphics/Resources/NBuffer.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef NBUFFER_H
|
||||
#define NBUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Graphics/Resources/Buffer.h>
|
||||
|
||||
class GfxDevice;
|
||||
|
||||
class NBuffer {
|
||||
public:
|
||||
void init(
|
||||
GfxDevice& gfxDevice,
|
||||
VkBufferUsageFlags usage,
|
||||
std::size_t dataSize,
|
||||
const std::string& debugName = "");
|
||||
|
||||
void cleanup(GfxDevice& gfxDevice);
|
||||
|
||||
void uploadNewData(
|
||||
VkCommandBuffer cmd,
|
||||
std::size_t frameIndex,
|
||||
void* newData,
|
||||
std::size_t dataSize,
|
||||
std::size_t offset = 0,
|
||||
bool sync = true) const;
|
||||
|
||||
const GPUBuffer& getBuffer() const { return gpuBuffer; }
|
||||
|
||||
private:
|
||||
std::size_t framesInFlight{0};
|
||||
std::size_t gpuBufferSize{0};
|
||||
std::vector<GPUBuffer> stagingBuffers;
|
||||
GPUBuffer gpuBuffer;
|
||||
bool initialized{false};
|
||||
};
|
||||
|
||||
#endif //NBUFFER_H
|
||||
@@ -1,8 +1,11 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#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<glm::vec4> colorImageClearValue;
|
||||
VkImageView depthImageView{VK_NULL_HANDLE};
|
||||
std::optional<float> depthImageClearValue;
|
||||
};
|
||||
|
||||
struct RenderInfo {
|
||||
VkRenderingAttachmentInfo colorAttachment;
|
||||
VkRenderingAttachmentInfo depthAttachment;
|
||||
VkRenderingInfo renderingInfo;
|
||||
};
|
||||
|
||||
RenderInfo createRenderingInfo(const RenderingInfoParams& params);
|
||||
|
||||
VkShaderModule loadShaderModule(const std::filesystem::path& path, VkDevice device);
|
||||
|
||||
}
|
||||
#endif //UTIL_H
|
||||
|
||||
19
destrum/include/destrum/Graphics/ids.h
Normal file
19
destrum/include/destrum/Graphics/ids.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef IDS_H
|
||||
#define IDS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
using MeshID = std::size_t;
|
||||
constexpr MeshID NULL_MESH_ID = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
using ImageID = std::uint16_t;
|
||||
constexpr ImageID NULL_IMAGE_ID = std::numeric_limits<std::uint16_t>::max();
|
||||
|
||||
using MaterialId = std::uint32_t;
|
||||
constexpr MaterialId NULL_MATERIAL_ID = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
using BindlessID = std::uint32_t;
|
||||
constexpr BindlessID NULL_BINDLESS_ID = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
#endif //IDS_H
|
||||
36
destrum/include/destrum/Graphics/imageLoader.h
Normal file
36
destrum/include/destrum/Graphics/imageLoader.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef IMAGELOADER_H
|
||||
#define IMAGELOADER_H
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
struct ImageData {
|
||||
ImageData() = default;
|
||||
~ImageData();
|
||||
|
||||
// move only
|
||||
ImageData(ImageData&& o) = default;
|
||||
ImageData& operator=(ImageData&& o) = default;
|
||||
|
||||
// no copies
|
||||
ImageData(const ImageData& o) = delete;
|
||||
ImageData& operator=(const ImageData& o) = delete;
|
||||
|
||||
// data
|
||||
unsigned char* pixels{nullptr};
|
||||
int width{0};
|
||||
int height{0};
|
||||
int channels{0};
|
||||
|
||||
// HDR only
|
||||
float* hdrPixels{nullptr};
|
||||
bool hdr{false};
|
||||
int comp{0};
|
||||
|
||||
bool shouldSTBFree{false};
|
||||
};
|
||||
|
||||
namespace util {
|
||||
ImageData loadImage(const std::filesystem::path& p);
|
||||
}
|
||||
|
||||
#endif //IMAGELOADER_H
|
||||
@@ -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<float>(params.renderSize.x) / static_cast<float>(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
|
||||
|
||||
195
destrum/src/Graphics/Camera.cpp
Normal file
195
destrum/src/Graphics/Camera.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include <destrum/Graphics/Camera.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
Camera::Camera(const glm::vec3& position, const glm::vec3& up): m_position{position}, m_up{up} {
|
||||
}
|
||||
|
||||
void Camera::Update(float deltaTime) {
|
||||
// const auto MyWindow = static_cast<Window*>(glfwGetWindowUserPointer(Window::gWindow));
|
||||
//
|
||||
// #pragma region Keyboard Movement
|
||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
||||
//
|
||||
// float MovementSpeed = m_movementSpeed;
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) {
|
||||
// MovementSpeed *= 2.0f;
|
||||
// }
|
||||
//
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_W) == GLFW_PRESS) {
|
||||
// m_position += m_forward * deltaTime * MovementSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_A) == GLFW_PRESS) {
|
||||
// m_position -= m_right * deltaTime * MovementSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_S) == GLFW_PRESS) {
|
||||
// m_position -= m_forward * deltaTime * MovementSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_D) == GLFW_PRESS) {
|
||||
// m_position += m_right * deltaTime * MovementSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_E) == GLFW_PRESS) {
|
||||
// m_position += m_up * deltaTime * MovementSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_Q) == GLFW_PRESS) {
|
||||
// m_position -= m_up * deltaTime * MovementSpeed;
|
||||
// }
|
||||
//
|
||||
// // Looking around with arrow keys
|
||||
// constexpr float lookSpeed = 1.0f * glm::radians(1.f);
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
|
||||
// totalPitch += lookSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
|
||||
// totalPitch -= lookSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
|
||||
// totalYaw -= lookSpeed;
|
||||
// }
|
||||
// if (glfwGetKey(Window::gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
|
||||
// totalYaw += lookSpeed;
|
||||
// }
|
||||
//
|
||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>(), glm::half_pi<float>());
|
||||
//
|
||||
// const glm::mat4 yawMatrix = glm::rotate(glm::mat4(1.0f), -totalYaw, glm::vec3(0, 1, 0));
|
||||
// const glm::mat4 pitchMatrix = glm::rotate(glm::mat4(1.0f), totalPitch, glm::vec3(0, 0, 1));
|
||||
//
|
||||
// const glm::mat4 rotationMatrix = yawMatrix * pitchMatrix;
|
||||
//
|
||||
// m_forward = glm::vec3(rotationMatrix * glm::vec4(1, 0, 0, 0));
|
||||
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||
// m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||
// #pragma endregion
|
||||
//
|
||||
// #pragma region Mouse Looking
|
||||
// static bool wasCursorLockedLastFrame = false;
|
||||
// static double lastMouseX = 0.0;
|
||||
// static double lastMouseY = 0.0;
|
||||
// static bool firstMouse = true;
|
||||
//
|
||||
// bool isLocked = MyWindow->isCursorLocked();
|
||||
// if (isLocked) {
|
||||
// double mouseX, mouseY;
|
||||
// glfwGetCursorPos(Window::gWindow, &mouseX, &mouseY);
|
||||
//
|
||||
// // Reset mouse reference when locking the cursor (prevents jump)
|
||||
// if (!wasCursorLockedLastFrame) {
|
||||
// lastMouseX = mouseX;
|
||||
// lastMouseY = mouseY;
|
||||
// firstMouse = false;
|
||||
// }
|
||||
//
|
||||
// float xOffset = static_cast<float>(mouseX - lastMouseX);
|
||||
// float yOffset = static_cast<float>(lastMouseY - mouseY); // Reversed: y goes up
|
||||
//
|
||||
// lastMouseX = mouseX;
|
||||
// lastMouseY = mouseY;
|
||||
//
|
||||
// constexpr float sensitivity = 0.002f;
|
||||
// xOffset *= sensitivity;
|
||||
// yOffset *= sensitivity;
|
||||
//
|
||||
// totalYaw += xOffset;
|
||||
// totalPitch += yOffset;
|
||||
//
|
||||
// totalPitch = glm::clamp(totalPitch, -glm::half_pi<float>() + 0.01f, glm::half_pi<float>() - 0.01f);
|
||||
//
|
||||
// m_frustum.update(m_projectionMatrix * m_viewMatrix);
|
||||
// }
|
||||
//
|
||||
// wasCursorLockedLastFrame = isLocked;
|
||||
// #pragma endregion
|
||||
//
|
||||
// #pragma region Controller Movement
|
||||
//
|
||||
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1)) {
|
||||
// GLFWgamepadstate state;
|
||||
// if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1) && glfwGetGamepadState(GLFW_JOYSTICK_1, &state)) {
|
||||
// float moveSpeed = MovementSpeed * deltaTime;
|
||||
// const float rotSpeed = 1.5f * deltaTime;
|
||||
//
|
||||
//
|
||||
// //TODO: god knows what this is
|
||||
// auto deadzone = [] (float value, float threshold = 0.1f) {
|
||||
// if (fabs(value) < threshold)
|
||||
// return 0.0f;
|
||||
// const float sign = (value > 0) ? 1.0f : -1.0f;
|
||||
// return sign * (fabs(value) - threshold) / (1.0f - threshold);
|
||||
// };
|
||||
//
|
||||
// //LT is x2 speed
|
||||
// const bool isLTPressed = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
|
||||
// if (isLTPressed) {
|
||||
// moveSpeed *= 3.0f;
|
||||
// }
|
||||
//
|
||||
// const float lx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
|
||||
// const float ly = deadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
|
||||
// const float rx = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
|
||||
// const float ry = deadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
|
||||
//
|
||||
// m_position += m_forward * (-ly * moveSpeed);
|
||||
// m_position += m_right * (lx * moveSpeed);
|
||||
//
|
||||
// const float lTrigger = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) / 2.0f;
|
||||
// const float rTrigger = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) / 2.0f;
|
||||
// const float vertical = (rTrigger - lTrigger);
|
||||
// m_position += m_up * vertical * moveSpeed;
|
||||
//
|
||||
// totalYaw += rx * rotSpeed;
|
||||
// totalPitch -= ry * rotSpeed;
|
||||
// }
|
||||
// }
|
||||
// #pragma endregion
|
||||
|
||||
CalculateProjectionMatrix();
|
||||
CalculateViewMatrix();
|
||||
}
|
||||
|
||||
void Camera::CalculateViewMatrix() {
|
||||
if (m_useTarget) {
|
||||
m_forward = glm::normalize(m_target - m_position);
|
||||
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||
m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||
m_viewMatrix = glm::lookAt(m_position, m_target, m_up);
|
||||
} else {
|
||||
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
|
||||
}
|
||||
|
||||
m_invMatrix = glm::inverse(m_viewMatrix);
|
||||
}
|
||||
|
||||
void Camera::CalculateProjectionMatrix() {
|
||||
m_projectionMatrix = glm::perspectiveRH_ZO(glm::radians(fovAngle), m_aspectRatio, m_zNear, m_zFar);
|
||||
}
|
||||
|
||||
void Camera::ClearTarget() {
|
||||
m_useTarget = false;
|
||||
m_forward = glm::normalize(m_target - m_position);
|
||||
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||
m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||
}
|
||||
|
||||
void Camera::SetTarget(const glm::vec3& target) {
|
||||
m_target = target;
|
||||
m_useTarget = true;
|
||||
// m_forward = glm::normalize(m_target - m_position);
|
||||
// m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||
// m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||
}
|
||||
|
||||
void Camera::Target(const glm::vec3& target) {
|
||||
glm::vec3 directionToTarget = glm::normalize(target - m_position);
|
||||
|
||||
m_forward = glm::normalize(target - m_position);
|
||||
m_right = glm::normalize(glm::cross(m_forward, glm::vec3(0, 1, 0)));
|
||||
m_up = glm::normalize(glm::cross(m_right, m_forward));
|
||||
|
||||
m_viewMatrix = glm::lookAt(m_position, m_position + m_forward, m_up);
|
||||
m_invMatrix = glm::inverse(m_viewMatrix);
|
||||
}
|
||||
565
destrum/src/Graphics/GfxDevice.cpp
Normal file
565
destrum/src/Graphics/GfxDevice.cpp
Normal file
@@ -0,0 +1,565 @@
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
|
||||
#include "destrum/Graphics/Util.h"
|
||||
|
||||
#define VOLK_IMPLEMENTATION
|
||||
#include <volk.h>
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include <filesystem>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <destrum/Graphics/Init.h>
|
||||
|
||||
#include "destrum/Graphics/imageLoader.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
GfxDevice::GfxDevice(): imageCache(*this) {
|
||||
}
|
||||
|
||||
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
|
||||
VK_CHECK(volkInitialize());
|
||||
|
||||
instance = vkb::InstanceBuilder{}
|
||||
.set_app_name(appName.c_str())
|
||||
.set_app_version(1, 0, 0)
|
||||
.request_validation_layers()
|
||||
.use_default_debug_messenger()
|
||||
.require_api_version(1, 3, 0)
|
||||
.build()
|
||||
.value();
|
||||
|
||||
volkLoadInstance(instance);
|
||||
|
||||
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
|
||||
if (res != SDL_TRUE) {
|
||||
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
|
||||
.imageCubeArray = VK_TRUE,
|
||||
.geometryShader = VK_TRUE, // for im3d
|
||||
.depthClamp = VK_TRUE,
|
||||
.samplerAnisotropy = VK_TRUE,
|
||||
};
|
||||
|
||||
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
|
||||
.descriptorIndexing = true,
|
||||
.descriptorBindingSampledImageUpdateAfterBind = true,
|
||||
.descriptorBindingStorageImageUpdateAfterBind = true,
|
||||
.descriptorBindingPartiallyBound = true,
|
||||
.descriptorBindingVariableDescriptorCount = true,
|
||||
.runtimeDescriptorArray = true,
|
||||
.scalarBlockLayout = true,
|
||||
.bufferDeviceAddress = true,
|
||||
};
|
||||
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
|
||||
.synchronization2 = true,
|
||||
.dynamicRendering = true,
|
||||
};
|
||||
|
||||
physicalDevice = vkb::PhysicalDeviceSelector{instance}
|
||||
.set_minimum_version(1, 3)
|
||||
.set_required_features(deviceFeatures)
|
||||
.set_required_features_12(features12)
|
||||
.set_required_features_13(features13)
|
||||
.set_surface(surface)
|
||||
.select()
|
||||
.value();
|
||||
|
||||
device = vkb::DeviceBuilder{physicalDevice}.build().value();
|
||||
|
||||
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
|
||||
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
|
||||
|
||||
//Vma
|
||||
const auto vulkanFunctions = VmaVulkanFunctions{
|
||||
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
|
||||
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
|
||||
};
|
||||
|
||||
const auto allocatorInfo = VmaAllocatorCreateInfo{
|
||||
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
||||
.physicalDevice = physicalDevice,
|
||||
.device = device,
|
||||
.pVulkanFunctions = &vulkanFunctions,
|
||||
.instance = instance,
|
||||
};
|
||||
vmaCreateAllocator(&allocatorInfo, &allocator);
|
||||
|
||||
executor.init(device, graphicsQueueFamily, graphicsQueue);
|
||||
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
|
||||
|
||||
swapchain.initSync(device);
|
||||
|
||||
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
|
||||
|
||||
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
||||
auto& commandPool = frames[i].commandPool;
|
||||
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
|
||||
|
||||
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
|
||||
auto& mainCommandBuffer = frames[i].commandBuffer;
|
||||
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
|
||||
}
|
||||
//
|
||||
// { // create white texture
|
||||
// std::uint32_t pixel = 0xFFFFFFFF;
|
||||
// whiteImageId = createImage(
|
||||
// {
|
||||
// .format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
// .extent = VkExtent3D{1, 1, 1},
|
||||
// },
|
||||
// "white texture",
|
||||
// &pixel);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
assert(width != 0 && height != 0);
|
||||
waitIdle();
|
||||
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
|
||||
}
|
||||
|
||||
VkCommandBuffer GfxDevice::beginFrame() {
|
||||
swapchain.beginFrame(getCurrentFrameIndex());
|
||||
|
||||
const auto& frame = getCurrentFrame();
|
||||
const auto& cmd = frame.commandBuffer;
|
||||
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) {
|
||||
// get swapchain image
|
||||
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
|
||||
if (swapchainImage == VK_NULL_HANDLE) {
|
||||
spdlog::info("Swapchain is freaky, skipping frame...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
|
||||
swapchain.resetFences(getCurrentFrameIndex());
|
||||
|
||||
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
{
|
||||
// clear swapchain image
|
||||
VkImageSubresourceRange clearRange =
|
||||
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if (props.copyImageIntoSwapchain) {
|
||||
// copy from draw image into swapchain
|
||||
// vkutil::transitionImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
|
||||
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
vkutil::transitionImage(
|
||||
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
|
||||
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
const auto filter = VK_FILTER_LINEAR;
|
||||
// // if (props.drawImageBlitRect != glm::ivec4{}) {
|
||||
// vkutil::copyImageToImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// swapchainImage,
|
||||
// drawImage.getExtent2D(),
|
||||
// props.drawImageBlitRect.x,
|
||||
// props.drawImageBlitRect.y,
|
||||
// props.drawImageBlitRect.z,
|
||||
// props.drawImageBlitRect.w,
|
||||
// filter);
|
||||
// } else {
|
||||
// // will stretch image to swapchain
|
||||
// vkutil::copyImageToImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// swapchainImage,
|
||||
// drawImage.getExtent2D(),
|
||||
// getSwapchainExtent(),
|
||||
// filter);
|
||||
// }
|
||||
|
||||
// prepare for present
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VK_CHECK(vkEndCommandBuffer(cmd));
|
||||
|
||||
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
|
||||
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
|
||||
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
void GfxDevice::cleanup() {
|
||||
}
|
||||
|
||||
void GfxDevice::waitIdle() {
|
||||
VK_CHECK(vkDeviceWaitIdle(device));
|
||||
}
|
||||
|
||||
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
|
||||
executor.immediateSubmit(std::move(f));
|
||||
}
|
||||
|
||||
GPUBuffer GfxDevice::createBuffer(
|
||||
std::size_t allocSize,
|
||||
VkBufferUsageFlags usage,
|
||||
VmaMemoryUsage memoryUsage) const
|
||||
{
|
||||
const auto bufferInfo = VkBufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = allocSize,
|
||||
.usage = usage,
|
||||
};
|
||||
|
||||
const auto allocInfo = VmaAllocationCreateInfo{
|
||||
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT |
|
||||
// TODO: allow to set VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT when needed
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
.usage = memoryUsage,
|
||||
};
|
||||
|
||||
GPUBuffer buffer{};
|
||||
VK_CHECK(vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer.buffer, &buffer.allocation, &buffer.info));
|
||||
if ((usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) != 0) {
|
||||
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
||||
.buffer = buffer.buffer,
|
||||
};
|
||||
buffer.address = vkGetBufferDeviceAddress(device, &deviceAdressInfo);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
VkDeviceAddress GfxDevice::getBufferAddress(const GPUBuffer& buffer) const
|
||||
{
|
||||
const auto deviceAdressInfo = VkBufferDeviceAddressInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
||||
.buffer = buffer.buffer,
|
||||
};
|
||||
return vkGetBufferDeviceAddress(device, &deviceAdressInfo);
|
||||
}
|
||||
|
||||
void GfxDevice::destroyBuffer(const GPUBuffer& buffer) const
|
||||
{
|
||||
vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation);
|
||||
}
|
||||
//
|
||||
// GPUImage GfxDevice::loadImageFromFileRaw(
|
||||
// const std::filesystem::path& path,
|
||||
// VkFormat format,
|
||||
// VkImageUsageFlags usage,
|
||||
// bool mipMap) const
|
||||
// {
|
||||
// auto data = util::loadImage(path);
|
||||
// if (!data.pixels) {
|
||||
// fmt::println("[error] failed to load image from '{}'", path.string());
|
||||
// return getImage(errorImageId);
|
||||
// }
|
||||
//
|
||||
// auto image = createImageRaw({
|
||||
// .format = format,
|
||||
// .usage = usage | //
|
||||
// VK_IMAGE_USAGE_TRANSFER_DST_BIT | // for uploading pixel data to image
|
||||
// VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // for generating mips
|
||||
// .extent =
|
||||
// VkExtent3D{
|
||||
// .width = (std::uint32_t)data.width,
|
||||
// .height = (std::uint32_t)data.height,
|
||||
// .depth = 1,
|
||||
// },
|
||||
// .mipMap = mipMap,
|
||||
// });
|
||||
// uploadImageData(image, data.pixels);
|
||||
//
|
||||
// image.debugName = path.string();
|
||||
// vkutil::addDebugLabel(device, image.image, path.string().c_str());
|
||||
//
|
||||
// return image;
|
||||
// }
|
||||
|
||||
ImageID GfxDevice::createImage(
|
||||
const vkutil::CreateImageInfo& createInfo,
|
||||
const std::string& debugName,
|
||||
void* pixelData,
|
||||
ImageID imageId)
|
||||
{
|
||||
auto image = createImageRaw(createInfo);
|
||||
if (!debugName.empty()) {
|
||||
vkutil::addDebugLabel(device, image.image, debugName.c_str());
|
||||
image.debugName = debugName;
|
||||
}
|
||||
if (pixelData) {
|
||||
uploadImageData(image, pixelData);
|
||||
}
|
||||
if (imageId != NULL_IMAGE_ID) {
|
||||
return imageCache.addImage(imageId, std::move(image));
|
||||
} else {
|
||||
return addImageToCache(std::move(image));
|
||||
}
|
||||
}
|
||||
|
||||
ImageID GfxDevice::createDrawImage(
|
||||
VkFormat format,
|
||||
glm::ivec2 size,
|
||||
const std::string& debugName,
|
||||
ImageID imageId)
|
||||
{
|
||||
assert(size.x > 0 && size.y > 0);
|
||||
const auto extent = VkExtent3D{
|
||||
.width = (std::uint32_t)size.x,
|
||||
.height = (std::uint32_t)size.y,
|
||||
.depth = 1,
|
||||
};
|
||||
|
||||
VkImageUsageFlags usages{};
|
||||
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
const auto createImageInfo = vkutil::CreateImageInfo{
|
||||
.format = format,
|
||||
.usage = usages,
|
||||
.extent = extent,
|
||||
};
|
||||
return createImage(createImageInfo, debugName, nullptr, imageId);
|
||||
}
|
||||
|
||||
ImageID GfxDevice::loadImageFromFile(
|
||||
const std::filesystem::path& path,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags usage,
|
||||
bool mipMap)
|
||||
{
|
||||
return imageCache.loadImageFromFile(path, format, usage, mipMap);
|
||||
}
|
||||
|
||||
const GPUImage& GfxDevice::getImage(ImageID id) const
|
||||
{
|
||||
return imageCache.getImage(id);
|
||||
}
|
||||
|
||||
ImageID GfxDevice::addImageToCache(GPUImage img)
|
||||
{
|
||||
return imageCache.addImage(std::move(img));
|
||||
}
|
||||
|
||||
GPUImage GfxDevice::createImageRaw(
|
||||
const vkutil::CreateImageInfo& createInfo,
|
||||
std::optional<VmaAllocationCreateInfo> customAllocationCreateInfo) const
|
||||
{
|
||||
std::uint32_t mipLevels = 1;
|
||||
if (createInfo.mipMap) {
|
||||
const auto maxExtent = std::max(createInfo.extent.width, createInfo.extent.height);
|
||||
mipLevels = (std::uint32_t)std::floor(std::log2(maxExtent)) + 1;
|
||||
}
|
||||
|
||||
if (createInfo.isCubemap) {
|
||||
assert(createInfo.numLayers % 6 == 0);
|
||||
assert(!createInfo.mipMap);
|
||||
assert((createInfo.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0);
|
||||
}
|
||||
|
||||
auto imgInfo = VkImageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.flags = createInfo.flags,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = createInfo.format,
|
||||
.extent = createInfo.extent,
|
||||
.mipLevels = mipLevels,
|
||||
.arrayLayers = createInfo.numLayers,
|
||||
.samples = createInfo.samples,
|
||||
.tiling = createInfo.tiling,
|
||||
.usage = createInfo.usage,
|
||||
};
|
||||
|
||||
static const auto defaultAllocInfo = VmaAllocationCreateInfo{
|
||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||
.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
};
|
||||
const auto allocInfo = customAllocationCreateInfo.has_value() ?
|
||||
customAllocationCreateInfo.value() :
|
||||
defaultAllocInfo;
|
||||
|
||||
GPUImage image{};
|
||||
image.format = createInfo.format;
|
||||
image.usage = createInfo.usage;
|
||||
image.extent = createInfo.extent;
|
||||
image.mipLevels = mipLevels;
|
||||
image.numLayers = createInfo.numLayers;
|
||||
image.isCubemap = createInfo.isCubemap;
|
||||
|
||||
VK_CHECK(
|
||||
vmaCreateImage(allocator, &imgInfo, &allocInfo, &image.image, &image.allocation, nullptr));
|
||||
|
||||
// create view only when usage flags allow it
|
||||
bool shouldCreateView = ((createInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) ||
|
||||
((createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) ||
|
||||
((createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) ||
|
||||
((createInfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
|
||||
|
||||
if (shouldCreateView) {
|
||||
VkImageAspectFlags aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if (createInfo.format == VK_FORMAT_D32_SFLOAT) { // TODO: support other depth formats
|
||||
aspectFlag = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
|
||||
auto viewType =
|
||||
createInfo.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
if (createInfo.isCubemap && createInfo.numLayers == 6) {
|
||||
viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
}
|
||||
|
||||
const auto viewCreateInfo = VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image.image,
|
||||
.viewType = viewType,
|
||||
.format = createInfo.format,
|
||||
.subresourceRange =
|
||||
VkImageSubresourceRange{
|
||||
.aspectMask = aspectFlag,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = mipLevels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = createInfo.numLayers,
|
||||
},
|
||||
};
|
||||
|
||||
VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &image.imageView));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const
|
||||
{
|
||||
int numChannels = 4;
|
||||
if (image.format == VK_FORMAT_R8_UNORM) {
|
||||
// FIXME: support more types
|
||||
numChannels = 1;
|
||||
}
|
||||
const auto dataSize =
|
||||
image.extent.depth * image.extent.width * image.extent.height * numChannels;
|
||||
|
||||
const auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
|
||||
memcpy(uploadBuffer.info.pMappedData, pixelData, dataSize);
|
||||
|
||||
executor.immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
assert(
|
||||
(image.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 &&
|
||||
"Image needs to have VK_IMAGE_USAGE_TRANSFER_DST_BIT to upload data to it");
|
||||
vkutil::transitionImage(
|
||||
cmd, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
const auto copyRegion = VkBufferImageCopy{
|
||||
.bufferOffset = 0,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.imageExtent = image.extent,
|
||||
};
|
||||
|
||||
vkCmdCopyBufferToImage(
|
||||
cmd,
|
||||
uploadBuffer.buffer,
|
||||
image.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
©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
|
||||
}
|
||||
77
destrum/src/Graphics/ImageCache.cpp
Normal file
77
destrum/src/Graphics/ImageCache.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <destrum/Graphics/ImageCache.h>
|
||||
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
|
||||
ImageCache::ImageCache(GfxDevice& gfxDevice) : gfxDevice(gfxDevice)
|
||||
{}
|
||||
|
||||
ImageID ImageCache::loadImageFromFile(
|
||||
const std::filesystem::path& path,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags usage,
|
||||
bool mipMap)
|
||||
{
|
||||
for (const auto& [id, info] : loadedImagesInfo) {
|
||||
// TODO: calculate some hash to not have to linear search every time?
|
||||
if (info.path == path && info.format == format && info.usage == usage &&
|
||||
info.mipMap == mipMap) {
|
||||
// std::cout << "Already loaded: " << path << std::endl;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
auto image = gfxDevice.loadImageFromFileRaw(path, format, usage, mipMap);
|
||||
if (image.isInitialized() && image.getBindlessId() == errorImageId) {
|
||||
return errorImageId;
|
||||
}
|
||||
|
||||
const auto id = getFreeImageId();
|
||||
addImage(id, std::move(image));
|
||||
|
||||
loadedImagesInfo.emplace(
|
||||
id,
|
||||
LoadedImageInfo{
|
||||
.path = path,
|
||||
.format = format,
|
||||
.usage = usage,
|
||||
.mipMap = mipMap,
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
ImageID ImageCache::addImage(GPUImage image)
|
||||
{
|
||||
return addImage(getFreeImageId(), std::move(image));
|
||||
}
|
||||
|
||||
ImageID ImageCache::addImage(ImageID id, GPUImage image)
|
||||
{
|
||||
image.setBindlessId(static_cast<std::uint32_t>(id));
|
||||
if (id != images.size()) {
|
||||
images[id] = std::move(image); // replacing existing image
|
||||
} else {
|
||||
images.push_back(std::move(image));
|
||||
}
|
||||
// bindlessSetManager.addImage(gfxDevice.getDevice(), id, image.imageView);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
const GPUImage& ImageCache::getImage(ImageID id) const
|
||||
{
|
||||
return images.at(id);
|
||||
}
|
||||
|
||||
ImageID ImageCache::getFreeImageId() const
|
||||
{
|
||||
return images.size();
|
||||
}
|
||||
|
||||
void ImageCache::destroyImages()
|
||||
{
|
||||
for (const auto& image : images) {
|
||||
gfxDevice.destroyImage(image);
|
||||
}
|
||||
images.clear();
|
||||
loadedImagesInfo.clear();
|
||||
}
|
||||
30
destrum/src/Graphics/ImageLoader.cpp
Normal file
30
destrum/src/Graphics/ImageLoader.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <destrum/Graphics/imageLoader.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
ImageData::~ImageData()
|
||||
{
|
||||
if (shouldSTBFree) {
|
||||
stbi_image_free(pixels);
|
||||
stbi_image_free(hdrPixels);
|
||||
}
|
||||
}
|
||||
|
||||
namespace util
|
||||
{
|
||||
ImageData loadImage(const std::filesystem::path& p)
|
||||
{
|
||||
ImageData data;
|
||||
data.shouldSTBFree = true;
|
||||
if (stbi_is_hdr(p.string().c_str())) {
|
||||
data.hdr = true;
|
||||
data.hdrPixels = stbi_loadf(p.string().c_str(), &data.width, &data.height, &data.comp, 4);
|
||||
} else {
|
||||
data.pixels = stbi_load(p.string().c_str(), &data.width, &data.height, &data.channels, 4);
|
||||
}
|
||||
data.channels = 4;
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
86
destrum/src/Graphics/MeshCache.cpp
Normal file
86
destrum/src/Graphics/MeshCache.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <destrum/Graphics/MeshCache.h>
|
||||
|
||||
#include <destrum/Graphics/Resources/Mesh.h>
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
#include <destrum/Graphics/Util.h>
|
||||
// #include <destrum/Math/Util.h>
|
||||
|
||||
MeshID MeshCache::addMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh)
|
||||
{
|
||||
auto gpuMesh = GPUMesh{
|
||||
.numVertices = (std::uint32_t)cpuMesh.vertices.size(),
|
||||
.numIndices = (std::uint32_t)cpuMesh.indices.size(),
|
||||
.minPos = cpuMesh.minPos,
|
||||
.maxPos = cpuMesh.maxPos,
|
||||
};
|
||||
|
||||
std::vector<glm::vec3> positions(cpuMesh.vertices.size());
|
||||
for (std::size_t i = 0; i < cpuMesh.vertices.size(); ++i) {
|
||||
positions[i] = cpuMesh.vertices[i].position;
|
||||
}
|
||||
|
||||
uploadMesh(gfxDevice, cpuMesh, gpuMesh);
|
||||
const auto id = meshes.size();
|
||||
meshes.push_back(std::move(gpuMesh));
|
||||
return id;
|
||||
}
|
||||
|
||||
void MeshCache::uploadMesh(GfxDevice& gfxDevice, const CPUMesh& cpuMesh, GPUMesh& gpuMesh) const
|
||||
{
|
||||
// create index buffer
|
||||
const auto indexBufferSize = cpuMesh.indices.size() * sizeof(std::uint32_t);
|
||||
gpuMesh.indexBuffer = gfxDevice.createBuffer(
|
||||
indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
||||
|
||||
// create vertex buffer
|
||||
const auto vertexBufferSize = cpuMesh.vertices.size() * sizeof(CPUMesh::Vertex);
|
||||
gpuMesh.vertexBuffer = gfxDevice.createBuffer(
|
||||
vertexBufferSize,
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
|
||||
|
||||
const auto staging =
|
||||
gfxDevice
|
||||
.createBuffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
|
||||
|
||||
// copy data
|
||||
void* data = staging.info.pMappedData;
|
||||
memcpy(data, cpuMesh.vertices.data(), vertexBufferSize);
|
||||
memcpy((char*)data + vertexBufferSize, cpuMesh.indices.data(), indexBufferSize);
|
||||
|
||||
gfxDevice.immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
const auto vertexCopy = VkBufferCopy{
|
||||
.srcOffset = 0,
|
||||
.dstOffset = 0,
|
||||
.size = vertexBufferSize,
|
||||
};
|
||||
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.vertexBuffer.buffer, 1, &vertexCopy);
|
||||
|
||||
const auto indexCopy = VkBufferCopy{
|
||||
.srcOffset = vertexBufferSize,
|
||||
.dstOffset = 0,
|
||||
.size = indexBufferSize,
|
||||
};
|
||||
vkCmdCopyBuffer(cmd, staging.buffer, gpuMesh.indexBuffer.buffer, 1, &indexCopy);
|
||||
});
|
||||
|
||||
gfxDevice.destroyBuffer(staging);
|
||||
|
||||
const auto vtxBufferName = cpuMesh.name + " (vtx)";
|
||||
const auto idxBufferName = cpuMesh.name + " (idx)";
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.vertexBuffer.buffer, vtxBufferName.c_str());
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuMesh.indexBuffer.buffer, idxBufferName.c_str());
|
||||
}
|
||||
|
||||
const GPUMesh& MeshCache::getMesh(MeshID id) const
|
||||
{
|
||||
return meshes.at(id);
|
||||
}
|
||||
|
||||
void MeshCache::cleanup(const GfxDevice& gfxDevice)
|
||||
{
|
||||
for (const auto& mesh : meshes) {
|
||||
gfxDevice.destroyBuffer(mesh.indexBuffer);
|
||||
gfxDevice.destroyBuffer(mesh.vertexBuffer);
|
||||
}
|
||||
}
|
||||
130
destrum/src/Graphics/Pipelines/MeshPipeline.cpp
Normal file
130
destrum/src/Graphics/Pipelines/MeshPipeline.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <destrum/Graphics/Pipelines/MeshPipeline.h>
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
|
||||
MeshPipeline::MeshPipeline() {
|
||||
}
|
||||
|
||||
MeshPipeline::~MeshPipeline() {
|
||||
}
|
||||
|
||||
void MeshPipeline::init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat) {
|
||||
const auto& device = gfxDevice.getDevice();
|
||||
|
||||
// const auto vertexShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv"), device);
|
||||
// const auto fragShader = vkutil::loadShaderModule(AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv"), device);
|
||||
|
||||
|
||||
const auto vertexShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.vert.spv");
|
||||
const auto fragShader = AssetFS::GetInstance().GetFullPath("engine://shaders/mesh.frag.spv");
|
||||
|
||||
|
||||
// vkutil::addDebugLabel(device, vertexShader, "mesh.vert");
|
||||
// vkutil::addDebugLabel(device, vertexShader, "mesh.frag");
|
||||
|
||||
const auto bufferRange = VkPushConstantRange{
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = sizeof(PushConstants),
|
||||
};
|
||||
|
||||
const auto pushConstantRanges = std::array{bufferRange};
|
||||
// const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()};
|
||||
// m_pipelineLayout = vkutil::createPipelineLayout(device, layouts, pushConstantRanges);
|
||||
// vkutil::addDebugLabel(device, pipelineLayout, "mesh pipeline layout");
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
// pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
|
||||
// pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
|
||||
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
|
||||
|
||||
if (vkCreatePipelineLayout(gfxDevice.getDevice().device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Could not make pipleine layout");
|
||||
}
|
||||
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||
pipelineConfig.name = "Mesh Pipeline";
|
||||
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
||||
|
||||
pipelineConfig.vertexAttributeDescriptions = {};
|
||||
pipelineConfig.vertexBindingDescriptions = {};
|
||||
|
||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
||||
pipelineConfig.depthAttachment = depthImageFormat;
|
||||
|
||||
m_pipeline = std::make_unique<Pipeline>(
|
||||
gfxDevice,
|
||||
vertexShader.string(),
|
||||
fragShader.string(),
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
|
||||
void MeshPipeline::draw(VkCommandBuffer cmd,
|
||||
VkExtent2D renderExtent,
|
||||
const GfxDevice& gfxDevice,
|
||||
const MeshCache& meshCache,
|
||||
const MaterialCache& materialCache,
|
||||
const Camera& camera,
|
||||
const GPUBuffer& sceneDataBuffer,
|
||||
const std::vector<MeshDrawCommand>& drawCommands,
|
||||
const std::vector<std::size_t>& sortedDrawCommands) {
|
||||
// vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
m_pipeline->bind(cmd);
|
||||
// gfxDevice.bindBindlessDescSet(cmd, pipelineLayout);
|
||||
|
||||
const auto viewport = VkViewport{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = (float)renderExtent.width,
|
||||
.height = (float)renderExtent.height,
|
||||
.minDepth = 0.f,
|
||||
.maxDepth = 1.f,
|
||||
};
|
||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
||||
|
||||
const auto scissor = VkRect2D{
|
||||
.offset = {},
|
||||
.extent = renderExtent,
|
||||
};
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
|
||||
auto prevMeshId = NULL_MESH_ID;
|
||||
|
||||
// const auto frustum = edge::createFrustumFromCamera(camera);
|
||||
|
||||
for (const auto& dcIdx : drawCommands) {
|
||||
// const auto& dc = drawCommands[dcIdx];
|
||||
const auto& dc = dcIdx;
|
||||
|
||||
// if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
const auto& mesh = meshCache.getMesh(dc.meshId);
|
||||
if (dc.meshId != prevMeshId) {
|
||||
prevMeshId = dc.meshId;
|
||||
vkCmdBindIndexBuffer(cmd, mesh.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
}
|
||||
|
||||
assert(dc.materialId != NULL_MATERIAL_ID);
|
||||
const auto pushConstants = PushConstants{
|
||||
.transform = dc.transformMatrix,
|
||||
.sceneDataBuffer = sceneDataBuffer.address,
|
||||
.vertexBuffer = mesh.vertexBuffer.address,
|
||||
.materialId = dc.materialId,
|
||||
};
|
||||
vkCmdPushConstants(
|
||||
cmd,
|
||||
m_pipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0,
|
||||
sizeof(PushConstants),
|
||||
&pushConstants);
|
||||
|
||||
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
149
destrum/src/Graphics/Renderer.cpp
Normal file
149
destrum/src/Graphics/Renderer.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <destrum/Graphics/Renderer.h>
|
||||
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
GameRenderer::GameRenderer(MeshCache& meshCache): meshCache{meshCache} {
|
||||
}
|
||||
|
||||
void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) {
|
||||
sceneDataBuffer.init(
|
||||
gfxDevice,
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
sizeof(GPUSceneData),
|
||||
"scene data");
|
||||
|
||||
createDrawImage(gfxDevice, drawImageSize, true);
|
||||
|
||||
meshPipeline = std::make_unique<MeshPipeline>();
|
||||
meshPipeline->init(gfxDevice, drawImageFormat, depthImageFormat);
|
||||
|
||||
}
|
||||
|
||||
void GameRenderer::beginDrawing(GfxDevice& gfxDevice) {
|
||||
meshDrawCommands.clear();
|
||||
}
|
||||
|
||||
void GameRenderer::endDrawing() {
|
||||
//Sort the drawlist
|
||||
}
|
||||
|
||||
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) {
|
||||
const auto gpuSceneData = GPUSceneData{
|
||||
.view = sceneData.camera.GetViewMatrix(),
|
||||
.proj = sceneData.camera.GetProjectionMatrix(),
|
||||
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
|
||||
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
|
||||
.ambientColor = {sceneData.ambientColor},
|
||||
.ambientIntensity = sceneData.ambientIntensity,
|
||||
.fogColor = {sceneData.fogColor},
|
||||
.fogDensity = sceneData.fogDensity,
|
||||
.materialsBuffer = materialCache.getMaterialDataBufferAddress(),
|
||||
};
|
||||
sceneDataBuffer.uploadNewData(
|
||||
cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData));
|
||||
|
||||
const auto& drawImage = gfxDevice.getImage(drawImageId);
|
||||
const auto& depthImage = gfxDevice.getImage(depthImageId);
|
||||
|
||||
// vkutil::cmdBeginLabel(cmd, "Geometry");
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
drawImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
depthImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
||||
|
||||
const auto renderInfo = vkutil::createRenderingInfo({
|
||||
.renderExtent = drawImage.getExtent2D(),
|
||||
.colorImageView = drawImage.imageView,
|
||||
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
|
||||
.depthImageView = depthImage.imageView,
|
||||
.depthImageClearValue = 0.f,
|
||||
});
|
||||
|
||||
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
|
||||
meshPipeline->draw(
|
||||
cmd,
|
||||
drawImage.getExtent2D(),
|
||||
gfxDevice,
|
||||
meshCache,
|
||||
materialCache,
|
||||
camera,
|
||||
sceneDataBuffer.getBuffer(),
|
||||
meshDrawCommands,
|
||||
sortedMeshDrawCommands);
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
// vkutil::cmdEndLabel(cmd);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GameRenderer::cleanup() {
|
||||
}
|
||||
|
||||
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialId materialId) {
|
||||
const auto& mesh = meshCache.getMesh(id);
|
||||
// const auto worldBoundingSphere = edge::calculateBoundingSphereWorld(transform, mesh.boundingSphere, false);
|
||||
assert(materialId != NULL_MATERIAL_ID);
|
||||
|
||||
meshDrawCommands.push_back(MeshDrawCommand{
|
||||
.meshId = id,
|
||||
.transformMatrix = transform,
|
||||
.materialId = materialId,
|
||||
});
|
||||
}
|
||||
|
||||
void GameRenderer::createDrawImage(GfxDevice& gfxDevice,
|
||||
const glm::ivec2& drawImageSize,
|
||||
bool firstCreate)
|
||||
{
|
||||
const VkExtent3D drawImageExtent{
|
||||
.width = (std::uint32_t)drawImageSize.x,
|
||||
.height = (std::uint32_t)drawImageSize.y,
|
||||
.depth = 1,
|
||||
};
|
||||
|
||||
constexpr VkSampleCountFlagBits noMsaa = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
{ // setup draw image (single-sampled)
|
||||
VkImageUsageFlags usages{};
|
||||
usages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
usages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
usages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
usages |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
auto createImageInfo = vkutil::CreateImageInfo{
|
||||
.format = drawImageFormat,
|
||||
.usage = usages,
|
||||
.extent = drawImageExtent,
|
||||
.samples = noMsaa,
|
||||
};
|
||||
|
||||
// reuse the same id if creating again
|
||||
drawImageId = gfxDevice.createImage(createImageInfo, "draw image", nullptr, drawImageId);
|
||||
|
||||
if (firstCreate) {
|
||||
// Optional: a separate post-fx target (ping-pong)
|
||||
// postFXDrawImageId = gfxDevice.createImage(createImageInfo, "post FX draw image");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // setup depth image (single-sampled)
|
||||
auto createInfo = vkutil::CreateImageInfo{
|
||||
.format = depthImageFormat,
|
||||
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.extent = drawImageExtent,
|
||||
.samples = noMsaa,
|
||||
};
|
||||
|
||||
depthImageId = gfxDevice.createImage(createInfo, "depth image", nullptr, depthImageId);
|
||||
|
||||
}
|
||||
}
|
||||
1
destrum/src/Graphics/Resources/GPUImage.cpp
Normal file
1
destrum/src/Graphics/Resources/GPUImage.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <destrum/Graphics/GPUImage.h>
|
||||
122
destrum/src/Graphics/Resources/NBuffer.cpp
Normal file
122
destrum/src/Graphics/Resources/NBuffer.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <destrum/Graphics/Resources/NBuffer.h>
|
||||
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <volk.h>
|
||||
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
void NBuffer::init(
|
||||
GfxDevice& gfxDevice,
|
||||
VkBufferUsageFlags usage,
|
||||
std::size_t dataSize,
|
||||
const std::string& debugName)
|
||||
{
|
||||
assert(FRAMES_IN_FLIGHT > 0);
|
||||
assert(dataSize > 0);
|
||||
|
||||
framesInFlight = FRAMES_IN_FLIGHT;
|
||||
gpuBufferSize = dataSize;
|
||||
|
||||
gpuBuffer = gfxDevice.createBuffer(
|
||||
dataSize, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE);
|
||||
vkutil::addDebugLabel(gfxDevice.getDevice(), gpuBuffer.buffer, debugName.c_str());
|
||||
|
||||
for (std::size_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
||||
stagingBuffers.push_back(gfxDevice.createBuffer(
|
||||
dataSize, usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST));
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void NBuffer::cleanup(GfxDevice& device)
|
||||
{
|
||||
for (const auto& stagingBuffer : stagingBuffers) {
|
||||
device.destroyBuffer(stagingBuffer);
|
||||
}
|
||||
stagingBuffers.clear();
|
||||
|
||||
device.destroyBuffer(gpuBuffer);
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void NBuffer::uploadNewData(
|
||||
VkCommandBuffer cmd,
|
||||
std::size_t frameIndex,
|
||||
void* newData,
|
||||
std::size_t dataSize,
|
||||
std::size_t offset,
|
||||
bool sync) const
|
||||
{
|
||||
assert(initialized);
|
||||
assert(frameIndex < framesInFlight);
|
||||
assert(offset + dataSize <= gpuBufferSize && "NBuffer::uploadNewData: out of bounds write");
|
||||
|
||||
if (dataSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// sync with previous read
|
||||
if (sync) {
|
||||
const auto bufferBarrier = VkBufferMemoryBarrier2{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_MEMORY_READ_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
|
||||
.buffer = gpuBuffer.buffer,
|
||||
.offset = 0,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
};
|
||||
const auto dependencyInfo = VkDependencyInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.bufferMemoryBarrierCount = 1,
|
||||
.pBufferMemoryBarriers = &bufferBarrier,
|
||||
};
|
||||
vkCmdPipelineBarrier2(cmd, &dependencyInfo);
|
||||
}
|
||||
|
||||
auto& staging = stagingBuffers[frameIndex];
|
||||
auto* mappedData = reinterpret_cast<std::uint8_t*>(staging.info.pMappedData);
|
||||
memcpy((void*)&mappedData[offset], newData, dataSize);
|
||||
|
||||
const auto region = VkBufferCopy2{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2,
|
||||
.srcOffset = (VkDeviceSize)offset,
|
||||
.dstOffset = (VkDeviceSize)offset,
|
||||
.size = dataSize,
|
||||
};
|
||||
const auto bufCopyInfo = VkCopyBufferInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2,
|
||||
.srcBuffer = staging.buffer,
|
||||
.dstBuffer = gpuBuffer.buffer,
|
||||
.regionCount = 1,
|
||||
.pRegions = ®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);
|
||||
}
|
||||
}
|
||||
316
destrum/src/Graphics/Util.cpp
Normal file
316
destrum/src/Graphics/Util.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
#include <fstream>
|
||||
#include <destrum/Graphics/Init.h>
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
#include <volk.h>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
|
||||
VkImageAspectFlags aspectMask =
|
||||
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
|
||||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
|
||||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT :
|
||||
VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
VkImageMemoryBarrier2 imageBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
|
||||
.oldLayout = currentLayout,
|
||||
.newLayout = newLayout,
|
||||
.image = image,
|
||||
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
|
||||
};
|
||||
|
||||
VkDependencyInfo depInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &imageBarrier,
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier2(cmd, &depInfo);
|
||||
}
|
||||
|
||||
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
|
||||
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
|
||||
}
|
||||
|
||||
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
|
||||
const auto blitRegion = VkImageBlit2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
|
||||
.srcSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffsets =
|
||||
{
|
||||
{},
|
||||
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
|
||||
},
|
||||
.dstSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstOffsets =
|
||||
{
|
||||
{(std::int32_t)destX, (std::int32_t)destY},
|
||||
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
|
||||
},
|
||||
};
|
||||
|
||||
const auto blitInfo = VkBlitImageInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
|
||||
.srcImage = source,
|
||||
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.dstImage = destination,
|
||||
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.regionCount = 1,
|
||||
.pRegions = &blitRegion,
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
vkCmdBlitImage2(cmd, &blitInfo);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkImage image, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE,
|
||||
.objectHandle = (std::uint64_t)image,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void vkutil::addDebugLabel(VkDevice device, VkBuffer buffer, const char* label) {
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_BUFFER,
|
||||
.objectHandle = (std::uint64_t)buffer,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
vkutil::RenderInfo vkutil::createRenderingInfo(const RenderingInfoParams& params) {
|
||||
assert(
|
||||
(params.colorImageView || params.depthImageView != nullptr) &&
|
||||
"Either draw or depth image should be present");
|
||||
assert(
|
||||
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
|
||||
"renderExtent not specified");
|
||||
|
||||
RenderInfo ri;
|
||||
if (params.colorImageView) {
|
||||
ri.colorAttachment = VkRenderingAttachmentInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = params.colorImageView,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
};
|
||||
if (params.colorImageClearValue) {
|
||||
const auto col = params.colorImageClearValue.value();
|
||||
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
|
||||
}
|
||||
}
|
||||
|
||||
if (params.depthImageView) {
|
||||
ri.depthAttachment = VkRenderingAttachmentInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = params.depthImageView,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
};
|
||||
if (params.depthImageClearValue) {
|
||||
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
|
||||
}
|
||||
}
|
||||
|
||||
ri.renderingInfo = VkRenderingInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea =
|
||||
VkRect2D{
|
||||
.offset = {},
|
||||
.extent = params.renderExtent,
|
||||
},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
|
||||
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
|
||||
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
|
||||
};
|
||||
|
||||
return ri;
|
||||
}
|
||||
|
||||
VkShaderModule vkutil::loadShaderModule(const std::filesystem::path& path, VkDevice device) {
|
||||
std::ifstream file(path, std::ios::ate | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
spdlog::error("failed to open shader");
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
const auto fileSize = file.tellg();
|
||||
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
|
||||
|
||||
file.seekg(0);
|
||||
file.read((char*)buffer.data(), fileSize);
|
||||
file.close();
|
||||
|
||||
auto info = VkShaderModuleCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = buffer.size() * sizeof(std::uint32_t),
|
||||
.pCode = buffer.data(),
|
||||
};
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
if (vkCreateShaderModule(device, &info, nullptr, &shaderModule) != VK_SUCCESS) {
|
||||
spdlog::error("Failed to load");
|
||||
std::exit(1);
|
||||
}
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkImage image, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE,
|
||||
.objectHandle = (std::uint64_t)image,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkImageView imageView, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE_VIEW,
|
||||
.objectHandle = (std::uint64_t)imageView,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkShaderModule shaderModule, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SHADER_MODULE,
|
||||
.objectHandle = (std::uint64_t)shaderModule,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkPipeline pipeline, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_PIPELINE,
|
||||
.objectHandle = (std::uint64_t)pipeline,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkPipelineLayout layout, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_PIPELINE_LAYOUT,
|
||||
.objectHandle = (std::uint64_t)layout,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkBuffer buffer, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_BUFFER,
|
||||
.objectHandle = (std::uint64_t)buffer,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
void addDebugLabel(VkDevice device, VkSampler sampler, const char* label)
|
||||
{
|
||||
const auto nameInfo = VkDebugUtilsObjectNameInfoEXT{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.objectType = VK_OBJECT_TYPE_SAMPLER,
|
||||
.objectHandle = (std::uint64_t)sampler,
|
||||
.pObjectName = label,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
|
||||
vkutil::RenderInfo createRenderingInfo(const vkutil::RenderingInfoParams& params)
|
||||
{
|
||||
assert(
|
||||
(params.colorImageView || params.depthImageView != nullptr) &&
|
||||
"Either draw or depth image should be present");
|
||||
assert(
|
||||
params.renderExtent.width != 0.f && params.renderExtent.height != 0.f &&
|
||||
"renderExtent not specified");
|
||||
|
||||
vkutil::RenderInfo ri;
|
||||
if (params.colorImageView) {
|
||||
ri.colorAttachment = VkRenderingAttachmentInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = params.colorImageView,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = params.colorImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
};
|
||||
if (params.colorImageClearValue) {
|
||||
const auto col = params.colorImageClearValue.value();
|
||||
ri.colorAttachment.clearValue.color = {col[0], col[1], col[2], col[3]};
|
||||
}
|
||||
}
|
||||
|
||||
if (params.depthImageView) {
|
||||
ri.depthAttachment = VkRenderingAttachmentInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = params.depthImageView,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = params.depthImageClearValue ? VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
};
|
||||
if (params.depthImageClearValue) {
|
||||
ri.depthAttachment.clearValue.depthStencil.depth = params.depthImageClearValue.value();
|
||||
}
|
||||
}
|
||||
|
||||
ri.renderingInfo = VkRenderingInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea =
|
||||
VkRect2D{
|
||||
.offset = {},
|
||||
.extent = params.renderExtent,
|
||||
},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = params.colorImageView ? 1u : 0u,
|
||||
.pColorAttachments = params.colorImageView ? &ri.colorAttachment : nullptr,
|
||||
.pDepthAttachment = params.depthImageView ? &ri.depthAttachment : nullptr,
|
||||
};
|
||||
|
||||
return ri;
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
#include <destrum/Graphics/GfxDevice.h>
|
||||
|
||||
#include "destrum/Graphics/Util.h"
|
||||
|
||||
#define VOLK_IMPLEMENTATION
|
||||
#include <volk.h>
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <destrum/Graphics/Init.h>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
GfxDevice::GfxDevice() {
|
||||
}
|
||||
|
||||
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
|
||||
VK_CHECK(volkInitialize());
|
||||
|
||||
instance = vkb::InstanceBuilder{}
|
||||
.set_app_name(appName.c_str())
|
||||
.set_app_version(1, 0, 0)
|
||||
.request_validation_layers()
|
||||
.use_default_debug_messenger()
|
||||
.require_api_version(1, 3, 0)
|
||||
.build()
|
||||
.value();
|
||||
|
||||
volkLoadInstance(instance);
|
||||
|
||||
const auto res = SDL_Vulkan_CreateSurface(window, instance, &surface);
|
||||
if (res != SDL_TRUE) {
|
||||
spdlog::error("Failed to create Vulkan surface: {}", SDL_GetError());
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
constexpr auto deviceFeatures = VkPhysicalDeviceFeatures{
|
||||
.imageCubeArray = VK_TRUE,
|
||||
.geometryShader = VK_TRUE, // for im3d
|
||||
.depthClamp = VK_TRUE,
|
||||
.samplerAnisotropy = VK_TRUE,
|
||||
};
|
||||
|
||||
constexpr auto features12 = VkPhysicalDeviceVulkan12Features{
|
||||
.descriptorIndexing = true,
|
||||
.descriptorBindingSampledImageUpdateAfterBind = true,
|
||||
.descriptorBindingStorageImageUpdateAfterBind = true,
|
||||
.descriptorBindingPartiallyBound = true,
|
||||
.descriptorBindingVariableDescriptorCount = true,
|
||||
.runtimeDescriptorArray = true,
|
||||
.scalarBlockLayout = true,
|
||||
.bufferDeviceAddress = true,
|
||||
};
|
||||
constexpr auto features13 = VkPhysicalDeviceVulkan13Features{
|
||||
.synchronization2 = true,
|
||||
.dynamicRendering = true,
|
||||
};
|
||||
|
||||
physicalDevice = vkb::PhysicalDeviceSelector{instance}
|
||||
.set_minimum_version(1, 3)
|
||||
.set_required_features(deviceFeatures)
|
||||
.set_required_features_12(features12)
|
||||
.set_required_features_13(features13)
|
||||
.set_surface(surface)
|
||||
.select()
|
||||
.value();
|
||||
|
||||
device = vkb::DeviceBuilder{physicalDevice}.build().value();
|
||||
|
||||
graphicsQueueFamily = device.get_queue_index(vkb::QueueType::graphics).value();
|
||||
graphicsQueue = device.get_queue(vkb::QueueType::graphics).value();
|
||||
|
||||
//Vma
|
||||
const auto vulkanFunctions = VmaVulkanFunctions{
|
||||
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
|
||||
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
|
||||
};
|
||||
|
||||
const auto allocatorInfo = VmaAllocatorCreateInfo{
|
||||
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
||||
.physicalDevice = physicalDevice,
|
||||
.device = device,
|
||||
.pVulkanFunctions = &vulkanFunctions,
|
||||
.instance = instance,
|
||||
};
|
||||
vmaCreateAllocator(&allocatorInfo, &allocator);
|
||||
|
||||
executor.init(device, graphicsQueueFamily, graphicsQueue);
|
||||
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
swapchainFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
swapchain.createSwapchain(this, swapchainFormat, w, h, vSync);
|
||||
|
||||
swapchain.initSync(device);
|
||||
|
||||
const auto poolCreateInfo = vkinit::commandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamily);
|
||||
|
||||
for (std::uint32_t i = 0; i < FRAMES_IN_FLIGHT; ++i) {
|
||||
auto& commandPool = frames[i].commandPool;
|
||||
VK_CHECK(vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool));
|
||||
|
||||
const auto cmdAllocInfo = vkinit::commandBufferAllocateInfo(commandPool, 1);
|
||||
auto& mainCommandBuffer = frames[i].commandBuffer;
|
||||
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
|
||||
}
|
||||
//
|
||||
// { // create white texture
|
||||
// std::uint32_t pixel = 0xFFFFFFFF;
|
||||
// whiteImageId = createImage(
|
||||
// {
|
||||
// .format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
// .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
// .extent = VkExtent3D{1, 1, 1},
|
||||
// },
|
||||
// "white texture",
|
||||
// &pixel);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
assert(width != 0 && height != 0);
|
||||
waitIdle();
|
||||
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, false);
|
||||
}
|
||||
|
||||
VkCommandBuffer GfxDevice::beginFrame() {
|
||||
swapchain.beginFrame(getCurrentFrameIndex());
|
||||
|
||||
const auto& frame = getCurrentFrame();
|
||||
const auto& cmd = frame.commandBuffer;
|
||||
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void GfxDevice::endFrame(VkCommandBuffer cmd, const VImage& drawImage, const EndFrameProps& props) {
|
||||
// get swapchain image
|
||||
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
|
||||
if (swapchainImage == VK_NULL_HANDLE) {
|
||||
std::printf("Swapchain is dirty, skipping frame...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
|
||||
swapchain.resetFences(getCurrentFrameIndex());
|
||||
|
||||
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
{
|
||||
// clear swapchain image
|
||||
VkImageSubresourceRange clearRange =
|
||||
vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
VkClearColorValue clearValue;
|
||||
static int superFrameNumber = 0;
|
||||
superFrameNumber += 1;
|
||||
if (superFrameNumber > 255) {
|
||||
superFrameNumber = 0;
|
||||
}
|
||||
float flash = std::abs(std::sin(superFrameNumber * 0.1f));
|
||||
clearValue = { { 0.0f, 0.0f, flash, 1.0f } };
|
||||
|
||||
// const auto clearValue = props.clearColor;
|
||||
vkCmdClearColorImage(cmd, swapchainImage, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);
|
||||
}
|
||||
|
||||
// if (props.copyImageIntoSwapchain) {
|
||||
// copy from draw image into swapchain
|
||||
// vkutil::transitionImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
|
||||
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
vkutil::transitionImage(
|
||||
cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
|
||||
// const auto filter = props.drawImageLinearBlit ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
const auto filter = VK_FILTER_LINEAR;
|
||||
// // if (props.drawImageBlitRect != glm::ivec4{}) {
|
||||
// vkutil::copyImageToImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// swapchainImage,
|
||||
// drawImage.getExtent2D(),
|
||||
// props.drawImageBlitRect.x,
|
||||
// props.drawImageBlitRect.y,
|
||||
// props.drawImageBlitRect.z,
|
||||
// props.drawImageBlitRect.w,
|
||||
// filter);
|
||||
// } else {
|
||||
// // will stretch image to swapchain
|
||||
// vkutil::copyImageToImage(
|
||||
// cmd,
|
||||
// drawImage.getImage(),
|
||||
// swapchainImage,
|
||||
// drawImage.getExtent2D(),
|
||||
// getSwapchainExtent(),
|
||||
// filter);
|
||||
// }
|
||||
|
||||
// prepare for present
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VK_CHECK(vkEndCommandBuffer(cmd));
|
||||
|
||||
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
|
||||
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
|
||||
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
void GfxDevice::cleanup() {
|
||||
}
|
||||
|
||||
void GfxDevice::waitIdle() {
|
||||
VK_CHECK(vkDeviceWaitIdle(device));
|
||||
}
|
||||
|
||||
void GfxDevice::immediateSubmit(ImmediateExecuteFunction&& f) const {
|
||||
executor.immediateSubmit(std::move(f));
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include <destrum/Graphics/MeshCache.h>
|
||||
@@ -1,79 +0,0 @@
|
||||
#include <destrum/Graphics/Init.h>
|
||||
#include <destrum/Graphics/Util.h>
|
||||
|
||||
#include <volk.h>
|
||||
|
||||
void vkutil::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) {
|
||||
VkImageAspectFlags aspectMask =
|
||||
(currentLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
|
||||
newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ||
|
||||
newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL) ?
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT :
|
||||
VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
VkImageMemoryBarrier2 imageBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
|
||||
.oldLayout = currentLayout,
|
||||
.newLayout = newLayout,
|
||||
.image = image,
|
||||
.subresourceRange = vkinit::imageSubresourceRange(aspectMask),
|
||||
};
|
||||
|
||||
VkDependencyInfo depInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &imageBarrier,
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier2(cmd, &depInfo);
|
||||
}
|
||||
|
||||
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize, VkFilter filter) {
|
||||
copyImageToImage(cmd, source, destination, srcSize, 0, 0, dstSize.width, dstSize.height, filter);
|
||||
}
|
||||
|
||||
void vkutil::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, int destX, int destY, int destW, int destH, VkFilter filter) {
|
||||
const auto blitRegion = VkImageBlit2{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
|
||||
.srcSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffsets =
|
||||
{
|
||||
{},
|
||||
{(std::int32_t)srcSize.width, (std::int32_t)srcSize.height, 1},
|
||||
},
|
||||
.dstSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstOffsets =
|
||||
{
|
||||
{(std::int32_t)destX, (std::int32_t)destY},
|
||||
{(std::int32_t)(destX + destW), (std::int32_t)(destY + destH), 1},
|
||||
},
|
||||
};
|
||||
|
||||
const auto blitInfo = VkBlitImageInfo2{
|
||||
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
|
||||
.srcImage = source,
|
||||
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.dstImage = destination,
|
||||
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.regionCount = 1,
|
||||
.pRegions = &blitRegion,
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
vkCmdBlitImage2(cmd, &blitInfo);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include <destrum/Graphics/VImage.h>
|
||||
1
destrum/third_party/CMakeLists.txt
vendored
1
destrum/third_party/CMakeLists.txt
vendored
@@ -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")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "Lightkeeper.h"
|
||||
|
||||
LightKeeper::LightKeeper() {
|
||||
|
||||
LightKeeper::LightKeeper(): App() {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user