Add first steps in using the GPU for the ray tracing

This commit is contained in:
Magnus Norddahl 2021-10-28 23:27:25 +02:00
parent 410a0e0c7c
commit 3e11f81d5a
9 changed files with 4240 additions and 5 deletions

View file

@ -163,6 +163,13 @@ set( SOURCES
src/lightmap/collision.cpp src/lightmap/collision.cpp
src/lightmap/delauneytriangulator.cpp src/lightmap/delauneytriangulator.cpp
src/lightmap/delauneytriangulator.h 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/angle.cpp
src/math/bounds.cpp src/math/bounds.cpp
src/math/mathlib.cpp src/math/mathlib.cpp
@ -210,6 +217,128 @@ set( HEADERS
src/math/mathlib.h 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( SSE_MATTERS )
if( FULL_SSE2 ) if( FULL_SSE2 )
message( STATUS "Using SSE2 math everywhere." ) message( STATUS "Using SSE2 math everywhere." )
@ -243,8 +372,20 @@ if( WIN32 )
else( CMAKE_COMPILER_IS_GNUCXX ) else( CMAKE_COMPILER_IS_GNUCXX )
set( SOURCES ${SOURCES} src/platform/windows/resource.rc ) set( SOURCES ${SOURCES} src/platform/windows/resource.rc )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( CMAKE_COMPILER_IS_GNUCXX )
set(THIRDPARTY_SOURCES ${THIRDPARTY_SOURCES} ${THIRDPARTY_WIN32_SOURCES})
else()
set(THIRDPARTY_SOURCES ${THIRDPARTY_SOURCES} ${THIRDPARTY_UNIX_SOURCES})
endif( WIN32 ) 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 ) if( CMAKE_COMPILER_IS_GNUCXX )
set( ZDRAY_LIBS "${ZLIB_LIBRARIES}" pthread ) set( ZDRAY_LIBS "${ZLIB_LIBRARIES}" pthread )
endif( CMAKE_COMPILER_IS_GNUCXX ) 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_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) 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} ) 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" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/.+")
source_group("Sources\\BlockmapBuilder" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/blockmapbuilder/.+") 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\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/math/.+")
source_group("Sources\\Lightmap" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/.+") 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("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/.+")

View file

@ -21,6 +21,7 @@
#include "level/level.h" #include "level/level.h"
#include "lightmap/lightmap.h" #include "lightmap/lightmap.h"
#include "lightmap/raytracer.h" #include "lightmap/raytracer.h"
#include "lightmap/gpuraytracer.h"
//#include "rejectbuilder.h" //#include "rejectbuilder.h"
#include <memory> #include <memory>
@ -687,15 +688,21 @@ void FProcessor::BuildNodes()
} }
} }
//#define USE_GPU_RAYTRACER
//#define USE_CPU_RAYTRACER
void FProcessor::BuildLightmaps() void FProcessor::BuildLightmaps()
{ {
Level.SetupLights(); Level.SetupLights();
LightmapMesh = std::make_unique<LevelMesh>(Level, Samples, LMDims); LightmapMesh = std::make_unique<LevelMesh>(Level, Samples, LMDims);
#if 1 #if defined(USE_GPU_RAYTRACER)
DLightRaytracer raytracer; GPURaytracer raytracer;
raytracer.Raytrace(LightmapMesh.get());
#elif defined(USE_CPU_RAYTRACER)
Raytracer raytracer;
raytracer.Raytrace(LightmapMesh.get()); raytracer.Raytrace(LightmapMesh.get());
#else #else
Raytracer raytracer; DLightRaytracer raytracer;
raytracer.Raytrace(LightmapMesh.get()); raytracer.Raytrace(LightmapMesh.get());
#endif #endif
LightmapMesh->CreateTextures(); LightmapMesh->CreateTextures();

View file

@ -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 <map>
#include <vector>
#include <algorithm>
#include <zlib.h>
extern int Multisample;
extern int LightBounce;
extern float GridSize;
GPURaytracer::GPURaytracer()
{
device = std::make_unique<VulkanDevice>();
}
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<SurfaceTask> 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);
}
}

View file

@ -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<VulkanDevice> device;
};

View file

@ -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<VulkanShader> 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<unsigned int> 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<VulkanShader>(device, shaderModule);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,515 @@
#include "vulkandevice.h"
#include "vulkanobjects.h"
#include <algorithm>
#include <set>
#include <string>
#include <mutex>
VulkanDevice::VulkanDevice(int vk_device, bool vk_debug, std::function<void(const char* typestr, const std::string& msg)> 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<std::string> 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<bool>(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<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> 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<std::string> seenMessages;
static int totalMessages;
std::unique_lock<std::mutex> 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<VkLayerProperties> VulkanDevice::getAvailableLayers()
{
uint32_t layerCount;
VkResult result = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
result = vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
return availableLayers;
}
std::vector<VkExtensionProperties> VulkanDevice::getExtensions()
{
uint32_t extensionCount = 0;
VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions(extensionCount);
result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
return extensions;
}
std::vector<VulkanPhysicalDevice> 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<VkPhysicalDevice> devices(deviceCount);
result = vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
if (result != VK_SUCCESS)
throw std::runtime_error("vkEnumeratePhysicalDevices failed (2)");
std::vector<VulkanPhysicalDevice> 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<const char *> VulkanDevice::getPlatformExtensions()
{
return
{
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
}
#else
std::vector<const char *> 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!");
}

126
src/lightmap/vulkandevice.h Normal file
View file

@ -0,0 +1,126 @@
#pragma once
#include <functional>
#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<VkExtensionProperties> extensions;
std::vector<VkQueueFamilyProperties> 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<void(const char* typestr, const std::string& msg)> 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<VkLayerProperties> availableLayers;
std::vector<VkExtensionProperties> extensions;
std::vector<const char *> enabledExtensions;
std::vector<const char *> optionalExtensions = { VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME };
std::vector<const char*> enabledValidationLayers;
// Device setup
VkPhysicalDeviceFeatures enabledDeviceFeatures = {};
std::vector<const char *> enabledDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
std::vector<const char *> 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<VulkanPhysicalDevice> availableDevices;
std::vector<VulkanCompatibleDevice> supportedDevices;
static void initVolk();
private:
int vk_device;
bool vk_debug;
std::function<void(const char* typestr, const std::string& msg)> 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<VkLayerProperties> getAvailableLayers();
static std::vector<VkExtensionProperties> getExtensions();
static std::vector<const char *> getPlatformExtensions();
static std::vector<VulkanPhysicalDevice> getPhysicalDevices(VkInstance instance);
};

1138
src/lightmap/vulkanobjects.h Normal file

File diff suppressed because it is too large Load diff