diff --git a/CMakeLists.txt b/CMakeLists.txt index c05cc48..a6a8717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,13 @@ set( SOURCES src/lightmap/collision.cpp src/lightmap/delauneytriangulator.cpp src/lightmap/delauneytriangulator.h + src/lightmap/vulkandevice.cpp + src/lightmap/vulkandevice.h + src/lightmap/vulkanobjects.h + src/lightmap/vulkanbuilders.cpp + src/lightmap/vulkanbuilders.h + src/lightmap/gpuraytracer.cpp + src/lightmap/gpuraytracer.h src/math/angle.cpp src/math/bounds.cpp src/math/mathlib.cpp @@ -210,6 +217,128 @@ set( HEADERS src/math/mathlib.h ) +set(THIRDPARTY_SOURCES + ../thirdparty/vk_mem_alloc/vk_mem_alloc.h + ../thirdparty/vk_mem_alloc/vk_mem_alloc.cpp + ../thirdparty/volk/volk.c + ../thirdparty/volk/volk.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/SymbolTable.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/propagateNoContraction.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/PoolAlloc.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Intermediate.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/gl_types.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/parseVersions.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/attribute.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Scan.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/iomapper.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/ParseHelper.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/glslang_tab.cpp.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/SymbolTable.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/RemoveTree.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Versions.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/reflection.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/LiveTraverser.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/iomapper.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/intermOut.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Versions.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Initialize.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/linkValidate.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/InfoSink.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Constant.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/IntermTraverse.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/propagateNoContraction.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/glslang_tab.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/ShaderLang.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/Pp.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpTokens.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpAtom.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpContext.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpTokens.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpScanner.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/preprocessor/PpContext.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/attribute.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/localintermediate.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/parseConst.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Initialize.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/limits.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/ParseContextBase.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/RemoveTree.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/ParseHelper.cpp + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/Scan.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/reflection.h + ../thirdparty/ShaderCompiler/glslang/MachineIndependent/ScanContext.h + ../thirdparty/ShaderCompiler/glslang/OSDependent/osinclude.h + ../thirdparty/ShaderCompiler/glslang/GenericCodeGen/Link.cpp + ../thirdparty/ShaderCompiler/glslang/GenericCodeGen/CodeGen.cpp + ../thirdparty/ShaderCompiler/glslang/Public/ShaderLang.h + ../thirdparty/ShaderCompiler/glslang/Include/ConstantUnion.h + ../thirdparty/ShaderCompiler/glslang/Include/InitializeGlobals.h + ../thirdparty/ShaderCompiler/glslang/Include/Common.h + ../thirdparty/ShaderCompiler/glslang/Include/PoolAlloc.h + ../thirdparty/ShaderCompiler/glslang/Include/arrays.h + ../thirdparty/ShaderCompiler/glslang/Include/ShHandle.h + ../thirdparty/ShaderCompiler/glslang/Include/InfoSink.h + ../thirdparty/ShaderCompiler/glslang/Include/ResourceLimits.h + ../thirdparty/ShaderCompiler/glslang/Include/Types.h + ../thirdparty/ShaderCompiler/glslang/Include/revision.h + ../thirdparty/ShaderCompiler/glslang/Include/BaseTypes.h + ../thirdparty/ShaderCompiler/glslang/Include/intermediate.h + ../thirdparty/ShaderCompiler/spirv/Logger.h + ../thirdparty/ShaderCompiler/spirv/GlslangToSpv.cpp + ../thirdparty/ShaderCompiler/spirv/SPVRemapper.h + ../thirdparty/ShaderCompiler/spirv/GLSL.ext.EXT.h + ../thirdparty/ShaderCompiler/spirv/hex_float.h + ../thirdparty/ShaderCompiler/spirv/doc.cpp + ../thirdparty/ShaderCompiler/spirv/disassemble.cpp + ../thirdparty/ShaderCompiler/spirv/SpvPostProcess.cpp + ../thirdparty/ShaderCompiler/spirv/bitutils.h + ../thirdparty/ShaderCompiler/spirv/InReadableOrder.cpp + ../thirdparty/ShaderCompiler/spirv/GLSL.ext.AMD.h + ../thirdparty/ShaderCompiler/spirv/GLSL.ext.NV.h + ../thirdparty/ShaderCompiler/spirv/SPVRemapper.cpp + ../thirdparty/ShaderCompiler/spirv/SpvBuilder.h + ../thirdparty/ShaderCompiler/spirv/GLSL.ext.KHR.h + ../thirdparty/ShaderCompiler/spirv/disassemble.h + ../thirdparty/ShaderCompiler/spirv/SpvBuilder.cpp + ../thirdparty/ShaderCompiler/spirv/GlslangToSpv.h + ../thirdparty/ShaderCompiler/spirv/doc.h + ../thirdparty/ShaderCompiler/spirv/SpvTools.cpp + ../thirdparty/ShaderCompiler/spirv/spvIR.h + ../thirdparty/ShaderCompiler/spirv/Logger.cpp + ../thirdparty/ShaderCompiler/spirv/SpvTools.h + ../thirdparty/ShaderCompiler/spirv/GLSL.std.450.h + ../thirdparty/ShaderCompiler/OGLCompilersDLL/InitializeDll.cpp + ../thirdparty/ShaderCompiler/OGLCompilersDLL/InitializeDll.h + ../thirdparty/vulkan/vk_sdk_platform.h + ../thirdparty/vulkan/vulkan_fuchsia.h + ../thirdparty/vulkan/vulkan_win32.h + ../thirdparty/vulkan/vulkan_ios.h + ../thirdparty/vulkan/vk_icd.h + ../thirdparty/vulkan/vk_layer.h + ../thirdparty/vulkan/vulkan_xcb.h + ../thirdparty/vulkan/vulkan_macos.h + ../thirdparty/vulkan/vk_layer_dispatch_table.h + ../thirdparty/vulkan/vulkan_ggp.h + ../thirdparty/vulkan/vulkan_core.h + ../thirdparty/vulkan/vulkan_wayland.h + ../thirdparty/vulkan/vulkan_metal.h + ../thirdparty/vulkan/vulkan_android.h + ../thirdparty/vulkan/vulkan_vi.h + ../thirdparty/vulkan/vulkan.h + ../thirdparty/vulkan/vk_platform.h + ../thirdparty/vulkan/vulkan_mir.h + ../thirdparty/vulkan/vulkan_xlib_xrandr.h + ../thirdparty/vulkan/vulkan_xlib.h +) + +set(THIRDPARTY_WIN32_SOURCES + ../thirdparty/ShaderCompiler/glslang/OSDependent/Windows/ossource.cpp +) + +set(THIRDPARTY_UNIX_SOURCES + ../thirdparty/ShaderCompiler/glslang/OSDependent/Unix/ossource.cpp +) + if( SSE_MATTERS ) if( FULL_SSE2 ) message( STATUS "Using SSE2 math everywhere." ) @@ -243,8 +372,20 @@ if( WIN32 ) else( CMAKE_COMPILER_IS_GNUCXX ) set( SOURCES ${SOURCES} src/platform/windows/resource.rc ) endif( CMAKE_COMPILER_IS_GNUCXX ) + + set(THIRDPARTY_SOURCES ${THIRDPARTY_SOURCES} ${THIRDPARTY_WIN32_SOURCES}) +else() + set(THIRDPARTY_SOURCES ${THIRDPARTY_SOURCES} ${THIRDPARTY_UNIX_SOURCES}) endif( WIN32 ) +if(MSVC) + # Use all cores for compilation + set(CMAKE_CXX_FLAGS "/MP ${CMAKE_CXX_FLAGS}") + + # Ignore warnings in third party code + set_source_files_properties(${THIRDPARTY_SOURCES} PROPERTIES COMPILE_FLAGS "/wd4244 /wd4267 /wd4005 /wd4018 -D_CRT_SECURE_NO_WARNINGS") +endif() + if( CMAKE_COMPILER_IS_GNUCXX ) set( ZDRAY_LIBS "${ZLIB_LIBRARIES}" pthread ) endif( CMAKE_COMPILER_IS_GNUCXX ) @@ -265,9 +406,9 @@ set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) -add_executable( zdray ${SOURCES} ${HEADERS} ) +add_executable( zdray ${SOURCES} ${HEADERS} ${THIRDPARTY_SOURCES} ) target_link_libraries( zdray ${ZDRAY_LIBS} ${PROF_LIB} ) -include_directories( src "${ZLIB_INCLUDE_DIR}" ) +include_directories( src "${ZLIB_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty" ) source_group("Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/.+") source_group("Sources\\BlockmapBuilder" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/blockmapbuilder/.+") @@ -282,3 +423,9 @@ source_group("Sources\\Wad" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src source_group("Sources\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/math/.+") source_group("Sources\\Lightmap" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/.+") source_group("Sources\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/models/.+") + +source_group("thirdparty" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/.+") +source_group("thirdparty\\ShaderCompiler" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/ShaderCompiler/.+") +source_group("thirdparty\\vk_mem_alloc" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/vk_mem_alloc/.+") +source_group("thirdparty\\volk" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/volk/.+") +source_group("thirdparty\\vulkan" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/vulkan/.+") diff --git a/src/level/level.cpp b/src/level/level.cpp index e68e24d..ec6a3e2 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -21,6 +21,7 @@ #include "level/level.h" #include "lightmap/lightmap.h" #include "lightmap/raytracer.h" +#include "lightmap/gpuraytracer.h" //#include "rejectbuilder.h" #include @@ -687,15 +688,21 @@ void FProcessor::BuildNodes() } } +//#define USE_GPU_RAYTRACER +//#define USE_CPU_RAYTRACER + void FProcessor::BuildLightmaps() { Level.SetupLights(); LightmapMesh = std::make_unique(Level, Samples, LMDims); -#if 1 - DLightRaytracer raytracer; +#if defined(USE_GPU_RAYTRACER) + GPURaytracer raytracer; + raytracer.Raytrace(LightmapMesh.get()); +#elif defined(USE_CPU_RAYTRACER) + Raytracer raytracer; raytracer.Raytrace(LightmapMesh.get()); #else - Raytracer raytracer; + DLightRaytracer raytracer; raytracer.Raytrace(LightmapMesh.get()); #endif LightmapMesh->CreateTextures(); diff --git a/src/lightmap/gpuraytracer.cpp b/src/lightmap/gpuraytracer.cpp new file mode 100644 index 0000000..93bd55f --- /dev/null +++ b/src/lightmap/gpuraytracer.cpp @@ -0,0 +1,330 @@ + +#include "math/mathlib.h" +#include "surfaces.h" +#include "level/level.h" +#include "gpuraytracer.h" +#include "surfacelight.h" +#include "worker.h" +#include "framework/binfile.h" +#include "framework/templates.h" +#include "framework/halffloat.h" +#include "vulkanbuilders.h" +#include +#include +#include +#include + +extern int Multisample; +extern int LightBounce; +extern float GridSize; + +GPURaytracer::GPURaytracer() +{ + device = std::make_unique(); +} + +GPURaytracer::~GPURaytracer() +{ +} + +void GPURaytracer::Raytrace(LevelMesh* level) +{ + mesh = level; + + printf("Creating vertex and index buffers\n"); + + size_t vertexbuffersize = (size_t)mesh->MeshVertices.Size() * sizeof(Vec3); + size_t indexbuffersize = (size_t)mesh->MeshElements.Size() * sizeof(uint32_t); + size_t aabbbuffersize = sizeof(Vec3) * 2; + size_t transferbuffersize = vertexbuffersize + indexbuffersize + aabbbuffersize; + size_t vertexoffset = 0; + size_t indexoffset = vertexoffset + vertexbuffersize; + size_t aabboffset = indexoffset + indexbuffersize; + + Vec3 aabbs[2] = { mesh->MeshVertices[0], mesh->MeshVertices[1] }; + for (Vec3& v : mesh->MeshVertices) + { + aabbs[0].x = std::min(aabbs[0].x, v.x); + aabbs[0].y = std::min(aabbs[0].y, v.y); + aabbs[0].z = std::min(aabbs[0].z, v.z); + aabbs[1].x = std::max(aabbs[1].x, v.x); + aabbs[1].y = std::max(aabbs[1].y, v.y); + aabbs[1].z = std::max(aabbs[1].z, v.z); + } + + BufferBuilder vbuilder; + vbuilder.setUsage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + vbuilder.setSize(vertexbuffersize); + auto vertexBuffer = vbuilder.create(device.get()); + + BufferBuilder ibuilder; + ibuilder.setUsage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + ibuilder.setSize(indexbuffersize); + auto indexBuffer = ibuilder.create(device.get()); + + BufferBuilder abuilder; + abuilder.setUsage(/*VK_BUFFER_USAGE_AABB_BUFFER_BIT |*/ VK_IMAGE_USAGE_TRANSFER_DST_BIT); + abuilder.setSize(aabbbuffersize); + auto aabbBuffer = abuilder.create(device.get()); + + BufferBuilder tbuilder; + tbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + tbuilder.setSize(transferbuffersize); + auto transferBuffer = tbuilder.create(device.get()); + auto data = (uint8_t*)transferBuffer->Map(0, transferbuffersize); + memcpy(data + vertexoffset, mesh->MeshVertices.Data(), vertexbuffersize); + memcpy(data + indexoffset, mesh->MeshElements.Data(), indexbuffersize); + memcpy(data + aabboffset, aabbs, aabbbuffersize); + transferBuffer->Unmap(); + + printf("Creating bottom level acceleration structure\n"); + + AccelerationStructureBuilder blbuilder; + blbuilder.setUsage(VMA_MEMORY_USAGE_GPU_ONLY); + blbuilder.setType(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV); + blbuilder.setInstanceCount(1); + blbuilder.addTriangles(); + blbuilder.setVertices(vertexBuffer.get(), 0, mesh->MeshVertices.Size(), sizeof(Vec3), VK_FORMAT_R32G32B32_SFLOAT); + blbuilder.setIndices(indexBuffer.get(), 0, mesh->MeshElements.Size(), VK_INDEX_TYPE_UINT32); + auto blAccelStruct = blbuilder.create(device.get()); + + ScratchBufferBuilder blsbuilder; + blsbuilder.setAccelerationStruct(blAccelStruct.get()); + auto blScratchBuffer = blsbuilder.create(device.get()); + + printf("Creating top level acceleration structure\n"); + + AccelerationStructureBuilder tlbuilder; + tlbuilder.setUsage(VMA_MEMORY_USAGE_GPU_ONLY); + tlbuilder.setType(VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV); + tlbuilder.setInstanceCount(1); + //tlbuilder.addAABBs(aabbBuffer.get(), 1, sizeof(Vec3) * 2, 0); + auto topLevel = tlbuilder.create(device.get()); + + printf("Tracing light probes\n"); + + Worker::RunJob((int)mesh->lightProbes.size(), [=](int id) { + RaytraceProbeSample(&mesh->lightProbes[id]); + }); + + printf("Tracing surfaces (%d bounces)\n", LightBounce); + + struct SurfaceTask + { + int surf, x, y; + }; + std::vector tasks; + + for (size_t i = 0; i < mesh->surfaces.size(); i++) + { + Surface* surface = mesh->surfaces[i].get(); + int sampleWidth = surface->lightmapDims[0]; + int sampleHeight = surface->lightmapDims[1]; + for (int y = 0; y < sampleHeight; y++) + { + for (int x = 0; x < sampleWidth; x++) + { + SurfaceTask task; + task.surf = (int)i; + task.x = x; + task.y = y; + tasks.push_back(task); + } + } + } + + Worker::RunJob((int)tasks.size(), [=](int id) { + const SurfaceTask& task = tasks[id]; + RaytraceSurfaceSample(mesh->surfaces[task.surf].get(), task.x, task.y); + }); + + printf("Raytrace complete\n"); +} + +void GPURaytracer::RaytraceProbeSample(LightProbeSample* probe) +{ + Vec3 color(0.0f, 0.0f, 0.0f); + + for (ThingLight& light : mesh->map->ThingLights) + { + Vec3 lightOrigin = light.LightOrigin(); + float lightRadius = light.LightRadius(); + + if (probe->Position.DistanceSq(lightOrigin) > (lightRadius * lightRadius)) + continue; + + if (mesh->TraceAnyHit(lightOrigin, probe->Position)) + continue; // this light is occluded by something + + Vec3 dir = (lightOrigin - probe->Position); + float dist = dir.Unit(); + dir.Normalize(); + + color += light.rgb * (light.SpotAttenuation(dir) * light.DistAttenuation(dist) * light.intensity); + } + + probe->Color = color; +} + +void GPURaytracer::RaytraceSurfaceSample(Surface* surface, int x, int y) +{ + Vec3 normal = surface->plane.Normal(); + Vec3 pos = surface->lightmapOrigin + normal + surface->lightmapSteps[0] * (float)x + surface->lightmapSteps[1] * (float)y; + + Vec3 incoming(0.0f, 0.0f, 0.0f); + if (LightBounce > 0) + { + float totalWeight = 0.0f; + for (int i = 0; i < SAMPLE_COUNT; i++) + { + Vec2 Xi = Hammersley(i, SAMPLE_COUNT); + Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f); + Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal); + float NdotL = std::max(Vec3::Dot(normal, L), 0.0f); + if (NdotL > 0.0f) + { + incoming += TracePath(pos, L, i) * NdotL; + totalWeight += NdotL; + } + } + incoming = incoming / totalWeight / (float)LightBounce; + } + + incoming = incoming + GetSurfaceEmittance(surface, 0.0f) + GetLightEmittance(surface, pos); + + size_t sampleWidth = surface->lightmapDims[0]; + surface->samples[x + y * sampleWidth] = incoming; +} + +Vec3 GPURaytracer::GetLightEmittance(Surface* surface, const Vec3& pos) +{ + Vec3 emittance = Vec3(0.0f); + for (ThingLight& light : mesh->map->ThingLights) + { + Vec3 lightOrigin = light.LightOrigin(); + float lightRadius = light.LightRadius(); + + if (surface->plane.Distance(lightOrigin) - surface->plane.d < 0) + continue; // completely behind the plane + + if (pos.DistanceSq(lightOrigin) > (lightRadius * lightRadius)) + continue; // light too far away + + Vec3 dir = (lightOrigin - pos); + float dist = dir.Unit(); + dir.Normalize(); + + float attenuation = light.SpotAttenuation(dir) * light.DistAttenuation(dist) * surface->plane.Normal().Dot(dir); + if (attenuation <= 0.0f) + continue; + + if (mesh->TraceAnyHit(lightOrigin, pos)) + continue; // this light is occluded by something + + emittance += light.rgb * (attenuation * light.intensity); + } + return emittance; +} + +Vec3 GPURaytracer::TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth) +{ + if (depth >= LightBounce) + return Vec3(0.0f); + + LevelTraceHit hit = mesh->Trace(pos, pos + dir * 1000.0f); + if (hit.fraction == 1.0f) + return Vec3(0.0f); + + Vec3 normal = hit.hitSurface->plane.Normal(); + Vec3 hitpos = hit.start * (1.0f - hit.fraction) + hit.end * hit.fraction + normal * 0.1f; + + Vec3 emittance = GetSurfaceEmittance(hit.hitSurface, pos.Distance(hitpos)) + GetLightEmittance(hit.hitSurface, hitpos) * 0.5f; + + Vec2 Xi = Hammersley(sampleIndex, SAMPLE_COUNT); + Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f); + Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal); + + float NdotL = std::max(Vec3::Dot(normal, L), 0.0f); + if (NdotL <= 0.0f) + return emittance; + + const float p = 1 / (2 * M_PI); + Vec3 incoming = TracePath(hitpos, normal, (sampleIndex + depth + 1) % SAMPLE_COUNT, depth + 1); + + return emittance + incoming * NdotL / p; +} + +float GPURaytracer::RadicalInverse_VdC(uint32_t bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10f; // / 0x100000000 +} + +Vec2 GPURaytracer::Hammersley(uint32_t i, uint32_t N) +{ + return Vec2(float(i) / float(N), RadicalInverse_VdC(i)); +} + +Vec3 GPURaytracer::ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness) +{ + float a = roughness * roughness; + + float phi = 2.0f * M_PI * Xi.x; + float cosTheta = sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y)); + float sinTheta = sqrt(1.0f - cosTheta * cosTheta); + + // from spherical coordinates to cartesian coordinates + Vec3 H(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta); + + // from tangent-space vector to world-space sample vector + Vec3 up = std::abs(N.z) < 0.999f ? Vec3(0.0f, 0.0f, 1.0f) : Vec3(1.0f, 0.0f, 0.0f); + Vec3 tangent = Vec3::Normalize(Vec3::Cross(up, N)); + Vec3 bitangent = Vec3::Cross(N, tangent); + + Vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; + return Vec3::Normalize(sampleVec); +} + +Vec3 GPURaytracer::GetSurfaceEmittance(Surface* surface, float distance) +{ + SurfaceLightDef* def = nullptr; + if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE) + { + int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef; + if (lightdefidx != -1) + { + def = &mesh->map->SurfaceLights[lightdefidx]; + } + } + else if (surface->type == ST_FLOOR || surface->type == ST_CEILING) + { + MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex]; + IntSector* sector = mesh->map->GetSectorFromSubSector(sub); + + if (sector && surface->numVerts > 0) + { + if (sector->floorlightdef != -1 && surface->type == ST_FLOOR) + { + def = &mesh->map->SurfaceLights[sector->floorlightdef]; + } + else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING) + { + def = &mesh->map->SurfaceLights[sector->ceilinglightdef]; + } + } + } + + if (def) + { + float attenuation = std::max(1.0f - (distance / def->distance), 0.0f); + return def->rgb * (attenuation * def->intensity); + } + else + { + return Vec3(0.0f); + } +} diff --git a/src/lightmap/gpuraytracer.h b/src/lightmap/gpuraytracer.h new file mode 100644 index 0000000..8ad3066 --- /dev/null +++ b/src/lightmap/gpuraytracer.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "vulkandevice.h" + +class LevelMesh; + +class GPURaytracer +{ +public: + GPURaytracer(); + ~GPURaytracer(); + + void Raytrace(LevelMesh* level); + +private: + void RaytraceProbeSample(LightProbeSample* probe); + void RaytraceSurfaceSample(Surface* surface, int x, int y); + Vec3 TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth = 0); + + Vec3 GetLightEmittance(Surface* surface, const Vec3& pos); + Vec3 GetSurfaceEmittance(Surface* surface, float distance); + + static float RadicalInverse_VdC(uint32_t bits); + static Vec2 Hammersley(uint32_t i, uint32_t N); + static Vec3 ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness); + + int SAMPLE_COUNT = 1024;// 128;// 1024; + + LevelMesh* mesh = nullptr; + + std::unique_ptr device; +}; diff --git a/src/lightmap/vulkanbuilders.cpp b/src/lightmap/vulkanbuilders.cpp new file mode 100644 index 0000000..bba96d9 --- /dev/null +++ b/src/lightmap/vulkanbuilders.cpp @@ -0,0 +1,192 @@ + +#include "vulkanbuilders.h" + +static const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; + +void ShaderBuilder::init() +{ + ShInitialize(); +} + +void ShaderBuilder::deinit() +{ + ShFinalize(); +} + +ShaderBuilder::ShaderBuilder() +{ +} + +void ShaderBuilder::setVertexShader(const std::string &c) { code = c; stage = EShLanguage::EShLangVertex; } +void ShaderBuilder::setTessControlShader(const std::string &c) { code = c; stage = EShLanguage::EShLangTessControl; } +void ShaderBuilder::setTessEvaluationShader(const std::string &c) { code = c; stage = EShLanguage::EShLangTessEvaluation; } +void ShaderBuilder::setGeometryShader(const std::string &c) { code = c; stage = EShLanguage::EShLangGeometry; } +void ShaderBuilder::setFragmentShader(const std::string &c) { code = c; stage = EShLanguage::EShLangFragment; } +void ShaderBuilder::setComputeShader(const std::string &c) { code = c; stage = EShLanguage::EShLangCompute; } +void ShaderBuilder::setRayGenShader(const std::string &c) { code = c; stage = EShLanguage::EShLangRayGenNV; } +void ShaderBuilder::setIntersectShader(const std::string &c) { code = c; stage = EShLanguage::EShLangIntersectNV; } +void ShaderBuilder::setAnyHitShader(const std::string &c) { code = c; stage = EShLanguage::EShLangAnyHitNV; } +void ShaderBuilder::setClosestHitShader(const std::string &c) { code = c; stage = EShLanguage::EShLangClosestHitNV; } +void ShaderBuilder::setMissShader(const std::string &c) { code = c; stage = EShLanguage::EShLangMissNV; } +void ShaderBuilder::setCallableShader(const std::string &c) { code = c; stage = EShLanguage::EShLangCallableNV; } +void ShaderBuilder::setTaskShader(const std::string &c) { code = c; stage = EShLanguage::EShLangTaskNV; } +void ShaderBuilder::setMeshShader(const std::string &c) { code = c; stage = EShLanguage::EShLangMeshNV; } + +std::unique_ptr ShaderBuilder::create(VulkanDevice *device) +{ + EShLanguage stage = (EShLanguage)this->stage; + const char *sources[] = { code.c_str() }; + + TBuiltInResource resources = DefaultTBuiltInResource; + + glslang::TShader shader(stage); + shader.setStrings(sources, 1); + shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100); + shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); + shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0); + bool compileSuccess = shader.parse(&resources, 110, false, EShMsgVulkanRules); + if (!compileSuccess) + { + throw std::runtime_error(std::string("Shader compile failed: ") + shader.getInfoLog()); + } + + glslang::TProgram program; + program.addShader(&shader); + bool linkSuccess = program.link(EShMsgDefault); + if (!linkSuccess) + { + throw std::runtime_error(std::string("Shader link failed: ") + program.getInfoLog()); + } + + glslang::TIntermediate *intermediate = program.getIntermediate(stage); + if (!intermediate) + { + throw std::runtime_error("Internal shader compiler error"); + } + + glslang::SpvOptions spvOptions; + spvOptions.generateDebugInfo = false; + spvOptions.disableOptimizer = false; + spvOptions.optimizeSize = true; + + std::vector spirv; + spv::SpvBuildLogger logger; + glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions); + + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = spirv.size() * sizeof(unsigned int); + createInfo.pCode = spirv.data(); + + VkShaderModule shaderModule; + VkResult result = vkCreateShaderModule(device->device, &createInfo, nullptr, &shaderModule); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan shader module"); + + return std::make_unique(device, shaderModule); +} diff --git a/src/lightmap/vulkanbuilders.h b/src/lightmap/vulkanbuilders.h new file mode 100644 index 0000000..d9e0a52 --- /dev/null +++ b/src/lightmap/vulkanbuilders.h @@ -0,0 +1,1747 @@ +#pragma once + +#include "vulkanobjects.h" +#include + +template +class FixedSizeVector +{ +public: + const T *data() const { return mItems; } + T *data() { return mItems; } + + size_t size() const { return mCount; } + + const T &back() const { return mItems[mCount - 1]; } + T &back() { return mItems[mCount - 1]; } + + void push_back(T && value) + { + assert(mCount != maxsize && "FixedSizeVector is too small"); + mItems[mCount++] = std::move(value); + } + + void push_back(const T &value) + { + assert(mCount != maxsize && "FixedSizeVector is too small"); + mItems[mCount++] = value; + } + +private: + T mItems[maxsize]; + size_t mCount = 0; +}; + +class ImageBuilder +{ +public: + ImageBuilder(); + + void setSize(int width, int height, int miplevels = 1); + void setSamples(VkSampleCountFlagBits samples); + void setFormat(VkFormat format); + void setUsage(VkImageUsageFlags imageUsage, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY, VmaAllocationCreateFlags allocFlags = 0); + void setMemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits = 0); + void setLinearTiling(); + + bool isFormatSupported(VulkanDevice *device); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkImageCreateInfo imageInfo = {}; + VmaAllocationCreateInfo allocInfo = {}; +}; + +class ImageViewBuilder +{ +public: + ImageViewBuilder(); + + void setImage(VulkanImage *image, VkFormat format, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkImageViewCreateInfo viewInfo = {}; +}; + +class SamplerBuilder +{ +public: + SamplerBuilder(); + + void setAddressMode(VkSamplerAddressMode addressMode); + void setAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w); + void setMinFilter(VkFilter minFilter); + void setMagFilter(VkFilter magFilter); + void setMipmapMode(VkSamplerMipmapMode mode); + void setAnisotropy(float maxAnisotropy); + void setMaxLod(float value); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkSamplerCreateInfo samplerInfo = {}; +}; + +class BufferBuilder +{ +public: + BufferBuilder(); + + void setSize(size_t size); + void setUsage(VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY, VmaAllocationCreateFlags allocFlags = 0); + void setMemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits = 0); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkBufferCreateInfo bufferInfo = {}; + VmaAllocationCreateInfo allocInfo = {}; +}; + +class ShaderBuilder +{ +public: + ShaderBuilder(); + + static void init(); + static void deinit(); + + void setVertexShader(const std::string &code); + void setTessControlShader(const std::string &code); + void setTessEvaluationShader(const std::string &code); + void setGeometryShader(const std::string &code); + void setFragmentShader(const std::string &code); + void setComputeShader(const std::string &code); + void setRayGenShader(const std::string &code); + void setIntersectShader(const std::string &code); + void setAnyHitShader(const std::string &code); + void setClosestHitShader(const std::string &code); + void setMissShader(const std::string &code); + void setCallableShader(const std::string &code); + void setTaskShader(const std::string &code); + void setMeshShader(const std::string &code); + + std::unique_ptr create(VulkanDevice *device); + +private: + std::string code; + int stage; +}; + +class AccelerationStructureBuilder +{ +public: + AccelerationStructureBuilder(); + + void setUsage(VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags = 0); + + void setType(VkAccelerationStructureTypeNV type, VkBuildAccelerationStructureFlagsNV flags = 0); + void setInstanceCount(int instanceCount); + + void addTriangles(VkGeometryFlagsNV flags = 0); + void setVertices(VulkanBuffer *vertexData, size_t vertexOffset, size_t vertexCount, size_t vertexStride, VkFormat vertexFormat); + void setIndices(VulkanBuffer *indexData, size_t indexOffset, size_t indexCount, VkIndexType indexType); + void setTransforms(VulkanBuffer *transformData, size_t transformOffset); + + void addAABBs(VulkanBuffer *aabbData, size_t numAABBs, size_t stride, size_t offset, VkBuildAccelerationStructureFlagsNV flags = 0); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkAccelerationStructureCreateInfoNV createInfo = {}; + VmaAllocationCreateInfo allocInfo = {}; + std::vector geometries; +}; + +class ScratchBufferBuilder +{ +public: + ScratchBufferBuilder(); + + void setAccelerationStruct(VulkanAccelerationStructure *accelstruct); + void setUpdateType(); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkAccelerationStructureMemoryRequirementsInfoNV reqInfo; + VmaAllocationCreateInfo allocInfo = {}; +}; + +class RayTracingPipelineBuilder +{ +public: + RayTracingPipelineBuilder(); + + void setLayout(VulkanPipelineLayout *layout); + void setMaxRecursionDepth(int depth); + void addShader(VkShaderStageFlagBits stage, VulkanShader *shader); + void addRayGenGroup(int rayGenShader); + void addMissGroup(int missShader); + void addTrianglesHitGroup(int closestHitShader, int anyHitShader); + void addProceduralHitGroup(int intersectionShader, int closestHitShader, int anyHitShader); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkRayTracingPipelineCreateInfoNV pipelineInfo = {}; + std::vector stages; + std::vector groups; +}; + +class ComputePipelineBuilder +{ +public: + ComputePipelineBuilder(); + + void setLayout(VulkanPipelineLayout *layout); + void setComputeShader(VulkanShader *shader); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkComputePipelineCreateInfo pipelineInfo = {}; + VkPipelineShaderStageCreateInfo stageInfo = {}; +}; + +class DescriptorSetLayoutBuilder +{ +public: + DescriptorSetLayoutBuilder(); + + void addBinding(int binding, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + FixedSizeVector bindings; +}; + +class DescriptorPoolBuilder +{ +public: + DescriptorPoolBuilder(); + + void setMaxSets(int value); + void addPoolSize(VkDescriptorType type, int count); + + std::unique_ptr create(VulkanDevice *device); + +private: + FixedSizeVector poolSizes; + VkDescriptorPoolCreateInfo poolInfo = {}; +}; + +class QueryPoolBuilder +{ +public: + QueryPoolBuilder(); + + void setQueryType(VkQueryType type, int count, VkQueryPipelineStatisticFlags pipelineStatistics = 0); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkQueryPoolCreateInfo poolInfo = {}; +}; + +class FramebufferBuilder +{ +public: + FramebufferBuilder(); + + void setRenderPass(VulkanRenderPass *renderPass); + void addAttachment(VulkanImageView *view); + void addAttachment(VkImageView view); + void setSize(int width, int height, int layers = 1); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkFramebufferCreateInfo framebufferInfo = {}; + FixedSizeVector attachments; +}; + +union FRenderStyle; + +class GraphicsPipelineBuilder +{ +public: + GraphicsPipelineBuilder(); + + void setSubpass(int subpass); + void setLayout(VulkanPipelineLayout *layout); + void setRenderPass(VulkanRenderPass *renderPass); + void setTopology(VkPrimitiveTopology topology); + void setViewport(float x, float y, float width, float height, float minDepth = 0.0f, float maxDepth = 1.0f); + void setScissor(int x, int y, int width, int height); + void setRasterizationSamples(VkSampleCountFlagBits samples); + + void setCull(VkCullModeFlags cullMode, VkFrontFace frontFace); + void setDepthStencilEnable(bool test, bool write, bool stencil); + void setDepthFunc(VkCompareOp func); + void setDepthClampEnable(bool value); + void setDepthBias(bool enable, float biasConstantFactor, float biasClamp, float biasSlopeFactor); + void setColorWriteMask(VkColorComponentFlags mask); + void setStencil(VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp, uint32_t compareMask, uint32_t writeMask, uint32_t reference); + + void setAdditiveBlendMode(); + void setAlphaBlendMode(); + void setBlendMode(VkBlendOp op, VkBlendFactor src, VkBlendFactor dst); + void setSubpassColorAttachmentCount(int count); + + void addVertexShader(VulkanShader *shader); + void addFragmentShader(VulkanShader *shader); + + void addVertexBufferBinding(int index, size_t stride); + void addVertexAttribute(int location, int binding, VkFormat format, size_t offset); + + void addDynamicState(VkDynamicState state); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkGraphicsPipelineCreateInfo pipelineInfo = { }; + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { }; + VkPipelineInputAssemblyStateCreateInfo inputAssembly = { }; + VkViewport viewport = { }; + VkRect2D scissor = { }; + VkPipelineViewportStateCreateInfo viewportState = { }; + VkPipelineRasterizationStateCreateInfo rasterizer = { }; + VkPipelineMultisampleStateCreateInfo multisampling = { }; + VkPipelineColorBlendAttachmentState colorBlendAttachment = { }; + VkPipelineColorBlendStateCreateInfo colorBlending = { }; + VkPipelineDepthStencilStateCreateInfo depthStencil = { }; + VkPipelineDynamicStateCreateInfo dynamicState = {}; + + std::vector shaderStages; + std::vector colorBlendAttachments; + std::vector vertexInputBindings; + std::vector vertexInputAttributes; + std::vector dynamicStates; +}; + +class PipelineLayoutBuilder +{ +public: + PipelineLayoutBuilder(); + + void addSetLayout(VulkanDescriptorSetLayout *setLayout); + void addPushConstantRange(VkShaderStageFlags stageFlags, size_t offset, size_t size); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + std::vector setLayouts; + std::vector pushConstantRanges; +}; + +class RenderPassBuilder +{ +public: + RenderPassBuilder(); + + void addAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkImageLayout initialLayout, VkImageLayout finalLayout); + void addDepthStencilAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkAttachmentLoadOp stencilLoad, VkAttachmentStoreOp stencilStore, VkImageLayout initialLayout, VkImageLayout finalLayout); + + void addExternalSubpassDependency(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + + void addSubpass(); + void addSubpassColorAttachmentRef(uint32_t index, VkImageLayout layout); + void addSubpassDepthStencilAttachmentRef(uint32_t index, VkImageLayout layout); + + std::unique_ptr create(VulkanDevice *device); + +private: + VkRenderPassCreateInfo renderPassInfo = { }; + + FixedSizeVector attachments; + FixedSizeVector dependencies; + FixedSizeVector subpasses; + + struct SubpassData + { + FixedSizeVector colorRefs; + VkAttachmentReference depthRef = { }; + }; + + FixedSizeVector, 8> subpassData; +}; + +class PipelineBarrier +{ +public: + void addMemory(VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + void addBuffer(VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + void addBuffer(VulkanBuffer *buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + void addImage(VulkanImage *image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); + void addImage(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); + void addQueueTransfer(int srcFamily, int dstFamily, VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + void addQueueTransfer(int srcFamily, int dstFamily, VulkanImage *image, VkImageLayout layout, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); + + void execute(VulkanCommandBuffer *commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags = 0); + +private: + FixedSizeVector memoryBarriers; + FixedSizeVector bufferMemoryBarriers; + FixedSizeVector imageMemoryBarriers; +}; + +class QueueSubmit +{ +public: + QueueSubmit(); + + void addCommandBuffer(VulkanCommandBuffer *buffer); + void addWait(VkPipelineStageFlags waitStageMask, VulkanSemaphore *semaphore); + void addSignal(VulkanSemaphore *semaphore); + void execute(VulkanDevice *device, VkQueue queue, VulkanFence *fence = nullptr); + +private: + VkSubmitInfo submitInfo = {}; + FixedSizeVector waitSemaphores; + FixedSizeVector waitStages; + FixedSizeVector signalSemaphores; + FixedSizeVector commandBuffers; +}; + +class WriteDescriptors +{ +public: + void addBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer); + void addBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer, size_t offset, size_t range); + void addStorageImage(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VkImageLayout imageLayout); + void addCombinedImageSampler(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VulkanSampler *sampler, VkImageLayout imageLayout); + void addAccelerationStructure(VulkanDescriptorSet *descriptorSet, int binding, VulkanAccelerationStructure *accelStruct); + + void updateSets(VulkanDevice *device); + +private: + struct WriteExtra + { + VkDescriptorImageInfo imageInfo; + VkDescriptorBufferInfo bufferInfo; + VkBufferView bufferView; + VkWriteDescriptorSetAccelerationStructureNV accelStruct; + }; + + std::vector writes; + std::vector> writeExtras; +}; + +///////////////////////////////////////////////////////////////////////////// + +inline ImageBuilder::ImageBuilder() +{ + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.depth = 1; + imageInfo.arrayLayers = 1; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Note: must either be VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; +} + +inline void ImageBuilder::setSize(int width, int height, int mipLevels) +{ + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.mipLevels = mipLevels; +} + +inline void ImageBuilder::setSamples(VkSampleCountFlagBits samples) +{ + imageInfo.samples = samples; +} + +inline void ImageBuilder::setFormat(VkFormat format) +{ + imageInfo.format = format; +} + +inline void ImageBuilder::setLinearTiling() +{ + imageInfo.tiling = VK_IMAGE_TILING_LINEAR; +} + +inline void ImageBuilder::setUsage(VkImageUsageFlags usage, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags) +{ + imageInfo.usage = usage; + allocInfo.usage = memoryUsage; + allocInfo.flags = allocFlags; +} + +inline bool ImageBuilder::isFormatSupported(VulkanDevice *device) +{ + VkImageFormatProperties properties = { }; + VkResult result = vkGetPhysicalDeviceImageFormatProperties(device->physicalDevice.device, imageInfo.format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags, &properties); + if (result != VK_SUCCESS) return false; + if (imageInfo.extent.width > properties.maxExtent.width) return false; + if (imageInfo.extent.height > properties.maxExtent.height) return false; + if (imageInfo.extent.depth > properties.maxExtent.depth) return false; + if (imageInfo.mipLevels > properties.maxMipLevels) return false; + if (imageInfo.arrayLayers > properties.maxArrayLayers) return false; + if ((imageInfo.samples & properties.sampleCounts) != imageInfo.samples) return false; + return true; +} + +inline std::unique_ptr ImageBuilder::create(VulkanDevice *device) +{ + VkImage image; + VmaAllocation allocation; + + VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan image"); + + return std::make_unique(device, image, allocation, imageInfo.extent.width, imageInfo.extent.height, imageInfo.mipLevels); +} + +///////////////////////////////////////////////////////////////////////////// + +inline ImageViewBuilder::ImageViewBuilder() +{ + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; +} + +inline void ImageViewBuilder::setImage(VulkanImage *image, VkFormat format, VkImageAspectFlags aspectMask) +{ + viewInfo.image = image->image; + viewInfo.format = format; + viewInfo.subresourceRange.levelCount = image->mipLevels; + viewInfo.subresourceRange.aspectMask = aspectMask; +} + +inline void ImageBuilder::setMemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits) +{ + allocInfo.requiredFlags = requiredFlags; + allocInfo.preferredFlags = preferredFlags; + allocInfo.memoryTypeBits = memoryTypeBits; +} + +inline std::unique_ptr ImageViewBuilder::create(VulkanDevice *device) +{ + VkImageView view; + VkResult result = vkCreateImageView(device->device, &viewInfo, nullptr, &view); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create texture image view"); + + return std::make_unique(device, view); +} + +///////////////////////////////////////////////////////////////////////////// + +inline SamplerBuilder::SamplerBuilder() +{ + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 100.0f; +} + +inline void SamplerBuilder::setAddressMode(VkSamplerAddressMode addressMode) +{ + samplerInfo.addressModeU = addressMode; + samplerInfo.addressModeV = addressMode; + samplerInfo.addressModeW = addressMode; +} + +inline void SamplerBuilder::setAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w) +{ + samplerInfo.addressModeU = u; + samplerInfo.addressModeV = v; + samplerInfo.addressModeW = w; +} + +inline void SamplerBuilder::setMinFilter(VkFilter minFilter) +{ + samplerInfo.minFilter = minFilter; +} + +inline void SamplerBuilder::setMagFilter(VkFilter magFilter) +{ + samplerInfo.magFilter = magFilter; +} + +inline void SamplerBuilder::setMipmapMode(VkSamplerMipmapMode mode) +{ + samplerInfo.mipmapMode = mode; +} + +inline void SamplerBuilder::setAnisotropy(float maxAnisotropy) +{ + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = maxAnisotropy; +} + +inline void SamplerBuilder::setMaxLod(float value) +{ + samplerInfo.maxLod = value; +} + +inline std::unique_ptr SamplerBuilder::create(VulkanDevice *device) +{ + VkSampler sampler; + VkResult result = vkCreateSampler(device->device, &samplerInfo, nullptr, &sampler); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create texture sampler"); + return std::make_unique(device, sampler); +} + +///////////////////////////////////////////////////////////////////////////// + +inline BufferBuilder::BufferBuilder() +{ + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; +} + +inline void BufferBuilder::setSize(size_t size) +{ + bufferInfo.size = size; +} + +inline void BufferBuilder::setUsage(VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags) +{ + bufferInfo.usage = bufferUsage; + allocInfo.usage = memoryUsage; + allocInfo.flags = allocFlags; +} + +inline void BufferBuilder::setMemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits) +{ + allocInfo.requiredFlags = requiredFlags; + allocInfo.preferredFlags = preferredFlags; + allocInfo.memoryTypeBits = memoryTypeBits; +} + +inline std::unique_ptr BufferBuilder::create(VulkanDevice *device) +{ + VkBuffer buffer; + VmaAllocation allocation; + + VkResult result = vmaCreateBuffer(device->allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); + if (result != VK_SUCCESS) + throw std::runtime_error("could not allocate memory for vulkan buffer"); + + return std::make_unique(device, buffer, allocation, (size_t)bufferInfo.size); +} + +///////////////////////////////////////////////////////////////////////////// + +inline AccelerationStructureBuilder::AccelerationStructureBuilder() +{ + createInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; + createInfo.info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; +} + +inline void AccelerationStructureBuilder::setUsage(VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags) +{ + allocInfo.usage = memoryUsage; + allocInfo.flags = allocFlags; +} + +inline void AccelerationStructureBuilder::setType(VkAccelerationStructureTypeNV type, VkBuildAccelerationStructureFlagsNV flags) +{ + createInfo.info.type = type; + createInfo.info.flags = flags; +} + +inline void AccelerationStructureBuilder::setInstanceCount(int instanceCount) +{ + createInfo.info.instanceCount = instanceCount; +} + +inline void AccelerationStructureBuilder::addTriangles(VkGeometryFlagsNV flags) +{ + VkGeometryNV g = {}; + g.sType = VK_STRUCTURE_TYPE_GEOMETRY_NV; + g.flags = flags; + g.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_NV; + g.geometry.triangles.indexType = VK_INDEX_TYPE_NONE_NV; + geometries.push_back(g); + + createInfo.info.pGeometries = geometries.data(); + createInfo.info.geometryCount = (uint32_t)geometries.size(); +} + +inline void AccelerationStructureBuilder::setVertices(VulkanBuffer *vertexData, size_t vertexOffset, size_t vertexCount, size_t vertexStride, VkFormat vertexFormat) +{ + auto &g = geometries.back(); + g.geometry.triangles.vertexData = vertexData->buffer; + g.geometry.triangles.vertexOffset = vertexOffset; + g.geometry.triangles.vertexCount = (uint32_t)vertexCount; + g.geometry.triangles.vertexStride = (uint32_t)vertexStride; + g.geometry.triangles.vertexFormat = vertexFormat; +} + +inline void AccelerationStructureBuilder::setIndices(VulkanBuffer *indexData, size_t indexOffset, size_t indexCount, VkIndexType indexType) +{ + auto &g = geometries.back(); + g.geometry.triangles.indexData = indexData->buffer; + g.geometry.triangles.indexOffset = indexOffset; + g.geometry.triangles.indexCount = (uint32_t)indexCount; + g.geometry.triangles.indexType = indexType; +} + +inline void AccelerationStructureBuilder::setTransforms(VulkanBuffer *transformData, size_t transformOffset) +{ + auto &g = geometries.back(); + g.geometry.triangles.transformData = transformData->buffer; + g.geometry.triangles.transformOffset = transformOffset; +} + +inline void AccelerationStructureBuilder::addAABBs(VulkanBuffer *aabbData, size_t numAABBs, size_t stride, size_t offset, VkGeometryFlagsNV flags) +{ + VkGeometryNV g = {}; + g.sType = VK_STRUCTURE_TYPE_GEOMETRY_NV; + g.flags = flags; + g.geometryType = VK_GEOMETRY_TYPE_AABBS_NV; + g.geometry.aabbs.aabbData = aabbData->buffer; + g.geometry.aabbs.numAABBs = (uint32_t)numAABBs; + g.geometry.aabbs.stride = (uint32_t)stride; + g.geometry.aabbs.offset = offset; + geometries.push_back(g); + + createInfo.info.pGeometries = geometries.data(); + createInfo.info.geometryCount = (uint32_t)geometries.size(); +} + +inline std::unique_ptr AccelerationStructureBuilder::create(VulkanDevice *device) +{ + VkAccelerationStructureNV accelstruct; + VkResult result = vkCreateAccelerationStructureNV(device->device, &createInfo, nullptr, &accelstruct); + if (result != VK_SUCCESS) + throw std::runtime_error("could not create vulkan acceleration structure"); + + VkAccelerationStructureMemoryRequirementsInfoNV reqInfo = {}; + reqInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + reqInfo.type = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV; + reqInfo.accelerationStructure = accelstruct; + + VkMemoryRequirements2KHR memoryRequirements2 = {}; + memoryRequirements2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + vkGetAccelerationStructureMemoryRequirementsNV(device->device, &reqInfo, &memoryRequirements2); + + VmaAllocation allocation; + VmaAllocationInfo allocResultInfo = {}; + result = vmaAllocateMemory(device->allocator, &memoryRequirements2.memoryRequirements, &allocInfo, &allocation, &allocResultInfo); + if (result != VK_SUCCESS) + { + vkDestroyAccelerationStructureNV(device->device, accelstruct, nullptr); + throw std::runtime_error("could not allocate memory for vulkan acceleration structure"); + } + + VkBindAccelerationStructureMemoryInfoNV bindInfo = {}; + bindInfo.sType = VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV; + bindInfo.accelerationStructure = accelstruct; + bindInfo.memory = allocResultInfo.deviceMemory; + bindInfo.memoryOffset = allocResultInfo.offset; + result = vkBindAccelerationStructureMemoryNV(device->device, 1, &bindInfo); + if (result != VK_SUCCESS) + { + vmaFreeMemory(device->allocator, allocation); + vkDestroyAccelerationStructureNV(device->device, accelstruct, nullptr); + throw std::runtime_error("could not bind memory to vulkan acceleration structure"); + } + + return std::make_unique(device, accelstruct, allocation, std::move(createInfo.info), std::move(geometries)); +} + +///////////////////////////////////////////////////////////////////////////// + +inline ScratchBufferBuilder::ScratchBufferBuilder() +{ + reqInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + reqInfo.type = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV; + + allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +} + +inline void ScratchBufferBuilder::setAccelerationStruct(VulkanAccelerationStructure *accelstruct) +{ + reqInfo.accelerationStructure = accelstruct->accelstruct; +} + +inline void ScratchBufferBuilder::setUpdateType() +{ + reqInfo.type = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV; +} + +inline std::unique_ptr ScratchBufferBuilder::create(VulkanDevice *device) +{ + VkMemoryRequirements2KHR memoryRequirements2 = {}; + memoryRequirements2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; + vkGetAccelerationStructureMemoryRequirementsNV(device->device, &reqInfo, &memoryRequirements2); + + VmaAllocation allocation; + VmaAllocationInfo allocResultInfo = {}; + VkResult result = vmaAllocateMemory(device->allocator, &memoryRequirements2.memoryRequirements, &allocInfo, &allocation, &allocResultInfo); + if (result != VK_SUCCESS) + throw std::runtime_error("could not allocate memory for vulkan scratch buffer"); + + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + bufferInfo.size = memoryRequirements2.memoryRequirements.size; + bufferInfo.usage = VK_BUFFER_USAGE_RAY_TRACING_BIT_NV; + + VkBuffer buffer; + result = vkCreateBuffer(device->device, &bufferInfo, nullptr, &buffer); + + result = vkBindBufferMemory(device->device, buffer, allocResultInfo.deviceMemory, allocResultInfo.offset); + if (result != VK_SUCCESS) + { + vmaFreeMemory(device->allocator, allocation); + vkDestroyBuffer(device->device, buffer, nullptr); + throw std::runtime_error("could not bind memory to vulkan scratch buffer"); + } + + return std::make_unique(device, buffer, allocation, (size_t)bufferInfo.size); +} + +///////////////////////////////////////////////////////////////////////////// + +inline RayTracingPipelineBuilder::RayTracingPipelineBuilder() +{ + pipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV; +} + +inline void RayTracingPipelineBuilder::setLayout(VulkanPipelineLayout *layout) +{ + pipelineInfo.layout = layout->layout; +} + +inline void RayTracingPipelineBuilder::setMaxRecursionDepth(int depth) +{ + pipelineInfo.maxRecursionDepth = depth; +} + +inline void RayTracingPipelineBuilder::addShader(VkShaderStageFlagBits stage, VulkanShader *shader) +{ + VkPipelineShaderStageCreateInfo stageInfo = {}; + stageInfo.stage = stage; + stageInfo.module = shader->module; + stageInfo.pName = "main"; + stages.push_back(stageInfo); + + pipelineInfo.pStages = stages.data(); + pipelineInfo.stageCount = (uint32_t)stages.size(); +} + +inline void RayTracingPipelineBuilder::addRayGenGroup(int rayGenShader) +{ + VkRayTracingShaderGroupCreateInfoNV group = {}; + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + group.generalShader = rayGenShader; + group.closestHitShader = VK_SHADER_UNUSED_NV; + group.anyHitShader = VK_SHADER_UNUSED_NV; + group.intersectionShader = VK_SHADER_UNUSED_NV; + groups.push_back(group); + + pipelineInfo.pGroups = groups.data(); + pipelineInfo.groupCount = (uint32_t)groups.size(); +} + +inline void RayTracingPipelineBuilder::addMissGroup(int missShader) +{ + VkRayTracingShaderGroupCreateInfoNV group = {}; + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + group.generalShader = missShader; + group.closestHitShader = VK_SHADER_UNUSED_NV; + group.anyHitShader = VK_SHADER_UNUSED_NV; + group.intersectionShader = VK_SHADER_UNUSED_NV; + groups.push_back(group); + + pipelineInfo.pGroups = groups.data(); + pipelineInfo.groupCount = (uint32_t)groups.size(); +} + +inline void RayTracingPipelineBuilder::addTrianglesHitGroup(int closestHitShader, int anyHitShader) +{ + VkRayTracingShaderGroupCreateInfoNV group = {}; + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV; + group.generalShader = VK_SHADER_UNUSED_NV; + group.closestHitShader = closestHitShader; + group.anyHitShader = anyHitShader; + group.intersectionShader = VK_SHADER_UNUSED_NV; + groups.push_back(group); + + pipelineInfo.pGroups = groups.data(); + pipelineInfo.groupCount = (uint32_t)groups.size(); +} + +inline void RayTracingPipelineBuilder::addProceduralHitGroup(int intersectionShader, int closestHitShader, int anyHitShader) +{ + VkRayTracingShaderGroupCreateInfoNV group = {}; + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV; + group.generalShader = VK_SHADER_UNUSED_NV; + group.closestHitShader = closestHitShader; + group.anyHitShader = anyHitShader; + group.intersectionShader = intersectionShader; + groups.push_back(group); + + pipelineInfo.pGroups = groups.data(); + pipelineInfo.groupCount = (uint32_t)groups.size(); +} + +inline std::unique_ptr RayTracingPipelineBuilder::create(VulkanDevice *device) +{ + VkPipeline pipeline; + VkResult result = vkCreateRayTracingPipelinesNV(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateRayTracingPipelinesNV failed"); + + std::vector shaderGroupHandles(device->physicalDevice.rayTracingProperties.shaderGroupHandleSize * groups.size()); + if (!groups.empty()) + { + result = vkGetRayTracingShaderGroupHandlesNV(device->device, pipeline, 0, (uint32_t)groups.size(), shaderGroupHandles.size(), shaderGroupHandles.data()); + if (result != VK_SUCCESS) + { + vkDestroyPipeline(device->device, pipeline, nullptr); + throw std::runtime_error("vkGetRayTracingShaderGroupHandlesNV failed"); + } + } + + return std::make_unique(device, pipeline, shaderGroupHandles); +} + +///////////////////////////////////////////////////////////////////////////// + +inline ComputePipelineBuilder::ComputePipelineBuilder() +{ + pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; +} + +inline void ComputePipelineBuilder::setLayout(VulkanPipelineLayout *layout) +{ + pipelineInfo.layout = layout->layout; +} + +inline void ComputePipelineBuilder::setComputeShader(VulkanShader *shader) +{ + stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + stageInfo.module = shader->module; + stageInfo.pName = "main"; + + pipelineInfo.stage = stageInfo; +} + +inline std::unique_ptr ComputePipelineBuilder::create(VulkanDevice *device) +{ + VkPipeline pipeline; + vkCreateComputePipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); + return std::make_unique(device, pipeline); +} + +///////////////////////////////////////////////////////////////////////////// + +inline DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder() +{ + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; +} + +inline void DescriptorSetLayoutBuilder::addBinding(int index, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags) +{ + VkDescriptorSetLayoutBinding binding = { }; + binding.binding = index; + binding.descriptorType = type; + binding.descriptorCount = arrayCount; + binding.stageFlags = stageFlags; + binding.pImmutableSamplers = nullptr; + bindings.push_back(binding); + + layoutInfo.bindingCount = (uint32_t)bindings.size(); + layoutInfo.pBindings = bindings.data(); +} + +inline std::unique_ptr DescriptorSetLayoutBuilder::create(VulkanDevice *device) +{ + VkDescriptorSetLayout layout; + VkResult result = vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &layout); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create descriptor set layout"); + return std::make_unique(device, layout); +} + +///////////////////////////////////////////////////////////////////////////// + +inline DescriptorPoolBuilder::DescriptorPoolBuilder() +{ + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.maxSets = 1; + poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; +} + +inline void DescriptorPoolBuilder::setMaxSets(int value) +{ + poolInfo.maxSets = value; +} + +inline void DescriptorPoolBuilder::addPoolSize(VkDescriptorType type, int count) +{ + VkDescriptorPoolSize size; + size.type = type; + size.descriptorCount = count; + poolSizes.push_back(size); + + poolInfo.poolSizeCount = (uint32_t)poolSizes.size(); + poolInfo.pPoolSizes = poolSizes.data(); +} + +inline std::unique_ptr DescriptorPoolBuilder::create(VulkanDevice *device) +{ + VkDescriptorPool descriptorPool; + VkResult result = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create descriptor pool"); + return std::make_unique(device, descriptorPool); +} + +///////////////////////////////////////////////////////////////////////////// + +inline QueryPoolBuilder::QueryPoolBuilder() +{ + poolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; +} + +inline void QueryPoolBuilder::setQueryType(VkQueryType type, int count, VkQueryPipelineStatisticFlags pipelineStatistics) +{ + poolInfo.queryType = type; + poolInfo.queryCount = count; + poolInfo.pipelineStatistics = pipelineStatistics; +} + +inline std::unique_ptr QueryPoolBuilder::create(VulkanDevice *device) +{ + VkQueryPool queryPool; + VkResult result = vkCreateQueryPool(device->device, &poolInfo, nullptr, &queryPool); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create query pool"); + return std::make_unique(device, queryPool); +} + +///////////////////////////////////////////////////////////////////////////// + +inline FramebufferBuilder::FramebufferBuilder() +{ + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; +} + +inline void FramebufferBuilder::setRenderPass(VulkanRenderPass *renderPass) +{ + framebufferInfo.renderPass = renderPass->renderPass; +} + +inline void FramebufferBuilder::addAttachment(VulkanImageView *view) +{ + attachments.push_back(view->view); + + framebufferInfo.attachmentCount = (uint32_t)attachments.size(); + framebufferInfo.pAttachments = attachments.data(); +} + +inline void FramebufferBuilder::addAttachment(VkImageView view) +{ + attachments.push_back(view); + + framebufferInfo.attachmentCount = (uint32_t)attachments.size(); + framebufferInfo.pAttachments = attachments.data(); +} + +inline void FramebufferBuilder::setSize(int width, int height, int layers) +{ + framebufferInfo.width = width; + framebufferInfo.height = height; + framebufferInfo.layers = 1; +} + +inline std::unique_ptr FramebufferBuilder::create(VulkanDevice *device) +{ + VkFramebuffer framebuffer = 0; + VkResult result = vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to create framebuffer"); + return std::make_unique(device, framebuffer); +} + +///////////////////////////////////////////////////////////////////////////// + +inline GraphicsPipelineBuilder::GraphicsPipelineBuilder() +{ + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + depthStencil.front = {}; + depthStencil.back = {}; + + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; +} + +inline void GraphicsPipelineBuilder::setRasterizationSamples(VkSampleCountFlagBits samples) +{ + multisampling.rasterizationSamples = samples; +} + +inline void GraphicsPipelineBuilder::setSubpass(int subpass) +{ + pipelineInfo.subpass = subpass; +} + +inline void GraphicsPipelineBuilder::setLayout(VulkanPipelineLayout *layout) +{ + pipelineInfo.layout = layout->layout; +} + +inline void GraphicsPipelineBuilder::setRenderPass(VulkanRenderPass *renderPass) +{ + pipelineInfo.renderPass = renderPass->renderPass; +} + +inline void GraphicsPipelineBuilder::setTopology(VkPrimitiveTopology topology) +{ + inputAssembly.topology = topology; +} + +inline void GraphicsPipelineBuilder::setViewport(float x, float y, float width, float height, float minDepth, float maxDepth) +{ + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = width; + viewport.height = height; + viewport.minDepth = minDepth; + viewport.maxDepth = maxDepth; + + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; +} + +inline void GraphicsPipelineBuilder::setScissor(int x, int y, int width, int height) +{ + scissor.offset.x = x; + scissor.offset.y = y; + scissor.extent.width = width; + scissor.extent.height = height; + + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; +} + +inline void GraphicsPipelineBuilder::setCull(VkCullModeFlags cullMode, VkFrontFace frontFace) +{ + rasterizer.cullMode = cullMode; + rasterizer.frontFace = frontFace; +} + +inline void GraphicsPipelineBuilder::setDepthStencilEnable(bool test, bool write, bool stencil) +{ + depthStencil.depthTestEnable = test ? VK_TRUE : VK_FALSE; + depthStencil.depthWriteEnable = write ? VK_TRUE : VK_FALSE; + depthStencil.stencilTestEnable = stencil ? VK_TRUE : VK_FALSE; + + pipelineInfo.pDepthStencilState = (test || write || stencil) ? &depthStencil : nullptr; +} + +inline void GraphicsPipelineBuilder::setStencil(VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp, uint32_t compareMask, uint32_t writeMask, uint32_t reference) +{ + depthStencil.front.failOp = failOp; + depthStencil.front.passOp = passOp; + depthStencil.front.depthFailOp = depthFailOp; + depthStencil.front.compareOp = compareOp; + depthStencil.front.compareMask = compareMask; + depthStencil.front.writeMask = writeMask; + depthStencil.front.reference = reference; + + depthStencil.back.failOp = failOp; + depthStencil.back.passOp = passOp; + depthStencil.back.depthFailOp = depthFailOp; + depthStencil.back.compareOp = compareOp; + depthStencil.back.compareMask = compareMask; + depthStencil.back.writeMask = writeMask; + depthStencil.back.reference = reference; +} + +inline void GraphicsPipelineBuilder::setDepthFunc(VkCompareOp func) +{ + depthStencil.depthCompareOp = func; +} + +inline void GraphicsPipelineBuilder::setDepthClampEnable(bool value) +{ + rasterizer.depthClampEnable = value ? VK_TRUE : VK_FALSE; +} + +inline void GraphicsPipelineBuilder::setDepthBias(bool enable, float biasConstantFactor, float biasClamp, float biasSlopeFactor) +{ + rasterizer.depthBiasEnable = enable ? VK_TRUE : VK_FALSE; + rasterizer.depthBiasConstantFactor = biasConstantFactor; + rasterizer.depthBiasClamp = biasClamp; + rasterizer.depthBiasSlopeFactor = biasSlopeFactor; +} + +inline void GraphicsPipelineBuilder::setColorWriteMask(VkColorComponentFlags mask) +{ + colorBlendAttachment.colorWriteMask = mask; +} + +inline void GraphicsPipelineBuilder::setAdditiveBlendMode() +{ + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; +} + +inline void GraphicsPipelineBuilder::setAlphaBlendMode() +{ + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; +} + +inline void GraphicsPipelineBuilder::setBlendMode(VkBlendOp op, VkBlendFactor src, VkBlendFactor dst) +{ + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = src; + colorBlendAttachment.dstColorBlendFactor = dst; + colorBlendAttachment.colorBlendOp = op; + colorBlendAttachment.srcAlphaBlendFactor = src; + colorBlendAttachment.dstAlphaBlendFactor = dst; + colorBlendAttachment.alphaBlendOp = op; +} + +inline void GraphicsPipelineBuilder::setSubpassColorAttachmentCount(int count) +{ + colorBlendAttachments.resize(count, colorBlendAttachment); + colorBlending.pAttachments = colorBlendAttachments.data(); + colorBlending.attachmentCount = (uint32_t)colorBlendAttachments.size(); +} + +inline void GraphicsPipelineBuilder::addVertexShader(VulkanShader *shader) +{ + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = shader->module; + vertShaderStageInfo.pName = "main"; + shaderStages.push_back(vertShaderStageInfo); + + pipelineInfo.stageCount = (uint32_t)shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); +} + +inline void GraphicsPipelineBuilder::addFragmentShader(VulkanShader *shader) +{ + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = shader->module; + fragShaderStageInfo.pName = "main"; + shaderStages.push_back(fragShaderStageInfo); + + pipelineInfo.stageCount = (uint32_t)shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); +} + +inline void GraphicsPipelineBuilder::addVertexBufferBinding(int index, size_t stride) +{ + VkVertexInputBindingDescription desc = {}; + desc.binding = index; + desc.stride = (uint32_t)stride; + desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + vertexInputBindings.push_back(desc); + + vertexInputInfo.vertexBindingDescriptionCount = (uint32_t)vertexInputBindings.size(); + vertexInputInfo.pVertexBindingDescriptions = vertexInputBindings.data(); +} + +inline void GraphicsPipelineBuilder::addVertexAttribute(int location, int binding, VkFormat format, size_t offset) +{ + VkVertexInputAttributeDescription desc = { }; + desc.location = location; + desc.binding = binding; + desc.format = format; + desc.offset = (uint32_t)offset; + vertexInputAttributes.push_back(desc); + + vertexInputInfo.vertexAttributeDescriptionCount = (uint32_t)vertexInputAttributes.size(); + vertexInputInfo.pVertexAttributeDescriptions = vertexInputAttributes.data(); +} + +inline void GraphicsPipelineBuilder::addDynamicState(VkDynamicState state) +{ + dynamicStates.push_back(state); + dynamicState.dynamicStateCount = (uint32_t)dynamicStates.size(); + dynamicState.pDynamicStates = dynamicStates.data(); +} + +inline std::unique_ptr GraphicsPipelineBuilder::create(VulkanDevice *device) +{ + VkPipeline pipeline = 0; + VkResult result = vkCreateGraphicsPipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create graphics pipeline"); + return std::make_unique(device, pipeline); +} + +///////////////////////////////////////////////////////////////////////////// + +inline PipelineLayoutBuilder::PipelineLayoutBuilder() +{ + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; +} + +inline void PipelineLayoutBuilder::addSetLayout(VulkanDescriptorSetLayout *setLayout) +{ + setLayouts.push_back(setLayout->layout); + pipelineLayoutInfo.setLayoutCount = (uint32_t)setLayouts.size(); + pipelineLayoutInfo.pSetLayouts = setLayouts.data(); +} + +inline void PipelineLayoutBuilder::addPushConstantRange(VkShaderStageFlags stageFlags, size_t offset, size_t size) +{ + VkPushConstantRange range = { }; + range.stageFlags = stageFlags; + range.offset = (uint32_t)offset; + range.size = (uint32_t)size; + pushConstantRanges.push_back(range); + pipelineLayoutInfo.pushConstantRangeCount = (uint32_t)pushConstantRanges.size(); + pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data(); +} + +inline std::unique_ptr PipelineLayoutBuilder::create(VulkanDevice *device) +{ + VkPipelineLayout pipelineLayout; + VkResult result = vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, nullptr, &pipelineLayout); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create pipeline layout"); + return std::make_unique(device, pipelineLayout); +} + +///////////////////////////////////////////////////////////////////////////// + +inline RenderPassBuilder::RenderPassBuilder() +{ + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; +} + +inline void RenderPassBuilder::addAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkImageLayout initialLayout, VkImageLayout finalLayout) +{ + VkAttachmentDescription attachment = {}; + attachment.format = format; + attachment.samples = samples; + attachment.loadOp = load; + attachment.storeOp = store; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = initialLayout; + attachment.finalLayout = finalLayout; + attachments.push_back(attachment); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.attachmentCount = (uint32_t)attachments.size(); +} + +inline void RenderPassBuilder::addDepthStencilAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkAttachmentLoadOp stencilLoad, VkAttachmentStoreOp stencilStore, VkImageLayout initialLayout, VkImageLayout finalLayout) +{ + VkAttachmentDescription attachment = {}; + attachment.format = format; + attachment.samples = samples; + attachment.loadOp = load; + attachment.storeOp = store; + attachment.stencilLoadOp = stencilLoad; + attachment.stencilStoreOp = stencilStore; + attachment.initialLayout = initialLayout; + attachment.finalLayout = finalLayout; + attachments.push_back(attachment); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.attachmentCount = (uint32_t)attachments.size(); +} + +inline void RenderPassBuilder::addExternalSubpassDependency(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = srcStageMask; + dependency.srcAccessMask = srcAccessMask; + dependency.dstStageMask = dstStageMask; + dependency.dstAccessMask = dstAccessMask; + + dependencies.push_back(dependency); + renderPassInfo.pDependencies = dependencies.data(); + renderPassInfo.dependencyCount = (uint32_t)dependencies.size(); +} + +inline void RenderPassBuilder::addSubpass() +{ + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + + subpasses.push_back(subpass); + renderPassInfo.pSubpasses = subpasses.data(); + renderPassInfo.subpassCount = (uint32_t)subpasses.size(); + + subpassData.push_back(std::make_unique()); +} + +inline void RenderPassBuilder::addSubpassColorAttachmentRef(uint32_t index, VkImageLayout layout) +{ + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = index; + colorAttachmentRef.layout = layout; + + subpassData.back()->colorRefs.push_back(colorAttachmentRef); + subpasses.back().pColorAttachments = subpassData.back()->colorRefs.data(); + subpasses.back().colorAttachmentCount = (uint32_t)subpassData.back()->colorRefs.size(); +} + +inline void RenderPassBuilder::addSubpassDepthStencilAttachmentRef(uint32_t index, VkImageLayout layout) +{ + VkAttachmentReference &depthAttachmentRef = subpassData.back()->depthRef; + depthAttachmentRef.attachment = index; + depthAttachmentRef.layout = layout; + + VkSubpassDescription &subpass = subpasses.back(); + subpass.pDepthStencilAttachment = &depthAttachmentRef; +} + +inline std::unique_ptr RenderPassBuilder::create(VulkanDevice *device) +{ + VkRenderPass renderPass = 0; + VkResult result = vkCreateRenderPass(device->device, &renderPassInfo, nullptr, &renderPass); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create render pass"); + return std::make_unique(device, renderPass); +} + +///////////////////////////////////////////////////////////////////////////// + +inline void PipelineBarrier::addMemory(VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + VkMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + memoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::addBuffer(VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + addBuffer(buffer, 0, buffer->size, srcAccessMask, dstAccessMask); +} + +inline void PipelineBarrier::addBuffer(VulkanBuffer *buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + VkBufferMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.buffer = buffer->buffer; + barrier.offset = offset; + barrier.size = size; + bufferMemoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::addImage(VulkanImage *image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount) +{ + addImage(image->image, oldLayout, newLayout, srcAccessMask, dstAccessMask, aspectMask, baseMipLevel, levelCount); +} + +inline void PipelineBarrier::addImage(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount) +{ + VkImageMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = aspectMask; + barrier.subresourceRange.baseMipLevel = baseMipLevel; + barrier.subresourceRange.levelCount = levelCount; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + imageMemoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::addQueueTransfer(int srcFamily, int dstFamily, VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + VkBufferMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + barrier.srcQueueFamilyIndex = srcFamily; + barrier.dstQueueFamilyIndex = dstFamily; + barrier.buffer = buffer->buffer; + barrier.offset = 0; + barrier.size = buffer->size; + bufferMemoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::addQueueTransfer(int srcFamily, int dstFamily, VulkanImage *image, VkImageLayout layout, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount) +{ + VkImageMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = layout; + barrier.newLayout = layout; + barrier.srcQueueFamilyIndex = srcFamily; + barrier.dstQueueFamilyIndex = dstFamily; + barrier.image = image->image; + barrier.subresourceRange.aspectMask = aspectMask; + barrier.subresourceRange.baseMipLevel = baseMipLevel; + barrier.subresourceRange.levelCount = levelCount; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + imageMemoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::execute(VulkanCommandBuffer *commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags) +{ + commandBuffer->pipelineBarrier( + srcStageMask, dstStageMask, dependencyFlags, + (uint32_t)memoryBarriers.size(), memoryBarriers.data(), + (uint32_t)bufferMemoryBarriers.size(), bufferMemoryBarriers.data(), + (uint32_t)imageMemoryBarriers.size(), imageMemoryBarriers.data()); +} + +///////////////////////////////////////////////////////////////////////////// + +inline QueueSubmit::QueueSubmit() +{ + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; +} + +inline void QueueSubmit::addCommandBuffer(VulkanCommandBuffer *buffer) +{ + commandBuffers.push_back(buffer->buffer); + submitInfo.pCommandBuffers = commandBuffers.data(); + submitInfo.commandBufferCount = (uint32_t)commandBuffers.size(); +} + +inline void QueueSubmit::addWait(VkPipelineStageFlags waitStageMask, VulkanSemaphore *semaphore) +{ + waitStages.push_back(waitStageMask); + waitSemaphores.push_back(semaphore->semaphore); + + submitInfo.pWaitDstStageMask = waitStages.data(); + submitInfo.pWaitSemaphores = waitSemaphores.data(); + submitInfo.waitSemaphoreCount = (uint32_t)waitSemaphores.size(); +} + +inline void QueueSubmit::addSignal(VulkanSemaphore *semaphore) +{ + signalSemaphores.push_back(semaphore->semaphore); + submitInfo.pSignalSemaphores = signalSemaphores.data(); + submitInfo.signalSemaphoreCount = (uint32_t)signalSemaphores.size(); +} + +inline void QueueSubmit::execute(VulkanDevice *device, VkQueue queue, VulkanFence *fence) +{ + VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE); + if (result < VK_SUCCESS) + throw std::runtime_error("Failed to submit command buffer"); +} + +///////////////////////////////////////////////////////////////////////////// + +inline void WriteDescriptors::addBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer) +{ + addBuffer(descriptorSet, binding, type, buffer, 0, buffer->size); +} + +inline void WriteDescriptors::addBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer, size_t offset, size_t range) +{ + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = buffer->buffer; + bufferInfo.offset = offset; + bufferInfo.range = range; + + auto extra = std::make_unique(); + extra->bufferInfo = bufferInfo; + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet->set; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = type; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &extra->bufferInfo; + writes.push_back(descriptorWrite); + writeExtras.push_back(std::move(extra)); +} + +inline void WriteDescriptors::addStorageImage(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VkImageLayout imageLayout) +{ + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = view->view; + imageInfo.imageLayout = imageLayout; + + auto extra = std::make_unique(); + extra->imageInfo = imageInfo; + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet->set; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pImageInfo = &extra->imageInfo; + writes.push_back(descriptorWrite); + writeExtras.push_back(std::move(extra)); +} + +inline void WriteDescriptors::addCombinedImageSampler(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VulkanSampler *sampler, VkImageLayout imageLayout) +{ + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = view->view; + imageInfo.sampler = sampler->sampler; + imageInfo.imageLayout = imageLayout; + + auto extra = std::make_unique(); + extra->imageInfo = imageInfo; + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet->set; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pImageInfo = &extra->imageInfo; + writes.push_back(descriptorWrite); + writeExtras.push_back(std::move(extra)); +} + +inline void WriteDescriptors::addAccelerationStructure(VulkanDescriptorSet *descriptorSet, int binding, VulkanAccelerationStructure *accelStruct) +{ + auto extra = std::make_unique(); + extra->accelStruct = {}; + extra->accelStruct.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV; + extra->accelStruct.accelerationStructureCount = 1; + extra->accelStruct.pAccelerationStructures = &accelStruct->accelstruct; + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet->set; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pNext = &extra->accelStruct; + writes.push_back(descriptorWrite); + writeExtras.push_back(std::move(extra)); +} + +inline void WriteDescriptors::updateSets(VulkanDevice *device) +{ + vkUpdateDescriptorSets(device->device, (uint32_t)writes.size(), writes.data(), 0, nullptr); +} diff --git a/src/lightmap/vulkandevice.cpp b/src/lightmap/vulkandevice.cpp new file mode 100644 index 0000000..b93b226 --- /dev/null +++ b/src/lightmap/vulkandevice.cpp @@ -0,0 +1,515 @@ + +#include "vulkandevice.h" +#include "vulkanobjects.h" +#include +#include +#include +#include + +VulkanDevice::VulkanDevice(int vk_device, bool vk_debug, std::function printLogCallback) : vk_device(vk_device), vk_debug(vk_debug), printLogCallback(printLogCallback) +{ + try + { + initVolk(); + createInstance(); + selectPhysicalDevice(); + selectFeatures(); + createDevice(); + createAllocator(); + } + catch (...) + { + releaseResources(); + throw; + } +} + +VulkanDevice::~VulkanDevice() +{ + releaseResources(); +} + +void VulkanDevice::selectFeatures() +{ + enabledDeviceFeatures.samplerAnisotropy = physicalDevice.features.samplerAnisotropy; + enabledDeviceFeatures.fragmentStoresAndAtomics = physicalDevice.features.fragmentStoresAndAtomics; + enabledDeviceFeatures.depthClamp = physicalDevice.features.depthClamp; + enabledDeviceFeatures.shaderClipDistance = physicalDevice.features.shaderClipDistance; +} + +bool VulkanDevice::checkRequiredFeatures(const VkPhysicalDeviceFeatures &f) +{ + return + f.samplerAnisotropy == VK_TRUE && + f.fragmentStoresAndAtomics == VK_TRUE && + f.depthClamp == VK_TRUE; +} + +void VulkanDevice::selectPhysicalDevice() +{ + availableDevices = getPhysicalDevices(instance); + if (availableDevices.empty()) + throw std::runtime_error("No Vulkan devices found. Either the graphics card has no vulkan support or the driver is too old."); + + // createSurface(); + + supportedDevices.clear(); + + for (size_t idx = 0; idx < availableDevices.size(); idx++) + { + const auto &info = availableDevices[idx]; + + if (!checkRequiredFeatures(info.features)) + continue; + + std::set requiredExtensionSearch(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end()); + for (const auto &ext : info.extensions) + requiredExtensionSearch.erase(ext.extensionName); + if (!requiredExtensionSearch.empty()) + continue; + + VulkanCompatibleDevice dev; + dev.device = &availableDevices[idx]; + + bool window = false; // static_cast(createSurfaceWindow); + + // Figure out what can present + if (window) + { + for (int i = 0; i < (int)info.queueFamilies.size(); i++) + { + VkBool32 presentSupport = false; + VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.device, i, surface, &presentSupport); + if (result == VK_SUCCESS && info.queueFamilies[i].queueCount > 0 && presentSupport) + { + dev.presentFamily = i; + break; + } + } + } + + // The vulkan spec states that graphics and compute queues can always do transfer. + // Furthermore the spec states that graphics queues always can do compute. + // Last, the spec makes it OPTIONAL whether the VK_QUEUE_TRANSFER_BIT is set for such queues, but they MUST support transfer. + // + // In short: pick the first graphics queue family for everything. + for (int i = 0; i < (int)info.queueFamilies.size(); i++) + { + const auto &queueFamily = info.queueFamilies[i]; + if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)) + { + dev.graphicsFamily = i; + break; + } + } + + if (dev.graphicsFamily != -1 && (!window || dev.presentFamily != -1)) + { + supportedDevices.push_back(dev); + } + } + + if (supportedDevices.empty()) + throw std::runtime_error("No Vulkan device supports the minimum requirements of this application"); + + // The device order returned by Vulkan can be anything. Prefer discrete > integrated > virtual gpu > cpu > other + std::stable_sort(supportedDevices.begin(), supportedDevices.end(), [&](const auto &a, const auto b) { + + // Sort by GPU type first. This will ensure the "best" device is most likely to map to vk_device 0 + static const int typeSort[] = { 4, 1, 0, 2, 3 }; + int sortA = a.device->properties.deviceType < 5 ? typeSort[a.device->properties.deviceType] : (int)a.device->properties.deviceType; + int sortB = b.device->properties.deviceType < 5 ? typeSort[b.device->properties.deviceType] : (int)b.device->properties.deviceType; + if (sortA != sortB) + return sortA < sortB; + + // Then sort by the device's unique ID so that vk_device uses a consistent order + int sortUUID = memcmp(a.device->properties.pipelineCacheUUID, b.device->properties.pipelineCacheUUID, VK_UUID_SIZE); + return sortUUID < 0; + }); + + size_t selected = vk_device; + if (selected >= supportedDevices.size()) + selected = 0; + + // Enable optional extensions we are interested in, if they are available on this device + for (const auto &ext : supportedDevices[selected].device->extensions) + { + for (const auto &opt : optionalDeviceExtensions) + { + if (strcmp(ext.extensionName, opt) == 0) + { + enabledDeviceExtensions.push_back(opt); + } + } + } + + physicalDevice = *supportedDevices[selected].device; + graphicsFamily = supportedDevices[selected].graphicsFamily; + presentFamily = supportedDevices[selected].presentFamily; +} + +bool VulkanDevice::supportsDeviceExtension(const char *ext) const +{ + return std::find(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end(), ext) != enabledDeviceExtensions.end(); +} + +void VulkanDevice::createAllocator() +{ + VmaAllocatorCreateInfo allocinfo = {}; + if (supportsDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) && supportsDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) + allocinfo.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + allocinfo.physicalDevice = physicalDevice.device; + allocinfo.device = device; + allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024; + if (vmaCreateAllocator(&allocinfo, &allocator) != VK_SUCCESS) + throw std::runtime_error("Unable to create allocator"); +} + +void VulkanDevice::createDevice() +{ + float queuePriority = 1.0f; + std::vector queueCreateInfos; + + std::set neededFamilies; + neededFamilies.insert(graphicsFamily); + if (presentFamily != -1) + neededFamilies.insert(presentFamily); + + for (int index : neededFamilies) + { + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = index; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(); + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.pEnabledFeatures = &enabledDeviceFeatures; + deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size(); + deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); + deviceCreateInfo.enabledLayerCount = 0; + + VkResult result = vkCreateDevice(physicalDevice.device, &deviceCreateInfo, nullptr, &device); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan device"); + + volkLoadDevice(device); + + vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue); + if (presentFamily != -1) + vkGetDeviceQueue(device, presentFamily, 0, &presentQueue); +} + +/* +#ifdef WIN32 +void VulkanDevice::createSurface() +{ + VkWin32SurfaceCreateInfoKHR windowCreateInfo = {}; + windowCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + windowCreateInfo.hwnd = window; + windowCreateInfo.hinstance = GetModuleHandle(nullptr); + + VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, &surface); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan surface"); +} +#else +void VulkanDevice::createSurface() +{ + for (const auto &info : availableDevices) + { + for (uint32_t i = 0; i < (uint32_t)info.queueFamilies.size(); i++) + { + const auto &queueFamily = info.queueFamilies[i]; + if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)) + { + uint32_t graphicsFamily = i; + auto handles = createSurfaceWindow(info.device, graphicsFamily); + + VkXlibSurfaceCreateInfoKHR windowCreateInfo = {}; + windowCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + windowCreateInfo.dpy = handles.first; + windowCreateInfo.window = handles.second; + + VkResult result = vkCreateXlibSurfaceKHR(instance, &windowCreateInfo, nullptr, &surface); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan surface"); + + return; + } + } + } + throw std::runtime_error("No vulkan device supports graphics!"); +} +#endif +*/ + +void VulkanDevice::createInstance() +{ + availableLayers = getAvailableLayers(); + extensions = getExtensions(); + enabledExtensions = getPlatformExtensions(); + + std::string debugLayer = "VK_LAYER_LUNARG_standard_validation"; + bool wantDebugLayer = vk_debug; + bool debugLayerFound = false; + for (const VkLayerProperties &layer : availableLayers) + { + if (layer.layerName == debugLayer && wantDebugLayer) + { + enabledValidationLayers.push_back(debugLayer.c_str()); + enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + debugLayerFound = true; + } + } + + // Enable optional instance extensions we are interested in + for (const auto &ext : extensions) + { + for (const auto &opt : optionalExtensions) + { + if (strcmp(ext.extensionName, opt) == 0) + { + enabledExtensions.push_back(opt); + } + } + } + + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "UT99"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "UT99"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_1; + + VkInstanceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); + createInfo.enabledLayerCount = (uint32_t)enabledValidationLayers.size(); + createInfo.ppEnabledLayerNames = enabledValidationLayers.data(); + createInfo.ppEnabledExtensionNames = enabledExtensions.data(); + + VkResult result = vkCreateInstance(&createInfo, nullptr, &instance); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create vulkan instance"); + + volkLoadInstance(instance); + + if (debugLayerFound) + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = + //VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + //VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = debugCallback; + createInfo.pUserData = this; + result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateDebugUtilsMessengerEXT failed"); + + debugLayerActive = true; + } +} + +VkBool32 VulkanDevice::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) +{ + VulkanDevice *device = (VulkanDevice*)userData; + + static std::mutex mtx; + static std::set seenMessages; + static int totalMessages; + + std::unique_lock lock(mtx); + + std::string msg = callbackData->pMessage; + + bool found = seenMessages.find(msg) != seenMessages.end(); + if (!found) + { + if (totalMessages < 20) + { + totalMessages++; + seenMessages.insert(msg); + + const char *typestr; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + typestr = "vulkan error"; + } + else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + { + typestr = "vulkan warning"; + } + else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) + { + typestr = "vulkan info"; + } + else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) + { + typestr = "vulkan verbose"; + } + else + { + typestr = "vulkan"; + } + + if (device->printLogCallback) + device->printLogCallback(typestr, msg); + } + } + + return VK_FALSE; +} + +std::vector VulkanDevice::getAvailableLayers() +{ + uint32_t layerCount; + VkResult result = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + result = vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + return availableLayers; +} + +std::vector VulkanDevice::getExtensions() +{ + uint32_t extensionCount = 0; + VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + + std::vector extensions(extensionCount); + result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + return extensions; +} + +std::vector VulkanDevice::getPhysicalDevices(VkInstance instance) +{ + uint32_t deviceCount = 0; + VkResult result = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + if (result == VK_ERROR_INITIALIZATION_FAILED) // Some drivers return this when a card does not support vulkan + return {}; + if (result != VK_SUCCESS) + throw std::runtime_error("vkEnumeratePhysicalDevices failed"); + if (deviceCount == 0) + return {}; + + std::vector devices(deviceCount); + result = vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + if (result != VK_SUCCESS) + throw std::runtime_error("vkEnumeratePhysicalDevices failed (2)"); + + std::vector devinfo(deviceCount); + for (size_t i = 0; i < devices.size(); i++) + { + auto &dev = devinfo[i]; + dev.device = devices[i]; + + VkPhysicalDeviceProperties2 props = {}; + VkPhysicalDeviceRayTracingPropertiesNV rayprops = {}; + props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props.pNext = &rayprops; + rayprops.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV; + vkGetPhysicalDeviceProperties2(dev.device, &props); + dev.properties = props.properties; + dev.rayTracingProperties = rayprops; + dev.rayTracingProperties.pNext = nullptr; + + vkGetPhysicalDeviceMemoryProperties(dev.device, &dev.memoryProperties); + vkGetPhysicalDeviceFeatures(dev.device, &dev.features); + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(dev.device, &queueFamilyCount, nullptr); + dev.queueFamilies.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(dev.device, &queueFamilyCount, dev.queueFamilies.data()); + + uint32_t deviceExtensionCount = 0; + vkEnumerateDeviceExtensionProperties(dev.device, nullptr, &deviceExtensionCount, nullptr); + dev.extensions.resize(deviceExtensionCount); + vkEnumerateDeviceExtensionProperties(dev.device, nullptr, &deviceExtensionCount, dev.extensions.data()); + } + return devinfo; +} + +#ifdef WIN32 +std::vector VulkanDevice::getPlatformExtensions() +{ + return + { + VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_WIN32_SURFACE_EXTENSION_NAME + }; +} +#else +std::vector VulkanDevice::getPlatformExtensions() +{ + return + { + VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_XLIB_SURFACE_EXTENSION_NAME + }; +} +#endif + +void VulkanDevice::initVolk() +{ + static bool volkInited = false; + if (!volkInited && volkInitialize() != VK_SUCCESS) + { + throw std::runtime_error("Unable to find Vulkan"); + } + volkInited = true; + auto iver = volkGetInstanceVersion(); + if (iver == 0) + { + throw std::runtime_error("Vulkan not supported"); + } +} + +void VulkanDevice::releaseResources() +{ + if (device) + vkDeviceWaitIdle(device); + + if (allocator) + vmaDestroyAllocator(allocator); + allocator = VK_NULL_HANDLE; + + if (device) + vkDestroyDevice(device, nullptr); + device = VK_NULL_HANDLE; + + if (surface) + vkDestroySurfaceKHR(instance, surface, nullptr); + surface = VK_NULL_HANDLE; + + if (debugMessenger) + vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); + + if (instance) + vkDestroyInstance(instance, nullptr); + instance = VK_NULL_HANDLE; +} + +uint32_t VulkanDevice::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) +{ + for (uint32_t i = 0; i < physicalDevice.memoryProperties.memoryTypeCount; i++) + { + if ((typeFilter & (1 << i)) && (physicalDevice.memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + return i; + } + + throw std::runtime_error("failed to find suitable memory type!"); +} diff --git a/src/lightmap/vulkandevice.h b/src/lightmap/vulkandevice.h new file mode 100644 index 0000000..af44033 --- /dev/null +++ b/src/lightmap/vulkandevice.h @@ -0,0 +1,126 @@ +#pragma once + +#include + +#ifdef WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#endif + +#include "volk/volk.h" +#include "vk_mem_alloc/vk_mem_alloc.h" + +#ifdef WIN32 +#undef min +#undef max +#endif + +#include "ShaderCompiler/glslang/Public/ShaderLang.h" +#include "ShaderCompiler/spirv/GlslangToSpv.h" + +class VulkanSwapChain; +class VulkanSemaphore; +class VulkanFence; + +class VulkanPhysicalDevice +{ +public: + VkPhysicalDevice device = VK_NULL_HANDLE; + + std::vector extensions; + std::vector queueFamilies; + VkPhysicalDeviceProperties properties = {}; + VkPhysicalDeviceRayTracingPropertiesNV rayTracingProperties = {}; + VkPhysicalDeviceFeatures features = {}; + VkPhysicalDeviceMemoryProperties memoryProperties = {}; +}; + +class VulkanCompatibleDevice +{ +public: + VulkanPhysicalDevice *device = nullptr; + int graphicsFamily = -1; + int presentFamily = -1; +}; + +class VulkanDevice +{ +public: + VulkanDevice(int vk_device = 0, bool vk_debug = false, std::function printLogCallback = {}); + ~VulkanDevice(); + + void setDebugObjectName(const char *name, uint64_t handle, VkObjectType type) + { + if (!debugLayerActive) return; + + VkDebugUtilsObjectNameInfoEXT info = {}; + info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + info.objectHandle = handle; + info.objectType = type; + info.pObjectName = name; + vkSetDebugUtilsObjectNameEXT(device, &info); + } + + uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + + // Instance setup + std::vector availableLayers; + std::vector extensions; + std::vector enabledExtensions; + std::vector optionalExtensions = { VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME }; + std::vector enabledValidationLayers; + + // Device setup + VkPhysicalDeviceFeatures enabledDeviceFeatures = {}; + std::vector enabledDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + std::vector optionalDeviceExtensions = { + VK_EXT_HDR_METADATA_EXTENSION_NAME, + VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, + VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, + VK_NV_RAY_TRACING_EXTENSION_NAME + }; + VulkanPhysicalDevice physicalDevice; + bool debugLayerActive = false; + + VkInstance instance = VK_NULL_HANDLE; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VmaAllocator allocator = VK_NULL_HANDLE; + + VkQueue graphicsQueue = VK_NULL_HANDLE; + VkQueue presentQueue = VK_NULL_HANDLE; + + int graphicsFamily = -1; + int presentFamily = -1; + + // Physical device info + std::vector availableDevices; + std::vector supportedDevices; + + static void initVolk(); + +private: + int vk_device; + bool vk_debug; + std::function printLogCallback; + + void createInstance(); + //void createSurface(); + void selectPhysicalDevice(); + void selectFeatures(); + void createDevice(); + void createAllocator(); + void releaseResources(); + + bool supportsDeviceExtension(const char *ext) const; + + static bool checkRequiredFeatures(const VkPhysicalDeviceFeatures &f); + + VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); + + static std::vector getAvailableLayers(); + static std::vector getExtensions(); + static std::vector getPlatformExtensions(); + static std::vector getPhysicalDevices(VkInstance instance); +}; diff --git a/src/lightmap/vulkanobjects.h b/src/lightmap/vulkanobjects.h new file mode 100644 index 0000000..b712a7d --- /dev/null +++ b/src/lightmap/vulkanobjects.h @@ -0,0 +1,1138 @@ +#pragma once + +#include "VulkanDevice.h" + +class VulkanCommandPool; +class VulkanDescriptorPool; + +class VulkanSemaphore +{ +public: + VulkanSemaphore(VulkanDevice *device); + ~VulkanSemaphore(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)semaphore, VK_OBJECT_TYPE_SEMAPHORE); } + + VulkanDevice *device = nullptr; + VkSemaphore semaphore = VK_NULL_HANDLE; + +private: + VulkanSemaphore(const VulkanSemaphore &) = delete; + VulkanSemaphore &operator=(const VulkanSemaphore &) = delete; +}; + +class VulkanFence +{ +public: + VulkanFence(VulkanDevice *device); + ~VulkanFence(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)fence, VK_OBJECT_TYPE_FENCE); } + + VulkanDevice *device = nullptr; + VkFence fence = VK_NULL_HANDLE; + +private: + VulkanFence(const VulkanFence &) = delete; + VulkanFence &operator=(const VulkanFence &) = delete; +}; + +class VulkanBuffer +{ +public: + VulkanBuffer(VulkanDevice *device, VkBuffer buffer, VmaAllocation allocation, size_t size); + ~VulkanBuffer(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_BUFFER); } + + VulkanDevice *device = nullptr; + + VkBuffer buffer; + VmaAllocation allocation; + size_t size = 0; + + void *Map(size_t offset, size_t size); + void Unmap(); + +private: + VulkanBuffer(const VulkanBuffer &) = delete; + VulkanBuffer &operator=(const VulkanBuffer &) = delete; +}; + +class VulkanFramebuffer +{ +public: + VulkanFramebuffer(VulkanDevice *device, VkFramebuffer framebuffer); + ~VulkanFramebuffer(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)framebuffer, VK_OBJECT_TYPE_FRAMEBUFFER); } + + VulkanDevice *device; + VkFramebuffer framebuffer; + +private: + VulkanFramebuffer(const VulkanFramebuffer &) = delete; + VulkanFramebuffer &operator=(const VulkanFramebuffer &) = delete; +}; + +class VulkanImage +{ +public: + VulkanImage(VulkanDevice *device, VkImage image, VmaAllocation allocation, int width, int height, int mipLevels); + ~VulkanImage(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)image, VK_OBJECT_TYPE_IMAGE); } + + VkImage image = VK_NULL_HANDLE; + int width = 0; + int height = 0; + int mipLevels = 1; + + void *Map(size_t offset, size_t size); + void Unmap(); + +private: + VulkanDevice *device = nullptr; + VmaAllocation allocation; + + VulkanImage(const VulkanImage &) = delete; + VulkanImage &operator=(const VulkanImage &) = delete; +}; + +class VulkanImageView +{ +public: + VulkanImageView(VulkanDevice *device, VkImageView view); + ~VulkanImageView(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)view, VK_OBJECT_TYPE_IMAGE_VIEW); } + + VkImageView view = VK_NULL_HANDLE; + +private: + VulkanImageView(const VulkanImageView &) = delete; + VulkanImageView &operator=(const VulkanImageView &) = delete; + + VulkanDevice *device = nullptr; +}; + +class VulkanSampler +{ +public: + VulkanSampler(VulkanDevice *device, VkSampler sampler); + ~VulkanSampler(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)sampler, VK_OBJECT_TYPE_SAMPLER); } + + VkSampler sampler = VK_NULL_HANDLE; + +private: + VulkanSampler(const VulkanSampler &) = delete; + VulkanSampler &operator=(const VulkanSampler &) = delete; + + VulkanDevice *device = nullptr; +}; + +class VulkanShader +{ +public: + VulkanShader(VulkanDevice *device, VkShaderModule module); + ~VulkanShader(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)module, VK_OBJECT_TYPE_SHADER_MODULE); } + + VkShaderModule module = VK_NULL_HANDLE; + +private: + VulkanDevice *device = nullptr; + + VulkanShader(const VulkanShader &) = delete; + VulkanShader &operator=(const VulkanShader &) = delete; +}; + +class VulkanDescriptorSetLayout +{ +public: + VulkanDescriptorSetLayout(VulkanDevice *device, VkDescriptorSetLayout layout); + ~VulkanDescriptorSetLayout(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT); } + + VulkanDevice *device; + VkDescriptorSetLayout layout; + +private: + VulkanDescriptorSetLayout(const VulkanDescriptorSetLayout &) = delete; + VulkanDescriptorSetLayout &operator=(const VulkanDescriptorSetLayout &) = delete; +}; + +class VulkanDescriptorSet +{ +public: + VulkanDescriptorSet(VulkanDevice *device, VulkanDescriptorPool *pool, VkDescriptorSet set); + ~VulkanDescriptorSet(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)set, VK_OBJECT_TYPE_DESCRIPTOR_SET); } + + VulkanDevice *device; + VulkanDescriptorPool *pool; + VkDescriptorSet set; + +private: + VulkanDescriptorSet(const VulkanDescriptorSet &) = delete; + VulkanDescriptorSet &operator=(const VulkanDescriptorSet &) = delete; +}; + +class VulkanDescriptorPool +{ +public: + VulkanDescriptorPool(VulkanDevice *device, VkDescriptorPool pool); + ~VulkanDescriptorPool(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_DESCRIPTOR_POOL); } + + std::unique_ptr allocate(VulkanDescriptorSetLayout *layout); + + VulkanDevice *device; + VkDescriptorPool pool; + +private: + VulkanDescriptorPool(const VulkanDescriptorPool &) = delete; + VulkanDescriptorPool &operator=(const VulkanDescriptorPool &) = delete; +}; + +class VulkanQueryPool +{ +public: + VulkanQueryPool(VulkanDevice *device, VkQueryPool pool); + ~VulkanQueryPool(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_QUERY_POOL); } + + bool getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void *data, VkDeviceSize stride, VkQueryResultFlags flags); + + VulkanDevice *device = nullptr; + VkQueryPool pool = VK_NULL_HANDLE; + +private: + VulkanQueryPool(const VulkanQueryPool &) = delete; + VulkanQueryPool &operator=(const VulkanQueryPool &) = delete; +}; + +class VulkanAccelerationStructure +{ +public: + VulkanAccelerationStructure(VulkanDevice *device, VkAccelerationStructureNV accelstruct, VmaAllocation allocation, VkAccelerationStructureInfoNV &&info, std::vector &&geometries); + ~VulkanAccelerationStructure(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)accelstruct, VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV); } + + VulkanDevice *device; + VkAccelerationStructureNV accelstruct; + VmaAllocation allocation; + + VkAccelerationStructureInfoNV info; + std::vector geometries; + +private: + VulkanAccelerationStructure(const VulkanAccelerationStructure &) = delete; + VulkanAccelerationStructure &operator=(const VulkanAccelerationStructure &) = delete; +}; + +class VulkanPipeline +{ +public: + VulkanPipeline(VulkanDevice *device, VkPipeline pipeline, std::vector shaderGroupHandles = {}); + ~VulkanPipeline(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pipeline, VK_OBJECT_TYPE_PIPELINE); } + + VulkanDevice *device; + VkPipeline pipeline; + std::vector shaderGroupHandles; + +private: + VulkanPipeline(const VulkanPipeline &) = delete; + VulkanPipeline &operator=(const VulkanPipeline &) = delete; +}; + +class VulkanPipelineLayout +{ +public: + VulkanPipelineLayout(VulkanDevice *device, VkPipelineLayout layout); + ~VulkanPipelineLayout(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_PIPELINE_LAYOUT); } + + VulkanDevice *device; + VkPipelineLayout layout; + +private: + VulkanPipelineLayout(const VulkanPipelineLayout &) = delete; + VulkanPipelineLayout &operator=(const VulkanPipelineLayout &) = delete; +}; + +class VulkanRenderPass +{ +public: + VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass); + ~VulkanRenderPass(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)renderPass, VK_OBJECT_TYPE_RENDER_PASS); } + + VulkanDevice *device; + VkRenderPass renderPass; + +private: + VulkanRenderPass(const VulkanRenderPass &) = delete; + VulkanRenderPass &operator=(const VulkanRenderPass &) = delete; +}; + +class RenderPassBegin +{ +public: + RenderPassBegin(); + + void setRenderPass(VulkanRenderPass *renderpass); + void setRenderArea(int x, int y, int width, int height); + void setFramebuffer(VulkanFramebuffer *framebuffer); + void addClearColor(float r, float g, float b, float a); + void addClearDepth(float value); + void addClearStencil(int value); + void addClearDepthStencil(float depthValue, int stencilValue); + + VkRenderPassBeginInfo renderPassInfo = {}; + +private: + std::vector clearValues; +}; + +class VulkanCommandBuffer +{ +public: + VulkanCommandBuffer(VulkanCommandPool *pool); + ~VulkanCommandBuffer(); + + void SetDebugName(const char *name); + + void begin(); + void end(); + + void bindPipeline(VkPipelineBindPoint pipelineBindPoint, VulkanPipeline *pipeline); + void bindPipeline(VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); + void setViewport(uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); + void setScissor(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); + void setLineWidth(float lineWidth); + void setDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); + void setBlendConstants(const float blendConstants[4]); + void setDepthBounds(float minDepthBounds, float maxDepthBounds); + void setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask); + void setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask); + void setStencilReference(VkStencilFaceFlags faceMask, uint32_t reference); + void bindDescriptorSet(VkPipelineBindPoint pipelineBindPoint, VulkanPipelineLayout *layout, uint32_t setIndex, VulkanDescriptorSet *descriptorSet, uint32_t dynamicOffsetCount = 0, const uint32_t* pDynamicOffsets = nullptr); + void bindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets); + void bindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); + void bindVertexBuffers(uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets); + void draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); + void drawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); + void drawIndirect(VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); + void drawIndexedIndirect(VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); + void dispatch(uint32_t x, uint32_t y, uint32_t z); + void dispatchIndirect(VkBuffer buffer, VkDeviceSize offset); + void copyBuffer(VulkanBuffer *srcBuffer, VulkanBuffer *dstBuffer, VkDeviceSize srcOffset = 0, VkDeviceSize dstOffset = 0, VkDeviceSize size = VK_WHOLE_SIZE); + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions); + void copyImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions); + void blitImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); + void copyBufferToImage(VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); + void copyImageToBuffer(VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); + void updateBuffer(VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); + void fillBuffer(VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); + void clearColorImage(VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); + void clearDepthStencilImage(VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); + void clearAttachments(uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects); + void resolveImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions); + void setEvent(VkEvent event, VkPipelineStageFlags stageMask); + void resetEvent(VkEvent event, VkPipelineStageFlags stageMask); + void waitEvents(uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); + void pipelineBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); + void beginQuery(VulkanQueryPool *queryPool, uint32_t query, VkQueryControlFlags flags); + void beginQuery(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); + void endQuery(VulkanQueryPool *queryPool, uint32_t query); + void endQuery(VkQueryPool queryPool, uint32_t query); + void resetQueryPool(VulkanQueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount); + void resetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); + void writeTimestamp(VkPipelineStageFlagBits pipelineStage, VulkanQueryPool *queryPool, uint32_t query); + void writeTimestamp(VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); + void copyQueryPoolResults(VulkanQueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount, VulkanBuffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); + void copyQueryPoolResults(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); + void pushConstants(VulkanPipelineLayout *layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); + void pushConstants(VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); + void beginRenderPass(const RenderPassBegin &renderPassBegin, VkSubpassContents contents = VK_SUBPASS_CONTENTS_INLINE); + void beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents); + void nextSubpass(VkSubpassContents contents); + void endRenderPass(); + void executeCommands(uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); + + void buildAccelerationStructure(const VkAccelerationStructureInfoNV* pInfo, VulkanBuffer *instanceData, VkDeviceSize instanceOffset, VkBool32 update, VulkanAccelerationStructure *dst, VulkanAccelerationStructure *src, VulkanBuffer *scratch, VkDeviceSize scratchOffset); + void buildAccelerationStructure(const VkAccelerationStructureInfoNV* pInfo, VkBuffer instanceData, VkDeviceSize instanceOffset, VkBool32 update, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkBuffer scratch, VkDeviceSize scratchOffset); + void copyAccelerationStructure(VulkanAccelerationStructure *dst, VulkanAccelerationStructure *src, VkCopyAccelerationStructureModeNV mode); + void copyAccelerationStructure(VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkCopyAccelerationStructureModeNV mode); + void traceRays(VulkanBuffer *raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VulkanBuffer *missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VulkanBuffer *hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VulkanBuffer *callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth); + void traceRays(VkBuffer raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VkBuffer missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VkBuffer hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VkBuffer callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth); + void writeAccelerationStructuresProperties(uint32_t accelerationStructureCount, const VkAccelerationStructureNV* pAccelerationStructures, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery); + + void debugFullPipelineBarrier(); + + VkCommandBuffer buffer = nullptr; + +private: + VulkanCommandPool *pool = nullptr; + + VulkanCommandBuffer(const VulkanCommandBuffer &) = delete; + VulkanCommandBuffer &operator=(const VulkanCommandBuffer &) = delete; +}; + +class VulkanCommandPool +{ +public: + VulkanCommandPool(VulkanDevice *device, int queueFamilyIndex); + ~VulkanCommandPool(); + + void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_COMMAND_POOL); } + + std::unique_ptr createBuffer(); + + VkCommandPool pool = VK_NULL_HANDLE; + +private: + VulkanDevice *device = nullptr; + + VulkanCommandPool(const VulkanCommandPool &) = delete; + VulkanCommandPool &operator=(const VulkanCommandPool &) = delete; + + friend class VulkanCommandBuffer; +}; + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanSemaphore::VulkanSemaphore(VulkanDevice *device) : device(device) +{ + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkResult result = vkCreateSemaphore(device->device, &semaphoreInfo, nullptr, &semaphore); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to create semaphore!"); +} + +inline VulkanSemaphore::~VulkanSemaphore() +{ + vkDestroySemaphore(device->device, semaphore, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanFence::VulkanFence(VulkanDevice *device) : device(device) +{ + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkResult result = vkCreateFence(device->device, &fenceInfo, nullptr, &fence); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to create fence!"); +} + +inline VulkanFence::~VulkanFence() +{ + vkDestroyFence(device->device, fence, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanBuffer::VulkanBuffer(VulkanDevice *device, VkBuffer buffer, VmaAllocation allocation, size_t size) : device(device), buffer(buffer), allocation(allocation), size(size) +{ +} + +inline VulkanBuffer::~VulkanBuffer() +{ + vmaDestroyBuffer(device->allocator, buffer, allocation); +} + +inline void *VulkanBuffer::Map(size_t offset, size_t size) +{ + void *data; + VkResult result = vmaMapMemory(device->allocator, allocation, &data); + return (result == VK_SUCCESS) ? ((uint8_t*)data) + offset : nullptr; +} + +inline void VulkanBuffer::Unmap() +{ + vmaUnmapMemory(device->allocator, allocation); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanCommandPool::VulkanCommandPool(VulkanDevice *device, int queueFamilyIndex) : device(device) +{ + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndex; + poolInfo.flags = 0; + + VkResult result = vkCreateCommandPool(device->device, &poolInfo, nullptr, &pool); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create command pool"); +} + +inline VulkanCommandPool::~VulkanCommandPool() +{ + vkDestroyCommandPool(device->device, pool, nullptr); +} + +inline std::unique_ptr VulkanCommandPool::createBuffer() +{ + return std::make_unique(this); +} + +///////////////////////////////////////////////////////////////////////////// + +inline RenderPassBegin::RenderPassBegin() +{ + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; +} + +inline void RenderPassBegin::setRenderPass(VulkanRenderPass *renderPass) +{ + renderPassInfo.renderPass = renderPass->renderPass; +} + +inline void RenderPassBegin::setRenderArea(int x, int y, int width, int height) +{ + renderPassInfo.renderArea.offset.x = x; + renderPassInfo.renderArea.offset.y = y; + renderPassInfo.renderArea.extent.width = width; + renderPassInfo.renderArea.extent.height = height; +} + +inline void RenderPassBegin::setFramebuffer(VulkanFramebuffer *framebuffer) +{ + renderPassInfo.framebuffer = framebuffer->framebuffer; +} + +inline void RenderPassBegin::addClearColor(float r, float g, float b, float a) +{ + VkClearValue clearValue = { }; + clearValue.color = { r, g, b, a }; + clearValues.push_back(clearValue); + + renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); +} + +inline void RenderPassBegin::addClearDepth(float value) +{ + VkClearValue clearValue = { }; + clearValue.depthStencil.depth = value; + clearValues.push_back(clearValue); + + renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); +} + +inline void RenderPassBegin::addClearStencil(int value) +{ + VkClearValue clearValue = { }; + clearValue.depthStencil.stencil = value; + clearValues.push_back(clearValue); + + renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); +} + +inline void RenderPassBegin::addClearDepthStencil(float depthValue, int stencilValue) +{ + VkClearValue clearValue = { }; + clearValue.depthStencil.depth = depthValue; + clearValue.depthStencil.stencil = stencilValue; + clearValues.push_back(clearValue); + + renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool *pool) : pool(pool) +{ + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = pool->pool; + allocInfo.commandBufferCount = 1; + + VkResult result = vkAllocateCommandBuffers(pool->device->device, &allocInfo, &buffer); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not create command buffer"); +} + +inline VulkanCommandBuffer::~VulkanCommandBuffer() +{ + vkFreeCommandBuffers(pool->device->device, pool->pool, 1, &buffer); +} + +inline void VulkanCommandBuffer::begin() +{ + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + beginInfo.pInheritanceInfo = nullptr; + + VkResult result = vkBeginCommandBuffer(buffer, &beginInfo); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to begin recording command buffer!"); +} + +inline void VulkanCommandBuffer::end() +{ + VkResult result = vkEndCommandBuffer(buffer); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to record command buffer!"); +} + +inline void VulkanCommandBuffer::debugFullPipelineBarrier() +{ + VkMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + barrier.srcAccessMask = + VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = + VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT | + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT | + VK_ACCESS_HOST_WRITE_BIT; + + vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr); +} + +inline void VulkanCommandBuffer::bindPipeline(VkPipelineBindPoint pipelineBindPoint, VulkanPipeline *pipeline) +{ + bindPipeline(pipelineBindPoint, pipeline->pipeline); +} + +inline void VulkanCommandBuffer::bindPipeline(VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) +{ + vkCmdBindPipeline(buffer, pipelineBindPoint, pipeline); +} + +inline void VulkanCommandBuffer::setViewport(uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports) +{ + vkCmdSetViewport(buffer, firstViewport, viewportCount, pViewports); +} + +inline void VulkanCommandBuffer::setScissor(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) +{ + vkCmdSetScissor(buffer, firstScissor, scissorCount, pScissors); +} + +inline void VulkanCommandBuffer::setLineWidth(float lineWidth) +{ + vkCmdSetLineWidth(buffer, lineWidth); +} + +inline void VulkanCommandBuffer::setDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) +{ + vkCmdSetDepthBias(buffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor); +} + +inline void VulkanCommandBuffer::setBlendConstants(const float blendConstants[4]) +{ + vkCmdSetBlendConstants(buffer, blendConstants); +} + +inline void VulkanCommandBuffer::setDepthBounds(float minDepthBounds, float maxDepthBounds) +{ + vkCmdSetDepthBounds(buffer, minDepthBounds, maxDepthBounds); +} + +inline void VulkanCommandBuffer::setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask) +{ + vkCmdSetStencilCompareMask(buffer, faceMask, compareMask); +} + +inline void VulkanCommandBuffer::setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask) +{ + vkCmdSetStencilWriteMask(buffer, faceMask, writeMask); +} + +inline void VulkanCommandBuffer::setStencilReference(VkStencilFaceFlags faceMask, uint32_t reference) +{ + vkCmdSetStencilReference(buffer, faceMask, reference); +} + +inline void VulkanCommandBuffer::bindDescriptorSet(VkPipelineBindPoint pipelineBindPoint, VulkanPipelineLayout *layout, uint32_t setIndex, VulkanDescriptorSet *descriptorSet, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) +{ + bindDescriptorSets(pipelineBindPoint, layout->layout, setIndex, 1, &descriptorSet->set, dynamicOffsetCount, pDynamicOffsets); +} + +inline void VulkanCommandBuffer::bindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) +{ + vkCmdBindDescriptorSets(buffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); +} + +inline void VulkanCommandBuffer::bindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) +{ + vkCmdBindIndexBuffer(this->buffer, buffer, offset, indexType); +} + +inline void VulkanCommandBuffer::bindVertexBuffers(uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) +{ + vkCmdBindVertexBuffers(buffer, firstBinding, bindingCount, pBuffers, pOffsets); +} + +inline void VulkanCommandBuffer::draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) +{ + vkCmdDraw(buffer, vertexCount, instanceCount, firstVertex, firstInstance); +} + +inline void VulkanCommandBuffer::drawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) +{ + vkCmdDrawIndexed(buffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); +} + +inline void VulkanCommandBuffer::drawIndirect(VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) +{ + vkCmdDrawIndirect(this->buffer, buffer, offset, drawCount, stride); +} + +inline void VulkanCommandBuffer::drawIndexedIndirect(VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) +{ + vkCmdDrawIndexedIndirect(this->buffer, buffer, offset, drawCount, stride); +} + +inline void VulkanCommandBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z) +{ + vkCmdDispatch(buffer, x, y, z); +} + +inline void VulkanCommandBuffer::dispatchIndirect(VkBuffer buffer, VkDeviceSize offset) +{ + vkCmdDispatchIndirect(this->buffer, buffer, offset); +} + +inline void VulkanCommandBuffer::copyBuffer(VulkanBuffer *srcBuffer, VulkanBuffer *dstBuffer, VkDeviceSize srcOffset, VkDeviceSize dstOffset, VkDeviceSize size) +{ + VkBufferCopy region = { }; + region.srcOffset = srcOffset; + region.dstOffset = dstOffset; + region.size = (size == VK_WHOLE_SIZE) ? dstBuffer->size : size; + copyBuffer(srcBuffer->buffer, dstBuffer->buffer, 1, ®ion); +} + +inline void VulkanCommandBuffer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions) +{ + vkCmdCopyBuffer(buffer, srcBuffer, dstBuffer, regionCount, pRegions); +} + +inline void VulkanCommandBuffer::copyImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions) +{ + vkCmdCopyImage(buffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); +} + +inline void VulkanCommandBuffer::blitImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter) +{ + vkCmdBlitImage(buffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter); +} + +inline void VulkanCommandBuffer::copyBufferToImage(VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions) +{ + vkCmdCopyBufferToImage(buffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions); +} + +inline void VulkanCommandBuffer::copyImageToBuffer(VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) +{ + vkCmdCopyImageToBuffer(buffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions); +} + +inline void VulkanCommandBuffer::updateBuffer(VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) +{ + vkCmdUpdateBuffer(buffer, dstBuffer, dstOffset, dataSize, pData); +} + +inline void VulkanCommandBuffer::fillBuffer(VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) +{ + vkCmdFillBuffer(buffer, dstBuffer, dstOffset, size, data); +} + +inline void VulkanCommandBuffer::clearColorImage(VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) +{ + vkCmdClearColorImage(buffer, image, imageLayout, pColor, rangeCount, pRanges); +} + +inline void VulkanCommandBuffer::clearDepthStencilImage(VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) +{ + vkCmdClearDepthStencilImage(buffer, image, imageLayout, pDepthStencil, rangeCount, pRanges); +} + +inline void VulkanCommandBuffer::clearAttachments(uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects) +{ + vkCmdClearAttachments(buffer, attachmentCount, pAttachments, rectCount, pRects); +} + +inline void VulkanCommandBuffer::resolveImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions) +{ + vkCmdResolveImage(buffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); +} + +inline void VulkanCommandBuffer::setEvent(VkEvent event, VkPipelineStageFlags stageMask) +{ + vkCmdSetEvent(buffer, event, stageMask); +} + +inline void VulkanCommandBuffer::resetEvent(VkEvent event, VkPipelineStageFlags stageMask) +{ + vkCmdResetEvent(buffer, event, stageMask); +} + +inline void VulkanCommandBuffer::waitEvents(uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) +{ + vkCmdWaitEvents(buffer, eventCount, pEvents, srcStageMask, dstStageMask, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); +} + +inline void VulkanCommandBuffer::pipelineBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) +{ + vkCmdPipelineBarrier(buffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); +} + +inline void VulkanCommandBuffer::beginQuery(VulkanQueryPool *queryPool, uint32_t query, VkQueryControlFlags flags) +{ + beginQuery(queryPool->pool, query, flags); +} + +inline void VulkanCommandBuffer::beginQuery(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) +{ + vkCmdBeginQuery(buffer, queryPool, query, flags); +} + +inline void VulkanCommandBuffer::endQuery(VulkanQueryPool *queryPool, uint32_t query) +{ + endQuery(queryPool->pool, query); +} + +inline void VulkanCommandBuffer::endQuery(VkQueryPool queryPool, uint32_t query) +{ + vkCmdEndQuery(buffer, queryPool, query); +} + +inline void VulkanCommandBuffer::resetQueryPool(VulkanQueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount) +{ + resetQueryPool(queryPool->pool, firstQuery, queryCount); +} + +inline void VulkanCommandBuffer::resetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) +{ + vkCmdResetQueryPool(buffer, queryPool, firstQuery, queryCount); +} + +inline void VulkanCommandBuffer::writeTimestamp(VkPipelineStageFlagBits pipelineStage, VulkanQueryPool *queryPool, uint32_t query) +{ + writeTimestamp(pipelineStage, queryPool->pool, query); +} + +inline void VulkanCommandBuffer::writeTimestamp(VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query) +{ + vkCmdWriteTimestamp(buffer, pipelineStage, queryPool, query); +} + +inline void VulkanCommandBuffer::copyQueryPoolResults(VulkanQueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount, VulkanBuffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) +{ + copyQueryPoolResults(queryPool->pool, firstQuery, queryCount, dstBuffer->buffer, dstOffset, stride, flags); +} + +inline void VulkanCommandBuffer::copyQueryPoolResults(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) +{ + vkCmdCopyQueryPoolResults(buffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags); +} + +inline void VulkanCommandBuffer::pushConstants(VulkanPipelineLayout *layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues) +{ + pushConstants(layout->layout, stageFlags, offset, size, pValues); +} + +inline void VulkanCommandBuffer::pushConstants(VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues) +{ + vkCmdPushConstants(buffer, layout, stageFlags, offset, size, pValues); +} + +inline void VulkanCommandBuffer::beginRenderPass(const RenderPassBegin &renderPassBegin, VkSubpassContents contents) +{ + beginRenderPass(&renderPassBegin.renderPassInfo, contents); +} + +inline void VulkanCommandBuffer::beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents) +{ + vkCmdBeginRenderPass(buffer, pRenderPassBegin, contents); +} + +inline void VulkanCommandBuffer::nextSubpass(VkSubpassContents contents) +{ + vkCmdNextSubpass(buffer, contents); +} + +inline void VulkanCommandBuffer::endRenderPass() +{ + vkCmdEndRenderPass(buffer); +} + +inline void VulkanCommandBuffer::executeCommands(uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) +{ + vkCmdExecuteCommands(buffer, commandBufferCount, pCommandBuffers); +} + +inline void VulkanCommandBuffer::buildAccelerationStructure(const VkAccelerationStructureInfoNV* pInfo, VulkanBuffer *instanceData, VkDeviceSize instanceOffset, VkBool32 update, VulkanAccelerationStructure *dst, VulkanAccelerationStructure *src, VulkanBuffer *scratch, VkDeviceSize scratchOffset) +{ + buildAccelerationStructure(pInfo, instanceData->buffer, instanceOffset, update, dst->accelstruct, src->accelstruct, scratch->buffer, scratchOffset); +} + +inline void VulkanCommandBuffer::buildAccelerationStructure(const VkAccelerationStructureInfoNV* pInfo, VkBuffer instanceData, VkDeviceSize instanceOffset, VkBool32 update, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkBuffer scratch, VkDeviceSize scratchOffset) +{ + vkCmdBuildAccelerationStructureNV(buffer, pInfo, instanceData, instanceOffset, update, dst, src, scratch, scratchOffset); +} + +inline void VulkanCommandBuffer::copyAccelerationStructure(VulkanAccelerationStructure *dst, VulkanAccelerationStructure *src, VkCopyAccelerationStructureModeNV mode) +{ + copyAccelerationStructure(dst->accelstruct, src->accelstruct, mode); +} + +inline void VulkanCommandBuffer::copyAccelerationStructure(VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkCopyAccelerationStructureModeNV mode) +{ + vkCmdCopyAccelerationStructureNV(buffer, dst, src, mode); +} + +inline void VulkanCommandBuffer::traceRays(VulkanBuffer *raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VulkanBuffer *missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VulkanBuffer *hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VulkanBuffer *callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth) +{ + traceRays( + raygenShaderBindingTableBuffer->buffer, raygenShaderBindingOffset, + missShaderBindingTableBuffer ? missShaderBindingTableBuffer->buffer : VK_NULL_HANDLE, missShaderBindingOffset, missShaderBindingStride, + hitShaderBindingTableBuffer ? hitShaderBindingTableBuffer->buffer : VK_NULL_HANDLE, hitShaderBindingOffset, hitShaderBindingStride, + callableShaderBindingTableBuffer ? callableShaderBindingTableBuffer->buffer : VK_NULL_HANDLE, callableShaderBindingOffset, callableShaderBindingStride, + width, height, depth); +} + +inline void VulkanCommandBuffer::traceRays(VkBuffer raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VkBuffer missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VkBuffer hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VkBuffer callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth) +{ + vkCmdTraceRaysNV(buffer, raygenShaderBindingTableBuffer, raygenShaderBindingOffset, missShaderBindingTableBuffer, missShaderBindingOffset, missShaderBindingStride, hitShaderBindingTableBuffer, hitShaderBindingOffset, hitShaderBindingStride, callableShaderBindingTableBuffer, callableShaderBindingOffset, callableShaderBindingStride, width, height, depth); +} + +inline void VulkanCommandBuffer::writeAccelerationStructuresProperties(uint32_t accelerationStructureCount, const VkAccelerationStructureNV* pAccelerationStructures, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery) +{ + vkCmdWriteAccelerationStructuresPropertiesNV(buffer, accelerationStructureCount, pAccelerationStructures, queryType, queryPool, firstQuery); +} + +inline void VulkanCommandBuffer::SetDebugName(const char *name) +{ + pool->device->setDebugObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_COMMAND_BUFFER); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanShader::VulkanShader(VulkanDevice *device, VkShaderModule module) : module(module), device(device) +{ +} + +inline VulkanShader::~VulkanShader() +{ + vkDestroyShaderModule(device->device, module, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VulkanDevice *device, VkDescriptorSetLayout layout) : device(device), layout(layout) +{ +} + +inline VulkanDescriptorSetLayout::~VulkanDescriptorSetLayout() +{ + vkDestroyDescriptorSetLayout(device->device, layout, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanDescriptorSet::VulkanDescriptorSet(VulkanDevice *device, VulkanDescriptorPool *pool, VkDescriptorSet set) : device(device), pool(pool), set(set) +{ +} + +inline VulkanDescriptorSet::~VulkanDescriptorSet() +{ + vkFreeDescriptorSets(device->device, pool->pool, 1, &set); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanDescriptorPool::VulkanDescriptorPool(VulkanDevice *device, VkDescriptorPool pool) : device(device), pool(pool) +{ +} + +inline VulkanDescriptorPool::~VulkanDescriptorPool() +{ + vkDestroyDescriptorPool(device->device, pool, nullptr); +} + +inline std::unique_ptr VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout *layout) +{ + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = pool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &layout->layout; + + VkDescriptorSet descriptorSet; + VkResult result = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet); + if (result != VK_SUCCESS) + throw std::runtime_error("Could not allocate descriptor sets"); + + return std::make_unique(device, this, descriptorSet); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanQueryPool::VulkanQueryPool(VulkanDevice *device, VkQueryPool pool) : device(device), pool(pool) +{ +} + +inline VulkanQueryPool::~VulkanQueryPool() +{ + vkDestroyQueryPool(device->device, pool, nullptr); +} + +inline bool VulkanQueryPool::getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void *data, VkDeviceSize stride, VkQueryResultFlags flags) +{ + VkResult result = vkGetQueryPoolResults(device->device, pool, firstQuery, queryCount, dataSize, data, stride, flags); + if (result != VK_SUCCESS && result != VK_NOT_READY) + throw std::runtime_error("vkGetQueryPoolResults failed"); + return result == VK_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanFramebuffer::VulkanFramebuffer(VulkanDevice *device, VkFramebuffer framebuffer) : device(device), framebuffer(framebuffer) +{ +} + +inline VulkanFramebuffer::~VulkanFramebuffer() +{ + vkDestroyFramebuffer(device->device, framebuffer, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanImage::VulkanImage(VulkanDevice *device, VkImage image, VmaAllocation allocation, int width, int height, int mipLevels) : image(image), width(width), height(height), mipLevels(mipLevels), device(device), allocation(allocation) +{ +} + +inline VulkanImage::~VulkanImage() +{ + vmaDestroyImage(device->allocator, image, allocation); +} + +inline void *VulkanImage::Map(size_t offset, size_t size) +{ + void *data; + VkResult result = vmaMapMemory(device->allocator, allocation, &data); + return (result == VK_SUCCESS) ? ((uint8_t*)data) + offset : nullptr; +} + +inline void VulkanImage::Unmap() +{ + vmaUnmapMemory(device->allocator, allocation); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanImageView::VulkanImageView(VulkanDevice *device, VkImageView view) : view(view), device(device) +{ +} + +inline VulkanImageView::~VulkanImageView() +{ + vkDestroyImageView(device->device, view, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanSampler::VulkanSampler(VulkanDevice *device, VkSampler sampler) : sampler(sampler), device(device) +{ +} + +inline VulkanSampler::~VulkanSampler() +{ + vkDestroySampler(device->device, sampler, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanAccelerationStructure::VulkanAccelerationStructure(VulkanDevice *device, VkAccelerationStructureNV accelstruct, VmaAllocation allocation, VkAccelerationStructureInfoNV &&info, std::vector &&geometries) + : device(device), accelstruct(accelstruct), allocation(allocation), info(std::move(info)), geometries(std::move(geometries)) +{ +} + +inline VulkanAccelerationStructure::~VulkanAccelerationStructure() +{ + vkDestroyAccelerationStructureNV(device->device, accelstruct, nullptr); + vmaFreeMemory(device->allocator, allocation); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanPipeline::VulkanPipeline(VulkanDevice *device, VkPipeline pipeline, std::vector shaderGroupHandles) : device(device), pipeline(pipeline), shaderGroupHandles(shaderGroupHandles) +{ +} + +inline VulkanPipeline::~VulkanPipeline() +{ + vkDestroyPipeline(device->device, pipeline, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanPipelineLayout::VulkanPipelineLayout(VulkanDevice *device, VkPipelineLayout layout) : device(device), layout(layout) +{ +} + +inline VulkanPipelineLayout::~VulkanPipelineLayout() +{ + vkDestroyPipelineLayout(device->device, layout, nullptr); +} + +///////////////////////////////////////////////////////////////////////////// + +inline VulkanRenderPass::VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass) : device(device), renderPass(renderPass) +{ +} + +inline VulkanRenderPass::~VulkanRenderPass() +{ + vkDestroyRenderPass(device->device, renderPass, nullptr); +}